summaryrefslogtreecommitdiff
path: root/arch/ppc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc')
-rw-r--r--arch/ppc/Kconfig179
-rw-r--r--arch/ppc/kernel/Makefile3
-rw-r--r--arch/ppc/kernel/cpu_setup_6xx.S44
-rw-r--r--arch/ppc/kernel/cpu_setup_power4.S182
-rw-r--r--arch/ppc/kernel/head.S79
-rw-r--r--arch/ppc/kernel/idle_power4.S34
-rw-r--r--arch/ppc/kernel/misc.S4
-rw-r--r--arch/ppc/kernel/pci.c103
-rw-r--r--arch/ppc/kernel/ppc_ksyms.c15
-rw-r--r--arch/ppc/kernel/setup.c34
-rw-r--r--arch/ppc/kernel/smp-tbsync.c181
-rw-r--r--arch/ppc/kernel/smp.c52
-rw-r--r--arch/ppc/mm/hashtable.S41
-rw-r--r--arch/ppc/mm/init.c2
-rw-r--r--arch/ppc/mm/pgtable.c10
-rw-r--r--arch/ppc/mm/ppc_mmu.c7
-rw-r--r--arch/ppc/mm/tlb.c20
-rw-r--r--arch/ppc/platforms/Makefile3
-rw-r--r--arch/ppc/platforms/pmac_backlight.c11
-rw-r--r--arch/ppc/platforms/pmac_cpufreq.c186
-rw-r--r--arch/ppc/platforms/pmac_feature.c683
-rw-r--r--arch/ppc/platforms/pmac_low_i2c.c513
-rw-r--r--arch/ppc/platforms/pmac_nvram.c523
-rw-r--r--arch/ppc/platforms/pmac_pci.c634
-rw-r--r--arch/ppc/platforms/pmac_pic.c61
-rw-r--r--arch/ppc/platforms/pmac_setup.c23
-rw-r--r--arch/ppc/platforms/pmac_smp.c97
-rw-r--r--arch/ppc/platforms/pmac_time.c8
-rw-r--r--arch/ppc/syslib/Makefile1
-rw-r--r--arch/ppc/syslib/of_device.c3
-rw-r--r--arch/ppc/syslib/open_pic.c17
-rw-r--r--arch/ppc/syslib/open_pic2.c716
-rw-r--r--arch/ppc/syslib/prom.c36
-rw-r--r--arch/ppc/syslib/prom_init.c166
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 = &macrisc_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",