Commit a7fdd90b authored by Paul Mackerras's avatar Paul Mackerras

[PATCH] ppc: Remove powermac support from ARCH=ppc

This makes it possible to build kernels for PReP and/or CHRP
with ARCH=ppc by removing the (non-building) powermac support.
It's now also possible to select PReP and CHRP independently.
Powermac users should now build with ARCH=powerpc instead of
ARCH=ppc.  (This does mean that it is no longer possible to
build a 32-bit kernel for a G5.)
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent e8625d46
......@@ -58,11 +58,11 @@ config 6xx
help
There are four types of PowerPC chips supported. The more common
types (601, 603, 604, 740, 750, 7400), the Motorola embedded
versions (821, 823, 850, 855, 860, 52xx, 82xx, 83xx), the IBM embedded
versions (403 and 405) and the high end 64 bit Power processors
(POWER 3, POWER4, and IBM 970 also known as G5)
versions (821, 823, 850, 855, 860, 52xx, 82xx, 83xx), the IBM
embedded versions (403 and 405) and the POWER3 processor.
(For support for more recent 64-bit processors, set ARCH=powerpc.)
Unless you are building a kernel for one of the embedded processor
systems, 64 bit IBM RS/6000 or an Apple G5, choose 6xx.
systems or a POWER3-based IBM RS/6000, choose 6xx.
Note that the kernel runs in 32-bit mode even on 64-bit chips.
Also note that because the 52xx, 82xx, & 83xx family has a 603e core,
specific support for that chipset is asked later on.
......@@ -77,10 +77,6 @@ config POWER3
select PPC_FPU
bool "POWER3"
config POWER4
select PPC_FPU
bool "POWER4 and 970 (G5)"
config 8xx
bool "8xx"
......@@ -123,7 +119,7 @@ config PHYS_64BIT
config ALTIVEC
bool "AltiVec Support"
depends on 6xx || POWER4
depends on 6xx
depends on !8260 && !83xx
---help---
This option enables kernel support for the Altivec extensions to the
......@@ -235,18 +231,9 @@ config KEXEC
source "drivers/cpufreq/Kconfig"
config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks"
depends on CPU_FREQ && ADB_PMU
select CPU_FREQ_TABLE
help
This adds support for frequency switching on Apple PowerBooks,
this currently includes some models of iBook & Titanium
PowerBook.
config PPC601_SYNC_FIX
bool "Workarounds for PPC601 bugs"
depends on 6xx && (PPC_PREP || PPC_PMAC)
depends on 6xx && PPC_PREP
help
Some versions of the PPC601 (the first PowerPC chip) have bugs which
mean that extra synchronization instructions are required near
......@@ -258,26 +245,17 @@ config PPC601_SYNC_FIX
If in doubt, say Y here.
config HOTPLUG_CPU
bool "Support for enabling/disabling CPUs"
depends on SMP && HOTPLUG && EXPERIMENTAL && PPC_PMAC
---help---
Say Y here to be able to disable and re-enable individual
CPUs at runtime on SMP machines.
Say N if you are unsure.
source arch/ppc/platforms/4xx/Kconfig
source arch/ppc/platforms/85xx/Kconfig
config PPC64BRIDGE
bool
depends on POWER3 || POWER4
depends on POWER3
default y
config PPC_STD_MMU
bool
depends on 6xx || POWER3 || POWER4
depends on 6xx || POWER3
default y
config NOT_COHERENT_CACHE
......@@ -505,7 +483,7 @@ endchoice
choice
prompt "Machine Type"
depends on 6xx || POWER3 || POWER4
depends on 6xx || POWER3
default PPC_MULTIPLATFORM
---help---
Linux currently supports several different kinds of PowerPC-based
......@@ -516,11 +494,15 @@ choice
Platform) machines (including all of the recent IBM RS/6000 and
pSeries machines), and several embedded PowerPC systems containing
4xx, 6xx, 7xx, 8xx, 74xx, and 82xx processors. Currently, the
default option is to build a kernel which works on the first three.
default option is to build a kernel which works on PReP and CHRP.
Select CHRP/PowerMac/PReP if configuring for an IBM RS/6000 or
pSeries machine, a Power Macintosh (including iMacs, iBooks and
Powerbooks), or a PReP machine.
Note that support for Apple machines is now only available with
ARCH=powerpc, and has been removed from this menu. If you wish
to build a kernel for an Apple machine, exit this configuration
process and re-run it with ARCH=powerpc.
Select CHRP/PReP if configuring for an IBM RS/6000 or
pSeries machine, or a PReP machine.
Select Gemini if configuring for a Synergy Microsystems' Gemini
series Single Board Computer. More information is available at:
......@@ -530,7 +512,7 @@ choice
available at: <http://linux-apus.sourceforge.net/>.
config PPC_MULTIPLATFORM
bool "CHRP/PowerMac/PReP"
bool "CHRP/PReP"
config APUS
bool "Amiga-APUS"
......@@ -768,25 +750,14 @@ config CPM2
on it (826x, 827x, 8560).
config PPC_CHRP
bool
bool "Support for CHRP (Common Hardware Reference Platform) machines"
depends on PPC_MULTIPLATFORM
select PPC_I8259
select PPC_INDIRECT_PCI
default y
config PPC_PMAC
bool
depends on PPC_MULTIPLATFORM
select PPC_INDIRECT_PCI
default y
config PPC_PMAC64
bool
depends on PPC_PMAC && POWER4
default y
config PPC_PREP
bool
bool "Support for PReP (PowerPC Reference Platform) machines"
depends on PPC_MULTIPLATFORM
select PPC_I8259
select PPC_INDIRECT_PCI
......@@ -794,7 +765,7 @@ config PPC_PREP
config PPC_OF
bool
depends on PPC_PMAC || PPC_CHRP
depends on PPC_CHRP
default y
config PPC_GEN550
......@@ -1166,7 +1137,7 @@ config ISA
config GENERIC_ISA_DMA
bool
depends on POWER3 || POWER4 || 6xx && !CPM2
depends on POWER3 || 6xx && !CPM2
default y
config PPC_I8259
......
......@@ -18,7 +18,7 @@ BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd
bootdir-y := simple
bootdir-$(CONFIG_PPC_OF) += openfirmware
subdir-y := lib common images
subdir-$(CONFIG_PPC_OF) += of1275
subdir-$(CONFIG_PPC_MULTIPLATFORM) += of1275
# for cleaning
subdir- += simple openfirmware
......
......@@ -21,26 +21,16 @@ bootlib := $(boot)/lib
of1275 := $(boot)/of1275
images := $(boot)/images
OBJCOPY_ARGS := -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment
COFF_LD_ARGS := -T $(srctree)/$(boot)/ld.script -e _start -Ttext 0x00500000 \
-Bstatic
CHRP_LD_ARGS := -T $(srctree)/$(boot)/ld.script -e _start -Ttext 0x00800000
NEWWORLD_LD_ARGS:= -T $(srctree)/$(boot)/ld.script -e _start -Ttext 0x01000000
COMMONOBJS := start.o misc.o common.o
COFFOBJS := coffcrt0.o $(COMMONOBJS) coffmain.o
CHRPOBJS := crt0.o $(COMMONOBJS) chrpmain.o
NEWWORLDOBJS := crt0.o $(COMMONOBJS) newworldmain.o
targets := $(COFFOBJS) $(CHRPOBJS) $(NEWWORLDOBJS) dummy.o
COFFOBJS := $(addprefix $(obj)/, $(COFFOBJS))
targets := $(CHRPOBJS) dummy.o
CHRPOBJS := $(addprefix $(obj)/, $(CHRPOBJS))
NEWWORLDOBJS := $(addprefix $(obj)/, $(NEWWORLDOBJS))
LIBS := lib/lib.a $(bootlib)/lib.a $(of1275)/lib.a $(common)/lib.a
HACKCOFF := $(utils)/hack-coff
ifdef CONFIG_SMP
END := .smp
endif
......@@ -72,56 +62,11 @@ targets += image.initrd.o
$(obj)/image.initrd.o: $(obj)/image.o $(images)/ramdisk.image.gz FORCE
$(call if_changed,genimage-initrd)
# Create the note section for New-World PowerMacs.
quiet_cmd_mknote = MKNOTE $@
cmd_mknote = $(utils)/mknote > $@
targets += note
$(obj)/note: $(utils)/mknote FORCE
$(call if_changed,mknote)
$(obj)/coffcrt0.o: EXTRA_AFLAGS := -DXCOFF
targets += coffcrt0.o crt0.o
$(obj)/coffcrt0.o $(obj)/crt0.o: $(common)/crt0.S FORCE
targets += crt0.o
$(obj)/crt0.o: $(common)/crt0.S FORCE
$(call if_changed_dep,as_o_S)
quiet_cmd_gencoffb = COFF $@
cmd_gencoffb = $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) $< $(LIBS) && \
$(OBJCOPY) $@ $@ -R .comment $(del-ramdisk-sec)
targets += coffboot
$(obj)/coffboot: $(obj)/image.o $(COFFOBJS) $(LIBS) $(srctree)/$(boot)/ld.script FORCE
$(call if_changed,gencoffb)
targets += coffboot.initrd
$(obj)/coffboot.initrd: $(obj)/image.initrd.o $(COFFOBJS) $(LIBS) \
$(srctree)/$(boot)/ld.script FORCE
$(call if_changed,gencoffb)
quiet_cmd_gen-coff = COFF $@
cmd_gen-coff = $(OBJCOPY) $(OBJCOPY_ARGS) $< $@ && \
$(HACKCOFF) $@ && \
ln -sf $(notdir $@) $(images)/zImage$(initrd).pmac
$(images)/vmlinux.coff: $(obj)/coffboot
$(call cmd,gen-coff)
$(images)/vmlinux.initrd.coff: $(obj)/coffboot.initrd
$(call cmd,gen-coff)
quiet_cmd_gen-elf-pmac = ELF $@
cmd_gen-elf-pmac = $(LD) $(NEWWORLD_LD_ARGS) -o $@ \
$(NEWWORLDOBJS) $(LIBS) $< && \
$(OBJCOPY) $@ $@ --add-section=.note=$(obj)/note \
-R .comment $(del-ramdisk-sec)
$(images)/vmlinux.elf-pmac: $(obj)/image.o $(NEWWORLDOBJS) $(LIBS) \
$(obj)/note $(srctree)/$(boot)/ld.script
$(call cmd,gen-elf-pmac)
$(images)/vmlinux.initrd.elf-pmac: $(obj)/image.initrd.o $(NEWWORLDOBJS) \
$(LIBS) $(obj)/note \
$(srctree)/$(boot)/ld.script
$(call cmd,gen-elf-pmac)
quiet_cmd_gen-chrp = CHRP $@
cmd_gen-chrp = $(LD) $(CHRP_LD_ARGS) -o $@ $(CHRPOBJS) $< $(LIBS) && \
$(OBJCOPY) $@ $@ -R .comment $(del-ramdisk-sec)
......@@ -139,46 +84,23 @@ $(images)/zImage.chrp-rs6k $(images)/zImage.initrd.chrp-rs6k: \
%-rs6k: %
$(call cmd,addnote)
quiet_cmd_gen-miboot = GEN $@
cmd_gen-miboot = $(OBJCOPY) $(OBJCOPY_ARGS) \
--add-section=$1=$(word 2, $^) $< $@
$(images)/miboot.image: $(obj)/dummy.o $(images)/vmlinux.gz
$(call cmd,gen-miboot,image)
$(images)/miboot.initrd.image: $(images)/miboot.image $(images)/ramdisk.image.gz
$(call cmd,gen-miboot,initrd)
# The targets used on the make command-line
.PHONY: zImage zImage.initrd
zImage: $(images)/vmlinux.coff \
$(images)/vmlinux.elf-pmac \
$(images)/zImage.chrp \
$(images)/zImage.chrp-rs6k \
$(images)/miboot.image
zImage: $(images)/zImage.chrp \
$(images)/zImage.chrp-rs6k
@echo ' kernel: $@ is ready ($<)'
zImage.initrd: $(images)/vmlinux.initrd.coff \
$(images)/vmlinux.initrd.elf-pmac \
$(images)/zImage.initrd.chrp \
$(images)/zImage.initrd.chrp-rs6k \
$(images)/miboot.initrd.image
zImage.initrd: $(images)/zImage.initrd.chrp \
$(images)/zImage.initrd.chrp-rs6k
@echo ' kernel: $@ is ready ($<)'
TFTPIMAGE := /tftpboot/zImage
.PHONY: znetboot znetboot.initrd
znetboot: $(images)/vmlinux.coff \
$(images)/vmlinux.elf-pmac \
$(images)/zImage.chrp
cp $(images)/vmlinux.coff $(TFTPIMAGE).pmac$(END)
cp $(images)/vmlinux.elf-pmac $(TFTPIMAGE).pmac$(END).elf
znetboot: $(images)/zImage.chrp
cp $(images)/zImage.chrp $(TFTPIMAGE).chrp$(END)
@echo ' kernel: $@ is ready ($<)'
znetboot.initrd:$(images)/vmlinux.initrd.coff \
$(images)/vmlinux.initrd.elf-pmac \
$(images)/zImage.initrd.chrp
cp $(images)/vmlinux.initrd.coff $(TFTPIMAGE).pmac$(END)
cp $(images)/vmlinux.initrd.elf-pmac $(TFTPIMAGE).pmac$(END).elf
znetboot.initrd:$(images)/zImage.initrd.chrp
cp $(images)/zImage.initrd.chrp $(TFTPIMAGE).chrp$(END)
@echo ' kernel: $@ is ready ($<)'
/*
* Copyright (C) Paul Mackerras 1997.
*
* 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/string.h>
#include <asm/processor.h>
#include <asm/page.h>
#include "nonstdio.h"
#include "of1275.h"
/* Passed from the linker */
extern char __image_begin, __image_end;
extern char __ramdisk_begin[], __ramdisk_end;
extern char _start, _end;
extern char image_data[], initrd_data[];
extern int initrd_len, image_len;
extern unsigned int heap_max;
extern void flush_cache(void *start, unsigned int len);
extern void gunzip(void *, int, unsigned char *, int *);
extern void make_bi_recs(unsigned long addr, char *name, unsigned int mach,
unsigned int progend);
extern void setup_bats(unsigned long start);
char *avail_ram;
char *begin_avail, *end_avail;
char *avail_high;
#define SCRATCH_SIZE (128 << 10)
static char heap[SCRATCH_SIZE];
static unsigned long ram_start = 0;
static unsigned long ram_end = 0x1000000;
static unsigned long prog_start = 0x800000;
static unsigned long prog_size = 0x700000;
typedef void (*kernel_start_t)(int, int, void *);
void boot(int a1, int a2, void *prom)
{
unsigned sa, len;
void *dst;
unsigned char *im;
unsigned initrd_start, initrd_size;
printf("coffboot starting: loaded at 0x%p\n", &_start);
setup_bats(ram_start);
initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin);
if (initrd_size) {
initrd_start = (ram_end - initrd_size) & ~0xFFF;
a1 = initrd_start;
a2 = initrd_size;
claim(initrd_start, ram_end - initrd_start, 0);
printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r",
initrd_start, (char *)(&__ramdisk_begin), initrd_size);
memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size);
prog_size = initrd_start - prog_start;
} else
a2 = 0xdeadbeef;
im = (char *)(&__image_begin);
len = (char *)(&__image_end) - (char *)(&__image_begin);
/* claim 4MB starting at PROG_START */
claim(prog_start, prog_size, 0);
map(prog_start, prog_start, prog_size);
dst = (void *) prog_start;
if (im[0] == 0x1f && im[1] == 0x8b) {
/* set up scratch space */
begin_avail = avail_high = avail_ram = heap;
end_avail = heap + sizeof(heap);
printf("heap at 0x%p\n", avail_ram);
printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len);
gunzip(dst, prog_size, im, &len);
printf("done %u bytes\n", len);
printf("%u bytes of heap consumed, max in use %u\n",
avail_high - begin_avail, heap_max);
} else {
memmove(dst, im, len);
}
flush_cache(dst, len);
make_bi_recs(((unsigned long) dst + len), "coffboot", _MACH_Pmac,
(prog_start + prog_size));
sa = (unsigned long)prog_start;
printf("start address = 0x%x\n", sa);
(*(kernel_start_t)sa)(a1, a2, prom);
printf("returned?\n");
pause();
}
/*
* Copyright (C) Paul Mackerras 1997.
*
* 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/string.h>
#include "nonstdio.h"
#include "of1275.h"
#include <asm/processor.h>
#include <asm/page.h>
/* Passed from the linker */
extern char __image_begin, __image_end;
extern char __ramdisk_begin[], __ramdisk_end;
extern char _start, _end;
extern unsigned int heap_max;
extern void flush_cache(void *start, unsigned int len);
extern void gunzip(void *, int, unsigned char *, int *);
extern void make_bi_recs(unsigned long addr, char *name, unsigned int mach,
unsigned int progend);
char *avail_ram;
char *begin_avail, *end_avail;
char *avail_high;
#define RAM_END (16 << 20)
#define PROG_START 0x00010000
#define PROG_SIZE 0x007f0000
#define SCRATCH_SIZE (128 << 10)
typedef void (*kernel_start_t)(int, int, void *);
void boot(int a1, int a2, void *prom)
{
unsigned sa, len;
void *dst;
unsigned char *im;
unsigned initrd_start, initrd_size;
printf("chrpboot starting: loaded at 0x%p\n", &_start);
initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin);
if (initrd_size) {
initrd_start = (RAM_END - initrd_size) & ~0xFFF;
a1 = initrd_start;
a2 = initrd_size;
claim(initrd_start, RAM_END - initrd_start, 0);
printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r",
initrd_start, (char *)(&__ramdisk_begin), initrd_size);
memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size);
} else
a2 = 0xdeadbeef;
im = (char *)(&__image_begin);
len = (char *)(&__image_end) - (char *)(&__image_begin);
/* claim 3MB starting at PROG_START */
claim(PROG_START, PROG_SIZE, 0);
dst = (void *) PROG_START;
if (im[0] == 0x1f && im[1] == 0x8b) {
/* claim some memory for scratch space */
avail_ram = (char *) claim(0, SCRATCH_SIZE, 0x10);
begin_avail = avail_high = avail_ram;
end_avail = avail_ram + SCRATCH_SIZE;
printf("heap at 0x%p\n", avail_ram);
printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len);
gunzip(dst, PROG_SIZE, im, &len);
printf("done %u bytes\n", len);
printf("%u bytes of heap consumed, max in use %u\n",
avail_high - begin_avail, heap_max);
release(begin_avail, SCRATCH_SIZE);
} else {
memmove(dst, im, len);
}
flush_cache(dst, len);
make_bi_recs(((unsigned long) dst + len), "chrpboot", _MACH_Pmac,
(PROG_START + PROG_SIZE));
sa = (unsigned long)PROG_START;
printf("start address = 0x%x\n", sa);
(*(kernel_start_t)sa)(a1, a2, prom);
printf("returned?\n");
pause();
}
......@@ -9,7 +9,6 @@ extra-$(CONFIG_44x) := head_44x.o
extra-$(CONFIG_FSL_BOOKE) := head_fsl_booke.o
extra-$(CONFIG_8xx) := head_8xx.o
extra-$(CONFIG_6xx) += idle_6xx.o
extra-$(CONFIG_POWER4) += idle_power4.o
extra-y += vmlinux.lds
obj-y := entry.o traps.o idle.o time.o misc.o \
......@@ -17,7 +16,6 @@ obj-y := entry.o traps.o idle.o time.o misc.o \
ppc_htab.o
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
obj-$(CONFIG_POWER4) += cpu_setup_power4.o
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-mapping.o
obj-$(CONFIG_PCI) += pci.o
......
......@@ -204,78 +204,6 @@ _GLOBAL(call_setup_cpu)
mtctr r5
bctr
#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
* other place (as most of the cpufreq code in via-pmu
*/
_GLOBAL(low_choose_750fx_pll)
/* Clear MSR:EE */
mfmsr r7
rlwinm r0,r7,0,17,15
mtmsr r0
/* If switching to PLL1, disable HID0:BTIC */
cmplwi cr0,r3,0
beq 1f
mfspr r5,SPRN_HID0
rlwinm r5,r5,0,27,25
sync
mtspr SPRN_HID0,r5
isync
sync
1:
/* Calc new HID1 value */
mfspr r4,SPRN_HID1 /* Build a HID1:PS bit from parameter */
rlwinm r5,r3,16,15,15 /* Clear out HID1:PS from value read */
rlwinm r4,r4,0,16,14 /* Could have I used rlwimi here ? */
or r4,r4,r5
mtspr SPRN_HID1,r4
/* Store new HID1 image */
rlwinm r6,r1,0,0,18
lwz r6,TI_CPU(r6)
slwi r6,r6,2
addis r6,r6,nap_save_hid1@ha
stw r4,nap_save_hid1@l(r6)
/* If switching to PLL0, enable HID0:BTIC */
cmplwi cr0,r3,0
bne 1f
mfspr r5,SPRN_HID0
ori r5,r5,HID0_BTIC
sync
mtspr SPRN_HID0,r5
isync
sync
1:
/* Return */
mtmsr r7
blr
_GLOBAL(low_choose_7447a_dfs)
/* Clear MSR:EE */
mfmsr r7
rlwinm r0,r7,0,17,15
mtmsr r0
/* Calc new HID1 value */
mfspr r4,SPRN_HID1
insrwi r4,r3,1,9 /* insert parameter into bit 9 */
sync
mtspr SPRN_HID1,r4
sync
isync
/* Return */
mtmsr r7
blr
#endif /* CONFIG_CPU_FREQ_PMAC && CONFIG_6xx */
/*
* complement mask on the msr then "or" some values on.
* _nmask_and_or_msr(nmask, value_to_or)
......
/*
* Common pmac/prep/chrp pci routines. -- Cort
* Common prep/chrp pci routines. -- Cort
*/
#include <linux/config.h>
......@@ -50,8 +50,7 @@ static void fixup_cpc710_pci64(struct pci_dev* dev);
static u8* pci_to_OF_bus_map;
#endif
/* By default, we don't re-assign bus numbers. We do this only on
* some pmacs
/* By default, we don't re-assign bus numbers.
*/
int pci_assign_all_buses;
......@@ -780,17 +779,6 @@ 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)
......@@ -1040,216 +1028,6 @@ void pcibios_add_platform_entries(struct pci_dev *pdev)
}
#ifdef CONFIG_PPC_PMAC
/*
* This set of routines checks for PCI<->PCI bridges that have closed
* IO resources and have child devices. It tries to re-open an IO
* window on them.
*
* This is a _temporary_ fix to workaround a problem with Apple's OF
* closing IO windows on P2P bridges when the OF drivers of cards
* below this bridge don't claim any IO range (typically ATI or
* Adaptec).
*
* A more complete fix would be to use drivers/pci/setup-bus.c, which
* involves a working pcibios_fixup_pbus_ranges(), some more care about
* ordering when creating the host bus resources, and maybe a few more
* minor tweaks
*/
/* Initialize bridges with base/limit values we have collected */
static void __init
do_update_p2p_io_resource(struct pci_bus *bus, int enable_vga)
{
struct pci_dev *bridge = bus->self;
struct pci_controller* hose = (struct pci_controller *)bridge->sysdata;
u32 l;
u16 w;
struct resource res;
if (bus->resource[0] == NULL)
return;
res = *(bus->resource[0]);
DBG("Remapping Bus %d, bridge: %s\n", bus->number, pci_name(bridge));
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);
/* Set up the top and bottom of the PCI I/O segment for this bus. */
pci_read_config_dword(bridge, PCI_IO_BASE, &l);
l &= 0xffff000f;
l |= (res.start >> 8) & 0x00f0;
l |= res.end & 0xf000;
pci_write_config_dword(bridge, PCI_IO_BASE, l);
if ((l & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
l = (res.start >> 16) | (res.end & 0xffff0000);
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, l);
}
pci_read_config_word(bridge, PCI_COMMAND, &w);
w |= PCI_COMMAND_IO;
pci_write_config_word(bridge, PCI_COMMAND, w);
#if 0 /* Enabling this causes XFree 4.2.0 to hang during PCI probe */
if (enable_vga) {
pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &w);
w |= PCI_BRIDGE_CTL_VGA;
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, w);
}
#endif
}
/* This function is pretty basic and actually quite broken for the
* general case, it's enough for us right now though. It's supposed
* to tell us if we need to open an IO range at all or not and what
* size.
*/
static int __init
check_for_io_childs(struct pci_bus *bus, struct resource* res, int *found_vga)
{
struct pci_dev *dev;
int i;
int rc = 0;
#define push_end(res, size) do { unsigned long __sz = (size) ; \
res->end = ((res->end + __sz) / (__sz + 1)) * (__sz + 1) + __sz; \
} while (0)
list_for_each_entry(dev, &bus->devices, bus_list) {
u16 class = dev->class >> 8;
if (class == PCI_CLASS_DISPLAY_VGA ||
class == PCI_CLASS_NOT_DEFINED_VGA)
*found_vga = 1;
if (class >> 8 == PCI_BASE_CLASS_BRIDGE && dev->subordinate)
rc |= check_for_io_childs(dev->subordinate, res, found_vga);
if (class == PCI_CLASS_BRIDGE_CARDBUS)
push_end(res, 0xfff);
for (i=0; i<PCI_NUM_RESOURCES; i++) {
struct resource *r;
unsigned long r_size;
if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI
&& i >= PCI_BRIDGE_RESOURCES)
continue;
r = &dev->resource[i];
r_size = r->end - r->start;
if (r_size < 0xfff)
r_size = 0xfff;
if (r->flags & IORESOURCE_IO && (r_size) != 0) {
rc = 1;
push_end(res, r_size);
}
}
}
return rc;
}
/* Here we scan all P2P bridges of a given level that have a closed
* IO window. Note that the test for the presence of a VGA card should
* be improved to take into account already configured P2P bridges,
* currently, we don't see them and might end up configuring 2 bridges
* with VGA pass through enabled
*/
static void __init
do_fixup_p2p_level(struct pci_bus *bus)
{
struct pci_bus *b;
int i, parent_io;
int has_vga = 0;
for (parent_io=0; parent_io<4; parent_io++)
if (bus->resource[parent_io]
&& bus->resource[parent_io]->flags & IORESOURCE_IO)
break;
if (parent_io >= 4)
return;
list_for_each_entry(b, &bus->children, node) {
struct pci_dev *d = b->self;
struct pci_controller* hose = (struct pci_controller *)d->sysdata;
struct resource *res = b->resource[0];
struct resource tmp_res;
unsigned long max;
int found_vga = 0;
memset(&tmp_res, 0, sizeof(tmp_res));
tmp_res.start = bus->resource[parent_io]->start;
/* We don't let low addresses go through that closed P2P bridge, well,
* that may not be necessary but I feel safer that way
*/
if (tmp_res.start == 0)
tmp_res.start = 0x1000;
if (!list_empty(&b->devices) && res && res->flags == 0 &&
res != bus->resource[parent_io] &&
(d->class >> 8) == PCI_CLASS_BRIDGE_PCI &&
check_for_io_childs(b, &tmp_res, &found_vga)) {
u8 io_base_lo;
printk(KERN_INFO "Fixing up IO bus %s\n", b->name);
if (found_vga) {
if (has_vga) {
printk(KERN_WARNING "Skipping VGA, already active"
" on bus segment\n");
found_vga = 0;
} else
has_vga = 1;
}
pci_read_config_byte(d, PCI_IO_BASE, &io_base_lo);
if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32)
max = ((unsigned long) hose->io_base_virt
- isa_io_base) + 0xffffffff;
else
max = ((unsigned long) hose->io_base_virt
- isa_io_base) + 0xffff;
*res = tmp_res;
res->flags = IORESOURCE_IO;
res->name = b->name;
/* Find a resource in the parent where we can allocate */
for (i = 0 ; i < 4; i++) {
struct resource *r = bus->resource[i];
if (!r)
continue;
if ((r->flags & IORESOURCE_IO) == 0)
continue;
DBG("Trying to allocate from %08lx, size %08lx from parent"
" res %d: %08lx -> %08lx\n",
res->start, res->end, i, r->start, r->end);
if (allocate_resource(r, res, res->end + 1, res->start, max,
res->end + 1, NULL, NULL) < 0) {
DBG("Failed !\n");
continue;
}
do_update_p2p_io_resource(b, found_vga);
break;
}
}
do_fixup_p2p_level(b);
}
}
static void
pcibios_fixup_p2p_bridges(void)
{
struct pci_bus *b;
list_for_each_entry(b, &pci_root_buses, node)
do_fixup_p2p_level(b);
}
#endif /* CONFIG_PPC_PMAC */
static int __init
pcibios_init(void)
{
......@@ -1290,9 +1068,6 @@ pcibios_init(void)
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
#ifdef CONFIG_PPC_PMAC
pcibios_fixup_p2p_bridges();
#endif /* CONFIG_PPC_PMAC */
pcibios_assign_resources();
/* Call machine dependent post-init code */
......@@ -1722,17 +1497,6 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn)
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;
......
......@@ -34,7 +34,6 @@
#include <asm/system.h>
#include <asm/pci-bridge.h>
#include <asm/irq.h>
#include <asm/pmac_feature.h>
#include <asm/dma.h>
#include <asm/machdep.h>
#include <asm/hw_irq.h>
......@@ -58,7 +57,6 @@ extern void machine_check_exception(struct pt_regs *regs);
extern void alignment_exception(struct pt_regs *regs);
extern void program_check_exception(struct pt_regs *regs);
extern void single_step_exception(struct pt_regs *regs);
extern int pmac_newworld;
extern int sys_sigreturn(struct pt_regs *regs);
long long __ashrdi3(long long, int);
......@@ -213,10 +211,6 @@ EXPORT_SYMBOL(adb_try_handler_change);
EXPORT_SYMBOL(cuda_request);
EXPORT_SYMBOL(cuda_poll);
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_PPC_PMAC
EXPORT_SYMBOL(sys_ctrler);
EXPORT_SYMBOL(pmac_newworld);
#endif
#ifdef CONFIG_PPC_OF
EXPORT_SYMBOL(find_devices);
EXPORT_SYMBOL(find_type_devices);
......@@ -241,9 +235,6 @@ EXPORT_SYMBOL(of_node_put);
#if defined(CONFIG_BOOTX_TEXT)
EXPORT_SYMBOL(btext_update_display);
#endif
#if defined(CONFIG_SCSI) && defined(CONFIG_PPC_PMAC)
EXPORT_SYMBOL(note_scsi_host);
#endif
#ifdef CONFIG_VT
EXPORT_SYMBOL(kd_mksound);
#endif
......
/*
* Common prep/pmac/chrp boot and setup code.
* Common prep/chrp boot and setup code.
*/
#include <linux/config.h>
......@@ -35,7 +35,6 @@
#include <asm/machdep.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/pmac_feature.h>
#include <asm/sections.h>
#include <asm/nvram.h>
#include <asm/xmon.h>
......@@ -55,7 +54,6 @@
extern void platform_init(unsigned long r3, unsigned long r4,
unsigned long r5, unsigned long r6, unsigned long r7);
extern void bootx_init(unsigned long r4, unsigned long phys);
extern void identify_cpu(unsigned long offset, unsigned long cpu);
extern void do_cpu_ftr_fixups(unsigned long offset);
extern void reloc_got2(unsigned long offset);
......@@ -80,8 +78,6 @@ EXPORT_SYMBOL(_machine);
extern void prep_init(unsigned long r3, unsigned long r4,
unsigned long r5, unsigned long r6, unsigned long r7);
extern void pmac_init(unsigned long r3, unsigned long r4,
unsigned long r5, unsigned long r6, unsigned long r7);
extern void chrp_init(unsigned long r3, unsigned long r4,
unsigned long r5, unsigned long r6, unsigned long r7);
......@@ -324,20 +320,15 @@ early_init(int r3, int r4, int r5)
identify_cpu(offset, 0);
do_cpu_ftr_fixups(offset);
#if defined(CONFIG_PPC_MULTIPLATFORM)
#if defined(CONFIG_PPC_OF)
reloc_got2(offset);
/* If we came here from BootX, clear the screen,
* set up some pointers and return. */
if ((r3 == 0x426f6f58) && (r5 == 0))
bootx_init(r4, phys);
/*
* don't do anything on prep
* for now, don't use bootinfo because it breaks yaboot 0.5
* and assume that if we didn't find a magic number, we have OF
*/
else if (*(unsigned long *)(0) != 0xdeadc0de)
if (*(unsigned long *)(0) != 0xdeadc0de)
phys = prom_init(r3, r4, (prom_entry)r5);
reloc_got2(-offset);
......@@ -424,6 +415,7 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
}
#endif
#ifdef CONFIG_PPC_OF
have_of = 1;
/* prom_init has already been called from __start */
......@@ -495,19 +487,17 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
#endif /* CONFIG_ADB */
switch (_machine) {
#ifdef CONFIG_PPC_PMAC
case _MACH_Pmac:
pmac_init(r3, r4, r5, r6, r7);
break;
#endif
#ifdef CONFIG_PPC_CHRP
case _MACH_chrp:
chrp_init(r3, r4, r5, r6, r7);
break;
#endif
}
#endif /* CONFIG_PPC_OF */
}
#endif /* CONFIG_PPC_MULTIPLATFORM */
#ifdef CONFIG_PPC_OF
#ifdef CONFIG_SERIAL_CORE_CONSOLE
extern char *of_stdout_device;
......@@ -564,7 +554,7 @@ static int __init set_preferred_console(void)
}
console_initcall(set_preferred_console);
#endif /* CONFIG_SERIAL_CORE_CONSOLE */
#endif /* CONFIG_PPC_MULTIPLATFORM */
#endif /* CONFIG_PPC_OF */
struct bi_record *find_bootinfo(void)
{
......@@ -747,14 +737,6 @@ void __init setup_arch(char **cmdline_p)
if (ppc_md.init_early)
ppc_md.init_early();
#ifdef CONFIG_PPC_MULTIPLATFORM
/* This could be called "early setup arch", it must be done
* now because xmon need it
*/
if (_machine == _MACH_Pmac)
pmac_feature_init(); /* New cool way */
#endif
#ifdef CONFIG_XMON
xmon_init(1);
if (strstr(cmd_line, "xmon"))
......
......@@ -38,9 +38,6 @@
#include <asm/io.h>
#include <asm/reg.h>
#include <asm/xmon.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
#include <asm/pmc.h>
#ifdef CONFIG_XMON
......@@ -85,12 +82,6 @@ int die(const char * str, struct pt_regs * fp, long err)
int nl = 0;
console_verbose();
spin_lock_irq(&die_lock);
#ifdef CONFIG_PMAC_BACKLIGHT
if (_machine == _MACH_Pmac) {
set_backlight_enable(1);
set_backlight_level(BACKLIGHT_MAX);
}
#endif
printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
#ifdef CONFIG_PREEMPT
printk("PREEMPT ");
......@@ -159,7 +150,7 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
*/
static inline int check_io_access(struct pt_regs *regs)
{
#if defined CONFIG_PPC_PMAC || defined CONFIG_8xx
#if defined CONFIG_8xx
unsigned long msr = regs->msr;
const struct exception_table_entry *entry;
unsigned int *nip = (unsigned int *)regs->nip;
......@@ -196,7 +187,7 @@ static inline int check_io_access(struct pt_regs *regs)
return 1;
}
}
#endif /* CONFIG_PPC_PMAC */
#endif /* CONFIG_8xx */
return 0;
}
......
......@@ -67,10 +67,6 @@ unsigned long ppc_memoffset = PAGE_OFFSET;
int mem_init_done;
int init_bootmem_done;
int boot_mapsize;
#ifdef CONFIG_PPC_PMAC
unsigned long agp_special_page;
EXPORT_SYMBOL(agp_special_page);
#endif
extern char _end[];
extern char etext[], _stext[];
......@@ -423,10 +419,6 @@ void __init mem_init(void)
addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ;
addr += PAGE_SIZE)
SetPageReserved(virt_to_page(addr));
#endif
#ifdef CONFIG_PPC_PMAC
if (agp_special_page)
SetPageReserved(virt_to_page(agp_special_page));
#endif
for (addr = PAGE_OFFSET; addr < (unsigned long)high_memory;
addr += PAGE_SIZE) {
......@@ -463,11 +455,6 @@ void __init mem_init(void)
initpages<< (PAGE_SHIFT-10),
(unsigned long) (totalhigh_pages << (PAGE_SHIFT-10)));
#ifdef CONFIG_PPC_PMAC
if (agp_special_page)
printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page);
#endif
mem_init_done = 1;
}
......@@ -512,22 +499,6 @@ set_phys_avail(unsigned long total_memory)
if (rtas_data)
mem_pieces_remove(&phys_avail, rtas_data, rtas_size, 1);
#endif
#ifdef CONFIG_PPC_PMAC
/* Because of some uninorth weirdness, we need a page of
* memory as high as possible (it must be outside of the
* bus address seen as the AGP aperture). It will be used
* by the r128 DRM driver
*
* FIXME: We need to make sure that page doesn't overlap any of the\
* above. This could be done by improving mem_pieces_find to be able
* to do a backward search from the end of the list.
*/
if (_machine == _MACH_Pmac && find_devices("uni-north-agp")) {
agp_special_page = (total_memory - PAGE_SIZE);
mem_pieces_remove(&phys_avail, agp_special_page, PAGE_SIZE, 0);
agp_special_page = (unsigned long)__va(agp_special_page);
}
#endif /* CONFIG_PPC_PMAC */
}
/* Mark some memory as reserved by removing it from phys_avail. */
......
......@@ -3,26 +3,18 @@
#
# Extra CFLAGS so we don't have to do relative includes
CFLAGS_pmac_setup.o += -Iarch/$(ARCH)/mm
CFLAGS_chrp_setup.o += -Iarch/$(ARCH)/mm
obj-$(CONFIG_APUS) += apus_setup.o
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_low_i2c.o pmac_cache.o
obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o \
chrp_pegasos_eth.o
ifeq ($(CONFIG_PPC_CHRP),y)
obj-$(CONFIG_NVRAM) += chrp_nvram.o
endif
obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_setup.o
ifeq ($(CONFIG_PPC_PMAC),y)
obj-$(CONFIG_NVRAM) += pmac_nvram.o
obj-$(CONFIG_CPU_FREQ_PMAC) += pmac_cpufreq.o
endif
obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o
obj-$(CONFIG_PREP_RESIDUAL) += residual.o
obj-$(CONFIG_PQ2ADS) += pq2ads.o
obj-$(CONFIG_TQM8260) += tqm8260_setup.o
......@@ -47,6 +39,5 @@ obj-$(CONFIG_LITE5200) += lite5200.o
obj-$(CONFIG_EV64360) += ev64360.o
ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_PPC_PMAC) += pmac_smp.o
obj-$(CONFIG_PPC_CHRP) += chrp_smp.o
endif
......@@ -275,7 +275,7 @@ chrp_find_bridges(void)
setup_python(hose, dev);
} else if (is_mot
|| strncmp(model, "Motorola, Grackle", 17) == 0) {
setup_grackle(hose);
setup_indirect_pci(hose, 0xfec00000, 0xfee00000);
} else if (is_longtrail) {
void __iomem *p = ioremap(GG2_PCI_CONFIG_BASE, 0x80000);
hose->ops = &gg2_pci_ops;
......
......@@ -53,6 +53,7 @@
#include <asm/i8259.h>
#include <asm/open_pic.h>
#include <asm/xmon.h>
#include "mem_pieces.h"
unsigned long chrp_get_rtc_time(void);
int chrp_set_rtc_time(unsigned long nowtime);
......@@ -65,7 +66,6 @@ void rtas_display_progress(char *, unsigned short);
void rtas_indicator_progress(char *, unsigned short);
void btext_progress(char *, unsigned short);
extern unsigned long pmac_find_end_of_memory(void);
extern int of_show_percpuinfo(struct seq_file *, int);
int _chrp_type;
......@@ -467,6 +467,75 @@ chrp_init2(void)
ppc_md.progress(" Have fun! ", 0x7777);
}
static struct device_node *memory_node;
static int __init get_mem_prop(char *name, struct mem_pieces *mp)
{
struct reg_property *rp;
int i, s;
unsigned int *ip;
int nac = prom_n_addr_cells(memory_node);
int nsc = prom_n_size_cells(memory_node);
ip = (unsigned int *) get_property(memory_node, name, &s);
if (ip == NULL) {
printk(KERN_ERR "error: couldn't get %s property on /memory\n",
name);
return 0;
}
s /= (nsc + nac) * 4;
rp = mp->regions;
for (i = 0; i < s; ++i, ip += nac+nsc) {
if (nac >= 2 && ip[nac-2] != 0)
continue;
rp->address = ip[nac-1];
if (nsc >= 2 && ip[nac+nsc-2] != 0)
rp->size = ~0U;
else
rp->size = ip[nac+nsc-1];
++rp;
}
mp->n_regions = rp - mp->regions;
/* Make sure the pieces are sorted. */
mem_pieces_sort(mp);
mem_pieces_coalesce(mp);
return 1;
}
static unsigned long __init chrp_find_end_of_memory(void)
{
unsigned long a, total;
struct mem_pieces phys_mem;
/*
* Find out where physical memory is, and check that it
* starts at 0 and is contiguous. It seems that RAM is
* always physically contiguous on Power Macintoshes.
*
* Supporting discontiguous physical memory isn't hard,
* it just makes the virtual <-> physical mapping functions
* more complicated (or else you end up wasting space
* in mem_map).
*/
memory_node = find_devices("memory");
if (memory_node == NULL || !get_mem_prop("reg", &phys_mem)
|| phys_mem.n_regions == 0)
panic("No RAM??");
a = phys_mem.regions[0].address;
if (a != 0)
panic("RAM doesn't start at physical address 0");
total = phys_mem.regions[0].size;
if (phys_mem.n_regions > 1) {
printk("RAM starting at 0x%x is not contiguous\n",
phys_mem.regions[1].address);
printk("Using RAM from 0 to 0x%lx\n", total-1);
}
return total;
}
void __init
chrp_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
......@@ -525,7 +594,7 @@ chrp_init(unsigned long r3, unsigned long r4, unsigned long r5,
ppc_md.get_rtc_time = chrp_get_rtc_time;
ppc_md.calibrate_decr = chrp_calibrate_decr;
ppc_md.find_end_of_memory = pmac_find_end_of_memory;
ppc_md.find_end_of_memory = chrp_find_end_of_memory;
if (rtas_data) {
struct device_node *rtas;
......
......@@ -163,13 +163,75 @@ unsigned long chrp_get_rtc_time(void)
return mktime(year, mon, day, hour, min, sec);
}
/*
* Calibrate the decrementer frequency with the VIA timer 1.
*/
#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */
/* VIA registers */
#define RS 0x200 /* skip between registers */
#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
#define ACR (11*RS) /* Auxiliary control register */
#define IFR (13*RS) /* Interrupt flag register */
/* Bits in ACR */
#define T1MODE 0xc0 /* Timer 1 mode */
#define T1MODE_CONT 0x40 /* continuous interrupts */
/* Bits in IFR and IER */
#define T1_INT 0x40 /* Timer 1 interrupt */
static int __init chrp_via_calibrate_decr(void)
{
struct device_node *vias;
volatile unsigned char __iomem *via;
int count = VIA_TIMER_FREQ_6 / 100;
unsigned int dstart, dend;
vias = find_devices("via-cuda");
if (vias == 0)
vias = find_devices("via");
if (vias == 0 || vias->n_addrs == 0)
return 0;
via = ioremap(vias->addrs[0].address, vias->addrs[0].size);
/* set timer 1 for continuous interrupts */
out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
/* set the counter to a small value */
out_8(&via[T1CH], 2);
/* set the latch to `count' */
out_8(&via[T1LL], count);
out_8(&via[T1LH], count >> 8);
/* wait until it hits 0 */
while ((in_8(&via[IFR]) & T1_INT) == 0)
;
dstart = get_dec();
/* clear the interrupt & wait until it hits 0 again */
in_8(&via[T1CL]);
while ((in_8(&via[IFR]) & T1_INT) == 0)
;
dend = get_dec();
tb_ticks_per_jiffy = (dstart - dend) / ((6 * HZ)/100);
tb_to_us = mulhwu_scale_factor(dstart - dend, 60000);
printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n",
tb_ticks_per_jiffy, dstart - dend);
iounmap(via);
return 1;
}
void __init chrp_calibrate_decr(void)
{
struct device_node *cpu;
unsigned int freq, *fp;
if (via_calibrate_decr())
if (chrp_via_calibrate_decr())
return;
/*
......
/*
* Miscellaneous procedures for dealing with the PowerMac hardware.
* Contains support for the backlight.
*
* Copyright (C) 2000 Benjamin Herrenschmidt
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/reboot.h>
#include <linux/nvram.h>
#include <linux/console.h>
#include <asm/sections.h>
#include <asm/ptrace.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/nvram.h>
#include <asm/backlight.h>
#include <linux/adb.h>
#include <linux/pmu.h>
static struct backlight_controller *backlighter;
static void* backlighter_data;
static int backlight_autosave;
static int backlight_level = BACKLIGHT_MAX;
static int backlight_enabled = 1;
static int backlight_req_level = -1;
static int backlight_req_enable = -1;
static void backlight_callback(void *);
static DECLARE_WORK(backlight_work, backlight_callback, NULL);
void register_backlight_controller(struct backlight_controller *ctrler,
void *data, char *type)
{
struct device_node* bk_node;
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
/* Special case for the old PowerBook since I can't test on it */
backlight_autosave = machine_is_compatible("AAPL,3400/2400")
|| machine_is_compatible("AAPL,3500");
if ((backlight_autosave
|| machine_is_compatible("AAPL,PowerBook1998")
|| machine_is_compatible("PowerBook1,1"))
&& !strcmp(type, "pmu"))
valid = 1;
#endif
if (bk_node) {
prop = get_property(bk_node, "backlight-control", NULL);
if (prop && !strncmp(prop, type, strlen(type)))
valid = 1;
}
if (!valid)
return;
backlighter = ctrler;
backlighter_data = data;
if (bk_node && !backlight_autosave)
prop = get_property(bk_node, "bklt", NULL);
else
prop = NULL;
if (prop) {
backlight_level = ((*prop)+1) >> 1;
if (backlight_level > BACKLIGHT_MAX)
backlight_level = BACKLIGHT_MAX;
}
#ifdef CONFIG_ADB_PMU
if (backlight_autosave) {
struct adb_request req;
pmu_request(&req, NULL, 2, 0xd9, 0);
while (!req.complete)
pmu_poll();
backlight_level = req.reply[0] >> 4;
}
#endif
acquire_console_sem();
if (!backlighter->set_enable(1, backlight_level, data))
backlight_enabled = 1;
release_console_sem();
printk(KERN_INFO "Registered \"%s\" backlight controller,"
"level: %d/15\n", type, backlight_level);
}
EXPORT_SYMBOL(register_backlight_controller);
void unregister_backlight_controller(struct backlight_controller
*ctrler, void *data)
{
/* We keep the current backlight level (for now) */
if (ctrler == backlighter && data == backlighter_data)
backlighter = NULL;
}
EXPORT_SYMBOL(unregister_backlight_controller);
static int __set_backlight_enable(int enable)
{
int rc;
if (!backlighter)
return -ENODEV;
acquire_console_sem();
rc = backlighter->set_enable(enable, backlight_level,
backlighter_data);
if (!rc)
backlight_enabled = enable;
release_console_sem();
return rc;
}
int set_backlight_enable(int enable)
{
if (!backlighter)
return -ENODEV;
backlight_req_enable = enable;
schedule_work(&backlight_work);
return 0;
}
EXPORT_SYMBOL(set_backlight_enable);
int get_backlight_enable(void)
{
if (!backlighter)
return -ENODEV;
return backlight_enabled;
}
EXPORT_SYMBOL(get_backlight_enable);
static int __set_backlight_level(int level)
{
int rc = 0;
if (!backlighter)
return -ENODEV;
if (level < BACKLIGHT_MIN)
level = BACKLIGHT_OFF;
if (level > BACKLIGHT_MAX)
level = BACKLIGHT_MAX;
acquire_console_sem();
if (backlight_enabled)
rc = backlighter->set_level(level, backlighter_data);
if (!rc)
backlight_level = level;
release_console_sem();
if (!rc && !backlight_autosave) {
level <<=1;
if (level & 0x10)
level |= 0x01;
// -- todo: save to property "bklt"
}
return rc;
}
int set_backlight_level(int level)
{
if (!backlighter)
return -ENODEV;
backlight_req_level = level;
schedule_work(&backlight_work);
return 0;
}
EXPORT_SYMBOL(set_backlight_level);
int get_backlight_level(void)
{
if (!backlighter)
return -ENODEV;
return backlight_level;
}
EXPORT_SYMBOL(get_backlight_level);
static void backlight_callback(void *dummy)
{
int level, enable;
do {
level = backlight_req_level;
enable = backlight_req_enable;
mb();
if (level >= 0)
__set_backlight_level(level);
if (enable >= 0)
__set_backlight_enable(enable);
} while(cmpxchg(&backlight_req_level, level, -1) != level ||
cmpxchg(&backlight_req_enable, enable, -1) != enable);
}
/*
* This file contains low-level cache management functions
* used for sleep and CPU speed changes on Apple machines.
* (In fact the only thing that is Apple-specific is that we assume
* that we can read from ROM at physical address 0xfff00000.)
*
* Copyright (C) 2004 Paul Mackerras (paulus@samba.org) and
* 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/ppc_asm.h>
#include <asm/cputable.h>
/*
* Flush and disable all data caches (dL1, L2, L3). This is used
* when going to sleep, when doing a PMU based cpufreq transition,
* or when "offlining" a CPU on SMP machines. This code is over
* paranoid, but I've had enough issues with various CPU revs and
* bugs that I decided it was worth beeing over cautious
*/
_GLOBAL(flush_disable_caches)
#ifndef CONFIG_6xx
blr
#else
BEGIN_FTR_SECTION
b flush_disable_745x
END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
BEGIN_FTR_SECTION
b flush_disable_75x
END_FTR_SECTION_IFSET(CPU_FTR_L2CR)
b __flush_disable_L1
/* This is the code for G3 and 74[01]0 */
flush_disable_75x:
mflr r10
/* Turn off EE and DR in MSR */
mfmsr r11
rlwinm r0,r11,0,~MSR_EE
rlwinm r0,r0,0,~MSR_DR
sync
mtmsr r0
isync
/* Stop DST streams */
BEGIN_FTR_SECTION
DSSALL
sync
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
/* Stop DPM */
mfspr r8,SPRN_HID0 /* Save SPRN_HID0 in r8 */
rlwinm r4,r8,0,12,10 /* Turn off HID0[DPM] */
sync
mtspr SPRN_HID0,r4 /* Disable DPM */
sync
/* Disp-flush L1. We have a weird problem here that I never
* totally figured out. On 750FX, using the ROM for the flush
* results in a non-working flush. We use that workaround for
* now until I finally understand what's going on. --BenH
*/
/* ROM base by default */
lis r4,0xfff0
mfpvr r3
srwi r3,r3,16
cmplwi cr0,r3,0x7000
bne+ 1f
/* RAM base on 750FX */
li r4,0
1: li r4,0x4000
mtctr r4
1: lwz r0,0(r4)
addi r4,r4,32
bdnz 1b
sync
isync
/* Disable / invalidate / enable L1 data */
mfspr r3,SPRN_HID0
rlwinm r3,r3,0,~(HID0_DCE | HID0_ICE)
mtspr SPRN_HID0,r3
sync
isync
ori r3,r3,(HID0_DCE|HID0_DCI|HID0_ICE|HID0_ICFI)
sync
isync
mtspr SPRN_HID0,r3
xori r3,r3,(HID0_DCI|HID0_ICFI)
mtspr SPRN_HID0,r3
sync
/* Get the current enable bit of the L2CR into r4 */
mfspr r5,SPRN_L2CR
/* Set to data-only (pre-745x bit) */
oris r3,r5,L2CR_L2DO@h
b 2f
/* When disabling L2, code must be in L1 */
.balign 32
1: mtspr SPRN_L2CR,r3
3: sync
isync
b 1f
2: b 3f
3: sync
isync
b 1b
1: /* disp-flush L2. The interesting thing here is that the L2 can be
* up to 2Mb ... so using the ROM, we'll end up wrapping back to memory
* but that is probbaly fine. We disp-flush over 4Mb to be safe
*/
lis r4,2
mtctr r4
lis r4,0xfff0
1: lwz r0,0(r4)
addi r4,r4,32
bdnz 1b
sync
isync
lis r4,2
mtctr r4
lis r4,0xfff0
1: dcbf 0,r4
addi r4,r4,32
bdnz 1b
sync
isync
/* now disable L2 */
rlwinm r5,r5,0,~L2CR_L2E
b 2f
/* When disabling L2, code must be in L1 */
.balign 32
1: mtspr SPRN_L2CR,r5
3: sync
isync
b 1f
2: b 3f
3: sync
isync
b 1b
1: sync
isync
/* Invalidate L2. This is pre-745x, we clear the L2I bit ourselves */
oris r4,r5,L2CR_L2I@h
mtspr SPRN_L2CR,r4
sync
isync
/* Wait for the invalidation to complete */
1: mfspr r3,SPRN_L2CR
rlwinm. r0,r3,0,31,31
bne 1b
/* Clear L2I */
xoris r4,r4,L2CR_L2I@h
sync
mtspr SPRN_L2CR,r4
sync
/* now disable the L1 data cache */
mfspr r0,SPRN_HID0
rlwinm r0,r0,0,~(HID0_DCE|HID0_ICE)
mtspr SPRN_HID0,r0
sync
isync
/* Restore HID0[DPM] to whatever it was before */
sync
mfspr r0,SPRN_HID0
rlwimi r0,r8,0,11,11 /* Turn back HID0[DPM] */
mtspr SPRN_HID0,r0
sync
/* restore DR and EE */
sync
mtmsr r11
isync
mtlr r10
blr
/* This code is for 745x processors */
flush_disable_745x:
/* Turn off EE and DR in MSR */
mfmsr r11
rlwinm r0,r11,0,~MSR_EE
rlwinm r0,r0,0,~MSR_DR
sync
mtmsr r0
isync
/* Stop prefetch streams */
DSSALL
sync
/* Disable L2 prefetching */
mfspr r0,SPRN_MSSCR0
rlwinm r0,r0,0,0,29
mtspr SPRN_MSSCR0,r0
sync
isync
lis r4,0
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
dcbf 0,r4
/* Due to a bug with the HW flush on some CPU revs, we occasionally
* experience data corruption. I'm adding a displacement flush along
* with a dcbf loop over a few Mb to "help". The problem isn't totally
* fixed by this in theory, but at least, in practice, I couldn't reproduce
* it even with a big hammer...
*/
lis r4,0x0002
mtctr r4
li r4,0
1:
lwz r0,0(r4)
addi r4,r4,32 /* Go to start of next cache line */
bdnz 1b
isync
/* Now, flush the first 4MB of memory */
lis r4,0x0002
mtctr r4
li r4,0
sync
1:
dcbf 0,r4
addi r4,r4,32 /* Go to start of next cache line */
bdnz 1b
/* Flush and disable the L1 data cache */
mfspr r6,SPRN_LDSTCR
lis r3,0xfff0 /* read from ROM for displacement flush */
li r4,0xfe /* start with only way 0 unlocked */
li r5,128 /* 128 lines in each way */
1: mtctr r5
rlwimi r6,r4,0,24,31
mtspr SPRN_LDSTCR,r6
sync
isync
2: lwz r0,0(r3) /* touch each cache line */
addi r3,r3,32
bdnz 2b
rlwinm r4,r4,1,24,30 /* move on to the next way */
ori r4,r4,1
cmpwi r4,0xff /* all done? */
bne 1b
/* now unlock the L1 data cache */
li r4,0
rlwimi r6,r4,0,24,31
sync
mtspr SPRN_LDSTCR,r6
sync
isync
/* Flush the L2 cache using the hardware assist */
mfspr r3,SPRN_L2CR
cmpwi r3,0 /* check if it is enabled first */
bge 4f
oris r0,r3,(L2CR_L2IO_745x|L2CR_L2DO_745x)@h
b 2f
/* When disabling/locking L2, code must be in L1 */
.balign 32
1: mtspr SPRN_L2CR,r0 /* lock the L2 cache */
3: sync
isync
b 1f
2: b 3f
3: sync
isync
b 1b
1: sync
isync
ori r0,r3,L2CR_L2HWF_745x
sync
mtspr SPRN_L2CR,r0 /* set the hardware flush bit */
3: mfspr r0,SPRN_L2CR /* wait for it to go to 0 */
andi. r0,r0,L2CR_L2HWF_745x
bne 3b
sync
rlwinm r3,r3,0,~L2CR_L2E
b 2f
/* When disabling L2, code must be in L1 */
.balign 32
1: mtspr SPRN_L2CR,r3 /* disable the L2 cache */
3: sync
isync
b 1f
2: b 3f
3: sync
isync
b 1b
1: sync
isync
oris r4,r3,L2CR_L2I@h
mtspr SPRN_L2CR,r4
sync
isync
1: mfspr r4,SPRN_L2CR
andis. r0,r4,L2CR_L2I@h
bne 1b
sync
BEGIN_FTR_SECTION
/* Flush the L3 cache using the hardware assist */
4: mfspr r3,SPRN_L3CR
cmpwi r3,0 /* check if it is enabled */
bge 6f
oris r0,r3,L3CR_L3IO@h
ori r0,r0,L3CR_L3DO
sync
mtspr SPRN_L3CR,r0 /* lock the L3 cache */
sync
isync
ori r0,r0,L3CR_L3HWF
sync
mtspr SPRN_L3CR,r0 /* set the hardware flush bit */
5: mfspr r0,SPRN_L3CR /* wait for it to go to zero */
andi. r0,r0,L3CR_L3HWF
bne 5b
rlwinm r3,r3,0,~L3CR_L3E
sync
mtspr SPRN_L3CR,r3 /* disable the L3 cache */
sync
ori r4,r3,L3CR_L3I
mtspr SPRN_L3CR,r4
1: mfspr r4,SPRN_L3CR
andi. r0,r4,L3CR_L3I
bne 1b
sync
END_FTR_SECTION_IFSET(CPU_FTR_L3CR)
6: mfspr r0,SPRN_HID0 /* now disable the L1 data cache */
rlwinm r0,r0,0,~HID0_DCE
mtspr SPRN_HID0,r0
sync
isync
mtmsr r11 /* restore DR and EE */
isync
blr
#endif /* CONFIG_6xx */
/*
* arch/ppc/platforms/pmac_cpufreq.c
*
* Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
* Copyright (C) 2004 John Steele Scott <toojays@toojays.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* TODO: Need a big cleanup here. Basically, we need to have different
* cpufreq_driver structures for the different type of HW instead of the
* current mess. We also need to better deal with the detection of the
* type of machine.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/slab.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/sysdev.h>
#include <linux/i2c.h>
#include <linux/hardirq.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/irq.h>
#include <asm/pmac_feature.h>
#include <asm/mmu_context.h>
#include <asm/sections.h>
#include <asm/cputable.h>
#include <asm/time.h>
#include <asm/system.h>
#include <asm/open_pic.h>
#include <asm/keylargo.h>
/* WARNING !!! This will cause calibrate_delay() to be called,
* but this is an __init function ! So you MUST go edit
* init/main.c to make it non-init before enabling DEBUG_FREQ
*/
#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_7447a_dfs(int dfs);
extern void low_choose_750fx_pll(int pll);
extern void low_sleep_handler(void);
/*
* Currently, PowerMac cpufreq supports only high & low frequencies
* that are set by the firmware
*/
static unsigned int low_freq;
static unsigned int hi_freq;
static unsigned int cur_freq;
static unsigned int sleep_freq;
/*
* Different models uses different mecanisms to switch the frequency
*/
static int (*set_speed_proc)(int low_speed);
static unsigned int (*get_speed_proc)(void);
/*
* Some definitions used by the various speedprocs
*/
static u32 voltage_gpio;
static u32 frequency_gpio;
static u32 slew_done_gpio;
static int no_schedule;
static int has_cpu_l2lve;
static int is_pmu_based;
/* There are only two frequency states for each processor. Values
* are in kHz for the time being.
*/
#define CPUFREQ_HIGH 0
#define CPUFREQ_LOW 1
static struct cpufreq_frequency_table pmac_cpu_freqs[] = {
{CPUFREQ_HIGH, 0},
{CPUFREQ_LOW, 0},
{0, CPUFREQ_TABLE_END},
};
static struct freq_attr* pmac_cpu_freqs_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static inline void local_delay(unsigned long ms)
{
if (no_schedule)
mdelay(ms);
else
msleep(ms);
}
static inline void wakeup_decrementer(void)
{
set_dec(tb_ticks_per_jiffy);
/* No currently-supported powerbook has a 601,
* so use get_tbl, not native
*/
last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
}
#ifdef DEBUG_FREQ
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
* core cpufreq framework's own calculation.
*/
extern void calibrate_delay(void);
unsigned long save_lpj = loops_per_jiffy;
calibrate_delay();
loops_per_jiffy = save_lpj;
}
#endif /* DEBUG_FREQ */
/* Switch CPU speed under 750FX CPU control
*/
static int cpu_750fx_cpu_speed(int low_speed)
{
u32 hid2;
if (low_speed == 0) {
/* ramping up, set voltage first */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05);
/* Make sure we sleep for at least 1ms */
local_delay(10);
/* tweak L2 for high voltage */
if (has_cpu_l2lve) {
hid2 = mfspr(SPRN_HID2);
hid2 &= ~0x2000;
mtspr(SPRN_HID2, hid2);
}
}
#ifdef CONFIG_6xx
low_choose_750fx_pll(low_speed);
#endif
if (low_speed == 1) {
/* tweak L2 for low voltage */
if (has_cpu_l2lve) {
hid2 = mfspr(SPRN_HID2);
hid2 |= 0x2000;
mtspr(SPRN_HID2, hid2);
}
/* ramping down, set voltage last */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
local_delay(10);
}
return 0;
}
static unsigned int cpu_750fx_get_cpu_speed(void)
{
if (mfspr(SPRN_HID1) & HID1_PS)
return low_freq;
else
return hi_freq;
}
/* Switch CPU speed using DFS */
static int dfs_set_cpu_speed(int low_speed)
{
if (low_speed == 0) {
/* ramping up, set voltage first */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05);
/* Make sure we sleep for at least 1ms */
local_delay(1);
}
/* set frequency */
#ifdef CONFIG_6xx
low_choose_7447a_dfs(low_speed);
#endif
udelay(100);
if (low_speed == 1) {
/* ramping down, set voltage last */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
local_delay(1);
}
return 0;
}
static unsigned int dfs_get_cpu_speed(void)
{
if (mfspr(SPRN_HID1) & HID1_DFS)
return low_freq;
else
return hi_freq;
}
/* Switch CPU speed using slewing GPIOs
*/
static int gpios_set_cpu_speed(int low_speed)
{
int gpio, timeout = 0;
/* 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 */
local_delay(10);
}
/* Set frequency */
gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0);
if (low_speed == ((gpio & 0x01) == 0))
goto skip;
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio,
low_speed ? 0x04 : 0x05);
udelay(200);
do {
if (++timeout > 100)
break;
local_delay(1);
gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0);
} while((gpio & 0x02) == 0);
skip:
/* 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 */
local_delay(10);
}
#ifdef DEBUG_FREQ
debug_calc_bogomips();
#endif
return 0;
}
/* Switch CPU speed under PMU control
*/
static int pmu_set_cpu_speed(int low_speed)
{
struct adb_request req;
unsigned long save_l2cr;
unsigned long save_l3cr;
unsigned int pic_prio;
unsigned long flags;
preempt_disable();
#ifdef DEBUG_FREQ
printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
#endif
pmu_suspend();
/* Disable all interrupt sources on openpic */
pic_prio = openpic_get_priority();
openpic_set_priority(0xf);
/* Make sure the decrementer won't interrupt us */
asm volatile("mtdec %0" : : "r" (0x7fffffff));
/* Make sure any pending DEC interrupt occuring while we did
* the above didn't re-enable the DEC */
mb();
asm volatile("mtdec %0" : : "r" (0x7fffffff));
/* We can now disable MSR_EE */
local_irq_save(flags);
/* Giveup the FPU & vec */
enable_kernel_fp();
#ifdef CONFIG_ALTIVEC
if (cpu_has_feature(CPU_FTR_ALTIVEC))
enable_kernel_altivec();
#endif /* CONFIG_ALTIVEC */
/* Save & disable L2 and L3 caches */
save_l3cr = _get_L3CR(); /* (returns -1 if not available) */
save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
/* Send the new speed command. My assumption is that this command
* will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep
*/
pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed);
while (!req.complete)
pmu_poll();
/* Prepare the northbridge for the speed transition */
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1);
/* Call low level code to backup CPU state and recover from
* hardware reset
*/
low_sleep_handler();
/* Restore the northbridge */
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0);
/* Restore L2 cache */
if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
_set_L2CR(save_l2cr);
/* Restore L3 cache */
if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
_set_L3CR(save_l3cr);
/* Restore userland MMU context */
set_context(current->active_mm->context, current->active_mm->pgd);
#ifdef DEBUG_FREQ
printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
#endif
/* Restore low level PMU operations */
pmu_unlock();
/* Restore decrementer */
wakeup_decrementer();
/* Restore interrupts */
openpic_set_priority(pic_prio);
/* Let interrupts flow again ... */
local_irq_restore(flags);
#ifdef DEBUG_FREQ
debug_calc_bogomips();
#endif
pmu_resume();
preempt_enable();
return 0;
}
static int do_set_cpu_speed(int speed_mode, int notify)
{
struct cpufreq_freqs freqs;
unsigned long l3cr;
static unsigned long prev_l3cr;
freqs.old = cur_freq;
freqs.new = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq;
freqs.cpu = smp_processor_id();
if (freqs.old == freqs.new)
return 0;
if (notify)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
if (speed_mode == CPUFREQ_LOW &&
cpu_has_feature(CPU_FTR_L3CR)) {
l3cr = _get_L3CR();
if (l3cr & L3CR_L3E) {
prev_l3cr = l3cr;
_set_L3CR(0);
}
}
set_speed_proc(speed_mode == CPUFREQ_LOW);
if (speed_mode == CPUFREQ_HIGH &&
cpu_has_feature(CPU_FTR_L3CR)) {
l3cr = _get_L3CR();
if ((prev_l3cr & L3CR_L3E) && l3cr != prev_l3cr)
_set_L3CR(prev_l3cr);
}
if (notify)
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
cur_freq = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq;
return 0;
}
static unsigned int pmac_cpufreq_get_speed(unsigned int cpu)
{
return cur_freq;
}
static int pmac_cpufreq_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs);
}
static int pmac_cpufreq_target( struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int newstate = 0;
if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
target_freq, relation, &newstate))
return -EINVAL;
return do_set_cpu_speed(newstate, 1);
}
unsigned int pmac_get_one_cpufreq(int i)
{
/* Supports only one CPU for now */
return (i == 0) ? cur_freq : 0;
}
static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
if (policy->cpu != 0)
return -ENODEV;
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policy->cur = cur_freq;
cpufreq_frequency_table_get_attr(pmac_cpu_freqs, policy->cpu);
return cpufreq_frequency_table_cpuinfo(policy, pmac_cpu_freqs);
}
static u32 read_gpio(struct device_node *np)
{
u32 *reg = (u32 *)get_property(np, "reg", NULL);
u32 offset;
if (reg == NULL)
return 0;
/* That works for all keylargos but shall be fixed properly
* some day... The problem is that it seems we can't rely
* on the "reg" property of the GPIO nodes, they are either
* relative to the base of KeyLargo or to the base of the
* GPIO space, and the device-tree doesn't help.
*/
offset = *reg;
if (offset < KEYLARGO_GPIO_LEVELS0)
offset += KEYLARGO_GPIO_LEVELS0;
return offset;
}
static int pmac_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg)
{
/* Ok, this could be made a bit smarter, but let's be robust for now. We
* always force a speed change to high speed before sleep, to make sure
* we have appropriate voltage and/or bus speed for the wakeup process,
* and to make sure our loops_per_jiffies are "good enough", that is will
* not cause too short delays if we sleep in low speed and wake in high
* speed..
*/
no_schedule = 1;
sleep_freq = cur_freq;
if (cur_freq == low_freq && !is_pmu_based)
do_set_cpu_speed(CPUFREQ_HIGH, 0);
return 0;
}
static int pmac_cpufreq_resume(struct cpufreq_policy *policy)
{
/* If we resume, first check if we have a get() function */
if (get_speed_proc)
cur_freq = get_speed_proc();
else
cur_freq = 0;
/* We don't, hrm... we don't really know our speed here, best
* is that we force a switch to whatever it was, which is
* probably high speed due to our suspend() routine
*/
do_set_cpu_speed(sleep_freq == low_freq ?
CPUFREQ_LOW : CPUFREQ_HIGH, 0);
no_schedule = 0;
return 0;
}
static struct cpufreq_driver pmac_cpufreq_driver = {
.verify = pmac_cpufreq_verify,
.target = pmac_cpufreq_target,
.get = pmac_cpufreq_get_speed,
.init = pmac_cpufreq_cpu_init,
.suspend = pmac_cpufreq_suspend,
.resume = pmac_cpufreq_resume,
.flags = CPUFREQ_PM_NO_WARN,
.attr = pmac_cpu_freqs_attr,
.name = "powermac",
.owner = THIS_MODULE,
};
static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode)
{
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");
u32 *value;
/*
* 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");
return 1;
}
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");
return 1;
}
/* 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;
set_speed_proc = gpios_set_cpu_speed;
return 1;
}
/* 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)
return 1;
low_freq = (*value) / 1000;
/* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree
* here */
if (low_freq < 100000)
low_freq *= 10;
value = (u32 *)get_property(cpunode, "max-clock-frequency", NULL);
if (!value)
return 1;
hi_freq = (*value) / 1000;
set_speed_proc = pmu_set_cpu_speed;
is_pmu_based = 1;
return 0;
}
static int pmac_cpufreq_init_7447A(struct device_node *cpunode)
{
struct device_node *volt_gpio_np;
if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
return 1;
volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
if (volt_gpio_np)
voltage_gpio = read_gpio(volt_gpio_np);
if (!voltage_gpio){
printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n");
return 1;
}
/* OF only reports the high frequency */
hi_freq = cur_freq;
low_freq = cur_freq/2;
/* Read actual frequency from CPU */
cur_freq = dfs_get_cpu_speed();
set_speed_proc = dfs_set_cpu_speed;
get_speed_proc = dfs_get_cpu_speed;
return 0;
}
static int pmac_cpufreq_init_750FX(struct device_node *cpunode)
{
struct device_node *volt_gpio_np;
u32 pvr, *value;
if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
return 1;
hi_freq = cur_freq;
value = (u32 *)get_property(cpunode, "reduced-clock-frequency", NULL);
if (!value)
return 1;
low_freq = (*value) / 1000;
volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
if (volt_gpio_np)
voltage_gpio = read_gpio(volt_gpio_np);
pvr = mfspr(SPRN_PVR);
has_cpu_l2lve = !((pvr & 0xf00) == 0x100);
set_speed_proc = cpu_750fx_cpu_speed;
get_speed_proc = cpu_750fx_get_cpu_speed;
cur_freq = cpu_750fx_get_cpu_speed();
return 0;
}
/* Currently, we support the following machines:
*
* - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz)
* - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz)
* - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz)
* - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz)
* - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz)
* - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage)
* - Recent MacRISC3 laptops
* - All new machines with 7447A CPUs
*/
static int __init pmac_cpufreq_setup(void)
{
struct device_node *cpunode;
u32 *value;
if (strstr(cmd_line, "nocpufreq"))
return 0;
/* Assume only one CPU */
cpunode = find_type_devices("cpu");
if (!cpunode)
goto out;
/* Get current cpu clock freq */
value = (u32 *)get_property(cpunode, "clock-frequency", NULL);
if (!value)
goto out;
cur_freq = (*value) / 1000;
/* Check for 7447A based MacRISC3 */
if (machine_is_compatible("MacRISC3") &&
get_property(cpunode, "dynamic-power-step", NULL) &&
PVR_VER(mfspr(SPRN_PVR)) == 0x8003) {
pmac_cpufreq_init_7447A(cpunode);
/* Check for other MacRISC3 machines */
} else if (machine_is_compatible("PowerBook3,4") ||
machine_is_compatible("PowerBook3,5") ||
machine_is_compatible("MacRISC3")) {
pmac_cpufreq_init_MacRISC3(cpunode);
/* Else check for iBook2 500/600 */
} else if (machine_is_compatible("PowerBook4,1")) {
hi_freq = cur_freq;
low_freq = 400000;
set_speed_proc = pmu_set_cpu_speed;
is_pmu_based = 1;
}
/* Else check for TiPb 550 */
else if (machine_is_compatible("PowerBook3,3") && cur_freq == 550000) {
hi_freq = cur_freq;
low_freq = 500000;
set_speed_proc = pmu_set_cpu_speed;
is_pmu_based = 1;
}
/* Else check for TiPb 400 & 500 */
else if (machine_is_compatible("PowerBook3,2")) {
/* We only know about the 400 MHz and the 500Mhz model
* they both have 300 MHz as low frequency
*/
if (cur_freq < 350000 || cur_freq > 550000)
goto out;
hi_freq = cur_freq;
low_freq = 300000;
set_speed_proc = pmu_set_cpu_speed;
is_pmu_based = 1;
}
/* Else check for 750FX */
else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000)
pmac_cpufreq_init_750FX(cpunode);
out:
if (set_speed_proc == NULL)
return -ENODEV;
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\n",
low_freq/1000, hi_freq/1000, cur_freq/1000);
return cpufreq_register_driver(&pmac_cpufreq_driver);
}
module_init(pmac_cpufreq_setup);
/*
* arch/ppc/platforms/pmac_feature.c
*
* Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au)
* 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.
*
* TODO:
*
* - Replace mdelay with some schedule loop if possible
* - Shorten some obfuscated delays on some routines (like modem
* power)
* - Refcount some clocks (see darwin)
* - Split split split...
*
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <asm/sections.h>
#include <asm/errno.h>
#include <asm/ohare.h>
#include <asm/heathrow.h>
#include <asm/keylargo.h>
#include <asm/uninorth.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/dbdma.h>
#include <asm/pci-bridge.h>
#include <asm/pmac_low_i2c.h>
#undef DEBUG_FEATURE
#ifdef DEBUG_FEATURE
#define DBG(fmt,...) printk(KERN_DEBUG fmt)
#else
#define DBG(fmt,...)
#endif
#ifdef CONFIG_6xx
extern int powersave_lowspeed;
#endif
extern int powersave_nap;
extern struct device_node *k2_skiplist[2];
/*
* We use a single global lock to protect accesses. Each driver has
* to take care of its own locking
*/
static DEFINE_SPINLOCK(feature_lock);
#define LOCK(flags) spin_lock_irqsave(&feature_lock, flags);
#define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags);
/*
* Instance of some macio stuffs
*/
struct macio_chip macio_chips[MAX_MACIO_CHIPS];
struct macio_chip* macio_find(struct device_node* child, int type)
{
while(child) {
int i;
for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++)
if (child == macio_chips[i].of_node &&
(!type || macio_chips[i].type == type))
return &macio_chips[i];
child = child->parent;
}
return NULL;
}
EXPORT_SYMBOL_GPL(macio_find);
static const char* macio_names[] =
{
"Unknown",
"Grand Central",
"OHare",
"OHareII",
"Heathrow",
"Gatwick",
"Paddington",
"Keylargo",
"Pangea",
"Intrepid",
"K2"
};
/*
* Uninorth reg. access. Note that Uni-N regs are big endian
*/
#define UN_REG(r) (uninorth_base + ((r) >> 2))
#define UN_IN(r) (in_be32(UN_REG(r)))
#define UN_OUT(r,v) (out_be32(UN_REG(r), (v)))
#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v)))
#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v)))
static struct device_node* uninorth_node;
static u32 __iomem * uninorth_base;
static u32 uninorth_rev;
static int uninorth_u3;
static void __iomem *u3_ht;
/*
* For each motherboard family, we have a table of functions pointers
* that handle the various features.
*/
typedef long (*feature_call)(struct device_node* node, long param, long value);
struct feature_table_entry {
unsigned int selector;
feature_call function;
};
struct pmac_mb_def
{
const char* model_string;
const char* model_name;
int model_id;
struct feature_table_entry* features;
unsigned long board_flags;
};
static struct pmac_mb_def pmac_mb;
/*
* Here are the chip specific feature functions
*/
static inline int
simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int value)
{
struct macio_chip* macio;
unsigned long flags;
macio = macio_find(node, type);
if (!macio)
return -ENODEV;
LOCK(flags);
if (value)
MACIO_BIS(reg, mask);
else
MACIO_BIC(reg, mask);
(void)MACIO_IN32(reg);
UNLOCK(flags);
return 0;
}
#ifndef CONFIG_POWER4
static long
ohare_htw_scc_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long chan_mask;
unsigned long fcr;
unsigned long flags;
int htw, trans;
unsigned long rmask;
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
if (!strcmp(node->name, "ch-a"))
chan_mask = MACIO_FLAG_SCCA_ON;
else if (!strcmp(node->name, "ch-b"))
chan_mask = MACIO_FLAG_SCCB_ON;
else
return -ENODEV;
htw = (macio->type == macio_heathrow || macio->type == macio_paddington
|| macio->type == macio_gatwick);
/* On these machines, the HRW_SCC_TRANS_EN_N bit mustn't be touched */
trans = (pmac_mb.model_id != PMAC_TYPE_YOSEMITE &&
pmac_mb.model_id != PMAC_TYPE_YIKES);
if (value) {
#ifdef CONFIG_ADB_PMU
if ((param & 0xfff) == PMAC_SCC_IRDA)
pmu_enable_irled(1);
#endif /* CONFIG_ADB_PMU */
LOCK(flags);
fcr = MACIO_IN32(OHARE_FCR);
/* Check if scc cell need enabling */
if (!(fcr & OH_SCC_ENABLE)) {
fcr |= OH_SCC_ENABLE;
if (htw) {
/* Side effect: this will also power up the
* modem, but it's too messy to figure out on which
* ports this controls the tranceiver and on which
* it controls the modem
*/
if (trans)
fcr &= ~HRW_SCC_TRANS_EN_N;
MACIO_OUT32(OHARE_FCR, fcr);
fcr |= (rmask = HRW_RESET_SCC);
MACIO_OUT32(OHARE_FCR, fcr);
} else {
fcr |= (rmask = OH_SCC_RESET);
MACIO_OUT32(OHARE_FCR, fcr);
}
UNLOCK(flags);
(void)MACIO_IN32(OHARE_FCR);
mdelay(15);
LOCK(flags);
fcr &= ~rmask;
MACIO_OUT32(OHARE_FCR, fcr);
}
if (chan_mask & MACIO_FLAG_SCCA_ON)
fcr |= OH_SCCA_IO;
if (chan_mask & MACIO_FLAG_SCCB_ON)
fcr |= OH_SCCB_IO;
MACIO_OUT32(OHARE_FCR, fcr);
macio->flags |= chan_mask;
UNLOCK(flags);
if (param & PMAC_SCC_FLAG_XMON)
macio->flags |= MACIO_FLAG_SCC_LOCKED;
} else {
if (macio->flags & MACIO_FLAG_SCC_LOCKED)
return -EPERM;
LOCK(flags);
fcr = MACIO_IN32(OHARE_FCR);
if (chan_mask & MACIO_FLAG_SCCA_ON)
fcr &= ~OH_SCCA_IO;
if (chan_mask & MACIO_FLAG_SCCB_ON)
fcr &= ~OH_SCCB_IO;
MACIO_OUT32(OHARE_FCR, fcr);
if ((fcr & (OH_SCCA_IO | OH_SCCB_IO)) == 0) {
fcr &= ~OH_SCC_ENABLE;
if (htw && trans)
fcr |= HRW_SCC_TRANS_EN_N;
MACIO_OUT32(OHARE_FCR, fcr);
}
macio->flags &= ~(chan_mask);
UNLOCK(flags);
mdelay(10);
#ifdef CONFIG_ADB_PMU
if ((param & 0xfff) == PMAC_SCC_IRDA)
pmu_enable_irled(0);
#endif /* CONFIG_ADB_PMU */
}
return 0;
}
static long
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 long
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 long
ohare_ide_enable(struct device_node* node, long param, long value)
{
switch(param) {
case 0:
/* For some reason, setting the bit in set_initial_features()
* doesn't stick. I'm still investigating... --BenH.
*/
if (value)
simple_feature_tweak(node, macio_ohare,
OHARE_FCR, OH_IOBUS_ENABLE, 1);
return simple_feature_tweak(node, macio_ohare,
OHARE_FCR, OH_IDE0_ENABLE, value);
case 1:
return simple_feature_tweak(node, macio_ohare,
OHARE_FCR, OH_BAY_IDE_ENABLE, value);
default:
return -ENODEV;
}
}
static long
ohare_ide_reset(struct device_node* node, long param, long value)
{
switch(param) {
case 0:
return simple_feature_tweak(node, macio_ohare,
OHARE_FCR, OH_IDE0_RESET_N, !value);
case 1:
return simple_feature_tweak(node, macio_ohare,
OHARE_FCR, OH_IDE1_RESET_N, !value);
default:
return -ENODEV;
}
}
static long
ohare_sleep_state(struct device_node* node, long param, long value)
{
struct macio_chip* macio = &macio_chips[0];
if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
return -EPERM;
if (value == 1) {
MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE);
} else if (value == 0) {
MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
}
return 0;
}
static long
heathrow_modem_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
u8 gpio;
unsigned long flags;
macio = macio_find(node, macio_unknown);
if (!macio)
return -ENODEV;
gpio = MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1;
if (!value) {
LOCK(flags);
MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio);
UNLOCK(flags);
(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
mdelay(250);
}
if (pmac_mb.model_id != PMAC_TYPE_YOSEMITE &&
pmac_mb.model_id != PMAC_TYPE_YIKES) {
LOCK(flags);
if (value)
MACIO_BIC(HEATHROW_FCR, HRW_SCC_TRANS_EN_N);
else
MACIO_BIS(HEATHROW_FCR, HRW_SCC_TRANS_EN_N);
UNLOCK(flags);
(void)MACIO_IN32(HEATHROW_FCR);
mdelay(250);
}
if (value) {
LOCK(flags);
MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1);
(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250); LOCK(flags);
MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio);
(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250); LOCK(flags);
MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1);
(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250);
}
return 0;
}
static long
heathrow_floppy_enable(struct device_node* node, long param, long value)
{
return simple_feature_tweak(node, macio_unknown,
HEATHROW_FCR,
HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE,
value);
}
static long
heathrow_mesh_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
macio = macio_find(node, macio_unknown);
if (!macio)
return -ENODEV;
LOCK(flags);
/* Set clear mesh cell enable */
if (value)
MACIO_BIS(HEATHROW_FCR, HRW_MESH_ENABLE);
else
MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE);
(void)MACIO_IN32(HEATHROW_FCR);
udelay(10);
/* Set/Clear termination power */
if (value)
MACIO_BIC(HEATHROW_MBCR, 0x04000000);
else
MACIO_BIS(HEATHROW_MBCR, 0x04000000);
(void)MACIO_IN32(HEATHROW_MBCR);
udelay(10);
UNLOCK(flags);
return 0;
}
static long
heathrow_ide_enable(struct device_node* node, long param, long value)
{
switch(param) {
case 0:
return simple_feature_tweak(node, macio_unknown,
HEATHROW_FCR, HRW_IDE0_ENABLE, value);
case 1:
return simple_feature_tweak(node, macio_unknown,
HEATHROW_FCR, HRW_BAY_IDE_ENABLE, value);
default:
return -ENODEV;
}
}
static long
heathrow_ide_reset(struct device_node* node, long param, long value)
{
switch(param) {
case 0:
return simple_feature_tweak(node, macio_unknown,
HEATHROW_FCR, HRW_IDE0_RESET_N, !value);
case 1:
return simple_feature_tweak(node, macio_unknown,
HEATHROW_FCR, HRW_IDE1_RESET_N, !value);
default:
return -ENODEV;
}
}
static long
heathrow_bmac_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
if (value) {
LOCK(flags);
MACIO_BIS(HEATHROW_FCR, HRW_BMAC_IO_ENABLE);
MACIO_BIS(HEATHROW_FCR, HRW_BMAC_RESET);
UNLOCK(flags);
(void)MACIO_IN32(HEATHROW_FCR);
mdelay(10);
LOCK(flags);
MACIO_BIC(HEATHROW_FCR, HRW_BMAC_RESET);
UNLOCK(flags);
(void)MACIO_IN32(HEATHROW_FCR);
mdelay(10);
} else {
LOCK(flags);
MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE);
UNLOCK(flags);
}
return 0;
}
static long
heathrow_sound_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
/* B&W G3 and Yikes don't support that properly (the
* sound appear to never come back after beeing shut down).
*/
if (pmac_mb.model_id == PMAC_TYPE_YOSEMITE ||
pmac_mb.model_id == PMAC_TYPE_YIKES)
return 0;
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
if (value) {
LOCK(flags);
MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N);
UNLOCK(flags);
(void)MACIO_IN32(HEATHROW_FCR);
} else {
LOCK(flags);
MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
UNLOCK(flags);
}
return 0;
}
static u32 save_fcr[6];
static u32 save_mbcr;
static u32 save_gpio_levels[2];
static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT];
static u8 save_gpio_normal[KEYLARGO_GPIO_CNT];
static u32 save_unin_clock_ctl;
static struct dbdma_regs save_dbdma[13];
static struct dbdma_regs save_alt_dbdma[13];
static void
dbdma_save(struct macio_chip* macio, struct dbdma_regs* save)
{
int i;
/* Save state & config of DBDMA channels */
for (i=0; i<13; i++) {
volatile struct dbdma_regs __iomem * chan = (void __iomem *)
(macio->base + ((0x8000+i*0x100)>>2));
save[i].cmdptr_hi = in_le32(&chan->cmdptr_hi);
save[i].cmdptr = in_le32(&chan->cmdptr);
save[i].intr_sel = in_le32(&chan->intr_sel);
save[i].br_sel = in_le32(&chan->br_sel);
save[i].wait_sel = in_le32(&chan->wait_sel);
}
}
static void
dbdma_restore(struct macio_chip* macio, struct dbdma_regs* save)
{
int i;
/* Save state & config of DBDMA channels */
for (i=0; i<13; i++) {
volatile struct dbdma_regs __iomem * chan = (void __iomem *)
(macio->base + ((0x8000+i*0x100)>>2));
out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16);
while (in_le32(&chan->status) & ACTIVE)
mb();
out_le32(&chan->cmdptr_hi, save[i].cmdptr_hi);
out_le32(&chan->cmdptr, save[i].cmdptr);
out_le32(&chan->intr_sel, save[i].intr_sel);
out_le32(&chan->br_sel, save[i].br_sel);
out_le32(&chan->wait_sel, save[i].wait_sel);
}
}
static void
heathrow_sleep(struct macio_chip* macio, int secondary)
{
if (secondary) {
dbdma_save(macio, save_alt_dbdma);
save_fcr[2] = MACIO_IN32(0x38);
save_fcr[3] = MACIO_IN32(0x3c);
} else {
dbdma_save(macio, save_dbdma);
save_fcr[0] = MACIO_IN32(0x38);
save_fcr[1] = MACIO_IN32(0x3c);
save_mbcr = MACIO_IN32(0x34);
/* Make sure sound is shut down */
MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
/* This seems to be necessary as well or the fan
* keeps coming up and battery drains fast */
MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE);
MACIO_BIC(HEATHROW_FCR, HRW_IDE0_RESET_N);
/* Make sure eth is down even if module or sleep
* won't work properly */
MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE | HRW_BMAC_RESET);
}
/* Make sure modem is shut down */
MACIO_OUT8(HRW_GPIO_MODEM_RESET,
MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1);
MACIO_BIS(HEATHROW_FCR, HRW_SCC_TRANS_EN_N);
MACIO_BIC(HEATHROW_FCR, OH_SCCA_IO|OH_SCCB_IO|HRW_SCC_ENABLE);
/* Let things settle */
(void)MACIO_IN32(HEATHROW_FCR);
}
static void
heathrow_wakeup(struct macio_chip* macio, int secondary)
{
if (secondary) {
MACIO_OUT32(0x38, save_fcr[2]);
(void)MACIO_IN32(0x38);
mdelay(1);
MACIO_OUT32(0x3c, save_fcr[3]);
(void)MACIO_IN32(0x38);
mdelay(10);
dbdma_restore(macio, save_alt_dbdma);
} else {
MACIO_OUT32(0x38, save_fcr[0] | HRW_IOBUS_ENABLE);
(void)MACIO_IN32(0x38);
mdelay(1);
MACIO_OUT32(0x3c, save_fcr[1]);
(void)MACIO_IN32(0x38);
mdelay(1);
MACIO_OUT32(0x34, save_mbcr);
(void)MACIO_IN32(0x38);
mdelay(10);
dbdma_restore(macio, save_dbdma);
}
}
static long
heathrow_sleep_state(struct device_node* node, long param, long value)
{
if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
return -EPERM;
if (value == 1) {
if (macio_chips[1].type == macio_gatwick)
heathrow_sleep(&macio_chips[0], 1);
heathrow_sleep(&macio_chips[0], 0);
} else if (value == 0) {
heathrow_wakeup(&macio_chips[0], 0);
if (macio_chips[1].type == macio_gatwick)
heathrow_wakeup(&macio_chips[0], 1);
}
return 0;
}
static long
core99_scc_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
unsigned long chan_mask;
u32 fcr;
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
if (!strcmp(node->name, "ch-a"))
chan_mask = MACIO_FLAG_SCCA_ON;
else if (!strcmp(node->name, "ch-b"))
chan_mask = MACIO_FLAG_SCCB_ON;
else
return -ENODEV;
if (value) {
int need_reset_scc = 0;
int need_reset_irda = 0;
LOCK(flags);
fcr = MACIO_IN32(KEYLARGO_FCR0);
/* Check if scc cell need enabling */
if (!(fcr & KL0_SCC_CELL_ENABLE)) {
fcr |= KL0_SCC_CELL_ENABLE;
need_reset_scc = 1;
}
if (chan_mask & MACIO_FLAG_SCCA_ON) {
fcr |= KL0_SCCA_ENABLE;
/* Don't enable line drivers for I2S modem */
if ((param & 0xfff) == PMAC_SCC_I2S1)
fcr &= ~KL0_SCC_A_INTF_ENABLE;
else
fcr |= KL0_SCC_A_INTF_ENABLE;
}
if (chan_mask & MACIO_FLAG_SCCB_ON) {
fcr |= KL0_SCCB_ENABLE;
/* Perform irda specific inits */
if ((param & 0xfff) == PMAC_SCC_IRDA) {
fcr &= ~KL0_SCC_B_INTF_ENABLE;
fcr |= KL0_IRDA_ENABLE;
fcr |= KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE;
fcr |= KL0_IRDA_SOURCE1_SEL;
fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0);
fcr &= ~(KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND);
need_reset_irda = 1;
} else
fcr |= KL0_SCC_B_INTF_ENABLE;
}
MACIO_OUT32(KEYLARGO_FCR0, fcr);
macio->flags |= chan_mask;
if (need_reset_scc) {
MACIO_BIS(KEYLARGO_FCR0, KL0_SCC_RESET);
(void)MACIO_IN32(KEYLARGO_FCR0);
UNLOCK(flags);
mdelay(15);
LOCK(flags);
MACIO_BIC(KEYLARGO_FCR0, KL0_SCC_RESET);
}
if (need_reset_irda) {
MACIO_BIS(KEYLARGO_FCR0, KL0_IRDA_RESET);
(void)MACIO_IN32(KEYLARGO_FCR0);
UNLOCK(flags);
mdelay(15);
LOCK(flags);
MACIO_BIC(KEYLARGO_FCR0, KL0_IRDA_RESET);
}
UNLOCK(flags);
if (param & PMAC_SCC_FLAG_XMON)
macio->flags |= MACIO_FLAG_SCC_LOCKED;
} else {
if (macio->flags & MACIO_FLAG_SCC_LOCKED)
return -EPERM;
LOCK(flags);
fcr = MACIO_IN32(KEYLARGO_FCR0);
if (chan_mask & MACIO_FLAG_SCCA_ON)
fcr &= ~KL0_SCCA_ENABLE;
if (chan_mask & MACIO_FLAG_SCCB_ON) {
fcr &= ~KL0_SCCB_ENABLE;
/* Perform irda specific clears */
if ((param & 0xfff) == PMAC_SCC_IRDA) {
fcr &= ~KL0_IRDA_ENABLE;
fcr &= ~(KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE);
fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0);
fcr &= ~(KL0_IRDA_SOURCE1_SEL|KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND);
}
}
MACIO_OUT32(KEYLARGO_FCR0, fcr);
if ((fcr & (KL0_SCCA_ENABLE | KL0_SCCB_ENABLE)) == 0) {
fcr &= ~KL0_SCC_CELL_ENABLE;
MACIO_OUT32(KEYLARGO_FCR0, fcr);
}
macio->flags &= ~(chan_mask);
UNLOCK(flags);
mdelay(10);
}
return 0;
}
static long
core99_modem_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
u8 gpio;
unsigned long flags;
/* Hack for internal USB modem */
if (node == NULL) {
if (macio_chips[0].type != macio_keylargo)
return -ENODEV;
node = macio_chips[0].of_node;
}
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
gpio = MACIO_IN8(KL_GPIO_MODEM_RESET);
gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE;
gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA;
if (!value) {
LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
UNLOCK(flags);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
mdelay(250);
}
LOCK(flags);
if (value) {
MACIO_BIC(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
UNLOCK(flags);
(void)MACIO_IN32(KEYLARGO_FCR2);
mdelay(250);
} else {
MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
UNLOCK(flags);
}
if (value) {
LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250); LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250); LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250);
}
return 0;
}
static long
pangea_modem_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
u8 gpio;
unsigned long flags;
/* Hack for internal USB modem */
if (node == NULL) {
if (macio_chips[0].type != macio_pangea &&
macio_chips[0].type != macio_intrepid)
return -ENODEV;
node = macio_chips[0].of_node;
}
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
gpio = MACIO_IN8(KL_GPIO_MODEM_RESET);
gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE;
gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA;
if (!value) {
LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
UNLOCK(flags);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
mdelay(250);
}
LOCK(flags);
if (value) {
MACIO_OUT8(KL_GPIO_MODEM_POWER,
KEYLARGO_GPIO_OUTPUT_ENABLE);
UNLOCK(flags);
(void)MACIO_IN32(KEYLARGO_FCR2);
mdelay(250);
} else {
MACIO_OUT8(KL_GPIO_MODEM_POWER,
KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
UNLOCK(flags);
}
if (value) {
LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250); LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250); LOCK(flags);
MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
UNLOCK(flags); mdelay(250);
}
return 0;
}
static long
core99_ata100_enable(struct device_node* node, long value)
{
unsigned long flags;
struct pci_dev *pdev = NULL;
u8 pbus, pid;
if (uninorth_rev < 0x24)
return -ENODEV;
LOCK(flags);
if (value)
UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_ATA100);
else
UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_ATA100);
(void)UN_IN(UNI_N_CLOCK_CNTL);
UNLOCK(flags);
udelay(20);
if (value) {
if (pci_device_from_OF_node(node, &pbus, &pid) == 0)
pdev = pci_find_slot(pbus, pid);
if (pdev == NULL)
return 0;
pci_enable_device(pdev);
pci_set_master(pdev);
}
return 0;
}
static long
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
*/
switch(param) {
case 0:
return simple_feature_tweak(node, macio_unknown,
KEYLARGO_FCR1, KL1_EIDE0_ENABLE, value);
case 1:
return simple_feature_tweak(node, macio_unknown,
KEYLARGO_FCR1, KL1_EIDE1_ENABLE, value);
case 2:
return simple_feature_tweak(node, macio_unknown,
KEYLARGO_FCR1, KL1_UIDE_ENABLE, value);
case 3:
return core99_ata100_enable(node, value);
default:
return -ENODEV;
}
}
static long
core99_ide_reset(struct device_node* node, long param, long value)
{
switch(param) {
case 0:
return simple_feature_tweak(node, macio_unknown,
KEYLARGO_FCR1, KL1_EIDE0_RESET_N, !value);
case 1:
return simple_feature_tweak(node, macio_unknown,
KEYLARGO_FCR1, KL1_EIDE1_RESET_N, !value);
case 2:
return simple_feature_tweak(node, macio_unknown,
KEYLARGO_FCR1, KL1_UIDE_RESET_N, !value);
default:
return -ENODEV;
}
}
static long
core99_gmac_enable(struct device_node* node, long param, long value)
{
unsigned long flags;
LOCK(flags);
if (value)
UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
else
UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
(void)UN_IN(UNI_N_CLOCK_CNTL);
UNLOCK(flags);
udelay(20);
return 0;
}
static long
core99_gmac_phy_reset(struct device_node* node, long param, long value)
{
unsigned long flags;
struct macio_chip* macio;
macio = &macio_chips[0];
if (macio->type != macio_keylargo && macio->type != macio_pangea &&
macio->type != macio_intrepid)
return -ENODEV;
LOCK(flags);
MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
(void)MACIO_IN8(KL_GPIO_ETH_PHY_RESET);
UNLOCK(flags);
mdelay(10);
LOCK(flags);
MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, /*KEYLARGO_GPIO_OUTPUT_ENABLE | */
KEYLARGO_GPIO_OUTOUT_DATA);
UNLOCK(flags);
mdelay(10);
return 0;
}
static long
core99_sound_chip_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
/* Do a better probe code, screamer G4 desktops &
* iMacs can do that too, add a recalibrate in
* the driver as well
*/
if (pmac_mb.model_id == PMAC_TYPE_PISMO ||
pmac_mb.model_id == PMAC_TYPE_TITANIUM) {
LOCK(flags);
if (value)
MACIO_OUT8(KL_GPIO_SOUND_POWER,
KEYLARGO_GPIO_OUTPUT_ENABLE |
KEYLARGO_GPIO_OUTOUT_DATA);
else
MACIO_OUT8(KL_GPIO_SOUND_POWER,
KEYLARGO_GPIO_OUTPUT_ENABLE);
(void)MACIO_IN8(KL_GPIO_SOUND_POWER);
UNLOCK(flags);
}
return 0;
}
static long
core99_airport_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
int state;
macio = macio_find(node, 0);
if (!macio)
return -ENODEV;
/* Hint: we allow passing of macio itself for the sake of the
* sleep code
*/
if (node != macio->of_node &&
(!node->parent || node->parent != macio->of_node))
return -ENODEV;
state = (macio->flags & MACIO_FLAG_AIRPORT_ON) != 0;
if (value == state)
return 0;
if (value) {
/* This code is a reproduction of OF enable-cardslot
* and init-wireless methods, slightly hacked until
* I got it working.
*/
LOCK(flags);
MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 5);
(void)MACIO_IN8(KEYLARGO_GPIO_0+0xf);
UNLOCK(flags);
mdelay(10);
LOCK(flags);
MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 4);
(void)MACIO_IN8(KEYLARGO_GPIO_0+0xf);
UNLOCK(flags);
mdelay(10);
LOCK(flags);
MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16);
(void)MACIO_IN32(KEYLARGO_FCR2);
udelay(10);
MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xb, 0);
(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xb);
udelay(10);
MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28);
(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xa);
udelay(10);
MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28);
(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xd);
udelay(10);
MACIO_OUT8(KEYLARGO_GPIO_0+0xd, 0x28);
(void)MACIO_IN8(KEYLARGO_GPIO_0+0xd);
udelay(10);
MACIO_OUT8(KEYLARGO_GPIO_0+0xe, 0x28);
(void)MACIO_IN8(KEYLARGO_GPIO_0+0xe);
UNLOCK(flags);
udelay(10);
MACIO_OUT32(0x1c000, 0);
mdelay(1);
MACIO_OUT8(0x1a3e0, 0x41);
(void)MACIO_IN8(0x1a3e0);
udelay(10);
LOCK(flags);
MACIO_BIS(KEYLARGO_FCR2, KL2_CARDSEL_16);
(void)MACIO_IN32(KEYLARGO_FCR2);
UNLOCK(flags);
mdelay(100);
macio->flags |= MACIO_FLAG_AIRPORT_ON;
} else {
LOCK(flags);
MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16);
(void)MACIO_IN32(KEYLARGO_FCR2);
MACIO_OUT8(KL_GPIO_AIRPORT_0, 0);
MACIO_OUT8(KL_GPIO_AIRPORT_1, 0);
MACIO_OUT8(KL_GPIO_AIRPORT_2, 0);
MACIO_OUT8(KL_GPIO_AIRPORT_3, 0);
MACIO_OUT8(KL_GPIO_AIRPORT_4, 0);
(void)MACIO_IN8(KL_GPIO_AIRPORT_4);
UNLOCK(flags);
macio->flags &= ~MACIO_FLAG_AIRPORT_ON;
}
return 0;
}
#ifdef CONFIG_SMP
static long
core99_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;
const int dflt_reset_lines[] = { KL_GPIO_RESET_CPU0,
KL_GPIO_RESET_CPU1,
KL_GPIO_RESET_CPU2,
KL_GPIO_RESET_CPU3 };
macio = &macio_chips[0];
if (macio->type != macio_keylargo)
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)
reset_io = dflt_reset_lines[param];
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 */
static long
core99_usb_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio;
unsigned long flags;
char* prop;
int number;
u32 reg;
macio = &macio_chips[0];
if (macio->type != macio_keylargo && macio->type != macio_pangea &&
macio->type != macio_intrepid)
return -ENODEV;
prop = (char *)get_property(node, "AAPL,clock-id", NULL);
if (!prop)
return -ENODEV;
if (strncmp(prop, "usb0u048", 8) == 0)
number = 0;
else if (strncmp(prop, "usb1u148", 8) == 0)
number = 2;
else if (strncmp(prop, "usb2u248", 8) == 0)
number = 4;
else
return -ENODEV;
/* Sorry for the brute-force locking, but this is only used during
* sleep and the timing seem to be critical
*/
LOCK(flags);
if (value) {
/* Turn ON */
if (number == 0) {
MACIO_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
(void)MACIO_IN32(KEYLARGO_FCR0);
UNLOCK(flags);
mdelay(1);
LOCK(flags);
MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
} 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_FCR1, 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);
}
if (macio->type == macio_intrepid) {
/* wait for clock stopped bits to clear */
u32 test0 = 0, test1 = 0;
u32 status0, status1;
int timeout = 1000;
UNLOCK(flags);
switch (number) {
case 0:
test0 = UNI_N_CLOCK_STOPPED_USB0;
test1 = UNI_N_CLOCK_STOPPED_USB0PCI;
break;
case 2:
test0 = UNI_N_CLOCK_STOPPED_USB1;
test1 = UNI_N_CLOCK_STOPPED_USB1PCI;
break;
case 4:
test0 = UNI_N_CLOCK_STOPPED_USB2;
test1 = UNI_N_CLOCK_STOPPED_USB2PCI;
break;
}
do {
if (--timeout <= 0) {
printk(KERN_ERR "core99_usb_enable: "
"Timeout waiting for clocks\n");
break;
}
mdelay(1);
status0 = UN_IN(UNI_N_CLOCK_STOP_STATUS0);
status1 = UN_IN(UNI_N_CLOCK_STOP_STATUS1);
} while ((status0 & test0) | (status1 & test1));
LOCK(flags);
}
} else {
/* Turn OFF */
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) {
if (macio->type != macio_intrepid)
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 if (number == 2) {
if (macio->type != macio_intrepid)
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) {
udelay(1);
MACIO_BIS(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1));
(void)MACIO_IN32(KEYLARGO_FCR1);
}
udelay(1);
}
UNLOCK(flags);
return 0;
}
static long
core99_firewire_enable(struct device_node* node, long param, long value)
{
unsigned long flags;
struct macio_chip* macio;
macio = &macio_chips[0];
if (macio->type != macio_keylargo && macio->type != macio_pangea &&
macio->type != macio_intrepid)
return -ENODEV;
if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED))
return -ENODEV;
LOCK(flags);
if (value) {
UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
(void)UN_IN(UNI_N_CLOCK_CNTL);
} else {
UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
(void)UN_IN(UNI_N_CLOCK_CNTL);
}
UNLOCK(flags);
mdelay(1);
return 0;
}
static long
core99_firewire_cable_power(struct device_node* node, long param, long value)
{
unsigned long flags;
struct macio_chip* macio;
/* Trick: we allow NULL node */
if ((pmac_mb.board_flags & PMAC_MB_HAS_FW_POWER) == 0)
return -ENODEV;
macio = &macio_chips[0];
if (macio->type != macio_keylargo && macio->type != macio_pangea &&
macio->type != macio_intrepid)
return -ENODEV;
if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED))
return -ENODEV;
LOCK(flags);
if (value) {
MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 0);
MACIO_IN8(KL_GPIO_FW_CABLE_POWER);
udelay(10);
} else {
MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 4);
MACIO_IN8(KL_GPIO_FW_CABLE_POWER); udelay(10);
}
UNLOCK(flags);
mdelay(1);
return 0;
}
static long
intrepid_aack_delay_enable(struct device_node* node, long param, long value)
{
unsigned long flags;
if (uninorth_rev < 0xd2)
return -ENODEV;
LOCK(flags);
if (param)
UN_BIS(UNI_N_AACK_DELAY, UNI_N_AACK_DELAY_ENABLE);
else
UN_BIC(UNI_N_AACK_DELAY, UNI_N_AACK_DELAY_ENABLE);
UNLOCK(flags);
return 0;
}
#endif /* CONFIG_POWER4 */
static long
core99_read_gpio(struct device_node* node, long param, long value)
{
struct macio_chip* macio = &macio_chips[0];
return MACIO_IN8(param);
}
static long
core99_write_gpio(struct device_node* node, long param, long value)
{
struct macio_chip* macio = &macio_chips[0];
MACIO_OUT8(param, (u8)(value & 0xff));
return 0;
}
#ifdef CONFIG_POWER4
static long
g5_gmac_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio = &macio_chips[0];
unsigned long flags;
u8 pbus, pid;
LOCK(flags);
if (value) {
MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE);
mb();
k2_skiplist[0] = NULL;
} else {
k2_skiplist[0] = node;
mb();
MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE);
}
UNLOCK(flags);
mdelay(1);
return 0;
}
static long
g5_fw_enable(struct device_node* node, long param, long value)
{
struct macio_chip* macio = &macio_chips[0];
unsigned long flags;
LOCK(flags);
if (value) {
MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE);
mb();
k2_skiplist[1] = NULL;
} else {
k2_skiplist[1] = node;
mb();
MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE);
}
UNLOCK(flags);
mdelay(1);
return 0;
}
static long
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
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 g5_phy_disable_cpu1(void)
{
UN_OUT(U3_API_PHY_CONFIG_1, 0);
}
#endif /* CONFIG_POWER4 */
#ifndef CONFIG_POWER4
static void
keylargo_shutdown(struct macio_chip* macio, int sleep_mode)
{
u32 temp;
if (sleep_mode) {
mdelay(1);
MACIO_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND);
(void)MACIO_IN32(KEYLARGO_FCR0);
mdelay(1);
}
MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
KL0_SCC_CELL_ENABLE |
KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE |
KL0_IRDA_CLK19_ENABLE);
MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
MACIO_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
MACIO_BIC(KEYLARGO_FCR1,
KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N |
KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N |
KL1_UIDE_ENABLE);
MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
MACIO_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE);
temp = MACIO_IN32(KEYLARGO_FCR3);
if (macio->rev >= 2) {
temp |= KL3_SHUTDOWN_PLL2X;
if (sleep_mode)
temp |= KL3_SHUTDOWN_PLL_TOTAL;
}
temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
KL3_SHUTDOWN_PLLKW35;
if (sleep_mode)
temp |= KL3_SHUTDOWN_PLLKW12;
temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
| KL3_CLK31_ENABLE | KL3_I2S1_CLK18_ENABLE | KL3_I2S0_CLK18_ENABLE);
if (sleep_mode)
temp &= ~(KL3_TIMER_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
MACIO_OUT32(KEYLARGO_FCR3, temp);
/* Flush posted writes & wait a bit */
(void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1);
}
static void
pangea_shutdown(struct macio_chip* macio, int sleep_mode)
{
u32 temp;
MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
KL0_SCC_CELL_ENABLE |
KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE);
MACIO_BIC(KEYLARGO_FCR1,
KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
KL1_UIDE_ENABLE);
if (pmac_mb.board_flags & PMAC_MB_MOBILE)
MACIO_BIC(KEYLARGO_FCR1, KL1_UIDE_RESET_N);
MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
temp = MACIO_IN32(KEYLARGO_FCR3);
temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
KL3_SHUTDOWN_PLLKW35;
temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE | KL3_CLK31_ENABLE
| KL3_I2S0_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE);
if (sleep_mode)
temp &= ~(KL3_VIA_CLK16_ENABLE | KL3_TIMER_CLK18_ENABLE);
MACIO_OUT32(KEYLARGO_FCR3, temp);
/* Flush posted writes & wait a bit */
(void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1);
}
static void
intrepid_shutdown(struct macio_chip* macio, int sleep_mode)
{
u32 temp;
MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
KL0_SCC_CELL_ENABLE);
MACIO_BIC(KEYLARGO_FCR1,
/*KL1_USB2_CELL_ENABLE |*/
KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE);
if (pmac_mb.board_flags & PMAC_MB_MOBILE)
MACIO_BIC(KEYLARGO_FCR1, KL1_UIDE_RESET_N);
temp = MACIO_IN32(KEYLARGO_FCR3);
temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE |
KL3_I2S1_CLK18_ENABLE | KL3_I2S0_CLK18_ENABLE);
if (sleep_mode)
temp &= ~(KL3_TIMER_CLK18_ENABLE | KL3_IT_VIA_CLK32_ENABLE);
MACIO_OUT32(KEYLARGO_FCR3, temp);
/* Flush posted writes & wait a bit */
(void)MACIO_IN32(KEYLARGO_FCR0);
mdelay(10);
}
void pmac_tweak_clock_spreading(int enable)
{
struct macio_chip* macio = &macio_chips[0];
/* Hack for doing clock spreading on some machines PowerBooks and
* iBooks. This implements the "platform-do-clockspreading" OF
* property as decoded manually on various models. For safety, we also
* check the product ID in the device-tree in cases we'll whack the i2c
* chip to make reasonably sure we won't set wrong values in there
*
* Of course, ultimately, we have to implement a real parser for
* the platform-do-* stuff...
*/
if (macio->type == macio_intrepid) {
struct device_node *clock =
of_find_node_by_path("/uni-n@f8000000/hw-clock");
if (clock && get_property(clock, "platform-do-clockspreading",
NULL)) {
printk(KERN_INFO "%sabling clock spreading on Intrepid"
" ASIC\n", enable ? "En" : "Dis");
if (enable)
UN_OUT(UNI_N_CLOCK_SPREADING, 2);
else
UN_OUT(UNI_N_CLOCK_SPREADING, 0);
mdelay(40);
}
of_node_put(clock);
}
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(ui2c, "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) | (enable ? 0xc0 : 0xba);
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) | (enable ? 0xd0 : 0xc0);
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;
}
printk(KERN_INFO "%sabling clock spreading on i2c clock chip\n",
enable ? "En" : "Dis");
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;
}
}
static int
core99_sleep(void)
{
struct macio_chip* macio;
int i;
macio = &macio_chips[0];
if (macio->type != macio_keylargo && macio->type != macio_pangea &&
macio->type != macio_intrepid)
return -ENODEV;
/* We power off the wireless slot in case it was not done
* by the driver. We don't power it on automatically however
*/
if (macio->flags & MACIO_FLAG_AIRPORT_ON)
core99_airport_enable(macio->of_node, 0, 0);
/* We power off the FW cable. Should be done by the driver... */
if (macio->flags & MACIO_FLAG_FW_SUPPORTED) {
core99_firewire_enable(NULL, 0, 0);
core99_firewire_cable_power(NULL, 0, 0);
}
/* We make sure int. modem is off (in case driver lost it) */
if (macio->type == macio_keylargo)
core99_modem_enable(macio->of_node, 0, 0);
else
pangea_modem_enable(macio->of_node, 0, 0);
/* We make sure the sound is off as well */
core99_sound_chip_enable(macio->of_node, 0, 0);
/*
* Save various bits of KeyLargo
*/
/* Save the state of the various GPIOs */
save_gpio_levels[0] = MACIO_IN32(KEYLARGO_GPIO_LEVELS0);
save_gpio_levels[1] = MACIO_IN32(KEYLARGO_GPIO_LEVELS1);
for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
save_gpio_extint[i] = MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+i);
for (i=0; i<KEYLARGO_GPIO_CNT; i++)
save_gpio_normal[i] = MACIO_IN8(KEYLARGO_GPIO_0+i);
/* Save the FCRs */
if (macio->type == macio_keylargo)
save_mbcr = MACIO_IN32(KEYLARGO_MBCR);
save_fcr[0] = MACIO_IN32(KEYLARGO_FCR0);
save_fcr[1] = MACIO_IN32(KEYLARGO_FCR1);
save_fcr[2] = MACIO_IN32(KEYLARGO_FCR2);
save_fcr[3] = MACIO_IN32(KEYLARGO_FCR3);
save_fcr[4] = MACIO_IN32(KEYLARGO_FCR4);
if (macio->type == macio_pangea || macio->type == macio_intrepid)
save_fcr[5] = MACIO_IN32(KEYLARGO_FCR5);
/* Save state & config of DBDMA channels */
dbdma_save(macio, save_dbdma);
/*
* Turn off as much as we can
*/
if (macio->type == macio_pangea)
pangea_shutdown(macio, 1);
else if (macio->type == macio_intrepid)
intrepid_shutdown(macio, 1);
else if (macio->type == macio_keylargo)
keylargo_shutdown(macio, 1);
/*
* Put the host bridge to sleep
*/
save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
/* Note: do not switch GMAC off, driver does it when necessary, WOL must keep it
* enabled !
*/
UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
~(/*UNI_N_CLOCK_CNTL_GMAC|*/UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
udelay(100);
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
mdelay(10);
/*
* FIXME: A bit of black magic with OpenPIC (don't ask me why)
*/
if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) {
MACIO_BIS(0x506e0, 0x00400000);
MACIO_BIS(0x506e0, 0x80000000);
}
return 0;
}
static int
core99_wake_up(void)
{
struct macio_chip* macio;
int i;
macio = &macio_chips[0];
if (macio->type != macio_keylargo && macio->type != macio_pangea &&
macio->type != macio_intrepid)
return -ENODEV;
/*
* Wakeup the host bridge
*/
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
udelay(10);
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
udelay(10);
/*
* Restore KeyLargo
*/
if (macio->type == macio_keylargo) {
MACIO_OUT32(KEYLARGO_MBCR, save_mbcr);
(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
}
MACIO_OUT32(KEYLARGO_FCR0, save_fcr[0]);
(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
MACIO_OUT32(KEYLARGO_FCR1, save_fcr[1]);
(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
MACIO_OUT32(KEYLARGO_FCR2, save_fcr[2]);
(void)MACIO_IN32(KEYLARGO_FCR2); udelay(10);
MACIO_OUT32(KEYLARGO_FCR3, save_fcr[3]);
(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
MACIO_OUT32(KEYLARGO_FCR4, save_fcr[4]);
(void)MACIO_IN32(KEYLARGO_FCR4); udelay(10);
if (macio->type == macio_pangea || macio->type == macio_intrepid) {
MACIO_OUT32(KEYLARGO_FCR5, save_fcr[5]);
(void)MACIO_IN32(KEYLARGO_FCR5); udelay(10);
}
dbdma_restore(macio, save_dbdma);
MACIO_OUT32(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]);
MACIO_OUT32(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]);
for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+i, save_gpio_extint[i]);
for (i=0; i<KEYLARGO_GPIO_CNT; i++)
MACIO_OUT8(KEYLARGO_GPIO_0+i, save_gpio_normal[i]);
/* FIXME more black magic with OpenPIC ... */
if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) {
MACIO_BIC(0x506e0, 0x00400000);
MACIO_BIC(0x506e0, 0x80000000);
}
UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl);
udelay(100);
return 0;
}
static long
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
*/
if (param == 1) {
if (value == 1) {
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_IDLE2);
} else {
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
udelay(10);
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
udelay(10);
}
return 0;
}
if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
return -EPERM;
if (value == 1)
return core99_sleep();
else if (value == 0)
return core99_wake_up();
return 0;
}
#endif /* CONFIG_POWER4 */
static long
generic_dev_can_wake(struct device_node* node, long param, long value)
{
/* Todo: eventually check we are really dealing with on-board
* video device ...
*/
if (pmac_mb.board_flags & PMAC_MB_MAY_SLEEP)
pmac_mb.board_flags |= PMAC_MB_CAN_SLEEP;
return 0;
}
static long
generic_get_mb_info(struct device_node* node, long param, long value)
{
switch(param) {
case PMAC_MB_INFO_MODEL:
return pmac_mb.model_id;
case PMAC_MB_INFO_FLAGS:
return pmac_mb.board_flags;
case PMAC_MB_INFO_NAME:
/* hack hack hack... but should work */
*((const char **)value) = pmac_mb.model_name;
return 0;
}
return -EINVAL;
}
/*
* Table definitions
*/
/* Used on any machine
*/
static struct feature_table_entry any_features[] = {
{ PMAC_FTR_GET_MB_INFO, generic_get_mb_info },
{ PMAC_FTR_DEVICE_CAN_WAKE, generic_dev_can_wake },
{ 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
*/
static struct feature_table_entry ohare_features[] = {
{ PMAC_FTR_SCC_ENABLE, ohare_htw_scc_enable },
{ PMAC_FTR_SWIM3_ENABLE, ohare_floppy_enable },
{ PMAC_FTR_MESH_ENABLE, ohare_mesh_enable },
{ PMAC_FTR_IDE_ENABLE, ohare_ide_enable},
{ PMAC_FTR_IDE_RESET, ohare_ide_reset},
{ PMAC_FTR_SLEEP_STATE, ohare_sleep_state },
{ 0, NULL }
};
/* Heathrow desktop machines (Beige G3).
* Separated as some features couldn't be properly tested
* and the serial port control bits appear to confuse it.
*/
static struct feature_table_entry heathrow_desktop_features[] = {
{ PMAC_FTR_SWIM3_ENABLE, heathrow_floppy_enable },
{ PMAC_FTR_MESH_ENABLE, heathrow_mesh_enable },
{ PMAC_FTR_IDE_ENABLE, heathrow_ide_enable },
{ PMAC_FTR_IDE_RESET, heathrow_ide_reset },
{ PMAC_FTR_BMAC_ENABLE, heathrow_bmac_enable },
{ 0, NULL }
};
/* Heathrow based laptop, that is the Wallstreet and mainstreet
* powerbooks.
*/
static struct feature_table_entry heathrow_laptop_features[] = {
{ PMAC_FTR_SCC_ENABLE, ohare_htw_scc_enable },
{ PMAC_FTR_MODEM_ENABLE, heathrow_modem_enable },
{ PMAC_FTR_SWIM3_ENABLE, heathrow_floppy_enable },
{ PMAC_FTR_MESH_ENABLE, heathrow_mesh_enable },
{ PMAC_FTR_IDE_ENABLE, heathrow_ide_enable },
{ PMAC_FTR_IDE_RESET, heathrow_ide_reset },
{ PMAC_FTR_BMAC_ENABLE, heathrow_bmac_enable },
{ PMAC_FTR_SOUND_CHIP_ENABLE, heathrow_sound_enable },
{ PMAC_FTR_SLEEP_STATE, heathrow_sleep_state },
{ 0, NULL }
};
/* Paddington based machines
* The lombard (101) powerbook, first iMac models, B&W G3 and Yikes G4.
*/
static struct feature_table_entry paddington_features[] = {
{ PMAC_FTR_SCC_ENABLE, ohare_htw_scc_enable },
{ PMAC_FTR_MODEM_ENABLE, heathrow_modem_enable },
{ PMAC_FTR_SWIM3_ENABLE, heathrow_floppy_enable },
{ PMAC_FTR_MESH_ENABLE, heathrow_mesh_enable },
{ PMAC_FTR_IDE_ENABLE, heathrow_ide_enable },
{ PMAC_FTR_IDE_RESET, heathrow_ide_reset },
{ PMAC_FTR_BMAC_ENABLE, heathrow_bmac_enable },
{ PMAC_FTR_SOUND_CHIP_ENABLE, heathrow_sound_enable },
{ PMAC_FTR_SLEEP_STATE, heathrow_sleep_state },
{ 0, NULL }
};
/* Core99 & MacRISC 2 machines (all machines released since the
* iBook (included), that is all AGP machines, except pangea
* chipset. The pangea chipset is the "combo" UniNorth/KeyLargo
* used on iBook2 & iMac "flow power".
*/
static struct feature_table_entry core99_features[] = {
{ PMAC_FTR_SCC_ENABLE, core99_scc_enable },
{ PMAC_FTR_MODEM_ENABLE, core99_modem_enable },
{ PMAC_FTR_IDE_ENABLE, core99_ide_enable },
{ PMAC_FTR_IDE_RESET, core99_ide_reset },
{ PMAC_FTR_GMAC_ENABLE, core99_gmac_enable },
{ PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset },
{ PMAC_FTR_SOUND_CHIP_ENABLE, core99_sound_chip_enable },
{ PMAC_FTR_AIRPORT_ENABLE, core99_airport_enable },
{ PMAC_FTR_USB_ENABLE, core99_usb_enable },
{ PMAC_FTR_1394_ENABLE, core99_firewire_enable },
{ PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power },
{ PMAC_FTR_SLEEP_STATE, core99_sleep_state },
#ifdef CONFIG_SMP
{ PMAC_FTR_RESET_CPU, core99_reset_cpu },
#endif /* CONFIG_SMP */
{ PMAC_FTR_READ_GPIO, core99_read_gpio },
{ PMAC_FTR_WRITE_GPIO, core99_write_gpio },
{ 0, NULL }
};
/* RackMac
*/
static struct feature_table_entry rackmac_features[] = {
{ PMAC_FTR_SCC_ENABLE, core99_scc_enable },
{ PMAC_FTR_IDE_ENABLE, core99_ide_enable },
{ PMAC_FTR_IDE_RESET, core99_ide_reset },
{ PMAC_FTR_GMAC_ENABLE, core99_gmac_enable },
{ PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset },
{ PMAC_FTR_USB_ENABLE, core99_usb_enable },
{ PMAC_FTR_1394_ENABLE, core99_firewire_enable },
{ PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power },
{ PMAC_FTR_SLEEP_STATE, core99_sleep_state },
#ifdef CONFIG_SMP
{ PMAC_FTR_RESET_CPU, core99_reset_cpu },
#endif /* CONFIG_SMP */
{ PMAC_FTR_READ_GPIO, core99_read_gpio },
{ PMAC_FTR_WRITE_GPIO, core99_write_gpio },
{ 0, NULL }
};
/* Pangea features
*/
static struct feature_table_entry pangea_features[] = {
{ PMAC_FTR_SCC_ENABLE, core99_scc_enable },
{ PMAC_FTR_MODEM_ENABLE, pangea_modem_enable },
{ PMAC_FTR_IDE_ENABLE, core99_ide_enable },
{ PMAC_FTR_IDE_RESET, core99_ide_reset },
{ PMAC_FTR_GMAC_ENABLE, core99_gmac_enable },
{ PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset },
{ PMAC_FTR_SOUND_CHIP_ENABLE, core99_sound_chip_enable },
{ PMAC_FTR_AIRPORT_ENABLE, core99_airport_enable },
{ PMAC_FTR_USB_ENABLE, core99_usb_enable },
{ PMAC_FTR_1394_ENABLE, core99_firewire_enable },
{ PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power },
{ PMAC_FTR_SLEEP_STATE, core99_sleep_state },
{ PMAC_FTR_READ_GPIO, core99_read_gpio },
{ PMAC_FTR_WRITE_GPIO, core99_write_gpio },
{ 0, NULL }
};
/* Intrepid features
*/
static struct feature_table_entry intrepid_features[] = {
{ PMAC_FTR_SCC_ENABLE, core99_scc_enable },
{ PMAC_FTR_MODEM_ENABLE, pangea_modem_enable },
{ PMAC_FTR_IDE_ENABLE, core99_ide_enable },
{ PMAC_FTR_IDE_RESET, core99_ide_reset },
{ PMAC_FTR_GMAC_ENABLE, core99_gmac_enable },
{ PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset },
{ PMAC_FTR_SOUND_CHIP_ENABLE, core99_sound_chip_enable },
{ PMAC_FTR_AIRPORT_ENABLE, core99_airport_enable },
{ PMAC_FTR_USB_ENABLE, core99_usb_enable },
{ PMAC_FTR_1394_ENABLE, core99_firewire_enable },
{ PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power },
{ PMAC_FTR_SLEEP_STATE, core99_sleep_state },
{ PMAC_FTR_READ_GPIO, core99_read_gpio },
{ PMAC_FTR_WRITE_GPIO, core99_write_gpio },
{ PMAC_FTR_AACK_DELAY_ENABLE, intrepid_aack_delay_enable },
{ 0, NULL }
};
#else /* CONFIG_POWER4 */
/* G5 features
*/
static struct feature_table_entry g5_features[] = {
{ 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[] = {
#ifndef CONFIG_POWER4
/*
* Desktops
*/
{ "AAPL,8500", "PowerMac 8500/8600",
PMAC_TYPE_PSURGE, NULL,
0
},
{ "AAPL,9500", "PowerMac 9500/9600",
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
},
{ "AAPL,ShinerESB", "Apple Network Server",
PMAC_TYPE_ANS, NULL,
0
},
{ "AAPL,e407", "Alchemy",
PMAC_TYPE_ALCHEMY, NULL,
0
},
{ "AAPL,e411", "Gazelle",
PMAC_TYPE_GAZELLE, NULL,
0
},
{ "AAPL,Gossamer", "PowerMac G3 (Gossamer)",
PMAC_TYPE_GOSSAMER, heathrow_desktop_features,
0
},
{ "AAPL,PowerMac G3", "PowerMac G3 (Silk)",
PMAC_TYPE_SILK, heathrow_desktop_features,
0
},
{ "PowerMac1,1", "Blue&White G3",
PMAC_TYPE_YOSEMITE, paddington_features,
0
},
{ "PowerMac1,2", "PowerMac G4 PCI Graphics",
PMAC_TYPE_YIKES, paddington_features,
0
},
{ "PowerMac2,1", "iMac FireWire",
PMAC_TYPE_FW_IMAC, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99
},
{ "PowerMac2,2", "iMac FireWire",
PMAC_TYPE_FW_IMAC, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99
},
{ "PowerMac3,1", "PowerMac G4 AGP Graphics",
PMAC_TYPE_SAWTOOTH, core99_features,
PMAC_MB_OLD_CORE99
},
{ "PowerMac3,2", "PowerMac G4 AGP Graphics",
PMAC_TYPE_SAWTOOTH, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99
},
{ "PowerMac3,3", "PowerMac G4 AGP Graphics",
PMAC_TYPE_SAWTOOTH, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99
},
{ "PowerMac3,4", "PowerMac G4 Silver",
PMAC_TYPE_QUICKSILVER, core99_features,
PMAC_MB_MAY_SLEEP
},
{ "PowerMac3,5", "PowerMac G4 Silver",
PMAC_TYPE_QUICKSILVER, core99_features,
PMAC_MB_MAY_SLEEP
},
{ "PowerMac3,6", "PowerMac G4 Windtunnel",
PMAC_TYPE_WINDTUNNEL, core99_features,
PMAC_MB_MAY_SLEEP,
},
{ "PowerMac4,1", "iMac \"Flower Power\"",
PMAC_TYPE_PANGEA_IMAC, pangea_features,
PMAC_MB_MAY_SLEEP
},
{ "PowerMac4,2", "Flat panel iMac",
PMAC_TYPE_FLAT_PANEL_IMAC, pangea_features,
PMAC_MB_CAN_SLEEP
},
{ "PowerMac4,4", "eMac",
PMAC_TYPE_EMAC, core99_features,
PMAC_MB_MAY_SLEEP
},
{ "PowerMac5,1", "PowerMac G4 Cube",
PMAC_TYPE_CUBE, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99
},
{ "PowerMac6,1", "Flat panel iMac",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP,
},
{ "PowerMac6,3", "Flat panel iMac",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP,
},
{ "PowerMac6,4", "eMac",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP,
},
{ "PowerMac10,1", "Mac mini",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER,
},
{ "iMac,1", "iMac (first generation)",
PMAC_TYPE_ORIG_IMAC, paddington_features,
0
},
/*
* Xserve's
*/
{ "RackMac1,1", "XServe",
PMAC_TYPE_RACKMAC, rackmac_features,
0,
},
{ "RackMac1,2", "XServe rev. 2",
PMAC_TYPE_RACKMAC, rackmac_features,
0,
},
/*
* Laptops
*/
{ "AAPL,3400/2400", "PowerBook 3400",
PMAC_TYPE_HOOPER, ohare_features,
PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE
},
{ "AAPL,3500", "PowerBook 3500",
PMAC_TYPE_KANGA, ohare_features,
PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE
},
{ "AAPL,PowerBook1998", "PowerBook Wallstreet",
PMAC_TYPE_WALLSTREET, heathrow_laptop_features,
PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE
},
{ "PowerBook1,1", "PowerBook 101 (Lombard)",
PMAC_TYPE_101_PBOOK, paddington_features,
PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE
},
{ "PowerBook2,1", "iBook (first generation)",
PMAC_TYPE_ORIG_IBOOK, core99_features,
PMAC_MB_CAN_SLEEP | PMAC_MB_OLD_CORE99 | PMAC_MB_MOBILE
},
{ "PowerBook2,2", "iBook FireWire",
PMAC_TYPE_FW_IBOOK, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER |
PMAC_MB_OLD_CORE99 | PMAC_MB_MOBILE
},
{ "PowerBook3,1", "PowerBook Pismo",
PMAC_TYPE_PISMO, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER |
PMAC_MB_OLD_CORE99 | PMAC_MB_MOBILE
},
{ "PowerBook3,2", "PowerBook Titanium",
PMAC_TYPE_TITANIUM, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook3,3", "PowerBook Titanium II",
PMAC_TYPE_TITANIUM2, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook3,4", "PowerBook Titanium III",
PMAC_TYPE_TITANIUM3, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook3,5", "PowerBook Titanium IV",
PMAC_TYPE_TITANIUM4, core99_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook4,1", "iBook 2",
PMAC_TYPE_IBOOK2, pangea_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook4,2", "iBook 2",
PMAC_TYPE_IBOOK2, pangea_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook4,3", "iBook 2 rev. 2",
PMAC_TYPE_IBOOK2, pangea_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE
},
{ "PowerBook5,1", "PowerBook G4 17\"",
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_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,3", "PowerBook G4 17\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,4", "PowerBook G4 15\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,5", "PowerBook G4 17\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,6", "PowerBook G4 15\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,7", "PowerBook G4 17\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,8", "PowerBook G4 15\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook5,9", "PowerBook G4 17\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,1", "PowerBook G4 12\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,2", "PowerBook G4",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,3", "iBook G4",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,4", "PowerBook G4 12\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,5", "iBook G4",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,7", "iBook G4",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE,
},
{ "PowerBook6,8", "PowerBook G4 12\"",
PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features,
PMAC_MB_MAY_SLEEP | 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
*/
long
pmac_do_feature_call(unsigned int selector, ...)
{
struct device_node* node;
long param, value;
int i;
feature_call func = NULL;
va_list args;
if (pmac_mb.features)
for (i=0; pmac_mb.features[i].function; i++)
if (pmac_mb.features[i].selector == selector) {
func = pmac_mb.features[i].function;
break;
}
if (!func)
for (i=0; any_features[i].function; i++)
if (any_features[i].selector == selector) {
func = any_features[i].function;
break;
}
if (!func)
return -ENODEV;
va_start(args, selector);
node = (struct device_node*)va_arg(args, void*);
param = va_arg(args, long);
value = va_arg(args, long);
va_end(args);
return func(node, param, value);
}
static int __init
probe_motherboard(void)
{
int i;
struct macio_chip* macio = &macio_chips[0];
const char* model = NULL;
struct device_node *dt;
/* Lookup known motherboard type in device-tree. First try an
* exact match on the "model" property, then try a "compatible"
* match is none is found.
*/
dt = find_devices("device-tree");
if (dt != NULL)
model = (const char *) get_property(dt, "model", NULL);
for(i=0; model && i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) {
if (strcmp(model, pmac_mb_defs[i].model_string) == 0) {
pmac_mb = pmac_mb_defs[i];
goto found;
}
}
for(i=0; i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) {
if (machine_is_compatible(pmac_mb_defs[i].model_string)) {
pmac_mb = pmac_mb_defs[i];
goto found;
}
}
/* 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";
break;
case macio_ohare:
pmac_mb.model_id = PMAC_TYPE_UNKNOWN_OHARE;
pmac_mb.model_name = "Unknown OHare-based";
break;
case macio_heathrow:
pmac_mb.model_id = PMAC_TYPE_UNKNOWN_HEATHROW;
pmac_mb.model_name = "Unknown Heathrow-based";
pmac_mb.features = heathrow_desktop_features;
break;
case macio_paddington:
pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PADDINGTON;
pmac_mb.model_name = "Unknown Paddington-based";
pmac_mb.features = paddington_features;
break;
case macio_keylargo:
pmac_mb.model_id = PMAC_TYPE_UNKNOWN_CORE99;
pmac_mb.model_name = "Unknown Keylargo-based";
pmac_mb.features = core99_features;
break;
case macio_pangea:
pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PANGEA;
pmac_mb.model_name = "Unknown Pangea-based";
pmac_mb.features = pangea_features;
break;
case macio_intrepid:
pmac_mb.model_id = PMAC_TYPE_UNKNOWN_INTREPID;
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_UNKNOWN_K2;
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 __iomem * mach_id_ptr = ioremap(0xf3000034, 4);
if (!mach_id_ptr)
return -ENODEV;
/* Here, I used to disable the media-bay on comet. It
* appears this is wrong, the floppy connector is actually
* a kind of media-bay and works with the current driver.
*/
if (__raw_readl(mach_id_ptr) & 0x20000000UL)
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.
* It appears that uninorth rev 3 has a problem with it, we don't
* enable it on those. In theory, the flush-on-lock property is
* supposed to be set when not supported, but I'm not very confident
* that all Apple OF revs did it properly, I do it the paranoid way.
*/
while (uninorth_base && uninorth_rev > 3) {
struct device_node* np = find_path_device("/cpus");
if (!np || !np->child) {
printk(KERN_WARNING "Can't find CPU(s) in device tree !\n");
break;
}
np = np->child;
/* Nap mode not supported on SMP */
if (np->sibling)
break;
/* Nap mode not supported if flush-on-lock property is present */
if (get_property(np, "flush-on-lock", NULL))
break;
powersave_nap = 1;
printk(KERN_INFO "Processor NAP mode on idle enabled.\n");
break;
}
/* On CPUs that support it (750FX), lowspeed by default during
* NAP mode
*/
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))
pmac_mb.board_flags |= PMAC_MB_MOBILE;
printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name);
return 0;
}
/* Initialize the Core99 UniNorth host bridge and memory controller
*/
static void __init
probe_uninorth(void)
{
unsigned long actrl;
/* Locate core99 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) {
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 %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
*/
if (uninorth_rev < 0x11) {
actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
UN_OUT(UNI_N_ARB_CTRL, actrl);
}
/* Some more magic as done by them in recent MacOS X on UniNorth
* revs 1.5 to 2.O and Pangea. Seem to toggle the UniN Maxbus/PCI
* memory timeout
*/
if ((uninorth_rev >= 0x11 && uninorth_rev <= 0x24) || uninorth_rev == 0xc0)
UN_OUT(0x2160, UN_IN(0x2160) & 0x00ffffff);
}
static void __init
probe_one_macio(const char* name, const char* compat, int type)
{
struct device_node* node;
int i;
volatile u32 __iomem * base;
u32* revp;
node = find_devices(name);
if (!node || !node->n_addrs)
return;
if (compat)
do {
if (device_is_compatible(node, compat))
break;
node = node->next;
} while (node);
if (!node)
return;
for(i=0; i<MAX_MACIO_CHIPS; i++) {
if (!macio_chips[i].of_node)
break;
if (macio_chips[i].of_node == node)
return;
}
if (i >= MAX_MACIO_CHIPS) {
printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n");
printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name);
return;
}
base = ioremap(node->addrs[0].address, node->addrs[0].size);
if (!base) {
printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n");
return;
}
if (type == macio_keylargo) {
u32* did = (u32 *)get_property(node, "device-id", NULL);
if (*did == 0x00000025)
type = macio_pangea;
if (*did == 0x0000003e)
type = macio_intrepid;
}
macio_chips[i].of_node = node;
macio_chips[i].type = type;
macio_chips[i].base = base;
macio_chips[i].flags = MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON;
macio_chips[i].name = macio_names[type];
revp = (u32 *)get_property(node, "revision-id", NULL);
if (revp)
macio_chips[i].rev = *revp;
printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n",
macio_names[type], macio_chips[i].rev, macio_chips[i].base);
}
static int __init
probe_macios(void)
{
/* Warning, ordering is important */
probe_one_macio("gc", NULL, macio_grand_central);
probe_one_macio("ohare", NULL, macio_ohare);
probe_one_macio("pci106b,7", NULL, macio_ohareII);
probe_one_macio("mac-io", "keylargo", macio_keylargo);
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
&& macio_chips[1].type == macio_heathrow) {
struct macio_chip temp = macio_chips[0];
macio_chips[0] = macio_chips[1];
macio_chips[1] = temp;
}
if (macio_chips[0].type == macio_ohareII
&& macio_chips[1].type == macio_ohare) {
struct macio_chip temp = macio_chips[0];
macio_chips[0] = macio_chips[1];
macio_chips[1] = temp;
}
macio_chips[0].lbus.index = 0;
macio_chips[1].lbus.index = 1;
return (macio_chips[0].of_node == NULL) ? -ENODEV : 0;
}
static void __init
initial_serial_shutdown(struct device_node* np)
{
int len;
struct slot_names_prop {
int count;
char name[1];
} *slots;
char *conn;
int port_type = PMAC_SCC_ASYNC;
int modem = 0;
slots = (struct slot_names_prop *)get_property(np, "slot-names", &len);
conn = get_property(np, "AAPL,connector", &len);
if (conn && (strcmp(conn, "infrared") == 0))
port_type = PMAC_SCC_IRDA;
else if (device_is_compatible(np, "cobalt"))
modem = 1;
else if (slots && slots->count > 0) {
if (strcmp(slots->name, "IrDA") == 0)
port_type = PMAC_SCC_IRDA;
else if (strcmp(slots->name, "Modem") == 0)
modem = 1;
}
if (modem)
pmac_call_feature(PMAC_FTR_MODEM_ENABLE, np, 0, 0);
pmac_call_feature(PMAC_FTR_SCC_ENABLE, np, port_type, 0);
}
static void __init
set_initial_features(void)
{
struct device_node* np;
/* That hack appears to be necessary for some StarMax motherboards
* but I'm not too sure it was audited for side-effects on other
* ohare based machines...
* Since I still have difficulties figuring the right way to
* differenciate them all and since that hack was there for a long
* time, I'll keep it around
*/
if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) {
struct macio_chip* macio = &macio_chips[0];
MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES);
} else if (macio_chips[0].type == macio_ohare) {
struct macio_chip* macio = &macio_chips[0];
MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
} else if (macio_chips[1].type == macio_ohare) {
struct macio_chip* macio = &macio_chips[1];
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 = 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 = 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 (np->parent
&& device_is_compatible(np->parent, "uni-north")
&& (device_is_compatible(np, "pci106b,18") ||
device_is_compatible(np, "pci106b,30") ||
device_is_compatible(np, "pci11c1,5811"))) {
macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED;
core99_firewire_enable(np, 0, 1);
}
np = of_find_node_by_name(np, "firewire");
}
/* Enable ATA-100 before PCI probe. */
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 = of_find_node_by_name(np, "ata-6");
}
/* Switch airport off */
np = find_devices("radio");
while(np) {
if (np && np->parent == macio_chips[0].of_node) {
macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON;
core99_airport_enable(np, 0, 0);
}
np = np->next;
}
}
/* On all machines that support sound PM, switch sound off */
if (macio_chips[0].of_node)
pmac_do_feature_call(PMAC_FTR_SOUND_CHIP_ENABLE,
macio_chips[0].of_node, 0, 0);
/* While on some desktop G3s, we turn it back on */
if (macio_chips[0].of_node && macio_chips[0].type == macio_heathrow
&& (pmac_mb.model_id == PMAC_TYPE_GOSSAMER ||
pmac_mb.model_id == PMAC_TYPE_SILK)) {
struct macio_chip* macio = &macio_chips[0];
MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N);
}
/* Some machine models need the clock chip to be properly setup for
* clock spreading now. This should be a platform function but we
* don't do these at the moment
*/
pmac_tweak_clock_spreading(1);
#endif /* CONFIG_POWER4 */
/* On all machines, switch modem & serial ports off */
np = find_devices("ch-a");
while(np) {
initial_serial_shutdown(np);
np = np->next;
}
np = find_devices("ch-b");
while(np) {
initial_serial_shutdown(np);
np = np->next;
}
}
void __init
pmac_feature_init(void)
{
/* Detect the UniNorth memory controller */
probe_uninorth();
/* Probe mac-io controllers */
if (probe_macios()) {
printk(KERN_WARNING "No mac-io chip found\n");
return;
}
/* Setup low-level i2c stuffs */
pmac_init_low_i2c();
/* Probe machine type */
if (probe_motherboard())
printk(KERN_WARNING "Unknown PowerMac !\n");
/* Set some initial features (turn off some chips that will
* be later turned on)
*/
set_initial_features();
}
int __init
pmac_feature_late_init(void)
{
struct device_node* np;
/* Request some resources late */
if (uninorth_node)
request_OF_resource(uninorth_node, 0, NULL);
np = find_devices("hammerhead");
if (np)
request_OF_resource(np, 0, NULL);
np = find_devices("interrupt-controller");
if (np)
request_OF_resource(np, 0, NULL);
return 0;
}
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 */
/*
* Early video resume hook
*/
static void (*pmac_early_vresume_proc)(void *data);
static void *pmac_early_vresume_data;
void pmac_set_early_video_resume(void (*proc)(void *data), void *data)
{
if (_machine != _MACH_Pmac)
return;
preempt_disable();
pmac_early_vresume_proc = proc;
pmac_early_vresume_data = data;
preempt_enable();
}
EXPORT_SYMBOL(pmac_set_early_video_resume);
void pmac_call_early_video_resume(void)
{
if (pmac_early_vresume_proc)
pmac_early_vresume_proc(pmac_early_vresume_data);
}
/*
* AGP related suspend/resume code
*/
static struct pci_dev *pmac_agp_bridge;
static int (*pmac_agp_suspend)(struct pci_dev *bridge);
static int (*pmac_agp_resume)(struct pci_dev *bridge);
void pmac_register_agp_pm(struct pci_dev *bridge,
int (*suspend)(struct pci_dev *bridge),
int (*resume)(struct pci_dev *bridge))
{
if (suspend || resume) {
pmac_agp_bridge = bridge;
pmac_agp_suspend = suspend;
pmac_agp_resume = resume;
return;
}
if (bridge != pmac_agp_bridge)
return;
pmac_agp_suspend = pmac_agp_resume = NULL;
return;
}
EXPORT_SYMBOL(pmac_register_agp_pm);
void pmac_suspend_agp_for_card(struct pci_dev *dev)
{
if (pmac_agp_bridge == NULL || pmac_agp_suspend == NULL)
return;
if (pmac_agp_bridge->bus != dev->bus)
return;
pmac_agp_suspend(pmac_agp_bridge);
}
EXPORT_SYMBOL(pmac_suspend_agp_for_card);
void pmac_resume_agp_for_card(struct pci_dev *dev)
{
if (pmac_agp_bridge == NULL || pmac_agp_resume == NULL)
return;
if (pmac_agp_bridge->bus != dev->bus)
return;
pmac_agp_resume(pmac_agp_bridge);
}
EXPORT_SYMBOL(pmac_resume_agp_for_card);
/*
* 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 */
void __iomem * 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(host->base + (((unsigned)reg) << host->bsteps));
}
static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val)
{
out_8(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 = 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);
/*
* arch/ppc/platforms/pmac_nvram.c
*
* Copyright (C) 2002 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.
*
* Todo: - add support for the OF persistent properties
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/nvram.h>
#include <linux/init.h>
#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>
#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
/* 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 {
u8 signature;
u8 cksum;
u16 len;
char name[12];
u8 data[0];
};
struct core99_header {
struct chrp_header hdr;
u32 adler;
u32 generation;
u32 reserved[2];
};
/*
* Read and write the non-volatile RAM on PowerMacs and CHRP machines.
*/
static int nvram_naddrs;
static volatile unsigned char *nvram_addr;
static volatile unsigned char *nvram_data;
static int nvram_mult, is_core_99;
static int core99_bank = 0;
static int nvram_partitions[3];
static DEFINE_SPINLOCK(nv_lock);
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;
static unsigned char core99_nvram_read_byte(int addr)
{
if (nvram_image == NULL)
return 0xff;
return nvram_image[addr];
}
static void core99_nvram_write_byte(int addr, unsigned char val)
{
if (nvram_image == NULL)
return;
nvram_image[addr] = val;
}
static unsigned char direct_nvram_read_byte(int addr)
{
return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]);
}
static void direct_nvram_write_byte(int addr, unsigned char val)
{
out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val);
}
static unsigned char 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 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 pmu_nvram_complete(struct adb_request *req)
{
if (req->arg)
complete((struct completion *)req->arg);
}
static unsigned char pmu_nvram_read_byte(int addr)
{
struct adb_request req;
DECLARE_COMPLETION(req_complete);
req.arg = system_state == 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_state == SYSTEM_RUNNING)
wait_for_completion(&req_complete);
while (!req.complete)
pmu_poll();
return req.reply[0];
}
static void pmu_nvram_write_byte(int addr, unsigned char val)
{
struct adb_request req;
DECLARE_COMPLETION(req_complete);
req.arg = system_state == 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_state == SYSTEM_RUNNING)
wait_for_completion(&req_complete);
while (!req.complete)
pmu_poll();
}
#endif /* CONFIG_ADB_PMU */
static u8 chrp_checksum(struct chrp_header* hdr)
{
u8 *ptr;
u16 sum = hdr->signature;
for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++)
sum += *ptr;
while (sum > 0xFF)
sum = (sum & 0xFF) + (sum>>8);
return sum;
}
static u32 core99_calc_adler(u8 *buffer)
{
int cnt;
u32 low, high;
buffer += CORE99_ADLER_START;
low = 1;
high = 0;
for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) {
if ((cnt % 5000) == 0) {
high %= 65521UL;
high %= 65521UL;
}
low += buffer[cnt];
high += low;
}
low %= 65521UL;
high %= 65521UL;
return (high << 16) | low;
}
static u32 core99_check(u8* datas)
{
struct core99_header* hdr99 = (struct core99_header*)datas;
if (hdr99->hdr.signature != CORE99_SIGNATURE) {
DBG("Invalid signature\n");
return 0;
}
if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) {
DBG("Invalid checksum\n");
return 0;
}
if (hdr99->adler != core99_calc_adler(datas)) {
DBG("Invalid adler\n");
return 0;
}
return hdr99->generation;
}
static int sm_erase_bank(int bank)
{
int stat, i;
unsigned long timeout;
u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
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(KERN_ERR "nvram: Sharp/Micron flash erase failed !\n");
return -ENXIO;
}
return 0;
}
static int 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, SM_FLASH_CMD_WRITE_SETUP);
udelay(1);
out_8(base+i, datas[i]);
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, 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 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 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(KERN_ERR "nvram: AMD flash write failed !\n");
return -ENXIO;
}
return 0;
}
static void __init lookup_partitions(void)
{
u8 buffer[17];
int i, offset;
struct chrp_header* hdr;
if (pmac_newworld) {
nvram_partitions[pmac_nvram_OF] = -1;
nvram_partitions[pmac_nvram_XPRAM] = -1;
nvram_partitions[pmac_nvram_NR] = -1;
hdr = (struct chrp_header *)buffer;
offset = 0;
buffer[16] = 0;
do {
for (i=0;i<16;i++)
buffer[i] = nvram_read_byte(offset+i);
if (!strcmp(hdr->name, "common"))
nvram_partitions[pmac_nvram_OF] = offset + 0x10;
if (!strcmp(hdr->name, "APL,MacOS75")) {
nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10;
nvram_partitions[pmac_nvram_NR] = offset + 0x110;
}
offset += (hdr->len * 0x10);
} while(offset < NVRAM_SIZE);
} else {
nvram_partitions[pmac_nvram_OF] = 0x1800;
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 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
mdelay(2000);
#endif
}
void __init pmac_nvram_init(void)
{
struct device_node *dp;
nvram_naddrs = 0;
dp = find_devices("nvram");
if (dp == NULL) {
printk(KERN_ERR "Can't find NVRAM device\n");
return;
}
nvram_naddrs = dp->n_addrs;
is_core_99 = device_is_compatible(dp, "nvram,flash");
if (is_core_99) {
int i;
u32 gen_bank0, gen_bank1;
if (nvram_naddrs < 1) {
printk(KERN_ERR "nvram: no address\n");
return;
}
nvram_image = alloc_bootmem(NVRAM_SIZE);
if (nvram_image == NULL) {
printk(KERN_ERR "nvram: can't allocate ram image\n");
return;
}
nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2);
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;
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);
}
lookup_partitions();
}
int pmac_get_partition(int partition)
{
return nvram_partitions[partition];
}
u8 pmac_xpram_read(int xpaddr)
{
int offset = nvram_partitions[pmac_nvram_XPRAM];
if (offset < 0)
return 0xff;
return ppc_md.nvram_read_val(xpaddr + offset);
}
void pmac_xpram_write(int xpaddr, u8 data)
{
int offset = nvram_partitions[pmac_nvram_XPRAM];
if (offset < 0)
return;
ppc_md.nvram_write_val(xpaddr + offset, data);
}
EXPORT_SYMBOL(pmac_get_partition);
EXPORT_SYMBOL(pmac_xpram_read);
EXPORT_SYMBOL(pmac_xpram_write);
/*
* Support for PCI bridges found on Power Macintoshes.
* 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)
*
* 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/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#undef DEBUG
#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;
extern int pcibios_assign_bus_offset;
struct device_node *k2_skiplist[2];
/*
* Magic constants for enabling cache coherency in the bandit/PSX bridge.
*/
#define BANDIT_DEVID_2 8
#define BANDIT_REVID 3
#define BANDIT_DEVNUM 11
#define BANDIT_MAGIC 0x50
#define BANDIT_COHERENT 0x40
static int __init
fixup_one_level_bus_range(struct device_node *node, int higher)
{
for (; node != 0;node = node->sibling) {
int * bus_range;
unsigned int *class_code;
int len;
/* For PCI<->PCI bridges or CardBus bridges, we go down */
class_code = (unsigned int *) get_property(node, "class-code", NULL);
if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
continue;
bus_range = (int *) get_property(node, "bus-range", &len);
if (bus_range != NULL && len > 2 * sizeof(int)) {
if (bus_range[1] > higher)
higher = bus_range[1];
}
higher = fixup_one_level_bus_range(node->child, higher);
}
return higher;
}
/* This routine fixes the "bus-range" property of all bridges in the
* system since they tend to have their "last" member wrong on macs
*
* Note that the bus numbers manipulated here are OF bus numbers, they
* are not Linux bus numbers.
*/
static void __init
fixup_bus_range(struct device_node *bridge)
{
int * bus_range;
int len;
/* 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",
bridge->full_name);
return;
}
bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
}
/*
* 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
* have 2 bandit controllers (2 PCI busses).
*
* "Chaos" is used in some "Bandit"-type machines as a bridge
* for the separate display bus. It is accessed the same
* way as bandit, but cannot be probed for devices. It therefore
* has its own config access functions.
*
* 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 an
* AGP bus which is dealt with the old UniNorth access routines
* and a HyperTransport bus which uses its own set of access
* functions.
*/
#define MACRISC_CFA0(devfn, off) \
((1 << (unsigned long)PCI_SLOT(dev_fn)) \
| (((unsigned long)PCI_FUNC(dev_fn)) << 8) \
| (((unsigned long)(off)) & 0xFCUL))
#define MACRISC_CFA1(bus, devfn, off) \
((((unsigned long)(bus)) << 16) \
|(((unsigned long)(devfn)) << 8) \
|(((unsigned long)(off)) & 0xFCUL) \
|1UL)
static void volatile __iomem *
macrisc_cfg_access(struct pci_controller* hose, u8 bus, u8 dev_fn, u8 offset)
{
unsigned int caddr;
if (bus == hose->first_busno) {
if (dev_fn < (11 << 3))
return NULL;
caddr = MACRISC_CFA0(dev_fn, offset);
} else
caddr = MACRISC_CFA1(bus, dev_fn, offset);
/* Uninorth will return garbage if we don't read back the value ! */
do {
out_le32(hose->cfg_addr, caddr);
} while (in_le32(hose->cfg_addr) != caddr);
offset &= has_uninorth ? 0x07 : 0x03;
return hose->cfg_data + offset;
}
static int
macrisc_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 *val)
{
struct pci_controller *hose = bus->sysdata;
void volatile __iomem *addr;
addr = macrisc_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(addr);
break;
case 2:
*val = in_le16(addr);
break;
default:
*val = in_le32(addr);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int
macrisc_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 val)
{
struct pci_controller *hose = bus->sysdata;
void volatile __iomem *addr;
addr = macrisc_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(addr, val);
(void) in_8(addr);
break;
case 2:
out_le16(addr, val);
(void) in_le16(addr);
break;
default:
out_le32(addr, val);
(void) in_le32(addr);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops macrisc_pci_ops =
{
macrisc_read_config,
macrisc_write_config
};
/*
* Verifiy that a specific (bus, dev_fn) exists on chaos
*/
static int
chaos_validate_dev(struct pci_bus *bus, int devfn, int offset)
{
struct device_node *np;
u32 *vendor, *device;
np = pci_busdev_to_OF_node(bus, devfn);
if (np == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
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;
}
static int
chaos_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 *val)
{
int result = chaos_validate_dev(bus, devfn, offset);
if (result == PCIBIOS_BAD_REGISTER_NUMBER)
*val = ~0U;
if (result != PCIBIOS_SUCCESSFUL)
return result;
return macrisc_read_config(bus, devfn, offset, len, val);
}
static int
chaos_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 val)
{
int result = chaos_validate_dev(bus, devfn, offset);
if (result != PCIBIOS_SUCCESSFUL)
return result;
return macrisc_write_config(bus, devfn, offset, len, val);
}
static struct pci_ops chaos_pci_ops =
{
chaos_read_config,
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 void volatile __iomem *
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 hose->cfg_data + U3_HT_CFA0(devfn, offset);
} else
return hose->cfg_data + U3_HT_CFA1(bus, devfn, offset);
}
static int
u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 *val)
{
struct pci_controller *hose = bus->sysdata;
void volatile __iomem *addr;
int i;
struct device_node *np = pci_busdev_to_OF_node(bus, devfn);
if (np == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
/*
* 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] == np) {
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(addr);
break;
case 2:
*val = in_le16(addr);
break;
default:
*val = in_le32(addr);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int
u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 val)
{
struct pci_controller *hose = bus->sysdata;
void volatile __iomem *addr;
int i;
struct device_node *np = pci_busdev_to_OF_node(bus, devfn);
if (np == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
/*
* 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] == np)
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(addr, val);
(void) in_8(addr);
break;
case 2:
out_le16(addr, val);
(void) in_le16(addr);
break;
default:
out_le32(addr, val);
(void) in_le32(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.
* N.B. we could clean this up using the hose ops directly.
*/
static void __init
init_bandit(struct pci_controller *bp)
{
unsigned int vendev, magic;
int rev;
/* read the word at offset 0 in config space for device 11 */
out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID);
udelay(2);
vendev = in_le32(bp->cfg_data);
if (vendev == (PCI_DEVICE_ID_APPLE_BANDIT << 16) +
PCI_VENDOR_ID_APPLE) {
/* read the revision id */
out_le32(bp->cfg_addr,
(1UL << BANDIT_DEVNUM) + PCI_REVISION_ID);
udelay(2);
rev = in_8(bp->cfg_data);
if (rev != BANDIT_REVID)
printk(KERN_WARNING
"Unknown revision %d for bandit\n", rev);
} else if (vendev != (BANDIT_DEVID_2 << 16) + PCI_VENDOR_ID_APPLE) {
printk(KERN_WARNING "bandit isn't? (%x)\n", vendev);
return;
}
/* read the word at offset 0x50 */
out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC);
udelay(2);
magic = in_le32(bp->cfg_data);
if ((magic & BANDIT_COHERENT) != 0)
return;
magic |= BANDIT_COHERENT;
udelay(2);
out_le32(bp->cfg_data, magic);
printk(KERN_INFO "Cache coherency enabled for bandit/PSX\n");
}
/*
* Tweak the PCI-PCI bridge chip on the blue & white G3s.
*/
static void __init
init_p2pbridge(void)
{
struct device_node *p2pbridge;
struct pci_controller* hose;
u8 bus, devfn;
u16 val;
/* XXX it would be better here to identify the specific
PCI-PCI bridge chip we have. */
if ((p2pbridge = find_devices("pci-bridge")) == 0
|| p2pbridge->parent == NULL
|| strcmp(p2pbridge->parent->name, "pci") != 0)
return;
if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) {
DBG("Can't find PCI infos for PCI<->PCI bridge\n");
return;
}
/* Warning: At this point, we have not yet renumbered all busses.
* So we must use OF walking to find out hose
*/
hose = pci_find_hose_for_OF_device(p2pbridge);
if (!hose) {
DBG("Can't find hose for PCI<->PCI bridge\n");
return;
}
if (early_read_config_word(hose, bus, devfn,
PCI_BRIDGE_CONTROL, &val) < 0) {
printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n");
return;
}
val &= ~PCI_BRIDGE_CTL_MASTER_ABORT;
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", NULL);
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)
{
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();
/* We are still having some issues with the Xserve G4, enabling
* some offset between bus number and domains for now when we
* assign all busses should help for now
*/
if (pci_assign_all_buses)
pcibios_assign_bus_offset = 0x10;
#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) \
| (((o) & ~3) << 24))
#define GRACKLE_PICR1_STG 0x00000040
#define GRACKLE_PICR1_LOOPSNOOP 0x00000010
/* N.B. this is called before bridges is initialized, so we can't
use grackle_pcibios_{read,write}_config_dword. */
static inline void grackle_set_stg(struct pci_controller* bp, int enable)
{
unsigned int val;
out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
val = in_le32(bp->cfg_data);
val = enable? (val | GRACKLE_PICR1_STG) :
(val & ~GRACKLE_PICR1_STG);
out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
out_le32(bp->cfg_data, val);
(void)in_le32(bp->cfg_data);
}
static inline void grackle_set_loop_snoop(struct pci_controller *bp, int enable)
{
unsigned int val;
out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
val = in_le32(bp->cfg_data);
val = enable? (val | GRACKLE_PICR1_LOOPSNOOP) :
(val & ~GRACKLE_PICR1_LOOPSNOOP);
out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
out_le32(bp->cfg_data, val);
(void)in_le32(bp->cfg_data);
}
static int __init
setup_uninorth(struct pci_controller* hose, struct reg_property* addr)
{
pci_assign_all_buses = 1;
has_uninorth = 1;
hose->ops = &macrisc_pci_ops;
hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000);
hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000);
/* We "know" that the bridge at f2000000 has the PCI slots. */
return addr->address == 0xf2000000;
}
static void __init
setup_bandit(struct pci_controller* hose, struct reg_property* addr)
{
hose->ops = &macrisc_pci_ops;
hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000);
hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000);
init_bandit(hose);
}
static void __init
setup_chaos(struct pci_controller* hose, struct reg_property* addr)
{
/* assume a `chaos' bridge */
hose->ops = &chaos_pci_ops;
hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000);
hose->cfg_data = 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_buses 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 = 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 its configuration register (if any).
*/
hose->io_base_phys = 0xf4000000;
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)
{
setup_indirect_pci(hose, 0xfec00000, 0xfee00000);
if (machine_is_compatible("AAPL,PowerBook1998"))
grackle_set_loop_snoop(hose, 1);
#if 0 /* Disabled for now, HW problems ??? */
grackle_set_stg(hose, 1);
#endif
}
/*
* We assume that if we have a G3 powermac, we have one bridge called
* "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 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 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);
return 0;
}
static void __init
pcibios_fixup_OF_interrupts(void)
{
struct pci_dev* dev = NULL;
/*
* Open Firmware often doesn't initialize the
* PCI_INTERRUPT_LINE config register properly, so we
* should find the device node and apply the interrupt
* obtained from the OF device-tree
*/
for_each_pci_dev(dev) {
struct device_node *node;
node = pci_device_to_OF_node(dev);
/* this is the node, see if it has interrupts */
if (node && node->n_intrs > 0)
dev->irq = node->intrs[0].line;
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
}
}
void __init
pmac_pcibios_fixup(void)
{
/* Fixup interrupts according to OF tree */
pcibios_fixup_OF_interrupts();
}
int
pmac_pci_enable_device_hook(struct pci_dev *dev, int initial)
{
struct device_node* node;
int updatecfg = 0;
int uninorth_child;
node = pci_device_to_OF_node(dev);
/* We don't want to enable USB controllers absent from the OF tree
* (iBook second controller)
*/
if (dev->vendor == PCI_VENDOR_ID_APPLE
&& (dev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10))
&& !node) {
printk(KERN_INFO "Apple USB OHCI %s disabled by firmware\n",
pci_name(dev));
return -EINVAL;
}
if (!node)
return 0;
uninorth_child = node->parent &&
device_is_compatible(node->parent, "uni-north");
/* Firewire & GMAC were disabled after PCI probe, the driver is
* claiming them, we must re-enable them now.
*/
if (uninorth_child && !strcmp(node->name, "firewire") &&
(device_is_compatible(node, "pci106b,18") ||
device_is_compatible(node, "pci106b,30") ||
device_is_compatible(node, "pci11c1,5811"))) {
pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, node, 0, 1);
pmac_call_feature(PMAC_FTR_1394_ENABLE, node, 0, 1);
updatecfg = 1;
}
if (uninorth_child && !strcmp(node->name, "ethernet") &&
device_is_compatible(node, "gmac")) {
pmac_call_feature(PMAC_FTR_GMAC_ENABLE, node, 0, 1);
updatecfg = 1;
}
if (updatecfg) {
u16 cmd;
/*
* Make sure PCI is correctly configured
*
* We use old pci_bios versions of the function since, by
* default, gmac is not powered up, and so will be absent
* from the kernel initial PCI lookup.
*
* Should be replaced by 2.4 new PCI mechanisms and really
* register the device.
*/
pci_read_config_word(dev, PCI_COMMAND, &cmd);
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, pci_cache_line_size);
}
return 0;
}
/* We power down some devices after they have been probed. They'll
* be powered back on later on
*/
void __init
pmac_pcibios_after_init(void)
{
struct device_node* nd;
#ifdef CONFIG_BLK_DEV_IDE
struct pci_dev *dev = NULL;
/* OF fails to initialize IDE controllers on macs
* (and maybe other machines)
*
* Ideally, this should be moved to the IDE layer, but we need
* to check specifically with Andre Hedrick how to do it cleanly
* since the common IDE code seem to care about the fact that the
* BIOS may have disabled a controller.
*
* -- BenH
*/
for_each_pci_dev(dev) {
if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE)
pci_enable_device(dev);
}
#endif /* CONFIG_BLK_DEV_IDE */
nd = find_devices("firewire");
while (nd) {
if (nd->parent && (device_is_compatible(nd, "pci106b,18") ||
device_is_compatible(nd, "pci106b,30") ||
device_is_compatible(nd, "pci11c1,5811"))
&& device_is_compatible(nd->parent, "uni-north")) {
pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0);
pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0);
}
nd = nd->next;
}
nd = find_devices("ethernet");
while (nd) {
if (nd->parent && device_is_compatible(nd, "gmac")
&& device_is_compatible(nd->parent, "uni-north"))
pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0);
nd = nd->next;
}
}
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);
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_ANY_ID, pmac_pci_fixup_cardbus);
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");
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata);
/*
* Disable second function on K2-SATA, it's broken
* and disable IO BARs on first one
*/
void 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);
}
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, 0x0240, pmac_pci_fixup_k2_sata);
/*
* Support for the interrupt controllers found on Power Macintosh,
* currently Apple's "Grand Central" interrupt controller in all
* it's incarnations. OpenPIC support used on newer machines is
* in a separate file
*
* 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
* 2 of the License, or (at your option) any later version.
*
*/
#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/sysdev.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#include <asm/time.h>
#include <asm/open_pic.h>
#include <asm/xmon.h>
#include <asm/pmac_feature.h>
#include <asm/machdep.h>
#include "pmac_pic.h"
/*
* XXX this should be in xmon.h, but putting it there means xmon.h
* has to include <linux/interrupt.h> (to get irqreturn_t), which
* causes all sorts of problems. -- paulus
*/
extern irqreturn_t xmon_irq(int, void *, struct pt_regs *);
struct pmac_irq_hw {
unsigned int event;
unsigned int enable;
unsigned int ack;
unsigned int level;
};
/* Default addresses */
static volatile struct pmac_irq_hw *pmac_irq_hw[4] = {
(struct pmac_irq_hw *) 0xf3000020,
(struct pmac_irq_hw *) 0xf3000010,
(struct pmac_irq_hw *) 0xf4000020,
(struct pmac_irq_hw *) 0xf4000010,
};
#define GC_LEVEL_MASK 0x3ff00000
#define OHARE_LEVEL_MASK 0x1ff00000
#define HEATHROW_LEVEL_MASK 0x1ff00000
static int max_irqs;
static int max_real_irqs;
static u32 level_mask[4];
static DEFINE_SPINLOCK(pmac_pic_lock);
#define GATWICK_IRQ_POOL_SIZE 10
static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE];
#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
static unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
/*
* Mark an irq as "lost". This is only used on the pmac
* since it can lose interrupts (see pmac_set_irq_mask).
* -- Cort
*/
void
__set_lost(unsigned long irq_nr, int nokick)
{
if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
atomic_inc(&ppc_n_lost_interrupts);
if (!nokick)
set_dec(1);
}
}
static void
pmac_mask_and_ack_irq(unsigned int irq_nr)
{
unsigned long bit = 1UL << (irq_nr & 0x1f);
int i = irq_nr >> 5;
unsigned long flags;
if ((unsigned)irq_nr >= max_irqs)
return;
clear_bit(irq_nr, ppc_cached_irq_mask);
if (test_and_clear_bit(irq_nr, ppc_lost_interrupts))
atomic_dec(&ppc_n_lost_interrupts);
spin_lock_irqsave(&pmac_pic_lock, flags);
out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
out_le32(&pmac_irq_hw[i]->ack, bit);
do {
/* make sure ack gets to controller before we enable
interrupts */
mb();
} while((in_le32(&pmac_irq_hw[i]->enable) & bit)
!= (ppc_cached_irq_mask[i] & bit));
spin_unlock_irqrestore(&pmac_pic_lock, flags);
}
static void pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
{
unsigned long bit = 1UL << (irq_nr & 0x1f);
int i = irq_nr >> 5;
unsigned long flags;
if ((unsigned)irq_nr >= max_irqs)
return;
spin_lock_irqsave(&pmac_pic_lock, flags);
/* enable unmasked interrupts */
out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
do {
/* make sure mask gets to controller before we
return to user */
mb();
} while((in_le32(&pmac_irq_hw[i]->enable) & bit)
!= (ppc_cached_irq_mask[i] & bit));
/*
* Unfortunately, setting the bit in the enable register
* when the device interrupt is already on *doesn't* set
* the bit in the flag register or request another interrupt.
*/
if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level))
__set_lost((ulong)irq_nr, nokicklost);
spin_unlock_irqrestore(&pmac_pic_lock, flags);
}
/* When an irq gets requested for the first client, if it's an
* edge interrupt, we clear any previous one on the controller
*/
static unsigned int pmac_startup_irq(unsigned int irq_nr)
{
unsigned long bit = 1UL << (irq_nr & 0x1f);
int i = irq_nr >> 5;
if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
out_le32(&pmac_irq_hw[i]->ack, bit);
set_bit(irq_nr, ppc_cached_irq_mask);
pmac_set_irq_mask(irq_nr, 0);
return 0;
}
static void pmac_mask_irq(unsigned int irq_nr)
{
clear_bit(irq_nr, ppc_cached_irq_mask);
pmac_set_irq_mask(irq_nr, 0);
mb();
}
static void pmac_unmask_irq(unsigned int irq_nr)
{
set_bit(irq_nr, ppc_cached_irq_mask);
pmac_set_irq_mask(irq_nr, 0);
}
static void pmac_end_irq(unsigned int irq_nr)
{
if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
&& irq_desc[irq_nr].action) {
set_bit(irq_nr, ppc_cached_irq_mask);
pmac_set_irq_mask(irq_nr, 1);
}
}
struct hw_interrupt_type pmac_pic = {
.typename = " PMAC-PIC ",
.startup = pmac_startup_irq,
.enable = pmac_unmask_irq,
.disable = pmac_mask_irq,
.ack = pmac_mask_and_ack_irq,
.end = pmac_end_irq,
};
struct hw_interrupt_type gatwick_pic = {
.typename = " GATWICK ",
.startup = pmac_startup_irq,
.enable = pmac_unmask_irq,
.disable = pmac_mask_irq,
.ack = pmac_mask_and_ack_irq,
.end = pmac_end_irq,
};
static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs)
{
int irq, bits;
for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) {
int i = irq >> 5;
bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
/* We must read level interrupts from the level register */
bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
bits &= ppc_cached_irq_mask[i];
if (bits == 0)
continue;
irq += __ilog2(bits);
__do_IRQ(irq, regs);
return IRQ_HANDLED;
}
printk("gatwick irq not from gatwick pic\n");
return IRQ_NONE;
}
int
pmac_get_irq(struct pt_regs *regs)
{
int irq;
unsigned long bits = 0;
#ifdef CONFIG_SMP
void psurge_smp_message_recv(struct pt_regs *);
/* IPI's are a hack on the powersurge -- Cort */
if ( smp_processor_id() != 0 ) {
psurge_smp_message_recv(regs);
return -2; /* ignore, already handled */
}
#endif /* CONFIG_SMP */
for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
int i = irq >> 5;
bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
/* We must read level interrupts from the level register */
bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
bits &= ppc_cached_irq_mask[i];
if (bits == 0)
continue;
irq += __ilog2(bits);
break;
}
return irq;
}
/* This routine will fix some missing interrupt values in the device tree
* on the gatwick mac-io controller used by some PowerBooks
*/
static void __init
pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base)
{
struct device_node *node;
int count;
memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool));
node = gw->child;
count = 0;
while(node)
{
/* Fix SCC */
if (strcasecmp(node->name, "escc") == 0)
if (node->child) {
if (node->child->n_intrs < 3) {
node->child->intrs = &gatwick_int_pool[count];
count += 3;
}
node->child->n_intrs = 3;
node->child->intrs[0].line = 15+irq_base;
node->child->intrs[1].line = 4+irq_base;
node->child->intrs[2].line = 5+irq_base;
printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n",
node->child->intrs[0].line,
node->child->intrs[1].line,
node->child->intrs[2].line);
}
/* Fix media-bay & left SWIM */
if (strcasecmp(node->name, "media-bay") == 0) {
struct device_node* ya_node;
if (node->n_intrs == 0)
node->intrs = &gatwick_int_pool[count++];
node->n_intrs = 1;
node->intrs[0].line = 29+irq_base;
printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n",
node->intrs[0].line);
ya_node = node->child;
while(ya_node)
{
if (strcasecmp(ya_node->name, "floppy") == 0) {
if (ya_node->n_intrs < 2) {
ya_node->intrs = &gatwick_int_pool[count];
count += 2;
}
ya_node->n_intrs = 2;
ya_node->intrs[0].line = 19+irq_base;
ya_node->intrs[1].line = 1+irq_base;
printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n",
ya_node->intrs[0].line, ya_node->intrs[1].line);
}
if (strcasecmp(ya_node->name, "ata4") == 0) {
if (ya_node->n_intrs < 2) {
ya_node->intrs = &gatwick_int_pool[count];
count += 2;
}
ya_node->n_intrs = 2;
ya_node->intrs[0].line = 14+irq_base;
ya_node->intrs[1].line = 3+irq_base;
printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n",
ya_node->intrs[0].line, ya_node->intrs[1].line);
}
ya_node = ya_node->sibling;
}
}
node = node->sibling;
}
if (count > 10) {
printk("WARNING !! Gatwick interrupt pool overflow\n");
printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE);
printk(" requested = %d\n", count);
}
}
/*
* The PowerBook 3400/2400/3500 can have a combo ethernet/modem
* card which includes an ohare chip that acts as a second interrupt
* controller. If we find this second ohare, set it up and fix the
* interrupt value in the device tree for the ethernet chip.
*/
static int __init enable_second_ohare(void)
{
unsigned char bus, devfn;
unsigned short cmd;
unsigned long addr;
struct device_node *irqctrler = find_devices("pci106b,7");
struct device_node *ether;
if (irqctrler == NULL || irqctrler->n_addrs <= 0)
return -1;
addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40);
pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20);
max_irqs = 64;
if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) {
struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler);
if (!hose)
printk(KERN_ERR "Can't find PCI hose for OHare2 !\n");
else {
early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
cmd &= ~PCI_COMMAND_IO;
early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd);
}
}
/* Fix interrupt for the modem/ethernet combo controller. The number
in the device tree (27) is bogus (correct for the ethernet-only
board but not the combo ethernet/modem board).
The real interrupt is 28 on the second controller -> 28+32 = 60.
*/
ether = find_devices("pci1011,14");
if (ether && ether->n_intrs > 0) {
ether->intrs[0].line = 60;
printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n",
ether->intrs[0].line);
}
/* Return the interrupt number of the cascade */
return irqctrler->intrs[0].line;
}
#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)
__do_IRQ(irq, regs);
return IRQ_HANDLED;
}
static struct irqaction k2u3_cascade_action = {
.handler = k2u3_action,
.flags = 0,
.mask = CPU_MASK_NONE,
.name = "U3->K2 Cascade",
};
#endif /* CONFIG_POWER4 */
#ifdef CONFIG_XMON
static struct irqaction xmon_action = {
.handler = xmon_irq,
.flags = 0,
.mask = CPU_MASK_NONE,
.name = "NMI - XMON"
};
#endif
static struct irqaction gatwick_cascade_action = {
.handler = gatwick_action,
.flags = SA_INTERRUPT,
.mask = CPU_MASK_NONE,
.name = "cascade",
};
void __init pmac_pic_init(void)
{
int i;
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.
*/
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)
{
if (irqctrler->n_addrs > 0)
{
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, 128);
OpenPIC_InitSenses = senses;
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 (setup_irq(irqctrler2->intrs[0].line,
&k2u3_cascade_action))
printk("Unable to get OpenPIC IRQ for cascade\n");
}
#endif /* CONFIG_POWER4 */
#ifdef CONFIG_XMON
{
struct device_node* pswitch;
int nmi_irq;
pswitch = find_devices("programmer-switch");
if (pswitch && pswitch->n_intrs) {
nmi_irq = pswitch->intrs[0].line;
openpic_init_nmi_irq(nmi_irq);
setup_irq(nmi_irq, &xmon_action);
}
}
#endif /* CONFIG_XMON */
return;
}
irqctrler = NULL;
}
/* Get the level/edge settings, assume if it's not
* a Grand Central nor an OHare, then it's an Heathrow
* (or Paddington).
*/
if (find_devices("gc"))
level_mask[0] = GC_LEVEL_MASK;
else if (find_devices("ohare")) {
level_mask[0] = OHARE_LEVEL_MASK;
/* We might have a second cascaded ohare */
level_mask[1] = OHARE_LEVEL_MASK;
} else {
level_mask[0] = HEATHROW_LEVEL_MASK;
level_mask[1] = 0;
/* We might have a second cascaded heathrow */
level_mask[2] = HEATHROW_LEVEL_MASK;
level_mask[3] = 0;
}
/*
* G3 powermacs and 1999 G3 PowerBooks have 64 interrupts,
* 1998 G3 Series PowerBooks have 128,
* other powermacs have 32.
* The combo ethernet/modem card for the Powerstar powerbooks
* (2400/3400/3500, ohare based) has a second ohare chip
* effectively making a total of 64.
*/
max_irqs = max_real_irqs = 32;
irqctrler = find_devices("mac-io");
if (irqctrler)
{
max_real_irqs = 64;
if (irqctrler->next)
max_irqs = 128;
else
max_irqs = 64;
}
for ( i = 0; i < max_real_irqs ; i++ )
irq_desc[i].handler = &pmac_pic;
/* get addresses of first controller */
if (irqctrler) {
if (irqctrler->n_addrs > 0) {
addr = (unsigned long)
ioremap(irqctrler->addrs[0].address, 0x40);
for (i = 0; i < 2; ++i)
pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
(addr + (2 - i) * 0x10);
}
/* get addresses of second controller */
irqctrler = irqctrler->next;
if (irqctrler && irqctrler->n_addrs > 0) {
addr = (unsigned long)
ioremap(irqctrler->addrs[0].address, 0x40);
for (i = 2; i < 4; ++i)
pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
(addr + (4 - i) * 0x10);
irq_cascade = irqctrler->intrs[0].line;
if (device_is_compatible(irqctrler, "gatwick"))
pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs);
}
} else {
/* older powermacs have a GC (grand central) or ohare at
f3000000, with interrupt control registers at f3000020. */
addr = (unsigned long) ioremap(0xf3000000, 0x40);
pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20);
}
/* PowerBooks 3400 and 3500 can have a second controller in a second
ohare chip, on the combo ethernet/modem card */
if (machine_is_compatible("AAPL,3400/2400")
|| machine_is_compatible("AAPL,3500"))
irq_cascade = enable_second_ohare();
/* disable all interrupts in all controllers */
for (i = 0; i * 32 < max_irqs; ++i)
out_le32(&pmac_irq_hw[i]->enable, 0);
/* mark level interrupts */
for (i = 0; i < max_irqs; i++)
if (level_mask[i >> 5] & (1UL << (i & 0x1f)))
irq_desc[i].status = IRQ_LEVEL;
/* get interrupt line of secondary interrupt controller */
if (irq_cascade >= 0) {
printk(KERN_INFO "irq: secondary controller on irq %d\n",
(int)irq_cascade);
for ( i = max_real_irqs ; i < max_irqs ; i++ )
irq_desc[i].handler = &gatwick_pic;
setup_irq(irq_cascade, &gatwick_cascade_action);
}
printk("System has %d possible interrupts\n", max_irqs);
if (max_irqs != max_real_irqs)
printk(KERN_DEBUG "%d interrupts on main controller\n",
max_real_irqs);
#ifdef CONFIG_XMON
setup_irq(20, &xmon_action);
#endif /* CONFIG_XMON */
}
#ifdef CONFIG_PM
/*
* These procedures are used in implementing sleep on the powerbooks.
* sleep_save_intrs() saves the states of all interrupt enables
* and disables all interrupts except for the nominated one.
* sleep_restore_intrs() restores the states of all interrupt enables.
*/
unsigned long sleep_save_mask[2];
/* This used to be passed by the PMU driver but that link got
* broken with the new driver model. We use this tweak for now...
*/
static int pmacpic_find_viaint(void)
{
int viaint = -1;
#ifdef CONFIG_ADB_PMU
struct device_node *np;
if (pmu_get_model() != PMU_OHARE_BASED)
goto not_found;
np = of_find_node_by_name(NULL, "via-pmu");
if (np == NULL)
goto not_found;
viaint = np->intrs[0].line;
#endif /* CONFIG_ADB_PMU */
not_found:
return viaint;
}
static int pmacpic_suspend(struct sys_device *sysdev, pm_message_t state)
{
int viaint = pmacpic_find_viaint();
sleep_save_mask[0] = ppc_cached_irq_mask[0];
sleep_save_mask[1] = ppc_cached_irq_mask[1];
ppc_cached_irq_mask[0] = 0;
ppc_cached_irq_mask[1] = 0;
if (viaint > 0)
set_bit(viaint, ppc_cached_irq_mask);
out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]);
if (max_real_irqs > 32)
out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]);
(void)in_le32(&pmac_irq_hw[0]->event);
/* make sure mask gets to controller before we return to caller */
mb();
(void)in_le32(&pmac_irq_hw[0]->enable);
return 0;
}
static int pmacpic_resume(struct sys_device *sysdev)
{
int i;
out_le32(&pmac_irq_hw[0]->enable, 0);
if (max_real_irqs > 32)
out_le32(&pmac_irq_hw[1]->enable, 0);
mb();
for (i = 0; i < max_real_irqs; ++i)
if (test_bit(i, sleep_save_mask))
pmac_unmask_irq(i);
return 0;
}
#endif /* CONFIG_PM */
static struct sysdev_class pmacpic_sysclass = {
set_kset_name("pmac_pic"),
};
static struct sys_device device_pmacpic = {
.id = 0,
.cls = &pmacpic_sysclass,
};
static struct sysdev_driver driver_pmacpic = {
#ifdef CONFIG_PM
.suspend = &pmacpic_suspend,
.resume = &pmacpic_resume,
#endif /* CONFIG_PM */
};
static int __init init_pmacpic_sysfs(void)
{
if (max_irqs == 0)
return -ENODEV;
printk(KERN_DEBUG "Registering pmac pic with sysfs...\n");
sysdev_class_register(&pmacpic_sysclass);
sysdev_register(&device_pmacpic);
sysdev_driver_register(&pmacpic_sysclass, &driver_pmacpic);
return 0;
}
subsys_initcall(init_pmacpic_sysfs);
#ifndef __PPC_PLATFORMS_PMAC_PIC_H
#define __PPC_PLATFORMS_PMAC_PIC_H
#include <linux/irq.h>
extern struct hw_interrupt_type pmac_pic;
void pmac_pic_init(void);
int pmac_get_irq(struct pt_regs *regs);
#endif /* __PPC_PLATFORMS_PMAC_PIC_H */
/*
* arch/ppc/platforms/setup.c
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Adapted for Power Macintosh by Paul Mackerras
* Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
*
* Derived from "arch/alpha/kernel/setup.c"
* Copyright (C) 1995 Linus Torvalds
*
* 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
* 2 of the License, or (at your option) any later version.
*
*/
/*
* bootup setup stuff..
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <linux/initrd.h>
#include <linux/vt_kern.h>
#include <linux/console.h>
#include <linux/ide.h>
#include <linux/pci.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
#include <linux/seq_file.h>
#include <linux/root_dev.h>
#include <linux/bitops.h>
#include <linux/suspend.h>
#include <asm/reg.h>
#include <asm/sections.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/pci-bridge.h>
#include <asm/ohare.h>
#include <asm/mediabay.h>
#include <asm/machdep.h>
#include <asm/dma.h>
#include <asm/bootx.h>
#include <asm/cputable.h>
#include <asm/btext.h>
#include <asm/pmac_feature.h>
#include <asm/time.h>
#include <asm/of_device.h>
#include <asm/mmu_context.h>
#include "pmac_pic.h"
#include "mem_pieces.h"
#undef SHOW_GATWICK_IRQS
extern long pmac_time_init(void);
extern unsigned long pmac_get_rtc_time(void);
extern int pmac_set_rtc_time(unsigned long nowtime);
extern void pmac_read_rtc_time(void);
extern void pmac_calibrate_decr(void);
extern void pmac_pcibios_fixup(void);
extern void pmac_find_bridges(void);
extern unsigned long pmac_ide_get_base(int index);
extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
unsigned long data_port, unsigned long ctrl_port, int *irq);
extern void pmac_nvram_update(void);
extern unsigned char pmac_nvram_read_byte(int addr);
extern void pmac_nvram_write_byte(int addr, unsigned char val);
extern int pmac_pci_enable_device_hook(struct pci_dev *dev, int initial);
extern void pmac_pcibios_after_init(void);
extern int of_show_percpuinfo(struct seq_file *m, int i);
struct device_node *memory_node;
unsigned char drive_info;
int ppc_override_l2cr = 0;
int ppc_override_l2cr_value;
int has_l2cache = 0;
static int current_root_goodness = -1;
extern int pmac_newworld;
#define DEFAULT_ROOT_DEVICE Root_SDA1 /* sda1 - slightly silly choice */
extern void zs_kgdb_hook(int tty_num);
static void ohare_init(void);
#ifdef CONFIG_BOOTX_TEXT
static void pmac_progress(char *s, unsigned short hex);
#endif
sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN;
#ifdef CONFIG_SMP
extern struct smp_ops_t psurge_smp_ops;
extern struct smp_ops_t core99_smp_ops;
#endif /* CONFIG_SMP */
static int
pmac_show_cpuinfo(struct seq_file *m)
{
struct device_node *np;
char *pp;
int plen;
int mbmodel = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
NULL, PMAC_MB_INFO_MODEL, 0);
unsigned int mbflags = (unsigned int)pmac_call_feature(PMAC_FTR_GET_MB_INFO,
NULL, PMAC_MB_INFO_FLAGS, 0);
char* mbname;
if (pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_NAME, (int)&mbname) != 0)
mbname = "Unknown";
/* find motherboard type */
seq_printf(m, "machine\t\t: ");
np = find_devices("device-tree");
if (np != NULL) {
pp = (char *) get_property(np, "model", NULL);
if (pp != NULL)
seq_printf(m, "%s\n", pp);
else
seq_printf(m, "PowerMac\n");
pp = (char *) get_property(np, "compatible", &plen);
if (pp != NULL) {
seq_printf(m, "motherboard\t:");
while (plen > 0) {
int l = strlen(pp) + 1;
seq_printf(m, " %s", pp);
plen -= l;
pp += l;
}
seq_printf(m, "\n");
}
} else
seq_printf(m, "PowerMac\n");
/* print parsed model */
seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname);
seq_printf(m, "pmac flags\t: %08x\n", mbflags);
/* find l2 cache info */
np = find_devices("l2-cache");
if (np == 0)
np = find_type_devices("cache");
if (np != 0) {
unsigned int *ic = (unsigned int *)
get_property(np, "i-cache-size", NULL);
unsigned int *dc = (unsigned int *)
get_property(np, "d-cache-size", NULL);
seq_printf(m, "L2 cache\t:");
has_l2cache = 1;
if (get_property(np, "cache-unified", NULL) != 0 && dc) {
seq_printf(m, " %dK unified", *dc / 1024);
} else {
if (ic)
seq_printf(m, " %dK instruction", *ic / 1024);
if (dc)
seq_printf(m, "%s %dK data",
(ic? " +": ""), *dc / 1024);
}
pp = get_property(np, "ram-type", NULL);
if (pp)
seq_printf(m, " %s", pp);
seq_printf(m, "\n");
}
/* find ram info */
np = find_devices("memory");
if (np != 0) {
int n;
struct reg_property *reg = (struct reg_property *)
get_property(np, "reg", &n);
if (reg != 0) {
unsigned long total = 0;
for (n /= sizeof(struct reg_property); n > 0; --n)
total += (reg++)->size;
seq_printf(m, "memory\t\t: %luMB\n", total >> 20);
}
}
/* Checks "l2cr-value" property in the registry */
np = find_devices("cpus");
if (np == 0)
np = find_type_devices("cpu");
if (np != 0) {
unsigned int *l2cr = (unsigned int *)
get_property(np, "l2cr-value", NULL);
if (l2cr != 0) {
seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr);
}
}
/* Indicate newworld/oldworld */
seq_printf(m, "pmac-generation\t: %s\n",
pmac_newworld ? "NewWorld" : "OldWorld");
return 0;
}
static int
pmac_show_percpuinfo(struct seq_file *m, int i)
{
#ifdef CONFIG_CPU_FREQ_PMAC
extern unsigned int pmac_get_one_cpufreq(int i);
unsigned int freq = pmac_get_one_cpufreq(i);
if (freq != 0) {
seq_printf(m, "clock\t\t: %dMHz\n", freq/1000);
return 0;
}
#endif /* CONFIG_CPU_FREQ_PMAC */
return of_show_percpuinfo(m, i);
}
static volatile u32 *sysctrl_regs;
void __init
pmac_setup_arch(void)
{
struct device_node *cpu;
int *fp;
unsigned long pvr;
pvr = PVR_VER(mfspr(SPRN_PVR));
/* Set loops_per_jiffy to a half-way reasonable value,
for use until calibrate_delay gets called. */
cpu = find_type_devices("cpu");
if (cpu != 0) {
fp = (int *) get_property(cpu, "clock-frequency", NULL);
if (fp != 0) {
if (pvr == 4 || pvr >= 8)
/* 604, G3, G4 etc. */
loops_per_jiffy = *fp / HZ;
else
/* 601, 603, etc. */
loops_per_jiffy = *fp / (2*HZ);
} else
loops_per_jiffy = 50000000 / HZ;
}
/* this area has the CPU identification register
and some registers used by smp boards */
sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000);
ohare_init();
/* Lookup PCI hosts */
pmac_find_bridges();
/* Checks "l2cr-value" property in the registry */
if (cpu_has_feature(CPU_FTR_L2CR)) {
struct device_node *np = find_devices("cpus");
if (np == 0)
np = find_type_devices("cpu");
if (np != 0) {
unsigned int *l2cr = (unsigned int *)
get_property(np, "l2cr-value", NULL);
if (l2cr != 0) {
ppc_override_l2cr = 1;
ppc_override_l2cr_value = *l2cr;
_set_L2CR(0);
_set_L2CR(ppc_override_l2cr_value);
}
}
}
if (ppc_override_l2cr)
printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n",
ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000)
? "enabled" : "disabled");
#ifdef CONFIG_KGDB
zs_kgdb_hook(0);
#endif
#ifdef CONFIG_ADB_CUDA
find_via_cuda();
#else
if (find_devices("via-cuda")) {
printk("WARNING ! Your machine is Cuda based but your kernel\n");
printk(" wasn't compiled with CONFIG_ADB_CUDA option !\n");
}
#endif
#ifdef CONFIG_ADB_PMU
find_via_pmu();
#else
if (find_devices("via-pmu")) {
printk("WARNING ! Your machine is PMU based but your kernel\n");
printk(" wasn't compiled with CONFIG_ADB_PMU option !\n");
}
#endif
#ifdef CONFIG_NVRAM
pmac_nvram_init();
#endif
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start)
ROOT_DEV = Root_RAM0;
else
#endif
ROOT_DEV = DEFAULT_ROOT_DEVICE;
#ifdef CONFIG_SMP
/* Check for Core99 */
if (find_devices("uni-n") || find_devices("u3"))
smp_ops = &core99_smp_ops;
else
smp_ops = &psurge_smp_ops;
#endif /* CONFIG_SMP */
pci_create_OF_bus_map();
}
static void __init ohare_init(void)
{
/*
* Turn on the L2 cache.
* We assume that we have a PSX memory controller iff
* we have an ohare I/O controller.
*/
if (find_devices("ohare") != NULL) {
if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) {
if (sysctrl_regs[4] & 0x10)
sysctrl_regs[4] |= 0x04000020;
else
sysctrl_regs[4] |= 0x04000000;
if(has_l2cache)
printk(KERN_INFO "Level 2 cache enabled\n");
}
}
}
extern char *bootpath;
extern char *bootdevice;
void *boot_host;
int boot_target;
int boot_part;
extern dev_t boot_dev;
#ifdef CONFIG_SCSI
void __init
note_scsi_host(struct device_node *node, void *host)
{
int l;
char *p;
l = strlen(node->full_name);
if (bootpath != NULL && bootdevice != NULL
&& strncmp(node->full_name, bootdevice, l) == 0
&& (bootdevice[l] == '/' || bootdevice[l] == 0)) {
boot_host = host;
/*
* There's a bug in OF 1.0.5. (Why am I not surprised.)
* If you pass a path like scsi/sd@1:0 to canon, it returns
* something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0
* That is, the scsi target number doesn't get preserved.
* So we pick the target number out of bootpath and use that.
*/
p = strstr(bootpath, "/sd@");
if (p != NULL) {
p += 4;
boot_target = simple_strtoul(p, NULL, 10);
p = strchr(p, ':');
if (p != NULL)
boot_part = simple_strtoul(p + 1, NULL, 10);
}
}
}
#endif
#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
static dev_t __init
find_ide_boot(void)
{
char *p;
int n;
dev_t __init pmac_find_ide_boot(char *bootdevice, int n);
if (bootdevice == NULL)
return 0;
p = strrchr(bootdevice, '/');
if (p == NULL)
return 0;
n = p - bootdevice;
return pmac_find_ide_boot(bootdevice, n);
}
#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */
static void __init
find_boot_device(void)
{
#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
boot_dev = find_ide_boot();
#endif
}
static int initializing = 1;
/* TODO: Merge the suspend-to-ram with the common code !!!
* currently, this is a stub implementation for suspend-to-disk
* only
*/
#ifdef CONFIG_SOFTWARE_SUSPEND
static int pmac_pm_prepare(suspend_state_t state)
{
printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
return 0;
}
static int pmac_pm_enter(suspend_state_t state)
{
printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
/* Giveup the lazy FPU & vec so we don't have to back them
* up from the low level code
*/
enable_kernel_fp();
#ifdef CONFIG_ALTIVEC
if (cur_cpu_spec->cpu_features & CPU_FTR_ALTIVEC)
enable_kernel_altivec();
#endif /* CONFIG_ALTIVEC */
return 0;
}
static int pmac_pm_finish(suspend_state_t state)
{
printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
/* Restore userland MMU context */
set_context(current->active_mm->context, current->active_mm->pgd);
return 0;
}
static struct pm_ops pmac_pm_ops = {
.pm_disk_mode = PM_DISK_SHUTDOWN,
.prepare = pmac_pm_prepare,
.enter = pmac_pm_enter,
.finish = pmac_pm_finish,
};
#endif /* CONFIG_SOFTWARE_SUSPEND */
static int pmac_late_init(void)
{
initializing = 0;
#ifdef CONFIG_SOFTWARE_SUSPEND
pm_set_ops(&pmac_pm_ops);
#endif /* CONFIG_SOFTWARE_SUSPEND */
return 0;
}
late_initcall(pmac_late_init);
/* can't be __init - can be called whenever a disk is first accessed */
void
note_bootable_part(dev_t dev, int part, int goodness)
{
static int found_boot = 0;
char *p;
if (!initializing)
return;
if ((goodness <= current_root_goodness) &&
ROOT_DEV != DEFAULT_ROOT_DEVICE)
return;
p = strstr(saved_command_line, "root=");
if (p != NULL && (p == saved_command_line || p[-1] == ' '))
return;
if (!found_boot) {
find_boot_device();
found_boot = 1;
}
if (!boot_dev || dev == boot_dev) {
ROOT_DEV = dev + part;
boot_dev = 0;
current_root_goodness = goodness;
}
}
static void
pmac_restart(char *cmd)
{
#ifdef CONFIG_ADB_CUDA
struct adb_request req;
#endif /* CONFIG_ADB_CUDA */
switch (sys_ctrler) {
#ifdef CONFIG_ADB_CUDA
case SYS_CTRLER_CUDA:
cuda_request(&req, NULL, 2, CUDA_PACKET,
CUDA_RESET_SYSTEM);
for (;;)
cuda_poll();
break;
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_ADB_PMU
case SYS_CTRLER_PMU:
pmu_restart();
break;
#endif /* CONFIG_ADB_PMU */
default: ;
}
}
static void
pmac_power_off(void)
{
#ifdef CONFIG_ADB_CUDA
struct adb_request req;
#endif /* CONFIG_ADB_CUDA */
switch (sys_ctrler) {
#ifdef CONFIG_ADB_CUDA
case SYS_CTRLER_CUDA:
cuda_request(&req, NULL, 2, CUDA_PACKET,
CUDA_POWERDOWN);
for (;;)
cuda_poll();
break;
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_ADB_PMU
case SYS_CTRLER_PMU:
pmu_shutdown();
break;
#endif /* CONFIG_ADB_PMU */
default: ;
}
}
static void
pmac_halt(void)
{
pmac_power_off();
}
/*
* Read in a property describing some pieces of memory.
*/
static int __init
get_mem_prop(char *name, struct mem_pieces *mp)
{
struct reg_property *rp;
int i, s;
unsigned int *ip;
int nac = prom_n_addr_cells(memory_node);
int nsc = prom_n_size_cells(memory_node);
ip = (unsigned int *) get_property(memory_node, name, &s);
if (ip == NULL) {
printk(KERN_ERR "error: couldn't get %s property on /memory\n",
name);
return 0;
}
s /= (nsc + nac) * 4;
rp = mp->regions;
for (i = 0; i < s; ++i, ip += nac+nsc) {
if (nac >= 2 && ip[nac-2] != 0)
continue;
rp->address = ip[nac-1];
if (nsc >= 2 && ip[nac+nsc-2] != 0)
rp->size = ~0U;
else
rp->size = ip[nac+nsc-1];
++rp;
}
mp->n_regions = rp - mp->regions;
/* Make sure the pieces are sorted. */
mem_pieces_sort(mp);
mem_pieces_coalesce(mp);
return 1;
}
/*
* On systems with Open Firmware, collect information about
* physical RAM and which pieces are already in use.
* At this point, we have (at least) the first 8MB mapped with a BAT.
* Our text, data, bss use something over 1MB, starting at 0.
* Open Firmware may be using 1MB at the 4MB point.
*/
unsigned long __init
pmac_find_end_of_memory(void)
{
unsigned long a, total;
struct mem_pieces phys_mem;
/*
* Find out where physical memory is, and check that it
* starts at 0 and is contiguous. It seems that RAM is
* always physically contiguous on Power Macintoshes.
*
* Supporting discontiguous physical memory isn't hard,
* it just makes the virtual <-> physical mapping functions
* more complicated (or else you end up wasting space
* in mem_map).
*/
memory_node = find_devices("memory");
if (memory_node == NULL || !get_mem_prop("reg", &phys_mem)
|| phys_mem.n_regions == 0)
panic("No RAM??");
a = phys_mem.regions[0].address;
if (a != 0)
panic("RAM doesn't start at physical address 0");
total = phys_mem.regions[0].size;
if (phys_mem.n_regions > 1) {
printk("RAM starting at 0x%x is not contiguous\n",
phys_mem.regions[1].address);
printk("Using RAM from 0 to 0x%lx\n", total-1);
}
return total;
}
void __init
pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
{
/* isa_io_base gets set in pmac_find_bridges */
isa_mem_base = PMAC_ISA_MEM_BASE;
pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
ISA_DMA_THRESHOLD = ~0L;
DMA_MODE_READ = 1;
DMA_MODE_WRITE = 2;
ppc_md.setup_arch = pmac_setup_arch;
ppc_md.show_cpuinfo = pmac_show_cpuinfo;
ppc_md.show_percpuinfo = pmac_show_percpuinfo;
ppc_md.init_IRQ = pmac_pic_init;
ppc_md.get_irq = pmac_get_irq; /* Changed later on ... */
ppc_md.pcibios_fixup = pmac_pcibios_fixup;
ppc_md.pcibios_enable_device_hook = pmac_pci_enable_device_hook;
ppc_md.pcibios_after_init = pmac_pcibios_after_init;
ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot;
ppc_md.restart = pmac_restart;
ppc_md.power_off = pmac_power_off;
ppc_md.halt = pmac_halt;
ppc_md.time_init = pmac_time_init;
ppc_md.set_rtc_time = pmac_set_rtc_time;
ppc_md.get_rtc_time = pmac_get_rtc_time;
ppc_md.calibrate_decr = pmac_calibrate_decr;
ppc_md.find_end_of_memory = pmac_find_end_of_memory;
ppc_md.feature_call = pmac_do_feature_call;
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
#ifdef CONFIG_BLK_DEV_IDE_PMAC
ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports;
ppc_ide_md.default_io_base = pmac_ide_get_base;
#endif /* CONFIG_BLK_DEV_IDE_PMAC */
#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
#ifdef CONFIG_BOOTX_TEXT
ppc_md.progress = pmac_progress;
#endif /* CONFIG_BOOTX_TEXT */
if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0);
}
#ifdef CONFIG_BOOTX_TEXT
static void __init
pmac_progress(char *s, unsigned short hex)
{
if (boot_text_mapped) {
btext_drawstring(s);
btext_drawchar('\n');
}
}
#endif /* CONFIG_BOOTX_TEXT */
static int __init
pmac_declare_of_platform_devices(void)
{
struct device_node *np;
np = find_devices("uni-n");
if (np) {
for (np = np->child; np != NULL; np = np->sibling)
if (strncmp(np->name, "i2c", 3) == 0) {
of_platform_device_create(np, "uni-n-i2c",
NULL);
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",
NULL);
break;
}
}
np = find_devices("valkyrie");
if (np)
of_platform_device_create(np, "valkyrie", NULL);
np = find_devices("platinum");
if (np)
of_platform_device_create(np, "platinum", NULL);
return 0;
}
device_initcall(pmac_declare_of_platform_devices);
/*
* This file contains sleep low-level functions for PowerBook G3.
* Copyright (C) 1999 Benjamin Herrenschmidt (benh@kernel.crashing.org)
* and Paul Mackerras (paulus@samba.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/cache.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#define MAGIC 0x4c617273 /* 'Lars' */
/*
* Structure for storing CPU registers on the stack.
*/
#define SL_SP 0
#define SL_PC 4
#define SL_MSR 8
#define SL_SDR1 0xc
#define SL_SPRG0 0x10 /* 4 sprg's */
#define SL_DBAT0 0x20
#define SL_IBAT0 0x28
#define SL_DBAT1 0x30
#define SL_IBAT1 0x38
#define SL_DBAT2 0x40
#define SL_IBAT2 0x48
#define SL_DBAT3 0x50
#define SL_IBAT3 0x58
#define SL_TB 0x60
#define SL_R2 0x68
#define SL_CR 0x6c
#define SL_R12 0x70 /* r12 to r31 */
#define SL_SIZE (SL_R12 + 80)
.section .text
.align 5
#if defined(CONFIG_PM) || defined(CONFIG_CPU_FREQ_PMAC)
/* This gets called by via-pmu.c late during the sleep process.
* The PMU was already send the sleep command and will shut us down
* soon. We need to save all that is needed and setup the wakeup
* vector that will be called by the ROM on wakeup
*/
_GLOBAL(low_sleep_handler)
#ifndef CONFIG_6xx
blr
#else
mflr r0
stw r0,4(r1)
stwu r1,-SL_SIZE(r1)
mfcr r0
stw r0,SL_CR(r1)
stw r2,SL_R2(r1)
stmw r12,SL_R12(r1)
/* Save MSR & SDR1 */
mfmsr r4
stw r4,SL_MSR(r1)
mfsdr1 r4
stw r4,SL_SDR1(r1)
/* Get a stable timebase and save it */
1: mftbu r4
stw r4,SL_TB(r1)
mftb r5
stw r5,SL_TB+4(r1)
mftbu r3
cmpw r3,r4
bne 1b
/* Save SPRGs */
mfsprg r4,0
stw r4,SL_SPRG0(r1)
mfsprg r4,1
stw r4,SL_SPRG0+4(r1)
mfsprg r4,2
stw r4,SL_SPRG0+8(r1)
mfsprg r4,3
stw r4,SL_SPRG0+12(r1)
/* Save BATs */
mfdbatu r4,0
stw r4,SL_DBAT0(r1)
mfdbatl r4,0
stw r4,SL_DBAT0+4(r1)
mfdbatu r4,1
stw r4,SL_DBAT1(r1)
mfdbatl r4,1
stw r4,SL_DBAT1+4(r1)
mfdbatu r4,2
stw r4,SL_DBAT2(r1)
mfdbatl r4,2
stw r4,SL_DBAT2+4(r1)
mfdbatu r4,3
stw r4,SL_DBAT3(r1)
mfdbatl r4,3
stw r4,SL_DBAT3+4(r1)
mfibatu r4,0
stw r4,SL_IBAT0(r1)
mfibatl r4,0
stw r4,SL_IBAT0+4(r1)
mfibatu r4,1
stw r4,SL_IBAT1(r1)
mfibatl r4,1
stw r4,SL_IBAT1+4(r1)
mfibatu r4,2
stw r4,SL_IBAT2(r1)
mfibatl r4,2
stw r4,SL_IBAT2+4(r1)
mfibatu r4,3
stw r4,SL_IBAT3(r1)
mfibatl r4,3
stw r4,SL_IBAT3+4(r1)
/* Backup various CPU config stuffs */
bl __save_cpu_setup
/* The ROM can wake us up via 2 different vectors:
* - On wallstreet & lombard, we must write a magic
* value 'Lars' at address 4 and a pointer to a
* memory location containing the PC to resume from
* at address 0.
* - On Core99, we must store the wakeup vector at
* address 0x80 and eventually it's parameters
* at address 0x84. I've have some trouble with those
* parameters however and I no longer use them.
*/
lis r5,grackle_wake_up@ha
addi r5,r5,grackle_wake_up@l
tophys(r5,r5)
stw r5,SL_PC(r1)
lis r4,KERNELBASE@h
tophys(r5,r1)
addi r5,r5,SL_PC
lis r6,MAGIC@ha
addi r6,r6,MAGIC@l
stw r5,0(r4)
stw r6,4(r4)
/* Setup stuffs at 0x80-0x84 for Core99 */
lis r3,core99_wake_up@ha
addi r3,r3,core99_wake_up@l
tophys(r3,r3)
stw r3,0x80(r4)
stw r5,0x84(r4)
/* Store a pointer to our backup storage into
* a kernel global
*/
lis r3,sleep_storage@ha
addi r3,r3,sleep_storage@l
stw r5,0(r3)
.globl low_cpu_die
low_cpu_die:
/* Flush & disable all caches */
bl flush_disable_caches
/* Turn off data relocation. */
mfmsr r3 /* Save MSR in r7 */
rlwinm r3,r3,0,28,26 /* Turn off DR bit */
sync
mtmsr r3
isync
BEGIN_FTR_SECTION
/* Flush any pending L2 data prefetches to work around HW bug */
sync
lis r3,0xfff0
lwz r0,0(r3) /* perform cache-inhibited load to ROM */
sync /* (caches are disabled at this point) */
END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
/*
* Set the HID0 and MSR for sleep.
*/
mfspr r2,SPRN_HID0
rlwinm r2,r2,0,10,7 /* clear doze, nap */
oris r2,r2,HID0_SLEEP@h
sync
isync
mtspr SPRN_HID0,r2
sync
/* This loop puts us back to sleep in case we have a spurrious
* wakeup so that the host bridge properly stays asleep. The
* CPU will be turned off, either after a known time (about 1
* second) on wallstreet & lombard, or as soon as the CPU enters
* SLEEP mode on core99
*/
mfmsr r2
oris r2,r2,MSR_POW@h
1: sync
mtmsr r2
isync
b 1b
/*
* Here is the resume code.
*/
/*
* Core99 machines resume here
* r4 has the physical address of SL_PC(sp) (unused)
*/
_GLOBAL(core99_wake_up)
/* Make sure HID0 no longer contains any sleep bit and that data cache
* is disabled
*/
mfspr r3,SPRN_HID0
rlwinm r3,r3,0,11,7 /* clear SLEEP, NAP, DOZE bits */
rlwinm 3,r3,0,18,15 /* clear DCE, ICE */
mtspr SPRN_HID0,r3
sync
isync
/* sanitize MSR */
mfmsr r3
ori r3,r3,MSR_EE|MSR_IP
xori r3,r3,MSR_EE|MSR_IP
sync
isync
mtmsr r3
sync
isync
/* Recover sleep storage */
lis r3,sleep_storage@ha
addi r3,r3,sleep_storage@l
tophys(r3,r3)
lwz r1,0(r3)
/* Pass thru to older resume code ... */
/*
* Here is the resume code for older machines.
* r1 has the physical address of SL_PC(sp).
*/
grackle_wake_up:
/* Restore the kernel's segment registers before
* we do any r1 memory access as we are not sure they
* are in a sane state above the first 256Mb region
*/
li r0,16 /* load up segment register values */
mtctr r0 /* for context 0 */
lis r3,0x2000 /* Ku = 1, VSID = 0 */
li r4,0
3: mtsrin r3,r4
addi r3,r3,0x111 /* increment VSID */
addis r4,r4,0x1000 /* address of next segment */
bdnz 3b
sync
isync
subi r1,r1,SL_PC
/* Restore various CPU config stuffs */
bl __restore_cpu_setup
/* Make sure all FPRs have been initialized */
bl reloc_offset
bl __init_fpu_registers
/* Invalidate & enable L1 cache, we don't care about
* whatever the ROM may have tried to write to memory
*/
bl __inval_enable_L1
/* Restore the BATs, and SDR1. Then we can turn on the MMU. */
lwz r4,SL_SDR1(r1)
mtsdr1 r4
lwz r4,SL_SPRG0(r1)
mtsprg 0,r4
lwz r4,SL_SPRG0+4(r1)
mtsprg 1,r4
lwz r4,SL_SPRG0+8(r1)
mtsprg 2,r4
lwz r4,SL_SPRG0+12(r1)
mtsprg 3,r4
lwz r4,SL_DBAT0(r1)
mtdbatu 0,r4
lwz r4,SL_DBAT0+4(r1)
mtdbatl 0,r4
lwz r4,SL_DBAT1(r1)
mtdbatu 1,r4
lwz r4,SL_DBAT1+4(r1)
mtdbatl 1,r4
lwz r4,SL_DBAT2(r1)
mtdbatu 2,r4
lwz r4,SL_DBAT2+4(r1)
mtdbatl 2,r4
lwz r4,SL_DBAT3(r1)
mtdbatu 3,r4
lwz r4,SL_DBAT3+4(r1)
mtdbatl 3,r4
lwz r4,SL_IBAT0(r1)
mtibatu 0,r4
lwz r4,SL_IBAT0+4(r1)
mtibatl 0,r4
lwz r4,SL_IBAT1(r1)
mtibatu 1,r4
lwz r4,SL_IBAT1+4(r1)
mtibatl 1,r4
lwz r4,SL_IBAT2(r1)
mtibatu 2,r4
lwz r4,SL_IBAT2+4(r1)
mtibatl 2,r4
lwz r4,SL_IBAT3(r1)
mtibatu 3,r4
lwz r4,SL_IBAT3+4(r1)
mtibatl 3,r4
BEGIN_FTR_SECTION
li r4,0
mtspr SPRN_DBAT4U,r4
mtspr SPRN_DBAT4L,r4
mtspr SPRN_DBAT5U,r4
mtspr SPRN_DBAT5L,r4
mtspr SPRN_DBAT6U,r4
mtspr SPRN_DBAT6L,r4
mtspr SPRN_DBAT7U,r4
mtspr SPRN_DBAT7L,r4
mtspr SPRN_IBAT4U,r4
mtspr SPRN_IBAT4L,r4
mtspr SPRN_IBAT5U,r4
mtspr SPRN_IBAT5L,r4
mtspr SPRN_IBAT6U,r4
mtspr SPRN_IBAT6L,r4
mtspr SPRN_IBAT7U,r4
mtspr SPRN_IBAT7L,r4
END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
/* Flush all TLBs */
lis r4,0x1000
1: addic. r4,r4,-0x1000
tlbie r4
blt 1b
sync
/* restore the MSR and turn on the MMU */
lwz r3,SL_MSR(r1)
bl turn_on_mmu
/* get back the stack pointer */
tovirt(r1,r1)
/* Restore TB */
li r3,0
mttbl r3
lwz r3,SL_TB(r1)
lwz r4,SL_TB+4(r1)
mttbu r3
mttbl r4
/* Restore the callee-saved registers and return */
lwz r0,SL_CR(r1)
mtcr r0
lwz r2,SL_R2(r1)
lmw r12,SL_R12(r1)
addi r1,r1,SL_SIZE
lwz r0,4(r1)
mtlr r0
blr
turn_on_mmu:
mflr r4
tovirt(r4,r4)
mtsrr0 r4
mtsrr1 r3
sync
isync
rfi
#endif /* defined(CONFIG_PM) || defined(CONFIG_CPU_FREQ) */
.section .data
.balign L1_CACHE_BYTES
sleep_storage:
.long 0
.balign L1_CACHE_BYTES, 0
#endif /* CONFIG_6xx */
.section .text
/*
* SMP support for power macintosh.
*
* We support both the old "powersurge" SMP architecture
* and the current Core99 (G4 PowerMac) machines.
*
* Note that we don't support the very first rev. of
* Apple/DayStar 2 CPUs board, the one with the funky
* watchdog. Hopefully, none of these should be there except
* maybe internally to Apple. I should probably still add some
* code to detect this card though and disable SMP. --BenH.
*
* Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net)
* and Ben Herrenschmidt <benh@kernel.crashing.org>.
*
* Support for DayStar quad CPU cards
* Copyright (C) XLR8, Inc. 1994-2000
*
* 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 <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/cpu.h>
#include <asm/ptrace.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/sections.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/smp.h>
#include <asm/residual.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/time.h>
#include <asm/open_pic.h>
#include <asm/cacheflush.h>
#include <asm/keylargo.h>
/*
* Powersurge (old powermac SMP) support.
*/
extern void __secondary_start_pmac_0(void);
/* Addresses for powersurge registers */
#define HAMMERHEAD_BASE 0xf8000000
#define HHEAD_CONFIG 0x90
#define HHEAD_SEC_INTR 0xc0
/* register for interrupting the primary processor on the powersurge */
/* N.B. this is actually the ethernet ROM! */
#define PSURGE_PRI_INTR 0xf3019000
/* register for storing the start address for the secondary processor */
/* N.B. this is the PCI config space address register for the 1st bridge */
#define PSURGE_START 0xf2800000
/* Daystar/XLR8 4-CPU card */
#define PSURGE_QUAD_REG_ADDR 0xf8800000
#define PSURGE_QUAD_IRQ_SET 0
#define PSURGE_QUAD_IRQ_CLR 1
#define PSURGE_QUAD_IRQ_PRIMARY 2
#define PSURGE_QUAD_CKSTOP_CTL 3
#define PSURGE_QUAD_PRIMARY_ARB 4
#define PSURGE_QUAD_BOARD_ID 6
#define PSURGE_QUAD_WHICH_CPU 7
#define PSURGE_QUAD_CKSTOP_RDBK 8
#define PSURGE_QUAD_RESET_CTL 11
#define PSURGE_QUAD_OUT(r, v) (out_8(quad_base + ((r) << 4) + 4, (v)))
#define PSURGE_QUAD_IN(r) (in_8(quad_base + ((r) << 4) + 4) & 0x0f)
#define PSURGE_QUAD_BIS(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v)))
#define PSURGE_QUAD_BIC(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v)))
/* virtual addresses for the above */
static volatile u8 __iomem *hhead_base;
static volatile u8 __iomem *quad_base;
static volatile u32 __iomem *psurge_pri_intr;
static volatile u8 __iomem *psurge_sec_intr;
static volatile u32 __iomem *psurge_start;
/* values for psurge_type */
#define PSURGE_NONE -1
#define PSURGE_DUAL 0
#define PSURGE_QUAD_OKEE 1
#define PSURGE_QUAD_COTTON 2
#define PSURGE_QUAD_ICEGRASS 3
/* what sort of powersurge board we have */
static int psurge_type = PSURGE_NONE;
/* L2 and L3 cache settings to pass from CPU0 to CPU1 */
volatile static long int core99_l2_cache;
volatile static long int core99_l3_cache;
/* Timebase freeze GPIO */
static unsigned int core99_tb_gpio;
/* Sync flag for HW tb sync */
static volatile int sec_tb_reset = 0;
static unsigned int pri_tb_hi, pri_tb_lo;
static unsigned int pri_tb_stamp;
static void __devinit core99_init_caches(int cpu)
{
if (!cpu_has_feature(CPU_FTR_L2CR))
return;
if (cpu == 0) {
core99_l2_cache = _get_L2CR();
printk("CPU0: L2CR is %lx\n", core99_l2_cache);
} else {
printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR());
_set_L2CR(0);
_set_L2CR(core99_l2_cache);
printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache);
}
if (!cpu_has_feature(CPU_FTR_L3CR))
return;
if (cpu == 0){
core99_l3_cache = _get_L3CR();
printk("CPU0: L3CR is %lx\n", core99_l3_cache);
} else {
printk("CPU%d: L3CR was %lx\n", cpu, _get_L3CR());
_set_L3CR(0);
_set_L3CR(core99_l3_cache);
printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache);
}
}
/*
* Set and clear IPIs for powersurge.
*/
static inline void psurge_set_ipi(int cpu)
{
if (psurge_type == PSURGE_NONE)
return;
if (cpu == 0)
in_be32(psurge_pri_intr);
else if (psurge_type == PSURGE_DUAL)
out_8(psurge_sec_intr, 0);
else
PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_SET, 1 << cpu);
}
static inline void psurge_clr_ipi(int cpu)
{
if (cpu > 0) {
switch(psurge_type) {
case PSURGE_DUAL:
out_8(psurge_sec_intr, ~0);
case PSURGE_NONE:
break;
default:
PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, 1 << cpu);
}
}
}
/*
* On powersurge (old SMP powermac architecture) we don't have
* separate IPIs for separate messages like openpic does. Instead
* we have a bitmap for each processor, where a 1 bit means that
* the corresponding message is pending for that processor.
* Ideally each cpu's entry would be in a different cache line.
* -- paulus.
*/
static unsigned long psurge_smp_message[NR_CPUS];
void psurge_smp_message_recv(struct pt_regs *regs)
{
int cpu = smp_processor_id();
int msg;
/* clear interrupt */
psurge_clr_ipi(cpu);
if (num_online_cpus() < 2)
return;
/* make sure there is a message there */
for (msg = 0; msg < 4; msg++)
if (test_and_clear_bit(msg, &psurge_smp_message[cpu]))
smp_message_recv(msg, regs);
}
irqreturn_t psurge_primary_intr(int irq, void *d, struct pt_regs *regs)
{
psurge_smp_message_recv(regs);
return IRQ_HANDLED;
}
static void smp_psurge_message_pass(int target, int msg)
{
int i;
if (num_online_cpus() < 2)
return;
for (i = 0; i < NR_CPUS; i++) {
if (!cpu_online(i))
continue;
if (target == MSG_ALL
|| (target == MSG_ALL_BUT_SELF && i != smp_processor_id())
|| target == i) {
set_bit(msg, &psurge_smp_message[i]);
psurge_set_ipi(i);
}
}
}
/*
* Determine a quad card presence. We read the board ID register, we
* force the data bus to change to something else, and we read it again.
* It it's stable, then the register probably exist (ugh !)
*/
static int __init psurge_quad_probe(void)
{
int type;
unsigned int i;
type = PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID);
if (type < PSURGE_QUAD_OKEE || type > PSURGE_QUAD_ICEGRASS
|| type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID))
return PSURGE_DUAL;
/* looks OK, try a slightly more rigorous test */
/* bogus is not necessarily cacheline-aligned,
though I don't suppose that really matters. -- paulus */
for (i = 0; i < 100; i++) {
volatile u32 bogus[8];
bogus[(0+i)%8] = 0x00000000;
bogus[(1+i)%8] = 0x55555555;
bogus[(2+i)%8] = 0xFFFFFFFF;
bogus[(3+i)%8] = 0xAAAAAAAA;
bogus[(4+i)%8] = 0x33333333;
bogus[(5+i)%8] = 0xCCCCCCCC;
bogus[(6+i)%8] = 0xCCCCCCCC;
bogus[(7+i)%8] = 0x33333333;
wmb();
asm volatile("dcbf 0,%0" : : "r" (bogus) : "memory");
mb();
if (type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID))
return PSURGE_DUAL;
}
return type;
}
static void __init psurge_quad_init(void)
{
int procbits;
if (ppc_md.progress) ppc_md.progress("psurge_quad_init", 0x351);
procbits = ~PSURGE_QUAD_IN(PSURGE_QUAD_WHICH_CPU);
if (psurge_type == PSURGE_QUAD_ICEGRASS)
PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits);
else
PSURGE_QUAD_BIC(PSURGE_QUAD_CKSTOP_CTL, procbits);
mdelay(33);
out_8(psurge_sec_intr, ~0);
PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, procbits);
PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits);
if (psurge_type != PSURGE_QUAD_ICEGRASS)
PSURGE_QUAD_BIS(PSURGE_QUAD_CKSTOP_CTL, procbits);
PSURGE_QUAD_BIC(PSURGE_QUAD_PRIMARY_ARB, procbits);
mdelay(33);
PSURGE_QUAD_BIC(PSURGE_QUAD_RESET_CTL, procbits);
mdelay(33);
PSURGE_QUAD_BIS(PSURGE_QUAD_PRIMARY_ARB, procbits);
mdelay(33);
}
static int __init smp_psurge_probe(void)
{
int i, ncpus;
/* We don't do SMP on the PPC601 -- paulus */
if (PVR_VER(mfspr(SPRN_PVR)) == 1)
return 1;
/*
* The powersurge cpu board can be used in the generation
* of powermacs that have a socket for an upgradeable cpu card,
* including the 7500, 8500, 9500, 9600.
* The device tree doesn't tell you if you have 2 cpus because
* OF doesn't know anything about the 2nd processor.
* Instead we look for magic bits in magic registers,
* in the hammerhead memory controller in the case of the
* dual-cpu powersurge board. -- paulus.
*/
if (find_devices("hammerhead") == NULL)
return 1;
hhead_base = ioremap(HAMMERHEAD_BASE, 0x800);
quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024);
psurge_sec_intr = hhead_base + HHEAD_SEC_INTR;
psurge_type = psurge_quad_probe();
if (psurge_type != PSURGE_DUAL) {
psurge_quad_init();
/* All released cards using this HW design have 4 CPUs */
ncpus = 4;
} else {
iounmap(quad_base);
if ((in_8(hhead_base + HHEAD_CONFIG) & 0x02) == 0) {
/* not a dual-cpu card */
iounmap(hhead_base);
psurge_type = PSURGE_NONE;
return 1;
}
ncpus = 2;
}
psurge_start = ioremap(PSURGE_START, 4);
psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4);
/* this is not actually strictly necessary -- paulus. */
for (i = 1; i < ncpus; ++i)
smp_hw_index[i] = i;
if (ppc_md.progress) ppc_md.progress("smp_psurge_probe - done", 0x352);
return ncpus;
}
static void __init smp_psurge_kick_cpu(int nr)
{
unsigned long start = __pa(__secondary_start_pmac_0) + nr * 8;
unsigned long a;
/* may need to flush here if secondary bats aren't setup */
for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32)
asm volatile("dcbf 0,%0" : : "r" (a) : "memory");
asm volatile("sync");
if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353);
out_be32(psurge_start, start);
mb();
psurge_set_ipi(nr);
udelay(10);
psurge_clr_ipi(nr);
if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354);
}
/*
* With the dual-cpu powersurge board, the decrementers and timebases
* of both cpus are frozen after the secondary cpu is started up,
* until we give the secondary cpu another interrupt. This routine
* uses this to get the timebases synchronized.
* -- paulus.
*/
static void __init psurge_dual_sync_tb(int cpu_nr)
{
int t;
set_dec(tb_ticks_per_jiffy);
set_tb(0, 0);
last_jiffy_stamp(cpu_nr) = 0;
if (cpu_nr > 0) {
mb();
sec_tb_reset = 1;
return;
}
/* wait for the secondary to have reset its TB before proceeding */
for (t = 10000000; t > 0 && !sec_tb_reset; --t)
;
/* now interrupt the secondary, starting both TBs */
psurge_set_ipi(1);
smp_tb_synchronized = 1;
}
static struct irqaction psurge_irqaction = {
.handler = psurge_primary_intr,
.flags = SA_INTERRUPT,
.mask = CPU_MASK_NONE,
.name = "primary IPI",
};
static void __init smp_psurge_setup_cpu(int cpu_nr)
{
if (cpu_nr == 0) {
/* If we failed to start the second CPU, we should still
* send it an IPI to start the timebase & DEC or we might
* have them stuck.
*/
if (num_online_cpus() < 2) {
if (psurge_type == PSURGE_DUAL)
psurge_set_ipi(1);
return;
}
/* reset the entry point so if we get another intr we won't
* try to startup again */
out_be32(psurge_start, 0x100);
if (setup_irq(30, &psurge_irqaction))
printk(KERN_ERR "Couldn't get primary IPI interrupt");
}
if (psurge_type == PSURGE_DUAL)
psurge_dual_sync_tb(cpu_nr);
}
void __init smp_psurge_take_timebase(void)
{
/* Dummy implementation */
}
void __init smp_psurge_give_timebase(void)
{
/* Dummy implementation */
}
static int __init smp_core99_probe(void)
{
#ifdef CONFIG_6xx
extern int powersave_nap;
#endif
struct device_node *cpus, *firstcpu;
int i, ncpus = 0, boot_cpu = -1;
u32 *tbprop = NULL;
if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345);
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);
if (machine_is_compatible("MacRISC4")) {
extern struct smp_ops_t core99_smp_ops;
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();
for (i = 1; i < ncpus; ++i)
smp_hw_index[i] = i;
#ifdef CONFIG_6xx
powersave_nap = 0;
#endif
core99_init_caches(0);
}
return ncpus;
}
static void __devinit smp_core99_kick_cpu(int nr)
{
unsigned long save_vector, new_vector;
unsigned long flags;
volatile unsigned long *vector
= ((volatile unsigned long *)(KERNELBASE+0x100));
if (nr < 0 || nr > 3)
return;
if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346);
local_irq_save(flags);
local_irq_disable();
/* Save reset vector */
save_vector = *vector;
/* Setup fake reset vector that does
* b __secondary_start_pmac_0 + nr*8 - KERNELBASE
*/
new_vector = (unsigned long) __secondary_start_pmac_0 + nr * 8;
*vector = 0x48000002 + new_vector - KERNELBASE;
/* flush data cache and inval instruction cache */
flush_icache_range((unsigned long) vector, (unsigned long) vector + 4);
/* Put some life in our friend */
pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0);
/* FIXME: We wait a bit for the CPU to take the exception, I should
* instead wait for the entry code to set something for me. Well,
* ideally, all that crap will be done in prom.c and the CPU left
* in a RAM-based wait loop like CHRP.
*/
mdelay(1);
/* Restore our exception vector */
*vector = save_vector;
flush_icache_range((unsigned long) vector, (unsigned long) vector + 4);
local_irq_restore(flags);
if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347);
}
static void __devinit smp_core99_setup_cpu(int cpu_nr)
{
/* Setup L2/L3 */
if (cpu_nr != 0)
core99_init_caches(cpu_nr);
/* Setup openpic */
do_openpic_setup_cpu();
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);
}
}
/* not __init, called in sleep/wakeup code */
void smp_core99_take_timebase(void)
{
unsigned long flags;
/* tell the primary we're here */
sec_tb_reset = 1;
mb();
/* wait for the primary to set pri_tb_hi/lo */
while (sec_tb_reset < 2)
mb();
/* set our stuff the same as the primary */
local_irq_save(flags);
set_dec(1);
set_tb(pri_tb_hi, pri_tb_lo);
last_jiffy_stamp(smp_processor_id()) = pri_tb_stamp;
mb();
/* tell the primary we're done */
sec_tb_reset = 0;
mb();
local_irq_restore(flags);
}
/* not __init, called in sleep/wakeup code */
void smp_core99_give_timebase(void)
{
unsigned long flags;
unsigned int t;
/* wait for the secondary to be in take_timebase */
for (t = 100000; t > 0 && !sec_tb_reset; --t)
udelay(10);
if (!sec_tb_reset) {
printk(KERN_WARNING "Timeout waiting sync on second CPU\n");
return;
}
/* freeze the timebase and read it */
/* disable interrupts so the timebase is disabled for the
shortest possible time */
local_irq_save(flags);
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4);
pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
mb();
pri_tb_hi = get_tbu();
pri_tb_lo = get_tbl();
pri_tb_stamp = last_jiffy_stamp(smp_processor_id());
mb();
/* tell the secondary we're ready */
sec_tb_reset = 2;
mb();
/* wait for the secondary to have taken it */
for (t = 100000; t > 0 && sec_tb_reset; --t)
udelay(10);
if (sec_tb_reset)
printk(KERN_WARNING "Timeout waiting sync(2) on second CPU\n");
else
smp_tb_synchronized = 1;
/* Now, restart the timebase by leaving the GPIO to an open collector */
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0);
pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
local_irq_restore(flags);
}
/* PowerSurge-style Macs */
struct smp_ops_t psurge_smp_ops = {
.message_pass = smp_psurge_message_pass,
.probe = smp_psurge_probe,
.kick_cpu = smp_psurge_kick_cpu,
.setup_cpu = smp_psurge_setup_cpu,
.give_timebase = smp_psurge_give_timebase,
.take_timebase = smp_psurge_take_timebase,
};
/* Core99 Macs (dual G4s) */
struct smp_ops_t core99_smp_ops = {
.message_pass = smp_openpic_message_pass,
.probe = smp_core99_probe,
.kick_cpu = smp_core99_kick_cpu,
.setup_cpu = smp_core99_setup_cpu,
.give_timebase = smp_core99_give_timebase,
.take_timebase = smp_core99_take_timebase,
};
#ifdef CONFIG_HOTPLUG_CPU
int __cpu_disable(void)
{
cpu_clear(smp_processor_id(), cpu_online_map);
/* XXX reset cpu affinity here */
openpic_set_priority(0xf);
asm volatile("mtdec %0" : : "r" (0x7fffffff));
mb();
udelay(20);
asm volatile("mtdec %0" : : "r" (0x7fffffff));
return 0;
}
extern void low_cpu_die(void) __attribute__((noreturn)); /* in pmac_sleep.S */
static int cpu_dead[NR_CPUS];
void cpu_die(void)
{
local_irq_disable();
cpu_dead[smp_processor_id()] = 1;
mb();
low_cpu_die();
}
void __cpu_die(unsigned int cpu)
{
int timeout;
timeout = 1000;
while (!cpu_dead[cpu]) {
if (--timeout == 0) {
printk("CPU %u refused to die!\n", cpu);
break;
}
msleep(1);
}
cpu_callin_map[cpu] = 0;
cpu_dead[cpu] = 0;
}
#endif
/*
* Support for periodic interrupts (100 per second) and for getting
* the current time from the RTC on Power Macintoshes.
*
* We use the decrementer register for our periodic interrupts.
*
* Paul Mackerras August 1996.
* Copyright (C) 1996 Paul Mackerras.
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
#include <linux/hardirq.h>
#include <asm/sections.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/nvram.h>
/* Apparently the RTC stores seconds since 1 Jan 1904 */
#define RTC_OFFSET 2082844800
/*
* Calibrate the decrementer frequency with the VIA timer 1.
*/
#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */
/* VIA registers */
#define RS 0x200 /* skip between registers */
#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
#define ACR (11*RS) /* Auxiliary control register */
#define IFR (13*RS) /* Interrupt flag register */
/* Bits in ACR */
#define T1MODE 0xc0 /* Timer 1 mode */
#define T1MODE_CONT 0x40 /* continuous interrupts */
/* Bits in IFR and IER */
#define T1_INT 0x40 /* Timer 1 interrupt */
extern struct timezone sys_tz;
long __init
pmac_time_init(void)
{
#ifdef CONFIG_NVRAM
s32 delta = 0;
int dst;
delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
if (delta & 0x00800000UL)
delta |= 0xFF000000UL;
dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
dst ? "on" : "off");
return delta;
#else
return 0;
#endif
}
unsigned long
pmac_get_rtc_time(void)
{
#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU)
struct adb_request req;
unsigned long now;
#endif
/* Get the time from the RTC */
switch (sys_ctrler) {
#ifdef CONFIG_ADB_CUDA
case SYS_CTRLER_CUDA:
if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
return 0;
while (!req.complete)
cuda_poll();
if (req.reply_len != 7)
printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
req.reply_len);
now = (req.reply[3] << 24) + (req.reply[4] << 16)
+ (req.reply[5] << 8) + req.reply[6];
return now - RTC_OFFSET;
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_ADB_PMU
case SYS_CTRLER_PMU:
if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
return 0;
while (!req.complete)
pmu_poll();
if (req.reply_len != 4)
printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
req.reply_len);
now = (req.reply[0] << 24) + (req.reply[1] << 16)
+ (req.reply[2] << 8) + req.reply[3];
return now - RTC_OFFSET;
#endif /* CONFIG_ADB_PMU */
default: ;
}
return 0;
}
int
pmac_set_rtc_time(unsigned long nowtime)
{
#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU)
struct adb_request req;
#endif
nowtime += RTC_OFFSET;
switch (sys_ctrler) {
#ifdef CONFIG_ADB_CUDA
case SYS_CTRLER_CUDA:
if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0)
return 0;
while (!req.complete)
cuda_poll();
if ((req.reply_len != 3) && (req.reply_len != 7))
printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n",
req.reply_len);
return 1;
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_ADB_PMU
case SYS_CTRLER_PMU:
if (pmu_request(&req, NULL, 5, PMU_SET_RTC,
nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0)
return 0;
while (!req.complete)
pmu_poll();
if (req.reply_len != 0)
printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n",
req.reply_len);
return 1;
#endif /* CONFIG_ADB_PMU */
default:
return 0;
}
}
/*
* Calibrate the decrementer register using VIA timer 1.
* This is used both on powermacs and CHRP machines.
*/
int __init
via_calibrate_decr(void)
{
struct device_node *vias;
volatile unsigned char __iomem *via;
int count = VIA_TIMER_FREQ_6 / 100;
unsigned int dstart, dend;
vias = find_devices("via-cuda");
if (vias == 0)
vias = find_devices("via-pmu");
if (vias == 0)
vias = find_devices("via");
if (vias == 0 || vias->n_addrs == 0)
return 0;
via = ioremap(vias->addrs[0].address, vias->addrs[0].size);
/* set timer 1 for continuous interrupts */
out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
/* set the counter to a small value */
out_8(&via[T1CH], 2);
/* set the latch to `count' */
out_8(&via[T1LL], count);
out_8(&via[T1LH], count >> 8);
/* wait until it hits 0 */
while ((in_8(&via[IFR]) & T1_INT) == 0)
;
dstart = get_dec();
/* clear the interrupt & wait until it hits 0 again */
in_8(&via[T1CL]);
while ((in_8(&via[IFR]) & T1_INT) == 0)
;
dend = get_dec();
tb_ticks_per_jiffy = (dstart - dend) / ((6 * HZ)/100);
tb_to_us = mulhwu_scale_factor(dstart - dend, 60000);
printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n",
tb_ticks_per_jiffy, dstart - dend);
iounmap(via);
return 1;
}
#ifdef CONFIG_PM
/*
* Reset the time after a sleep.
*/
static int
time_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
static unsigned long time_diff;
unsigned long flags;
unsigned long seq;
switch (when) {
case PBOOK_SLEEP_NOW:
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
time_diff = xtime.tv_sec - pmac_get_rtc_time();
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
break;
case PBOOK_WAKE:
write_seqlock_irqsave(&xtime_lock, flags);
xtime.tv_sec = pmac_get_rtc_time() + time_diff;
xtime.tv_nsec = 0;
last_rtc_update = xtime.tv_sec;
write_sequnlock_irqrestore(&xtime_lock, flags);
break;
}
return PBOOK_SLEEP_OK;
}
static struct pmu_sleep_notifier time_sleep_notifier = {
time_sleep_notify, SLEEP_LEVEL_MISC,
};
#endif /* CONFIG_PM */
/*
* Query the OF and get the decr frequency.
* This was taken from the pmac time_init() when merging the prep/pmac
* time functions.
*/
void __init
pmac_calibrate_decr(void)
{
struct device_node *cpu;
unsigned int freq, *fp;
#ifdef CONFIG_PM
pmu_register_sleep_notifier(&time_sleep_notifier);
#endif /* CONFIG_PM */
/* We assume MacRISC2 machines have correct device-tree
* calibration. That's better since the VIA itself seems
* to be slightly off. --BenH
*/
if (!machine_is_compatible("MacRISC2") &&
!machine_is_compatible("MacRISC3") &&
!machine_is_compatible("MacRISC4"))
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.
*/
cpu = find_type_devices("cpu");
if (cpu == 0)
panic("can't find cpu node in time_init");
fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL);
if (fp == 0)
panic("can't get cpu timebase frequency");
freq = *fp;
printk("time_init: decrementer frequency = %u.%.6u MHz\n",
freq/1000000, freq%1000000);
tb_ticks_per_jiffy = freq / HZ;
tb_to_us = mulhwu_scale_factor(freq, 1000000);
}
......@@ -39,8 +39,6 @@ obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o $(wdt-mpc8xx-y) \
ppc_sys.o mpc8xx_devices.o mpc8xx_sys.o
obj-$(CONFIG_PCI_QSPAN) += qspan_pci.o
obj-$(CONFIG_PPC_OF) += prom_init.o prom.o
obj-$(CONFIG_PPC_PMAC) += open_pic.o
obj-$(CONFIG_POWER4) += open_pic2.o
obj-$(CONFIG_PPC_CHRP) += open_pic.o
obj-$(CONFIG_PPC_PREP) += open_pic.o todc_time.o
obj-$(CONFIG_BAMBOO) += pci_auto.o todc_time.o
......
......@@ -70,8 +70,6 @@ int use_of_interrupt_tree;
struct device_node *dflt_interrupt_controller;
int num_interrupt_controllers;
int pmac_newworld;
extern unsigned int rtas_entry; /* physical pointer */
extern struct device_node *allnodes;
......@@ -123,22 +121,13 @@ finish_device_tree(void)
unsigned long mem = (unsigned long) klimit;
struct device_node *np;
/* All newworld pmac machines and CHRPs now use the interrupt tree */
/* All CHRPs now use the interrupt tree */
for (np = allnodes; np != NULL; np = np->allnext) {
if (get_property(np, "interrupt-parent", NULL)) {
use_of_interrupt_tree = 1;
break;
}
}
if (_machine == _MACH_Pmac && use_of_interrupt_tree)
pmac_newworld = 1;
#ifdef CONFIG_BOOTX_TEXT
if (boot_infos && pmac_newworld) {
prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n");
prom_print(" You should use an Open Firmware bootloader\n");
}
#endif /* CONFIG_BOOTX_TEXT */
if (use_of_interrupt_tree) {
/*
......@@ -434,16 +423,10 @@ finish_node_interrupts(struct device_node *np, unsigned long mem_start)
* those machines, we want to offset interrupts from the
* second openpic by 128 -- BenH
*/
if (_machine != _MACH_Pmac && num_interrupt_controllers > 1
if (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)
......
......@@ -18,7 +18,6 @@
#include <asm/bootx.h>
#include <asm/machdep.h>
#include <asm/errno.h>
#include <asm/pmac_feature.h>
#include <asm/processor.h>
#include <asm/delay.h>
#include <asm/btext.h>
......@@ -27,11 +26,9 @@ static volatile unsigned char *sccc, *sccd;
unsigned int TXRDY, RXRDY, DLAB;
static int xmon_expect(const char *str, unsigned int timeout);
static int use_serial;
static int use_screen;
static int via_modem;
static int xmon_use_sccb;
static struct device_node *channel_node;
#define TB_SPEED 25000000
......@@ -112,96 +109,21 @@ xmon_map_scc(void)
#ifdef CONFIG_PPC_MULTIPLATFORM
volatile unsigned char *base;
if (_machine == _MACH_Pmac) {
struct device_node *np;
unsigned long addr;
#ifdef CONFIG_BOOTX_TEXT
if (!use_screen && !use_serial
&& !machine_is_compatible("iMac")) {
/* see if there is a keyboard in the device tree
with a parent of type "adb" */
for (np = find_devices("keyboard"); np; np = np->next)
if (np->parent && np->parent->type
&& strcmp(np->parent->type, "adb") == 0)
break;
/* needs to be hacked if xmon_printk is to be used
from within find_via_pmu() */
#ifdef CONFIG_ADB_PMU
if (np != NULL && boot_text_mapped && find_via_pmu())
use_screen = 1;
#endif
#ifdef CONFIG_ADB_CUDA
if (np != NULL && boot_text_mapped && find_via_cuda())
use_screen = 1;
#endif
}
if (!use_screen && (np = find_devices("escc")) != NULL) {
/*
* look for the device node for the serial port
* we're using and see if it says it has a modem
*/
char *name = xmon_use_sccb? "ch-b": "ch-a";
char *slots;
int l;
np = np->child;
while (np != NULL && strcmp(np->name, name) != 0)
np = np->sibling;
if (np != NULL) {
/* XXX should parse this properly */
channel_node = np;
slots = get_property(np, "slot-names", &l);
if (slots != NULL && l >= 10
&& strcmp(slots+4, "Modem") == 0)
via_modem = 1;
}
}
btext_drawstring("xmon uses ");
if (use_screen)
btext_drawstring("screen and keyboard\n");
else {
if (via_modem)
btext_drawstring("modem on ");
btext_drawstring(xmon_use_sccb? "printer": "modem");
btext_drawstring(" port\n");
}
#endif /* CONFIG_BOOTX_TEXT */
#ifdef CHRP_ESCC
addr = 0xc1013020;
#else
addr = 0xf3013020;
#endif
TXRDY = 4;
RXRDY = 1;
np = find_devices("mac-io");
if (np && np->n_addrs)
addr = np->addrs[0].address + 0x13020;
base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
sccc = base + (addr & ~PAGE_MASK);
sccd = sccc + 0x10;
}
#ifdef CONFIG_PPC_CHRP
else {
base = (volatile unsigned char *) isa_io_base;
if (_machine == _MACH_chrp)
base = (volatile unsigned char *)
ioremap(chrp_find_phys_io_base(), 0x1000);
sccc = base + 0x3fd;
sccd = base + 0x3f8;
if (xmon_use_sccb) {
sccc -= 0x100;
sccd -= 0x100;
}
TXRDY = 0x20;
RXRDY = 1;
DLAB = 0x80;
base = (volatile unsigned char *) isa_io_base;
if (_machine == _MACH_chrp)
base = (volatile unsigned char *)
ioremap(chrp_find_phys_io_base(), 0x1000);
sccc = base + 0x3fd;
sccd = base + 0x3f8;
if (xmon_use_sccb) {
sccc -= 0x100;
sccd -= 0x100;
}
TXRDY = 0x20;
RXRDY = 1;
DLAB = 0x80;
#endif /* CONFIG_PPC_CHRP */
#elif defined(CONFIG_GEMINI)
/* should already be mapped by the kernel boot */
......@@ -385,16 +307,6 @@ xmon_read_poll(void)
return *sccd;
}
static unsigned char scc_inittab[] = {
13, 0, /* set baud rate divisor */
12, 1,
14, 1, /* baud rate gen enable, src=rtxc */
11, 0x50, /* clocks = br gen */
5, 0xea, /* tx 8 bits, assert DTR & RTS */
4, 0x46, /* x16 clock, 1 stop */
3, 0xc1, /* rx enable, 8 bits */
};
void
xmon_init_scc(void)
{
......@@ -407,43 +319,6 @@ xmon_init_scc(void)
sccd[3] = 3; eieio(); /* LCR = 8N1 */
sccd[1] = 0; eieio(); /* IER = 0 */
}
else if ( _machine == _MACH_Pmac )
{
int i, x;
if (channel_node != 0)
pmac_call_feature(
PMAC_FTR_SCC_ENABLE,
channel_node,
PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
printk(KERN_INFO "Serial port locked ON by debugger !\n");
if (via_modem && channel_node != 0) {
unsigned int t0;
pmac_call_feature(
PMAC_FTR_MODEM_ENABLE,
channel_node, 0, 1);
printk(KERN_INFO "Modem powered up by debugger !\n");
t0 = readtb();
while (readtb() - t0 < 3*TB_SPEED)
eieio();
}
/* use the B channel if requested */
if (xmon_use_sccb) {
sccc = (volatile unsigned char *)
((unsigned long)sccc & ~0x20);
sccd = sccc + 0x10;
}
for (i = 20000; i != 0; --i) {
x = *sccc; eieio();
}
*sccc = 9; eieio(); /* reset A or B side */
*sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
for (i = 0; i < sizeof(scc_inittab); ++i) {
*sccc = scc_inittab[i];
eieio();
}
}
scc_initialized = 1;
if (via_modem) {
for (;;) {
......@@ -632,19 +507,9 @@ xmon_fgets(char *str, int nb, void *f)
void
xmon_enter(void)
{
#ifdef CONFIG_ADB_PMU
if (_machine == _MACH_Pmac) {
pmu_suspend();
}
#endif
}
void
xmon_leave(void)
{
#ifdef CONFIG_ADB_PMU
if (_machine == _MACH_Pmac) {
pmu_resume();
}
#endif
}
......@@ -16,9 +16,6 @@
#include <asm/bootx.h>
#include <asm/machdep.h>
#include <asm/xmon.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
#include "nonstdio.h"
#include "privinst.h"
......@@ -260,16 +257,6 @@ int xmon(struct pt_regs *excp)
*/
#endif /* CONFIG_SMP */
remove_bpts();
#ifdef CONFIG_PMAC_BACKLIGHT
if( setjmp(bus_error_jmp) == 0 ) {
debugger_fault_handler = handle_fault;
sync();
set_backlight_enable(1);
set_backlight_level(BACKLIGHT_MAX);
sync();
}
debugger_fault_handler = NULL;
#endif /* CONFIG_PMAC_BACKLIGHT */
cmd = cmds(excp);
if (cmd == 's') {
xmon_trace[smp_processor_id()] = SSTEP;
......
......@@ -2734,7 +2734,7 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
* BIOS does tho. Right now, all this PM stuff is pmac-only for that
* reason. --BenH
*/
#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF)
#if defined(CONFIG_PM) && defined(CONFIG_PPC_PMAC)
if (_machine == _MACH_Pmac && rinfo->of_node) {
if (rinfo->is_mobility && rinfo->pm_reg &&
rinfo->family <= CHIP_FAMILY_RV250)
......@@ -2778,12 +2778,12 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000);
#endif
}
#endif /* defined(CONFIG_PM) && defined(CONFIG_PPC_OF) */
#endif /* defined(CONFIG_PM) && defined(CONFIG_PPC_PMAC) */
}
void radeonfb_pm_exit(struct radeonfb_info *rinfo)
{
#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF)
#if defined(CONFIG_PM) && defined(CONFIG_PPC_PMAC)
if (rinfo->pm_mode != radeon_pm_none)
pmac_set_early_video_resume(NULL, NULL);
#endif
......
......@@ -167,6 +167,14 @@ extern int of_address_to_resource(struct device_node *dev, int index,
extern int of_pci_address_to_resource(struct device_node *dev, int bar,
struct resource *r);
#ifndef CONFIG_PPC_OF
/*
* Fallback definitions for builds where we don't have prom.c included.
*/
#define machine_is_compatible(x) 0
#define of_find_compatible_node(f, t, c) NULL
#define get_property(p, n, l) NULL
#endif
#endif /* _PPC_PROM_H */
#endif /* __KERNEL__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment