forked from Minki/linux
[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: Paul Mackerras <paulus@samba.org>
This commit is contained in:
parent
e8625d4635
commit
a7fdd90bc4
@ -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 ($<)'
|
||||
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
|
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,359 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,735 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,511 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
@ -1,584 +0,0 @@
|
||||
/*
|
||||
* 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);
|
File diff suppressed because it is too large
Load Diff
@ -1,693 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
@ -1,11 +0,0 @@
|
||||
#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 */
|
@ -1,745 +0,0 @@
|
||||
/*
|
||||
* 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);
|
@ -1,396 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,692 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,291 +0,0 @@
|
||||
/*
|
||||
* 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,81 +109,7 @@ 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 *)
|
||||
@ -201,7 +124,6 @@ xmon_map_scc(void)
|
||||
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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user