diff options
Diffstat (limited to 'arch/ppc')
34 files changed, 3707 insertions, 964 deletions
diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index dda6dc91e1b7..d780cbcef8b7 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -30,6 +30,10 @@ config PPC32 bool default y +# All PPCs use generic nvram driver through ppc_md +config GENERIC_NVRAM + bool + default y source "init/Kconfig" @@ -989,8 +993,6 @@ config HOTPLUG source "drivers/pcmcia/Kconfig" -source "drivers/parport/Kconfig" - endmenu menu "Advanced setup" @@ -1088,179 +1090,10 @@ config PIN_TLB depends on ADVANCED_OPTIONS && 8xx endmenu -source "drivers/base/Kconfig" - -source "drivers/mtd/Kconfig" - -source "drivers/pnp/Kconfig" - -source "drivers/block/Kconfig" - -source "drivers/md/Kconfig" - -source "drivers/ide/Kconfig" - -source "drivers/scsi/Kconfig" - -source "drivers/message/fusion/Kconfig" - -source "drivers/ieee1394/Kconfig" - -source "drivers/message/i2o/Kconfig" - -source "net/Kconfig" - -source "drivers/isdn/Kconfig" - -source "drivers/video/Kconfig" - -source "drivers/cdrom/Kconfig" - -source "drivers/input/Kconfig" - - -menu "Macintosh device drivers" - -# we want to change this to something like CONFIG_SYSCTRL_CUDA/PMU -config ADB_CUDA - bool "Support for CUDA based PowerMacs" - depends on PPC_PMAC - help - This provides support for CUDA based Power Macintosh systems. This - includes most OldWorld PowerMacs, the first generation iMacs, the - Blue&White G3 and the "Yikes" G4 (PCI Graphics). All later models - should use CONFIG_ADB_PMU instead. It is safe to say Y here even if - your machine doesn't have a CUDA. - - If unsure say Y. - -config ADB_PMU - bool "Support for PMU based PowerMacs" - depends on PPC_PMAC - help - On PowerBooks, iBooks, and recent iMacs and Power Macintoshes, the - PMU is an embedded microprocessor whose primary function is to - control system power, and battery charging on the portable models. - The PMU also controls the ADB (Apple Desktop Bus) which connects to - the keyboard and mouse on some machines, as well as the non-volatile - RAM and the RTC (real time clock) chip. Say Y to enable support for - this device; you should do so if your machine is one of those - mentioned above. - -config PMAC_PBOOK - bool "Power management support for PowerBooks" - depends on ADB_PMU - ---help--- - This provides support for putting a PowerBook to sleep; it also - enables media bay support. Power management works on the - PB2400/3400/3500, Wallstreet, Lombard, and Bronze PowerBook G3 and - the Titanium Powerbook G4, as well as the iBooks. You should get - the power management daemon, pmud, to make it work and you must have - the /dev/pmu device (see the pmud README). - - Get pmud from <ftp://ftp.samba.org/pub/ppclinux/pmud/>. - - If you have a PowerBook, you should say Y here. - - You may also want to compile the dma sound driver as a module and - have it autoloaded. The act of removing the module shuts down the - sound hardware for more power savings. - -config PM - bool - depends on PPC_PMAC && ADB_PMU && PMAC_PBOOK - default y - -config PMAC_APM_EMU - tristate "APM emulation" - depends on PMAC_PBOOK - -# made a separate option since backlight may end up beeing used -# on non-powerbook machines (but only on PMU based ones AFAIK) -config PMAC_BACKLIGHT - bool "Backlight control for LCD screens" - depends on ADB_PMU - help - Say Y here to build in code to manage the LCD backlight on a - Macintosh PowerBook. With this code, the backlight will be turned - on and off appropriately on power-management and lid-open/lid-closed - events; also, the PowerBook button device will be enabled so you can - change the screen brightness. - -config MAC_FLOPPY - bool "Support for PowerMac floppy" - depends on PPC_PMAC - help - If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple) - floppy controller, say Y here. Most commonly found in PowerMacs. - -config MAC_SERIAL - tristate "Support for PowerMac serial ports (OBSOLETE DRIVER)" - depends on PPC_PMAC - help - This driver is obsolete. Use CONFIG_SERIAL_PMACZILOG in - "Character devices --> Serial drivers --> PowerMac z85c30" option. - -config ADB - bool "Apple Desktop Bus (ADB) support" - depends on PPC_PMAC - help - Apple Desktop Bus (ADB) support is for support of devices which - are connected to an ADB port. ADB devices tend to have 4 pins. - If you have an Apple Macintosh prior to the iMac, an iBook or - PowerBook, or a "Blue and White G3", you probably want to say Y - here. Otherwise say N. - -config ADB_MACIO - bool "Include MacIO (CHRP) ADB driver" - depends on ADB - help - Say Y here to include direct support for the ADB controller in the - Hydra chip used on PowerPC Macintoshes of the CHRP type. (The Hydra - also includes a MESH II SCSI controller, DBDMA controller, VIA chip, - OpenPIC controller and two RS422/Geoports.) - -config INPUT_ADBHID - bool "Support for ADB input devices (keyboard, mice, ...)" - depends on ADB && INPUT=y - help - Say Y here if you want to have ADB (Apple Desktop Bus) HID devices - such as keyboards, mice, joysticks, trackpads or graphic tablets - handled by the input layer. If you say Y here, make sure to say Y to - the corresponding drivers "Keyboard support" (CONFIG_INPUT_KEYBDEV), - "Mouse Support" (CONFIG_INPUT_MOUSEDEV) and "Event interface - support" (CONFIG_INPUT_EVDEV) as well. - - If unsure, say Y. - -config MAC_EMUMOUSEBTN - bool "Support for mouse button 2+3 emulation" - depends on INPUT_ADBHID - help - This provides generic support for emulating the 2nd and 3rd mouse - button with keypresses. If you say Y here, the emulation is still - disabled by default. The emulation is controlled by these sysctl - entries: - /proc/sys/dev/mac_hid/mouse_button_emulation - /proc/sys/dev/mac_hid/mouse_button2_keycode - /proc/sys/dev/mac_hid/mouse_button3_keycode - - If you have an Apple machine with a 1-button mouse, say Y here. - -config ANSLCD - bool "Support for ANS LCD display" - depends on ADB_CUDA - -endmenu - -source "drivers/char/Kconfig" - -source "drivers/media/Kconfig" +source "drivers/Kconfig" source "fs/Kconfig" -source "sound/Kconfig" - source "arch/ppc/8xx_io/Kconfig" source "arch/ppc/8260_io/Kconfig" @@ -1285,8 +1118,6 @@ config SERIAL_SICC_CONSOLE endmenu -source "drivers/usb/Kconfig" - source "lib/Kconfig" diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 4d51caaf322a..33b6ca87127d 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -22,11 +22,12 @@ obj-y := entry.o traps.o irq.o idle.o time.o misc.o \ semaphore.o syscalls.o setup.o \ cputable.o ppc_htab.o obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o +obj-$(CONFIG_POWER4) += cpu_setup_power4.o obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_PCI) += pci-dma.o obj-$(CONFIG_KGDB) += ppc-stub.o -obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_SMP) += smp.o smp-tbsync.o obj-$(CONFIG_TAU) += temp.o ifdef CONFIG_MATH_EMULATION diff --git a/arch/ppc/kernel/cpu_setup_6xx.S b/arch/ppc/kernel/cpu_setup_6xx.S index f805844fd853..9e4e48ffb641 100644 --- a/arch/ppc/kernel/cpu_setup_6xx.S +++ b/arch/ppc/kernel/cpu_setup_6xx.S @@ -142,7 +142,7 @@ setup_7410_workarounds: sync isync blr - + /* 740/750/7400/7410 * Enable Store Gathering (SGE), Address Brodcast (ABE), * Branch History Table (BHTE), Branch Target ICache (BTIC) @@ -213,7 +213,7 @@ setup_745x_specifics: li r7,CPU_FTR_CAN_NAP andc r6,r6,r7 stw r6,CPU_SPEC_FEATURES(r5) -1: +1: mfspr r11,HID0 /* All of the bits we have to set..... @@ -248,20 +248,21 @@ END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) /* Definitions for the table use to save CPU states */ #define CS_HID0 0 #define CS_HID1 4 -#define CS_MSSCR0 8 -#define CS_MSSSR0 12 -#define CS_ICTRL 16 -#define CS_LDSTCR 20 -#define CS_LDSTDB 24 -#define CS_SIZE 28 +#define CS_HID2 8 +#define CS_MSSCR0 12 +#define CS_MSSSR0 16 +#define CS_ICTRL 20 +#define CS_LDSTCR 24 +#define CS_LDSTDB 28 +#define CS_SIZE 32 .data .balign L1_CACHE_LINE_SIZE -cpu_state_storage: +cpu_state_storage: .space CS_SIZE .balign L1_CACHE_LINE_SIZE,0 .text - + /* Called in normal context to backup CPU 0 state. This * does not include cache settings. This function is also * called for machine sleep. This does not include the MMU @@ -311,11 +312,18 @@ _GLOBAL(__save_cpu_setup) stw r4,CS_LDSTCR(r5) mfspr r4,SPRN_LDSTDB stw r4,CS_LDSTDB(r5) -1: +1: bne cr5,1f /* Backup 750FX specific registers */ mfspr r4,SPRN_HID1 stw r4,CS_HID1(r5) + /* If rev 2.x, backup HID2 */ + mfspr r3,PVR + andi. r3,r3,0xff00 + cmpi cr0,r3,0x0200 + bne 1f + mfspr r4,SPRN_HID2 + stw r4,CS_HID2(r5) 1: mtcr r7 blr @@ -395,9 +403,19 @@ _GLOBAL(__restore_cpu_setup) sync 2: bne cr5,1f /* Restore 750FX specific registers - * that is restore PLL config & switch - * to PLL 0 + * that is restore HID2 on rev 2.x and PLL config & switch + * to PLL 0 on all */ + /* If rev 2.x, restore HID2 with low voltage bit cleared */ + mfspr r3,PVR + andi. r3,r3,0xff00 + cmpi cr0,r3,0x0200 + bne 4f + lwz r4,CS_HID2(r5) + rlwinm r4,r4,0,19,17 + mtspr SPRN_HID2,r4 + sync +4: lwz r4,CS_HID1(r5) rlwinm r5,r4,0,16,14 mtspr SPRN_HID1,r5 diff --git a/arch/ppc/kernel/cpu_setup_power4.S b/arch/ppc/kernel/cpu_setup_power4.S new file mode 100644 index 000000000000..0cff6fbad17c --- /dev/null +++ b/arch/ppc/kernel/cpu_setup_power4.S @@ -0,0 +1,182 @@ +/* + * This file contains low level CPU setup functions. + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <linux/config.h> +#include <asm/processor.h> +#include <asm/page.h> +#include <asm/ppc_asm.h> +#include <asm/cputable.h> +#include <asm/ppc_asm.h> +#include <asm/offsets.h> +#include <asm/cache.h> + +_GLOBAL(__power4_cpu_preinit) + /* + * On the PPC970, we have to turn off real-mode cache inhibit + * early, before we first turn the MMU off. + */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi r0,0x39 + bnelr + + li r0,0 + sync + mtspr SPRN_HID4,r0 + isync + sync + mtspr SPRN_HID5,r0 + isync + + mfspr r0,SPRN_HID1 + li r11,0x1200 /* enable i-fetch cacheability */ + sldi r11,r11,44 /* and prefetch */ + or r0,r0,r11 + mtspr SPRN_HID1,r0 + mtspr SPRN_HID1,r0 + isync + li r0,0 + sync + mtspr SPRN_HIOR,0 /* Clear interrupt prefix */ + isync + blr + +_GLOBAL(__setup_cpu_power4) + blr +_GLOBAL(__setup_cpu_ppc970) + mfspr r0,SPRN_HID0 + li r11,5 /* clear DOZE and SLEEP */ + rldimi r0,r11,52,8 /* set NAP and DPM */ + mtspr SPRN_HID0,r0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + sync + isync + blr + +/* Definitions for the table use to save CPU states */ +#define CS_HID0 0 +#define CS_HID1 8 +#define CS_HID4 16 +#define CS_HID5 24 +#define CS_SIZE 32 + + .data + .balign L1_CACHE_LINE_SIZE +cpu_state_storage: + .space CS_SIZE + .balign L1_CACHE_LINE_SIZE,0 + .text + +/* Called in normal context to backup CPU 0 state. This + * does not include cache settings. This function is also + * called for machine sleep. This does not include the MMU + * setup, BATs, etc... but rather the "special" registers + * like HID0, HID1, HID4, etc... + */ +_GLOBAL(__save_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + lis r5,cpu_state_storage@h + ori r5,r5,cpu_state_storage@l + + /* We only deal with 970 for now */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi r0,0x39 + bne 1f + + /* Save HID0,1,4 and 5 */ + mfspr r3,SPRN_HID0 + std r3,CS_HID0(r5) + mfspr r3,SPRN_HID1 + std r3,CS_HID1(r5) + mfspr r3,SPRN_HID4 + std r3,CS_HID4(r5) + mfspr r3,SPRN_HID5 + std r3,CS_HID5(r5) + +1: + mtcr r7 + blr + +/* Called with no MMU context (typically MSR:IR/DR off) to + * restore CPU state as backed up by the previous + * function. This does not include cache setting + */ +_GLOBAL(__restore_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + lis r5,(cpu_state_storage-KERNELBASE)@h + ori r5,r5,cpu_state_storage@l + + /* We only deal with 970 for now */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi r0,0x39 + bne 1f + + /* Clear interrupt prefix */ + li r0,0 + sync + mtspr SPRN_HIOR,0 + isync + + /* Restore HID0 */ + ld r3,CS_HID0(r5) + sync + isync + mtspr SPRN_HID0,r3 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + sync + isync + + /* Restore HID1 */ + ld r3,CS_HID1(r5) + sync + isync + mtspr SPRN_HID1,r3 + mtspr SPRN_HID1,r3 + sync + isync + + /* Restore HID4 */ + ld r3,CS_HID4(r5) + sync + isync + mtspr SPRN_HID4,r3 + sync + isync + + /* Restore HID5 */ + ld r3,CS_HID5(r5) + sync + isync + mtspr SPRN_HID5,r3 + sync + isync +1: + mtcr r7 + blr + diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 4f4c12491d7a..27ab72b2167b 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -141,17 +141,6 @@ __start: mr r27,r7 li r24,0 /* cpu # */ -#ifdef CONFIG_POWER4 -/* - * On the PPC970, we have to turn off real-mode cache inhibit - * early, before we first turn the MMU off. - */ - mfspr r0,SPRN_PVR - srwi r0,r0,16 - cmpwi r0,0x39 - beql ppc970_setup_hid -#endif /* CONFIG_POWER4 */ - /* * early_init() does the early machine identification and does * the necessary low-level setup and clears the BSS @@ -159,6 +148,14 @@ __start: */ bl early_init +/* + * On POWER4, we first need to tweak some CPU configuration registers + * like real mode cache inhibit or exception base + */ +#ifdef CONFIG_POWER4 + bl __power4_cpu_preinit +#endif /* CONFIG_POWER4 */ + #ifdef CONFIG_APUS /* On APUS the __va/__pa constants need to be set to the correct * values before continuing. @@ -1216,7 +1213,7 @@ __secondary_start_psurge99: __secondary_start: #ifdef CONFIG_PPC64BRIDGE mfmsr r0 - clrldi r0,r0,1 /* make sure it's in 32-bit mode */ + clrldi r0,r0,1 /* make sure it's in 32-bit mode */ SYNC MTMSRD(r0) isync @@ -1278,26 +1275,15 @@ __secondary_start: */ _GLOBAL(__setup_cpu_power3) blr -_GLOBAL(__setup_cpu_power4) - blr -_GLOBAL(__setup_cpu_ppc970) - blr _GLOBAL(__setup_cpu_generic) blr -#ifndef CONFIG_6xx +#if !defined(CONFIG_6xx) && !defined(CONFIG_POWER4) _GLOBAL(__save_cpu_setup) blr _GLOBAL(__restore_cpu_setup) -#ifdef CONFIG_POWER4 - /* turn off real-mode cache inhibit on the PPC970 */ - mfspr r0,SPRN_PVR - srwi r0,r0,16 - cmpwi r0,0x39 - beq ppc970_setup_hid -#endif blr -#endif /* CONFIG_6xx */ +#endif /* !defined(CONFIG_6xx) && !defined(CONFIG_POWER4) */ /* @@ -1633,10 +1619,14 @@ initial_mm_power4: lis r4,0x2000 /* set pseudo-segment reg 12 */ ori r5,r4,0x0ccc mtsr 12,r5 +#if 0 ori r5,r4,0x0888 /* set pseudo-segment reg 8 */ mtsr 8,r5 /* (for access to serial port) */ - ori r5,r4,0x0999 /* set pseudo-segment reg 8 */ +#endif +#ifdef CONFIG_BOOTX_TEXT + ori r5,r4,0x0999 /* set pseudo-segment reg 9 */ mtsr 9,r5 /* (for access to screen) */ +#endif mfmsr r0 clrldi r0,r0,1 sync @@ -1644,43 +1634,8 @@ initial_mm_power4: isync blr -/* - * On 970 (G5), we pre-set a few bits in HID0 & HID1 - */ -ppc970_setup_hid: - li r0,0 - sync - mtspr 0x3f4,r0 - isync - sync - mtspr 0x3f6,r0 - isync - mfspr r0,SPRN_HID0 - li r11,1 /* clear DOZE, NAP and SLEEP */ - rldimi r0,r11,52,8 /* set DPM */ - mtspr SPRN_HID0,r0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - sync - isync - mfspr r0,SPRN_HID1 - li r11,0x1200 /* enable i-fetch cacheability */ - sldi r11,r11,44 /* and prefetch */ - or r0,r0,r11 - mtspr SPRN_HID1,r0 - mtspr SPRN_HID1,r0 - isync - li r0,0 - sync - mtspr 0x137,0 - isync - blr #endif /* CONFIG_POWER4 */ - + #ifdef CONFIG_8260 /* Jump into the system reset for the rom. * We first disable the MMU, and then jump to the ROM reset address. diff --git a/arch/ppc/kernel/idle_power4.S b/arch/ppc/kernel/idle_power4.S index f02c12feeae0..538a044f4707 100644 --- a/arch/ppc/kernel/idle_power4.S +++ b/arch/ppc/kernel/idle_power4.S @@ -28,17 +28,11 @@ /* * Init idle, called at early CPU setup time from head.S for each CPU - * Make sure no rest of NAP mode remains in HID0, save default - * values for some CPU specific registers. Called with r24 - * containing CPU number and r3 reloc offset + * So nothing for now. Called with r24 containing CPU number and r3 + * reloc offset */ .globl init_idle_power4 init_idle_power4: -BEGIN_FTR_SECTION - mfspr r4,SPRN_HID0 - rlwinm r4,r4,0,10,8 /* Clear NAP */ - mtspr SPRN_HID0, r4 -END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) blr /* @@ -48,10 +42,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) */ .globl power4_idle power4_idle: - /* Check if we can nap or doze, put HID0 mask in r3 - */ - lis r3, 0 BEGIN_FTR_SECTION + blr +END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) /* We must dynamically check for the NAP feature as it * can be cleared by CPU init after the fixups are done */ @@ -59,16 +52,11 @@ BEGIN_FTR_SECTION lwz r4,cur_cpu_spec@l(r4) lwz r4,CPU_SPEC_FEATURES(r4) andi. r0,r4,CPU_FTR_CAN_NAP - beq 1f + beqlr /* Now check if user or arch enabled NAP mode */ lis r4,powersave_nap@ha lwz r4,powersave_nap@l(r4) cmpi 0,r4,0 - beq 1f - lis r3,HID0_NAP@h -1: -END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) - cmpi 0,r3,0 beqlr /* Clear MSR:EE */ @@ -85,18 +73,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) blr 1: /* Go to NAP now */ - mfspr r4,SPRN_HID0 - lis r5,(HID0_NAP|HID0_SLEEP)@h - andc r4,r4,r5 - or r4,r4,r3 - oris r4,r4,HID0_DPM@h /* that should be done once for all */ - mtspr SPRN_HID0,r4 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 - mfspr r0,SPRN_HID0 BEGIN_FTR_SECTION DSSALL sync diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 4f96a08a635e..aaffb6e71b96 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -201,7 +201,7 @@ _GLOBAL(call_setup_cpu) mr r4,r24 bctr -#ifdef CONFIG_CPU_FREQ_PMAC +#if defined(CONFIG_CPU_FREQ_PMAC) && defined(CONFIG_6xx) /* This gets called by via-pmu.c to switch the PLL selection * on 750fx CPU. This function should really be moved to some @@ -253,7 +253,7 @@ _GLOBAL(low_choose_750fx_pll) mtmsr r7 blr -#endif /* CONFIG_CPU_FREQ_PMAC */ +#endif /* CONFIG_CPU_FREQ_PMAC && CONFIG_6xx */ /* void local_save_flags_ptr(unsigned long *flags) */ _GLOBAL(local_save_flags_ptr) diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index dcc82d592f03..bb1a29201330 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -46,7 +46,9 @@ static int reparent_resources(struct resource *parent, struct resource *res); static void fixup_rev1_53c810(struct pci_dev* dev); static void fixup_cpc710_pci64(struct pci_dev* dev); #ifdef CONFIG_PPC_PMAC -static void pcibios_fixup_cardbus(struct pci_dev* dev); +extern void pmac_pci_fixup_cardbus(struct pci_dev* dev); +extern void pmac_pci_fixup_pciata(struct pci_dev* dev); +extern void pmac_pci_fixup_k2_sata(struct pci_dev* dev); #endif #ifdef CONFIG_PPC_OF static u8* pci_to_OF_bus_map; @@ -69,7 +71,9 @@ struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, #ifdef CONFIG_PPC_PMAC /* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */ - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_ANY_ID, pcibios_fixup_cardbus }, + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_ANY_ID, pmac_pci_fixup_cardbus }, + { PCI_FIXUP_FINAL, PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, 0x0240, pmac_pci_fixup_k2_sata }, #endif /* CONFIG_PPC_PMAC */ { 0 } }; @@ -155,42 +159,6 @@ pcibios_fixup_resources(struct pci_dev *dev) ppc_md.pcibios_fixup_resources(dev); } -#ifdef CONFIG_PPC_PMAC -static void -pcibios_fixup_cardbus(struct pci_dev* dev) -{ - if (_machine != _MACH_Pmac) - return; - /* - * Fix the interrupt routing on the various cardbus bridges - * used on powerbooks - */ - if (dev->vendor != PCI_VENDOR_ID_TI) - return; - if (dev->device == PCI_DEVICE_ID_TI_1130 || - dev->device == PCI_DEVICE_ID_TI_1131) { - u8 val; - /* Enable PCI interrupt */ - if (pci_read_config_byte(dev, 0x91, &val) == 0) - pci_write_config_byte(dev, 0x91, val | 0x30); - /* Disable ISA interrupt mode */ - if (pci_read_config_byte(dev, 0x92, &val) == 0) - pci_write_config_byte(dev, 0x92, val & ~0x06); - } - if (dev->device == PCI_DEVICE_ID_TI_1210 || - dev->device == PCI_DEVICE_ID_TI_1211 || - dev->device == PCI_DEVICE_ID_TI_1410) { - u8 val; - /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA - signal out the MFUNC0 pin */ - if (pci_read_config_byte(dev, 0x8c, &val) == 0) - pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2); - /* Disable ISA interrupt mode */ - if (pci_read_config_byte(dev, 0x92, &val) == 0) - pci_write_config_byte(dev, 0x92, val & ~0x06); - } -} -#endif /* CONFIG_PPC_PMAC */ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, @@ -832,6 +800,17 @@ pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) return NULL; /* Fixup bus number according to what OF think it is. */ +#ifdef CONFIG_PPC_PMAC + /* The G5 need a special case here. Basically, we don't remap all + * busses on it so we don't create the pci-OF-map. However, we do + * remap the AGP bus and so have to deal with it. A future better + * fix has to be done by making the remapping per-host and always + * filling the pci_to_OF map. --BenH + */ + if (_machine == _MACH_Pmac && busnr >= 0xf0) + busnr -= 0xf0; + else +#endif if (pci_to_OF_bus_map) busnr = pci_to_OF_bus_map[busnr]; if (busnr == 0xff) @@ -922,9 +901,10 @@ void __init pci_process_bridge_OF_ranges(struct pci_controller *hose, struct device_node *dev, int primary) { - unsigned int *ranges, *prev; + static unsigned int static_lc_ranges[256] __initdata; + unsigned int *dt_ranges, *lc_ranges, *ranges, *prev; unsigned int size; - int rlen = 0; + int rlen = 0, orig_rlen; int memno = 0; struct resource *res; int np, na = prom_n_addr_cells(dev); @@ -934,7 +914,22 @@ pci_process_bridge_OF_ranges(struct pci_controller *hose, * that can have more than 3 ranges, fortunately using contiguous * addresses -- BenH */ - ranges = (unsigned int *) get_property(dev, "ranges", &rlen); + dt_ranges = (unsigned int *) get_property(dev, "ranges", &rlen); + if (!dt_ranges) + return; + /* Sanity check, though hopefully that never happens */ + if (rlen > sizeof(static_lc_ranges)) { + printk(KERN_WARNING "OF ranges property too large !\n"); + rlen = sizeof(static_lc_ranges); + } + lc_ranges = static_lc_ranges; + memcpy(lc_ranges, dt_ranges, rlen); + orig_rlen = rlen; + + /* Let's work on a copy of the "ranges" property instead of damaging + * the device-tree image in memory + */ + ranges = lc_ranges; prev = NULL; while ((rlen -= np * sizeof(unsigned int)) >= 0) { if (prev) { @@ -959,10 +954,9 @@ pci_process_bridge_OF_ranges(struct pci_controller *hose, * (size depending on dev->n_addr_cells) * cells 4+5 or 5+6: the size of the range */ - rlen = 0; - hose->io_base_phys = 0; - ranges = (unsigned int *) get_property(dev, "ranges", &rlen); - while ((rlen -= np * sizeof(unsigned int)) >= 0) { + ranges = lc_ranges; + rlen = orig_rlen; + while (ranges && (rlen -= np * sizeof(unsigned int)) >= 0) { res = NULL; size = ranges[na+4]; switch (ranges[0] >> 24) { @@ -1059,7 +1053,7 @@ do_update_p2p_io_resource(struct pci_bus *bus, int enable_vga) res = *(bus->resource[0]); - DBG("Remapping Bus %d, bridge: %s\n", bus->number, bridge->name); + DBG("Remapping Bus %d, bridge: %s\n", bus->number, bridge->slot_name); res.start -= ((unsigned long) hose->io_base_virt - isa_io_base); res.end -= ((unsigned long) hose->io_base_virt - isa_io_base); DBG(" IO window: %08lx-%08lx\n", res.start, res.end); @@ -1662,12 +1656,23 @@ pci_bus_to_phys(unsigned int ba, int busnr) * Note that the returned IO or memory base is a physical address */ -long -sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) +long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) { - struct pci_controller* hose = pci_bus_to_hose(bus); + struct pci_controller* hose; long result = -EOPNOTSUPP; + /* Argh ! Please forgive me for that hack, but that's the + * simplest way to get existing XFree to not lockup on some + * G5 machines... So when something asks for bus 0 io base + * (bus 0 is HT root), we return the AGP one instead. + */ +#ifdef CONFIG_PPC_PMAC + if (_machine == _MACH_Pmac && machine_is_compatible("MacRISC4")) + if (bus == 0) + bus = 0xf0; +#endif /* CONFIG_PPC_PMAC */ + + hose = pci_bus_to_hose(bus); if (!hose) return -ENODEV; diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 390e35ba6476..ab5b941eaacb 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -75,6 +75,7 @@ int abs(int); extern unsigned long mm_ptov (unsigned long paddr); EXPORT_SYMBOL(clear_page); +EXPORT_SYMBOL(clear_user_page); EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(do_syscall_trace); EXPORT_SYMBOL(transfer_to_handler); @@ -236,12 +237,6 @@ EXPORT_SYMBOL(adb_try_handler_change); EXPORT_SYMBOL(cuda_request); EXPORT_SYMBOL(cuda_poll); #endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_PMAC_BACKLIGHT -EXPORT_SYMBOL(get_backlight_level); -EXPORT_SYMBOL(set_backlight_level); -EXPORT_SYMBOL(set_backlight_enable); -EXPORT_SYMBOL(register_backlight_controller); -#endif /* CONFIG_PMAC_BACKLIGHT */ #ifdef CONFIG_PPC_MULTIPLATFORM EXPORT_SYMBOL(_machine); #endif @@ -282,14 +277,6 @@ EXPORT_SYMBOL(note_scsi_host); #ifdef CONFIG_VT EXPORT_SYMBOL(kd_mksound); #endif -#ifdef CONFIG_NVRAM -EXPORT_SYMBOL(nvram_read_byte); -EXPORT_SYMBOL(nvram_write_byte); -#ifdef CONFIG_PPC_PMAC -EXPORT_SYMBOL(pmac_xpram_read); -EXPORT_SYMBOL(pmac_xpram_write); -#endif -#endif /* CONFIG_NVRAM */ EXPORT_SYMBOL(to_tm); EXPORT_SYMBOL(pm_power_off); diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 94c00edb40bf..7d1ecb35a619 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -35,6 +35,7 @@ #include <asm/system.h> #include <asm/pmac_feature.h> #include <asm/sections.h> +#include <asm/nvram.h> #include <asm/xmon.h> #if defined CONFIG_KGDB @@ -111,6 +112,9 @@ struct screen_info screen_info = { void machine_restart(char *cmd) { +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif ppc_md.restart(cmd); } @@ -118,6 +122,9 @@ EXPORT_SYMBOL(machine_restart); void machine_power_off(void) { +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif ppc_md.power_off(); } @@ -125,6 +132,9 @@ EXPORT_SYMBOL(machine_power_off); void machine_halt(void) { +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif ppc_md.halt(); } @@ -558,24 +568,30 @@ int __init ppc_setup_l2cr(char *str) __setup("l2cr=", ppc_setup_l2cr); #ifdef CONFIG_NVRAM -/* Generic nvram hooks we now look into ppc_md.nvram_read_val - * on pmac too ;) - * //XX Those 2 could be moved to headers - */ -unsigned char -nvram_read_byte(int addr) + +/* Generic nvram hooks used by drivers/char/gen_nvram.c */ +unsigned char nvram_read_byte(int addr) { if (ppc_md.nvram_read_val) return ppc_md.nvram_read_val(addr); return 0xff; } +EXPORT_SYMBOL(nvram_read_byte); -void -nvram_write_byte(unsigned char val, int addr) +void nvram_write_byte(unsigned char val, int addr) { if (ppc_md.nvram_write_val) - ppc_md.nvram_write_val(val, addr); + ppc_md.nvram_write_val(addr, val); +} +EXPORT_SYMBOL(nvram_write_byte); + +void nvram_sync(void) +{ + if (ppc_md.nvram_sync) + ppc_md.nvram_sync(); } +EXPORT_SYMBOL(nvram_sync); + #endif /* CONFIG_NVRAM */ static struct cpu cpu_devices[NR_CPUS]; diff --git a/arch/ppc/kernel/smp-tbsync.c b/arch/ppc/kernel/smp-tbsync.c new file mode 100644 index 000000000000..2c9cd95bcea6 --- /dev/null +++ b/arch/ppc/kernel/smp-tbsync.c @@ -0,0 +1,181 @@ +/* + * Smp timebase synchronization for ppc. + * + * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se) + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/unistd.h> +#include <linux/init.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/time.h> + +#define NUM_ITER 300 + +enum { + kExit=0, kSetAndTest, kTest +}; + +static struct { + volatile int tbu; + volatile int tbl; + volatile int mark; + volatile int cmd; + volatile int handshake; + int filler[3]; + + volatile int ack; + int filler2[7]; + + volatile int race_result; +} *tbsync; + +static volatile int running; + +static void __devinit +enter_contest( int mark, int add ) +{ + while( (int)(get_tbl() - mark) < 0 ) + tbsync->race_result = add; +} + +void __devinit +smp_generic_take_timebase( void ) +{ + int cmd, tbl, tbu; + + local_irq_disable(); + while( !running ) + ; + rmb(); + + for( ;; ) { + tbsync->ack = 1; + while( !tbsync->handshake ) + ; + rmb(); + + cmd = tbsync->cmd; + tbl = tbsync->tbl; + tbu = tbsync->tbu; + tbsync->ack = 0; + if( cmd == kExit ) + return; + + if( cmd == kSetAndTest ) { + while( tbsync->handshake ) + ; + asm volatile ("mttbl %0" :: "r" (tbl) ); + asm volatile ("mttbu %0" :: "r" (tbu) ); + } else { + while( tbsync->handshake ) + ; + } + enter_contest( tbsync->mark, -1 ); + } + local_irq_enable(); +} + +static int __devinit +start_contest( int cmd, int offset, int num ) +{ + int i, tbu, tbl, mark, score=0; + + tbsync->cmd = cmd; + + local_irq_disable(); + for( i=-3; i<num; ) { + tbl = get_tbl() + 400; + tbsync->tbu = tbu = get_tbu(); + tbsync->tbl = tbl + offset; + tbsync->mark = mark = tbl + 400; + + wmb(); + + tbsync->handshake = 1; + while( tbsync->ack ) + ; + + while( (int)(get_tbl() - tbl) <= 0 ) + ; + tbsync->handshake = 0; + enter_contest( mark, 1 ); + + while( !tbsync->ack ) + ; + + if( tbsync->tbu != get_tbu() || ((tbsync->tbl ^ get_tbl()) & 0x80000000) ) + continue; + if( i++ > 0 ) + score += tbsync->race_result; + } + local_irq_enable(); + return score; +} + +void __devinit +smp_generic_give_timebase( void ) +{ + int i, score, score2, old, min=0, max=5000, offset=1000; + + printk("Synchronizing timebase\n"); + + /* if this fails then this kernel won't work anyway... */ + tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL ); + memset( tbsync, 0, sizeof(*tbsync) ); + mb(); + running = 1; + + while( !tbsync->ack ) + ; + + /* binary search */ + for( old=-1 ; old != offset ; offset=(min+max)/2 ) { + score = start_contest( kSetAndTest, offset, NUM_ITER ); + + printk("score %d, offset %d\n", score, offset ); + + if( score > 0 ) + max = offset; + else + min = offset; + old = offset; + } + score = start_contest( kSetAndTest, min, NUM_ITER ); + score2 = start_contest( kSetAndTest, max, NUM_ITER ); + + printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 ); + score = abs( score ); + score2 = abs( score2 ); + offset = (score < score2) ? min : max; + + /* guard against inaccurate mttb */ + for( i=0; i<10; i++ ) { + start_contest( kSetAndTest, offset, NUM_ITER/10 ); + + if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 ) + score2 = -score2; + if( score2 <= score || score2 < 20 ) + break; + } + printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); + + /* exiting */ + tbsync->cmd = kExit; + wmb(); + tbsync->handshake = 1; + while( tbsync->ack ) + ; + tbsync->handshake = 0; + kfree( tbsync ); + tbsync = NULL; + running = 0; + + /* all done */ + smp_tb_synchronized = 1; +} diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 9d8e34c4702b..dcd9967d9383 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -61,10 +61,6 @@ static struct smp_ops_t *smp_ops; /* all cpu mappings are 1-1 -- Cort */ volatile unsigned long cpu_callin_map[NR_CPUS]; -#define TB_SYNC_PASSES 4 -volatile unsigned long __initdata tb_sync_flag = 0; -volatile unsigned long __initdata tb_offset = 0; - int start_secondary(void *); extern int cpu_idle(void *unused); void smp_call_function_interrupt(void); @@ -83,11 +79,14 @@ extern void __save_cpu_setup(void); #define PPC_MSG_INVALIDATE_TLB 2 #define PPC_MSG_XMON_BREAK 3 -#define smp_message_pass(t,m,d,w) \ - do { if (smp_ops) \ - atomic_inc(&ipi_sent); \ - smp_ops->message_pass((t),(m),(d),(w)); \ - } while(0) +static inline void +smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + if (smp_ops){ + atomic_inc(&ipi_sent); + smp_ops->message_pass(target,msg,data,wait); + } +} /* * Common functions @@ -291,41 +290,6 @@ void smp_call_function_interrupt(void) atomic_inc(&call_data->finished); } -/* FIXME: Do this properly for all archs --RR */ -static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED; -static unsigned int timebase_upper = 0, timebase_lower = 0; - -void __devinit -smp_generic_give_timebase(void) -{ - spin_lock(&timebase_lock); - do { - timebase_upper = get_tbu(); - timebase_lower = get_tbl(); - } while (timebase_upper != get_tbu()); - spin_unlock(&timebase_lock); - - while (timebase_upper || timebase_lower) - rmb(); -} - -void __devinit -smp_generic_take_timebase(void) -{ - int done = 0; - - while (!done) { - spin_lock(&timebase_lock); - if (timebase_upper || timebase_lower) { - set_tb(timebase_upper, timebase_lower); - timebase_upper = 0; - timebase_lower = 0; - done = 1; - } - spin_unlock(&timebase_lock); - } -} - static void __devinit smp_store_cpu_info(int id) { struct cpuinfo_PPC *c = &cpu_data[id]; diff --git a/arch/ppc/mm/hashtable.S b/arch/ppc/mm/hashtable.S index e79761e10ff0..992c244010a1 100644 --- a/arch/ppc/mm/hashtable.S +++ b/arch/ppc/mm/hashtable.S @@ -37,6 +37,32 @@ #endif /* CONFIG_SMP */ /* + * Sync CPUs with hash_page taking & releasing the hash + * table lock + */ +#ifdef CONFIG_SMP + .text +_GLOBAL(hash_page_sync) + lis r8,mmu_hash_lock@h + ori r8,r8,mmu_hash_lock@l + lis r0,0x0fff + b 10f +11: lwz r6,0(r8) + cmpwi 0,r6,0 + bne 11b +10: lwarx r6,0,r8 + cmpwi 0,r6,0 + bne- 11b + stwcx. r0,0,r8 + bne- 10b + isync + eieio + li r0,0 + stw r0,0(r8) + blr +#endif + +/* * Load a PTE into the hash table, if possible. * The address is in r4, and r3 contains an access flag: * _PAGE_RW (0x400) if a write. @@ -417,21 +443,6 @@ _GLOBAL(hash_page_patch_C) lwz r6,next_slot@l(r4) addi r6,r6,PTE_SIZE andi. r6,r6,7*PTE_SIZE -#ifdef CONFIG_POWER4 - /* - * Since we don't have BATs on POWER4, we rely on always having - * PTEs in the hash table to map the hash table and the code - * that manipulates it in virtual mode, namely flush_hash_page and - * flush_hash_segments. Otherwise we can get a DSI inside those - * routines which leads to a deadlock on the hash_table_lock on - * SMP machines. We avoid this by never overwriting the first - * PTE of each PTEG if it is already valid. - * -- paulus. - */ - bne 102f - li r6,PTE_SIZE -102: -#endif /* CONFIG_POWER4 */ stw r6,next_slot@l(r4) add r4,r3,r6 diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index e65213ea0b6e..b61a61ceea40 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -291,6 +291,8 @@ void __init MMU_init(void) ppc_md.progress("MMU:exit", 0x211); #ifdef CONFIG_BOOTX_TEXT + /* By default, we are no longer mapped */ + boot_text_mapped = 0; /* Must be done last, or ppc_md.progress will die. */ map_boot_text(); #endif diff --git a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c index c83b484d0787..b62b30ca4f78 100644 --- a/arch/ppc/mm/pgtable.c +++ b/arch/ppc/mm/pgtable.c @@ -44,6 +44,10 @@ int io_bat_index; extern char etext[], _stext[]; +#ifdef CONFIG_SMP +extern void hash_page_sync(void); +#endif + #ifdef HAVE_BATS extern unsigned long v_mapped_by_bats(unsigned long va); extern unsigned long p_mapped_by_bats(unsigned long pa); @@ -109,11 +113,17 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) void pte_free_kernel(pte_t *pte) { +#ifdef CONFIG_SMP + hash_page_sync(); +#endif free_page((unsigned long)pte); } void pte_free(struct page *pte) { +#ifdef CONFIG_SMP + hash_page_sync(); +#endif __free_page(pte); } diff --git a/arch/ppc/mm/ppc_mmu.c b/arch/ppc/mm/ppc_mmu.c index c60064993417..e8c030fbe9c4 100644 --- a/arch/ppc/mm/ppc_mmu.c +++ b/arch/ppc/mm/ppc_mmu.c @@ -83,6 +83,9 @@ unsigned long p_mapped_by_bats(unsigned long pa) unsigned long __init mmu_mapin_ram(void) { +#ifdef CONFIG_POWER4 + return 0; +#else unsigned long tot, bl, done; unsigned long max_size = (256<<20); unsigned long align; @@ -119,6 +122,7 @@ unsigned long __init mmu_mapin_ram(void) } return done; +#endif } /* @@ -244,9 +248,10 @@ void __init MMU_init_hw(void) Hash = mem_pieces_find(Hash_size, Hash_size); cacheable_memzero(Hash, Hash_size); _SDR1 = __pa(Hash) | SDR1_LOW_BITS; - Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); #endif /* CONFIG_POWER4 */ + Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", total_memory >> 20, Hash_size >> 10, Hash); diff --git a/arch/ppc/mm/tlb.c b/arch/ppc/mm/tlb.c index 832d2d2bd0c2..3bf70f65f655 100644 --- a/arch/ppc/mm/tlb.c +++ b/arch/ppc/mm/tlb.c @@ -47,6 +47,26 @@ void flush_hash_entry(struct mm_struct *mm, pte_t *ptep, unsigned long addr) } /* + * Called by ptep_test_and_clear_young() + */ +void flush_hash_one_pte(pte_t *ptep) +{ + struct page *ptepage; + struct mm_struct *mm; + unsigned long ptephys; + unsigned long addr; + + if (Hash == 0) + return; + + ptepage = virt_to_page(ptep); + mm = (struct mm_struct *) ptepage->mapping; + ptephys = __pa(ptep) & PAGE_MASK; + addr = ptepage->index + (((unsigned long)ptep & ~PAGE_MASK) << 9); + flush_hash_pages(mm->context, addr, ptephys, 1); +} + +/* * Called at the end of a mmu_gather operation to make sure the * TLB flush is completely done. */ diff --git a/arch/ppc/platforms/Makefile b/arch/ppc/platforms/Makefile index 9eb9829f3109..44026cb400ff 100644 --- a/arch/ppc/platforms/Makefile +++ b/arch/ppc/platforms/Makefile @@ -17,7 +17,8 @@ ifeq ($(CONFIG_APUS),y) obj-$(CONFIG_PCI) += apus_pci.o endif obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \ - pmac_feature.o pmac_pci.o pmac_sleep.o + pmac_feature.o pmac_pci.o pmac_sleep.o \ + pmac_low_i2c.o obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_time.o prep_setup.o ifeq ($(CONFIG_PPC_PMAC),y) diff --git a/arch/ppc/platforms/pmac_backlight.c b/arch/ppc/platforms/pmac_backlight.c index 8ff52ad34bf9..e0c797f59ac9 100644 --- a/arch/ppc/platforms/pmac_backlight.c +++ b/arch/ppc/platforms/pmac_backlight.c @@ -8,6 +8,7 @@ #include <linux/config.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/stddef.h> #include <linux/reboot.h> #include <linux/nvram.h> @@ -37,6 +38,10 @@ register_backlight_controller(struct backlight_controller *ctrler, void *data, c char *prop; int valid = 0; + /* There's already a matching controller, bail out */ + if (backlighter != NULL) + return; + bk_node = find_devices("backlight"); #ifdef CONFIG_ADB_PMU @@ -84,6 +89,7 @@ register_backlight_controller(struct backlight_controller *ctrler, void *data, c printk(KERN_INFO "Registered \"%s\" backlight controller, level: %d/15\n", type, backlight_level); } +EXPORT_SYMBOL(register_backlight_controller); void __pmac unregister_backlight_controller(struct backlight_controller *ctrler, void *data) @@ -92,6 +98,7 @@ unregister_backlight_controller(struct backlight_controller *ctrler, void *data) if (ctrler == backlighter && data == backlighter_data) backlighter = NULL; } +EXPORT_SYMBOL(unregister_backlight_controller); int __pmac set_backlight_enable(int enable) @@ -105,6 +112,7 @@ set_backlight_enable(int enable) backlight_enabled = enable; return rc; } +EXPORT_SYMBOL(set_backlight_enable); int __pmac get_backlight_enable(void) @@ -113,6 +121,7 @@ get_backlight_enable(void) return -ENODEV; return backlight_enabled; } +EXPORT_SYMBOL(get_backlight_enable); int __pmac set_backlight_level(int level) @@ -137,6 +146,7 @@ set_backlight_level(int level) } return rc; } +EXPORT_SYMBOL(set_backlight_level); int __pmac get_backlight_level(void) @@ -145,3 +155,4 @@ get_backlight_level(void) return -ENODEV; return backlight_level; } +EXPORT_SYMBOL(get_backlight_level); diff --git a/arch/ppc/platforms/pmac_cpufreq.c b/arch/ppc/platforms/pmac_cpufreq.c index 8d2ff4401ace..2c8cec2723ad 100644 --- a/arch/ppc/platforms/pmac_cpufreq.c +++ b/arch/ppc/platforms/pmac_cpufreq.c @@ -22,6 +22,7 @@ #include <linux/cpufreq.h> #include <linux/init.h> #include <linux/sysdev.h> +#include <linux/i2c.h> #include <asm/prom.h> #include <asm/machdep.h> #include <asm/irq.h> @@ -38,6 +39,14 @@ */ #undef DEBUG_FREQ +/* + * There is a problem with the core cpufreq code on SMP kernels, + * it won't recalculate the Bogomips properly + */ +#ifdef CONFIG_SMP +#warning "WARNING, CPUFREQ not recommended on SMP kernels" +#endif + extern void low_choose_750fx_pll(int pll); extern void low_sleep_handler(void); extern void openpic_suspend(struct sys_device *sysdev, u32 state); @@ -48,7 +57,14 @@ extern void enable_kernel_fp(void); static unsigned int low_freq; static unsigned int hi_freq; static unsigned int cur_freq; + +/* Clean that up some day ... use a func ptr or at least an enum... */ static int cpufreq_uses_pmu; +static int cpufreq_uses_gpios; + +static u32 voltage_gpio; +static u32 frequency_gpio; +static u32 slew_done_gpio; #define PMAC_CPU_LOW_SPEED 1 #define PMAC_CPU_HIGH_SPEED 0 @@ -65,8 +81,7 @@ static struct cpufreq_frequency_table pmac_cpu_freqs[] = { {0, CPUFREQ_TABLE_END}, }; -static inline void -wakeup_decrementer(void) +static inline void wakeup_decrementer(void) { set_dec(tb_ticks_per_jiffy); /* No currently-supported powerbook has a 601, @@ -76,8 +91,7 @@ wakeup_decrementer(void) } #ifdef DEBUG_FREQ -static inline void -debug_calc_bogomips(void) +static inline void debug_calc_bogomips(void) { /* This will cause a recalc of bogomips and display the * result. We backup/restore the value to avoid affecting the @@ -89,17 +103,18 @@ debug_calc_bogomips(void) calibrate_delay(); loops_per_jiffy = save_lpj; } -#endif +#endif /* DEBUG_FREQ */ /* Switch CPU speed under 750FX CPU control */ -static int __pmac -cpu_750fx_cpu_speed(int low_speed) +static int __pmac cpu_750fx_cpu_speed(int low_speed) { #ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); #endif +#ifdef CONFIG_6xx low_choose_750fx_pll(low_speed); +#endif #ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); debug_calc_bogomips(); @@ -108,15 +123,54 @@ cpu_750fx_cpu_speed(int low_speed) return 0; } +/* Switch CPU speed using slewing GPIOs + */ +static int __pmac gpios_set_cpu_speed(unsigned int low_speed) +{ + int gpio; + + /* If ramping up, set voltage first */ + if (low_speed == 0) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); + /* Delay is way too big but it's ok, we schedule */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } + + /* Set frequency */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, low_speed ? 0x04 : 0x05); + udelay(200); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); + } while((gpio & 0x02) == 0); + + /* If ramping down, set voltage last */ + if (low_speed == 1) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); + /* Delay is way too big but it's ok, we schedule */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } + +#ifdef DEBUG_FREQ + debug_calc_bogomips(); +#endif + + return 0; +} + /* Switch CPU speed under PMU control */ -static int __pmac -pmu_set_cpu_speed(unsigned int low_speed) +static int __pmac pmu_set_cpu_speed(unsigned int low_speed) { struct adb_request req; unsigned long save_l2cr; unsigned long save_l3cr; + preempt_disable(); + #ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); #endif @@ -197,11 +251,12 @@ pmu_set_cpu_speed(unsigned int low_speed) debug_calc_bogomips(); #endif + preempt_enable(); + return 0; } -static int __pmac -do_set_cpu_speed(int speed_mode) +static int __pmac do_set_cpu_speed(int speed_mode) { struct cpufreq_freqs freqs; int rc; @@ -216,6 +271,8 @@ do_set_cpu_speed(int speed_mode) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); if (cpufreq_uses_pmu) rc = pmu_set_cpu_speed(speed_mode); + else if (cpufreq_uses_gpios) + rc = gpios_set_cpu_speed(speed_mode); else rc = cpu_750fx_cpu_speed(speed_mode); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); @@ -224,16 +281,14 @@ do_set_cpu_speed(int speed_mode) return rc; } -static int __pmac -pmac_cpufreq_verify(struct cpufreq_policy *policy) +static int __pmac pmac_cpufreq_verify(struct cpufreq_policy *policy) { return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs); } -static int __pmac -pmac_cpufreq_target( struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) +static int __pmac pmac_cpufreq_target( struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { unsigned int newstate = 0; @@ -244,15 +299,13 @@ pmac_cpufreq_target( struct cpufreq_policy *policy, return do_set_cpu_speed(newstate); } -unsigned int __pmac -pmac_get_one_cpufreq(int i) +unsigned int __pmac pmac_get_one_cpufreq(int i) { /* Supports only one CPU for now */ return (i == 0) ? cur_freq : 0; } -static int __pmac -pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) +static int __pmac pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) { if (policy->cpu != 0) return -ENODEV; @@ -264,6 +317,18 @@ pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) return cpufreq_frequency_table_cpuinfo(policy, &pmac_cpu_freqs[0]); } +static u32 __pmac read_gpio(struct device_node *np) +{ + u32 *reg = (u32 *)get_property(np, "reg", NULL); + + if (reg == NULL) + return 0; + /* That works for all keylargos but shall be fixed properly + * some day... + */ + return 0x50 + (*reg); +} + static struct cpufreq_driver pmac_cpufreq_driver = { .verify = pmac_cpufreq_verify, .target = pmac_cpufreq_target, @@ -272,15 +337,17 @@ static struct cpufreq_driver pmac_cpufreq_driver = { .owner = THIS_MODULE, }; + /* Currently, we support the following machines: * + * - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz) * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) * - iBook2 500 (PMU based, 400Mhz & 500Mhz) * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) + * - Recent MacRISC3 machines */ -static int __init -pmac_cpufreq_setup(void) +static int __init pmac_cpufreq_setup(void) { struct device_node *cpunode; u32 *value; @@ -304,6 +371,74 @@ pmac_cpufreq_setup(void) if (machine_is_compatible("PowerBook3,4") || machine_is_compatible("PowerBook3,5") || machine_is_compatible("MacRISC3")) { + struct device_node *volt_gpio_np = of_find_node_by_name(NULL, "voltage-gpio"); + struct device_node *freq_gpio_np = of_find_node_by_name(NULL, "frequency-gpio"); + struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, "slewing-done"); + + /* + * Check to see if it's GPIO driven or PMU only + * + * The way we extract the GPIO address is slightly hackish, but it + * works well enough for now. We need to abstract the whole GPIO + * stuff sooner or later anyway + */ + + if (volt_gpio_np) + voltage_gpio = read_gpio(volt_gpio_np); + if (freq_gpio_np) + frequency_gpio = read_gpio(freq_gpio_np); + if (slew_done_gpio_np) + slew_done_gpio = read_gpio(slew_done_gpio_np); + + /* If we use the frequency GPIOs, calculate the min/max speeds based + * on the bus frequencies + */ + if (frequency_gpio && slew_done_gpio) { + int lenp, rc; + u32 *freqs, *ratio; + + freqs = (u32 *)get_property(cpunode, "bus-frequencies", &lenp); + lenp /= sizeof(u32); + if (freqs == NULL || lenp != 2) { + printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n"); + goto out; + } + ratio = (u32 *)get_property(cpunode, "processor-to-bus-ratio*2", NULL); + if (ratio == NULL) { + printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n"); + goto out; + } + + /* Get the min/max bus frequencies */ + low_freq = min(freqs[0], freqs[1]); + hi_freq = max(freqs[0], freqs[1]); + + /* Grrrr.. It _seems_ that the device-tree is lying on the low bus + * frequency, it claims it to be around 84Mhz on some models while + * it appears to be approx. 101Mhz on all. Let's hack around here... + * fortunately, we don't need to be too precise + */ + if (low_freq < 98000000) + low_freq = 101000000; + + /* Convert those to CPU core clocks */ + low_freq = (low_freq * (*ratio)) / 2000; + hi_freq = (hi_freq * (*ratio)) / 2000; + + /* Now we get the frequencies, we read the GPIO to see what is out current + * speed + */ + rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); + cur_freq = (rc & 0x01) ? hi_freq : low_freq; + + has_freq_ctl = 1; + cpufreq_uses_gpios = 1; + goto out; + } + + /* If we use the PMU, look for the min & max frequencies in the + * device-tree + */ value = (u32 *)get_property(cpunode, "min-clock-frequency", NULL); if (!value) goto out; @@ -359,6 +494,11 @@ out: pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; + printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); + printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz, switch method: %s\n", + low_freq/1000, hi_freq/1000, cur_freq/1000, + cpufreq_uses_pmu ? "PMU" : (cpufreq_uses_gpios ? "GPIOs" : "CPU")); + return cpufreq_register_driver(&pmac_cpufreq_driver); } diff --git a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c index e64779c8cc48..cde0f17a0b2e 100644 --- a/arch/ppc/platforms/pmac_feature.c +++ b/arch/ppc/platforms/pmac_feature.c @@ -41,6 +41,7 @@ #include <asm/pmac_feature.h> #include <asm/dbdma.h> #include <asm/pci-bridge.h> +#include <asm/pmac_low_i2c.h> #undef DEBUG_FEATURE @@ -50,9 +51,13 @@ #define DBG(fmt,...) #endif -/* Exported from arch/ppc/kernel/idle.c */ +#ifdef CONFIG_6xx extern int powersave_lowspeed; +#endif + extern int powersave_nap; +extern struct pci_dev *k2_skiplist[2]; + /* * We use a single global lock to protect accesses. Each driver has @@ -95,7 +100,8 @@ static const char* macio_names[] __pmacdata = "Paddington", "Keylargo", "Pangea", - "Intrepid" + "Intrepid", + "K2" }; @@ -113,14 +119,15 @@ static const char* macio_names[] __pmacdata = static struct device_node* uninorth_node __pmacdata; static u32* uninorth_base __pmacdata; static u32 uninorth_rev __pmacdata; - +static int uninorth_u3 __pmacdata; +static void *u3_ht; /* * For each motherboard family, we have a table of functions pointers * that handle the various features. */ -typedef int (*feature_call)(struct device_node* node, int param, int value); +typedef long (*feature_call)(struct device_node* node, long param, long value); struct feature_table_entry { unsigned int selector; @@ -161,8 +168,10 @@ simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int return 0; } -static int __pmac -ohare_htw_scc_enable(struct device_node* node, int param, int value) +#ifndef CONFIG_POWER4 + +static long __pmac +ohare_htw_scc_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long chan_mask; @@ -254,22 +263,22 @@ ohare_htw_scc_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -ohare_floppy_enable(struct device_node* node, int param, int value) +static long __pmac +ohare_floppy_enable(struct device_node* node, long param, long value) { return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_FLOPPY_ENABLE, value); } -static int __pmac -ohare_mesh_enable(struct device_node* node, int param, int value) +static long __pmac +ohare_mesh_enable(struct device_node* node, long param, long value) { return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_MESH_ENABLE, value); } -static int __pmac -ohare_ide_enable(struct device_node* node, int param, int value) +static long __pmac +ohare_ide_enable(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -289,8 +298,8 @@ ohare_ide_enable(struct device_node* node, int param, int value) } } -static int __pmac -ohare_ide_reset(struct device_node* node, int param, int value) +static long __pmac +ohare_ide_reset(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -304,8 +313,8 @@ ohare_ide_reset(struct device_node* node, int param, int value) } } -static int __pmac -ohare_sleep_state(struct device_node* node, int param, int value) +static long __pmac +ohare_sleep_state(struct device_node* node, long param, long value) { struct macio_chip* macio = &macio_chips[0]; @@ -320,8 +329,8 @@ ohare_sleep_state(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_modem_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_modem_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; u8 gpio; @@ -364,8 +373,8 @@ heathrow_modem_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_floppy_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_floppy_enable(struct device_node* node, long param, long value) { return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, @@ -373,8 +382,8 @@ heathrow_floppy_enable(struct device_node* node, int param, int value) value); } -static int __pmac -heathrow_mesh_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_mesh_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -390,22 +399,11 @@ heathrow_mesh_enable(struct device_node* node, int param, int value) MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE); (void)MACIO_IN32(HEATHROW_FCR); udelay(10); - /* Set/Clear termination power (todo: test ! the bit value - * used by Darwin doesn't seem to match what we used so - * far. If you experience problems, turn #if 1 into #if 0 - * and tell me about it --BenH. - */ -#if 1 + /* Set/Clear termination power */ if (value) - MACIO_BIC(HEATHROW_MBCR, 0x00000004); + MACIO_BIC(HEATHROW_MBCR, 0x04000000); else - MACIO_BIS(HEATHROW_MBCR, 0x00000004); -#else - if (value) - MACIO_BIC(HEATHROW_MBCR, 0x00040000); - else - MACIO_BIS(HEATHROW_MBCR, 0x00040000); -#endif + MACIO_BIS(HEATHROW_MBCR, 0x04000000); (void)MACIO_IN32(HEATHROW_MBCR); udelay(10); UNLOCK(flags); @@ -413,8 +411,8 @@ heathrow_mesh_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_ide_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_ide_enable(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -428,8 +426,8 @@ heathrow_ide_enable(struct device_node* node, int param, int value) } } -static int __pmac -heathrow_ide_reset(struct device_node* node, int param, int value) +static long __pmac +heathrow_ide_reset(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -443,8 +441,8 @@ heathrow_ide_reset(struct device_node* node, int param, int value) } } -static int __pmac -heathrow_bmac_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_bmac_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -472,8 +470,8 @@ heathrow_bmac_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_sound_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_sound_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -608,8 +606,8 @@ heathrow_wakeup(struct macio_chip* macio, int secondary) } } -static int __pmac -heathrow_sleep_state(struct device_node* node, int param, int value) +static long __pmac +heathrow_sleep_state(struct device_node* node, long param, long value) { if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) return -EPERM; @@ -625,8 +623,8 @@ heathrow_sleep_state(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_scc_enable(struct device_node* node, int param, int value) +static long __pmac +core99_scc_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -726,8 +724,8 @@ core99_scc_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_modem_enable(struct device_node* node, int param, int value) +static long __pmac +core99_modem_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; u8 gpio; @@ -778,8 +776,8 @@ core99_modem_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -pangea_modem_enable(struct device_node* node, int param, int value) +static long __pmac +pangea_modem_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; u8 gpio; @@ -833,8 +831,8 @@ pangea_modem_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_ata100_enable(struct device_node* node, int value) +static long __pmac +core99_ata100_enable(struct device_node* node, long value) { unsigned long flags; struct pci_dev *pdev = NULL; @@ -863,8 +861,8 @@ core99_ata100_enable(struct device_node* node, int value) return 0; } -static int __pmac -core99_ide_enable(struct device_node* node, int param, int value) +static long __pmac +core99_ide_enable(struct device_node* node, long param, long value) { /* Bus ID 0 to 2 are KeyLargo based IDE, busID 3 is U2 * based ata-100 @@ -886,8 +884,8 @@ core99_ide_enable(struct device_node* node, int param, int value) } } -static int __pmac -core99_ide_reset(struct device_node* node, int param, int value) +static long __pmac +core99_ide_reset(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -904,8 +902,8 @@ core99_ide_reset(struct device_node* node, int param, int value) } } -static int __pmac -core99_gmac_enable(struct device_node* node, int param, int value) +static long __pmac +core99_gmac_enable(struct device_node* node, long param, long value) { unsigned long flags; @@ -921,8 +919,8 @@ core99_gmac_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_gmac_phy_reset(struct device_node* node, int param, int value) +static long __pmac +core99_gmac_phy_reset(struct device_node* node, long param, long value) { unsigned long flags; struct macio_chip* macio; @@ -938,16 +936,16 @@ core99_gmac_phy_reset(struct device_node* node, int param, int value) UNLOCK(flags); mdelay(10); LOCK(flags); - MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE - | KEYLARGO_GPIO_OUTOUT_DATA); + MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, /*KEYLARGO_GPIO_OUTPUT_ENABLE | */ + KEYLARGO_GPIO_OUTOUT_DATA); UNLOCK(flags); mdelay(10); return 0; } -static int __pmac -core99_sound_chip_enable(struct device_node* node, int param, int value) +static long __pmac +core99_sound_chip_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -976,8 +974,8 @@ core99_sound_chip_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_airport_enable(struct device_node* node, int param, int value) +static long __pmac +core99_airport_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -1063,8 +1061,8 @@ core99_airport_enable(struct device_node* node, int param, int value) } #ifdef CONFIG_SMP -static int __pmac -core99_reset_cpu(struct device_node* node, int param, int value) +static long __pmac +core99_reset_cpu(struct device_node* node, long param, long value) { unsigned int reset_io = 0; unsigned long flags; @@ -1099,7 +1097,7 @@ core99_reset_cpu(struct device_node* node, int param, int value) MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); (void)MACIO_IN8(reset_io); udelay(1); - MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTOUT_DATA | KEYLARGO_GPIO_OUTPUT_ENABLE); + MACIO_OUT8(reset_io, 0); (void)MACIO_IN8(reset_io); UNLOCK(flags); @@ -1107,8 +1105,8 @@ core99_reset_cpu(struct device_node* node, int param, int value) } #endif /* CONFIG_SMP */ -static int __pmac -core99_usb_enable(struct device_node* node, int param, int value) +static long __pmac +core99_usb_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -1121,9 +1119,6 @@ core99_usb_enable(struct device_node* node, int param, int value) macio->type != macio_intrepid) return -ENODEV; - /* XXX Fix handling of 3rd USB controller in Intrepid, move the - * port connect stuff (KL4_*) to the sleep code eventually - */ prop = (char *)get_property(node, "AAPL,clock-id", NULL); if (!prop) return -ENODEV; @@ -1131,6 +1126,8 @@ core99_usb_enable(struct device_node* node, int param, int value) number = 0; else if (strncmp(prop, "usb1u148", 8) == 0) number = 2; + else if (strncmp(prop, "usb2u248", 8) == 0) + number = 4; else return -ENODEV; @@ -1147,44 +1144,79 @@ core99_usb_enable(struct device_node* node, int param, int value) mdelay(1); LOCK(flags); MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); - } else { + } else if (number == 2) { MACIO_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); UNLOCK(flags); (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); LOCK(flags); MACIO_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); + } else if (number == 4) { + MACIO_BIC(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1)); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR1); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR0, KL1_USB2_CELL_ENABLE); + } + if (number < 4) { + reg = MACIO_IN32(KEYLARGO_FCR4); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number)); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1)); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(10); + } else { + reg = MACIO_IN32(KEYLARGO_FCR3); + reg &= ~(KL3_IT_PORT_WAKEUP_ENABLE(0) | KL3_IT_PORT_RESUME_WAKE_EN(0) | + KL3_IT_PORT_CONNECT_WAKE_EN(0) | KL3_IT_PORT_DISCONNECT_WAKE_EN(0)); + reg &= ~(KL3_IT_PORT_WAKEUP_ENABLE(1) | KL3_IT_PORT_RESUME_WAKE_EN(1) | + KL3_IT_PORT_CONNECT_WAKE_EN(1) | KL3_IT_PORT_DISCONNECT_WAKE_EN(1)); + MACIO_OUT32(KEYLARGO_FCR3, reg); + (void)MACIO_IN32(KEYLARGO_FCR3); + udelay(10); } - reg = MACIO_IN32(KEYLARGO_FCR4); - reg &= ~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | - KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number)); - reg &= ~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | - KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1)); - MACIO_OUT32(KEYLARGO_FCR4, reg); - (void)MACIO_IN32(KEYLARGO_FCR4); - udelay(10); } else { /* Turn OFF */ - reg = MACIO_IN32(KEYLARGO_FCR4); - reg |= KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | - KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number); - reg |= KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | - KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1); - MACIO_OUT32(KEYLARGO_FCR4, reg); - (void)MACIO_IN32(KEYLARGO_FCR4); - udelay(1); + if (number < 4) { + reg = MACIO_IN32(KEYLARGO_FCR4); + reg |= KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number); + reg |= KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(1); + } else { + reg = MACIO_IN32(KEYLARGO_FCR3); + reg |= KL3_IT_PORT_WAKEUP_ENABLE(0) | KL3_IT_PORT_RESUME_WAKE_EN(0) | + KL3_IT_PORT_CONNECT_WAKE_EN(0) | KL3_IT_PORT_DISCONNECT_WAKE_EN(0); + reg |= KL3_IT_PORT_WAKEUP_ENABLE(1) | KL3_IT_PORT_RESUME_WAKE_EN(1) | + KL3_IT_PORT_CONNECT_WAKE_EN(1) | KL3_IT_PORT_DISCONNECT_WAKE_EN(1); + MACIO_OUT32(KEYLARGO_FCR3, reg); + (void)MACIO_IN32(KEYLARGO_FCR3); + udelay(1); + } if (number == 0) { MACIO_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); (void)MACIO_IN32(KEYLARGO_FCR0); udelay(1); MACIO_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); (void)MACIO_IN32(KEYLARGO_FCR0); - } else { + } else if (number == 2) { MACIO_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); (void)MACIO_IN32(KEYLARGO_FCR0); udelay(1); MACIO_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); (void)MACIO_IN32(KEYLARGO_FCR0); + } else if (number == 4) { + MACIO_BIC(KEYLARGO_FCR1, KL1_USB2_CELL_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR1); + udelay(1); + MACIO_BIS(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR1); } udelay(1); } @@ -1193,8 +1225,8 @@ core99_usb_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_firewire_enable(struct device_node* node, int param, int value) +static long __pmac +core99_firewire_enable(struct device_node* node, long param, long value) { unsigned long flags; struct macio_chip* macio; @@ -1220,8 +1252,8 @@ core99_firewire_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_firewire_cable_power(struct device_node* node, int param, int value) +static long __pmac +core99_firewire_cable_power(struct device_node* node, long param, long value) { unsigned long flags; struct macio_chip* macio; @@ -1251,8 +1283,10 @@ core99_firewire_cable_power(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_read_gpio(struct device_node* node, int param, int value) +#endif /* CONFIG_POWER4 */ + +static long __pmac +core99_read_gpio(struct device_node* node, long param, long value) { struct macio_chip* macio = &macio_chips[0]; @@ -1260,8 +1294,8 @@ core99_read_gpio(struct device_node* node, int param, int value) } -static int __pmac -core99_write_gpio(struct device_node* node, int param, int value) +static long __pmac +core99_write_gpio(struct device_node* node, long param, long value) { struct macio_chip* macio = &macio_chips[0]; @@ -1269,6 +1303,145 @@ core99_write_gpio(struct device_node* node, int param, int value) return 0; } +#ifdef CONFIG_POWER4 + +static long __pmac +g5_gmac_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + struct pci_dev *pdev; + u8 pbus, pid; + + /* XXX FIXME: We should fix pci_device_from_OF_node here, and + * get to a real pci_dev or we'll get into trouble with PCI + * domains the day we get overlapping numbers (like if we ever + * decide to show the HT root + */ + if (pci_device_from_OF_node(node, &pbus, &pid) == 0) + pdev = pci_find_slot(pbus, pid); + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + mb(); + k2_skiplist[0] = NULL; + } else { + k2_skiplist[0] = pdev; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +g5_fw_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + struct pci_dev *pdev; + u8 pbus, pid; + + /* XXX FIXME: We should fix pci_device_from_OF_node here, and + * get to a real pci_dev or we'll get into trouble with PCI + * domains the day we get overlapping numbers (like if we ever + * decide to show the HT root + */ + if (pci_device_from_OF_node(node, &pbus, &pid) == 0) + pdev = pci_find_slot(pbus, pid); + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + mb(); + k2_skiplist[1] = NULL; + } else { + k2_skiplist[0] = pdev; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +g5_mpic_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + + if (node->parent == NULL || strcmp(node->parent->name, "u3")) + return 0; + + LOCK(flags); + UN_BIS(U3_TOGGLE_REG, U3_MPIC_RESET | U3_MPIC_OUTPUT_ENABLE); + UNLOCK(flags); + + return 0; +} + +#ifdef CONFIG_SMP +static long __pmac +g5_reset_cpu(struct device_node* node, long param, long value) +{ + unsigned int reset_io = 0; + unsigned long flags; + struct macio_chip* macio; + struct device_node* np; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo2) + return -ENODEV; + + np = find_path_device("/cpus"); + if (np == NULL) + return -ENODEV; + for (np = np->child; np != NULL; np = np->sibling) { + u32* num = (u32 *)get_property(np, "reg", NULL); + u32* rst = (u32 *)get_property(np, "soft-reset", NULL); + if (num == NULL || rst == NULL) + continue; + if (param == *num) { + reset_io = *rst; + break; + } + } + if (np == NULL || reset_io == 0) + return -ENODEV; + + LOCK(flags); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(reset_io); + udelay(1); + MACIO_OUT8(reset_io, 0); + (void)MACIO_IN8(reset_io); + UNLOCK(flags); + + return 0; +} +#endif /* CONFIG_SMP */ + +/* + * This can be called from pmac_smp so isn't static + * + * This takes the second CPU off the bus on dual CPU machines + * running UP + */ +void __pmac g5_phy_disable_cpu1(void) +{ + UN_OUT(U3_API_PHY_CONFIG_1, 0); +} + +#endif /* CONFIG_POWER4 */ + +#ifndef CONFIG_POWER4 + static void __pmac keylargo_shutdown(struct macio_chip* macio, int sleep_mode) { @@ -1541,8 +1714,8 @@ core99_wake_up(void) return 0; } -static int __pmac -core99_sleep_state(struct device_node* node, int param, int value) +static long __pmac +core99_sleep_state(struct device_node* node, long param, long value) { /* Param == 1 means to enter the "fake sleep" mode that is * used for CPU speed switch @@ -1568,8 +1741,10 @@ core99_sleep_state(struct device_node* node, int param, int value) return 0; } -static int __pmac -generic_get_mb_info(struct device_node* node, int param, int value) +#endif /* CONFIG_POWER4 */ + +static long __pmac +generic_get_mb_info(struct device_node* node, long param, long value) { switch(param) { case PMAC_MB_INFO_MODEL: @@ -1579,9 +1754,9 @@ generic_get_mb_info(struct device_node* node, int param, int value) case PMAC_MB_INFO_NAME: /* hack hack hack... but should work */ *((const char **)value) = pmac_mb.model_name; - break; + return 0; } - return 0; + return -EINVAL; } @@ -1596,6 +1771,8 @@ static struct feature_table_entry any_features[] __pmacdata = { { 0, NULL } }; +#ifndef CONFIG_POWER4 + /* OHare based motherboards. Currently, we only use these on the * 2400,3400 and 3500 series powerbooks. Some older desktops seem * to have issues with turning on/off those asic cells @@ -1741,10 +1918,29 @@ static struct feature_table_entry intrepid_features[] __pmacdata = { { 0, NULL } }; +#else /* CONFIG_POWER4 */ + +/* G5 features + */ +static struct feature_table_entry g5_features[] __pmacdata = { + { PMAC_FTR_GMAC_ENABLE, g5_gmac_enable }, + { PMAC_FTR_1394_ENABLE, g5_fw_enable }, + { PMAC_FTR_ENABLE_MPIC, g5_mpic_enable }, +#ifdef CONFIG_SMP + { PMAC_FTR_RESET_CPU, g5_reset_cpu }, +#endif /* CONFIG_SMP */ + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { 0, NULL } +}; + +#endif /* CONFIG_POWER4 */ + static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { /* Warning: ordering is important as some models may claim * beeing compatible with several types */ +#ifndef CONFIG_POWER4 { "AAPL,8500", "PowerMac 8500/8600", PMAC_TYPE_PSURGE, NULL, 0 @@ -1753,6 +1949,14 @@ static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { PMAC_TYPE_PSURGE, NULL, 0 }, + { "AAPL,7200", "PowerMac 7200", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,7300", "PowerMac 7200/7300", + PMAC_TYPE_PSURGE, NULL, + 0 + }, { "AAPL,7500", "PowerMac 7500", PMAC_TYPE_PSURGE, NULL, 0 @@ -1905,20 +2109,43 @@ static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, }, + { "PowerBook5,2", "PowerBook G4 15\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,3", "PowerBook G4 17\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, { "PowerBook6,1", "PowerBook G4 12\"", PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, }, + { "PowerBook6,2", "PowerBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,3", "iBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, +#else /* CONFIG_POWER4 */ + { "PowerMac7,2", "PowerMac G5", + PMAC_TYPE_POWERMAC_G5, g5_features, + 0, + }, +#endif /* CONFIG_POWER4 */ }; /* * The toplevel feature_call callback */ -int __pmac +long __pmac pmac_do_feature_call(unsigned int selector, ...) { struct device_node* node; - int param, value, i; + long param, value; + int i; feature_call func = NULL; va_list args; @@ -1939,8 +2166,8 @@ pmac_do_feature_call(unsigned int selector, ...) va_start(args, selector); node = (struct device_node*)va_arg(args, void*); - param = va_arg(args, int); - value = va_arg(args, int); + param = va_arg(args, long); + value = va_arg(args, long); va_end(args); return func(node, param, value); @@ -1976,6 +2203,7 @@ probe_motherboard(void) /* Fallback to selection depending on mac-io chip type */ switch(macio->type) { +#ifndef CONFIG_POWER4 case macio_grand_central: pmac_mb.model_id = PMAC_TYPE_PSURGE; pmac_mb.model_name = "Unknown PowerSurge"; @@ -2009,10 +2237,18 @@ probe_motherboard(void) pmac_mb.model_name = "Unknown Intrepid-based"; pmac_mb.features = intrepid_features; break; +#else /* CONFIG_POWER4 */ + case macio_keylargo2: + pmac_mb.model_id = PMAC_TYPE_POWERMAC_G5; + pmac_mb.model_name = "Unknown G5"; + pmac_mb.features = g5_features; + break; +#endif /* CONFIG_POWER4 */ default: return -ENODEV; } found: +#ifndef CONFIG_POWER4 /* Fixup Hooper vs. Comet */ if (pmac_mb.model_id == PMAC_TYPE_HOOPER) { u32* mach_id_ptr = (u32*)ioremap(0xf3000034, 4); @@ -2026,6 +2262,7 @@ found: pmac_mb.model_id = PMAC_TYPE_COMET; iounmap(mach_id_ptr); } +#endif /* CONFIG_POWER4 */ #ifdef CONFIG_6xx /* Set default value of powersave_nap on machines that support it. @@ -2057,7 +2294,9 @@ found: */ powersave_lowspeed = 1; #endif /* CONFIG_6xx */ - +#ifdef CONFIG_POWER4 + powersave_nap = 1; +#endif /* Check for "mobile" machine */ if (model && (strncmp(model, "PowerBook", 9) == 0 || strncmp(model, "iBook", 5) == 0)) @@ -2076,18 +2315,26 @@ probe_uninorth(void) unsigned long actrl; /* Locate core99 Uni-N */ - uninorth_node = find_devices("uni-n"); + uninorth_node = of_find_node_by_name(NULL, "uni-n"); + /* Locate G5 u3 */ + if (uninorth_node == NULL) { + uninorth_node = of_find_node_by_name(NULL, "u3"); + uninorth_u3 = 1; + } if (uninorth_node && uninorth_node->n_addrs > 0) { - uninorth_base = ioremap(uninorth_node->addrs[0].address, 0x4000); + unsigned long address = uninorth_node->addrs[0].address; + uninorth_base = ioremap(address, 0x40000); uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); + if (uninorth_u3) + u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); } else uninorth_node = NULL; if (!uninorth_node) return; - printk(KERN_INFO "Found Uninorth memory controller & host bridge, revision: %d\n", - uninorth_rev); + printk(KERN_INFO "Found %s memory controller & host bridge, revision: %d\n", + uninorth_u3 ? "U3" : "UniNorth", uninorth_rev); printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); /* Set the arbitrer QAck delay according to what Apple does @@ -2172,6 +2419,7 @@ probe_macios(void) probe_one_macio("mac-io", "paddington", macio_paddington); probe_one_macio("mac-io", "gatwick", macio_gatwick); probe_one_macio("mac-io", "heathrow", macio_heathrow); + probe_one_macio("mac-io", "K2-Keylargo", macio_keylargo2); /* Make sure the "main" macio chip appear first */ if (macio_chips[0].type == macio_gatwick @@ -2244,19 +2492,60 @@ set_initial_features(void) MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); } +#ifdef CONFIG_POWER4 + if (macio_chips[0].type == macio_keylargo2) { +#ifndef CONFIG_SMP + /* On SMP machines running UP, we have the second CPU eating + * bus cycles. We need to take it off the bus. This is done + * from pmac_smp for SMP kernels running on one CPU + */ + np = of_find_node_by_type(NULL, "cpu"); + if (np != NULL) + np = of_find_node_by_type(np, "cpu"); + if (np != NULL) { + g5_phy_disable_cpu1(); + of_node_put(np); + } +#endif /* CONFIG_SMP */ + /* Enable GMAC for now for PCI probing. It will be disabled + * later on after PCI probe + */ + np = of_find_node_by_name(NULL, "ethernet"); + while(np) { + if (device_is_compatible(np, "K2-GMAC")) + g5_gmac_enable(np, 0, 1); + np = of_find_node_by_name(np, "ethernet"); + } + + /* Enable FW before PCI probe. Will be disabled later on + * Note: We should have a batter way to check that we are + * dealing with uninorth internal cell and not a PCI cell + * on the external PCI. The code below works though. + */ + np = of_find_node_by_name(NULL, "firewire"); + while(np) { + if (device_is_compatible(np, "pci106b,5811")) { + macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; + g5_fw_enable(np, 0, 1); + } + np = of_find_node_by_name(np, "firewire"); + } + } +#else /* CONFIG_POWER4 */ + if (macio_chips[0].type == macio_keylargo || macio_chips[0].type == macio_pangea || macio_chips[0].type == macio_intrepid) { /* Enable GMAC for now for PCI probing. It will be disabled * later on after PCI probe */ - np = find_devices("ethernet"); + np = of_find_node_by_name(NULL, "ethernet"); while(np) { if (np->parent && device_is_compatible(np->parent, "uni-north") && device_is_compatible(np, "gmac")) core99_gmac_enable(np, 0, 1); - np = np->next; + np = of_find_node_by_name(np, "ethernet"); } /* Enable FW before PCI probe. Will be disabled later on @@ -2264,7 +2553,7 @@ set_initial_features(void) * dealing with uninorth internal cell and not a PCI cell * on the external PCI. The code below works though. */ - np = find_devices("firewire"); + np = of_find_node_by_name(NULL, "firewire"); while(np) { if (np->parent && device_is_compatible(np->parent, "uni-north") @@ -2274,18 +2563,18 @@ set_initial_features(void) macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; core99_firewire_enable(np, 0, 1); } - np = np->next; + np = of_find_node_by_name(np, "firewire"); } /* Enable ATA-100 before PCI probe. */ - np = find_devices("ata-6"); + np = of_find_node_by_name(NULL, "ata-6"); while(np) { if (np->parent && device_is_compatible(np->parent, "uni-north") && device_is_compatible(np, "kauai-ata")) { core99_ata100_enable(np, 1); } - np = np->next; + np = of_find_node_by_name(np, "ata-6"); } /* Switch airport off */ @@ -2313,6 +2602,99 @@ set_initial_features(void) MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); } + /* Hack for bumping clock speed on the new PowerBooks and the + * iBook G4. This implements the "platform-do-clockspreading" OF + * property. For safety, we also check the product ID in the + * device-tree to make reasonably sure we won't set wrong values + * in the clock chip. + * + * Of course, ultimately, we have to implement a real parser for + * the platform-do-* stuff... + */ + while (machine_is_compatible("PowerBook5,2") || + machine_is_compatible("PowerBook5,3") || + machine_is_compatible("PowerBook6,2") || + machine_is_compatible("PowerBook6,3")) { + struct device_node *ui2c = of_find_node_by_type(NULL, "i2c"); + struct device_node *dt = of_find_node_by_name(NULL, "device-tree"); + u8 buffer[9]; + u32 *productID; + int i, rc, changed = 0; + + if (dt == NULL) + break; + productID = (u32 *)get_property(dt, "pid#", NULL); + if (productID == NULL) + break; + while(ui2c) { + struct device_node *p = of_get_parent(ui2c); + if (p && !strcmp(p->name, "uni-n")) + break; + ui2c = of_find_node_by_type(np, "i2c"); + } + if (ui2c == NULL) + break; + DBG("Trying to bump clock speed for PID: %08x...\n", *productID); + rc = pmac_low_i2c_open(ui2c, 1); + if (rc != 0) + break; + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); + DBG("read result: %d,", rc); + if (rc != 0) { + pmac_low_i2c_close(ui2c); + break; + } + for (i=0; i<9; i++) + DBG(" %02x", buffer[i]); + DBG("\n"); + + switch(*productID) { + case 0x1182: /* AlBook 12" rev 2 */ + case 0x1183: /* iBook G4 12" */ + buffer[0] = (buffer[0] & 0x8f) | 0x70; + buffer[2] = (buffer[2] & 0x7f) | 0x00; + buffer[5] = (buffer[5] & 0x80) | 0x31; + buffer[6] = (buffer[6] & 0x40) | 0xb0; + buffer[7] = (buffer[7] & 0x00) | 0xc0; + buffer[8] = (buffer[8] & 0x00) | 0x30; + changed = 1; + break; + case 0x3142: /* AlBook 15" (ATI M10) */ + case 0x3143: /* AlBook 17" (ATI M10) */ + buffer[0] = (buffer[0] & 0xaf) | 0x50; + buffer[2] = (buffer[2] & 0x7f) | 0x00; + buffer[5] = (buffer[5] & 0x80) | 0x31; + buffer[6] = (buffer[6] & 0x40) | 0xb0; + buffer[7] = (buffer[7] & 0x00) | 0xd0; + buffer[8] = (buffer[8] & 0x00) | 0x30; + changed = 1; + break; + default: + DBG("i2c-hwclock: Machine model not handled\n"); + break; + } + if (!changed) { + pmac_low_i2c_close(ui2c); + break; + } + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_stdsub); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_write, 0x80, buffer, 9); + DBG("write result: %d,", rc); + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); + DBG("read result: %d,", rc); + if (rc != 0) { + pmac_low_i2c_close(ui2c); + break; + } + for (i=0; i<9; i++) + DBG(" %02x", buffer[i]); + pmac_low_i2c_close(ui2c); + break; + } + +#endif /* CONFIG_POWER4 */ /* On all machines, switch modem & serial ports off */ np = find_devices("ch-a"); @@ -2339,6 +2721,9 @@ pmac_feature_init(void) return; } + /* Setup low-level i2c stuffs */ + pmac_init_low_i2c(); + /* Probe machine type */ if (probe_motherboard()) printk(KERN_WARNING "Unknown PowerMac !\n"); @@ -2367,3 +2752,55 @@ pmac_feature_late_init(void) } device_initcall(pmac_feature_late_init); + +#ifdef CONFIG_POWER4 + +static void dump_HT_speeds(char *name, u32 cfg, u32 frq) +{ + int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; + int bits[8] = { 8,16,0,32,2,4,0,0 }; + int freq = (frq >> 8) & 0xf; + + if (freqs[freq] == 0) + printk("%s: Unknown HT link frequency %x\n", name, freq); + else + printk("%s: %d MHz on main link, (%d in / %d out) bits width\n", + name, freqs[freq], + bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); +} + +void __init pmac_check_ht_link(void) +{ + u32 ufreq, freq, ucfg, cfg; + struct device_node *pcix_node; + u8 px_bus, px_devfn; + struct pci_controller *px_hose; + + (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); + ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); + ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); + dump_HT_speeds("U3 HyperTransport", cfg, freq); + + pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); + if (pcix_node == NULL) { + printk("No PCI-X bridge found\n"); + return; + } + if (pci_device_from_OF_node(pcix_node, &px_bus, &px_devfn) != 0) { + printk("PCI-X bridge found but not matched to pci\n"); + return; + } + px_hose = pci_find_hose_for_OF_device(pcix_node); + if (px_hose == NULL) { + printk("PCI-X bridge found but not matched to host\n"); + return; + } + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc4, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xcc, &freq); + dump_HT_speeds("PCI-X HT Uplink", cfg, freq); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc8, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xd0, &freq); + dump_HT_speeds("PCI-X HT Downlink", cfg, freq); +} + +#endif /* CONFIG_POWER4 */ diff --git a/arch/ppc/platforms/pmac_low_i2c.c b/arch/ppc/platforms/pmac_low_i2c.c new file mode 100644 index 000000000000..d07579f2b8b9 --- /dev/null +++ b/arch/ppc/platforms/pmac_low_i2c.c @@ -0,0 +1,513 @@ +/* + * arch/ppc/platforms/pmac_low_i2c.c + * + * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This file contains some low-level i2c access routines that + * need to be used by various bits of the PowerMac platform code + * at times where the real asynchronous & interrupt driven driver + * cannot be used. The API borrows some semantics from the darwin + * driver in order to ease the implementation of the platform + * properties parser + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <asm/keylargo.h> +#include <asm/uninorth.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/pmac_low_i2c.h> + +#define MAX_LOW_I2C_HOST 4 + +#if 1 +#define DBG(x...) do {\ + printk(KERN_DEBUG "KW:" x); \ + } while(0) +#else +#define DBGG(x...) +#endif + +struct low_i2c_host; + +typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len); + +struct low_i2c_host +{ + struct device_node *np; /* OF device node */ + struct semaphore mutex; /* Access mutex for use by i2c-keywest */ + low_i2c_func_t func; /* Access function */ + int is_open : 1; /* Poor man's access control */ + int mode; /* Current mode */ + int channel; /* Current channel */ + int num_channels; /* Number of channels */ + unsigned long base; /* For keywest-i2c, base address */ + int bsteps; /* And register stepping */ + int speed; /* And speed */ +}; + +static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST]; + +/* No locking is necessary on allocation, we are running way before + * anything can race with us + */ +static struct low_i2c_host *find_low_i2c_host(struct device_node *np) +{ + int i; + + for (i = 0; i < MAX_LOW_I2C_HOST; i++) + if (low_i2c_hosts[i].np == np) + return &low_i2c_hosts[i]; + return NULL; +} + +/* + * + * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's) + * + */ + +/* + * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h, + * should be moved somewhere in include/asm-ppc/ + */ +/* Register indices */ +typedef enum { + reg_mode = 0, + reg_control, + reg_status, + reg_isr, + reg_ier, + reg_addr, + reg_subaddr, + reg_data +} reg_t; + + +/* Mode register */ +#define KW_I2C_MODE_100KHZ 0x00 +#define KW_I2C_MODE_50KHZ 0x01 +#define KW_I2C_MODE_25KHZ 0x02 +#define KW_I2C_MODE_DUMB 0x00 +#define KW_I2C_MODE_STANDARD 0x04 +#define KW_I2C_MODE_STANDARDSUB 0x08 +#define KW_I2C_MODE_COMBINED 0x0C +#define KW_I2C_MODE_MODE_MASK 0x0C +#define KW_I2C_MODE_CHAN_MASK 0xF0 + +/* Control register */ +#define KW_I2C_CTL_AAK 0x01 +#define KW_I2C_CTL_XADDR 0x02 +#define KW_I2C_CTL_STOP 0x04 +#define KW_I2C_CTL_START 0x08 + +/* Status register */ +#define KW_I2C_STAT_BUSY 0x01 +#define KW_I2C_STAT_LAST_AAK 0x02 +#define KW_I2C_STAT_LAST_RW 0x04 +#define KW_I2C_STAT_SDA 0x08 +#define KW_I2C_STAT_SCL 0x10 + +/* IER & ISR registers */ +#define KW_I2C_IRQ_DATA 0x01 +#define KW_I2C_IRQ_ADDR 0x02 +#define KW_I2C_IRQ_STOP 0x04 +#define KW_I2C_IRQ_START 0x08 +#define KW_I2C_IRQ_MASK 0x0F + +/* State machine states */ +enum { + state_idle, + state_addr, + state_read, + state_write, + state_stop, + state_dead +}; + +#define WRONG_STATE(name) do {\ + printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ + name, __kw_state_names[state], isr); \ + } while(0) + +static const char *__kw_state_names[] = { + "state_idle", + "state_addr", + "state_read", + "state_write", + "state_stop", + "state_dead" +}; + +static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg) +{ + return in_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps)); +} + +static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) +{ + out_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps), val); + (void)__kw_read_reg(host, reg_subaddr); +} + +#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) +#define kw_read_reg(reg) __kw_read_reg(host, reg) + + +/* Don't schedule, the g5 fan controller is too + * timing sensitive + */ +static u8 kw_wait_interrupt(struct low_i2c_host* host) +{ + int i; + u8 isr; + + for (i = 0; i < 200000; i++) { + isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; + if (isr != 0) + return isr; + udelay(1); + } + return isr; +} + +static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) +{ + u8 ack; + + if (isr == 0) { + if (state != state_stop) { + DBG("KW: Timeout !\n"); + *rc = -EIO; + goto stop; + } + if (state == state_stop) { + ack = kw_read_reg(reg_status); + if (!(ack & KW_I2C_STAT_BUSY)) { + state = state_idle; + kw_write_reg(reg_ier, 0x00); + } + } + return state; + } + + if (isr & KW_I2C_IRQ_ADDR) { + ack = kw_read_reg(reg_status); + if (state != state_addr) { + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + WRONG_STATE("KW_I2C_IRQ_ADDR"); + *rc = -EIO; + goto stop; + } + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + *rc = -ENODEV; + DBG("KW: NAK on address\n"); + return state_stop; + } else { + if (rw) { + state = state_read; + if (*len > 1) + kw_write_reg(reg_control, KW_I2C_CTL_AAK); + } else { + state = state_write; + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } + } + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + } + + if (isr & KW_I2C_IRQ_DATA) { + if (state == state_read) { + **data = kw_read_reg(reg_data); + (*data)++; (*len)--; + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + if ((*len) == 0) + state = state_stop; + else if ((*len) == 1) + kw_write_reg(reg_control, 0); + } else if (state == state_write) { + ack = kw_read_reg(reg_status); + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + DBG("KW: nack on data write\n"); + *rc = -EIO; + goto stop; + } else if (*len) { + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } else { + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + state = state_stop; + *rc = 0; + } + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + } else { + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + WRONG_STATE("KW_I2C_IRQ_DATA"); + if (state != state_stop) { + *rc = -EIO; + goto stop; + } + } + } + + if (isr & KW_I2C_IRQ_STOP) { + kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); + if (state != state_stop) { + WRONG_STATE("KW_I2C_IRQ_STOP"); + *rc = -EIO; + } + return state_idle; + } + + if (isr & KW_I2C_IRQ_START) + kw_write_reg(reg_isr, KW_I2C_IRQ_START); + + return state; + + stop: + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + return state_stop; +} + +static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) +{ + u8 mode_reg = host->speed; + int state = state_addr; + int rc = 0; + + /* Setup mode & subaddress if any */ + switch(host->mode) { + case pmac_low_i2c_mode_dumb: + printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); + return -EINVAL; + case pmac_low_i2c_mode_std: + mode_reg |= KW_I2C_MODE_STANDARD; + break; + case pmac_low_i2c_mode_stdsub: + mode_reg |= KW_I2C_MODE_STANDARDSUB; + kw_write_reg(reg_subaddr, subaddr); + break; + case pmac_low_i2c_mode_combined: + mode_reg |= KW_I2C_MODE_COMBINED; + kw_write_reg(reg_subaddr, subaddr); + break; + } + + /* Setup channel & clear pending irqs */ + kw_write_reg(reg_isr, kw_read_reg(reg_isr)); + kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); + kw_write_reg(reg_status, 0); + + /* Set up address and r/w bit */ + kw_write_reg(reg_addr, addr); + + /* Start sending address & disable interrupt*/ + kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); + kw_write_reg(reg_control, KW_I2C_CTL_XADDR); + + /* State machine, to turn into an interrupt handler */ + while(state != state_idle) { + u8 isr = kw_wait_interrupt(host); + state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); + } + + return rc; +} + +static void keywest_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + unsigned long *psteps, *prate, steps, aoffset = 0; + struct device_node *parent; + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); + steps = psteps ? (*psteps) : 0x10; + for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) + steps >>= 1; + parent = of_get_parent(np); + host->num_channels = 1; + if (parent && parent->name[0] == 'u') { + host->num_channels = 2; + aoffset = 3; + } + /* Select interface rate */ + host->speed = KW_I2C_MODE_100KHZ; + prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); + if (prate) switch(*prate) { + case 100: + host->speed = KW_I2C_MODE_100KHZ; + break; + case 50: + host->speed = KW_I2C_MODE_50KHZ; + break; + case 25: + host->speed = KW_I2C_MODE_25KHZ; + break; + } + host->mode = pmac_low_i2c_mode_std; + host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset, + np->addrs[0].size); + host->func = keywest_low_i2c_func; +} + +/* + * + * PMU implementation + * + */ + + +#ifdef CONFIG_ADB_PMU + +static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) +{ + // TODO + return -ENODEV; +} + +static void pmu_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + host->num_channels = 3; + host->mode = pmac_low_i2c_mode_std; + host->func = pmu_low_i2c_func; +} + +#endif /* CONFIG_ADB_PMU */ + +void __init pmac_init_low_i2c(void) +{ + struct device_node *np; + + /* Probe keywest-i2c busses */ + np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); + while(np) { + keywest_low_i2c_add(np); + np = of_find_compatible_node(np, "i2c", "keywest-i2c"); + } + +#ifdef CONFIG_ADB_PMU + /* Probe PMU busses */ + np = of_find_node_by_name(NULL, "via-pmu"); + if (np) + pmu_low_i2c_add(np); +#endif /* CONFIG_ADB_PMU */ + + /* TODO: Add CUDA support as well */ +} + +int pmac_low_i2c_lock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + down(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_lock); + +int pmac_low_i2c_unlock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + up(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_unlock); + + +int pmac_low_i2c_open(struct device_node *np, int channel) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + if (channel >= host->num_channels) + return -EINVAL; + + down(&host->mutex); + host->is_open = 1; + host->channel = channel; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_open); + +int pmac_low_i2c_close(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + host->is_open = 0; + up(&host->mutex); + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_close); + +int pmac_low_i2c_setmode(struct device_node *np, int mode) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + host->mode = mode; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_setmode); + +int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + + return host->func(host, addrdir, subaddr, data, len); +} +EXPORT_SYMBOL(pmac_low_i2c_xfer); + diff --git a/arch/ppc/platforms/pmac_nvram.c b/arch/ppc/platforms/pmac_nvram.c index d11d2445b54e..f381f3f745f9 100644 --- a/arch/ppc/platforms/pmac_nvram.c +++ b/arch/ppc/platforms/pmac_nvram.c @@ -8,8 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Todo: - cleanup some coding horrors in the flash code - * - add support for the OF persistent properties + * Todo: - add support for the OF persistent properties */ #include <linux/config.h> #include <linux/module.h> @@ -21,29 +20,40 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/errno.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <linux/bootmem.h> +#include <linux/completion.h> +#include <linux/spinlock.h> #include <asm/sections.h> #include <asm/io.h> #include <asm/system.h> #include <asm/prom.h> #include <asm/machdep.h> #include <asm/nvram.h> -#include <linux/adb.h> -#include <linux/pmu.h> -#undef DEBUG +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif #define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ #define CORE99_SIGNATURE 0x5a #define CORE99_ADLER_START 0x14 -/* Core99 nvram is a flash */ -#define CORE99_FLASH_STATUS_DONE 0x80 -#define CORE99_FLASH_STATUS_ERR 0x38 -#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 -#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 -#define CORE99_FLASH_CMD_RESET 0xff -#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 +/* On Core99, nvram is either a sharp, a micron or an AMD flash */ +#define SM_FLASH_STATUS_DONE 0x80 +#define SM_FLASH_STATUS_ERR 0x38 +#define SM_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define SM_FLASH_CMD_ERASE_SETUP 0x20 +#define SM_FLASH_CMD_RESET 0xff +#define SM_FLASH_CMD_WRITE_SETUP 0x40 +#define SM_FLASH_CMD_CLEAR_STATUS 0x50 +#define SM_FLASH_CMD_READ_STATUS 0x70 /* CHRP NVRAM header */ struct chrp_header { @@ -70,21 +80,110 @@ static volatile unsigned char *nvram_data; static int nvram_mult, is_core_99; static int core99_bank = 0; static int nvram_partitions[3]; - -/* FIXME: kmalloc fails to allocate the image now that I had to move it - * before time_init(). For now, I allocate a static buffer here - * but it's a waste of space on all but core99 machines - */ -#if 0 -static char* nvram_image; -#else -static char nvram_image[NVRAM_SIZE] __pmacdata; -#endif +static spinlock_t nv_lock = SPIN_LOCK_UNLOCKED; extern int pmac_newworld; +extern int system_running; + +static int (*core99_write_bank)(int bank, u8* datas); +static int (*core99_erase_bank)(int bank); + +static char *nvram_image __pmacdata; + + +static unsigned char __pmac core99_nvram_read_byte(int addr) +{ + if (nvram_image == NULL) + return 0xff; + return nvram_image[addr]; +} + +static void __pmac core99_nvram_write_byte(int addr, unsigned char val) +{ + if (nvram_image == NULL) + return; + nvram_image[addr] = val; +} + + +static unsigned char __openfirmware direct_nvram_read_byte(int addr) +{ + return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]); +} + +static void __openfirmware direct_nvram_write_byte(int addr, unsigned char val) +{ + out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val); +} + + +static unsigned char __pmac indirect_nvram_read_byte(int addr) +{ + unsigned char val; + unsigned long flags; + + spin_lock_irqsave(&nv_lock, flags); + out_8(nvram_addr, addr >> 5); + val = in_8(&nvram_data[(addr & 0x1f) << 4]); + spin_unlock_irqrestore(&nv_lock, flags); + + return val; +} + +static void __pmac indirect_nvram_write_byte(int addr, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&nv_lock, flags); + out_8(nvram_addr, addr >> 5); + out_8(&nvram_data[(addr & 0x1f) << 4], val); + spin_unlock_irqrestore(&nv_lock, flags); +} + + +#ifdef CONFIG_ADB_PMU + +static void __pmac pmu_nvram_complete(struct adb_request *req) +{ + if (req->arg) + complete((struct completion *)req->arg); +} -static u8 __pmac -chrp_checksum(struct chrp_header* hdr) +static unsigned char __pmac pmu_nvram_read_byte(int addr) +{ + struct adb_request req; + DECLARE_COMPLETION(req_complete); + + req.arg = system_running ? &req_complete : NULL; + if (pmu_request(&req, pmu_nvram_complete, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + return 0xff; + if (system_running) + wait_for_completion(&req_complete); + while (!req.complete) + pmu_poll(); + return req.reply[0]; +} + +static void __pmac pmu_nvram_write_byte(int addr, unsigned char val) +{ + struct adb_request req; + DECLARE_COMPLETION(req_complete); + + req.arg = system_running ? &req_complete : NULL; + if (pmu_request(&req, pmu_nvram_complete, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + return; + if (system_running) + wait_for_completion(&req_complete); + while (!req.complete) + pmu_poll(); +} + +#endif /* CONFIG_ADB_PMU */ + + +static u8 __pmac chrp_checksum(struct chrp_header* hdr) { u8 *ptr; u16 sum = hdr->signature; @@ -95,8 +194,7 @@ chrp_checksum(struct chrp_header* hdr) return sum; } -static u32 __pmac -core99_calc_adler(u8 *buffer) +static u32 __pmac core99_calc_adler(u8 *buffer) { int cnt; u32 low, high; @@ -118,86 +216,186 @@ core99_calc_adler(u8 *buffer) return (high << 16) | low; } -static u32 __pmac -core99_check(u8* datas) +static u32 __pmac core99_check(u8* datas) { struct core99_header* hdr99 = (struct core99_header*)datas; if (hdr99->hdr.signature != CORE99_SIGNATURE) { -#ifdef DEBUG - printk("Invalid signature\n"); -#endif + DBG("Invalid signature\n"); return 0; } if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { -#ifdef DEBUG - printk("Invalid checksum\n"); -#endif + DBG("Invalid checksum\n"); return 0; } if (hdr99->adler != core99_calc_adler(datas)) { -#ifdef DEBUG - printk("Invalid adler\n"); -#endif + DBG("Invalid adler\n"); return 0; } return hdr99->generation; } -static int __pmac -core99_erase_bank(int bank) +static int __pmac sm_erase_bank(int bank) { int stat, i; + unsigned long timeout; u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; - out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); - out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); - do { stat = in_8(base); } - while(!(stat & CORE99_FLASH_STATUS_DONE)); - out_8(base, CORE99_FLASH_CMD_RESET); - if (stat & CORE99_FLASH_STATUS_ERR) { - printk("nvram: flash error 0x%02x on erase !\n", stat); - return -ENXIO; - } + DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank); + + out_8(base, SM_FLASH_CMD_ERASE_SETUP); + out_8(base, SM_FLASH_CMD_ERASE_CONFIRM); + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: Sharp/Miron flash erase timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + for (i=0; i<NVRAM_SIZE; i++) if (base[i] != 0xff) { - printk("nvram: flash erase failed !\n"); + printk(KERN_ERR "nvram: Sharp/Micron flash erase failed !\n"); return -ENXIO; } return 0; } -static int __pmac -core99_write_bank(int bank, u8* datas) +static int __pmac sm_write_bank(int bank, u8* datas) { int i, stat = 0; + unsigned long timeout; u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + DBG("nvram: Sharp/Micron Writing bank %d...\n", bank); + for (i=0; i<NVRAM_SIZE; i++) { - out_8(base+i, CORE99_FLASH_CMD_WRITE_SETUP); + out_8(base+i, SM_FLASH_CMD_WRITE_SETUP); + udelay(1); out_8(base+i, datas[i]); - do { stat = in_8(base); } - while(!(stat & CORE99_FLASH_STATUS_DONE)); - if (stat & CORE99_FLASH_STATUS_ERR) + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + if (!(stat & SM_FLASH_STATUS_DONE)) break; } - out_8(base, CORE99_FLASH_CMD_RESET); - if (stat & CORE99_FLASH_STATUS_ERR) { - printk("nvram: flash error 0x%02x on write !\n", stat); - return -ENXIO; + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + for (i=0; i<NVRAM_SIZE; i++) + if (base[i] != datas[i]) { + printk(KERN_ERR "nvram: Sharp/Micron flash write failed !\n"); + return -ENXIO; + } + return 0; +} + +static int __pmac amd_erase_bank(int bank) +{ + int i, stat = 0; + unsigned long timeout; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + DBG("nvram: AMD Erasing bank %d...\n", bank); + + /* Unlock 1 */ + out_8(base+0x555, 0xaa); + udelay(1); + /* Unlock 2 */ + out_8(base+0x2aa, 0x55); + udelay(1); + + /* Sector-Erase */ + out_8(base+0x555, 0x80); + udelay(1); + out_8(base+0x555, 0xaa); + udelay(1); + out_8(base+0x2aa, 0x55); + udelay(1); + out_8(base, 0x30); + udelay(1); + + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: AMD flash erase timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + + for (i=0; i<NVRAM_SIZE; i++) + if (base[i] != 0xff) { + printk(KERN_ERR "nvram: AMD flash erase failed !\n"); + return -ENXIO; + } + return 0; +} + +static int __pmac amd_write_bank(int bank, u8* datas) +{ + int i, stat = 0; + unsigned long timeout; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + DBG("nvram: AMD Writing bank %d...\n", bank); + + for (i=0; i<NVRAM_SIZE; i++) { + /* Unlock 1 */ + out_8(base+0x555, 0xaa); + udelay(1); + /* Unlock 2 */ + out_8(base+0x2aa, 0x55); + udelay(1); + + /* Write single word */ + out_8(base+0x555, 0xa0); + udelay(1); + out_8(base+i, datas[i]); + + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: AMD flash write timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + if (stat != 0) + break; } + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + for (i=0; i<NVRAM_SIZE; i++) if (base[i] != datas[i]) { - printk("nvram: flash write failed !\n"); + printk(KERN_ERR "nvram: AMD flash write failed !\n"); return -ENXIO; } return 0; } -static void __init -lookup_partitions(void) +static void __init lookup_partitions(void) { u8 buffer[17]; int i, offset; @@ -227,15 +425,49 @@ lookup_partitions(void) nvram_partitions[pmac_nvram_XPRAM] = 0x1300; nvram_partitions[pmac_nvram_NR] = 0x1400; } + DBG("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); + DBG("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); + DBG("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); +} + +static void __pmac core99_nvram_sync(void) +{ + struct core99_header* hdr99; + unsigned long flags; + + if (!is_core_99 || !nvram_data || !nvram_image) + return; + + spin_lock_irqsave(&nv_lock, flags); + if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, + NVRAM_SIZE)) + goto bail; + + DBG("Updating nvram...\n"); + + hdr99 = (struct core99_header*)nvram_image; + hdr99->generation++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank) + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + goto bail; + } + if (core99_write_bank) + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); + bail: + spin_unlock_irqrestore(&nv_lock, flags); + #ifdef DEBUG - printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); - printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); - printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); + mdelay(2000); #endif } -void __init -pmac_nvram_init(void) +void __init pmac_nvram_init(void) { struct device_node *dp; @@ -256,38 +488,65 @@ pmac_nvram_init(void) printk(KERN_ERR "nvram: no address\n"); return; } -#if 0 - nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); - if (!nvram_image) { - printk(KERN_ERR "nvram: can't allocate image\n"); + nvram_image = alloc_bootmem(NVRAM_SIZE); + if (nvram_image == NULL) { + printk(KERN_ERR "nvram: can't allocate ram image\n"); return; } -#endif nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); -#ifdef DEBUG - printk("nvram: Checking bank 0...\n"); -#endif + nvram_naddrs = 1; /* Make sure we get the correct case */ + + DBG("nvram: Checking bank 0...\n"); + gen_bank0 = core99_check((u8 *)nvram_data); gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; -#ifdef DEBUG - printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); - printk("nvram: Active bank is: %d\n", core99_bank); -#endif + + DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + DBG("nvram: Active bank is: %d\n", core99_bank); + for (i=0; i<NVRAM_SIZE; i++) nvram_image[i] = nvram_data[i + core99_bank*NVRAM_SIZE]; + + ppc_md.nvram_read_val = core99_nvram_read_byte; + ppc_md.nvram_write_val = core99_nvram_write_byte; + ppc_md.nvram_sync = core99_nvram_sync; + /* + * Maybe we could be smarter here though making an exclusive list + * of known flash chips is a bit nasty as older OF didn't provide us + * with a useful "compatible" entry. A solution would be to really + * identify the chip using flash id commands and base ourselves on + * a list of known chips IDs + */ + if (device_is_compatible(dp, "amd-0137")) { + core99_erase_bank = amd_erase_bank; + core99_write_bank = amd_write_bank; + } else { + core99_erase_bank = sm_erase_bank; + core99_write_bank = sm_write_bank; + } } else if (_machine == _MACH_chrp && nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address + isa_mem_base, dp->addrs[0].size); nvram_mult = 1; + ppc_md.nvram_read_val = direct_nvram_read_byte; + ppc_md.nvram_write_val = direct_nvram_write_byte; } else if (nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + ppc_md.nvram_read_val = direct_nvram_read_byte; + ppc_md.nvram_write_val = direct_nvram_write_byte; } else if (nvram_naddrs == 2) { nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + ppc_md.nvram_read_val = indirect_nvram_read_byte; + ppc_md.nvram_write_val = indirect_nvram_write_byte; } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { +#ifdef CONFIG_ADB_PMU nvram_naddrs = -1; + ppc_md.nvram_read_val = pmu_nvram_read_byte; + ppc_md.nvram_write_val = pmu_nvram_write_byte; +#endif /* CONFIG_ADB_PMU */ } else { printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", nvram_naddrs); @@ -295,117 +554,31 @@ pmac_nvram_init(void) lookup_partitions(); } -void __pmac -pmac_nvram_update(void) -{ - struct core99_header* hdr99; - - if (!is_core_99 || !nvram_data || !nvram_image) - return; - if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, - NVRAM_SIZE)) - return; -#ifdef DEBUG - printk("Updating nvram...\n"); -#endif - hdr99 = (struct core99_header*)nvram_image; - hdr99->generation++; - hdr99->hdr.signature = CORE99_SIGNATURE; - hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); - hdr99->adler = core99_calc_adler(nvram_image); - core99_bank = core99_bank ? 0 : 1; - if (core99_erase_bank(core99_bank)) { - printk("nvram: Error erasing bank %d\n", core99_bank); - return; - } - if (core99_write_bank(core99_bank, nvram_image)) - printk("nvram: Error writing bank %d\n", core99_bank); -} - -unsigned char __pmac -pmac_nvram_read_byte(int addr) -{ - switch (nvram_naddrs) { -#ifdef CONFIG_ADB_PMU - case -1: { - struct adb_request req; - - if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, - (addr >> 8) & 0xff, addr & 0xff)) - break; - while (!req.complete) - pmu_poll(); - return req.reply[0]; - } -#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; - eieio(); - return nvram_data[(addr & 0x1f) << 4]; - } - return 0; -} - -void __pmac -pmac_nvram_write_byte(int addr, unsigned char val) -{ - switch (nvram_naddrs) { -#ifdef CONFIG_ADB_PMU - case -1: { - struct adb_request req; - - if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, - (addr >> 8) & 0xff, addr & 0xff, val)) - break; - while (!req.complete) - pmu_poll(); - 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: - *nvram_addr = addr >> 5; - eieio(); - nvram_data[(addr & 0x1f) << 4] = val; - break; - } - eieio(); -} - -int __pmac -pmac_get_partition(int partition) +int __pmac pmac_get_partition(int partition) { return nvram_partitions[partition]; } -u8 __pmac -pmac_xpram_read(int xpaddr) +u8 __pmac pmac_xpram_read(int xpaddr) { int offset = nvram_partitions[pmac_nvram_XPRAM]; if (offset < 0) - return 0; + return 0xff; - return pmac_nvram_read_byte(xpaddr + offset); + return ppc_md.nvram_read_val(xpaddr + offset); } -void __pmac -pmac_xpram_write(int xpaddr, u8 data) +void __pmac pmac_xpram_write(int xpaddr, u8 data) { int offset = nvram_partitions[pmac_nvram_XPRAM]; if (offset < 0) return; - pmac_nvram_write_byte(data, xpaddr + offset); + ppc_md.nvram_write_val(xpaddr + offset, data); } + +EXPORT_SYMBOL(pmac_get_partition); +EXPORT_SYMBOL(pmac_xpram_read); +EXPORT_SYMBOL(pmac_xpram_write); diff --git a/arch/ppc/platforms/pmac_pci.c b/arch/ppc/platforms/pmac_pci.c index de1c6ec6b03e..ec44b4a34fad 100644 --- a/arch/ppc/platforms/pmac_pci.c +++ b/arch/ppc/platforms/pmac_pci.c @@ -1,13 +1,11 @@ /* * Support for PCI bridges found on Power Macintoshes. - * - * This includes support for bandit, chaos, grackle (motorola - * MPC106), and uninorth + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. * * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) * - * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org) - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -30,11 +28,30 @@ #undef DEBUG -static void add_bridges(struct device_node *dev); +#ifdef DEBUG +#ifdef CONFIG_XMON +extern void xmon_printf(const char *fmt, ...); +#define DBG(x...) xmon_printf(x) +#else +#define DBG(x...) printk(x) +#endif +#else +#define DBG(x...) +#endif + +static int add_bridge(struct device_node *dev); +extern void pmac_check_ht_link(void); /* XXX Could be per-controller, but I don't think we risk anything by * assuming we won't have both UniNorth and Bandit */ static int has_uninorth; +#ifdef CONFIG_POWER4 +static struct pci_controller *u3_agp; +#endif /* CONFIG_POWER4 */ + +extern u8 pci_cache_line_size; + +struct pci_dev *k2_skiplist[2]; /* * Magic constants for enabling cache coherency in the bandit/PSX bridge. @@ -51,7 +68,7 @@ fixup_one_level_bus_range(struct device_node *node, int higher) { for (; node != 0;node = node->sibling) { int * bus_range; - unsigned int *class_code; + unsigned int *class_code; int len; /* For PCI<->PCI bridges or CardBus bridges, we go down */ @@ -81,7 +98,7 @@ fixup_bus_range(struct device_node *bridge) int * bus_range; int len; - /* Lookup the "bus-range" property for the hose */ + /* Lookup the "bus-range" property for the hose */ bus_range = (int *) get_property(bridge, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s\n", @@ -92,7 +109,7 @@ fixup_bus_range(struct device_node *bridge) } /* - * Apple MacRISC (UniNorth, Bandit, Chaos) PCI controllers. + * Apple MacRISC (U3, UniNorth, Bandit, Chaos) PCI controllers. * * The "Bandit" version is present in all early PCI PowerMacs, * and up to the first ones using Grackle. Some machines may @@ -106,6 +123,11 @@ fixup_bus_range(struct device_node *bridge) * The "UniNorth" version is present in all Core99 machines * (iBook, G4, new IMacs, and all the recent Apple machines). * It contains 3 controllers in one ASIC. + * + * The U3 is the bridge used on G5 machines. It contains on + * AGP bus which is dealt with the old UniNorth access routines + * and an HyperTransport bus which uses its own set of access + * functions. */ #define MACRISC_CFA0(devfn, off) \ @@ -211,12 +233,22 @@ static struct pci_ops macrisc_pci_ops = static int __pmac chaos_validate_dev(struct pci_bus *bus, int devfn, int offset) { - if (pci_busdev_to_OF_node(bus, devfn) == 0) + struct device_node *np; + u32 *vendor, *device; + + np = pci_busdev_to_OF_node(bus, devfn); + if (np == NULL) return PCIBIOS_DEVICE_NOT_FOUND; - if (/*(dev->vendor == 0x106b) && (dev->device == 3) &&*/ (offset >= 0x10) - && (offset != 0x14) && (offset != 0x18) && (offset <= 0x24)) { + + vendor = (u32 *)get_property(np, "vendor-id", NULL); + device = (u32 *)get_property(np, "device-id", NULL); + if (vendor == NULL || device == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + if ((*vendor == 0x106b) && (*device == 3) && (offset >= 0x10) + && (offset != 0x14) && (offset != 0x18) && (offset <= 0x24)) return PCIBIOS_BAD_REGISTER_NUMBER; - } + return PCIBIOS_SUCCESSFUL; } @@ -248,6 +280,128 @@ static struct pci_ops chaos_pci_ops = chaos_write_config }; +#ifdef CONFIG_POWER4 + +/* + * These versions of U3 HyperTransport config space access ops do not + * implement self-view of the HT host yet + */ + +#define U3_HT_CFA0(devfn, off) \ + ((((unsigned long)devfn) << 8) | offset) +#define U3_HT_CFA1(bus, devfn, off) \ + (U3_HT_CFA0(devfn, off) \ + + (((unsigned long)bus) << 16) \ + + 0x01000000UL) + +static unsigned long __pmac +u3_ht_cfg_access(struct pci_controller* hose, u8 bus, u8 devfn, u8 offset) +{ + if (bus == hose->first_busno) { + /* For now, we don't self probe U3 HT bridge */ + if (PCI_FUNC(devfn) != 0 || PCI_SLOT(devfn) > 7 || + PCI_SLOT(devfn) < 1) + return 0; + return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset); + } else + return ((unsigned long)hose->cfg_data) + U3_HT_CFA1(bus, devfn, offset); +} + +static int __pmac +u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + unsigned int addr; + int i; + + /* + * When a device in K2 is powered down, we die on config + * cycle accesses. Fix that here. + */ + for (i=0; i<2; i++) + if (k2_skiplist[i] && k2_skiplist[i]->bus == bus && + k2_skiplist[i]->devfn == devfn) { + switch (len) { + case 1: + *val = 0xff; break; + case 2: + *val = 0xffff; break; + default: + *val = 0xfffffffful; break; + } + return PCIBIOS_SUCCESSFUL; + } + + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + *val = in_8((u8 *)addr); + break; + case 2: + *val = in_le16((u16 *)addr); + break; + default: + *val = in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int __pmac +u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + unsigned int addr; + int i; + + /* + * When a device in K2 is powered down, we die on config + * cycle accesses. Fix that here. + */ + for (i=0; i<2; i++) + if (k2_skiplist[i] && k2_skiplist[i]->bus == bus && + k2_skiplist[i]->devfn == devfn) + return PCIBIOS_SUCCESSFUL; + + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + out_8((u8 *)addr, val); + (void) in_8((u8 *)addr); + break; + case 2: + out_le16((u16 *)addr, val); + (void) in_le16((u16 *)addr); + break; + default: + out_le32((u32 *)addr, val); + (void) in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops u3_ht_pci_ops = +{ + u3_ht_read_config, + u3_ht_write_config +}; + +#endif /* CONFIG_POWER4 */ /* * For a bandit bridge, turn on cache coherency if necessary. @@ -309,9 +463,7 @@ init_p2pbridge(void) || strcmp(p2pbridge->parent->name, "pci") != 0) return; if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) { -#ifdef DEBUG - printk("Can't find PCI infos for PCI<->PCI bridge\n"); -#endif + DBG("Can't find PCI infos for PCI<->PCI bridge\n"); return; } /* Warning: At this point, we have not yet renumbered all busses. @@ -319,9 +471,7 @@ init_p2pbridge(void) */ hose = pci_find_hose_for_OF_device(p2pbridge); if (!hose) { -#ifdef DEBUG - printk("Can't find hose for PCI<->PCI bridge\n"); -#endif + DBG("Can't find hose for PCI<->PCI bridge\n"); return; } if (early_read_config_word(hose, bus, devfn, @@ -333,13 +483,114 @@ init_p2pbridge(void) early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val); } +/* + * Some Apple desktop machines have a NEC PD720100A USB2 controller + * on the motherboard. Open Firmware, on these, will disable the + * EHCI part of it so it behaves like a pair of OHCI's. This fixup + * code re-enables it ;) + */ +static void __init +fixup_nec_usb2(void) +{ + struct device_node *nec; + + for (nec = NULL; (nec = of_find_node_by_name(nec, "usb")) != NULL;) { + struct pci_controller *hose; + u32 data, *prop; + u8 bus, devfn; + + prop = (u32 *)get_property(nec, "vendor-id", NULL); + if (prop == NULL) + continue; + if (0x1033 != *prop) + continue; + prop = (u32 *)get_property(nec, "device-id", NULL); + if (prop == NULL) + continue; + if (0x0035 != *prop) + continue; + prop = (u32 *)get_property(nec, "reg", 0); + if (prop == NULL) + continue; + devfn = (prop[0] >> 8) & 0xff; + bus = (prop[0] >> 16) & 0xff; + if (PCI_FUNC(devfn) != 0) + continue; + hose = pci_find_hose_for_OF_device(nec); + if (!hose) + continue; + early_read_config_dword(hose, bus, devfn, 0xe4, &data); + if (data & 1UL) { + printk("Found NEC PD720100A USB2 chip with disabled EHCI, fixing up...\n"); + data &= ~1UL; + early_write_config_dword(hose, bus, devfn, 0xe4, data); + early_write_config_byte(hose, bus, devfn | 2, PCI_INTERRUPT_LINE, + nec->intrs[0].line); + } + } +} + void __init pmac_find_bridges(void) { - add_bridges(find_devices("bandit")); - add_bridges(find_devices("chaos")); - add_bridges(find_devices("pci")); + struct device_node *np, *root; + struct device_node *ht = NULL; + + root = of_find_node_by_path("/"); + if (root == NULL) { + printk(KERN_CRIT "pmac_find_bridges: can't find root of device tree\n"); + return; + } + for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { + if (np->name == NULL) + continue; + if (strcmp(np->name, "bandit") == 0 + || strcmp(np->name, "chaos") == 0 + || strcmp(np->name, "pci") == 0) { + if (add_bridge(np) == 0) + of_node_get(np); + } + if (strcmp(np->name, "ht") == 0) { + of_node_get(np); + ht = np; + } + } + of_node_put(root); + + /* Probe HT last as it relies on the agp resources to be already + * setup + */ + if (ht && add_bridge(ht) != 0) + of_node_put(ht); + init_p2pbridge(); + fixup_nec_usb2(); +#ifdef CONFIG_POWER4 + /* There is something wrong with DMA on U3/HT. I haven't figured out + * the details yet, but if I set the cache line size to 128 bytes like + * it should, I'm getting memory corruption caused by devices like + * sungem (even without the MWI bit set, but maybe sungem doesn't + * care). Right now, it appears that setting up a 64 bytes line size + * works properly, 64 bytes beeing the max transfer size of HT, I + * suppose this is related the way HT/PCI are hooked together. I still + * need to dive into more specs though to be really sure of what's + * going on. --BenH. + * + * Ok, apparently, it's just that HT can't do more than 64 bytes + * transactions. MWI seem to be meaningless there as well, it may + * be worth nop'ing out pci_set_mwi too though I haven't done that + * yet. + * + * Note that it's a bit different for whatever is in the AGP slot. + * For now, I don't care, but this can become a real issue, we + * should probably hook pci_set_mwi anyway to make sure it sets + * the real cache line size in there. + */ + if (machine_is_compatible("MacRISC4")) + pci_cache_line_size = 16; /* 64 bytes */ + + pmac_check_ht_link(); +#endif /* CONFIG_POWER4 */ } #define GRACKLE_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ @@ -410,6 +661,118 @@ setup_chaos(struct pci_controller* hose, struct reg_property* addr) ioremap(addr->address + 0xc00000, 0x1000); } +#ifdef CONFIG_POWER4 + +static void __init +setup_u3_agp(struct pci_controller* hose, struct reg_property* addr) +{ + /* On G5, we move AGP up to high bus number so we don't need + * to reassign bus numbers for HT. If we ever have P2P bridges + * on AGP, we'll have to move pci_assign_all_busses to the + * pci_controller structure so we enable it for AGP and not for + * HT childs. + * We hard code the address because of the different size of + * the reg address cell, we shall fix that by killing struct + * reg_property and using some accessor functions instead + */ + hose->first_busno = 0xf0; + hose->last_busno = 0xff; + has_uninorth = 1; + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); + hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); + + u3_agp = hose; +} + +static void __init +setup_u3_ht(struct pci_controller* hose, struct reg_property *addr) +{ + struct device_node *np = (struct device_node *)hose->arch_data; + int i, cur; + + hose->ops = &u3_ht_pci_ops; + + /* We hard code the address because of the different size of + * the reg address cell, we shall fix that by killing struct + * reg_property and using some accessor functions instead + */ + hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000); + + /* + * /ht node doesn't expose a "ranges" property, so we "remove" regions that + * have been allocated to AGP. So far, this version of the code doesn't assign + * any of the 0xfxxxxxxx "fine" memory regions to /ht. + * We need to fix that sooner or later by either parsing all child "ranges" + * properties or figuring out the U3 address space decoding logic and + * then read it's configuration register (if any). + */ + hose->io_base_phys = 0xf4000000 + 0x00400000; + hose->io_base_virt = ioremap(hose->io_base_phys, 0x00400000); + isa_io_base = (unsigned long) hose->io_base_virt; + hose->io_resource.name = np->full_name; + hose->io_resource.start = 0; + hose->io_resource.end = 0x003fffff; + hose->io_resource.flags = IORESOURCE_IO; + hose->pci_mem_offset = 0; + hose->first_busno = 0; + hose->last_busno = 0xef; + hose->mem_resources[0].name = np->full_name; + hose->mem_resources[0].start = 0x80000000; + hose->mem_resources[0].end = 0xefffffff; + hose->mem_resources[0].flags = IORESOURCE_MEM; + + if (u3_agp == NULL) { + DBG("U3 has no AGP, using full resource range\n"); + return; + } + + /* We "remove" the AGP resources from the resources allocated to HT, that + * is we create "holes". However, that code does assumptions that so far + * happen to be true (cross fingers...), typically that resources in the + * AGP node are properly ordered + */ + cur = 0; + for (i=0; i<3; i++) { + struct resource *res = &u3_agp->mem_resources[i]; + if (res->flags != IORESOURCE_MEM) + continue; + /* We don't care about "fine" resources */ + if (res->start >= 0xf0000000) + continue; + /* Check if it's just a matter of "shrinking" us in one direction */ + if (hose->mem_resources[cur].start == res->start) { + DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n", + cur, hose->mem_resources[cur].start, res->end + 1); + hose->mem_resources[cur].start = res->end + 1; + continue; + } + if (hose->mem_resources[cur].end == res->end) { + DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n", + cur, hose->mem_resources[cur].end, res->start - 1); + hose->mem_resources[cur].end = res->start - 1; + continue; + } + /* No, it's not the case, we need a hole */ + if (cur == 2) { + /* not enough resources to make a hole, we drop part of the range */ + printk(KERN_WARNING "Running out of resources for /ht host !\n"); + hose->mem_resources[cur].end = res->start - 1; + continue; + } + cur++; + DBG("U3/HT: hole, %d end at %08lx, %d start at %08lx\n", + cur-1, res->start - 1, cur, res->end + 1); + hose->mem_resources[cur].name = np->full_name; + hose->mem_resources[cur].flags = IORESOURCE_MEM; + hose->mem_resources[cur].start = res->end + 1; + hose->mem_resources[cur].end = hose->mem_resources[cur-1].end; + hose->mem_resources[cur-1].end = res->start - 1; + } +} + +#endif /* CONFIG_POWER4 */ + void __init setup_grackle(struct pci_controller *hose) { @@ -426,69 +789,77 @@ setup_grackle(struct pci_controller *hose) * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, * if we have one or more bandit or chaos bridges, we don't have a MPC106. */ -static void __init -add_bridges(struct device_node *dev) +static int __init +add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; struct reg_property *addr; char* disp_name; int *bus_range; - int first = 1, primary; + int primary = 1; + + DBG("Adding PCI host bridge %s\n", dev->full_name); + + addr = (struct reg_property *) get_property(dev, "reg", &len); + if (addr == NULL || len < sizeof(*addr)) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + return -ENODEV; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", + dev->full_name); + } + + hose = pcibios_alloc_controller(); + if (!hose) + return -ENOMEM; + hose->arch_data = dev; + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + disp_name = NULL; +#ifdef CONFIG_POWER4 + if (device_is_compatible(dev, "u3-agp")) { + setup_u3_agp(hose, addr); + disp_name = "U3-AGP"; + primary = 0; + } else if (device_is_compatible(dev, "u3-ht")) { + setup_u3_ht(hose, addr); + disp_name = "U3-HT"; + primary = 1; + } else +#endif /* CONFIG_POWER4 */ + if (device_is_compatible(dev, "uni-north")) { + primary = setup_uninorth(hose, addr); + disp_name = "UniNorth"; + } else if (strcmp(dev->name, "pci") == 0) { + /* XXX assume this is a mpc106 (grackle) */ + setup_grackle(hose); + disp_name = "Grackle (MPC106)"; + } else if (strcmp(dev->name, "bandit") == 0) { + setup_bandit(hose, addr); + disp_name = "Bandit"; + } else if (strcmp(dev->name, "chaos") == 0) { + setup_chaos(hose, addr); + disp_name = "Chaos"; + primary = 0; + } + printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n", + disp_name, addr->address, hose->first_busno, hose->last_busno); + DBG(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n", + hose, hose->cfg_addr, hose->cfg_data); + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pci_process_bridge_OF_ranges(hose, dev, primary); + + /* Fixup "bus-range" OF property */ + fixup_bus_range(dev); - for (; dev != NULL; dev = dev->next) { - addr = (struct reg_property *) get_property(dev, "reg", &len); - if (addr == NULL || len < sizeof(*addr)) { - printk(KERN_WARNING "Can't use %s: no address\n", - dev->full_name); - continue; - } - bus_range = (int *) get_property(dev, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", - dev->full_name); - } - - hose = pcibios_alloc_controller(); - if (!hose) - continue; - hose->arch_data = dev; - hose->first_busno = bus_range ? bus_range[0] : 0; - hose->last_busno = bus_range ? bus_range[1] : 0xff; - - disp_name = NULL; - primary = first; - if (device_is_compatible(dev, "uni-north")) { - primary = setup_uninorth(hose, addr); - disp_name = "UniNorth"; - } else if (strcmp(dev->name, "pci") == 0) { - /* XXX assume this is a mpc106 (grackle) */ - setup_grackle(hose); - disp_name = "Grackle (MPC106)"; - } else if (strcmp(dev->name, "bandit") == 0) { - setup_bandit(hose, addr); - disp_name = "Bandit"; - } else if (strcmp(dev->name, "chaos") == 0) { - setup_chaos(hose, addr); - disp_name = "Chaos"; - primary = 0; - } - printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n", - disp_name, addr->address, hose->first_busno, hose->last_busno); -#ifdef DEBUG - printk(" ->Hose at 0x%08lx, cfg_addr=0x%08lx,cfg_data=0x%08lx\n", - hose, hose->cfg_addr, hose->cfg_data); -#endif - - /* Interpret the "ranges" property */ - /* This also maps the I/O region and sets isa_io/mem_base */ - pci_process_bridge_OF_ranges(hose, dev, primary); - - /* Fixup "bus-range" OF property */ - fixup_bus_range(dev); - - first &= !primary; - } + return 0; } static void __init @@ -575,7 +946,7 @@ pmac_pci_enable_device_hook(struct pci_dev *dev, int initial) cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; pci_write_config_word(dev, PCI_COMMAND, cmd); pci_write_config_byte(dev, PCI_LATENCY_TIMER, 16); - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cache_line_size); } return 0; @@ -628,3 +999,108 @@ pmac_pcibios_after_init(void) } } +void pmac_pci_fixup_cardbus(struct pci_dev* dev) +{ + if (_machine != _MACH_Pmac) + return; + /* + * Fix the interrupt routing on the various cardbus bridges + * used on powerbooks + */ + if (dev->vendor != PCI_VENDOR_ID_TI) + return; + if (dev->device == PCI_DEVICE_ID_TI_1130 || + dev->device == PCI_DEVICE_ID_TI_1131) { + u8 val; + /* Enable PCI interrupt */ + if (pci_read_config_byte(dev, 0x91, &val) == 0) + pci_write_config_byte(dev, 0x91, val | 0x30); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); + } + if (dev->device == PCI_DEVICE_ID_TI_1210 || + dev->device == PCI_DEVICE_ID_TI_1211 || + dev->device == PCI_DEVICE_ID_TI_1410 || + dev->device == PCI_DEVICE_ID_TI_1510) { + u8 val; + /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA + signal out the MFUNC0 pin */ + if (pci_read_config_byte(dev, 0x8c, &val) == 0) + pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); + } +} + +void pmac_pci_fixup_pciata(struct pci_dev* dev) +{ + u8 progif = 0; + + /* + * On PowerMacs, we try to switch any PCI ATA controller to + * fully native mode + */ + if (_machine != _MACH_Pmac) + return; + /* Some controllers don't have the class IDE */ + if (dev->vendor == PCI_VENDOR_ID_PROMISE) + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20246: + case PCI_DEVICE_ID_PROMISE_20262: + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20270: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20277: + goto good; + } + /* Others, check PCI class */ + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return; + good: + pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + if ((progif & 5) != 5) { + printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n", pci_name(dev)); + (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); + if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || + (progif & 5) != 5) + printk(KERN_ERR "Rewrite of PROGIF failed !\n"); + } +} + +/* + * Disable second function on K2-SATA, it's broken + * and disable IO BARs on first one + */ +void __pmac pmac_pci_fixup_k2_sata(struct pci_dev* dev) +{ + int i; + u16 cmd; + + if (PCI_FUNC(dev->devfn) > 0) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 6; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } else { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_IO; + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 5; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } +} diff --git a/arch/ppc/platforms/pmac_pic.c b/arch/ppc/platforms/pmac_pic.c index 6ef68ec1d909..901566d3b22d 100644 --- a/arch/ppc/platforms/pmac_pic.c +++ b/arch/ppc/platforms/pmac_pic.c @@ -34,6 +34,7 @@ #include <asm/time.h> #include <asm/open_pic.h> #include <asm/xmon.h> +#include <asm/pmac_feature.h> #include "pmac_pic.h" @@ -363,32 +364,76 @@ static int __init enable_second_ohare(void) return irqctrler->intrs[0].line; } -void __init -pmac_pic_init(void) +#ifdef CONFIG_POWER4 +static irqreturn_t k2u3_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq; + + irq = openpic2_get_irq(regs); + if (irq != -1) + ppc_irq_dispatch_handler(regs, irq); + return IRQ_HANDLED; +} +#endif /* CONFIG_POWER4 */ + +void __init pmac_pic_init(void) { int i; - struct device_node *irqctrler; + struct device_node *irqctrler = NULL; + struct device_node *irqctrler2 = NULL; + struct device_node *np; unsigned long addr; int irq_cascade = -1; /* 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"); + np = find_type_devices("open-pic"); + while(np) { + if (np->parent && !strcmp(np->parent->name, "u3")) + irqctrler2 = np; + else + irqctrler = np; + np = np->next; + } if (irqctrler != NULL) { - printk("PowerMac using OpenPIC irq controller\n"); if (irqctrler->n_addrs > 0) { - unsigned char senses[NR_IRQS]; + unsigned char senses[128]; + + printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n", + irqctrler->addrs[0].address); - prom_get_irq_senses(senses, 0, NR_IRQS); + prom_get_irq_senses(senses, 0, 128); OpenPIC_InitSenses = senses; - OpenPIC_NumInitSenses = NR_IRQS; + OpenPIC_NumInitSenses = 128; ppc_md.get_irq = openpic_get_irq; + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler, 0, 0); OpenPIC_Addr = ioremap(irqctrler->addrs[0].address, irqctrler->addrs[0].size); openpic_init(0); + +#ifdef CONFIG_POWER4 + if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 && + irqctrler2->n_addrs > 0) { + printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n", + irqctrler2->addrs[0].address, + irqctrler2->intrs[0].line); + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0); + OpenPIC2_Addr = ioremap(irqctrler2->addrs[0].address, + irqctrler2->addrs[0].size); + prom_get_irq_senses(senses, PMAC_OPENPIC2_OFFSET, + PMAC_OPENPIC2_OFFSET+128); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = 128; + openpic2_init(PMAC_OPENPIC2_OFFSET); + if (request_irq(irqctrler2->intrs[0].line, k2u3_action, 0, + "U3->K2 Cascade", NULL)) + printk("Unable to get OpenPIC IRQ for cascade\n"); + } +#endif /* CONFIG_POWER4 */ + #ifdef CONFIG_XMON { struct device_node* pswitch; diff --git a/arch/ppc/platforms/pmac_setup.c b/arch/ppc/platforms/pmac_setup.c index 69bd381b43f6..10a2d5f270a3 100644 --- a/arch/ppc/platforms/pmac_setup.c +++ b/arch/ppc/platforms/pmac_setup.c @@ -332,7 +332,7 @@ pmac_setup_arch(void) #ifdef CONFIG_SMP /* Check for Core99 */ - if (find_devices("uni-n")) + if (find_devices("uni-n") || find_devices("u3")) ppc_md.smp_ops = &core99_smp_ops; else ppc_md.smp_ops = &psurge_smp_ops; @@ -469,10 +469,6 @@ pmac_restart(char *cmd) struct adb_request req; #endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_NVRAM - pmac_nvram_update(); -#endif - switch (sys_ctrler) { #ifdef CONFIG_ADB_CUDA case SYS_CTRLER_CUDA: @@ -498,10 +494,6 @@ pmac_power_off(void) struct adb_request req; #endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_NVRAM - pmac_nvram_update(); -#endif - switch (sys_ctrler) { #ifdef CONFIG_ADB_CUDA case SYS_CTRLER_CUDA: @@ -637,11 +629,6 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_md.get_rtc_time = pmac_get_rtc_time; ppc_md.calibrate_decr = pmac_calibrate_decr; -#ifdef CONFIG_NVRAM - ppc_md.nvram_read_val = pmac_nvram_read_byte; - ppc_md.nvram_write_val = pmac_nvram_write_byte; -#endif - ppc_md.find_end_of_memory = pmac_find_end_of_memory; ppc_md.feature_call = pmac_do_feature_call; @@ -685,6 +672,14 @@ pmac_declare_of_platform_devices(void) break; } } + np = find_devices("u3"); + if (np) { + for (np = np->child; np != NULL; np = np->sibling) + if (strncmp(np->name, "i2c", 3) == 0) { + of_platform_device_create(np, "u3-i2c"); + break; + } + } np = find_devices("valkyrie"); if (np) diff --git a/arch/ppc/platforms/pmac_smp.c b/arch/ppc/platforms/pmac_smp.c index d230b9890a05..7bac32b40c8e 100644 --- a/arch/ppc/platforms/pmac_smp.c +++ b/arch/ppc/platforms/pmac_smp.c @@ -119,8 +119,7 @@ static unsigned int core99_tb_gpio; /* Sync flag for HW tb sync */ static volatile int sec_tb_reset = 0; -static void __init -core99_init_caches(int cpu) +static void __init core99_init_caches(int cpu) { if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR)) return; @@ -188,8 +187,7 @@ static inline void psurge_clr_ipi(int cpu) */ static unsigned long psurge_smp_message[NR_CPUS]; -void __pmac -psurge_smp_message_recv(struct pt_regs *regs) +void __pmac psurge_smp_message_recv(struct pt_regs *regs) { int cpu = smp_processor_id(); int msg; @@ -206,15 +204,14 @@ psurge_smp_message_recv(struct pt_regs *regs) smp_message_recv(msg, regs); } -irqreturn_t __pmac -psurge_primary_intr(int irq, void *d, struct pt_regs *regs) +irqreturn_t __pmac psurge_primary_intr(int irq, void *d, struct pt_regs *regs) { psurge_smp_message_recv(regs); return IRQ_HANDLED; } -static void __pmac -smp_psurge_message_pass(int target, int msg, unsigned long data, int wait) +static void __pmac smp_psurge_message_pass(int target, int msg, unsigned long data, + int wait) { int i; @@ -410,8 +407,7 @@ static void __init psurge_dual_sync_tb(int cpu_nr) smp_tb_synchronized = 1; } -static void __init -smp_psurge_setup_cpu(int cpu_nr) +static void __init smp_psurge_setup_cpu(int cpu_nr) { if (cpu_nr == 0) { @@ -435,41 +431,54 @@ smp_psurge_setup_cpu(int cpu_nr) psurge_dual_sync_tb(cpu_nr); } -void __init -smp_psurge_take_timebase(void) +void __init smp_psurge_take_timebase(void) { /* Dummy implementation */ } -void __init -smp_psurge_give_timebase(void) +void __init smp_psurge_give_timebase(void) { /* Dummy implementation */ } -static int __init -smp_core99_probe(void) +static int __init smp_core99_probe(void) { +#ifdef CONFIG_6xx extern int powersave_nap; - struct device_node *cpus; - int i, ncpus = 1; +#endif + struct device_node *cpus, *firstcpu; + int i, ncpus = 0, boot_cpu = -1; u32 *tbprop; if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); - cpus = find_type_devices("cpu"); - if (cpus == NULL) - return 0; - - tbprop = (u32 *)get_property(cpus, "timebase-enable", NULL); - if (tbprop) - core99_tb_gpio = *tbprop; - else - core99_tb_gpio = KL_GPIO_TB_ENABLE; + cpus = firstcpu = find_type_devices("cpu"); + while(cpus != NULL) { + u32 *regprop = (u32 *)get_property(cpus, "reg", NULL); + char *stateprop = (char *)get_property(cpus, "state", NULL); + if (regprop != NULL && stateprop != NULL && + !strncmp(stateprop, "running", 7)) + boot_cpu = *regprop; + ++ncpus; + cpus = cpus->next; + } + if (boot_cpu == -1) + printk(KERN_WARNING "Couldn't detect boot CPU !\n"); + if (boot_cpu != 0) + printk(KERN_WARNING "Boot CPU is %d, unsupported setup !\n", boot_cpu); - while ((cpus = cpus->next) != NULL) - ++ncpus; + if (machine_is_compatible("MacRISC4")) { + extern struct smp_ops_t core99_smp_ops; - printk("smp_core99_probe: found %d cpus\n", ncpus); + core99_smp_ops.take_timebase = smp_generic_take_timebase; + core99_smp_ops.give_timebase = smp_generic_give_timebase; + } else { + if (firstcpu != NULL) + tbprop = (u32 *)get_property(firstcpu, "timebase-enable", NULL); + if (tbprop) + core99_tb_gpio = *tbprop; + else + core99_tb_gpio = KL_GPIO_TB_ENABLE; + } if (ncpus > 1) { openpic_request_IPIs(); @@ -484,8 +493,7 @@ smp_core99_probe(void) return ncpus; } -static void __init -smp_core99_kick_cpu(int nr) +static void __init smp_core99_kick_cpu(int nr) { unsigned long save_vector, new_vector; unsigned long flags; @@ -539,23 +547,31 @@ smp_core99_kick_cpu(int nr) if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); } -static void __init -smp_core99_setup_cpu(int cpu_nr) +static void __init smp_core99_setup_cpu(int cpu_nr) { - /* Setup some registers */ + /* Setup L2/L3 */ if (cpu_nr != 0) core99_init_caches(cpu_nr); /* Setup openpic */ do_openpic_setup_cpu(); - /* Setup L2/L3 */ - if (cpu_nr == 0) + if (cpu_nr == 0) { +#ifdef CONFIG_POWER4 + extern void g5_phy_disable_cpu1(void); + + /* If we didn't start the second CPU, we must take + * it off the bus + */ + if (machine_is_compatible("MacRISC4") && + num_online_cpus() < 2) + g5_phy_disable_cpu1(); +#endif /* CONFIG_POWER4 */ if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); + } } -void __init -smp_core99_take_timebase(void) +void __init smp_core99_take_timebase(void) { /* Secondary processor "takes" the timebase by freezing * it, resetting its local TB and telling CPU 0 to go on @@ -572,8 +588,7 @@ smp_core99_take_timebase(void) sec_tb_reset = 1; } -void __init -smp_core99_give_timebase(void) +void __init smp_core99_give_timebase(void) { unsigned int t; diff --git a/arch/ppc/platforms/pmac_time.c b/arch/ppc/platforms/pmac_time.c index 923afbdc7c56..cd8e3e7c4d13 100644 --- a/arch/ppc/platforms/pmac_time.c +++ b/arch/ppc/platforms/pmac_time.c @@ -266,6 +266,14 @@ pmac_calibrate_decr(void) if (via_calibrate_decr()) return; + /* Special case: QuickSilver G4s seem to have a badly calibrated + * timebase-frequency in OF, VIA is much better on these. We should + * probably implement calibration based on the KL timer on these + * machines anyway... -BenH + */ + if (machine_is_compatible("PowerMac3,5")) + if (via_calibrate_decr()) + return; /* * The cpu node should have a timebase-frequency property * to tell us the rate at which the decrementer counts. diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 9c5faac97350..b7ebf316b533 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PCI) += qspan_pci.o i8259.o endif obj-$(CONFIG_PPC_OF) += prom_init.o prom.o of_device.o obj-$(CONFIG_PPC_PMAC) += open_pic.o indirect_pci.o +obj-$(CONFIG_POWER4) += open_pic2.o obj-$(CONFIG_PPC_CHRP) += open_pic.o indirect_pci.o i8259.o obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \ diff --git a/arch/ppc/syslib/of_device.c b/arch/ppc/syslib/of_device.c index b27a75f1b986..46269ed21aee 100644 --- a/arch/ppc/syslib/of_device.c +++ b/arch/ppc/syslib/of_device.c @@ -183,6 +183,7 @@ void of_release_dev(struct device *dev) struct of_device *ofdev; ofdev = to_of_device(dev); + of_node_put(ofdev->node); kfree(ofdev); } @@ -242,7 +243,7 @@ struct of_device* of_platform_device_create(struct device_node *np, const char * return NULL; memset(dev, 0, sizeof(*dev)); - dev->node = np; + dev->node = of_node_get(np); dev->dma_mask = 0xffffffffUL; dev->dev.dma_mask = &dev->dma_mask; dev->dev.parent = NULL; diff --git a/arch/ppc/syslib/open_pic.c b/arch/ppc/syslib/open_pic.c index e1dcfea8ba02..2a922f21a449 100644 --- a/arch/ppc/syslib/open_pic.c +++ b/arch/ppc/syslib/open_pic.c @@ -610,12 +610,15 @@ void openpic_request_IPIs(void) void __devinit do_openpic_setup_cpu(void) { +#ifdef CONFIG_IRQ_ALL_CPUS int i; - u32 msk = 1 << smp_hw_index[smp_processor_id()]; - + u32 msk; +#endif spin_lock(&openpic_setup_lock); #ifdef CONFIG_IRQ_ALL_CPUS + msk = 1 << smp_hw_index[smp_processor_id()]; + /* let the openpic know we want intrs. default affinity * is 0xffffffff until changed via /proc * That's how it's done on x86. If we want it differently, then @@ -788,15 +791,25 @@ static void openpic_set_sense(u_int irq, int sense) */ static void openpic_ack_irq(unsigned int irq_nr) { +#ifdef __SLOW_VERSION__ openpic_disable_irq(irq_nr); openpic_eoi(); +#else + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + openpic_eoi(); +#endif } static void openpic_end_irq(unsigned int irq_nr) { +#ifdef __SLOW_VERSION__ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && irq_desc[irq_nr].action) openpic_enable_irq(irq_nr); +#else + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) + openpic_eoi(); +#endif } static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c new file mode 100644 index 000000000000..96ebd2389c9d --- /dev/null +++ b/arch/ppc/syslib/open_pic2.c @@ -0,0 +1,716 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * This is a duplicate of open_pic.c that deals with U3s MPIC on + * G5 PowerMacs. It's the same file except it's using big endian + * register accesses + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/sysdev.h> +#include <asm/ptrace.h> +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/sections.h> +#include <asm/open_pic.h> +#include <asm/i8259.h> +#include <asm/hardirq.h> + +#include "open_pic_defs.h" + +void *OpenPIC2_Addr; +static volatile struct OpenPIC *OpenPIC2 = NULL; +/* + * We define OpenPIC_InitSenses table thusly: + * bit 0x1: sense, 0 for edge and 1 for level. + * bit 0x2: polarity, 0 for negative, 1 for positive. + */ +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern int use_of_interrupt_tree; + +static u_int NumProcessors; +static u_int NumSources; +static int open_pic2_irq_offset; +static volatile OpenPIC_Source *ISR[NR_IRQS]; + +/* Global Operations */ +static void openpic2_disable_8259_pass_through(void); +static void openpic2_set_priority(u_int pri); +static void openpic2_set_spurious(u_int vector); + +/* Timer Interrupts */ +static void openpic2_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic2_maptimer(u_int timer, u_int cpumask); + +/* Interrupt Sources */ +static void openpic2_enable_irq(u_int irq); +static void openpic2_disable_irq(u_int irq); +static void openpic2_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic2_mapirq(u_int irq, u_int cpumask, u_int keepmask); + +/* + * These functions are not used but the code is kept here + * for completeness and future reference. + */ +static void openpic2_reset(void); +#ifdef notused +static void openpic2_enable_8259_pass_through(void); +static u_int openpic2_get_priority(void); +static u_int openpic2_get_spurious(void); +static void openpic2_set_sense(u_int irq, int sense); +#endif /* notused */ + +/* + * Description of the openpic for the higher-level irq code + */ +static void openpic2_end_irq(unsigned int irq_nr); +static void openpic2_ack_irq(unsigned int irq_nr); + +struct hw_interrupt_type open_pic2 = { + " OpenPIC2 ", + NULL, + NULL, + openpic2_enable_irq, + openpic2_disable_irq, + openpic2_ack_irq, + openpic2_end_irq, +}; + +/* + * Accesses to the current processor's openpic registers + * On cascaded controller, this is only CPU 0 + */ +#define THIS_CPU Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("open_pic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("open_pic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("open_pic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +extern unsigned long* _get_SP(void); +#define check_arg_irq(irq) \ + if (irq < open_pic2_irq_offset || irq >= NumSources+open_pic2_irq_offset \ + || ISR[irq - open_pic2_irq_offset] == 0) { \ + printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ + /*print_backtrace(_get_SP());*/ } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors){ \ + printk("open_pic2.c:%d: illegal cpu %d\n", __LINE__, cpu); \ + /*print_backtrace(_get_SP());*/ } +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +static u_int openpic2_read(volatile u_int *addr) +{ + u_int val; + + val = in_be32(addr); + return val; +} + +static inline void openpic2_write(volatile u_int *addr, u_int val) +{ + out_be32(addr, val); +} + +static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic2_read(addr); + return val & mask; +} + +inline void openpic2_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic2_read(addr); + openpic2_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic2_clearfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, 0); +} + +static inline void openpic2_setfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, mask); +} + +static void openpic2_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + openpic2_setfield(addr, OPENPIC_MASK); + while (openpic2_read(addr) & OPENPIC_ACTIVITY); + openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +static void openpic2_reset(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); + while (openpic2_readfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET)) + mb(); +} + +void __init openpic2_set_sources(int first_irq, int num_irqs, void *first_ISR) +{ + volatile OpenPIC_Source *src = first_ISR; + int i, last_irq; + + last_irq = first_irq + num_irqs; + if (last_irq > NumSources) + NumSources = last_irq; + if (src == 0) + src = &((struct OpenPIC *)OpenPIC2_Addr)->Source[first_irq]; + for (i = first_irq; i < last_irq; ++i, ++src) + ISR[i] = src; +} + +/* + * The `offset' parameter defines where the interrupts handled by the + * OpenPIC start in the space of interrupt numbers that the kernel knows + * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the + * kernel's interrupt numbering scheme. + * We assume there is only one OpenPIC. + */ +void __init openpic2_init(int offset) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC2_Addr) { + printk("No OpenPIC2 found !\n"); + return; + } + OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr; + + if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); + + t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + if (NumSources == 0) + openpic2_set_sources(0, + ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, + NULL); + printk("OpenPIC (2) Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC2); + timerfreq = openpic2_read(&OpenPIC2->Global.Timer_Frequency); + if (timerfreq) + printk("OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + open_pic2_irq_offset = offset; + + /* Initialize timer interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic2: timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic2_inittimer(i, 0, OPENPIC2_VEC_TIMER+i+offset); + /* No processor */ + openpic2_maptimer(i, 0); + } + + /* Initialize external interrupts */ + if (ppc_md.progress) ppc_md.progress("openpic2: external",0x3bc); + + openpic2_set_priority(0xf); + + /* Init all external sources, including possibly the cascade. */ + for (i = 0; i < NumSources; i++) { + int sense; + + if (ISR[i] == 0) + continue; + + /* the bootloader may have left it enabled (bad !) */ + openpic2_disable_irq(i+offset); + + sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE); + + if (sense & IRQ_SENSE_MASK) + irq_desc[i+offset].status = IRQ_LEVEL; + + /* Enabled, Priority 8 */ + openpic2_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), + (sense & IRQ_SENSE_MASK)); + /* Processor 0 */ + openpic2_mapirq(i, 1<<0, 0); + } + + /* Init descriptors */ + for (i = offset; i < NumSources + offset; i++) + irq_desc[i].handler = &open_pic2; + + /* Initialize the spurious interrupt */ + if (ppc_md.progress) ppc_md.progress("openpic2: spurious",0x3bd); + openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+offset); + + openpic2_disable_8259_pass_through(); + openpic2_set_priority(0); + + if (ppc_md.progress) ppc_md.progress("openpic2: exit",0x222); +} + +#ifdef notused +static void openpic2_enable_8259_pass_through(void) +{ + openpic2_clearfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} +#endif /* notused */ + +/* This can't be __init, it is used in openpic_sleep_restore_intrs */ +static void openpic2_disable_8259_pass_through(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +u_int openpic2_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +void openpic2_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI); +} + +#ifdef notused +static u_int openpic2_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} +#endif /* notused */ + +static void __init openpic2_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +#ifdef notused +static u_int openpic2_get_spurious(void) +{ + return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} +#endif /* notused */ + +/* This can't be __init, it is used in openpic_sleep_restore_intrs */ +static void openpic2_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +static spinlock_t openpic2_setup_lock = SPIN_LOCK_UNLOCKED; + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic2_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic2_safe_writefield(&OpenPIC2->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +static void __init openpic2_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic2_write(&OpenPIC2->Global.Timer[timer].Destination, + cpumask); +} + +/* + * Initalize the interrupt source which will generate an NMI. + * This raises the interrupt's priority from 8 to 9. + * + * irq: The logical IRQ which generates an NMI. + */ +void __init +openpic2_init_nmi_irq(u_int irq) +{ + check_arg_irq(irq); + openpic2_safe_writefield(&ISR[irq - open_pic2_irq_offset]->Vector_Priority, + OPENPIC_PRIORITY_MASK, + 9 << OPENPIC_PRIORITY_SHIFT); +} + +/* + * + * All functions below take an offset'ed irq argument + * + */ + + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic2_enable_irq(u_int irq) +{ + volatile u_int *vpp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; + openpic2_clearfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while (openpic2_readfield(vpp, OPENPIC_MASK)); +} + +static void openpic2_disable_irq(u_int irq) +{ + volatile u_int *vpp; + u32 vp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; + openpic2_setfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + vp = openpic2_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void __init +openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic2_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic2_mapirq(u_int irq, u_int physmask, u_int keepmask) +{ + if (ISR[irq] == 0) + return; + if (keepmask != 0) + physmask |= openpic2_read(&ISR[irq]->Destination) & keepmask; + openpic2_write(&ISR[irq]->Destination, physmask); +} + +#ifdef notused +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static void openpic2_set_sense(u_int irq, int sense) +{ + if (ISR[irq] != 0) + openpic2_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} +#endif /* notused */ + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic2_ack_irq(unsigned int irq_nr) +{ + openpic2_disable_irq(irq_nr); + openpic2_eoi(); +} + +static void openpic2_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + openpic2_enable_irq(irq_nr); +} + +int +openpic2_get_irq(struct pt_regs *regs) +{ + int irq = openpic2_irq(); + + if (irq == (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)) + irq = -1; + return irq; +} + +#ifdef CONFIG_PM + +/* + * We implement the IRQ controller as a sysdev and put it + * to sleep at powerdown stage (the callback is named suspend, + * but it's old semantics, for the Device Model, it's really + * powerdown). The possible problem is that another sysdev that + * happens to be suspend after this one will have interrupts off, + * that may be an issue... For now, this isn't an issue on pmac + * though... + */ + +static u32 save_ipi_vp[OPENPIC_NUM_IPI]; +static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES]; +static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES]; +static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS]; +static int openpic_suspend_count; + +static void openpic2_cached_enable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic2_irq_offset] &= ~OPENPIC_MASK; +} + +static void openpic2_cached_disable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic2_irq_offset] |= OPENPIC_MASK; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic2_suspend(struct sys_device *sysdev, u32 state) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&openpic2_setup_lock, flags); + + if (openpic_suspend_count++ > 0) { + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + return 0; + } + + open_pic2.enable = openpic2_cached_enable_irq; + open_pic2.disable = openpic2_cached_disable_irq; + + for (i=0; i<NumProcessors; i++) { + save_cpu_task_pri[i] = openpic2_read(&OpenPIC2->Processor[i].Current_Task_Priority); + openpic2_writefield(&OpenPIC2->Processor[i].Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); + } + + for (i=0; i<OPENPIC_NUM_IPI; i++) + save_ipi_vp[i] = openpic2_read(&OpenPIC2->Global.IPI_Vector_Priority(i)); + for (i=0; i<NumSources; i++) { + if (ISR[i] == 0) + continue; + save_irq_src_vp[i] = openpic2_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY; + save_irq_src_dest[i] = openpic2_read(&ISR[i]->Destination); + } + + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + + return 0; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic2_resume(struct sys_device *sysdev) +{ + int i; + unsigned long flags; + u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK | + OPENPIC_MASK; + + spin_lock_irqsave(&openpic2_setup_lock, flags); + + if ((--openpic_suspend_count) > 0) { + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + return 0; + } + + openpic2_reset(); + + /* OpenPIC sometimes seem to need some time to be fully back up... */ + do { + openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+open_pic2_irq_offset); + } while(openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) + != (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)); + + openpic2_disable_8259_pass_through(); + + for (i=0; i<OPENPIC_NUM_IPI; i++) + openpic2_write(&OpenPIC2->Global.IPI_Vector_Priority(i), + save_ipi_vp[i]); + for (i=0; i<NumSources; i++) { + if (ISR[i] == 0) + continue; + openpic2_write(&ISR[i]->Destination, save_irq_src_dest[i]); + openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + /* make sure mask gets to controller before we return to user */ + do { + openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + } while (openpic2_readfield(&ISR[i]->Vector_Priority, vppmask) + != (save_irq_src_vp[i] & vppmask)); + } + for (i=0; i<NumProcessors; i++) + openpic2_write(&OpenPIC2->Processor[i].Current_Task_Priority, + save_cpu_task_pri[i]); + + open_pic2.enable = openpic2_enable_irq; + open_pic2.disable = openpic2_disable_irq; + + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + + return 0; +} + +#endif /* CONFIG_PM */ + +/* HACK ALERT */ +static struct sysdev_class openpic2_sysclass = { + set_kset_name("openpic2"), +}; + +static struct sys_device device_openpic2 = { + .id = 0, + .cls = &openpic2_sysclass, +}; + +static struct sysdev_driver driver_openpic2 = { +#ifdef CONFIG_PM + .suspend = &openpic2_suspend, + .resume = &openpic2_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_openpic2_sysfs(void) +{ + int rc; + + if (!OpenPIC2_Addr) + return -ENODEV; + printk(KERN_DEBUG "Registering openpic2 with sysfs...\n"); + rc = sysdev_class_register(&openpic2_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys class\n"); + return -ENODEV; + } + rc = sys_device_register(&device_openpic2); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys device\n"); + return -ENODEV; + } + rc = sysdev_driver_register(&openpic2_sysclass, &driver_openpic2); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys driver\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_openpic2_sysfs); + diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c index c8387f1baef1..af98150540b8 100644 --- a/arch/ppc/syslib/prom.c +++ b/arch/ppc/syslib/prom.c @@ -160,7 +160,7 @@ finish_device_tree(void) match on /chosen.interrupt_controller */ if ((name != NULL && strcmp(name, "interrupt-controller") == 0) - || (ic != NULL && iclen == 0)) { + || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) { if (n == 0) dflt_interrupt_controller = np; ++n; @@ -217,7 +217,7 @@ finish_node(struct device_node *np, unsigned long mem_start, ifunc = interpret_macio_props; else if (!strcmp(np->type, "isa")) ifunc = interpret_isa_props; - else if (!strcmp(np->name, "uni-n")) + else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) ifunc = interpret_root_props; else if (!((ifunc == interpret_dbdma_props || ifunc == interpret_macio_props) @@ -431,10 +431,21 @@ finish_node_interrupts(struct device_node *np, unsigned long mem_start) * This doesn't cope with the general case of multiple * cascaded interrupt controllers, but then neither will * irq.c at the moment either. -- paulus + * The G5 triggers that code, I add a machine test. On + * those machines, we want to offset interrupts from the + * second openpic by 128 -- BenH */ - if (num_interrupt_controllers > 1 && ic != NULL + if (_machine != _MACH_Pmac && num_interrupt_controllers > 1 + && ic != NULL && get_property(ic, "interrupt-parent", NULL) == NULL) offset = 16; + else if (_machine == _MACH_Pmac && num_interrupt_controllers > 1 + && ic != NULL && ic->parent != NULL) { + char *name = get_property(ic->parent, "name", NULL); + if (name && !strcmp(name, "u3")) + offset = 128; + } + np->intrs[i].line = irq[0] + offset; if (n > 1) np->intrs[i].sense = irq[1]; @@ -1212,8 +1223,6 @@ find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range) * Request an OF device resource. Currently handles child of PCI devices, * or other nodes attached to the root node. Ultimately, put some * link to resources in the OF node. - * WARNING: out_resource->name should be initialized before calling this - * function. */ struct resource* __openfirmware request_OF_resource(struct device_node* node, int index, const char* name_postfix) @@ -1276,7 +1285,7 @@ release_OF_resource(struct device_node* node, int index) { struct pci_dev* pcidev; u8 pci_bus, pci_devfn; - unsigned long iomask; + unsigned long iomask, start, end; struct device_node* nd; struct resource* parent; struct resource *res = NULL; @@ -1305,18 +1314,23 @@ release_OF_resource(struct device_node* node, int index) if (pcidev) parent = find_parent_pci_resource(pcidev, &node->addrs[index]); if (!parent) { - printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", + printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", node->name); return -ENODEV; } - /* Find us in the parent */ + /* Find us in the parent and its childs */ res = parent->child; + start = node->addrs[index].address; + end = start + node->addrs[index].size - 1; while (res) { - if (res->start == node->addrs[index].address && - res->end == (res->start + node->addrs[index].size - 1)) + if (res->start == start && res->end == end && + (res->flags & IORESOURCE_BUSY)) break; - res = res->sibling; + if (res->start <= start && res->end >= end) + res = res->child; + else + res = res->sibling; } if (!res) return -ENODEV; diff --git a/arch/ppc/syslib/prom_init.c b/arch/ppc/syslib/prom_init.c index b49620cd6346..e114c595b149 100644 --- a/arch/ppc/syslib/prom_init.c +++ b/arch/ppc/syslib/prom_init.c @@ -260,6 +260,74 @@ prom_next_node(phandle *nodep) } } +#ifdef CONFIG_POWER4 +/* + * Set up a hash table with a set of entries in it to map the + * first 64MB of RAM. This is used on 64-bit machines since + * some of them don't have BATs. + */ + +static inline void make_pte(unsigned long htab, unsigned int hsize, + unsigned int va, unsigned int pa, int mode) +{ + unsigned int *pteg; + unsigned int hash, i, vsid; + + vsid = ((va >> 28) * 0x111) << 12; + hash = ((va ^ vsid) >> 5) & 0x7fff80; + pteg = (unsigned int *)(htab + (hash & (hsize - 1))); + for (i = 0; i < 8; ++i, pteg += 4) { + if ((pteg[1] & 1) == 0) { + pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; + pteg[3] = pa | mode; + break; + } + } +} + +extern unsigned long _SDR1; +extern PTE *Hash; +extern unsigned long Hash_size; + +static void __init +prom_alloc_htab(void) +{ + unsigned int hsize; + unsigned long htab; + unsigned int addr; + + /* + * Because of OF bugs we can't use the "claim" client + * interface to allocate memory for the hash table. + * This code is only used on 64-bit PPCs, and the only + * 64-bit PPCs at the moment are RS/6000s, and their + * OF is based at 0xc00000 (the 12M point), so we just + * arbitrarily use the 0x800000 - 0xc00000 region for the + * hash table. + * -- paulus. + */ + hsize = 4 << 20; /* POWER4 has no BATs */ + htab = (8 << 20); + call_prom("claim", 3, 1, htab, hsize, 0); + Hash = (void *)(htab + KERNELBASE); + Hash_size = hsize; + _SDR1 = htab + __ilog2(hsize) - 18; + + /* + * Put in PTEs for the first 64MB of RAM + */ + memset((void *)htab, 0, hsize); + for (addr = 0; addr < 0x4000000; addr += 0x1000) + make_pte(htab, hsize, addr + KERNELBASE, addr, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); +#if 0 /* DEBUG stuff mapping the SCC */ + make_pte(htab, hsize, 0x80013000, 0x80013000, + _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); +#endif +} +#endif /* CONFIG_POWER4 */ + + /* * If we have a display that we don't know how to drive, * we will want to try to execute OF's open method for it @@ -434,13 +502,30 @@ setup_disp_fake_bi(ihandle dp) address += 0x1000; #ifdef CONFIG_POWER4 - extern int boot_text_mapped; - btext_setup_display(width, height, depth, pitch, address); - boot_text_mapped = 0; -#else +#if CONFIG_TASK_SIZE > 0x80000000 +#error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5 +#endif + { + extern boot_infos_t disp_bi; + unsigned long va, pa, i, offset; + va = 0x90000000; + pa = address & 0xfffff000ul; + offset = address & 0x00000fff; + + for (i=0; i<0x4000; i++) { + make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa, + _PAGE_ACCESSED | _PAGE_NO_CACHE | + _PAGE_GUARDED | PP_RWXX); + va += 0x1000; + pa += 0x1000; + } + btext_setup_display(width, height, depth, pitch, 0x90000000 | offset); + disp_bi.dispDeviceBase = (u8 *)address; + } +#else /* CONFIG_POWER4 */ btext_setup_display(width, height, depth, pitch, address); btext_prepare_BAT(); -#endif +#endif /* CONFIG_POWER4 */ #endif /* CONFIG_BOOTX_TEXT */ } @@ -648,72 +733,6 @@ prom_hold_cpus(unsigned long mem) } } -#ifdef CONFIG_POWER4 -/* - * Set up a hash table with a set of entries in it to map the - * first 64MB of RAM. This is used on 64-bit machines since - * some of them don't have BATs. - * We assume the PTE will fit in the primary PTEG. - */ - -static inline void make_pte(unsigned long htab, unsigned int hsize, - unsigned int va, unsigned int pa, int mode) -{ - unsigned int *pteg; - unsigned int hash, i, vsid; - - vsid = ((va >> 28) * 0x111) << 12; - hash = ((va ^ vsid) >> 5) & 0x7fff80; - pteg = (unsigned int *)(htab + (hash & (hsize - 1))); - for (i = 0; i < 8; ++i, pteg += 4) { - if ((pteg[1] & 1) == 0) { - pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; - pteg[3] = pa | mode; - break; - } - } -} - -extern unsigned long _SDR1; -extern PTE *Hash; -extern unsigned long Hash_size; - -static void __init -prom_alloc_htab(void) -{ - unsigned int hsize; - unsigned long htab; - unsigned int addr; - - /* - * Because of OF bugs we can't use the "claim" client - * interface to allocate memory for the hash table. - * This code is only used on 64-bit PPCs, and the only - * 64-bit PPCs at the moment are RS/6000s, and their - * OF is based at 0xc00000 (the 12M point), so we just - * arbitrarily use the 0x800000 - 0xc00000 region for the - * hash table. - * -- paulus. - */ - hsize = 4 << 20; /* POWER4 has no BATs */ - htab = (8 << 20); - call_prom("claim", 3, 1, htab, hsize, 0); - Hash = (void *)(htab + KERNELBASE); - Hash_size = hsize; - _SDR1 = htab + __ilog2(hsize) - 18; - - /* - * Put in PTEs for the first 64MB of RAM - */ - cacheable_memzero((void *)htab, hsize); - for (addr = 0; addr < 0x4000000; addr += 0x1000) - make_pte(htab, hsize, addr + KERNELBASE, addr, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); - make_pte(htab, hsize, 0x80013000, 0x80013000, - _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); -} -#endif /* CONFIG_POWER4 */ - static void __init prom_instantiate_rtas(void) { @@ -836,9 +855,10 @@ prom_init(int r3, int r4, prom_entry pp) * 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) + if (offset == 0) { + prom_print("(already at 0xc0000000) phys=0\n"); phys = 0; - else if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu", + } else if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu", &prom_mmu, sizeof(prom_mmu)) <= 0) { prom_print(" no MMU found\n"); } else if ((int)call_prom_ret("call-method", 4, 4, result, "translate", |
