Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

115
arch/ppc/syslib/Makefile Normal file
View File

@@ -0,0 +1,115 @@
#
# Makefile for the linux kernel.
#
CFLAGS_prom_init.o += -fPIC
CFLAGS_btext.o += -fPIC
wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o
obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o
obj-$(CONFIG_PPC_OCP) += ocp.o
obj-$(CONFIG_IBM_OCP) += ibm_ocp.o
obj-$(CONFIG_44x) += ibm44x_common.o
obj-$(CONFIG_440GP) += ibm440gp_common.o
obj-$(CONFIG_440GX) += ibm440gx_common.o
obj-$(CONFIG_440SP) += ibm440gx_common.o ibm440sp_common.o
ifeq ($(CONFIG_4xx),y)
ifeq ($(CONFIG_VIRTEX_II_PRO),y)
obj-$(CONFIG_40x) += xilinx_pic.o
else
ifeq ($(CONFIG_403),y)
obj-$(CONFIG_40x) += ppc403_pic.o
else
obj-$(CONFIG_40x) += ppc4xx_pic.o
endif
endif
obj-$(CONFIG_44x) += ppc4xx_pic.o
obj-$(CONFIG_40x) += ppc4xx_setup.o
obj-$(CONFIG_GEN_RTC) += todc_time.o
obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o
obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o
ifeq ($(CONFIG_40x),y)
obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o
endif
endif
obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o $(wdt-mpc8xx-y)
ifeq ($(CONFIG_8xx),y)
obj-$(CONFIG_PCI) += qspan_pci.o i8259.o
endif
obj-$(CONFIG_PPC_OF) += prom_init.o prom.o of_device.o
obj-$(CONFIG_PPC_PMAC) += open_pic.o indirect_pci.o
obj-$(CONFIG_POWER4) += open_pic2.o
obj-$(CONFIG_PPC_CHRP) += open_pic.o indirect_pci.o i8259.o
obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o todc_time.o
obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \
todc_time.o
obj-$(CONFIG_CPCI690) += todc_time.o pci_auto.o
obj-$(CONFIG_EBONY) += indirect_pci.o pci_auto.o todc_time.o
obj-$(CONFIG_EV64260) += todc_time.o pci_auto.o
obj-$(CONFIG_CHESTNUT) += mv64360_pic.o pci_auto.o
obj-$(CONFIG_GEMINI) += open_pic.o indirect_pci.o
obj-$(CONFIG_GT64260) += gt64260_pic.o
obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \
pci_auto.o
obj-$(CONFIG_LOPEC) += i8259.o pci_auto.o todc_time.o
obj-$(CONFIG_HDPU) += pci_auto.o
obj-$(CONFIG_LUAN) += indirect_pci.o pci_auto.o todc_time.o
obj-$(CONFIG_KATANA) += pci_auto.o
obj-$(CONFIG_MCPN765) += todc_time.o indirect_pci.o pci_auto.o \
open_pic.o i8259.o hawk_common.o
obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \
pci_auto.o indirect_pci.o
obj-$(CONFIG_MV64360) += mv64360_pic.o
obj-$(CONFIG_MV64X60) += mv64x60.o mv64x60_win.o indirect_pci.o
obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \
pci_auto.o hawk_common.o
obj-$(CONFIG_MVME5100_IPMC761_PRESENT) += i8259.o
obj-$(CONFIG_OCOTEA) += indirect_pci.o pci_auto.o todc_time.o
obj-$(CONFIG_PAL4) += cpc700_pic.o
obj-$(CONFIG_PCORE) += todc_time.o i8259.o pci_auto.o
obj-$(CONFIG_POWERPMC250) += pci_auto.o
obj-$(CONFIG_PPLUS) += hawk_common.o open_pic.o i8259.o \
indirect_pci.o todc_time.o pci_auto.o
obj-$(CONFIG_PRPMC750) += open_pic.o indirect_pci.o pci_auto.o \
hawk_common.o
obj-$(CONFIG_HARRIER) += harrier.o
obj-$(CONFIG_PRPMC800) += open_pic.o indirect_pci.o pci_auto.o
obj-$(CONFIG_RADSTONE_PPC7D) += i8259.o pci_auto.o
obj-$(CONFIG_SANDPOINT) += i8259.o pci_auto.o todc_time.o
obj-$(CONFIG_SBC82xx) += todc_time.o
obj-$(CONFIG_SPRUCE) += cpc700_pic.o indirect_pci.o pci_auto.o \
todc_time.o
obj-$(CONFIG_8260) += m8260_setup.o
obj-$(CONFIG_PCI_8260) += m8260_pci.o indirect_pci.o
obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o
obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o
ifeq ($(CONFIG_PPC_GEN550),y)
obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o
obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o
endif
ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y)
obj-$(CONFIG_SERIAL_TEXT_DEBUG) += mv64x60_dbg.o
endif
obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_MPC10X_BRIDGE) += mpc10x_common.o indirect_pci.o
obj-$(CONFIG_MPC10X_OPENPIC) += open_pic.o
obj-$(CONFIG_40x) += dcr.o
obj-$(CONFIG_BOOKE) += dcr.o
obj-$(CONFIG_85xx) += open_pic.o ppc85xx_common.o ppc85xx_setup.o \
ppc_sys.o mpc85xx_sys.o \
mpc85xx_devices.o
ifeq ($(CONFIG_85xx),y)
obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o
endif
obj-$(CONFIG_83xx) += ipic.o ppc83xx_setup.o ppc_sys.o \
mpc83xx_sys.o mpc83xx_devices.o
ifeq ($(CONFIG_83xx),y)
obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o
endif
obj-$(CONFIG_MPC8555_CDS) += todc_time.o
obj-$(CONFIG_PPC_MPC52xx) += mpc52xx_setup.o mpc52xx_pic.o \
mpc52xx_sys.o mpc52xx_devices.o ppc_sys.o
ifeq ($(CONFIG_PPC_MPC52xx),y)
obj-$(CONFIG_PCI) += mpc52xx_pci.o
endif

861
arch/ppc/syslib/btext.c Normal file
View File

@@ -0,0 +1,861 @@
/*
* Procedures for drawing on the screen early on in the boot process.
*
* Benjamin Herrenschmidt <benh@kernel.crashing.org>
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/version.h>
#include <asm/sections.h>
#include <asm/bootx.h>
#include <asm/btext.h>
#include <asm/prom.h>
#include <asm/page.h>
#include <asm/mmu.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/reg.h>
#define NO_SCROLL
#ifndef NO_SCROLL
static void scrollscreen(void);
#endif
static void draw_byte(unsigned char c, long locX, long locY);
static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb);
static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb);
static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb);
static int g_loc_X;
static int g_loc_Y;
static int g_max_loc_X;
static int g_max_loc_Y;
unsigned long disp_BAT[2] __initdata = {0, 0};
#define cmapsz (16*256)
static unsigned char vga_font[cmapsz];
int boot_text_mapped;
int force_printk_to_btext = 0;
boot_infos_t disp_bi;
extern char *klimit;
/*
* Powermac can use btext_* after boot for xmon,
* chrp only uses it during early boot.
*/
#ifdef CONFIG_XMON
#define BTEXT __pmac
#define BTDATA __pmacdata
#else
#define BTEXT __init
#define BTDATA __initdata
#endif /* CONFIG_XMON */
/*
* This is called only when we are booted via BootX.
*/
void __init
btext_init(boot_infos_t *bi)
{
g_loc_X = 0;
g_loc_Y = 0;
g_max_loc_X = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8;
g_max_loc_Y = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16;
disp_bi = *bi;
boot_text_mapped = 1;
}
void __init
btext_welcome(void)
{
unsigned long flags;
unsigned long pvr;
boot_infos_t* bi = &disp_bi;
btext_drawstring("Welcome to Linux, kernel " UTS_RELEASE "\n");
btext_drawstring("\nlinked at : 0x");
btext_drawhex(KERNELBASE);
btext_drawstring("\nframe buffer at : 0x");
btext_drawhex((unsigned long)bi->dispDeviceBase);
btext_drawstring(" (phys), 0x");
btext_drawhex((unsigned long)bi->logicalDisplayBase);
btext_drawstring(" (log)");
btext_drawstring("\nklimit : 0x");
btext_drawhex((unsigned long)klimit);
btext_drawstring("\nMSR : 0x");
__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
btext_drawhex(flags);
__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
pvr >>= 16;
if (pvr > 1) {
btext_drawstring("\nHID0 : 0x");
__asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
btext_drawhex(flags);
}
if (pvr == 8 || pvr == 12 || pvr == 0x800c) {
btext_drawstring("\nICTC : 0x");
__asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
btext_drawhex(flags);
}
btext_drawstring("\n\n");
}
/* Calc BAT values for mapping the display and store them
* in disp_BAT. Those values are then used from head.S to map
* the display during identify_machine() and MMU_Init()
*
* The display is mapped to virtual address 0xD0000000, rather
* than 1:1, because some some CHRP machines put the frame buffer
* in the region starting at 0xC0000000 (KERNELBASE).
* This mapping is temporary and will disappear as soon as the
* setup done by MMU_Init() is applied.
*
* For now, we align the BAT and then map 8Mb on 601 and 16Mb
* on other PPCs. This may cause trouble if the framebuffer
* is really badly aligned, but I didn't encounter this case
* yet.
*/
void __init
btext_prepare_BAT(void)
{
boot_infos_t* bi = &disp_bi;
unsigned long vaddr = KERNELBASE + 0x10000000;
unsigned long addr;
unsigned long lowbits;
addr = (unsigned long)bi->dispDeviceBase;
if (!addr) {
boot_text_mapped = 0;
return;
}
if (PVR_VER(mfspr(SPRN_PVR)) != 1) {
/* 603, 604, G3, G4, ... */
lowbits = addr & ~0xFF000000UL;
addr &= 0xFF000000UL;
disp_BAT[0] = vaddr | (BL_16M<<2) | 2;
disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW);
} else {
/* 601 */
lowbits = addr & ~0xFF800000UL;
addr &= 0xFF800000UL;
disp_BAT[0] = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4;
disp_BAT[1] = addr | BL_8M | 0x40;
}
bi->logicalDisplayBase = (void *) (vaddr + lowbits);
}
/* This function will enable the early boot text when doing OF booting. This
* way, xmon output should work too
*/
void __init
btext_setup_display(int width, int height, int depth, int pitch,
unsigned long address)
{
boot_infos_t* bi = &disp_bi;
g_loc_X = 0;
g_loc_Y = 0;
g_max_loc_X = width / 8;
g_max_loc_Y = height / 16;
bi->logicalDisplayBase = (unsigned char *)address;
bi->dispDeviceBase = (unsigned char *)address;
bi->dispDeviceRowBytes = pitch;
bi->dispDeviceDepth = depth;
bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
bi->dispDeviceRect[2] = width;
bi->dispDeviceRect[3] = height;
boot_text_mapped = 1;
}
/* Here's a small text engine to use during early boot
* or for debugging purposes
*
* todo:
*
* - build some kind of vgacon with it to enable early printk
* - move to a separate file
* - add a few video driver hooks to keep in sync with display
* changes.
*/
void __openfirmware
map_boot_text(void)
{
unsigned long base, offset, size;
boot_infos_t *bi = &disp_bi;
unsigned char *vbase;
/* By default, we are no longer mapped */
boot_text_mapped = 0;
if (bi->dispDeviceBase == 0)
return;
base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL;
offset = ((unsigned long) bi->dispDeviceBase) - base;
size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset
+ bi->dispDeviceRect[0];
vbase = ioremap(base, size);
if (vbase == 0)
return;
bi->logicalDisplayBase = vbase + offset;
boot_text_mapped = 1;
}
/* Calc the base address of a given point (x,y) */
static unsigned char * BTEXT
calc_base(boot_infos_t *bi, int x, int y)
{
unsigned char *base;
base = bi->logicalDisplayBase;
if (base == 0)
base = bi->dispDeviceBase;
base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3);
base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes;
return base;
}
/* Adjust the display to a new resolution */
void
btext_update_display(unsigned long phys, int width, int height,
int depth, int pitch)
{
boot_infos_t *bi = &disp_bi;
if (bi->dispDeviceBase == 0)
return;
/* check it's the same frame buffer (within 256MB) */
if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000)
return;
bi->dispDeviceBase = (__u8 *) phys;
bi->dispDeviceRect[0] = 0;
bi->dispDeviceRect[1] = 0;
bi->dispDeviceRect[2] = width;
bi->dispDeviceRect[3] = height;
bi->dispDeviceDepth = depth;
bi->dispDeviceRowBytes = pitch;
if (boot_text_mapped) {
iounmap(bi->logicalDisplayBase);
boot_text_mapped = 0;
}
map_boot_text();
g_loc_X = 0;
g_loc_Y = 0;
g_max_loc_X = width / 8;
g_max_loc_Y = height / 16;
}
void BTEXT btext_clearscreen(void)
{
boot_infos_t* bi = &disp_bi;
unsigned long *base = (unsigned long *)calc_base(bi, 0, 0);
unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
(bi->dispDeviceDepth >> 3)) >> 2;
int i,j;
for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
{
unsigned long *ptr = base;
for(j=width; j; --j)
*(ptr++) = 0;
base += (bi->dispDeviceRowBytes >> 2);
}
}
__inline__ void dcbst(const void* addr)
{
__asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
}
void BTEXT btext_flushscreen(void)
{
boot_infos_t* bi = &disp_bi;
unsigned long *base = (unsigned long *)calc_base(bi, 0, 0);
unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
(bi->dispDeviceDepth >> 3)) >> 2;
int i,j;
for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
{
unsigned long *ptr = base;
for(j=width; j>0; j-=8) {
dcbst(ptr);
ptr += 8;
}
base += (bi->dispDeviceRowBytes >> 2);
}
}
#ifndef NO_SCROLL
static BTEXT void
scrollscreen(void)
{
boot_infos_t* bi = &disp_bi;
unsigned long *src = (unsigned long *)calc_base(bi,0,16);
unsigned long *dst = (unsigned long *)calc_base(bi,0,0);
unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
(bi->dispDeviceDepth >> 3)) >> 2;
int i,j;
#ifdef CONFIG_ADB_PMU
pmu_suspend(); /* PMU will not shut us down ! */
#endif
for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++)
{
unsigned long *src_ptr = src;
unsigned long *dst_ptr = dst;
for(j=width; j; --j)
*(dst_ptr++) = *(src_ptr++);
src += (bi->dispDeviceRowBytes >> 2);
dst += (bi->dispDeviceRowBytes >> 2);
}
for (i=0; i<16; i++)
{
unsigned long *dst_ptr = dst;
for(j=width; j; --j)
*(dst_ptr++) = 0;
dst += (bi->dispDeviceRowBytes >> 2);
}
#ifdef CONFIG_ADB_PMU
pmu_resume(); /* PMU will not shut us down ! */
#endif
}
#endif /* ndef NO_SCROLL */
void BTEXT btext_drawchar(char c)
{
int cline = 0, x;
if (!boot_text_mapped)
return;
switch (c) {
case '\b':
if (g_loc_X > 0)
--g_loc_X;
break;
case '\t':
g_loc_X = (g_loc_X & -8) + 8;
break;
case '\r':
g_loc_X = 0;
break;
case '\n':
g_loc_X = 0;
g_loc_Y++;
cline = 1;
break;
default:
draw_byte(c, g_loc_X++, g_loc_Y);
}
if (g_loc_X >= g_max_loc_X) {
g_loc_X = 0;
g_loc_Y++;
cline = 1;
}
#ifndef NO_SCROLL
while (g_loc_Y >= g_max_loc_Y) {
scrollscreen();
g_loc_Y--;
}
#else
/* wrap around from bottom to top of screen so we don't
waste time scrolling each line. -- paulus. */
if (g_loc_Y >= g_max_loc_Y)
g_loc_Y = 0;
if (cline) {
for (x = 0; x < g_max_loc_X; ++x)
draw_byte(' ', x, g_loc_Y);
}
#endif
}
void BTEXT
btext_drawstring(const char *c)
{
if (!boot_text_mapped)
return;
while (*c)
btext_drawchar(*c++);
}
void BTEXT
btext_drawhex(unsigned long v)
{
static char hex_table[] = "0123456789abcdef";
if (!boot_text_mapped)
return;
btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]);
btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]);
btext_drawchar(' ');
}
static void BTEXT
draw_byte(unsigned char c, long locX, long locY)
{
boot_infos_t* bi = &disp_bi;
unsigned char *base = calc_base(bi, locX << 3, locY << 4);
unsigned char *font = &vga_font[((unsigned long)c) * 16];
int rb = bi->dispDeviceRowBytes;
switch(bi->dispDeviceDepth) {
case 24:
case 32:
draw_byte_32(font, (unsigned long *)base, rb);
break;
case 15:
case 16:
draw_byte_16(font, (unsigned long *)base, rb);
break;
case 8:
draw_byte_8(font, (unsigned long *)base, rb);
break;
}
}
static unsigned long expand_bits_8[16] BTDATA = {
0x00000000,
0x000000ff,
0x0000ff00,
0x0000ffff,
0x00ff0000,
0x00ff00ff,
0x00ffff00,
0x00ffffff,
0xff000000,
0xff0000ff,
0xff00ff00,
0xff00ffff,
0xffff0000,
0xffff00ff,
0xffffff00,
0xffffffff
};
static unsigned long expand_bits_16[4] BTDATA = {
0x00000000,
0x0000ffff,
0xffff0000,
0xffffffff
};
static void BTEXT
draw_byte_32(unsigned char *font, unsigned long *base, int rb)
{
int l, bits;
int fg = 0xFFFFFFFFUL;
int bg = 0x00000000UL;
for (l = 0; l < 16; ++l)
{
bits = *font++;
base[0] = (-(bits >> 7) & fg) ^ bg;
base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
base[7] = (-(bits & 1) & fg) ^ bg;
base = (unsigned long *) ((char *)base + rb);
}
}
static void BTEXT
draw_byte_16(unsigned char *font, unsigned long *base, int rb)
{
int l, bits;
int fg = 0xFFFFFFFFUL;
int bg = 0x00000000UL;
unsigned long *eb = expand_bits_16;
for (l = 0; l < 16; ++l)
{
bits = *font++;
base[0] = (eb[bits >> 6] & fg) ^ bg;
base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
base[3] = (eb[bits & 3] & fg) ^ bg;
base = (unsigned long *) ((char *)base + rb);
}
}
static void BTEXT
draw_byte_8(unsigned char *font, unsigned long *base, int rb)
{
int l, bits;
int fg = 0x0F0F0F0FUL;
int bg = 0x00000000UL;
unsigned long *eb = expand_bits_8;
for (l = 0; l < 16; ++l)
{
bits = *font++;
base[0] = (eb[bits >> 4] & fg) ^ bg;
base[1] = (eb[bits & 0xf] & fg) ^ bg;
base = (unsigned long *) ((char *)base + rb);
}
}
static unsigned char vga_font[cmapsz] BTDATA = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd,
0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff,
0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe,
0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e,
0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30,
0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63,
0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8,
0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e,
0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb,
0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6,
0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c,
0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c,
0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18,
0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe,
0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde,
0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38,
0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0,
0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68,
0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60,
0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7,
0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66,
0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c,
0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c,
0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18,
0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c,
0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60,
0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc,
0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60,
0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60,
0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb,
0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60,
0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30,
0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3,
0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18,
0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6,
0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe,
0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c,
0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06,
0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe,
0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66,
0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6,
0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00,
0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b,
0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c,
0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18,
0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66,
0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18,
0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c,
0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06,
0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44,
0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8,
0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66,
0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66,
0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60,
0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18,
0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};

98
arch/ppc/syslib/cpc700.h Normal file
View File

@@ -0,0 +1,98 @@
/*
* arch/ppc/syslib/cpc700.h
*
* Header file for IBM CPC700 Host Bridge, et. al.
*
* Author: Mark A. Greer
* mgreer@mvista.com
*
* 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
* This file contains the defines and macros for the IBM CPC700 host bridge,
* memory controller, PIC, UARTs, IIC, and Timers.
*/
#ifndef __PPC_SYSLIB_CPC700_H__
#define __PPC_SYSLIB_CPC700_H__
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/init.h>
/* XXX no barriers? not even any volatiles? -- paulus */
#define CPC700_OUT_32(a,d) (*(u_int *)a = d)
#define CPC700_IN_32(a) (*(u_int *)a)
/*
* PCI Section
*/
#define CPC700_PCI_CONFIG_ADDR 0xfec00000
#define CPC700_PCI_CONFIG_DATA 0xfec00004
/* CPU -> PCI memory window 0 */
#define CPC700_PMM0_LOCAL 0xff400000 /* CPU physical addr */
#define CPC700_PMM0_MASK_ATTR 0xff400004 /* size and attrs */
#define CPC700_PMM0_PCI_LOW 0xff400008 /* PCI addr, low word */
#define CPC700_PMM0_PCI_HIGH 0xff40000c /* PCI addr, high wd */
/* CPU -> PCI memory window 1 */
#define CPC700_PMM1_LOCAL 0xff400010
#define CPC700_PMM1_MASK_ATTR 0xff400014
#define CPC700_PMM1_PCI_LOW 0xff400018
#define CPC700_PMM1_PCI_HIGH 0xff40001c
/* CPU -> PCI memory window 2 */
#define CPC700_PMM2_LOCAL 0xff400020
#define CPC700_PMM2_MASK_ATTR 0xff400024
#define CPC700_PMM2_PCI_LOW 0xff400028
#define CPC700_PMM2_PCI_HIGH 0xff40002c
/* PCI memory -> CPU window 1 */
#define CPC700_PTM1_MEMSIZE 0xff400030 /* window size */
#define CPC700_PTM1_LOCAL 0xff400034 /* CPU phys addr */
/* PCI memory -> CPU window 2 */
#define CPC700_PTM2_MEMSIZE 0xff400038 /* size and enable */
#define CPC700_PTM2_LOCAL 0xff40003c
/*
* PIC Section
*
* IBM calls the CPC700's programmable interrupt controller the Universal
* Interrupt Controller or UIC.
*/
/*
* UIC Register Addresses.
*/
#define CPC700_UIC_UICSR 0xff500880 /* Status Reg (Rd/Clr)*/
#define CPC700_UIC_UICSRS 0xff500884 /* Status Reg (Set) */
#define CPC700_UIC_UICER 0xff500888 /* Enable Reg */
#define CPC700_UIC_UICCR 0xff50088c /* Critical Reg */
#define CPC700_UIC_UICPR 0xff500890 /* Polarity Reg */
#define CPC700_UIC_UICTR 0xff500894 /* Trigger Reg */
#define CPC700_UIC_UICMSR 0xff500898 /* Masked Status Reg */
#define CPC700_UIC_UICVR 0xff50089c /* Vector Reg */
#define CPC700_UIC_UICVCR 0xff5008a0 /* Vector Config Reg */
#define CPC700_UIC_UICER_ENABLE 0x00000001 /* Enable an IRQ */
#define CPC700_UIC_UICVCR_31_HI 0x00000000 /* IRQ 31 hi priority */
#define CPC700_UIC_UICVCR_0_HI 0x00000001 /* IRQ 0 hi priority */
#define CPC700_UIC_UICVCR_BASE_MASK 0xfffffffc
#define CPC700_UIC_UICVCR_ORDER_MASK 0x00000001
/* Specify value of a bit for an IRQ. */
#define CPC700_UIC_IRQ_BIT(i) ((0x00000001) << (31 - (i)))
/*
* UIC Exports...
*/
extern struct hw_interrupt_type cpc700_pic;
extern unsigned int cpc700_irq_assigns[32][2];
extern void __init cpc700_init_IRQ(void);
extern int cpc700_get_irq(struct pt_regs *);
#endif /* __PPC_SYSLIB_CPC700_H__ */

View File

@@ -0,0 +1,187 @@
/*
* arch/ppc/syslib/cpc700_pic.c
*
* Interrupt controller support for IBM Spruce
*
* Authors: Mark Greer, Matt Porter, and Johnnie Peters
* mgreer@mvista.com
* mporter@mvista.com
* jpeters@mvista.com
*
* 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include "cpc700.h"
static void
cpc700_unmask_irq(unsigned int irq)
{
unsigned int tr_bits;
/*
* IRQ 31 is largest IRQ supported.
* IRQs 17-19 are reserved.
*/
if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
tr_bits = CPC700_IN_32(CPC700_UIC_UICTR);
if ((tr_bits & (1 << (31 - irq))) == 0) {
/* level trigger interrupt, clear bit in status
* register */
CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq));
}
/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq);
CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
}
return;
}
static void
cpc700_mask_irq(unsigned int irq)
{
/*
* IRQ 31 is largest IRQ supported.
* IRQs 17-19 are reserved.
*/
if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
ppc_cached_irq_mask[0] &=
~CPC700_UIC_IRQ_BIT(irq);
CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
}
return;
}
static void
cpc700_mask_and_ack_irq(unsigned int irq)
{
u_int bit;
/*
* IRQ 31 is largest IRQ supported.
* IRQs 17-19 are reserved.
*/
if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
bit = CPC700_UIC_IRQ_BIT(irq);
ppc_cached_irq_mask[0] &= ~bit;
CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */
}
return;
}
static struct hw_interrupt_type cpc700_pic = {
"CPC700 PIC",
NULL,
NULL,
cpc700_unmask_irq,
cpc700_mask_irq,
cpc700_mask_and_ack_irq,
NULL,
NULL
};
__init static void
cpc700_pic_init_irq(unsigned int irq)
{
unsigned int tmp;
/* Set interrupt sense */
tmp = CPC700_IN_32(CPC700_UIC_UICTR);
if (cpc700_irq_assigns[irq][0] == 0) {
tmp &= ~CPC700_UIC_IRQ_BIT(irq);
} else {
tmp |= CPC700_UIC_IRQ_BIT(irq);
}
CPC700_OUT_32(CPC700_UIC_UICTR, tmp);
/* Set interrupt polarity */
tmp = CPC700_IN_32(CPC700_UIC_UICPR);
if (cpc700_irq_assigns[irq][1]) {
tmp |= CPC700_UIC_IRQ_BIT(irq);
} else {
tmp &= ~CPC700_UIC_IRQ_BIT(irq);
}
CPC700_OUT_32(CPC700_UIC_UICPR, tmp);
/* Set interrupt critical */
tmp = CPC700_IN_32(CPC700_UIC_UICCR);
tmp |= CPC700_UIC_IRQ_BIT(irq);
CPC700_OUT_32(CPC700_UIC_UICCR, tmp);
return;
}
__init void
cpc700_init_IRQ(void)
{
int i;
ppc_cached_irq_mask[0] = 0;
CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000); /* Disable all irq's */
CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff); /* Clear cur intrs */
CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff); /* Gen INT not MCP */
CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000); /* Active low */
CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000); /* Level Sensitive */
CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI);
/* IRQ 0 is highest */
for (i = 0; i < 17; i++) {
irq_desc[i].handler = &cpc700_pic;
cpc700_pic_init_irq(i);
}
for (i = 20; i < 32; i++) {
irq_desc[i].handler = &cpc700_pic;
cpc700_pic_init_irq(i);
}
return;
}
/*
* Find the highest IRQ that generating an interrupt, if any.
*/
int
cpc700_get_irq(struct pt_regs *regs)
{
int irq = 0;
u_int irq_status, irq_test = 1;
irq_status = CPC700_IN_32(CPC700_UIC_UICMSR);
do
{
if (irq_status & irq_test)
break;
irq++;
irq_test <<= 1;
} while (irq < NR_IRQS);
if (irq == NR_IRQS)
irq = 33;
return (31 - irq);
}

83
arch/ppc/syslib/cpc710.h Normal file
View File

@@ -0,0 +1,83 @@
/*
* arch/ppc/syslib/cpc710.h
*
* Definitions for the IBM CPC710 PCI Host Bridge
*
* Author: Matt Porter <mporter@mvista.com>
*
* 2001 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#ifndef __PPC_PLATFORMS_CPC710_H
#define __PPC_PLATFORMS_CPC710_H
/* General bridge and memory controller registers */
#define PIDR 0xff000008
#define CNFR 0xff00000c
#define RSTR 0xff000010
#define UCTL 0xff001000
#define MPSR 0xff001010
#define SIOC 0xff001020
#define ABCNTL 0xff001030
#define SRST 0xff001040
#define ERRC 0xff001050
#define SESR 0xff001060
#define SEAR 0xff001070
#define SIOC1 0xff001090
#define PGCHP 0xff001100
#define GPDIR 0xff001130
#define GPOUT 0xff001150
#define ATAS 0xff001160
#define AVDG 0xff001170
#define MCCR 0xff001200
#define MESR 0xff001220
#define MEAR 0xff001230
#define MCER0 0xff001300
#define MCER1 0xff001310
#define MCER2 0xff001320
#define MCER3 0xff001330
#define MCER4 0xff001340
#define MCER5 0xff001350
#define MCER6 0xff001360
#define MCER7 0xff001370
/*
* PCI32/64 configuration registers
* Given as offsets from their
* respective physical segment BAR
*/
#define PIBAR 0x000f7800
#define PMBAR 0x000f7810
#define MSIZE 0x000f7f40
#define IOSIZE 0x000f7f60
#define SMBAR 0x000f7f80
#define SIBAR 0x000f7fc0
#define PSSIZE 0x000f8100
#define PPSIZE 0x000f8110
#define BARPS 0x000f8120
#define BARPP 0x000f8130
#define PSBAR 0x000f8140
#define PPBAR 0x000f8150
#define BPMDLK 0x000f8200 /* Bottom of Peripheral Memory Space */
#define TPMDLK 0x000f8210 /* Top of Peripheral Memory Space */
#define BIODLK 0x000f8220 /* Bottom of Peripheral I/O Space */
#define TIODLK 0x000f8230 /* Top of Perioheral I/O Space */
#define DLKCTRL 0x000f8240 /* Deadlock control */
#define DLKDEV 0x000f8250 /* Deadlock device */
/* System standard configuration registers space */
#define DCR 0xff200000
#define DID 0xff200004
#define BAR 0xff200018
/* Device specific configuration space */
#define PCIENB 0xff201000
/* Configuration space registers */
#define CPC710_BUS_NUMBER 0x40
#define CPC710_SUB_BUS_NUMBER 0x41
#endif /* __PPC_PLATFORMS_CPC710_H */

View File

@@ -0,0 +1,198 @@
/*
* General Purpose functions for the global management of the
* 8260 Communication Processor Module.
* Copyright (c) 1999 Dan Malek (dmalek@jlc.net)
* Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com)
* 2.3.99 Updates
*
* In addition to the individual control of the communication
* channels, there are a few functions that globally affect the
* communication processor.
*
* Buffer descriptors must be allocated from the dual ported memory
* space. The allocator for that is here. When the communication
* process is reset, we reclaim the memory available. There is
* currently no deallocator for this memory.
*/
#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/interrupt.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <asm/irq.h>
#include <asm/mpc8260.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/immap_cpm2.h>
#include <asm/cpm2.h>
#include <asm/rheap.h>
static void cpm2_dpinit(void);
cpm_cpm2_t *cpmp; /* Pointer to comm processor space */
/* We allocate this here because it is used almost exclusively for
* the communication processor devices.
*/
cpm2_map_t *cpm2_immr;
#define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3 reserve this amount
of space for CPM as it is larger
than on PQ2 */
void
cpm2_reset(void)
{
cpm2_immr = (cpm2_map_t *)ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE);
/* Reclaim the DP memory for our use.
*/
cpm2_dpinit();
/* Tell everyone where the comm processor resides.
*/
cpmp = &cpm2_immr->im_cpm;
}
/* Set a baud rate generator. This needs lots of work. There are
* eight BRGs, which can be connected to the CPM channels or output
* as clocks. The BRGs are in two different block of internal
* memory mapped space.
* The baud rate clock is the system clock divided by something.
* It was set up long ago during the initial boot phase and is
* is given to us.
* Baud rate clocks are zero-based in the driver code (as that maps
* to port numbers). Documentation uses 1-based numbering.
*/
#define BRG_INT_CLK (((bd_t *)__res)->bi_brgfreq)
#define BRG_UART_CLK (BRG_INT_CLK/16)
/* This function is used by UARTS, or anything else that uses a 16x
* oversampled clock.
*/
void
cpm_setbrg(uint brg, uint rate)
{
volatile uint *bp;
/* This is good enough to get SMCs running.....
*/
if (brg < 4) {
bp = (uint *)&cpm2_immr->im_brgc1;
}
else {
bp = (uint *)&cpm2_immr->im_brgc5;
brg -= 4;
}
bp += brg;
*bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN;
}
/* This function is used to set high speed synchronous baud rate
* clocks.
*/
void
cpm2_fastbrg(uint brg, uint rate, int div16)
{
volatile uint *bp;
if (brg < 4) {
bp = (uint *)&cpm2_immr->im_brgc1;
}
else {
bp = (uint *)&cpm2_immr->im_brgc5;
brg -= 4;
}
bp += brg;
*bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN;
if (div16)
*bp |= CPM_BRG_DIV16;
}
/*
* dpalloc / dpfree bits.
*/
static spinlock_t cpm_dpmem_lock;
/* 16 blocks should be enough to satisfy all requests
* until the memory subsystem goes up... */
static rh_block_t cpm_boot_dpmem_rh_block[16];
static rh_info_t cpm_dpmem_info;
static void cpm2_dpinit(void)
{
spin_lock_init(&cpm_dpmem_lock);
/* initialize the info header */
rh_init(&cpm_dpmem_info, 1,
sizeof(cpm_boot_dpmem_rh_block) /
sizeof(cpm_boot_dpmem_rh_block[0]),
cpm_boot_dpmem_rh_block);
/* Attach the usable dpmem area */
/* XXX: This is actually crap. CPM_DATAONLY_BASE and
* CPM_DATAONLY_SIZE is only a subset of the available dpram. It
* varies with the processor and the microcode patches activated.
* But the following should be at least safe.
*/
rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE,
CPM_DATAONLY_SIZE);
}
/* This function returns an index into the DPRAM area.
*/
uint cpm_dpalloc(uint size, uint align)
{
void *start;
unsigned long flags;
spin_lock_irqsave(&cpm_dpmem_lock, flags);
cpm_dpmem_info.alignment = align;
start = rh_alloc(&cpm_dpmem_info, size, "commproc");
spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
return (uint)start;
}
EXPORT_SYMBOL(cpm_dpalloc);
int cpm_dpfree(uint offset)
{
int ret;
unsigned long flags;
spin_lock_irqsave(&cpm_dpmem_lock, flags);
ret = rh_free(&cpm_dpmem_info, (void *)offset);
spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
return ret;
}
EXPORT_SYMBOL(cpm_dpfree);
/* not sure if this is ever needed */
uint cpm_dpalloc_fixed(uint offset, uint size, uint align)
{
void *start;
unsigned long flags;
spin_lock_irqsave(&cpm_dpmem_lock, flags);
cpm_dpmem_info.alignment = align;
start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc");
spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
return (uint)start;
}
EXPORT_SYMBOL(cpm_dpalloc_fixed);
void cpm_dpdump(void)
{
rh_dump(&cpm_dpmem_info);
}
EXPORT_SYMBOL(cpm_dpdump);
void *cpm_dpram_addr(uint offset)
{
return (void *)&cpm2_immr->im_dprambase[offset];
}
EXPORT_SYMBOL(cpm_dpram_addr);

172
arch/ppc/syslib/cpm2_pic.c Normal file
View File

@@ -0,0 +1,172 @@
/* The CPM2 internal interrupt controller. It is usually
* the only interrupt controller.
* There are two 32-bit registers (high/low) for up to 64
* possible interrupts.
*
* Now, the fun starts.....Interrupt Numbers DO NOT MAP
* in a simple arithmetic fashion to mask or pending registers.
* That is, interrupt 4 does not map to bit position 4.
* We create two tables, indexed by vector number, to indicate
* which register to use and which bit in the register to use.
*/
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/irq.h>
#include <asm/immap_cpm2.h>
#include <asm/mpc8260.h>
#include "cpm2_pic.h"
static u_char irq_to_siureg[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
/* bit numbers do not match the docs, these are precomputed so the bit for
* a given irq is (1 << irq_to_siubit[irq]) */
static u_char irq_to_siubit[] = {
0, 15, 14, 13, 12, 11, 10, 9,
8, 7, 6, 5, 4, 3, 2, 1,
2, 1, 15, 14, 13, 12, 11, 10,
9, 8, 7, 6, 5, 4, 3, 0,
31, 30, 29, 28, 27, 26, 25, 24,
23, 22, 21, 20, 19, 18, 17, 16,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
};
static void cpm2_mask_irq(unsigned int irq_nr)
{
int bit, word;
volatile uint *simr;
irq_nr -= CPM_IRQ_OFFSET;
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
simr = &(cpm2_immr->im_intctl.ic_simrh);
ppc_cached_irq_mask[word] &= ~(1 << bit);
simr[word] = ppc_cached_irq_mask[word];
}
static void cpm2_unmask_irq(unsigned int irq_nr)
{
int bit, word;
volatile uint *simr;
irq_nr -= CPM_IRQ_OFFSET;
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
simr = &(cpm2_immr->im_intctl.ic_simrh);
ppc_cached_irq_mask[word] |= 1 << bit;
simr[word] = ppc_cached_irq_mask[word];
}
static void cpm2_mask_and_ack(unsigned int irq_nr)
{
int bit, word;
volatile uint *simr, *sipnr;
irq_nr -= CPM_IRQ_OFFSET;
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
simr = &(cpm2_immr->im_intctl.ic_simrh);
sipnr = &(cpm2_immr->im_intctl.ic_sipnrh);
ppc_cached_irq_mask[word] &= ~(1 << bit);
simr[word] = ppc_cached_irq_mask[word];
sipnr[word] = 1 << bit;
}
static void cpm2_end_irq(unsigned int irq_nr)
{
int bit, word;
volatile uint *simr;
if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
&& irq_desc[irq_nr].action) {
irq_nr -= CPM_IRQ_OFFSET;
bit = irq_to_siubit[irq_nr];
word = irq_to_siureg[irq_nr];
simr = &(cpm2_immr->im_intctl.ic_simrh);
ppc_cached_irq_mask[word] |= 1 << bit;
simr[word] = ppc_cached_irq_mask[word];
}
}
static struct hw_interrupt_type cpm2_pic = {
.typename = " CPM2 SIU ",
.enable = cpm2_unmask_irq,
.disable = cpm2_mask_irq,
.ack = cpm2_mask_and_ack,
.end = cpm2_end_irq,
};
int cpm2_get_irq(struct pt_regs *regs)
{
int irq;
unsigned long bits;
/* For CPM2, read the SIVEC register and shift the bits down
* to get the irq number. */
bits = cpm2_immr->im_intctl.ic_sivec;
irq = bits >> 26;
if (irq == 0)
return(-1);
return irq+CPM_IRQ_OFFSET;
}
void cpm2_init_IRQ(void)
{
int i;
/* Clear the CPM IRQ controller, in case it has any bits set
* from the bootloader
*/
/* Mask out everything */
cpm2_immr->im_intctl.ic_simrh = 0x00000000;
cpm2_immr->im_intctl.ic_simrl = 0x00000000;
wmb();
/* Ack everything */
cpm2_immr->im_intctl.ic_sipnrh = 0xffffffff;
cpm2_immr->im_intctl.ic_sipnrl = 0xffffffff;
wmb();
/* Dummy read of the vector */
i = cpm2_immr->im_intctl.ic_sivec;
rmb();
/* Initialize the default interrupt mapping priorities,
* in case the boot rom changed something on us.
*/
cpm2_immr->im_intctl.ic_sicr = 0;
cpm2_immr->im_intctl.ic_scprrh = 0x05309770;
cpm2_immr->im_intctl.ic_scprrl = 0x05309770;
/* Enable chaining to OpenPIC, and make everything level
*/
for (i = 0; i < NR_CPM_INTS; i++) {
irq_desc[i+CPM_IRQ_OFFSET].handler = &cpm2_pic;
irq_desc[i+CPM_IRQ_OFFSET].status |= IRQ_LEVEL;
}
}

View File

@@ -0,0 +1,8 @@
#ifndef _PPC_KERNEL_CPM2_H
#define _PPC_KERNEL_CPM2_H
extern int cpm2_get_irq(struct pt_regs *regs);
extern void cpm2_init_IRQ(void);
#endif /* _PPC_KERNEL_CPM2_H */

41
arch/ppc/syslib/dcr.S Normal file
View File

@@ -0,0 +1,41 @@
/*
* arch/ppc/syslib/dcr.S
*
* "Indirect" DCR access
*
* Copyright (c) 2004 Eugene Surovegin <ebs@ebshome.net>
*
* 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 <asm/ppc_asm.h>
#include <asm/processor.h>
#define DCR_ACCESS_PROLOG(table) \
rlwinm r3,r3,4,18,27; \
lis r5,table@h; \
ori r5,r5,table@l; \
add r3,r3,r5; \
mtctr r3; \
bctr
_GLOBAL(__mfdcr)
DCR_ACCESS_PROLOG(__mfdcr_table)
_GLOBAL(__mtdcr)
DCR_ACCESS_PROLOG(__mtdcr_table)
__mfdcr_table:
mfdcr r3,0; blr
__mtdcr_table:
mtdcr 0,r4; blr
dcr = 1
.rept 1023
mfdcr r3,dcr; blr
mtdcr dcr,r4; blr
dcr = dcr + 1
.endr

16
arch/ppc/syslib/gen550.h Normal file
View File

@@ -0,0 +1,16 @@
/*
* arch/ppc/syslib/gen550.h
*
* gen550 prototypes
*
* Matt Porter <mporter@kernel.crashing.org>
*
* 2004 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
extern void gen550_progress(char *, unsigned short);
extern void gen550_init(int, struct uart_port *);
extern void gen550_kgdb_map_scc(void);

View File

@@ -0,0 +1,182 @@
/*
* arch/ppc/syslib/gen550_dbg.c
*
* A library of polled 16550 serial routines. These are intended to
* be used to support progress messages, xmon, kgdb, etc. on a
* variety of platforms.
*
* Adapted from lots of code ripped from the arch/ppc/boot/ polled
* 16550 support.
*
* Author: Matt Porter <mporter@mvista.com>
*
* 2002-2003 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/serial.h>
#include <linux/tty.h> /* For linux/serial_core.h */
#include <linux/serial_core.h>
#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <asm/machdep.h>
#include <asm/serial.h>
#include <asm/io.h>
#define SERIAL_BAUD 9600
/* SERIAL_PORT_DFNS is defined in <asm/serial.h> */
#ifndef SERIAL_PORT_DFNS
#define SERIAL_PORT_DFNS
#endif
static struct serial_state rs_table[RS_TABLE_SIZE] = {
SERIAL_PORT_DFNS /* defined in <asm/serial.h> */
};
static void (*serial_outb)(unsigned long, unsigned char);
static unsigned long (*serial_inb)(unsigned long);
static int shift;
unsigned long direct_inb(unsigned long addr)
{
return readb((void __iomem *)addr);
}
void direct_outb(unsigned long addr, unsigned char val)
{
writeb(val, (void __iomem *)addr);
}
unsigned long io_inb(unsigned long port)
{
return inb(port);
}
void io_outb(unsigned long port, unsigned char val)
{
outb(val, port);
}
unsigned long serial_init(int chan, void *ignored)
{
unsigned long com_port;
unsigned char lcr, dlm;
/* We need to find out which type io we're expecting. If it's
* 'SERIAL_IO_PORT', we get an offset from the isa_io_base.
* If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */
switch (rs_table[chan].io_type) {
case SERIAL_IO_PORT:
com_port = rs_table[chan].port;
serial_outb = io_outb;
serial_inb = io_inb;
break;
case SERIAL_IO_MEM:
com_port = (unsigned long)rs_table[chan].iomem_base;
serial_outb = direct_outb;
serial_inb = direct_inb;
break;
default:
/* We can't deal with it. */
return -1;
}
/* How far apart the registers are. */
shift = rs_table[chan].iomem_reg_shift;
/* save the LCR */
lcr = serial_inb(com_port + (UART_LCR << shift));
/* Access baud rate */
serial_outb(com_port + (UART_LCR << shift), UART_LCR_DLAB);
dlm = serial_inb(com_port + (UART_DLM << shift));
/*
* Test if serial port is unconfigured
* We assume that no-one uses less than 110 baud or
* less than 7 bits per character these days.
* -- paulus.
*/
if ((dlm <= 4) && (lcr & 2)) {
/* port is configured, put the old LCR back */
serial_outb(com_port + (UART_LCR << shift), lcr);
}
else {
/* Input clock. */
serial_outb(com_port + (UART_DLL << shift),
(rs_table[chan].baud_base / SERIAL_BAUD) & 0xFF);
serial_outb(com_port + (UART_DLM << shift),
(rs_table[chan].baud_base / SERIAL_BAUD) >> 8);
/* 8 data, 1 stop, no parity */
serial_outb(com_port + (UART_LCR << shift), 0x03);
/* RTS/DTR */
serial_outb(com_port + (UART_MCR << shift), 0x03);
/* Clear & enable FIFOs */
serial_outb(com_port + (UART_FCR << shift), 0x07);
}
return (com_port);
}
void
serial_putc(unsigned long com_port, unsigned char c)
{
while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0)
;
serial_outb(com_port, c);
}
unsigned char
serial_getc(unsigned long com_port)
{
while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0)
;
return serial_inb(com_port);
}
int
serial_tstc(unsigned long com_port)
{
return ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0);
}
void
serial_close(unsigned long com_port)
{
}
void
gen550_init(int i, struct uart_port *serial_req)
{
rs_table[i].io_type = serial_req->iotype;
rs_table[i].port = serial_req->iobase;
rs_table[i].iomem_base = serial_req->membase;
rs_table[i].iomem_reg_shift = serial_req->regshift;
rs_table[i].baud_base = serial_req->uartclk ? serial_req->uartclk / 16 : BASE_BAUD;
}
#ifdef CONFIG_SERIAL_TEXT_DEBUG
void
gen550_progress(char *s, unsigned short hex)
{
volatile unsigned int progress_debugport;
volatile char c;
progress_debugport = serial_init(0, NULL);
serial_putc(progress_debugport, '\r');
while ((c = *s++) != 0)
serial_putc(progress_debugport, c);
serial_putc(progress_debugport, '\n');
serial_putc(progress_debugport, '\r');
}
#endif /* CONFIG_SERIAL_TEXT_DEBUG */

View File

@@ -0,0 +1,86 @@
/*
* arch/ppc/syslib/gen550_kgdb.c
*
* Generic 16550 kgdb support intended to be useful on a variety
* of platforms. To enable this support, it is necessary to set
* the CONFIG_GEN550 option. Any virtual mapping of the serial
* port(s) to be used can be accomplished by setting
* ppc_md.early_serial_map to a platform-specific mapping function.
*
* Adapted from ppc4xx_kgdb.c.
*
* Author: Matt Porter <mporter@kernel.crashing.org>
*
* 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/machdep.h>
extern unsigned long serial_init(int, void *);
extern unsigned long serial_getc(unsigned long);
extern unsigned long serial_putc(unsigned long, unsigned char);
#if defined(CONFIG_KGDB_TTYS0)
#define KGDB_PORT 0
#elif defined(CONFIG_KGDB_TTYS1)
#define KGDB_PORT 1
#elif defined(CONFIG_KGDB_TTYS2)
#define KGDB_PORT 2
#elif defined(CONFIG_KGDB_TTYS3)
#define KGDB_PORT 3
#else
#error "invalid kgdb_tty port"
#endif
static volatile unsigned int kgdb_debugport;
void putDebugChar(unsigned char c)
{
if (kgdb_debugport == 0)
kgdb_debugport = serial_init(KGDB_PORT, NULL);
serial_putc(kgdb_debugport, c);
}
int getDebugChar(void)
{
if (kgdb_debugport == 0)
kgdb_debugport = serial_init(KGDB_PORT, NULL);
return(serial_getc(kgdb_debugport));
}
void kgdb_interruptible(int enable)
{
return;
}
void putDebugString(char* str)
{
while (*str != '\0') {
putDebugChar(*str);
str++;
}
putDebugChar('\r');
return;
}
/*
* Note: gen550_init() must be called already on the port we are going
* to use.
*/
void
gen550_kgdb_map_scc(void)
{
printk(KERN_DEBUG "kgdb init\n");
if (ppc_md.early_serial_map)
ppc_md.early_serial_map();
kgdb_debugport = serial_init(KGDB_PORT, NULL);
}

View File

@@ -0,0 +1,328 @@
/*
* arch/ppc/syslib/gt64260_pic.c
*
* Interrupt controller support for Galileo's GT64260.
*
* Author: Chris Zankel <source@mvista.com>
* Modified by: Mark A. Greer <mgreer@mvista.com>
*
* Based on sources from Rabeeh Khoury / Galileo Technology
*
* 2001 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
* This file contains the specific functions to support the GT64260
* interrupt controller.
*
* The GT64260 has two main interrupt registers (high and low) that
* summarizes the interrupts generated by the units of the GT64260.
* Each bit is assigned to an interrupt number, where the low register
* are assigned from IRQ0 to IRQ31 and the high cause register
* from IRQ32 to IRQ63
* The GPP (General Purpose Port) interrupts are assigned from IRQ64 (GPP0)
* to IRQ95 (GPP31).
* get_irq() returns the lowest interrupt number that is currently asserted.
*
* Note:
* - This driver does not initialize the GPP when used as an interrupt
* input.
*/
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/stddef.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/mv64x60.h>
#define CPU_INTR_STR "gt64260 cpu interface error"
#define PCI0_INTR_STR "gt64260 pci 0 error"
#define PCI1_INTR_STR "gt64260 pci 1 error"
/* ========================== forward declaration ========================== */
static void gt64260_unmask_irq(unsigned int);
static void gt64260_mask_irq(unsigned int);
/* ========================== local declarations =========================== */
struct hw_interrupt_type gt64260_pic = {
.typename = " gt64260_pic ",
.enable = gt64260_unmask_irq,
.disable = gt64260_mask_irq,
.ack = gt64260_mask_irq,
.end = gt64260_unmask_irq,
};
u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */
static struct mv64x60_handle bh;
/* gt64260_init_irq()
*
* This function initializes the interrupt controller. It assigns
* all interrupts from IRQ0 to IRQ95 to the gt64260 interrupt controller.
*
* Note:
* We register all GPP inputs as interrupt source, but disable them.
*/
void __init
gt64260_init_irq(void)
{
int i;
if (ppc_md.progress)
ppc_md.progress("gt64260_init_irq: enter", 0x0);
bh.v_base = mv64x60_get_bridge_vbase();
ppc_cached_irq_mask[0] = 0;
ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */
ppc_cached_irq_mask[2] = 0;
/* disable all interrupts and clear current interrupts */
mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0);
mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]);
mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]);
/* use the gt64260 for all (possible) interrupt sources */
for (i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++)
irq_desc[i].handler = &gt64260_pic;
if (ppc_md.progress)
ppc_md.progress("gt64260_init_irq: exit", 0x0);
}
/*
* gt64260_get_irq()
*
* This function returns the lowest interrupt number of all interrupts that
* are currently asserted.
*
* Input Variable(s):
* struct pt_regs* not used
*
* Output Variable(s):
* None.
*
* Returns:
* int <interrupt number> or -2 (bogus interrupt)
*/
int
gt64260_get_irq(struct pt_regs *regs)
{
int irq;
int irq_gpp;
irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_LO);
irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
if (irq == -1) {
irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_HI);
irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]);
if (irq == -1)
irq = -2; /* bogus interrupt, should never happen */
else {
if (irq >= 24) {
irq_gpp = mv64x60_read(&bh,
MV64x60_GPP_INTR_CAUSE);
irq_gpp = __ilog2(irq_gpp &
ppc_cached_irq_mask[2]);
if (irq_gpp == -1)
irq = -2;
else {
irq = irq_gpp + 64;
mv64x60_write(&bh,
MV64x60_GPP_INTR_CAUSE,
~(1 << (irq - 64)));
}
} else
irq += 32;
}
}
(void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE);
if (irq < 0)
return (irq);
else
return (gt64260_irq_base + irq);
}
/* gt64260_unmask_irq()
*
* This function enables an interrupt.
*
* Input Variable(s):
* unsigned int interrupt number (IRQ0...IRQ95).
*
* Output Variable(s):
* None.
*
* Returns:
* void
*/
static void
gt64260_unmask_irq(unsigned int irq)
{
irq -= gt64260_irq_base;
if (irq > 31)
if (irq > 63) /* unmask GPP irq */
mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
ppc_cached_irq_mask[2] |= (1 << (irq - 64)));
else /* mask high interrupt register */
mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI,
ppc_cached_irq_mask[1] |= (1 << (irq - 32)));
else /* mask low interrupt register */
mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO,
ppc_cached_irq_mask[0] |= (1 << irq));
(void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
return;
}
/* gt64260_mask_irq()
*
* This function disables the requested interrupt.
*
* Input Variable(s):
* unsigned int interrupt number (IRQ0...IRQ95).
*
* Output Variable(s):
* None.
*
* Returns:
* void
*/
static void
gt64260_mask_irq(unsigned int irq)
{
irq -= gt64260_irq_base;
if (irq > 31)
if (irq > 63) /* mask GPP irq */
mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
ppc_cached_irq_mask[2] &= ~(1 << (irq - 64)));
else /* mask high interrupt register */
mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI,
ppc_cached_irq_mask[1] &= ~(1 << (irq - 32)));
else /* mask low interrupt register */
mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO,
ppc_cached_irq_mask[0] &= ~(1 << irq));
(void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
return;
}
static irqreturn_t
gt64260_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
{
printk(KERN_ERR "gt64260_cpu_error_int_handler: %s 0x%08x\n",
"Error on CPU interface - Cause regiser",
mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE));
printk(KERN_ERR "\tCPU error register dump:\n");
printk(KERN_ERR "\tAddress low 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress high 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI));
printk(KERN_ERR "\tData low 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO));
printk(KERN_ERR "\tData high 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI));
printk(KERN_ERR "\tParity 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY));
mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
return IRQ_HANDLED;
}
static irqreturn_t
gt64260_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
{
u32 val;
unsigned int pci_bus = (unsigned int)dev_id;
if (pci_bus == 0) { /* Error on PCI 0 */
val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE);
printk(KERN_ERR "%s: Error in PCI %d Interface\n",
"gt64260_pci_error_int_handler", pci_bus);
printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
printk(KERN_ERR "\tCause register 0x%08x\n", val);
printk(KERN_ERR "\tAddress Low 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress High 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI));
printk(KERN_ERR "\tAttribute 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO));
printk(KERN_ERR "\tCommand 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD));
mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val);
}
if (pci_bus == 1) { /* Error on PCI 1 */
val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE);
printk(KERN_ERR "%s: Error in PCI %d Interface\n",
"gt64260_pci_error_int_handler", pci_bus);
printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
printk(KERN_ERR "\tCause register 0x%08x\n", val);
printk(KERN_ERR "\tAddress Low 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress High 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI));
printk(KERN_ERR "\tAttribute 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO));
printk(KERN_ERR "\tCommand 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD));
mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val);
}
return IRQ_HANDLED;
}
static int __init
gt64260_register_hdlrs(void)
{
int rc;
/* Register CPU interface error interrupt handler */
if ((rc = request_irq(MV64x60_IRQ_CPU_ERR,
gt64260_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0)))
printk(KERN_WARNING "Can't register cpu error handler: %d", rc);
mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0);
mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000fe);
/* Register PCI 0 error interrupt handler */
if ((rc = request_irq(MV64360_IRQ_PCI0, gt64260_pci_error_int_handler,
SA_INTERRUPT, PCI0_INTR_STR, (void *)0)))
printk(KERN_WARNING "Can't register pci 0 error handler: %d",
rc);
mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0);
mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0x003c0c24);
/* Register PCI 1 error interrupt handler */
if ((rc = request_irq(MV64360_IRQ_PCI1, gt64260_pci_error_int_handler,
SA_INTERRUPT, PCI1_INTR_STR, (void *)1)))
printk(KERN_WARNING "Can't register pci 1 error handler: %d",
rc);
mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0);
mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0x003c0c24);
return 0;
}
arch_initcall(gt64260_register_hdlrs);

302
arch/ppc/syslib/harrier.c Normal file
View File

@@ -0,0 +1,302 @@
/*
* arch/ppc/syslib/harrier.c
*
* Motorola MCG Harrier northbridge/memory controller support
*
* Author: Dale Farnsworth
* dale.farnsworth@mvista.com
*
* 2001 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/harrier_defs.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pci.h>
#include <asm/pci-bridge.h>
#include <asm/open_pic.h>
#include <asm/harrier.h>
/* define defaults for inbound windows */
#define HARRIER_ITAT_DEFAULT (HARRIER_ITAT_ENA | \
HARRIER_ITAT_MEM | \
HARRIER_ITAT_WPE | \
HARRIER_ITAT_GBL)
#define HARRIER_MPAT_DEFAULT (HARRIER_ITAT_ENA | \
HARRIER_ITAT_MEM | \
HARRIER_ITAT_WPE | \
HARRIER_ITAT_GBL)
/*
* Initialize the inbound window size on a non-monarch harrier.
*/
void __init harrier_setup_nonmonarch(uint ppc_reg_base, uint in0_size)
{
u16 temps;
u32 temp;
if (in0_size > HARRIER_ITSZ_2GB) {
printk
("harrier_setup_nonmonarch: Invalid window size code %d\n",
in0_size);
return;
}
/* Clear the PCI memory enable bit. If we don't, then when the
* inbound windows are enabled below, the corresponding BARs will be
* "live" and start answering to PCI memory reads from their default
* addresses (0x0), which overlap with system RAM.
*/
temps = in_le16((u16 *) (ppc_reg_base +
HARRIER_XCSR_CONFIG(PCI_COMMAND)));
temps &= ~(PCI_COMMAND_MEMORY);
out_le16((u16 *) (ppc_reg_base + HARRIER_XCSR_CONFIG(PCI_COMMAND)),
temps);
/* Setup a non-prefetchable inbound window */
out_le32((u32 *) (ppc_reg_base +
HARRIER_XCSR_CONFIG(HARRIER_ITSZ0_OFF)), in0_size);
temp = in_le32((u32 *) (ppc_reg_base +
HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF)));
temp &= ~HARRIER_ITAT_PRE;
temp |= HARRIER_ITAT_DEFAULT;
out_le32((u32 *) (ppc_reg_base +
HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF)), temp);
/* Enable the message passing block */
temp = in_le32((u32 *) (ppc_reg_base +
HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF)));
temp |= HARRIER_MPAT_DEFAULT;
out_le32((u32 *) (ppc_reg_base +
HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF)), temp);
}
void __init harrier_release_eready(uint ppc_reg_base)
{
ulong temp;
/*
* Set EREADY to allow the line to be pulled up after everyone is
* ready.
*/
temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF));
temp |= HARRIER_EREADY;
out_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF), temp);
}
void __init harrier_wait_eready(uint ppc_reg_base)
{
ulong temp;
/*
* Poll the ERDYS line until it goes high to indicate that all
* non-monarch PrPMCs are ready for bus enumeration (or that there are
* no PrPMCs present).
*/
/* FIXME: Add a timeout of some kind to prevent endless waits. */
do {
temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF));
} while (!(temp & HARRIER_ERDYS));
}
/*
* Initialize the Motorola MCG Harrier host bridge.
*
* This means setting up the PPC bus to PCI memory and I/O space mappings,
* setting the PCI memory space address of the MPIC (mapped straight
* through), and ioremap'ing the mpic registers.
* 'OpenPIC_Addr' will be set correctly by this routine.
* This routine will not change the PCI_CONFIG_ADDR or PCI_CONFIG_DATA
* addresses and assumes that the mapping of PCI memory space back to system
* memory is set up correctly by PPCBug.
*/
int __init
harrier_init(struct pci_controller *hose,
uint ppc_reg_base,
ulong processor_pci_mem_start,
ulong processor_pci_mem_end,
ulong processor_pci_io_start,
ulong processor_pci_io_end, ulong processor_mpic_base)
{
uint addr, offset;
/*
* Some sanity checks...
*/
if (((processor_pci_mem_start & 0xffff0000) != processor_pci_mem_start)
|| ((processor_pci_io_start & 0xffff0000) !=
processor_pci_io_start)) {
printk("harrier_init: %s\n",
"PPC to PCI mappings must start on 64 KB boundaries");
return -1;
}
if (((processor_pci_mem_end & 0x0000ffff) != 0x0000ffff) ||
((processor_pci_io_end & 0x0000ffff) != 0x0000ffff)) {
printk("harrier_init: PPC to PCI mappings %s\n",
"must end just before a 64 KB boundaries");
return -1;
}
if (((processor_pci_mem_end - processor_pci_mem_start) !=
(hose->mem_space.end - hose->mem_space.start)) ||
((processor_pci_io_end - processor_pci_io_start) !=
(hose->io_space.end - hose->io_space.start))) {
printk("harrier_init: %s\n",
"PPC and PCI memory or I/O space sizes don't match");
return -1;
}
if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) {
printk("harrier_init: %s\n",
"MPIC address must start on 256 KB boundary");
return -1;
}
if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) {
printk("harrier_init: %s\n",
"pci_dram_offset must be multiple of 64 KB");
return -1;
}
/*
* Program the OTAD/OTOF registers to set up the PCI Mem & I/O
* space mappings. These are the mappings going from the processor to
* the PCI bus.
*
* Note: Don't need to 'AND' start/end addresses with 0xffff0000
* because sanity check above ensures that they are properly
* aligned.
*/
/* Set up PPC->PCI Mem mapping */
addr = processor_pci_mem_start | (processor_pci_mem_end >> 16);
#ifdef CONFIG_HARRIER_STORE_GATHERING
offset = (hose->mem_space.start - processor_pci_mem_start) | 0x9a;
#else
offset = (hose->mem_space.start - processor_pci_mem_start) | 0x92;
#endif
out_be32((uint *) (ppc_reg_base + HARRIER_OTAD0_OFF), addr);
out_be32((uint *) (ppc_reg_base + HARRIER_OTOF0_OFF), offset);
/* Set up PPC->PCI I/O mapping -- Contiguous I/O space */
addr = processor_pci_io_start | (processor_pci_io_end >> 16);
offset = (hose->io_space.start - processor_pci_io_start) | 0x80;
out_be32((uint *) (ppc_reg_base + HARRIER_OTAD1_OFF), addr);
out_be32((uint *) (ppc_reg_base + HARRIER_OTOF1_OFF), offset);
/* Enable MPIC */
OpenPIC_Addr = (void *)processor_mpic_base;
addr = (processor_mpic_base >> 16) | 1;
out_be16((ushort *) (ppc_reg_base + HARRIER_MBAR_OFF), addr);
out_8((u_char *) (ppc_reg_base + HARRIER_MPIC_CSR_OFF),
HARRIER_MPIC_OPI_ENABLE);
return 0;
}
/*
* Find the amount of RAM present.
* This assumes that PPCBug has initialized the memory controller (SMC)
* on the Harrier correctly (i.e., it does no sanity checking).
* It also assumes that the memory base registers are set to configure the
* memory as contigous starting with "RAM A BASE", "RAM B BASE", etc.
* however, RAM base registers can be skipped (e.g. A, B, C are set,
* D is skipped but E is set is okay).
*/
#define MB (1024*1024UL)
static uint harrier_size_table[] __initdata = {
0 * MB, /* 0 ==> 0 MB */
32 * MB, /* 1 ==> 32 MB */
64 * MB, /* 2 ==> 64 MB */
64 * MB, /* 3 ==> 64 MB */
128 * MB, /* 4 ==> 128 MB */
128 * MB, /* 5 ==> 128 MB */
128 * MB, /* 6 ==> 128 MB */
256 * MB, /* 7 ==> 256 MB */
256 * MB, /* 8 ==> 256 MB */
256 * MB, /* 9 ==> 256 MB */
512 * MB, /* a ==> 512 MB */
512 * MB, /* b ==> 512 MB */
512 * MB, /* c ==> 512 MB */
1024 * MB, /* d ==> 1024 MB */
1024 * MB, /* e ==> 1024 MB */
2048 * MB, /* f ==> 2048 MB */
};
/*
* *** WARNING: You MUST have a BAT set up to map in the XCSR regs ***
*
* Read the memory controller's registers to determine the amount of system
* memory. Assumes that the memory controller registers are already mapped
* into virtual memory--too early to use ioremap().
*/
unsigned long __init harrier_get_mem_size(uint xcsr_base)
{
ulong last_addr;
int i;
uint vend_dev_id;
uint *size_table;
uint val;
uint *csrp;
uint size;
int size_table_entries;
vend_dev_id = in_be32((uint *) xcsr_base + PCI_VENDOR_ID);
if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) {
printk("harrier_get_mem_size: %s (0x%x)\n",
"Not a Motorola Memory Controller", vend_dev_id);
return 0;
}
vend_dev_id &= 0x0000ffff;
if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HARRIER) {
size_table = harrier_size_table;
size_table_entries = sizeof(harrier_size_table) /
sizeof(harrier_size_table[0]);
} else {
printk("harrier_get_mem_size: %s (0x%x)\n",
"Not a Harrier", vend_dev_id);
return 0;
}
last_addr = 0;
csrp = (uint *) (xcsr_base + HARRIER_SDBA_OFF);
for (i = 0; i < 8; i++) {
val = in_be32(csrp++);
if (val & 0x100) { /* If enabled */
size = val >> HARRIER_SDB_SIZE_SHIFT;
size &= HARRIER_SDB_SIZE_MASK;
if (size >= size_table_entries) {
break; /* Register not set correctly */
}
size = size_table[size];
val &= ~(size - 1);
val += size;
if (val > last_addr) {
last_addr = val;
}
}
}
return last_addr;
}

View File

@@ -0,0 +1,319 @@
/*
* arch/ppc/syslib/hawk_common.c
*
* Common Motorola PowerPlus Platform--really Falcon/Raven or HAWK.
*
* Author: Mark A. Greer
* mgreer@mvista.com
*
* 2001 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pci.h>
#include <asm/pci-bridge.h>
#include <asm/open_pic.h>
#include <asm/hawk.h>
/*
* The Falcon/Raven and HAWK has 4 sets of registers:
* 1) PPC Registers which define the mappings from PPC bus to PCI bus,
* etc.
* 2) PCI Registers which define the mappings from PCI bus to PPC bus and the
* MPIC base address.
* 3) MPIC registers.
* 4) System Memory Controller (SMC) registers.
*/
/*
* Initialize the Motorola MCG Raven or HAWK host bridge.
*
* This means setting up the PPC bus to PCI memory and I/O space mappings,
* setting the PCI memory space address of the MPIC (mapped straight
* through), and ioremap'ing the mpic registers.
* This routine will set the PCI_CONFIG_ADDR or PCI_CONFIG_DATA
* addresses based on the PCI I/O address that is passed in.
* 'OpenPIC_Addr' will be set correctly by this routine.
*/
int __init
hawk_init(struct pci_controller *hose,
uint ppc_reg_base,
ulong processor_pci_mem_start,
ulong processor_pci_mem_end,
ulong processor_pci_io_start,
ulong processor_pci_io_end,
ulong processor_mpic_base)
{
uint addr, offset;
/*
* Some sanity checks...
*/
if (((processor_pci_mem_start&0xffff0000) != processor_pci_mem_start) ||
((processor_pci_io_start &0xffff0000) != processor_pci_io_start)) {
printk("hawk_init: %s\n",
"PPC to PCI mappings must start on 64 KB boundaries");
return -1;
}
if (((processor_pci_mem_end &0x0000ffff) != 0x0000ffff) ||
((processor_pci_io_end &0x0000ffff) != 0x0000ffff)) {
printk("hawk_init: PPC to PCI mappings %s\n",
"must end just before a 64 KB boundaries");
return -1;
}
if (((processor_pci_mem_end - processor_pci_mem_start) !=
(hose->mem_space.end - hose->mem_space.start)) ||
((processor_pci_io_end - processor_pci_io_start) !=
(hose->io_space.end - hose->io_space.start))) {
printk("hawk_init: %s\n",
"PPC and PCI memory or I/O space sizes don't match");
return -1;
}
if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) {
printk("hawk_init: %s\n",
"MPIC address must start on 256 MB boundary");
return -1;
}
if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) {
printk("hawk_init: %s\n",
"pci_dram_offset must be multiple of 64 KB");
return -1;
}
/*
* Disable previous PPC->PCI mappings.
*/
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), 0x00000000);
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), 0x00000000);
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF2_OFF), 0x00000000);
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), 0x00000000);
/*
* Program the XSADD/XSOFF registers to set up the PCI Mem & I/O
* space mappings. These are the mappings going from the processor to
* the PCI bus.
*
* Note: Don't need to 'AND' start/end addresses with 0xffff0000
* because sanity check above ensures that they are properly
* aligned.
*/
/* Set up PPC->PCI Mem mapping */
addr = processor_pci_mem_start | (processor_pci_mem_end >> 16);
offset = (hose->mem_space.start - processor_pci_mem_start) | 0xd2;
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD0_OFF), addr);
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), offset);
/* Set up PPC->MPIC mapping on the bridge */
addr = processor_mpic_base |
(((processor_mpic_base + HAWK_MPIC_SIZE) >> 16) - 1);
/* No write posting for this PCI Mem space */
offset = (hose->mem_space.start - processor_pci_mem_start) | 0xc2;
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD1_OFF), addr);
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), offset);
/* Set up PPC->PCI I/O mapping -- Contiguous I/O space */
addr = processor_pci_io_start | (processor_pci_io_end >> 16);
offset = (hose->io_space.start - processor_pci_io_start) | 0xc0;
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD3_OFF), addr);
out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), offset);
hose->io_base_virt = (void *)ioremap(processor_pci_io_start,
(processor_pci_io_end - processor_pci_io_start + 1));
/*
* Set up the indirect method of accessing PCI config space.
* The PCI config addr/data pair based on start addr of PCI I/O space.
*/
setup_indirect_pci(hose,
processor_pci_io_start + HAWK_PCI_CONFIG_ADDR_OFF,
processor_pci_io_start + HAWK_PCI_CONFIG_DATA_OFF);
/*
* Disable previous PCI->PPC mappings.
*/
/* XXXX Put in mappings from PCI bus to processor bus XXXX */
/*
* Disable MPIC response to PCI I/O space (BAR 0).
* Make MPIC respond to PCI Mem space at specified address.
* (BAR 1).
*/
early_write_config_dword(hose,
0,
PCI_DEVFN(0,0),
PCI_BASE_ADDRESS_0,
0x00000000 | 0x1);
early_write_config_dword(hose,
0,
PCI_DEVFN(0,0),
PCI_BASE_ADDRESS_1,
(processor_mpic_base -
processor_pci_mem_start +
hose->mem_space.start) | 0x0);
/* Map MPIC into vitual memory */
OpenPIC_Addr = ioremap(processor_mpic_base, HAWK_MPIC_SIZE);
return 0;
}
/*
* Find the amount of RAM present.
* This assumes that PPCBug has initialized the memory controller (SMC)
* on the Falcon/HAWK correctly (i.e., it does no sanity checking).
* It also assumes that the memory base registers are set to configure the
* memory as contigous starting with "RAM A BASE", "RAM B BASE", etc.
* however, RAM base registers can be skipped (e.g. A, B, C are set,
* D is skipped but E is set is okay).
*/
#define MB (1024*1024)
static uint reg_offset_table[] __initdata = {
HAWK_SMC_RAM_A_SIZE_REG_OFF,
HAWK_SMC_RAM_B_SIZE_REG_OFF,
HAWK_SMC_RAM_C_SIZE_REG_OFF,
HAWK_SMC_RAM_D_SIZE_REG_OFF,
HAWK_SMC_RAM_E_SIZE_REG_OFF,
HAWK_SMC_RAM_F_SIZE_REG_OFF,
HAWK_SMC_RAM_G_SIZE_REG_OFF,
HAWK_SMC_RAM_H_SIZE_REG_OFF
};
static uint falcon_size_table[] __initdata = {
0 * MB, /* 0 ==> 0 MB */
16 * MB, /* 1 ==> 16 MB */
32 * MB, /* 2 ==> 32 MB */
64 * MB, /* 3 ==> 64 MB */
128 * MB, /* 4 ==> 128 MB */
256 * MB, /* 5 ==> 256 MB */
1024 * MB, /* 6 ==> 1024 MB (1 GB) */
};
static uint hawk_size_table[] __initdata = {
0 * MB, /* 0 ==> 0 MB */
32 * MB, /* 1 ==> 32 MB */
64 * MB, /* 2 ==> 64 MB */
64 * MB, /* 3 ==> 64 MB */
128 * MB, /* 4 ==> 128 MB */
128 * MB, /* 5 ==> 128 MB */
128 * MB, /* 6 ==> 128 MB */
256 * MB, /* 7 ==> 256 MB */
256 * MB, /* 8 ==> 256 MB */
512 * MB, /* 9 ==> 512 MB */
};
/*
* *** WARNING: You MUST have a BAT set up to map in the SMC regs ***
*
* Read the memory controller's registers to determine the amount of system
* memory. Assumes that the memory controller registers are already mapped
* into virtual memory--too early to use ioremap().
*/
unsigned long __init
hawk_get_mem_size(uint smc_base)
{
unsigned long total;
int i, size_table_entries, reg_limit;
uint vend_dev_id;
uint *size_table;
u_char val;
vend_dev_id = in_be32((uint *)smc_base + PCI_VENDOR_ID);
if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) {
printk("hawk_get_mem_size: %s (0x%x)\n",
"Not a Motorola Memory Controller", vend_dev_id);
return 0;
}
vend_dev_id &= 0x0000ffff;
if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_FALCON) {
size_table = falcon_size_table;
size_table_entries = sizeof(falcon_size_table) /
sizeof(falcon_size_table[0]);
reg_limit = FALCON_SMC_REG_COUNT;
}
else if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HAWK) {
size_table = hawk_size_table;
size_table_entries = sizeof(hawk_size_table) /
sizeof(hawk_size_table[0]);
reg_limit = HAWK_SMC_REG_COUNT;
}
else {
printk("hawk_get_mem_size: %s (0x%x)\n",
"Not a Falcon or HAWK", vend_dev_id);
return 0;
}
total = 0;
/* Check every reg because PPCBug may skip some */
for (i=0; i<reg_limit; i++) {
val = in_8((u_char *)(smc_base + reg_offset_table[i]));
if (val & 0x80) { /* If enabled */
val &= 0x0f;
/* Don't go past end of size_table */
if (val < size_table_entries) {
total += size_table[val];
}
else { /* Register not set correctly */
break;
}
}
}
return total;
}
int __init
hawk_mpic_init(unsigned int pci_mem_offset)
{
unsigned short devid;
unsigned int pci_membase;
/* Check the first PCI device to see if it is a Raven or Hawk. */
early_read_config_word(0, 0, 0, PCI_DEVICE_ID, &devid);
switch (devid) {
case PCI_DEVICE_ID_MOTOROLA_RAVEN:
case PCI_DEVICE_ID_MOTOROLA_HAWK:
break;
default:
OpenPIC_Addr = NULL;
return 1;
}
/* Read the memory base register. */
early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);
if (pci_membase == 0) {
OpenPIC_Addr = NULL;
return 1;
}
/* Map the MPIC registers to virtual memory. */
OpenPIC_Addr = ioremap(pci_membase + pci_mem_offset, 0x22000);
return 0;
}

211
arch/ppc/syslib/i8259.c Normal file
View File

@@ -0,0 +1,211 @@
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/i8259.h>
static volatile unsigned char *pci_intack; /* RO, gives us the irq vector */
unsigned char cached_8259[2] = { 0xff, 0xff };
#define cached_A1 (cached_8259[0])
#define cached_21 (cached_8259[1])
static DEFINE_SPINLOCK(i8259_lock);
int i8259_pic_irq_offset;
/*
* Acknowledge the IRQ using either the PCI host bridge's interrupt
* acknowledge feature or poll. How i8259_init() is called determines
* which is called. It should be noted that polling is broken on some
* IBM and Motorola PReP boxes so we must use the int-ack feature on them.
*/
int
i8259_irq(struct pt_regs *regs)
{
int irq;
spin_lock(&i8259_lock);
/* Either int-ack or poll for the IRQ */
if (pci_intack)
irq = *pci_intack;
else {
/* Perform an interrupt acknowledge cycle on controller 1. */
outb(0x0C, 0x20); /* prepare for poll */
irq = inb(0x20) & 7;
if (irq == 2 ) {
/*
* Interrupt is cascaded so perform interrupt
* acknowledge on controller 2.
*/
outb(0x0C, 0xA0); /* prepare for poll */
irq = (inb(0xA0) & 7) + 8;
}
}
if (irq == 7) {
/*
* This may be a spurious interrupt.
*
* Read the interrupt status register (ISR). If the most
* significant bit is not set then there is no valid
* interrupt.
*/
if (!pci_intack)
outb(0x0B, 0x20); /* ISR register */
if(~inb(0x20) & 0x80)
irq = -1;
}
spin_unlock(&i8259_lock);
return irq;
}
static void i8259_mask_and_ack_irq(unsigned int irq_nr)
{
unsigned long flags;
spin_lock_irqsave(&i8259_lock, flags);
if ( irq_nr >= i8259_pic_irq_offset )
irq_nr -= i8259_pic_irq_offset;
if (irq_nr > 7) {
cached_A1 |= 1 << (irq_nr-8);
inb(0xA1); /* DUMMY */
outb(cached_A1,0xA1);
outb(0x20,0xA0); /* Non-specific EOI */
outb(0x20,0x20); /* Non-specific EOI to cascade */
} else {
cached_21 |= 1 << irq_nr;
inb(0x21); /* DUMMY */
outb(cached_21,0x21);
outb(0x20,0x20); /* Non-specific EOI */
}
spin_unlock_irqrestore(&i8259_lock, flags);
}
static void i8259_set_irq_mask(int irq_nr)
{
outb(cached_A1,0xA1);
outb(cached_21,0x21);
}
static void i8259_mask_irq(unsigned int irq_nr)
{
unsigned long flags;
spin_lock_irqsave(&i8259_lock, flags);
if ( irq_nr >= i8259_pic_irq_offset )
irq_nr -= i8259_pic_irq_offset;
if ( irq_nr < 8 )
cached_21 |= 1 << irq_nr;
else
cached_A1 |= 1 << (irq_nr-8);
i8259_set_irq_mask(irq_nr);
spin_unlock_irqrestore(&i8259_lock, flags);
}
static void i8259_unmask_irq(unsigned int irq_nr)
{
unsigned long flags;
spin_lock_irqsave(&i8259_lock, flags);
if ( irq_nr >= i8259_pic_irq_offset )
irq_nr -= i8259_pic_irq_offset;
if ( irq_nr < 8 )
cached_21 &= ~(1 << irq_nr);
else
cached_A1 &= ~(1 << (irq_nr-8));
i8259_set_irq_mask(irq_nr);
spin_unlock_irqrestore(&i8259_lock, flags);
}
static void i8259_end_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))
&& irq_desc[irq].action)
i8259_unmask_irq(irq);
}
struct hw_interrupt_type i8259_pic = {
" i8259 ",
NULL,
NULL,
i8259_unmask_irq,
i8259_mask_irq,
i8259_mask_and_ack_irq,
i8259_end_irq,
NULL
};
static struct resource pic1_iores = {
.name = "8259 (master)",
.start = 0x20,
.end = 0x21,
.flags = IORESOURCE_BUSY,
};
static struct resource pic2_iores = {
.name = "8259 (slave)",
.start = 0xa0,
.end = 0xa1,
.flags = IORESOURCE_BUSY,
};
static struct resource pic_edgectrl_iores = {
.name = "8259 edge control",
.start = 0x4d0,
.end = 0x4d1,
.flags = IORESOURCE_BUSY,
};
static struct irqaction i8259_irqaction = {
.handler = no_action,
.flags = SA_INTERRUPT,
.mask = CPU_MASK_NONE,
.name = "82c59 secondary cascade",
};
/*
* i8259_init()
* intack_addr - PCI interrupt acknowledge (real) address which will return
* the active irq from the 8259
*/
void __init
i8259_init(long intack_addr)
{
unsigned long flags;
spin_lock_irqsave(&i8259_lock, flags);
/* init master interrupt controller */
outb(0x11, 0x20); /* Start init sequence */
outb(0x00, 0x21); /* Vector base */
outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
outb(0x01, 0x21); /* Select 8086 mode */
/* init slave interrupt controller */
outb(0x11, 0xA0); /* Start init sequence */
outb(0x08, 0xA1); /* Vector base */
outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
outb(0x01, 0xA1); /* Select 8086 mode */
/* always read ISR */
outb(0x0B, 0x20);
outb(0x0B, 0xA0);
/* Mask all interrupts */
outb(cached_A1, 0xA1);
outb(cached_21, 0x21);
spin_unlock_irqrestore(&i8259_lock, flags);
/* reserve our resources */
setup_irq( i8259_pic_irq_offset + 2, &i8259_irqaction);
request_resource(&ioport_resource, &pic1_iores);
request_resource(&ioport_resource, &pic2_iores);
request_resource(&ioport_resource, &pic_edgectrl_iores);
if (intack_addr != 0)
pci_intack = ioremap(intack_addr, 1);
}

View File

@@ -0,0 +1,76 @@
/*
* arch/ppc/syslib/ibm440gp_common.c
*
* PPC440GP system library
*
* Matt Porter <mporter@mvista.com>
* Copyright 2002-2003 MontaVista Software Inc.
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003 Zultys Technologies
*
* 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/types.h>
#include <asm/reg.h>
#include <asm/ibm44x.h>
#include <asm/mmu.h>
/*
* Calculate 440GP clocks
*/
void __init ibm440gp_get_clocks(struct ibm44x_clocks* p,
unsigned int sys_clk,
unsigned int ser_clk)
{
u32 cpc0_sys0 = mfdcr(DCRN_CPC0_SYS0);
u32 cpc0_cr0 = mfdcr(DCRN_CPC0_CR0);
u32 opdv = ((cpc0_sys0 >> 10) & 0x3) + 1;
u32 epdv = ((cpc0_sys0 >> 8) & 0x3) + 1;
if (cpc0_sys0 & 0x2){
/* Bypass system PLL */
p->cpu = p->plb = sys_clk;
}
else {
u32 fbdv, fwdva, fwdvb, m, vco;
fbdv = (cpc0_sys0 >> 18) & 0x0f;
if (!fbdv)
fbdv = 16;
fwdva = 8 - ((cpc0_sys0 >> 15) & 0x7);
fwdvb = 8 - ((cpc0_sys0 >> 12) & 0x7);
/* Feedback path */
if (cpc0_sys0 & 0x00000080){
/* PerClk */
m = fwdvb * opdv * epdv;
}
else {
/* CPU clock */
m = fbdv * fwdva;
}
vco = sys_clk * m;
p->cpu = vco / fwdva;
p->plb = vco / fwdvb;
}
p->opb = p->plb / opdv;
p->ebc = p->opb / epdv;
if (cpc0_cr0 & 0x00400000){
/* External UART clock */
p->uart0 = p->uart1 = ser_clk;
}
else {
/* Internal UART clock */
u32 uart_div = ((cpc0_cr0 >> 16) & 0x1f) + 1;
p->uart0 = p->uart1 = p->plb / uart_div;
}
}

View File

@@ -0,0 +1,35 @@
/*
* arch/ppc/kernel/ibm440gp_common.h
*
* PPC440GP system library
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003 Zultys Technologies
*
* 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.
*
*/
#ifdef __KERNEL__
#ifndef __PPC_SYSLIB_IBM440GP_COMMON_H
#define __PPC_SYSLIB_IBM440GP_COMMON_H
#ifndef __ASSEMBLY__
#include <linux/config.h>
#include <linux/init.h>
#include <syslib/ibm44x_common.h>
/*
* Please, refer to the Figure 13.1 in 440GP user manual
*
* if internal UART clock is used, ser_clk is ignored
*/
void ibm440gp_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk,
unsigned int ser_clk) __init;
#endif /* __ASSEMBLY__ */
#endif /* __PPC_SYSLIB_IBM440GP_COMMON_H */
#endif /* __KERNEL__ */

View File

@@ -0,0 +1,270 @@
/*
* arch/ppc/kernel/ibm440gx_common.c
*
* PPC440GX system library
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003, 2004 Zultys Technologies
*
* 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/interrupt.h>
#include <asm/ibm44x.h>
#include <asm/mmu.h>
#include <asm/processor.h>
#include <syslib/ibm440gx_common.h>
/*
* Calculate 440GX clocks
*/
static inline u32 __fix_zero(u32 v, u32 def){
return v ? v : def;
}
void __init ibm440gx_get_clocks(struct ibm44x_clocks* p, unsigned int sys_clk,
unsigned int ser_clk)
{
u32 pllc = CPR_READ(DCRN_CPR_PLLC);
u32 plld = CPR_READ(DCRN_CPR_PLLD);
u32 uart0 = SDR_READ(DCRN_SDR_UART0);
u32 uart1 = SDR_READ(DCRN_SDR_UART1);
/* Dividers */
u32 fbdv = __fix_zero((plld >> 24) & 0x1f, 32);
u32 fwdva = __fix_zero((plld >> 16) & 0xf, 16);
u32 fwdvb = __fix_zero((plld >> 8) & 7, 8);
u32 lfbdv = __fix_zero(plld & 0x3f, 64);
u32 pradv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMAD) >> 24) & 7, 8);
u32 prbdv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMBD) >> 24) & 7, 8);
u32 opbdv0 = __fix_zero((CPR_READ(DCRN_CPR_OPBD) >> 24) & 3, 4);
u32 perdv0 = __fix_zero((CPR_READ(DCRN_CPR_PERD) >> 24) & 3, 4);
/* Input clocks for primary dividers */
u32 clk_a, clk_b;
if (pllc & 0x40000000){
u32 m;
/* Feedback path */
switch ((pllc >> 24) & 7){
case 0:
/* PLLOUTx */
m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv;
break;
case 1:
/* CPU */
m = fwdva * pradv0;
break;
case 5:
/* PERClk */
m = fwdvb * prbdv0 * opbdv0 * perdv0;
break;
default:
printk(KERN_EMERG "invalid PLL feedback source\n");
goto bypass;
}
m *= fbdv;
p->vco = sys_clk * m;
clk_a = p->vco / fwdva;
clk_b = p->vco / fwdvb;
}
else {
bypass:
/* Bypass system PLL */
p->vco = 0;
clk_a = clk_b = sys_clk;
}
p->cpu = clk_a / pradv0;
p->plb = clk_b / prbdv0;
p->opb = p->plb / opbdv0;
p->ebc = p->opb / perdv0;
/* UARTs clock */
if (uart0 & 0x00800000)
p->uart0 = ser_clk;
else
p->uart0 = p->plb / __fix_zero(uart0 & 0xff, 256);
if (uart1 & 0x00800000)
p->uart1 = ser_clk;
else
p->uart1 = p->plb / __fix_zero(uart1 & 0xff, 256);
}
/* Issue L2C diagnostic command */
static inline u32 l2c_diag(u32 addr)
{
mtdcr(DCRN_L2C0_ADDR, addr);
mtdcr(DCRN_L2C0_CMD, L2C_CMD_DIAG);
while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ;
return mfdcr(DCRN_L2C0_DATA);
}
static irqreturn_t l2c_error_handler(int irq, void* dev, struct pt_regs* regs)
{
u32 sr = mfdcr(DCRN_L2C0_SR);
if (sr & L2C_SR_CPE){
/* Read cache trapped address */
u32 addr = l2c_diag(0x42000000);
printk(KERN_EMERG "L2C: Cache Parity Error, addr[16:26] = 0x%08x\n", addr);
}
if (sr & L2C_SR_TPE){
/* Read tag trapped address */
u32 addr = l2c_diag(0x82000000) >> 16;
printk(KERN_EMERG "L2C: Tag Parity Error, addr[16:26] = 0x%08x\n", addr);
}
/* Clear parity errors */
if (sr & (L2C_SR_CPE | L2C_SR_TPE)){
mtdcr(DCRN_L2C0_ADDR, 0);
mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
} else
printk(KERN_EMERG "L2C: LRU error\n");
return IRQ_HANDLED;
}
/* Enable L2 cache */
void __init ibm440gx_l2c_enable(void){
u32 r;
unsigned long flags;
/* Install error handler */
if (request_irq(87, l2c_error_handler, SA_INTERRUPT, "L2C", 0) < 0){
printk(KERN_ERR "Cannot install L2C error handler, cache is not enabled\n");
return;
}
local_irq_save(flags);
asm volatile ("sync" ::: "memory");
/* Disable SRAM */
mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) & ~SRAM_DPC_ENABLE);
mtdcr(DCRN_SRAM0_SB0CR, mfdcr(DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK);
mtdcr(DCRN_SRAM0_SB1CR, mfdcr(DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK);
mtdcr(DCRN_SRAM0_SB2CR, mfdcr(DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK);
mtdcr(DCRN_SRAM0_SB3CR, mfdcr(DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK);
/* Enable L2_MODE without ICU/DCU */
r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK);
r |= L2C_CFG_L2M | L2C_CFG_SS_256;
mtdcr(DCRN_L2C0_CFG, r);
mtdcr(DCRN_L2C0_ADDR, 0);
/* Hardware Clear Command */
mtdcr(DCRN_L2C0_CMD, L2C_CMD_HCC);
while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ;
/* Clear Cache Parity and Tag Errors */
mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
/* Enable 64G snoop region starting at 0 */
r = mfdcr(DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
r |= L2C_SNP_SSR_32G | L2C_SNP_ESR;
mtdcr(DCRN_L2C0_SNP0, r);
r = mfdcr(DCRN_L2C0_SNP1) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR;
mtdcr(DCRN_L2C0_SNP1, r);
asm volatile ("sync" ::: "memory");
/* Enable ICU/DCU ports */
r = mfdcr(DCRN_L2C0_CFG);
r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI
| L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM);
r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN
| L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM;
mtdcr(DCRN_L2C0_CFG, r);
asm volatile ("sync; isync" ::: "memory");
local_irq_restore(flags);
}
/* Disable L2 cache */
void __init ibm440gx_l2c_disable(void){
u32 r;
unsigned long flags;
local_irq_save(flags);
asm volatile ("sync" ::: "memory");
/* Disable L2C mode */
r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_L2M | L2C_CFG_ICU | L2C_CFG_DCU);
mtdcr(DCRN_L2C0_CFG, r);
/* Enable SRAM */
mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) | SRAM_DPC_ENABLE);
mtdcr(DCRN_SRAM0_SB0CR,
SRAM_SBCR_BAS0 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
mtdcr(DCRN_SRAM0_SB1CR,
SRAM_SBCR_BAS1 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
mtdcr(DCRN_SRAM0_SB2CR,
SRAM_SBCR_BAS2 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
mtdcr(DCRN_SRAM0_SB3CR,
SRAM_SBCR_BAS3 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
asm volatile ("sync; isync" ::: "memory");
local_irq_restore(flags);
}
void __init ibm440gx_l2c_setup(struct ibm44x_clocks* p)
{
/* Disable L2C on rev.A, rev.B and 800MHz version of rev.C,
enable it on all other revisions
*/
u32 pvr = mfspr(SPRN_PVR);
if (pvr == PVR_440GX_RA || pvr == PVR_440GX_RB ||
(pvr == PVR_440GX_RC && p->cpu > 667000000))
ibm440gx_l2c_disable();
else
ibm440gx_l2c_enable();
}
int __init ibm440gx_get_eth_grp(void)
{
return (SDR_READ(DCRN_SDR_PFC1) & DCRN_SDR_PFC1_EPS) >> DCRN_SDR_PFC1_EPS_SHIFT;
}
void __init ibm440gx_set_eth_grp(int group)
{
SDR_WRITE(DCRN_SDR_PFC1, (SDR_READ(DCRN_SDR_PFC1) & ~DCRN_SDR_PFC1_EPS) | (group << DCRN_SDR_PFC1_EPS_SHIFT));
}
void __init ibm440gx_tah_enable(void)
{
/* Enable TAH0 and TAH1 */
SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) &
~DCRN_SDR_MFR_TAH0);
SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) &
~DCRN_SDR_MFR_TAH1);
}
int ibm440gx_show_cpuinfo(struct seq_file *m){
u32 l2c_cfg = mfdcr(DCRN_L2C0_CFG);
const char* s;
if (l2c_cfg & L2C_CFG_L2M){
switch (l2c_cfg & (L2C_CFG_ICU | L2C_CFG_DCU)){
case L2C_CFG_ICU: s = "I-Cache only"; break;
case L2C_CFG_DCU: s = "D-Cache only"; break;
default: s = "I-Cache/D-Cache"; break;
}
}
else
s = "disabled";
seq_printf(m, "L2-Cache\t: %s (0x%08x 0x%08x)\n", s,
l2c_cfg, mfdcr(DCRN_L2C0_SR));
return 0;
}

View File

@@ -0,0 +1,57 @@
/*
* arch/ppc/kernel/ibm440gx_common.h
*
* PPC440GX system library
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003, 2004 Zultys Technologies
*
* 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.
*
*/
#ifdef __KERNEL__
#ifndef __PPC_SYSLIB_IBM440GX_COMMON_H
#define __PPC_SYSLIB_IBM440GX_COMMON_H
#ifndef __ASSEMBLY__
#include <linux/config.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <syslib/ibm44x_common.h>
/*
* Please, refer to the Figure 14.1 in 440GX user manual
*
* if internal UART clock is used, ser_clk is ignored
*/
void ibm440gx_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk,
unsigned int ser_clk) __init;
/* Enable L2 cache */
void ibm440gx_l2c_enable(void) __init;
/* Disable L2 cache */
void ibm440gx_l2c_disable(void) __init;
/* Enable/disable L2 cache for a particular chip revision */
void ibm440gx_l2c_setup(struct ibm44x_clocks*) __init;
/* Get Ethernet Group */
int ibm440gx_get_eth_grp(void) __init;
/* Set Ethernet Group */
void ibm440gx_set_eth_grp(int group) __init;
/* Enable TAH devices */
void ibm440gx_tah_enable(void) __init;
/* Add L2C info to /proc/cpuinfo */
int ibm440gx_show_cpuinfo(struct seq_file*);
#endif /* __ASSEMBLY__ */
#endif /* __PPC_SYSLIB_IBM440GX_COMMON_H */
#endif /* __KERNEL__ */

View File

@@ -0,0 +1,71 @@
/*
* arch/ppc/syslib/ibm440sp_common.c
*
* PPC440SP system library
*
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2002-2005 MontaVista Software Inc.
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003, 2004 Zultys Technologies
*
* 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/types.h>
#include <linux/serial.h>
#include <asm/param.h>
#include <asm/ibm44x.h>
#include <asm/mmu.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/ppc4xx_pic.h>
/*
* Read the 440SP memory controller to get size of system memory.
*/
unsigned long __init ibm440sp_find_end_of_memory(void)
{
u32 i;
u32 mem_size = 0;
/* Read two bank sizes and sum */
for (i=0; i<2; i++)
switch (mfdcr(DCRN_MQ0_BS0BAS + i) & MQ0_CONFIG_SIZE_MASK) {
case MQ0_CONFIG_SIZE_8M:
mem_size += PPC44x_MEM_SIZE_8M;
break;
case MQ0_CONFIG_SIZE_16M:
mem_size += PPC44x_MEM_SIZE_16M;
break;
case MQ0_CONFIG_SIZE_32M:
mem_size += PPC44x_MEM_SIZE_32M;
break;
case MQ0_CONFIG_SIZE_64M:
mem_size += PPC44x_MEM_SIZE_64M;
break;
case MQ0_CONFIG_SIZE_128M:
mem_size += PPC44x_MEM_SIZE_128M;
break;
case MQ0_CONFIG_SIZE_256M:
mem_size += PPC44x_MEM_SIZE_256M;
break;
case MQ0_CONFIG_SIZE_512M:
mem_size += PPC44x_MEM_SIZE_512M;
break;
case MQ0_CONFIG_SIZE_1G:
mem_size += PPC44x_MEM_SIZE_1G;
break;
case MQ0_CONFIG_SIZE_2G:
mem_size += PPC44x_MEM_SIZE_2G;
break;
default:
break;
}
return mem_size;
}

View File

@@ -0,0 +1,25 @@
/*
* arch/ppc/syslib/ibm440sp_common.h
*
* PPC440SP system library
*
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2004-2005 MontaVista Software, Inc.
*
* 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.
*
*/
#ifdef __KERNEL__
#ifndef __PPC_SYSLIB_IBM440SP_COMMON_H
#define __PPC_SYSLIB_IBM440SP_COMMON_H
#ifndef __ASSEMBLY__
extern unsigned long __init ibm440sp_find_end_of_memory(void);
#endif /* __ASSEMBLY__ */
#endif /* __PPC_SYSLIB_IBM440SP_COMMON_H */
#endif /* __KERNEL__ */

View File

@@ -0,0 +1,193 @@
/*
* arch/ppc/syslib/ibm44x_common.c
*
* PPC44x system library
*
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2002-2005 MontaVista Software Inc.
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003, 2004 Zultys Technologies
*
* 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/time.h>
#include <linux/types.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <asm/ibm44x.h>
#include <asm/mmu.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/ppc4xx_pic.h>
#include <asm/param.h>
#include <syslib/gen550.h>
phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size)
{
phys_addr_t page_4gb = 0;
/*
* Trap the least significant 32-bit portions of an
* address in the 440's 36-bit address space. Fix
* them up with the appropriate ERPN
*/
if ((addr >= PPC44x_IO_LO) && (addr <= PPC44x_IO_HI))
page_4gb = PPC44x_IO_PAGE;
else if ((addr >= PPC44x_PCI0CFG_LO) && (addr <= PPC44x_PCI0CFG_HI))
page_4gb = PPC44x_PCICFG_PAGE;
#ifdef CONFIG_440SP
else if ((addr >= PPC44x_PCI1CFG_LO) && (addr <= PPC44x_PCI1CFG_HI))
page_4gb = PPC44x_PCICFG_PAGE;
else if ((addr >= PPC44x_PCI2CFG_LO) && (addr <= PPC44x_PCI2CFG_HI))
page_4gb = PPC44x_PCICFG_PAGE;
#endif
else if ((addr >= PPC44x_PCIMEM_LO) && (addr <= PPC44x_PCIMEM_HI))
page_4gb = PPC44x_PCIMEM_PAGE;
return (page_4gb | addr);
};
EXPORT_SYMBOL(fixup_bigphys_addr);
void __init ibm44x_calibrate_decr(unsigned int freq)
{
tb_ticks_per_jiffy = freq / HZ;
tb_to_us = mulhwu_scale_factor(freq, 1000000);
/* Set the time base to zero */
mtspr(SPRN_TBWL, 0);
mtspr(SPRN_TBWU, 0);
/* Clear any pending timer interrupts */
mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
/* Enable decrementer interrupt */
mtspr(SPRN_TCR, TCR_DIE);
}
extern void abort(void);
static void ibm44x_restart(char *cmd)
{
local_irq_disable();
abort();
}
static void ibm44x_power_off(void)
{
local_irq_disable();
for(;;);
}
static void ibm44x_halt(void)
{
local_irq_disable();
for(;;);
}
/*
* Read the 44x memory controller to get size of system memory.
*/
static unsigned long __init ibm44x_find_end_of_memory(void)
{
u32 i, bank_config;
u32 mem_size = 0;
for (i=0; i<4; i++)
{
switch (i)
{
case 0:
mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B0CR);
break;
case 1:
mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B1CR);
break;
case 2:
mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B2CR);
break;
case 3:
mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B3CR);
break;
}
bank_config = mfdcr(DCRN_SDRAM0_CFGDATA);
if (!(bank_config & SDRAM_CONFIG_BANK_ENABLE))
continue;
switch (SDRAM_CONFIG_BANK_SIZE(bank_config))
{
case SDRAM_CONFIG_SIZE_8M:
mem_size += PPC44x_MEM_SIZE_8M;
break;
case SDRAM_CONFIG_SIZE_16M:
mem_size += PPC44x_MEM_SIZE_16M;
break;
case SDRAM_CONFIG_SIZE_32M:
mem_size += PPC44x_MEM_SIZE_32M;
break;
case SDRAM_CONFIG_SIZE_64M:
mem_size += PPC44x_MEM_SIZE_64M;
break;
case SDRAM_CONFIG_SIZE_128M:
mem_size += PPC44x_MEM_SIZE_128M;
break;
case SDRAM_CONFIG_SIZE_256M:
mem_size += PPC44x_MEM_SIZE_256M;
break;
case SDRAM_CONFIG_SIZE_512M:
mem_size += PPC44x_MEM_SIZE_512M;
break;
}
}
return mem_size;
}
void __init ibm44x_platform_init(void)
{
ppc_md.init_IRQ = ppc4xx_pic_init;
ppc_md.find_end_of_memory = ibm44x_find_end_of_memory;
ppc_md.restart = ibm44x_restart;
ppc_md.power_off = ibm44x_power_off;
ppc_md.halt = ibm44x_halt;
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
#ifdef CONFIG_KGDB
ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
#endif
/*
* The Abatron BDI JTAG debugger does not tolerate others
* mucking with the debug registers.
*/
#if !defined(CONFIG_BDI_SWITCH)
/* Enable internal debug mode */
mtspr(SPRN_DBCR0, (DBCR0_IDM));
/* Clear any residual debug events */
mtspr(SPRN_DBSR, 0xffffffff);
#endif
}
/* Called from MachineCheckException */
void platform_machine_check(struct pt_regs *regs)
{
printk("PLB0: BEAR=0x%08x%08x ACR= 0x%08x BESR= 0x%08x\n",
mfdcr(DCRN_PLB0_BEARH), mfdcr(DCRN_PLB0_BEARL),
mfdcr(DCRN_PLB0_ACR), mfdcr(DCRN_PLB0_BESR));
printk("POB0: BEAR=0x%08x%08x BESR0=0x%08x BESR1=0x%08x\n",
mfdcr(DCRN_POB0_BEARH), mfdcr(DCRN_POB0_BEARL),
mfdcr(DCRN_POB0_BESR0), mfdcr(DCRN_POB0_BESR1));
printk("OPB0: BEAR=0x%08x%08x BSTAT=0x%08x\n",
mfdcr(DCRN_OPB0_BEARH), mfdcr(DCRN_OPB0_BEARL),
mfdcr(DCRN_OPB0_BSTAT));
}

View File

@@ -0,0 +1,42 @@
/*
* arch/ppc/kernel/ibm44x_common.h
*
* PPC44x system library
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2003, 2004 Zultys Technologies
*
* 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.
*
*/
#ifdef __KERNEL__
#ifndef __PPC_SYSLIB_IBM44x_COMMON_H
#define __PPC_SYSLIB_IBM44x_COMMON_H
#ifndef __ASSEMBLY__
/*
* All clocks are in Hz
*/
struct ibm44x_clocks {
unsigned int vco; /* VCO, 0 if system PLL is bypassed */
unsigned int cpu; /* CPUCoreClk */
unsigned int plb; /* PLBClk */
unsigned int opb; /* OPBClk */
unsigned int ebc; /* PerClk */
unsigned int uart0;
unsigned int uart1;
};
/* common 44x platform init */
void ibm44x_platform_init(void) __init;
/* initialize decrementer and tick-related variables */
void ibm44x_calibrate_decr(unsigned int freq) __init;
#endif /* __ASSEMBLY__ */
#endif /* __PPC_SYSLIB_IBM44x_COMMON_H */
#endif /* __KERNEL__ */

View File

@@ -0,0 +1,9 @@
#include <linux/module.h>
#include <asm/ocp.h>
struct ocp_sys_info_data ocp_sys_info = {
.opb_bus_freq = 50000000, /* OPB Bus Frequency (Hz) */
.ebc_bus_freq = 33333333, /* EBC Bus Frequency (Hz) */
};
EXPORT_SYMBOL(ocp_sys_info);

View File

@@ -0,0 +1,135 @@
/*
* Support for indirect PCI bridges.
*
* Copyright (C) 1998 Gabriel Paubert.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
#ifdef CONFIG_PPC_INDIRECT_PCI_BE
#define PCI_CFG_OUT out_be32
#else
#define PCI_CFG_OUT out_le32
#endif
static int
indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 *val)
{
struct pci_controller *hose = bus->sysdata;
volatile void __iomem *cfg_data;
u8 cfg_type = 0;
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(bus->number, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
if (hose->set_cfg_type)
if (bus->number != hose->first_busno)
cfg_type = 1;
PCI_CFG_OUT(hose->cfg_addr,
(0x80000000 | ((bus->number - hose->bus_offset) << 16)
| (devfn << 8) | ((offset & 0xfc) | cfg_type)));
/*
* Note: the caller has already checked that offset is
* suitably aligned and that len is 1, 2 or 4.
*/
cfg_data = hose->cfg_data + (offset & 3);
switch (len) {
case 1:
*val = in_8(cfg_data);
break;
case 2:
*val = in_le16(cfg_data);
break;
default:
*val = in_le32(cfg_data);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int
indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
int len, u32 val)
{
struct pci_controller *hose = bus->sysdata;
volatile void __iomem *cfg_data;
u8 cfg_type = 0;
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(bus->number, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
if (hose->set_cfg_type)
if (bus->number != hose->first_busno)
cfg_type = 1;
PCI_CFG_OUT(hose->cfg_addr,
(0x80000000 | ((bus->number - hose->bus_offset) << 16)
| (devfn << 8) | ((offset & 0xfc) | cfg_type)));
/*
* Note: the caller has already checked that offset is
* suitably aligned and that len is 1, 2 or 4.
*/
cfg_data = hose->cfg_data + (offset & 3);
switch (len) {
case 1:
out_8(cfg_data, val);
break;
case 2:
out_le16(cfg_data, val);
break;
default:
out_le32(cfg_data, val);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops indirect_pci_ops =
{
indirect_read_config,
indirect_write_config
};
void __init
setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr,
void __iomem * cfg_data)
{
hose->cfg_addr = cfg_addr;
hose->cfg_data = cfg_data;
hose->ops = &indirect_pci_ops;
}
void __init
setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
{
unsigned long base = cfg_addr & PAGE_MASK;
void __iomem *mbase, *addr, *data;
mbase = ioremap(base, PAGE_SIZE);
addr = mbase + (cfg_addr & ~PAGE_MASK);
if ((cfg_data & PAGE_MASK) != base)
mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
data = mbase + (cfg_data & ~PAGE_MASK);
setup_indirect_pci_nomap(hose, addr, data);
}

646
arch/ppc/syslib/ipic.c Normal file
View File

@@ -0,0 +1,646 @@
/*
* include/asm-ppc/ipic.c
*
* IPIC routines implementations.
*
* Copyright 2005 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/sysdev.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/ipic.h>
#include <asm/mpc83xx.h>
#include "ipic.h"
static struct ipic p_ipic;
static struct ipic * primary_ipic;
static struct ipic_info ipic_info[] = {
[9] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_D,
.force = IPIC_SIFCR_H,
.bit = 24,
.prio_mask = 0,
},
[10] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_D,
.force = IPIC_SIFCR_H,
.bit = 25,
.prio_mask = 1,
},
[11] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_D,
.force = IPIC_SIFCR_H,
.bit = 26,
.prio_mask = 2,
},
[14] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_D,
.force = IPIC_SIFCR_H,
.bit = 29,
.prio_mask = 5,
},
[15] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_D,
.force = IPIC_SIFCR_H,
.bit = 30,
.prio_mask = 6,
},
[16] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_D,
.force = IPIC_SIFCR_H,
.bit = 31,
.prio_mask = 7,
},
[17] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_A,
.force = IPIC_SEFCR,
.bit = 1,
.prio_mask = 5,
},
[18] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_A,
.force = IPIC_SEFCR,
.bit = 2,
.prio_mask = 6,
},
[19] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_A,
.force = IPIC_SEFCR,
.bit = 3,
.prio_mask = 7,
},
[20] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_B,
.force = IPIC_SEFCR,
.bit = 4,
.prio_mask = 4,
},
[21] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_B,
.force = IPIC_SEFCR,
.bit = 5,
.prio_mask = 5,
},
[22] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_B,
.force = IPIC_SEFCR,
.bit = 6,
.prio_mask = 6,
},
[23] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_B,
.force = IPIC_SEFCR,
.bit = 7,
.prio_mask = 7,
},
[32] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 0,
.prio_mask = 0,
},
[33] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 1,
.prio_mask = 1,
},
[34] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 2,
.prio_mask = 2,
},
[35] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 3,
.prio_mask = 3,
},
[36] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 4,
.prio_mask = 4,
},
[37] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 5,
.prio_mask = 5,
},
[38] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 6,
.prio_mask = 6,
},
[39] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_H,
.prio = IPIC_SIPRR_A,
.force = IPIC_SIFCR_H,
.bit = 7,
.prio_mask = 7,
},
[48] = {
.pend = IPIC_SEPNR,
.mask = IPIC_SEMSR,
.prio = IPIC_SMPRR_A,
.force = IPIC_SEFCR,
.bit = 0,
.prio_mask = 4,
},
[64] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_A,
.force = IPIC_SIFCR_L,
.bit = 0,
.prio_mask = 0,
},
[65] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_A,
.force = IPIC_SIFCR_L,
.bit = 1,
.prio_mask = 1,
},
[66] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_A,
.force = IPIC_SIFCR_L,
.bit = 2,
.prio_mask = 2,
},
[67] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_A,
.force = IPIC_SIFCR_L,
.bit = 3,
.prio_mask = 3,
},
[68] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_B,
.force = IPIC_SIFCR_L,
.bit = 4,
.prio_mask = 0,
},
[69] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_B,
.force = IPIC_SIFCR_L,
.bit = 5,
.prio_mask = 1,
},
[70] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_B,
.force = IPIC_SIFCR_L,
.bit = 6,
.prio_mask = 2,
},
[71] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = IPIC_SMPRR_B,
.force = IPIC_SIFCR_L,
.bit = 7,
.prio_mask = 3,
},
[72] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 8,
},
[73] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 9,
},
[74] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 10,
},
[75] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 11,
},
[76] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 12,
},
[77] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 13,
},
[78] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 14,
},
[79] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 15,
},
[80] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 16,
},
[84] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 20,
},
[85] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 21,
},
[90] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 26,
},
[91] = {
.pend = IPIC_SIPNR_H,
.mask = IPIC_SIMSR_L,
.prio = 0,
.force = IPIC_SIFCR_L,
.bit = 27,
},
};
static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg)
{
return in_be32(base + (reg >> 2));
}
static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value)
{
out_be32(base + (reg >> 2), value);
}
static inline struct ipic * ipic_from_irq(unsigned int irq)
{
return primary_ipic;
}
static void ipic_enable_irq(unsigned int irq)
{
struct ipic *ipic = ipic_from_irq(irq);
unsigned int src = irq - ipic->irq_offset;
u32 temp;
temp = ipic_read(ipic->regs, ipic_info[src].mask);
temp |= (1 << (31 - ipic_info[src].bit));
ipic_write(ipic->regs, ipic_info[src].mask, temp);
}
static void ipic_disable_irq(unsigned int irq)
{
struct ipic *ipic = ipic_from_irq(irq);
unsigned int src = irq - ipic->irq_offset;
u32 temp;
temp = ipic_read(ipic->regs, ipic_info[src].mask);
temp &= ~(1 << (31 - ipic_info[src].bit));
ipic_write(ipic->regs, ipic_info[src].mask, temp);
}
static void ipic_disable_irq_and_ack(unsigned int irq)
{
struct ipic *ipic = ipic_from_irq(irq);
unsigned int src = irq - ipic->irq_offset;
u32 temp;
ipic_disable_irq(irq);
temp = ipic_read(ipic->regs, ipic_info[src].pend);
temp |= (1 << (31 - ipic_info[src].bit));
ipic_write(ipic->regs, ipic_info[src].pend, temp);
}
static void ipic_end_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
ipic_enable_irq(irq);
}
struct hw_interrupt_type ipic = {
.typename = " IPIC ",
.enable = ipic_enable_irq,
.disable = ipic_disable_irq,
.ack = ipic_disable_irq_and_ack,
.end = ipic_end_irq,
};
void __init ipic_init(phys_addr_t phys_addr,
unsigned int flags,
unsigned int irq_offset,
unsigned char *senses,
unsigned int senses_count)
{
u32 i, temp = 0;
primary_ipic = &p_ipic;
primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE);
primary_ipic->irq_offset = irq_offset;
ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0);
/* default priority scheme is grouped. If spread mode is required
* configure SICFR accordingly */
if (flags & IPIC_SPREADMODE_GRP_A)
temp |= SICFR_IPSA;
if (flags & IPIC_SPREADMODE_GRP_D)
temp |= SICFR_IPSD;
if (flags & IPIC_SPREADMODE_MIX_A)
temp |= SICFR_MPSA;
if (flags & IPIC_SPREADMODE_MIX_B)
temp |= SICFR_MPSB;
ipic_write(primary_ipic->regs, IPIC_SICNR, temp);
/* handle MCP route */
temp = 0;
if (flags & IPIC_DISABLE_MCP_OUT)
temp = SERCR_MCPR;
ipic_write(primary_ipic->regs, IPIC_SERCR, temp);
/* handle routing of IRQ0 to MCP */
temp = ipic_read(primary_ipic->regs, IPIC_SEMSR);
if (flags & IPIC_IRQ0_MCP)
temp |= SEMSR_SIRQ0;
else
temp &= ~SEMSR_SIRQ0;
ipic_write(primary_ipic->regs, IPIC_SEMSR, temp);
for (i = 0 ; i < NR_IPIC_INTS ; i++) {
irq_desc[i+irq_offset].handler = &ipic;
irq_desc[i+irq_offset].status = IRQ_LEVEL;
}
temp = 0;
for (i = 0 ; i < senses_count ; i++) {
if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) {
temp |= 1 << (16 - i);
if (i != 0)
irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0;
else
irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0;
}
}
ipic_write(primary_ipic->regs, IPIC_SECNR, temp);
printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS,
senses_count, primary_ipic->regs);
}
int ipic_set_priority(unsigned int irq, unsigned int priority)
{
struct ipic *ipic = ipic_from_irq(irq);
unsigned int src = irq - ipic->irq_offset;
u32 temp;
if (priority > 7)
return -EINVAL;
if (src > 127)
return -EINVAL;
if (ipic_info[src].prio == 0)
return -EINVAL;
temp = ipic_read(ipic->regs, ipic_info[src].prio);
if (priority < 4) {
temp &= ~(0x7 << (20 + (3 - priority) * 3));
temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3);
} else {
temp &= ~(0x7 << (4 + (7 - priority) * 3));
temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3);
}
ipic_write(ipic->regs, ipic_info[src].prio, temp);
return 0;
}
void ipic_set_highest_priority(unsigned int irq)
{
struct ipic *ipic = ipic_from_irq(irq);
unsigned int src = irq - ipic->irq_offset;
u32 temp;
temp = ipic_read(ipic->regs, IPIC_SICFR);
/* clear and set HPI */
temp &= 0x7f000000;
temp |= (src & 0x7f) << 24;
ipic_write(ipic->regs, IPIC_SICFR, temp);
}
void ipic_set_default_priority(void)
{
ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0);
ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1);
ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2);
ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3);
ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4);
ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5);
ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6);
ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7);
ipic_set_priority(MPC83xx_IRQ_UART1, 0);
ipic_set_priority(MPC83xx_IRQ_UART2, 1);
ipic_set_priority(MPC83xx_IRQ_SEC2, 2);
ipic_set_priority(MPC83xx_IRQ_IIC1, 5);
ipic_set_priority(MPC83xx_IRQ_IIC2, 6);
ipic_set_priority(MPC83xx_IRQ_SPI, 7);
ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0);
ipic_set_priority(MPC83xx_IRQ_PIT, 1);
ipic_set_priority(MPC83xx_IRQ_PCI1, 2);
ipic_set_priority(MPC83xx_IRQ_PCI2, 3);
ipic_set_priority(MPC83xx_IRQ_EXT0, 4);
ipic_set_priority(MPC83xx_IRQ_EXT1, 5);
ipic_set_priority(MPC83xx_IRQ_EXT2, 6);
ipic_set_priority(MPC83xx_IRQ_EXT3, 7);
ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0);
ipic_set_priority(MPC83xx_IRQ_MU, 1);
ipic_set_priority(MPC83xx_IRQ_SBA, 2);
ipic_set_priority(MPC83xx_IRQ_DMA, 3);
ipic_set_priority(MPC83xx_IRQ_EXT4, 4);
ipic_set_priority(MPC83xx_IRQ_EXT5, 5);
ipic_set_priority(MPC83xx_IRQ_EXT6, 6);
ipic_set_priority(MPC83xx_IRQ_EXT7, 7);
}
void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq)
{
struct ipic *ipic = primary_ipic;
u32 temp;
temp = ipic_read(ipic->regs, IPIC_SERMR);
temp |= (1 << (31 - mcp_irq));
ipic_write(ipic->regs, IPIC_SERMR, temp);
}
void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq)
{
struct ipic *ipic = primary_ipic;
u32 temp;
temp = ipic_read(ipic->regs, IPIC_SERMR);
temp &= (1 << (31 - mcp_irq));
ipic_write(ipic->regs, IPIC_SERMR, temp);
}
u32 ipic_get_mcp_status(void)
{
return ipic_read(primary_ipic->regs, IPIC_SERMR);
}
void ipic_clear_mcp_status(u32 mask)
{
ipic_write(primary_ipic->regs, IPIC_SERMR, mask);
}
/* Return an interrupt vector or -1 if no interrupt is pending. */
int ipic_get_irq(struct pt_regs *regs)
{
int irq;
irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f;
if (irq == 0) /* 0 --> no irq is pending */
irq = -1;
return irq;
}
static struct sysdev_class ipic_sysclass = {
set_kset_name("ipic"),
};
static struct sys_device device_ipic = {
.id = 0,
.cls = &ipic_sysclass,
};
static int __init init_ipic_sysfs(void)
{
int rc;
if (!primary_ipic->regs)
return -ENODEV;
printk(KERN_DEBUG "Registering ipic with sysfs...\n");
rc = sysdev_class_register(&ipic_sysclass);
if (rc) {
printk(KERN_ERR "Failed registering ipic sys class\n");
return -ENODEV;
}
rc = sysdev_register(&device_ipic);
if (rc) {
printk(KERN_ERR "Failed registering ipic sys device\n");
return -ENODEV;
}
return 0;
}
subsys_initcall(init_ipic_sysfs);

49
arch/ppc/syslib/ipic.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* arch/ppc/kernel/ipic.h
*
* IPIC private definitions and structure.
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor, Inc
*
* 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.
*/
#ifndef __IPIC_H__
#define __IPIC_H__
#include <asm/ipic.h>
#define MPC83xx_IPIC_SIZE (0x00100)
/* System Global Interrupt Configuration Register */
#define SICFR_IPSA 0x00010000
#define SICFR_IPSD 0x00080000
#define SICFR_MPSA 0x00200000
#define SICFR_MPSB 0x00400000
/* System External Interrupt Mask Register */
#define SEMSR_SIRQ0 0x00008000
/* System Error Control Register */
#define SERCR_MCPR 0x00000001
struct ipic {
volatile u32 __iomem *regs;
unsigned int irq_offset;
};
struct ipic_info {
u8 pend; /* pending register offset from base */
u8 mask; /* mask register offset from base */
u8 prio; /* priority register offset from base */
u8 force; /* force register offset from base */
u8 bit; /* register bit position (as per doc)
bit mask = 1 << (31 - bit) */
u8 prio_mask; /* priority mask value */
};
#endif /* __IPIC_H__ */

194
arch/ppc/syslib/m8260_pci.c Normal file
View File

@@ -0,0 +1,194 @@
/*
* (C) Copyright 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* (C) Copyright 2004 Red Hat, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/machdep.h>
#include <asm/pci-bridge.h>
#include <asm/immap_cpm2.h>
#include <asm/mpc8260.h>
#include "m8260_pci.h"
/* PCI bus configuration registers.
*/
static void __init m8260_setup_pci(struct pci_controller *hose)
{
volatile cpm2_map_t *immap = cpm2_immr;
unsigned long pocmr;
u16 tempShort;
#ifndef CONFIG_ATC /* already done in U-Boot */
/*
* Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
* and local bus for PCI (SIUMCR [LBPC]).
*/
immap->im_siu_conf.siu_82xx.sc_siumcr = 0x00640000;
#endif
/* Make PCI lowest priority */
/* Each 4 bits is a device bus request and the MS 4bits
is highest priority */
/* Bus 4bit value
--- ----------
CPM high 0b0000
CPM middle 0b0001
CPM low 0b0010
PCI reguest 0b0011
Reserved 0b0100
Reserved 0b0101
Internal Core 0b0110
External Master 1 0b0111
External Master 2 0b1000
External Master 3 0b1001
The rest are reserved */
immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893;
/* Park bus on core while modifying PCI Bus accesses */
immap->im_siu_conf.siu_82xx.sc_ppc_acr = 0x6;
/*
* Set up master window that allows the CPU to access PCI space. This
* window is set up using the first SIU PCIBR registers.
*/
immap->im_memctl.memc_pcimsk0 = MPC826x_PCI_MASK;
immap->im_memctl.memc_pcibr0 = MPC826x_PCI_BASE | PCIBR_ENABLE;
/* Disable machine check on no response or target abort */
immap->im_pci.pci_emr = cpu_to_le32(0x1fe7);
/* Release PCI RST (by default the PCI RST signal is held low) */
immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN);
/* give it some time */
mdelay(1);
/*
* Set up master window that allows the CPU to access PCI Memory (prefetch)
* space. This window is set up using the first set of Outbound ATU registers.
*/
immap->im_pci.pci_potar0 = cpu_to_le32(MPC826x_PCI_LOWER_MEM >> 12);
immap->im_pci.pci_pobar0 = cpu_to_le32((MPC826x_PCI_LOWER_MEM - MPC826x_PCI_MEM_OFFSET) >> 12);
pocmr = ((MPC826x_PCI_UPPER_MEM - MPC826x_PCI_LOWER_MEM) >> 12) ^ 0xfffff;
immap->im_pci.pci_pocmr0 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PREFETCH_EN);
/*
* Set up master window that allows the CPU to access PCI Memory (non-prefetch)
* space. This window is set up using the second set of Outbound ATU registers.
*/
immap->im_pci.pci_potar1 = cpu_to_le32(MPC826x_PCI_LOWER_MMIO >> 12);
immap->im_pci.pci_pobar1 = cpu_to_le32((MPC826x_PCI_LOWER_MMIO - MPC826x_PCI_MMIO_OFFSET) >> 12);
pocmr = ((MPC826x_PCI_UPPER_MMIO - MPC826x_PCI_LOWER_MMIO) >> 12) ^ 0xfffff;
immap->im_pci.pci_pocmr1 = cpu_to_le32(pocmr | POCMR_ENABLE);
/*
* Set up master window that allows the CPU to access PCI IO space. This window
* is set up using the third set of Outbound ATU registers.
*/
immap->im_pci.pci_potar2 = cpu_to_le32(MPC826x_PCI_IO_BASE >> 12);
immap->im_pci.pci_pobar2 = cpu_to_le32(MPC826x_PCI_LOWER_IO >> 12);
pocmr = ((MPC826x_PCI_UPPER_IO - MPC826x_PCI_LOWER_IO) >> 12) ^ 0xfffff;
immap->im_pci.pci_pocmr2 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PCI_IO);
/*
* Set up slave window that allows PCI masters to access MPC826x local memory.
* This window is set up using the first set of Inbound ATU registers
*/
immap->im_pci.pci_pitar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_LOCAL >> 12);
immap->im_pci.pci_pibar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_BUS >> 12);
pocmr = ((MPC826x_PCI_SLAVE_MEM_SIZE-1) >> 12) ^ 0xfffff;
immap->im_pci.pci_picmr0 = cpu_to_le32(pocmr | PICMR_ENABLE | PICMR_PREFETCH_EN);
/* See above for description - puts PCI request as highest priority */
immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567;
/* Park the bus on the PCI */
immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI;
/* Host mode - specify the bridge as a host-PCI bridge */
early_write_config_word(hose, 0, 0, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_HOST);
/* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */
early_read_config_word(hose, 0, 0, PCI_COMMAND, &tempShort);
early_write_config_word(hose, 0, 0, PCI_COMMAND,
tempShort | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
}
void __init m8260_find_bridges(void)
{
extern int pci_assign_all_busses;
struct pci_controller * hose;
pci_assign_all_busses = 1;
hose = pcibios_alloc_controller();
if (!hose)
return;
ppc_md.pci_swizzle = common_swizzle;
hose->first_busno = 0;
hose->bus_offset = 0;
hose->last_busno = 0xff;
setup_m8260_indirect_pci(hose,
(unsigned long)&cpm2_immr->im_pci.pci_cfg_addr,
(unsigned long)&cpm2_immr->im_pci.pci_cfg_data);
m8260_setup_pci(hose);
hose->pci_mem_offset = MPC826x_PCI_MEM_OFFSET;
isa_io_base =
(unsigned long) ioremap(MPC826x_PCI_IO_BASE,
MPC826x_PCI_IO_SIZE);
hose->io_base_virt = (void *) isa_io_base;
/* setup resources */
pci_init_resource(&hose->mem_resources[0],
MPC826x_PCI_LOWER_MEM,
MPC826x_PCI_UPPER_MEM,
IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory");
pci_init_resource(&hose->mem_resources[1],
MPC826x_PCI_LOWER_MMIO,
MPC826x_PCI_UPPER_MMIO,
IORESOURCE_MEM, "PCI memory");
pci_init_resource(&hose->io_resource,
MPC826x_PCI_LOWER_IO,
MPC826x_PCI_UPPER_IO,
IORESOURCE_IO, "PCI I/O");
}

View File

@@ -0,0 +1,76 @@
#ifndef _PPC_KERNEL_M8260_PCI_H
#define _PPC_KERNEL_M8260_PCI_H
#include <asm/m8260_pci.h>
/*
* Local->PCI map (from CPU) controlled by
* MPC826x master window
*
* 0x80000000 - 0xBFFFFFFF Total CPU2PCI space PCIBR0
*
* 0x80000000 - 0x9FFFFFFF PCI Mem with prefetch (Outbound ATU #1)
* 0xA0000000 - 0xAFFFFFFF PCI Mem w/o prefetch (Outbound ATU #2)
* 0xB0000000 - 0xB0FFFFFF 32-bit PCI IO (Outbound ATU #3)
*
* PCI->Local map (from PCI)
* MPC826x slave window controlled by
*
* 0x00000000 - 0x07FFFFFF MPC826x local memory (Inbound ATU #1)
*/
/*
* Slave window that allows PCI masters to access MPC826x local memory.
* This window is set up using the first set of Inbound ATU registers
*/
#ifndef MPC826x_PCI_SLAVE_MEM_LOCAL
#define MPC826x_PCI_SLAVE_MEM_LOCAL (((struct bd_info *)__res)->bi_memstart)
#define MPC826x_PCI_SLAVE_MEM_BUS (((struct bd_info *)__res)->bi_memstart)
#define MPC826x_PCI_SLAVE_MEM_SIZE (((struct bd_info *)__res)->bi_memsize)
#endif
/*
* This is the window that allows the CPU to access PCI address space.
* It will be setup with the SIU PCIBR0 register. All three PCI master
* windows, which allow the CPU to access PCI prefetch, non prefetch,
* and IO space (see below), must all fit within this window.
*/
#ifndef MPC826x_PCI_BASE
#define MPC826x_PCI_BASE 0x80000000
#define MPC826x_PCI_MASK 0xc0000000
#endif
#ifndef MPC826x_PCI_LOWER_MEM
#define MPC826x_PCI_LOWER_MEM 0x80000000
#define MPC826x_PCI_UPPER_MEM 0x9fffffff
#define MPC826x_PCI_MEM_OFFSET 0x00000000
#endif
#ifndef MPC826x_PCI_LOWER_MMIO
#define MPC826x_PCI_LOWER_MMIO 0xa0000000
#define MPC826x_PCI_UPPER_MMIO 0xafffffff
#define MPC826x_PCI_MMIO_OFFSET 0x00000000
#endif
#ifndef MPC826x_PCI_LOWER_IO
#define MPC826x_PCI_LOWER_IO 0x00000000
#define MPC826x_PCI_UPPER_IO 0x00ffffff
#define MPC826x_PCI_IO_BASE 0xb0000000
#define MPC826x_PCI_IO_SIZE 0x01000000
#endif
#ifndef _IO_BASE
#define _IO_BASE isa_io_base
#endif
#ifdef CONFIG_8260_PCI9
struct pci_controller;
extern void setup_m8260_indirect_pci(struct pci_controller* hose,
u32 cfg_addr, u32 cfg_data);
#else
#define setup_m8260_indirect_pci setup_indirect_pci
#endif
#endif /* _PPC_KERNEL_M8260_PCI_H */

View File

@@ -0,0 +1,473 @@
/*
* arch/ppc/platforms/mpc8260_pci9.c
*
* Workaround for device erratum PCI 9.
* See Motorola's "XPC826xA Family Device Errata Reference."
* The erratum applies to all 8260 family Hip4 processors. It is scheduled
* to be fixed in HiP4 Rev C. Erratum PCI 9 states that a simultaneous PCI
* inbound write transaction and PCI outbound read transaction can result in a
* bus deadlock. The suggested workaround is to use the IDMA controller to
* perform all reads from PCI configuration, memory, and I/O space.
*
* Author: andy_lowe@mvista.com
*
* 2003 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/string.h>
#include <asm/io.h>
#include <asm/pci-bridge.h>
#include <asm/machdep.h>
#include <asm/byteorder.h>
#include <asm/mpc8260.h>
#include <asm/immap_cpm2.h>
#include <asm/cpm2.h>
#include "m8260_pci.h"
#ifdef CONFIG_8260_PCI9
/*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */
#define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */
/* define a structure for the IDMA dpram usage */
typedef struct idma_dpram_s {
idma_t pram; /* IDMA parameter RAM */
u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */
idma_bd_t bd; /* buffer descriptor */
} idma_dpram_t;
/* define offsets relative to start of IDMA dpram */
#define IDMA_XFER_BUF_OFFSET (sizeof(idma_t))
#define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE)
/* define globals */
static volatile idma_dpram_t *idma_dpram;
/* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined,
* where n is 1, 2, 3, or 4. This selects the IDMA channel used for
* the PCI9 workaround.
*/
#ifdef CONFIG_8260_PCI9_IDMA1
#define IDMA_CHAN 0
#define PROFF_IDMA PROFF_IDMA1_BASE
#define IDMA_PAGE CPM_CR_IDMA1_PAGE
#define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK
#endif
#ifdef CONFIG_8260_PCI9_IDMA2
#define IDMA_CHAN 1
#define PROFF_IDMA PROFF_IDMA2_BASE
#define IDMA_PAGE CPM_CR_IDMA2_PAGE
#define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK
#endif
#ifdef CONFIG_8260_PCI9_IDMA3
#define IDMA_CHAN 2
#define PROFF_IDMA PROFF_IDMA3_BASE
#define IDMA_PAGE CPM_CR_IDMA3_PAGE
#define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK
#endif
#ifdef CONFIG_8260_PCI9_IDMA4
#define IDMA_CHAN 3
#define PROFF_IDMA PROFF_IDMA4_BASE
#define IDMA_PAGE CPM_CR_IDMA4_PAGE
#define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK
#endif
void idma_pci9_init(void)
{
uint dpram_offset;
volatile idma_t *pram;
volatile im_idma_t *idma_reg;
volatile cpm2_map_t *immap = cpm2_immr;
/* allocate IDMA dpram */
dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64);
idma_dpram = cpm_dpram_addr(dpram_offset);
/* initialize the IDMA parameter RAM */
memset((void *)idma_dpram, 0, sizeof(idma_dpram_t));
pram = &idma_dpram->pram;
pram->ibase = dpram_offset + IDMA_BD_OFFSET;
pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET;
pram->ss_max = 32;
pram->dts = 32;
/* initialize the IDMA_BASE pointer to the IDMA parameter RAM */
*((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset;
/* initialize the IDMA registers */
idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1;
idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */
idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */
printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n",
IDMA_CHAN + 1);
return;
}
/* Use the IDMA controller to transfer data from I/O memory to local RAM.
* The src address must be a physical address suitable for use by the DMA
* controller with no translation. The dst address must be a kernel virtual
* address. The dst address is translated to a physical address via
* virt_to_phys().
* The sinc argument specifies whether or not the source address is incremented
* by the DMA controller. The source address is incremented if and only if sinc
* is non-zero. The destination address is always incremented since the
* destination is always host RAM.
*/
static void
idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
{
unsigned long flags;
volatile idma_t *pram = &idma_dpram->pram;
volatile idma_bd_t *bd = &idma_dpram->bd;
volatile cpm2_map_t *immap = cpm2_immr;
local_irq_save(flags);
/* initialize IDMA parameter RAM for this transfer */
if (sinc)
pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
| IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
else
pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC
| IDMA_DCM_SD_MEM2MEM;
pram->ibdptr = pram->ibase;
pram->sts = unit_size;
pram->istate = 0;
/* initialize the buffer descriptor */
bd->dst = virt_to_phys(dst);
bd->src = (uint) src;
bd->len = bytes;
bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL
| IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
/* issue the START_IDMA command to the CP */
while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
CPM_CR_START_IDMA) | CPM_CR_FLG;
while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
/* wait for transfer to complete */
while(bd->flags & IDMA_BD_V);
local_irq_restore(flags);
return;
}
/* Use the IDMA controller to transfer data from I/O memory to local RAM.
* The dst address must be a physical address suitable for use by the DMA
* controller with no translation. The src address must be a kernel virtual
* address. The src address is translated to a physical address via
* virt_to_phys().
* The dinc argument specifies whether or not the dest address is incremented
* by the DMA controller. The source address is incremented if and only if sinc
* is non-zero. The source address is always incremented since the
* source is always host RAM.
*/
static void
idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc)
{
unsigned long flags;
volatile idma_t *pram = &idma_dpram->pram;
volatile idma_bd_t *bd = &idma_dpram->bd;
volatile cpm2_map_t *immap = cpm2_immr;
local_irq_save(flags);
/* initialize IDMA parameter RAM for this transfer */
if (dinc)
pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
| IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
else
pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
| IDMA_DCM_SD_MEM2MEM;
pram->ibdptr = pram->ibase;
pram->sts = unit_size;
pram->istate = 0;
/* initialize the buffer descriptor */
bd->dst = (uint) dst;
bd->src = virt_to_phys(src);
bd->len = bytes;
bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL
| IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
/* issue the START_IDMA command to the CP */
while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
CPM_CR_START_IDMA) | CPM_CR_FLG;
while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
/* wait for transfer to complete */
while(bd->flags & IDMA_BD_V);
local_irq_restore(flags);
return;
}
/* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed
* if the unit_size is 2, and 32-bit little-endian byte swapping is performed if
* the unit_size is 4.
*/
static void
idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
{
int i;
u8 *p;
idma_pci9_read(dst, src, bytes, unit_size, sinc);
switch(unit_size) {
case 2:
for (i = 0, p = dst; i < bytes; i += 2, p += 2)
swab16s((u16 *) p);
break;
case 4:
for (i = 0, p = dst; i < bytes; i += 4, p += 4)
swab32s((u32 *) p);
break;
default:
break;
}
}
EXPORT_SYMBOL(idma_pci9_init);
EXPORT_SYMBOL(idma_pci9_read);
EXPORT_SYMBOL(idma_pci9_read_le);
static inline int is_pci_mem(unsigned long addr)
{
if (addr >= MPC826x_PCI_LOWER_MMIO &&
addr <= MPC826x_PCI_UPPER_MMIO)
return 1;
if (addr >= MPC826x_PCI_LOWER_MEM &&
addr <= MPC826x_PCI_UPPER_MEM)
return 1;
return 0;
}
#define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000))
int readb(volatile unsigned char *addr)
{
u8 val;
unsigned long pa = iopa((unsigned long) addr);
if (!is_pci_mem(pa))
return in_8(addr);
idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
return val;
}
int readw(volatile unsigned short *addr)
{
u16 val;
unsigned long pa = iopa((unsigned long) addr);
if (!is_pci_mem(pa))
return in_le16(addr);
idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
return swab16(val);
}
unsigned readl(volatile unsigned *addr)
{
u32 val;
unsigned long pa = iopa((unsigned long) addr);
if (!is_pci_mem(pa))
return in_le32(addr);
idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
return swab32(val);
}
int inb(unsigned port)
{
u8 val;
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
return val;
}
int inw(unsigned port)
{
u16 val;
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
return swab16(val);
}
unsigned inl(unsigned port)
{
u32 val;
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
return swab32(val);
}
void insb(unsigned port, void *buf, int ns)
{
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0);
}
void insw(unsigned port, void *buf, int ns)
{
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);
}
void insl(unsigned port, void *buf, int nl)
{
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);
}
void insw_ns(unsigned port, void *buf, int ns)
{
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);
}
void insl_ns(unsigned port, void *buf, int nl)
{
u8 *addr = (u8 *)(port + _IO_BASE);
idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);
}
void *memcpy_fromio(void *dest, unsigned long src, size_t count)
{
unsigned long pa = iopa((unsigned long) src);
if (is_pci_mem(pa))
idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1);
else
memcpy(dest, (void *)src, count);
return dest;
}
EXPORT_SYMBOL(readb);
EXPORT_SYMBOL(readw);
EXPORT_SYMBOL(readl);
EXPORT_SYMBOL(inb);
EXPORT_SYMBOL(inw);
EXPORT_SYMBOL(inl);
EXPORT_SYMBOL(insb);
EXPORT_SYMBOL(insw);
EXPORT_SYMBOL(insl);
EXPORT_SYMBOL(insw_ns);
EXPORT_SYMBOL(insl_ns);
EXPORT_SYMBOL(memcpy_fromio);
#endif /* ifdef CONFIG_8260_PCI9 */
/* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c.
* Copyright (C) 1998 Gabriel Paubert.
*/
#ifndef CONFIG_8260_PCI9
#define cfg_read(val, addr, type, op) *val = op((type)(addr))
#else
#define cfg_read(val, addr, type, op) \
idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0)
#endif
#define cfg_write(val, addr, type, op) op((type *)(addr), (val))
static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where,
int size, u32 value)
{
struct pci_controller *hose = pbus->sysdata;
u8 cfg_type = 0;
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(pbus->number, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
if (hose->set_cfg_type)
if (pbus->number != hose->first_busno)
cfg_type = 1;
out_be32(hose->cfg_addr,
(((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
| ((pbus->number - hose->bus_offset) << 8) | 0x80);
switch (size)
{
case 1:
cfg_write(value, hose->cfg_data + (where & 3), u8, out_8);
break;
case 2:
cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16);
break;
case 4:
cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where,
int size, u32 *value)
{
struct pci_controller *hose = pbus->sysdata;
u8 cfg_type = 0;
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(pbus->number, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
if (hose->set_cfg_type)
if (pbus->number != hose->first_busno)
cfg_type = 1;
out_be32(hose->cfg_addr,
(((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
| ((pbus->number - hose->bus_offset) << 8) | 0x80);
switch (size)
{
case 1:
cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8);
break;
case 2:
cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16);
break;
case 4:
cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops indirect_pci_ops =
{
.read = indirect_read_config,
.write = indirect_write_config,
};
void
setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
{
hose->ops = &indirect_pci_ops;
hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4);
hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4);
}

View File

@@ -0,0 +1,264 @@
/*
* arch/ppc/syslib/m8260_setup.c
*
* Copyright (C) 1995 Linus Torvalds
* Adapted from 'alpha' version by Gary Thomas
* Modified by Cort Dougan (cort@cs.nmt.edu)
* Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
* Further modified for generic 8xx and 8260 by Dan.
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/initrd.h>
#include <linux/root_dev.h>
#include <linux/seq_file.h>
#include <linux/irq.h>
#include <asm/mmu.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/mpc8260.h>
#include <asm/immap_cpm2.h>
#include <asm/machdep.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include "cpm2_pic.h"
unsigned char __res[sizeof(bd_t)];
extern void cpm2_reset(void);
extern void m8260_find_bridges(void);
extern void idma_pci9_init(void);
/* Place-holder for board-specific init */
void __attribute__ ((weak)) __init
m82xx_board_setup(void)
{
}
static void __init
m8260_setup_arch(void)
{
/* Print out Vendor and Machine info. */
printk(KERN_INFO "%s %s port\n", CPUINFO_VENDOR, CPUINFO_MACHINE);
/* Reset the Communication Processor Module. */
cpm2_reset();
#ifdef CONFIG_8260_PCI9
/* Initialise IDMA for PCI erratum workaround */
idma_pci9_init();
#endif
#ifdef CONFIG_PCI_8260
m8260_find_bridges();
#endif
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start)
ROOT_DEV = Root_RAM0;
#endif
m82xx_board_setup();
}
/* The decrementer counts at the system (internal) clock frequency
* divided by four.
*/
static void __init
m8260_calibrate_decr(void)
{
bd_t *binfo = (bd_t *)__res;
int freq, divisor;
freq = binfo->bi_busfreq;
divisor = 4;
tb_ticks_per_jiffy = freq / HZ / divisor;
tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
}
/* The 8260 has an internal 1-second timer update register that
* we should use for this purpose.
*/
static uint rtc_time;
static int
m8260_set_rtc_time(unsigned long time)
{
rtc_time = time;
return(0);
}
static unsigned long
m8260_get_rtc_time(void)
{
/* Get time from the RTC.
*/
return((unsigned long)rtc_time);
}
#ifndef BOOTROM_RESTART_ADDR
#warning "Using default BOOTROM_RESTART_ADDR!"
#define BOOTROM_RESTART_ADDR 0xff000104
#endif
static void
m8260_restart(char *cmd)
{
extern void m8260_gorom(bd_t *bi, uint addr);
uint startaddr;
/* Most boot roms have a warmstart as the second instruction
* of the reset vector. If that doesn't work for you, change this
* or the reboot program to send a proper address.
*/
startaddr = BOOTROM_RESTART_ADDR;
if (cmd != NULL) {
if (!strncmp(cmd, "startaddr=", 10))
startaddr = simple_strtoul(&cmd[10], NULL, 0);
}
m8260_gorom((void*)__pa(__res), startaddr);
}
static void
m8260_halt(void)
{
local_irq_disable();
while (1);
}
static void
m8260_power_off(void)
{
m8260_halt();
}
static int
m8260_show_cpuinfo(struct seq_file *m)
{
bd_t *bp = (bd_t *)__res;
seq_printf(m, "vendor\t\t: %s\n"
"machine\t\t: %s\n"
"\n"
"mem size\t\t: 0x%08x\n"
"console baud\t\t: %d\n"
"\n"
"core clock\t: %u MHz\n"
"CPM clock\t: %u MHz\n"
"bus clock\t: %u MHz\n",
CPUINFO_VENDOR, CPUINFO_MACHINE, bp->bi_memsize,
bp->bi_baudrate, bp->bi_intfreq / 1000000,
bp->bi_cpmfreq / 1000000, bp->bi_busfreq / 1000000);
return 0;
}
/* Initialize the internal interrupt controller. The number of
* interrupts supported can vary with the processor type, and the
* 8260 family can have up to 64.
* External interrupts can be either edge or level triggered, and
* need to be initialized by the appropriate driver.
*/
static void __init
m8260_init_IRQ(void)
{
cpm2_init_IRQ();
/* Initialize the default interrupt mapping priorities,
* in case the boot rom changed something on us.
*/
cpm2_immr->im_intctl.ic_siprr = 0x05309770;
}
/*
* Same hack as 8xx
*/
static unsigned long __init
m8260_find_end_of_memory(void)
{
bd_t *binfo = (bd_t *)__res;
return binfo->bi_memsize;
}
/* Map the IMMR, plus anything else we can cover
* in that upper space according to the memory controller
* chip select mapping. Grab another bunch of space
* below that for stuff we can't cover in the upper.
*/
static void __init
m8260_map_io(void)
{
uint addr;
/* Map IMMR region to a 256MB BAT */
addr = (cpm2_immr != NULL) ? (uint)cpm2_immr : CPM_MAP_ADDR;
io_block_mapping(addr, addr, 0x10000000, _PAGE_IO);
/* Map I/O region to a 256MB BAT */
io_block_mapping(IO_VIRT_ADDR, IO_PHYS_ADDR, 0x10000000, _PAGE_IO);
}
/* Place-holder for board-specific ppc_md hooking */
void __attribute__ ((weak)) __init
m82xx_board_init(void)
{
}
/* Inputs:
* r3 - Optional pointer to a board information structure.
* r4 - Optional pointer to the physical starting address of the init RAM
* disk.
* r5 - Optional pointer to the physical ending address of the init RAM
* disk.
* r6 - Optional pointer to the physical starting address of any kernel
* command-line parameters.
* r7 - Optional pointer to the physical ending address of any kernel
* command-line parameters.
*/
void __init
platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
{
parse_bootinfo(find_bootinfo());
if ( r3 )
memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
#ifdef CONFIG_BLK_DEV_INITRD
/* take care of initrd if we have one */
if ( r4 ) {
initrd_start = r4 + KERNELBASE;
initrd_end = r5 + KERNELBASE;
}
#endif /* CONFIG_BLK_DEV_INITRD */
/* take care of cmd line */
if ( r6 ) {
*(char *)(r7+KERNELBASE) = 0;
strcpy(cmd_line, (char *)(r6+KERNELBASE));
}
ppc_md.setup_arch = m8260_setup_arch;
ppc_md.show_cpuinfo = m8260_show_cpuinfo;
ppc_md.init_IRQ = m8260_init_IRQ;
ppc_md.get_irq = cpm2_get_irq;
ppc_md.restart = m8260_restart;
ppc_md.power_off = m8260_power_off;
ppc_md.halt = m8260_halt;
ppc_md.set_rtc_time = m8260_set_rtc_time;
ppc_md.get_rtc_time = m8260_get_rtc_time;
ppc_md.calibrate_decr = m8260_calibrate_decr;
ppc_md.find_end_of_memory = m8260_find_end_of_memory;
ppc_md.setup_io_mappings = m8260_map_io;
/* Call back for board-specific settings and overrides. */
m82xx_board_init();
}

View File

@@ -0,0 +1,433 @@
/*
* arch/ppc/kernel/setup.c
*
* Copyright (C) 1995 Linus Torvalds
* Adapted from 'alpha' version by Gary Thomas
* Modified by Cort Dougan (cort@cs.nmt.edu)
* Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
* Further modified for generic 8xx by Dan.
*/
/*
* bootup setup stuff..
*/
#include <linux/config.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/major.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/initrd.h>
#include <linux/ioport.h>
#include <linux/bootmem.h>
#include <linux/seq_file.h>
#include <linux/root_dev.h>
#include <asm/mmu.h>
#include <asm/reg.h>
#include <asm/residual.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/mpc8xx.h>
#include <asm/8xx_immap.h>
#include <asm/machdep.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include <asm/xmon.h>
#include "ppc8xx_pic.h"
static int m8xx_set_rtc_time(unsigned long time);
static unsigned long m8xx_get_rtc_time(void);
void m8xx_calibrate_decr(void);
unsigned char __res[sizeof(bd_t)];
extern void m8xx_ide_init(void);
extern unsigned long find_available_memory(void);
extern void m8xx_cpm_reset(uint cpm_page);
extern void m8xx_wdt_handler_install(bd_t *bp);
extern void rpxfb_alloc_pages(void);
extern void cpm_interrupt_init(void);
void __attribute__ ((weak))
board_init(void)
{
}
void __init
m8xx_setup_arch(void)
{
int cpm_page;
cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
/* Reset the Communication Processor Module.
*/
m8xx_cpm_reset(cpm_page);
#ifdef CONFIG_FB_RPX
rpxfb_alloc_pages();
#endif
#ifdef notdef
ROOT_DEV = Root_HDA1; /* hda1 */
#endif
#ifdef CONFIG_BLK_DEV_INITRD
#if 0
ROOT_DEV = Root_FD0; /* floppy */
rd_prompt = 1;
rd_doload = 1;
rd_image_start = 0;
#endif
#if 0 /* XXX this may need to be updated for the new bootmem stuff,
or possibly just deleted (see set_phys_avail() in init.c).
- paulus. */
/* initrd_start and size are setup by boot/head.S and kernel/head.S */
if ( initrd_start )
{
if (initrd_end > *memory_end_p)
{
printk("initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)\ndisabling initrd\n",
initrd_end,*memory_end_p);
initrd_start = 0;
}
}
#endif
#endif
board_init();
}
void
abort(void)
{
#ifdef CONFIG_XMON
xmon(0);
#endif
machine_restart(NULL);
/* not reached */
for (;;);
}
/* A place holder for time base interrupts, if they are ever enabled. */
irqreturn_t timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
{
printk ("timebase_interrupt()\n");
return IRQ_HANDLED;
}
static struct irqaction tbint_irqaction = {
.handler = timebase_interrupt,
.mask = CPU_MASK_NONE,
.name = "tbint",
};
/* The decrementer counts at the system (internal) clock frequency divided by
* sixteen, or external oscillator divided by four. We force the processor
* to use system clock divided by sixteen.
*/
void __init m8xx_calibrate_decr(void)
{
bd_t *binfo = (bd_t *)__res;
int freq, fp, divisor;
/* Unlock the SCCR. */
((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = ~KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = KAPWR_KEY;
/* Force all 8xx processors to use divide by 16 processor clock. */
((volatile immap_t *)IMAP_ADDR)->im_clkrst.car_sccr |= 0x02000000;
/* Processor frequency is MHz.
* The value 'fp' is the number of decrementer ticks per second.
*/
fp = binfo->bi_intfreq / 16;
freq = fp*60; /* try to make freq/1e6 an integer */
divisor = 60;
printk("Decrementer Frequency = %d/%d\n", freq, divisor);
tb_ticks_per_jiffy = freq / HZ / divisor;
tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
/* Perform some more timer/timebase initialization. This used
* to be done elsewhere, but other changes caused it to get
* called more than once....that is a bad thing.
*
* First, unlock all of the registers we are going to modify.
* To protect them from corruption during power down, registers
* that are maintained by keep alive power are "locked". To
* modify these registers we have to write the key value to
* the key location associated with the register.
* Some boards power up with these unlocked, while others
* are locked. Writing anything (including the unlock code?)
* to the unlocked registers will lock them again. So, here
* we guarantee the registers are locked, then we unlock them
* for our use.
*/
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = ~KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = ~KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = ~KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = KAPWR_KEY;
/* Disable the RTC one second and alarm interrupts. */
((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc &=
~(RTCSC_SIE | RTCSC_ALE);
/* Enable the RTC */
((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc |=
(RTCSC_RTF | RTCSC_RTE);
/* Enabling the decrementer also enables the timebase interrupts
* (or from the other point of view, to get decrementer interrupts
* we have to enable the timebase). The decrementer interrupt
* is wired into the vector table, nothing to do here for that.
*/
((volatile immap_t *)IMAP_ADDR)->im_sit.sit_tbscr =
((mk_int_int_mask(DEC_INTERRUPT) << 8) |
(TBSCR_TBF | TBSCR_TBE));
if (setup_irq(DEC_INTERRUPT, &tbint_irqaction))
panic("Could not allocate timer IRQ!");
#ifdef CONFIG_8xx_WDT
/* Install watchdog timer handler early because it might be
* already enabled by the bootloader
*/
m8xx_wdt_handler_install(binfo);
#endif
}
/* The RTC on the MPC8xx is an internal register.
* We want to protect this during power down, so we need to unlock,
* modify, and re-lock.
*/
static int
m8xx_set_rtc_time(unsigned long time)
{
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY;
((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtc = time;
((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY;
return(0);
}
static unsigned long
m8xx_get_rtc_time(void)
{
/* Get time from the RTC. */
return((unsigned long)(((immap_t *)IMAP_ADDR)->im_sit.sit_rtc));
}
static void
m8xx_restart(char *cmd)
{
__volatile__ unsigned char dummy;
local_irq_disable();
((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080;
/* Clear the ME bit in MSR to cause checkstop on machine check
*/
mtmsr(mfmsr() & ~0x1000);
dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0];
printk("Restart failed\n");
while(1);
}
static void
m8xx_power_off(void)
{
m8xx_restart(NULL);
}
static void
m8xx_halt(void)
{
m8xx_restart(NULL);
}
static int
m8xx_show_percpuinfo(struct seq_file *m, int i)
{
bd_t *bp;
bp = (bd_t *)__res;
seq_printf(m, "clock\t\t: %ldMHz\n"
"bus clock\t: %ldMHz\n",
bp->bi_intfreq / 1000000,
bp->bi_busfreq / 1000000);
return 0;
}
#ifdef CONFIG_PCI
static struct irqaction mbx_i8259_irqaction = {
.handler = mbx_i8259_action,
.mask = CPU_MASK_NONE,
.name = "i8259 cascade",
};
#endif
/* Initialize the internal interrupt controller. The number of
* interrupts supported can vary with the processor type, and the
* 82xx family can have up to 64.
* External interrupts can be either edge or level triggered, and
* need to be initialized by the appropriate driver.
*/
static void __init
m8xx_init_IRQ(void)
{
int i;
for (i = SIU_IRQ_OFFSET ; i < SIU_IRQ_OFFSET + NR_SIU_INTS ; i++)
irq_desc[i].handler = &ppc8xx_pic;
cpm_interrupt_init();
#if defined(CONFIG_PCI)
for (i = I8259_IRQ_OFFSET ; i < I8259_IRQ_OFFSET + NR_8259_INTS ; i++)
irq_desc[i].handler = &i8259_pic;
i8259_pic_irq_offset = I8259_IRQ_OFFSET;
i8259_init(0);
/* The i8259 cascade interrupt must be level sensitive. */
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel &=
~(0x80000000 >> ISA_BRIDGE_INT);
if (setup_irq(ISA_BRIDGE_INT, &mbx_i8259_irqaction))
enable_irq(ISA_BRIDGE_INT);
#endif /* CONFIG_PCI */
}
/* -------------------------------------------------------------------- */
/*
* This is a big hack right now, but it may turn into something real
* someday.
*
* For the 8xx boards (at this time anyway), there is nothing to initialize
* associated the PROM. Rather than include all of the prom.c
* functions in the image just to get prom_init, all we really need right
* now is the initialization of the physical memory region.
*/
static unsigned long __init
m8xx_find_end_of_memory(void)
{
bd_t *binfo;
extern unsigned char __res[];
binfo = (bd_t *)__res;
return binfo->bi_memsize;
}
/*
* Now map in some of the I/O space that is generically needed
* or shared with multiple devices.
* All of this fits into the same 4Mbyte region, so it only
* requires one page table page. (or at least it used to -- paulus)
*/
static void __init
m8xx_map_io(void)
{
io_block_mapping(IMAP_ADDR, IMAP_ADDR, IMAP_SIZE, _PAGE_IO);
#ifdef CONFIG_MBX
io_block_mapping(NVRAM_ADDR, NVRAM_ADDR, NVRAM_SIZE, _PAGE_IO);
io_block_mapping(MBX_CSR_ADDR, MBX_CSR_ADDR, MBX_CSR_SIZE, _PAGE_IO);
io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO);
/* Map some of the PCI/ISA I/O space to get the IDE interface.
*/
io_block_mapping(PCI_ISA_IO_ADDR, PCI_ISA_IO_ADDR, 0x4000, _PAGE_IO);
io_block_mapping(PCI_IDE_ADDR, PCI_IDE_ADDR, 0x4000, _PAGE_IO);
#endif
#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC)
io_block_mapping(RPX_CSR_ADDR, RPX_CSR_ADDR, RPX_CSR_SIZE, _PAGE_IO);
#if !defined(CONFIG_PCI)
io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO);
#endif
#endif
#if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX)
io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO);
#endif
#ifdef CONFIG_FADS
io_block_mapping(BCSR_ADDR, BCSR_ADDR, BCSR_SIZE, _PAGE_IO);
#endif
#ifdef CONFIG_PCI
io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO);
#endif
#if defined(CONFIG_NETTA)
io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO);
#endif
}
void __init
platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
{
parse_bootinfo(find_bootinfo());
if ( r3 )
memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
#ifdef CONFIG_PCI
m8xx_setup_pci_ptrs();
#endif
#ifdef CONFIG_BLK_DEV_INITRD
/* take care of initrd if we have one */
if ( r4 )
{
initrd_start = r4 + KERNELBASE;
initrd_end = r5 + KERNELBASE;
}
#endif /* CONFIG_BLK_DEV_INITRD */
/* take care of cmd line */
if ( r6 )
{
*(char *)(r7+KERNELBASE) = 0;
strcpy(cmd_line, (char *)(r6+KERNELBASE));
}
ppc_md.setup_arch = m8xx_setup_arch;
ppc_md.show_percpuinfo = m8xx_show_percpuinfo;
ppc_md.irq_canonicalize = NULL;
ppc_md.init_IRQ = m8xx_init_IRQ;
ppc_md.get_irq = m8xx_get_irq;
ppc_md.init = NULL;
ppc_md.restart = m8xx_restart;
ppc_md.power_off = m8xx_power_off;
ppc_md.halt = m8xx_halt;
ppc_md.time_init = NULL;
ppc_md.set_rtc_time = m8xx_set_rtc_time;
ppc_md.get_rtc_time = m8xx_get_rtc_time;
ppc_md.calibrate_decr = m8xx_calibrate_decr;
ppc_md.find_end_of_memory = m8xx_find_end_of_memory;
ppc_md.setup_io_mappings = m8xx_map_io;
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
m8xx_ide_init();
#endif
}

View File

@@ -0,0 +1,99 @@
/*
* m8xx_wdt.c - MPC8xx watchdog driver
*
* Author: Florian Schirmer <jolt@tuxbox.org>
*
* 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/8xx_immap.h>
#include <syslib/m8xx_wdt.h>
static int wdt_timeout;
void m8xx_wdt_reset(void)
{
volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
imap->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */
imap->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */
}
static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
{
volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
m8xx_wdt_reset();
imap->im_sit.sit_piscr |= PISCR_PS; /* clear irq */
return IRQ_HANDLED;
}
void __init m8xx_wdt_handler_install(bd_t * binfo)
{
volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
u32 pitc;
u32 sypcr;
u32 pitrtclk;
sypcr = imap->im_siu_conf.sc_sypcr;
if (!(sypcr & 0x04)) {
printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
sypcr);
return;
}
m8xx_wdt_reset();
printk(KERN_NOTICE
"m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n",
(sypcr >> 16), sypcr & 0x01);
wdt_timeout = (sypcr >> 16) & 0xFFFF;
if (!wdt_timeout)
wdt_timeout = 0xFFFF;
if (sypcr & 0x01)
wdt_timeout *= 2048;
/*
* Fire trigger if half of the wdt ticked down
*/
if (imap->im_sit.sit_rtcsc & RTCSC_38K)
pitrtclk = 9600;
else
pitrtclk = 8192;
if ((wdt_timeout) > (UINT_MAX / pitrtclk))
pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2;
else
pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2;
imap->im_sit.sit_pitc = pitc << 16;
imap->im_sit.sit_piscr =
(mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE;
if (request_irq(PIT_INTERRUPT, m8xx_wdt_interrupt, 0, "watchdog", NULL))
panic("m8xx_wdt: could not allocate watchdog irq!");
printk(KERN_NOTICE
"m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc);
wdt_timeout /= binfo->bi_intfreq;
}
int m8xx_wdt_get_timeout(void)
{
return wdt_timeout;
}

View File

@@ -0,0 +1,16 @@
/*
* Author: Florian Schirmer <jolt@tuxbox.org>
*
* 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#ifndef _PPC_SYSLIB_M8XX_WDT_H
#define _PPC_SYSLIB_M8XX_WDT_H
extern void m8xx_wdt_handler_install(bd_t * binfo);
extern int m8xx_wdt_get_timeout(void);
extern void m8xx_wdt_reset(void);
#endif /* _PPC_SYSLIB_M8XX_WDT_H */

View File

@@ -0,0 +1,510 @@
/*
* arch/ppc/syslib/mpc10x_common.c
*
* Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge,
* Mem ctlr, EPIC, etc.
*
* Author: Mark A. Greer
* mgreer@mvista.com
*
* 2001 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
* *** WARNING - A BAT MUST be set to access the PCI config addr/data regs ***
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/machdep.h>
#include <asm/pci-bridge.h>
#include <asm/open_pic.h>
#include <asm/mpc10x.h>
#include <asm/ocp.h>
/* The OCP structure is fixed by code below, before OCP initialises.
paddr depends on where the board places the EUMB.
- fixed in mpc10x_bridge_init().
irq depends on two things:
> does the board use the EPIC at all? (PCORE does not).
> is the EPIC in serial or parallel mode?
- fixed in mpc10x_set_openpic().
*/
#ifdef CONFIG_MPC10X_OPENPIC
#ifdef CONFIG_EPIC_SERIAL_MODE
#define EPIC_IRQ_BASE (epic_serial_mode ? 16 : 5)
#else
#define EPIC_IRQ_BASE 5
#endif
#define MPC10X_I2C_IRQ (EPIC_IRQ_BASE + NUM_8259_INTERRUPTS)
#define MPC10X_DMA0_IRQ (EPIC_IRQ_BASE + 1 + NUM_8259_INTERRUPTS)
#define MPC10X_DMA1_IRQ (EPIC_IRQ_BASE + 2 + NUM_8259_INTERRUPTS)
#else
#define MPC10X_I2C_IRQ OCP_IRQ_NA
#define MPC10X_DMA0_IRQ OCP_IRQ_NA
#define MPC10X_DMA1_IRQ OCP_IRQ_NA
#endif
struct ocp_def core_ocp[] = {
{ .vendor = OCP_VENDOR_INVALID
}
};
static struct ocp_fs_i2c_data mpc10x_i2c_data = {
.flags = 0
};
static struct ocp_def mpc10x_i2c_ocp = {
.vendor = OCP_VENDOR_MOTOROLA,
.function = OCP_FUNC_IIC,
.index = 0,
.additions = &mpc10x_i2c_data
};
static struct ocp_def mpc10x_dma_ocp[2] = {
{ .vendor = OCP_VENDOR_MOTOROLA,
.function = OCP_FUNC_DMA,
.index = 0 },
{ .vendor = OCP_VENDOR_MOTOROLA,
.function = OCP_FUNC_DMA,
.index = 1 }
};
/* Set resources to match bridge memory map */
void __init
mpc10x_bridge_set_resources(int map, struct pci_controller *hose)
{
switch (map) {
case MPC10X_MEM_MAP_A:
pci_init_resource(&hose->io_resource,
0x00000000,
0x3f7fffff,
IORESOURCE_IO,
"PCI host bridge");
pci_init_resource (&hose->mem_resources[0],
0xc0000000,
0xfeffffff,
IORESOURCE_MEM,
"PCI host bridge");
break;
case MPC10X_MEM_MAP_B:
pci_init_resource(&hose->io_resource,
0x00000000,
0x00bfffff,
IORESOURCE_IO,
"PCI host bridge");
pci_init_resource (&hose->mem_resources[0],
0x80000000,
0xfcffffff,
IORESOURCE_MEM,
"PCI host bridge");
break;
default:
printk("mpc10x_bridge_set_resources: "
"Invalid map specified\n");
if (ppc_md.progress)
ppc_md.progress("mpc10x:exit1", 0x100);
}
}
/*
* Do some initialization and put the EUMB registers at the specified address
* (also map the EPIC registers into virtual space--OpenPIC_Addr will be set).
*
* The EPIC is not on the 106, only the 8240 and 107.
*/
int __init
mpc10x_bridge_init(struct pci_controller *hose,
uint current_map,
uint new_map,
uint phys_eumb_base)
{
int host_bridge, picr1, picr1_bit;
ulong pci_config_addr, pci_config_data;
u_char pir, byte;
if (ppc_md.progress) ppc_md.progress("mpc10x:enter", 0x100);
/* Set up for current map so we can get at config regs */
switch (current_map) {
case MPC10X_MEM_MAP_A:
setup_indirect_pci(hose,
MPC10X_MAPA_CNFG_ADDR,
MPC10X_MAPA_CNFG_DATA);
break;
case MPC10X_MEM_MAP_B:
setup_indirect_pci(hose,
MPC10X_MAPB_CNFG_ADDR,
MPC10X_MAPB_CNFG_DATA);
break;
default:
printk("mpc10x_bridge_init: %s\n",
"Invalid current map specified");
if (ppc_md.progress)
ppc_md.progress("mpc10x:exit1", 0x100);
return -1;
}
/* Make sure it's a supported bridge */
early_read_config_dword(hose,
0,
PCI_DEVFN(0,0),
PCI_VENDOR_ID,
&host_bridge);
switch (host_bridge) {
case MPC10X_BRIDGE_106:
case MPC10X_BRIDGE_8240:
case MPC10X_BRIDGE_107:
case MPC10X_BRIDGE_8245:
break;
default:
if (ppc_md.progress)
ppc_md.progress("mpc10x:exit2", 0x100);
return -1;
}
switch (new_map) {
case MPC10X_MEM_MAP_A:
MPC10X_SETUP_HOSE(hose, A);
pci_config_addr = MPC10X_MAPA_CNFG_ADDR;
pci_config_data = MPC10X_MAPA_CNFG_DATA;
picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_A;
break;
case MPC10X_MEM_MAP_B:
MPC10X_SETUP_HOSE(hose, B);
pci_config_addr = MPC10X_MAPB_CNFG_ADDR;
pci_config_data = MPC10X_MAPB_CNFG_DATA;
picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_B;
break;
default:
printk("mpc10x_bridge_init: %s\n",
"Invalid new map specified");
if (ppc_md.progress)
ppc_md.progress("mpc10x:exit3", 0x100);
return -1;
}
/* Make bridge use the 'new_map', if not already usng it */
if (current_map != new_map) {
early_read_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG,
&picr1);
picr1 = (picr1 & ~MPC10X_CFG_PICR1_ADDR_MAP_MASK) |
picr1_bit;
early_write_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG,
picr1);
asm volatile("sync");
/* Undo old mappings & map in new cfg data/addr regs */
iounmap((void *)hose->cfg_addr);
iounmap((void *)hose->cfg_data);
setup_indirect_pci(hose,
pci_config_addr,
pci_config_data);
}
/* Setup resources to match map */
mpc10x_bridge_set_resources(new_map, hose);
/*
* Want processor accesses of 0xFDxxxxxx to be mapped
* to PCI memory space at 0x00000000. Do not want
* host bridge to respond to PCI memory accesses of
* 0xFDxxxxxx. Do not want host bridge to respond
* to PCI memory addresses 0xFD000000-0xFDFFFFFF;
* want processor accesses from 0x000A0000-0x000BFFFF
* to be forwarded to system memory.
*
* Only valid if not in agent mode and using MAP B.
*/
if (new_map == MPC10X_MEM_MAP_B) {
early_read_config_byte(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_MAPB_OPTIONS_REG,
&byte);
byte &= ~(MPC10X_CFG_MAPB_OPTIONS_PFAE |
MPC10X_CFG_MAPB_OPTIONS_PCICH |
MPC10X_CFG_MAPB_OPTIONS_PROCCH);
if (host_bridge != MPC10X_BRIDGE_106) {
byte |= MPC10X_CFG_MAPB_OPTIONS_CFAE;
}
early_write_config_byte(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_MAPB_OPTIONS_REG,
byte);
}
if (host_bridge != MPC10X_BRIDGE_106) {
early_read_config_byte(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PIR_REG,
&pir);
if (pir != MPC10X_CFG_PIR_HOST_BRIDGE) {
printk("Host bridge in Agent mode\n");
/* Read or Set LMBAR & PCSRBAR? */
}
/* Set base addr of the 8240/107 EUMB. */
early_write_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_EUMBBAR,
phys_eumb_base);
#ifdef CONFIG_MPC10X_OPENPIC
/* Map EPIC register part of EUMB into vitual memory - PCORE
uses an i8259 instead of EPIC. */
OpenPIC_Addr =
ioremap(phys_eumb_base + MPC10X_EUMB_EPIC_OFFSET,
MPC10X_EUMB_EPIC_SIZE);
#endif
mpc10x_i2c_ocp.paddr = phys_eumb_base + MPC10X_EUMB_I2C_OFFSET;
mpc10x_i2c_ocp.irq = MPC10X_I2C_IRQ;
ocp_add_one_device(&mpc10x_i2c_ocp);
mpc10x_dma_ocp[0].paddr = phys_eumb_base +
MPC10X_EUMB_DMA_OFFSET + 0x100;
mpc10x_dma_ocp[0].irq = MPC10X_DMA0_IRQ;
ocp_add_one_device(&mpc10x_dma_ocp[0]);
mpc10x_dma_ocp[1].paddr = phys_eumb_base +
MPC10X_EUMB_DMA_OFFSET + 0x200;
mpc10x_dma_ocp[1].irq = MPC10X_DMA1_IRQ;
ocp_add_one_device(&mpc10x_dma_ocp[1]);
}
#ifdef CONFIG_MPC10X_STORE_GATHERING
mpc10x_enable_store_gathering(hose);
#else
mpc10x_disable_store_gathering(hose);
#endif
/*
* 8240 erratum 26, 8241/8245 erratum 29, 107 erratum 23: speculative
* PCI reads may return stale data so turn off.
*/
if ((host_bridge == MPC10X_BRIDGE_8240)
|| (host_bridge == MPC10X_BRIDGE_8245)
|| (host_bridge == MPC10X_BRIDGE_107)) {
early_read_config_dword(hose, 0, PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG, &picr1);
picr1 &= ~MPC10X_CFG_PICR1_SPEC_PCI_RD;
early_write_config_dword(hose, 0, PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG, picr1);
}
/*
* 8241/8245 erratum 28: PCI reads from local memory may return
* stale data. Workaround by setting PICR2[0] to disable copyback
* optimization. Oddly, the latest available user manual for the
* 8245 (Rev 2., dated 10/2003) says PICR2[0] is reserverd.
*/
if (host_bridge == MPC10X_BRIDGE_8245) {
ulong picr2;
early_read_config_dword(hose, 0, PCI_DEVFN(0,0),
MPC10X_CFG_PICR2_REG, &picr2);
picr2 |= MPC10X_CFG_PICR2_COPYBACK_OPT;
early_write_config_dword(hose, 0, PCI_DEVFN(0,0),
MPC10X_CFG_PICR2_REG, picr2);
}
if (ppc_md.progress) ppc_md.progress("mpc10x:exit", 0x100);
return 0;
}
/*
* Need to make our own PCI config space access macros because
* mpc10x_get_mem_size() is called before the data structures are set up for
* the 'early_xxx' and 'indirect_xxx' routines to work.
* Assumes bus 0.
*/
#define MPC10X_CFG_read(val, addr, type, op) *val = op((type)(addr))
#define MPC10X_CFG_write(val, addr, type, op) op((type *)(addr), (val))
#define MPC10X_PCI_OP(rw, size, type, op, mask) \
static void \
mpc10x_##rw##_config_##size(uint *cfg_addr, uint *cfg_data, int devfn, int offset, type val) \
{ \
out_be32(cfg_addr, \
((offset & 0xfc) << 24) | (devfn << 16) \
| (0 << 8) | 0x80); \
MPC10X_CFG_##rw(val, cfg_data + (offset & mask), type, op); \
return; \
}
MPC10X_PCI_OP(read, byte, u8 *, in_8, 3)
MPC10X_PCI_OP(read, dword, u32 *, in_le32, 0)
#if 0 /* Not used */
MPC10X_PCI_OP(write, byte, u8, out_8, 3)
MPC10X_PCI_OP(read, word, u16 *, in_le16, 2)
MPC10X_PCI_OP(write, word, u16, out_le16, 2)
MPC10X_PCI_OP(write, dword, u32, out_le32, 0)
#endif
/*
* Read the memory controller registers to determine the amount of memory in
* the system. This assumes that the firmware has correctly set up the memory
* controller registers.
*/
unsigned long __init
mpc10x_get_mem_size(uint mem_map)
{
uint *config_addr, *config_data, val;
ulong start, end, total, offset;
int i;
u_char bank_enables;
switch (mem_map) {
case MPC10X_MEM_MAP_A:
config_addr = (uint *)MPC10X_MAPA_CNFG_ADDR;
config_data = (uint *)MPC10X_MAPA_CNFG_DATA;
break;
case MPC10X_MEM_MAP_B:
config_addr = (uint *)MPC10X_MAPB_CNFG_ADDR;
config_data = (uint *)MPC10X_MAPB_CNFG_DATA;
break;
default:
return 0;
}
mpc10x_read_config_byte(config_addr,
config_data,
PCI_DEVFN(0,0),
MPC10X_MCTLR_MEM_BANK_ENABLES,
&bank_enables);
total = 0;
for (i=0; i<8; i++) {
if (bank_enables & (1 << i)) {
offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0);
mpc10x_read_config_dword(config_addr,
config_data,
PCI_DEVFN(0,0),
offset,
&val);
start = (val >> ((i & 3) << 3)) & 0xff;
offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0);
mpc10x_read_config_dword(config_addr,
config_data,
PCI_DEVFN(0,0),
offset,
&val);
val = (val >> ((i & 3) << 3)) & 0x03;
start = (val << 28) | (start << 20);
offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0);
mpc10x_read_config_dword(config_addr,
config_data,
PCI_DEVFN(0,0),
offset,
&val);
end = (val >> ((i & 3) << 3)) & 0xff;
offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0);
mpc10x_read_config_dword(config_addr,
config_data,
PCI_DEVFN(0,0),
offset,
&val);
val = (val >> ((i & 3) << 3)) & 0x03;
end = (val << 28) | (end << 20) | 0xfffff;
total += (end - start + 1);
}
}
return total;
}
int __init
mpc10x_enable_store_gathering(struct pci_controller *hose)
{
uint picr1;
early_read_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG,
&picr1);
picr1 |= MPC10X_CFG_PICR1_ST_GATH_EN;
early_write_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG,
picr1);
return 0;
}
int __init
mpc10x_disable_store_gathering(struct pci_controller *hose)
{
uint picr1;
early_read_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG,
&picr1);
picr1 &= ~MPC10X_CFG_PICR1_ST_GATH_EN;
early_write_config_dword(hose,
0,
PCI_DEVFN(0,0),
MPC10X_CFG_PICR1_REG,
picr1);
return 0;
}
#ifdef CONFIG_MPC10X_OPENPIC
void __init mpc10x_set_openpic(void)
{
/* Map external IRQs */
openpic_set_sources(0, EPIC_IRQ_BASE, OpenPIC_Addr + 0x10200);
/* Skip reserved space and map i2c and DMA Ch[01] */
openpic_set_sources(EPIC_IRQ_BASE, 3, OpenPIC_Addr + 0x11020);
/* Skip reserved space and map Message Unit Interrupt (I2O) */
openpic_set_sources(EPIC_IRQ_BASE + 3, 1, OpenPIC_Addr + 0x110C0);
openpic_init(NUM_8259_INTERRUPTS);
}
#endif

View File

@@ -0,0 +1,318 @@
/*
* arch/ppc/syslib/mpc52xx_devices.c
*
* Freescale MPC52xx device descriptions
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/fsl_devices.h>
#include <linux/resource.h>
#include <asm/mpc52xx.h>
#include <asm/ppc_sys.h>
static u64 mpc52xx_dma_mask = 0xffffffffULL;
static struct fsl_i2c_platform_data mpc52xx_fsl_i2c_pdata = {
.device_flags = FSL_I2C_DEV_CLOCK_5200,
};
/* We use relative offsets for IORESOURCE_MEM to be independent from the
* MBAR location at compile time
*/
/* TODO Add the BestComm initiator channel to the device definitions,
possibly using IORESOURCE_DMA. But that's when BestComm is ready ... */
struct platform_device ppc_sys_platform_devices[] = {
[MPC52xx_MSCAN1] = {
.name = "mpc52xx-mscan",
.id = 0,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x0900,
.end = 0x097f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_MSCAN1_IRQ,
.end = MPC52xx_MSCAN1_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_MSCAN2] = {
.name = "mpc52xx-mscan",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x0980,
.end = 0x09ff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_MSCAN2_IRQ,
.end = MPC52xx_MSCAN2_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_SPI] = {
.name = "mpc52xx-spi",
.id = -1,
.num_resources = 3,
.resource = (struct resource[]) {
{
.start = 0x0f00,
.end = 0x0f1f,
.flags = IORESOURCE_MEM,
},
{
.name = "modf",
.start = MPC52xx_SPI_MODF_IRQ,
.end = MPC52xx_SPI_MODF_IRQ,
.flags = IORESOURCE_IRQ,
},
{
.name = "spif",
.start = MPC52xx_SPI_SPIF_IRQ,
.end = MPC52xx_SPI_SPIF_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_USB] = {
.name = "ppc-soc-ohci",
.id = -1,
.num_resources = 2,
.dev.dma_mask = &mpc52xx_dma_mask,
.dev.coherent_dma_mask = 0xffffffffULL,
.resource = (struct resource[]) {
{
.start = 0x1000,
.end = 0x10ff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_USB_IRQ,
.end = MPC52xx_USB_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_BDLC] = {
.name = "mpc52xx-bdlc",
.id = -1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x1300,
.end = 0x130f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_BDLC_IRQ,
.end = MPC52xx_BDLC_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_PSC1] = {
.name = "mpc52xx-psc",
.id = 0,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x2000,
.end = 0x209f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_PSC1_IRQ,
.end = MPC52xx_PSC1_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_PSC2] = {
.name = "mpc52xx-psc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x2200,
.end = 0x229f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_PSC2_IRQ,
.end = MPC52xx_PSC2_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_PSC3] = {
.name = "mpc52xx-psc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x2400,
.end = 0x249f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_PSC3_IRQ,
.end = MPC52xx_PSC3_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_PSC4] = {
.name = "mpc52xx-psc",
.id = 3,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x2600,
.end = 0x269f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_PSC4_IRQ,
.end = MPC52xx_PSC4_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_PSC5] = {
.name = "mpc52xx-psc",
.id = 4,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x2800,
.end = 0x289f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_PSC5_IRQ,
.end = MPC52xx_PSC5_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_PSC6] = {
.name = "mpc52xx-psc",
.id = 5,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x2c00,
.end = 0x2c9f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_PSC6_IRQ,
.end = MPC52xx_PSC6_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_FEC] = {
.name = "mpc52xx-fec",
.id = -1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x3000,
.end = 0x33ff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_FEC_IRQ,
.end = MPC52xx_FEC_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_ATA] = {
.name = "mpc52xx-ata",
.id = -1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x3a00,
.end = 0x3aff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_ATA_IRQ,
.end = MPC52xx_ATA_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_I2C1] = {
.name = "fsl-i2c",
.id = 0,
.dev.platform_data = &mpc52xx_fsl_i2c_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x3d00,
.end = 0x3d1f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_I2C1_IRQ,
.end = MPC52xx_I2C1_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC52xx_I2C2] = {
.name = "fsl-i2c",
.id = 1,
.dev.platform_data = &mpc52xx_fsl_i2c_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x3d40,
.end = 0x3d5f,
.flags = IORESOURCE_MEM,
},
{
.start = MPC52xx_I2C2_IRQ,
.end = MPC52xx_I2C2_IRQ,
.flags = IORESOURCE_IRQ,
},
},
},
};
static int __init mach_mpc52xx_fixup(struct platform_device *pdev)
{
ppc_sys_fixup_mem_resource(pdev, MPC52xx_MBAR);
return 0;
}
static int __init mach_mpc52xx_init(void)
{
ppc_sys_device_fixup = mach_mpc52xx_fixup;
return 0;
}
postcore_initcall(mach_mpc52xx_init);

View File

@@ -0,0 +1,235 @@
/*
* arch/ppc/syslib/mpc52xx_pci.c
*
* PCI code for the Freescale MPC52xx embedded CPU.
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/config.h>
#include <asm/pci.h>
#include <asm/mpc52xx.h>
#include "mpc52xx_pci.h"
#include <asm/delay.h>
static int
mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
struct pci_controller *hose = bus->sysdata;
u32 value;
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(bus->number, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
out_be32(hose->cfg_addr,
(1 << 31) |
((bus->number - hose->bus_offset) << 16) |
(devfn << 8) |
(offset & 0xfc));
value = in_le32(hose->cfg_data);
if (len != 4) {
value >>= ((offset & 0x3) << 3);
value &= 0xffffffff >> (32 - (len << 3));
}
*val = value;
out_be32(hose->cfg_addr, 0);
return PCIBIOS_SUCCESSFUL;
}
static int
mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 val)
{
struct pci_controller *hose = bus->sysdata;
u32 value, mask;
if (ppc_md.pci_exclude_device)
if (ppc_md.pci_exclude_device(bus->number, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
out_be32(hose->cfg_addr,
(1 << 31) |
((bus->number - hose->bus_offset) << 16) |
(devfn << 8) |
(offset & 0xfc));
if (len != 4) {
value = in_le32(hose->cfg_data);
offset = (offset & 0x3) << 3;
mask = (0xffffffff >> (32 - (len << 3)));
mask <<= offset;
value &= ~mask;
val = value | ((val << offset) & mask);
}
out_le32(hose->cfg_data, val);
out_be32(hose->cfg_addr, 0);
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops mpc52xx_pci_ops = {
.read = mpc52xx_pci_read_config,
.write = mpc52xx_pci_write_config
};
static void __init
mpc52xx_pci_setup(struct mpc52xx_pci __iomem *pci_regs)
{
/* Setup control regs */
/* Nothing to do afaik */
/* Setup windows */
out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
MPC52xx_PCI_MEM_START + MPC52xx_PCI_MEM_OFFSET,
MPC52xx_PCI_MEM_START,
MPC52xx_PCI_MEM_SIZE ));
out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
MPC52xx_PCI_MMIO_START + MPC52xx_PCI_MEM_OFFSET,
MPC52xx_PCI_MMIO_START,
MPC52xx_PCI_MMIO_SIZE ));
out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
MPC52xx_PCI_IO_BASE,
MPC52xx_PCI_IO_START,
MPC52xx_PCI_IO_SIZE ));
out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK(
( MPC52xx_PCI_IWCR_ENABLE | /* iw0btar */
MPC52xx_PCI_IWCR_READ_MULTI |
MPC52xx_PCI_IWCR_MEM ),
( MPC52xx_PCI_IWCR_ENABLE | /* iw1btar */
MPC52xx_PCI_IWCR_READ |
MPC52xx_PCI_IWCR_MEM ),
( MPC52xx_PCI_IWCR_ENABLE | /* iw2btar */
MPC52xx_PCI_IWCR_IO )
));
out_be32(&pci_regs->tbatr0,
MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO );
out_be32(&pci_regs->tbatr1,
MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM );
out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD);
/* Reset the exteral bus ( internal PCI controller is NOT resetted ) */
/* Not necessary and can be a bad thing if for example the bootloader
is displaying a splash screen or ... Just left here for
documentation purpose if anyone need it */
#if 0
u32 tmp;
tmp = in_be32(&pci_regs->gscr);
out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR);
udelay(50);
out_be32(&pci_regs->gscr, tmp);
#endif
}
static void __init
mpc52xx_pci_fixup_resources(struct pci_dev *dev)
{
int i;
/* We don't rely on boot loader for PCI and resets all
devices */
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
struct resource *res = &dev->resource[i];
if (res->end > res->start) { /* Only valid resources */
res->end -= res->start;
res->start = 0;
res->flags |= IORESOURCE_UNSET;
}
}
/* The PCI Host bridge of MPC52xx has a prefetch memory resource
fixed to 1Gb. Doesn't fit in the resource system so we remove it */
if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) &&
(dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) {
struct resource *res = &dev->resource[1];
res->start = res->end = res->flags = 0;
}
}
void __init
mpc52xx_find_bridges(void)
{
struct mpc52xx_pci __iomem *pci_regs;
struct pci_controller *hose;
pci_assign_all_busses = 1;
pci_regs = ioremap(MPC52xx_PA(MPC52xx_PCI_OFFSET), MPC52xx_PCI_SIZE);
if (!pci_regs)
return;
hose = pcibios_alloc_controller();
if (!hose) {
iounmap(pci_regs);
return;
}
ppc_md.pci_swizzle = common_swizzle;
ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources;
hose->first_busno = 0;
hose->last_busno = 0xff;
hose->bus_offset = 0;
hose->ops = &mpc52xx_pci_ops;
mpc52xx_pci_setup(pci_regs);
hose->pci_mem_offset = MPC52xx_PCI_MEM_OFFSET;
isa_io_base =
(unsigned long) ioremap(MPC52xx_PCI_IO_BASE,
MPC52xx_PCI_IO_SIZE);
hose->io_base_virt = (void *) isa_io_base;
hose->cfg_addr = &pci_regs->car;
hose->cfg_data = (void __iomem *) isa_io_base;
/* Setup resources */
pci_init_resource(&hose->mem_resources[0],
MPC52xx_PCI_MEM_START,
MPC52xx_PCI_MEM_STOP,
IORESOURCE_MEM|IORESOURCE_PREFETCH,
"PCI prefetchable memory");
pci_init_resource(&hose->mem_resources[1],
MPC52xx_PCI_MMIO_START,
MPC52xx_PCI_MMIO_STOP,
IORESOURCE_MEM,
"PCI memory");
pci_init_resource(&hose->io_resource,
MPC52xx_PCI_IO_START,
MPC52xx_PCI_IO_STOP,
IORESOURCE_IO,
"PCI I/O");
}

View File

@@ -0,0 +1,139 @@
/*
* arch/ppc/syslib/mpc52xx_pci.h
*
* PCI Include file the Freescale MPC52xx embedded cpu chips
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Inspired from code written by Dale Farnsworth <dfarnsworth@mvista.com>
* for the 2.4 kernel.
*
* Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
* Copyright (C) 2003 MontaVista, Software, Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#ifndef __SYSLIB_MPC52xx_PCI_H__
#define __SYSLIB_MPC52xx_PCI_H__
/* ======================================================================== */
/* PCI windows config */
/* ======================================================================== */
/*
* Master windows : MPC52xx -> PCI
*
* 0x80000000 -> 0x9FFFFFFF PCI Mem prefetchable IW0BTAR
* 0xA0000000 -> 0xAFFFFFFF PCI Mem IW1BTAR
* 0xB0000000 -> 0xB0FFFFFF PCI IO IW2BTAR
*
* Slave windows : PCI -> MPC52xx
*
* 0xF0000000 -> 0xF003FFFF MPC52xx MBAR TBATR0
* 0x00000000 -> 0x3FFFFFFF MPC52xx local memory TBATR1
*/
#define MPC52xx_PCI_MEM_OFFSET 0x00000000 /* Offset for MEM MMIO */
#define MPC52xx_PCI_MEM_START 0x80000000
#define MPC52xx_PCI_MEM_SIZE 0x20000000
#define MPC52xx_PCI_MEM_STOP (MPC52xx_PCI_MEM_START+MPC52xx_PCI_MEM_SIZE-1)
#define MPC52xx_PCI_MMIO_START 0xa0000000
#define MPC52xx_PCI_MMIO_SIZE 0x10000000
#define MPC52xx_PCI_MMIO_STOP (MPC52xx_PCI_MMIO_START+MPC52xx_PCI_MMIO_SIZE-1)
#define MPC52xx_PCI_IO_BASE 0xb0000000
#define MPC52xx_PCI_IO_START 0x00000000
#define MPC52xx_PCI_IO_SIZE 0x01000000
#define MPC52xx_PCI_IO_STOP (MPC52xx_PCI_IO_START+MPC52xx_PCI_IO_SIZE-1)
#define MPC52xx_PCI_TARGET_IO MPC52xx_MBAR
#define MPC52xx_PCI_TARGET_MEM 0x00000000
/* ======================================================================== */
/* Structures mapping & Defines for PCI Unit */
/* ======================================================================== */
#define MPC52xx_PCI_GSCR_BM 0x40000000
#define MPC52xx_PCI_GSCR_PE 0x20000000
#define MPC52xx_PCI_GSCR_SE 0x10000000
#define MPC52xx_PCI_GSCR_XLB2PCI_MASK 0x07000000
#define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24
#define MPC52xx_PCI_GSCR_IPG2PCI_MASK 0x00070000
#define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16
#define MPC52xx_PCI_GSCR_BME 0x00004000
#define MPC52xx_PCI_GSCR_PEE 0x00002000
#define MPC52xx_PCI_GSCR_SEE 0x00001000
#define MPC52xx_PCI_GSCR_PR 0x00000001
#define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size) \
( ( (proc_ad) & 0xff000000 ) | \
( (((size) - 1) >> 8) & 0x00ff0000 ) | \
( ((pci_ad) >> 16) & 0x0000ff00 ) )
#define MPC52xx_PCI_IWCR_PACK(win0,win1,win2) (((win0) << 24) | \
((win1) << 16) | \
((win2) << 8))
#define MPC52xx_PCI_IWCR_DISABLE 0x0
#define MPC52xx_PCI_IWCR_ENABLE 0x1
#define MPC52xx_PCI_IWCR_READ 0x0
#define MPC52xx_PCI_IWCR_READ_LINE 0x2
#define MPC52xx_PCI_IWCR_READ_MULTI 0x4
#define MPC52xx_PCI_IWCR_MEM 0x0
#define MPC52xx_PCI_IWCR_IO 0x8
#define MPC52xx_PCI_TCR_P 0x01000000
#define MPC52xx_PCI_TCR_LD 0x00010000
#define MPC52xx_PCI_TBATR_DISABLE 0x0
#define MPC52xx_PCI_TBATR_ENABLE 0x1
#ifndef __ASSEMBLY__
struct mpc52xx_pci {
u32 idr; /* PCI + 0x00 */
u32 scr; /* PCI + 0x04 */
u32 ccrir; /* PCI + 0x08 */
u32 cr1; /* PCI + 0x0C */
u32 bar0; /* PCI + 0x10 */
u32 bar1; /* PCI + 0x14 */
u8 reserved1[16]; /* PCI + 0x18 */
u32 ccpr; /* PCI + 0x28 */
u32 sid; /* PCI + 0x2C */
u32 erbar; /* PCI + 0x30 */
u32 cpr; /* PCI + 0x34 */
u8 reserved2[4]; /* PCI + 0x38 */
u32 cr2; /* PCI + 0x3C */
u8 reserved3[32]; /* PCI + 0x40 */
u32 gscr; /* PCI + 0x60 */
u32 tbatr0; /* PCI + 0x64 */
u32 tbatr1; /* PCI + 0x68 */
u32 tcr; /* PCI + 0x6C */
u32 iw0btar; /* PCI + 0x70 */
u32 iw1btar; /* PCI + 0x74 */
u32 iw2btar; /* PCI + 0x78 */
u8 reserved4[4]; /* PCI + 0x7C */
u32 iwcr; /* PCI + 0x80 */
u32 icr; /* PCI + 0x84 */
u32 isr; /* PCI + 0x88 */
u32 arb; /* PCI + 0x8C */
u8 reserved5[104]; /* PCI + 0x90 */
u32 car; /* PCI + 0xF8 */
u8 reserved6[4]; /* PCI + 0xFC */
};
#endif /* __ASSEMBLY__ */
#endif /* __SYSLIB_MPC52xx_PCI_H__ */

View File

@@ -0,0 +1,257 @@
/*
* arch/ppc/syslib/mpc52xx_pic.c
*
* Programmable Interrupt Controller functions for the Freescale MPC52xx
* embedded CPU.
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Based on (well, mostly copied from) the code from the 2.4 kernel by
* Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
*
* Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
* Copyright (C) 2003 Montavista Software, Inc
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/stddef.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/mpc52xx.h>
static struct mpc52xx_intr __iomem *intr;
static struct mpc52xx_sdma __iomem *sdma;
static void
mpc52xx_ic_disable(unsigned int irq)
{
u32 val;
if (irq == MPC52xx_IRQ0) {
val = in_be32(&intr->ctrl);
val &= ~(1 << 11);
out_be32(&intr->ctrl, val);
}
else if (irq < MPC52xx_IRQ1) {
BUG();
}
else if (irq <= MPC52xx_IRQ3) {
val = in_be32(&intr->ctrl);
val &= ~(1 << (10 - (irq - MPC52xx_IRQ1)));
out_be32(&intr->ctrl, val);
}
else if (irq < MPC52xx_SDMA_IRQ_BASE) {
val = in_be32(&intr->main_mask);
val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE));
out_be32(&intr->main_mask, val);
}
else if (irq < MPC52xx_PERP_IRQ_BASE) {
val = in_be32(&sdma->IntMask);
val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE);
out_be32(&sdma->IntMask, val);
}
else {
val = in_be32(&intr->per_mask);
val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE));
out_be32(&intr->per_mask, val);
}
}
static void
mpc52xx_ic_enable(unsigned int irq)
{
u32 val;
if (irq == MPC52xx_IRQ0) {
val = in_be32(&intr->ctrl);
val |= 1 << 11;
out_be32(&intr->ctrl, val);
}
else if (irq < MPC52xx_IRQ1) {
BUG();
}
else if (irq <= MPC52xx_IRQ3) {
val = in_be32(&intr->ctrl);
val |= 1 << (10 - (irq - MPC52xx_IRQ1));
out_be32(&intr->ctrl, val);
}
else if (irq < MPC52xx_SDMA_IRQ_BASE) {
val = in_be32(&intr->main_mask);
val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)));
out_be32(&intr->main_mask, val);
}
else if (irq < MPC52xx_PERP_IRQ_BASE) {
val = in_be32(&sdma->IntMask);
val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE));
out_be32(&sdma->IntMask, val);
}
else {
val = in_be32(&intr->per_mask);
val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)));
out_be32(&intr->per_mask, val);
}
}
static void
mpc52xx_ic_ack(unsigned int irq)
{
u32 val;
/*
* Only some irqs are reset here, others in interrupting hardware.
*/
switch (irq) {
case MPC52xx_IRQ0:
val = in_be32(&intr->ctrl);
val |= 0x08000000;
out_be32(&intr->ctrl, val);
break;
case MPC52xx_CCS_IRQ:
val = in_be32(&intr->enc_status);
val |= 0x00000400;
out_be32(&intr->enc_status, val);
break;
case MPC52xx_IRQ1:
val = in_be32(&intr->ctrl);
val |= 0x04000000;
out_be32(&intr->ctrl, val);
break;
case MPC52xx_IRQ2:
val = in_be32(&intr->ctrl);
val |= 0x02000000;
out_be32(&intr->ctrl, val);
break;
case MPC52xx_IRQ3:
val = in_be32(&intr->ctrl);
val |= 0x01000000;
out_be32(&intr->ctrl, val);
break;
default:
if (irq >= MPC52xx_SDMA_IRQ_BASE
&& irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) {
out_be32(&sdma->IntPend,
1 << (irq - MPC52xx_SDMA_IRQ_BASE));
}
break;
}
}
static void
mpc52xx_ic_disable_and_ack(unsigned int irq)
{
mpc52xx_ic_disable(irq);
mpc52xx_ic_ack(irq);
}
static void
mpc52xx_ic_end(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
mpc52xx_ic_enable(irq);
}
static struct hw_interrupt_type mpc52xx_ic = {
.typename = " MPC52xx ",
.enable = mpc52xx_ic_enable,
.disable = mpc52xx_ic_disable,
.ack = mpc52xx_ic_disable_and_ack,
.end = mpc52xx_ic_end,
};
void __init
mpc52xx_init_irq(void)
{
int i;
u32 intr_ctrl;
/* Remap the necessary zones */
intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE);
sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE);
if ((intr==NULL) || (sdma==NULL))
panic("Can't ioremap PIC/SDMA register for init_irq !");
/* Disable all interrupt sources. */
out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */
out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */
out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */
out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */
intr_ctrl = in_be32(&intr->ctrl);
intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */
intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */
0x00001000 | /* MEE master external enable */
0x00000000 | /* 0 means disable IRQ 0-3 */
0x00000001; /* CEb route critical normally */
out_be32(&intr->ctrl, intr_ctrl);
/* Zero a bunch of the priority settings. */
out_be32(&intr->per_pri1, 0);
out_be32(&intr->per_pri2, 0);
out_be32(&intr->per_pri3, 0);
out_be32(&intr->main_pri1, 0);
out_be32(&intr->main_pri2, 0);
/* Initialize irq_desc[i].handler's with mpc52xx_ic. */
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].handler = &mpc52xx_ic;
irq_desc[i].status = IRQ_LEVEL;
}
#define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03)
for (i=0 ; i<4 ; i++) {
int mode;
mode = IRQn_MODE(intr_ctrl,i);
if ((mode == 0x1) || (mode == 0x2))
irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0;
}
}
int
mpc52xx_get_irq(struct pt_regs *regs)
{
u32 status;
int irq = -1;
status = in_be32(&intr->enc_status);
if (status & 0x00000400) { /* critical */
irq = (status >> 8) & 0x3;
if (irq == 2) /* high priority peripheral */
goto peripheral;
irq += MPC52xx_CRIT_IRQ_BASE;
}
else if (status & 0x00200000) { /* main */
irq = (status >> 16) & 0x1f;
if (irq == 4) /* low priority peripheral */
goto peripheral;
irq += MPC52xx_MAIN_IRQ_BASE;
}
else if (status & 0x20000000) { /* peripheral */
peripheral:
irq = (status >> 24) & 0x1f;
if (irq == 0) { /* bestcomm */
status = in_be32(&sdma->IntPend);
irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1;
}
else
irq += MPC52xx_PERP_IRQ_BASE;
}
return irq;
}

View File

@@ -0,0 +1,230 @@
/*
* arch/ppc/syslib/mpc52xx_setup.c
*
* Common code for the boards based on Freescale MPC52xx embedded CPU.
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Support for other bootloaders than UBoot by Dale Farnsworth
* <dfarnsworth@mvista.com>
*
* Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
* Copyright (C) 2003 Montavista Software, Inc
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/config.h>
#include <asm/io.h>
#include <asm/time.h>
#include <asm/mpc52xx.h>
#include <asm/mpc52xx_psc.h>
#include <asm/pgtable.h>
#include <asm/ppcboot.h>
extern bd_t __res;
static int core_mult[] = { /* CPU Frequency multiplier, taken */
0, 0, 0, 10, 20, 20, 25, 45, /* from the datasheet used to compute */
30, 55, 40, 50, 0, 60, 35, 0, /* CPU frequency from XLB freq and */
30, 25, 65, 10, 70, 20, 75, 45, /* external jumper config */
0, 55, 40, 50, 80, 60, 35, 0
};
void
mpc52xx_restart(char *cmd)
{
struct mpc52xx_gpt __iomem *gpt0 = MPC52xx_VA(MPC52xx_GPTx_OFFSET(0));
local_irq_disable();
/* Turn on the watchdog and wait for it to expire. It effectively
does a reset */
out_be32(&gpt0->count, 0x000000ff);
out_be32(&gpt0->mode, 0x00009004);
while (1);
}
void
mpc52xx_halt(void)
{
local_irq_disable();
while (1);
}
void
mpc52xx_power_off(void)
{
/* By default we don't have any way of shut down.
If a specific board wants to, it can set the power down
code to any hardware implementation dependent code */
mpc52xx_halt();
}
void __init
mpc52xx_set_bat(void)
{
/* Set BAT 2 to map the 0xf0000000 area */
/* This mapping is used during mpc52xx_progress,
* mpc52xx_find_end_of_memory, and UARTs/GPIO access for debug
*/
mb();
mtspr(SPRN_DBAT2U, 0xf0001ffe);
mtspr(SPRN_DBAT2L, 0xf000002a);
mb();
}
void __init
mpc52xx_map_io(void)
{
/* Here we only map the MBAR */
io_block_mapping(
MPC52xx_MBAR_VIRT, MPC52xx_MBAR, MPC52xx_MBAR_SIZE, _PAGE_IO);
}
#ifdef CONFIG_SERIAL_TEXT_DEBUG
#ifndef MPC52xx_PF_CONSOLE_PORT
#error "mpc52xx PSC for console not selected"
#endif
static void
mpc52xx_psc_putc(struct mpc52xx_psc __iomem *psc, unsigned char c)
{
while (!(in_be16(&psc->mpc52xx_psc_status) &
MPC52xx_PSC_SR_TXRDY));
out_8(&psc->mpc52xx_psc_buffer_8, c);
}
void
mpc52xx_progress(char *s, unsigned short hex)
{
char c;
struct mpc52xx_psc __iomem *psc;
psc = MPC52xx_VA(MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT));
while ((c = *s++) != 0) {
if (c == '\n')
mpc52xx_psc_putc(psc, '\r');
mpc52xx_psc_putc(psc, c);
}
mpc52xx_psc_putc(psc, '\r');
mpc52xx_psc_putc(psc, '\n');
}
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
unsigned long __init
mpc52xx_find_end_of_memory(void)
{
u32 ramsize = __res.bi_memsize;
/*
* if bootloader passed a memsize, just use it
* else get size from sdram config registers
*/
if (ramsize == 0) {
struct mpc52xx_mmap_ctl __iomem *mmap_ctl;
u32 sdram_config_0, sdram_config_1;
/* Temp BAT2 mapping active when this is called ! */
mmap_ctl = MPC52xx_VA(MPC52xx_MMAP_CTL_OFFSET);
sdram_config_0 = in_be32(&mmap_ctl->sdram0);
sdram_config_1 = in_be32(&mmap_ctl->sdram1);
if ((sdram_config_0 & 0x1f) >= 0x13)
ramsize = 1 << ((sdram_config_0 & 0xf) + 17);
if (((sdram_config_1 & 0x1f) >= 0x13) &&
((sdram_config_1 & 0xfff00000) == ramsize))
ramsize += 1 << ((sdram_config_1 & 0xf) + 17);
}
return ramsize;
}
void __init
mpc52xx_calibrate_decr(void)
{
int current_time, previous_time;
int tbl_start, tbl_end;
unsigned int xlbfreq, cpufreq, ipbfreq, pcifreq, divisor;
xlbfreq = __res.bi_busfreq;
/* if bootloader didn't pass bus frequencies, calculate them */
if (xlbfreq == 0) {
/* Get RTC & Clock manager modules */
struct mpc52xx_rtc __iomem *rtc;
struct mpc52xx_cdm __iomem *cdm;
rtc = ioremap(MPC52xx_PA(MPC52xx_RTC_OFFSET), MPC52xx_RTC_SIZE);
cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE);
if ((rtc==NULL) || (cdm==NULL))
panic("Can't ioremap RTC/CDM while computing bus freq");
/* Count bus clock during 1/64 sec */
out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */
previous_time = in_be32(&rtc->time);
while ((current_time = in_be32(&rtc->time)) == previous_time) ;
tbl_start = get_tbl();
previous_time = current_time;
while ((current_time = in_be32(&rtc->time)) == previous_time) ;
tbl_end = get_tbl();
out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */
/* Compute all frequency from that & CDM settings */
xlbfreq = (tbl_end - tbl_start) << 8;
cpufreq = (xlbfreq * core_mult[in_be32(&cdm->rstcfg)&0x1f])/10;
ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ?
xlbfreq / 2 : xlbfreq;
switch (in_8(&cdm->pci_clk_sel) & 3) {
case 0:
pcifreq = ipbfreq;
break;
case 1:
pcifreq = ipbfreq / 2;
break;
default:
pcifreq = xlbfreq / 4;
break;
}
__res.bi_busfreq = xlbfreq;
__res.bi_intfreq = cpufreq;
__res.bi_ipbfreq = ipbfreq;
__res.bi_pcifreq = pcifreq;
/* Release mapping */
iounmap(rtc);
iounmap(cdm);
}
divisor = 4;
tb_ticks_per_jiffy = xlbfreq / HZ / divisor;
tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000);
}
int mpc52xx_match_psc_function(int psc_idx, const char *func)
{
struct mpc52xx_psc_func *cf = mpc52xx_psc_functions;
while ((cf->id != -1) && (cf->func != NULL)) {
if ((cf->id == psc_idx) && !strcmp(cf->func,func))
return 1;
cf++;
}
return 0;
}

View File

@@ -0,0 +1,38 @@
/*
* arch/ppc/syslib/mpc52xx_sys.c
*
* Freescale MPC52xx system descriptions
*
*
* Maintainer : Sylvain Munaut <tnt@246tNt.com>
*
* Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <asm/ppc_sys.h>
struct ppc_sys_spec *cur_ppc_sys_spec;
struct ppc_sys_spec ppc_sys_specs[] = {
{
.ppc_sys_name = "5200",
.mask = 0xffff0000,
.value = 0x80110000,
.num_devices = 15,
.device_list = (enum ppc_sys_devices[])
{
MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI,
MPC52xx_USB, MPC52xx_BDLC, MPC52xx_PSC1, MPC52xx_PSC2,
MPC52xx_PSC3, MPC52xx_PSC4, MPC52xx_PSC5, MPC52xx_PSC6,
MPC52xx_FEC, MPC52xx_ATA, MPC52xx_I2C1, MPC52xx_I2C2,
},
},
{ /* default match */
.ppc_sys_name = "",
.mask = 0x00000000,
.value = 0x00000000,
},
};

View File

@@ -0,0 +1,237 @@
/*
* arch/ppc/platforms/83xx/mpc83xx_devices.c
*
* MPC83xx Device descriptions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/serial_8250.h>
#include <linux/fsl_devices.h>
#include <asm/mpc83xx.h>
#include <asm/irq.h>
#include <asm/ppc_sys.h>
/* We use offsets for IORESOURCE_MEM since we do not know at compile time
* what IMMRBAR is, will get fixed up by mach_mpc83xx_fixup
*/
static struct gianfar_platform_data mpc83xx_tsec1_pdata = {
.device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
FSL_GIANFAR_DEV_HAS_MULTI_INTR,
.phy_reg_addr = 0x24000,
};
static struct gianfar_platform_data mpc83xx_tsec2_pdata = {
.device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
FSL_GIANFAR_DEV_HAS_MULTI_INTR,
.phy_reg_addr = 0x24000,
};
static struct fsl_i2c_platform_data mpc83xx_fsl_i2c1_pdata = {
.device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
};
static struct fsl_i2c_platform_data mpc83xx_fsl_i2c2_pdata = {
.device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
};
static struct plat_serial8250_port serial_platform_data[] = {
[0] = {
.mapbase = 0x4500,
.irq = MPC83xx_IRQ_UART1,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
},
[1] = {
.mapbase = 0x4600,
.irq = MPC83xx_IRQ_UART2,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
},
};
struct platform_device ppc_sys_platform_devices[] = {
[MPC83xx_TSEC1] = {
.name = "fsl-gianfar",
.id = 1,
.dev.platform_data = &mpc83xx_tsec1_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = 0x24000,
.end = 0x24fff,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC83xx_IRQ_TSEC1_TX,
.end = MPC83xx_IRQ_TSEC1_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC83xx_IRQ_TSEC1_RX,
.end = MPC83xx_IRQ_TSEC1_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC83xx_IRQ_TSEC1_ERROR,
.end = MPC83xx_IRQ_TSEC1_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC83xx_TSEC2] = {
.name = "fsl-gianfar",
.id = 2,
.dev.platform_data = &mpc83xx_tsec2_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = 0x25000,
.end = 0x25fff,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC83xx_IRQ_TSEC2_TX,
.end = MPC83xx_IRQ_TSEC2_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC83xx_IRQ_TSEC2_RX,
.end = MPC83xx_IRQ_TSEC2_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC83xx_IRQ_TSEC2_ERROR,
.end = MPC83xx_IRQ_TSEC2_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC83xx_IIC1] = {
.name = "fsl-i2c",
.id = 1,
.dev.platform_data = &mpc83xx_fsl_i2c1_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x3000,
.end = 0x30ff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC83xx_IRQ_IIC1,
.end = MPC83xx_IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC83xx_IIC2] = {
.name = "fsl-i2c",
.id = 2,
.dev.platform_data = &mpc83xx_fsl_i2c2_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x3100,
.end = 0x31ff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC83xx_IRQ_IIC2,
.end = MPC83xx_IRQ_IIC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC83xx_DUART] = {
.name = "serial8250",
.id = 0,
.dev.platform_data = serial_platform_data,
},
[MPC83xx_SEC2] = {
.name = "fsl-sec2",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x30000,
.end = 0x3ffff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC83xx_IRQ_SEC2,
.end = MPC83xx_IRQ_SEC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC83xx_USB2_DR] = {
.name = "fsl-usb2-dr",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x22000,
.end = 0x22fff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC83xx_IRQ_USB2_DR,
.end = MPC83xx_IRQ_USB2_DR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC83xx_USB2_MPH] = {
.name = "fsl-usb2-mph",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x23000,
.end = 0x23fff,
.flags = IORESOURCE_MEM,
},
{
.start = MPC83xx_IRQ_USB2_MPH,
.end = MPC83xx_IRQ_USB2_MPH,
.flags = IORESOURCE_IRQ,
},
},
},
};
static int __init mach_mpc83xx_fixup(struct platform_device *pdev)
{
ppc_sys_fixup_mem_resource(pdev, immrbar);
return 0;
}
static int __init mach_mpc83xx_init(void)
{
if (ppc_md.progress)
ppc_md.progress("mach_mpc83xx_init:enter", 0);
ppc_sys_device_fixup = mach_mpc83xx_fixup;
return 0;
}
postcore_initcall(mach_mpc83xx_init);

View File

@@ -0,0 +1,100 @@
/*
* arch/ppc/platforms/83xx/mpc83xx_sys.c
*
* MPC83xx System descriptions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/ppc_sys.h>
struct ppc_sys_spec *cur_ppc_sys_spec;
struct ppc_sys_spec ppc_sys_specs[] = {
{
.ppc_sys_name = "8349E",
.mask = 0xFFFF0000,
.value = 0x80500000,
.num_devices = 8,
.device_list = (enum ppc_sys_devices[])
{
MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
MPC83xx_USB2_DR, MPC83xx_USB2_MPH
},
},
{
.ppc_sys_name = "8349",
.mask = 0xFFFF0000,
.value = 0x80510000,
.num_devices = 7,
.device_list = (enum ppc_sys_devices[])
{
MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
MPC83xx_IIC2, MPC83xx_DUART,
MPC83xx_USB2_DR, MPC83xx_USB2_MPH
},
},
{
.ppc_sys_name = "8347E",
.mask = 0xFFFF0000,
.value = 0x80520000,
.num_devices = 8,
.device_list = (enum ppc_sys_devices[])
{
MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
MPC83xx_USB2_DR, MPC83xx_USB2_MPH
},
},
{
.ppc_sys_name = "8347",
.mask = 0xFFFF0000,
.value = 0x80530000,
.num_devices = 7,
.device_list = (enum ppc_sys_devices[])
{
MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
MPC83xx_IIC2, MPC83xx_DUART,
MPC83xx_USB2_DR, MPC83xx_USB2_MPH
},
},
{
.ppc_sys_name = "8343E",
.mask = 0xFFFF0000,
.value = 0x80540000,
.num_devices = 7,
.device_list = (enum ppc_sys_devices[])
{
MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
MPC83xx_USB2_DR,
},
},
{
.ppc_sys_name = "8343",
.mask = 0xFFFF0000,
.value = 0x80550000,
.num_devices = 6,
.device_list = (enum ppc_sys_devices[])
{
MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
MPC83xx_IIC2, MPC83xx_DUART,
MPC83xx_USB2_DR,
},
},
{ /* default match */
.ppc_sys_name = "",
.mask = 0x00000000,
.value = 0x00000000,
},
};

View File

@@ -0,0 +1,552 @@
/*
* arch/ppc/platforms/85xx/mpc85xx_devices.c
*
* MPC85xx Device descriptions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/serial_8250.h>
#include <linux/fsl_devices.h>
#include <asm/mpc85xx.h>
#include <asm/irq.h>
#include <asm/ppc_sys.h>
/* We use offsets for IORESOURCE_MEM since we do not know at compile time
* what CCSRBAR is, will get fixed up by mach_mpc85xx_fixup
*/
static struct gianfar_platform_data mpc85xx_tsec1_pdata = {
.device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
FSL_GIANFAR_DEV_HAS_MULTI_INTR,
.phy_reg_addr = MPC85xx_ENET1_OFFSET,
};
static struct gianfar_platform_data mpc85xx_tsec2_pdata = {
.device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
FSL_GIANFAR_DEV_HAS_MULTI_INTR,
.phy_reg_addr = MPC85xx_ENET1_OFFSET,
};
static struct gianfar_platform_data mpc85xx_fec_pdata = {
.phy_reg_addr = MPC85xx_ENET1_OFFSET,
};
static struct fsl_i2c_platform_data mpc85xx_fsl_i2c_pdata = {
.device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
};
static struct plat_serial8250_port serial_platform_data[] = {
[0] = {
.mapbase = 0x4500,
.irq = MPC85xx_IRQ_DUART,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
},
[1] = {
.mapbase = 0x4600,
.irq = MPC85xx_IRQ_DUART,
.iotype = UPIO_MEM,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
},
};
struct platform_device ppc_sys_platform_devices[] = {
[MPC85xx_TSEC1] = {
.name = "fsl-gianfar",
.id = 1,
.dev.platform_data = &mpc85xx_tsec1_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET1_OFFSET,
.end = MPC85xx_ENET1_OFFSET +
MPC85xx_ENET1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC1_TX,
.end = MPC85xx_IRQ_TSEC1_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC1_RX,
.end = MPC85xx_IRQ_TSEC1_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC1_ERROR,
.end = MPC85xx_IRQ_TSEC1_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_TSEC2] = {
.name = "fsl-gianfar",
.id = 2,
.dev.platform_data = &mpc85xx_tsec2_pdata,
.num_resources = 4,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET2_OFFSET,
.end = MPC85xx_ENET2_OFFSET +
MPC85xx_ENET2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "tx",
.start = MPC85xx_IRQ_TSEC2_TX,
.end = MPC85xx_IRQ_TSEC2_TX,
.flags = IORESOURCE_IRQ,
},
{
.name = "rx",
.start = MPC85xx_IRQ_TSEC2_RX,
.end = MPC85xx_IRQ_TSEC2_RX,
.flags = IORESOURCE_IRQ,
},
{
.name = "error",
.start = MPC85xx_IRQ_TSEC2_ERROR,
.end = MPC85xx_IRQ_TSEC2_ERROR,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_FEC] = {
.name = "fsl-gianfar",
.id = 3,
.dev.platform_data = &mpc85xx_fec_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_ENET3_OFFSET,
.end = MPC85xx_ENET3_OFFSET +
MPC85xx_ENET3_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_FEC,
.end = MPC85xx_IRQ_FEC,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_IIC1] = {
.name = "fsl-i2c",
.id = 1,
.dev.platform_data = &mpc85xx_fsl_i2c_pdata,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_IIC1_OFFSET,
.end = MPC85xx_IIC1_OFFSET +
MPC85xx_IIC1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_IIC1,
.end = MPC85xx_IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA0] = {
.name = "fsl-dma",
.id = 0,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA0_OFFSET,
.end = MPC85xx_DMA0_OFFSET +
MPC85xx_DMA0_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA0,
.end = MPC85xx_IRQ_DMA0,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA1] = {
.name = "fsl-dma",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA1_OFFSET,
.end = MPC85xx_DMA1_OFFSET +
MPC85xx_DMA1_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA1,
.end = MPC85xx_IRQ_DMA1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA2] = {
.name = "fsl-dma",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA2_OFFSET,
.end = MPC85xx_DMA2_OFFSET +
MPC85xx_DMA2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA2,
.end = MPC85xx_IRQ_DMA2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DMA3] = {
.name = "fsl-dma",
.id = 3,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_DMA3_OFFSET,
.end = MPC85xx_DMA3_OFFSET +
MPC85xx_DMA3_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_DMA3,
.end = MPC85xx_IRQ_DMA3,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_DUART] = {
.name = "serial8250",
.id = 0,
.dev.platform_data = serial_platform_data,
},
[MPC85xx_PERFMON] = {
.name = "fsl-perfmon",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_PERFMON_OFFSET,
.end = MPC85xx_PERFMON_OFFSET +
MPC85xx_PERFMON_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_PERFMON,
.end = MPC85xx_IRQ_PERFMON,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_SEC2] = {
.name = "fsl-sec2",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = MPC85xx_SEC2_OFFSET,
.end = MPC85xx_SEC2_OFFSET +
MPC85xx_SEC2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MPC85xx_IRQ_SEC2,
.end = MPC85xx_IRQ_SEC2,
.flags = IORESOURCE_IRQ,
},
},
},
#ifdef CONFIG_CPM2
[MPC85xx_CPM_FCC1] = {
.name = "fsl-cpm-fcc",
.id = 1,
.num_resources = 3,
.resource = (struct resource[]) {
{
.start = 0x91300,
.end = 0x9131F,
.flags = IORESOURCE_MEM,
},
{
.start = 0x91380,
.end = 0x9139F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_FCC1,
.end = SIU_INT_FCC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_FCC2] = {
.name = "fsl-cpm-fcc",
.id = 2,
.num_resources = 3,
.resource = (struct resource[]) {
{
.start = 0x91320,
.end = 0x9133F,
.flags = IORESOURCE_MEM,
},
{
.start = 0x913A0,
.end = 0x913CF,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_FCC2,
.end = SIU_INT_FCC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_FCC3] = {
.name = "fsl-cpm-fcc",
.id = 3,
.num_resources = 3,
.resource = (struct resource[]) {
{
.start = 0x91340,
.end = 0x9135F,
.flags = IORESOURCE_MEM,
},
{
.start = 0x913D0,
.end = 0x913FF,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_FCC3,
.end = SIU_INT_FCC3,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_I2C] = {
.name = "fsl-cpm-i2c",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91860,
.end = 0x918BF,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_I2C,
.end = SIU_INT_I2C,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC1] = {
.name = "fsl-cpm-scc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A00,
.end = 0x91A1F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC1,
.end = SIU_INT_SCC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC2] = {
.name = "fsl-cpm-scc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A20,
.end = 0x91A3F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC2,
.end = SIU_INT_SCC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC3] = {
.name = "fsl-cpm-scc",
.id = 3,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A40,
.end = 0x91A5F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC3,
.end = SIU_INT_SCC3,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SCC4] = {
.name = "fsl-cpm-scc",
.id = 4,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A60,
.end = 0x91A7F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SCC4,
.end = SIU_INT_SCC4,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SPI] = {
.name = "fsl-cpm-spi",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91AA0,
.end = 0x91AFF,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SPI,
.end = SIU_INT_SPI,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_MCC1] = {
.name = "fsl-cpm-mcc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91B30,
.end = 0x91B3F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_MCC1,
.end = SIU_INT_MCC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_MCC2] = {
.name = "fsl-cpm-mcc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91B50,
.end = 0x91B5F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_MCC2,
.end = SIU_INT_MCC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SMC1] = {
.name = "fsl-cpm-smc",
.id = 1,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A80,
.end = 0x91A8F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SMC1,
.end = SIU_INT_SMC1,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_SMC2] = {
.name = "fsl-cpm-smc",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91A90,
.end = 0x91A9F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_SMC2,
.end = SIU_INT_SMC2,
.flags = IORESOURCE_IRQ,
},
},
},
[MPC85xx_CPM_USB] = {
.name = "fsl-cpm-usb",
.id = 2,
.num_resources = 2,
.resource = (struct resource[]) {
{
.start = 0x91B60,
.end = 0x91B7F,
.flags = IORESOURCE_MEM,
},
{
.start = SIU_INT_USB,
.end = SIU_INT_USB,
.flags = IORESOURCE_IRQ,
},
},
},
#endif /* CONFIG_CPM2 */
};
static int __init mach_mpc85xx_fixup(struct platform_device *pdev)
{
ppc_sys_fixup_mem_resource(pdev, CCSRBAR);
return 0;
}
static int __init mach_mpc85xx_init(void)
{
ppc_sys_device_fixup = mach_mpc85xx_fixup;
return 0;
}
postcore_initcall(mach_mpc85xx_init);

View File

@@ -0,0 +1,118 @@
/*
* arch/ppc/platforms/85xx/mpc85xx_sys.c
*
* MPC85xx System descriptions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/ppc_sys.h>
struct ppc_sys_spec *cur_ppc_sys_spec;
struct ppc_sys_spec ppc_sys_specs[] = {
{
.ppc_sys_name = "8540",
.mask = 0xFFFF0000,
.value = 0x80300000,
.num_devices = 10,
.device_list = (enum ppc_sys_devices[])
{
MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_FEC, MPC85xx_IIC1,
MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
MPC85xx_PERFMON, MPC85xx_DUART,
},
},
{
.ppc_sys_name = "8560",
.mask = 0xFFFF0000,
.value = 0x80700000,
.num_devices = 19,
.device_list = (enum ppc_sys_devices[])
{
MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
MPC85xx_PERFMON,
MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, MPC85xx_CPM_SCC4,
MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3,
MPC85xx_CPM_MCC1, MPC85xx_CPM_MCC2,
},
},
{
.ppc_sys_name = "8541",
.mask = 0xFFFF0000,
.value = 0x80720000,
.num_devices = 13,
.device_list = (enum ppc_sys_devices[])
{
MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
MPC85xx_PERFMON, MPC85xx_DUART,
MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
},
},
{
.ppc_sys_name = "8541E",
.mask = 0xFFFF0000,
.value = 0x807A0000,
.num_devices = 14,
.device_list = (enum ppc_sys_devices[])
{
MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
},
},
{
.ppc_sys_name = "8555",
.mask = 0xFFFF0000,
.value = 0x80710000,
.num_devices = 19,
.device_list = (enum ppc_sys_devices[])
{
MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
MPC85xx_PERFMON, MPC85xx_DUART,
MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
MPC85xx_CPM_USB,
},
},
{
.ppc_sys_name = "8555E",
.mask = 0xFFFF0000,
.value = 0x80790000,
.num_devices = 20,
.device_list = (enum ppc_sys_devices[])
{
MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
MPC85xx_CPM_USB,
},
},
{ /* default match */
.ppc_sys_name = "",
.mask = 0x00000000,
.value = 0x00000000,
},
};

View File

@@ -0,0 +1,426 @@
/*
* arch/ppc/kernel/mv64360_pic.c
*
* Interrupt controller support for Marvell's MV64360.
*
* Author: Rabeeh Khoury <rabeeh@galileo.co.il>
* Based on MV64360 PIC written by
* Chris Zankel <chris@mvista.com>
* Mark A. Greer <mgreer@mvista.com>
*
* Copyright 2004 MontaVista Software, Inc.
*
* 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 the specific functions to support the MV64360
* interrupt controller.
*
* The MV64360 has two main interrupt registers (high and low) that
* summarizes the interrupts generated by the units of the MV64360.
* Each bit is assigned to an interrupt number, where the low register
* are assigned from IRQ0 to IRQ31 and the high cause register
* from IRQ32 to IRQ63
* The GPP (General Purpose Pins) interrupts are assigned from IRQ64 (GPP0)
* to IRQ95 (GPP31).
* get_irq() returns the lowest interrupt number that is currently asserted.
*
* Note:
* - This driver does not initialize the GPP when used as an interrupt
* input.
*/
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/stddef.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/mv64x60.h>
#ifdef CONFIG_IRQ_ALL_CPUS
#error "The mv64360 does not support distribution of IRQs on all CPUs"
#endif
/* ========================== forward declaration ========================== */
static void mv64360_unmask_irq(unsigned int);
static void mv64360_mask_irq(unsigned int);
static irqreturn_t mv64360_cpu_error_int_handler(int, void *, struct pt_regs *);
static irqreturn_t mv64360_sram_error_int_handler(int, void *,
struct pt_regs *);
static irqreturn_t mv64360_pci_error_int_handler(int, void *, struct pt_regs *);
/* ========================== local declarations =========================== */
struct hw_interrupt_type mv64360_pic = {
.typename = " mv64360 ",
.enable = mv64360_unmask_irq,
.disable = mv64360_mask_irq,
.ack = mv64360_mask_irq,
.end = mv64360_unmask_irq,
};
#define CPU_INTR_STR "mv64360 cpu interface error"
#define SRAM_INTR_STR "mv64360 internal sram error"
#define PCI0_INTR_STR "mv64360 pci 0 error"
#define PCI1_INTR_STR "mv64360 pci 1 error"
static struct mv64x60_handle bh;
u32 mv64360_irq_base = 0; /* MV64360 handles the next 96 IRQs from here */
/* mv64360_init_irq()
*
* This function initializes the interrupt controller. It assigns
* all interrupts from IRQ0 to IRQ95 to the mv64360 interrupt controller.
*
* Input Variable(s):
* None.
*
* Outpu. Variable(s):
* None.
*
* Returns:
* void
*
* Note:
* We register all GPP inputs as interrupt source, but disable them.
*/
void __init
mv64360_init_irq(void)
{
int i;
if (ppc_md.progress)
ppc_md.progress("mv64360_init_irq: enter", 0x0);
bh.v_base = mv64x60_get_bridge_vbase();
ppc_cached_irq_mask[0] = 0;
ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */
ppc_cached_irq_mask[2] = 0;
/* disable all interrupts and clear current interrupts */
mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0);
mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,ppc_cached_irq_mask[0]);
mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,ppc_cached_irq_mask[1]);
/* All interrupts are level interrupts */
for (i = mv64360_irq_base; i < (mv64360_irq_base + 96); i++) {
irq_desc[i].status |= IRQ_LEVEL;
irq_desc[i].handler = &mv64360_pic;
}
if (ppc_md.progress)
ppc_md.progress("mv64360_init_irq: exit", 0x0);
}
/* mv64360_get_irq()
*
* This function returns the lowest interrupt number of all interrupts that
* are currently asserted.
*
* Input Variable(s):
* struct pt_regs* not used
*
* Output Variable(s):
* None.
*
* Returns:
* int <interrupt number> or -2 (bogus interrupt)
*
*/
int
mv64360_get_irq(struct pt_regs *regs)
{
int irq;
int irq_gpp;
#ifdef CONFIG_SMP
/*
* Second CPU gets only doorbell (message) interrupts.
* The doorbell interrupt is BIT28 in the main interrupt low cause reg.
*/
int cpu_nr = smp_processor_id();
if (cpu_nr == 1) {
if (!(mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO) &
(1 << MV64x60_IRQ_DOORBELL)))
return -1;
return mv64360_irq_base + MV64x60_IRQ_DOORBELL;
}
#endif
irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO);
irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
if (irq == -1) {
irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_HI);
irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]);
if (irq == -1)
irq = -2; /* bogus interrupt, should never happen */
else {
if ((irq >= 24) && (irq < MV64x60_IRQ_DOORBELL)) {
irq_gpp = mv64x60_read(&bh,
MV64x60_GPP_INTR_CAUSE);
irq_gpp = __ilog2(irq_gpp &
ppc_cached_irq_mask[2]);
if (irq_gpp == -1)
irq = -2;
else {
irq = irq_gpp + 64;
mv64x60_write(&bh,
MV64x60_GPP_INTR_CAUSE,
~(1 << (irq - 64)));
}
}
else
irq += 32;
}
}
(void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE);
if (irq < 0)
return (irq);
else
return (mv64360_irq_base + irq);
}
/* mv64360_unmask_irq()
*
* This function enables an interrupt.
*
* Input Variable(s):
* unsigned int interrupt number (IRQ0...IRQ95).
*
* Output Variable(s):
* None.
*
* Returns:
* void
*/
static void
mv64360_unmask_irq(unsigned int irq)
{
#ifdef CONFIG_SMP
/* second CPU gets only doorbell interrupts */
if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) {
mv64x60_set_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO,
(1 << MV64x60_IRQ_DOORBELL));
return;
}
#endif
irq -= mv64360_irq_base;
if (irq > 31) {
if (irq > 63) /* unmask GPP irq */
mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
ppc_cached_irq_mask[2] |= (1 << (irq - 64)));
else /* mask high interrupt register */
mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,
ppc_cached_irq_mask[1] |= (1 << (irq - 32)));
}
else /* mask low interrupt register */
mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,
ppc_cached_irq_mask[0] |= (1 << irq));
(void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
return;
}
/* mv64360_mask_irq()
*
* This function disables the requested interrupt.
*
* Input Variable(s):
* unsigned int interrupt number (IRQ0...IRQ95).
*
* Output Variable(s):
* None.
*
* Returns:
* void
*/
static void
mv64360_mask_irq(unsigned int irq)
{
#ifdef CONFIG_SMP
if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) {
mv64x60_clr_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO,
(1 << MV64x60_IRQ_DOORBELL));
return;
}
#endif
irq -= mv64360_irq_base;
if (irq > 31) {
if (irq > 63) /* mask GPP irq */
mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
ppc_cached_irq_mask[2] &= ~(1 << (irq - 64)));
else /* mask high interrupt register */
mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,
ppc_cached_irq_mask[1] &= ~(1 << (irq - 32)));
}
else /* mask low interrupt register */
mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,
ppc_cached_irq_mask[0] &= ~(1 << irq));
(void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
return;
}
static irqreturn_t
mv64360_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
{
printk(KERN_ERR "mv64360_cpu_error_int_handler: %s 0x%08x\n",
"Error on CPU interface - Cause regiser",
mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE));
printk(KERN_ERR "\tCPU error register dump:\n");
printk(KERN_ERR "\tAddress low 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress high 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI));
printk(KERN_ERR "\tData low 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO));
printk(KERN_ERR "\tData high 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI));
printk(KERN_ERR "\tParity 0x%08x\n",
mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY));
mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
return IRQ_HANDLED;
}
static irqreturn_t
mv64360_sram_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
{
printk(KERN_ERR "mv64360_sram_error_int_handler: %s 0x%08x\n",
"Error in internal SRAM - Cause register",
mv64x60_read(&bh, MV64360_SRAM_ERR_CAUSE));
printk(KERN_ERR "\tSRAM error register dump:\n");
printk(KERN_ERR "\tAddress Low 0x%08x\n",
mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress High 0x%08x\n",
mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_HI));
printk(KERN_ERR "\tData Low 0x%08x\n",
mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_LO));
printk(KERN_ERR "\tData High 0x%08x\n",
mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_HI));
printk(KERN_ERR "\tParity 0x%08x\n",
mv64x60_read(&bh, MV64360_SRAM_ERR_PARITY));
mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0);
return IRQ_HANDLED;
}
static irqreturn_t
mv64360_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
{
u32 val;
unsigned int pci_bus = (unsigned int)dev_id;
if (pci_bus == 0) { /* Error on PCI 0 */
val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE);
printk(KERN_ERR "%s: Error in PCI %d Interface\n",
"mv64360_pci_error_int_handler", pci_bus);
printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
printk(KERN_ERR "\tCause register 0x%08x\n", val);
printk(KERN_ERR "\tAddress Low 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress High 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI));
printk(KERN_ERR "\tAttribute 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO));
printk(KERN_ERR "\tCommand 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD));
mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val);
}
if (pci_bus == 1) { /* Error on PCI 1 */
val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE);
printk(KERN_ERR "%s: Error in PCI %d Interface\n",
"mv64360_pci_error_int_handler", pci_bus);
printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
printk(KERN_ERR "\tCause register 0x%08x\n", val);
printk(KERN_ERR "\tAddress Low 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO));
printk(KERN_ERR "\tAddress High 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI));
printk(KERN_ERR "\tAttribute 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO));
printk(KERN_ERR "\tCommand 0x%08x\n",
mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD));
mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val);
}
return IRQ_HANDLED;
}
static int __init
mv64360_register_hdlrs(void)
{
u32 mask;
int rc;
/* Clear old errors and register CPU interface error intr handler */
mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
if ((rc = request_irq(MV64x60_IRQ_CPU_ERR + mv64360_irq_base,
mv64360_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0)))
printk(KERN_WARNING "Can't register cpu error handler: %d", rc);
mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0);
mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000ff);
/* Clear old errors and register internal SRAM error intr handler */
mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0);
if ((rc = request_irq(MV64360_IRQ_SRAM_PAR_ERR + mv64360_irq_base,
mv64360_sram_error_int_handler,SA_INTERRUPT,SRAM_INTR_STR, 0)))
printk(KERN_WARNING "Can't register SRAM error handler: %d",rc);
/*
* Bit 0 reserved on 64360 and erratum FEr PCI-#11 (PCI internal
* data parity error set incorrectly) on rev 0 & 1 of 64460 requires
* bit 0 to be cleared.
*/
mask = 0x00a50c24;
if ((mv64x60_get_bridge_type() == MV64x60_TYPE_MV64460) &&
(mv64x60_get_bridge_rev() > 1))
mask |= 0x1; /* enable DPErr on 64460 */
/* Clear old errors and register PCI 0 error intr handler */
mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, 0);
if ((rc = request_irq(MV64360_IRQ_PCI0 + mv64360_irq_base,
mv64360_pci_error_int_handler,
SA_INTERRUPT, PCI0_INTR_STR, (void *)0)))
printk(KERN_WARNING "Can't register pci 0 error handler: %d",
rc);
mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0);
mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, mask);
/* Clear old errors and register PCI 1 error intr handler */
mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, 0);
if ((rc = request_irq(MV64360_IRQ_PCI1 + mv64360_irq_base,
mv64360_pci_error_int_handler,
SA_INTERRUPT, PCI1_INTR_STR, (void *)1)))
printk(KERN_WARNING "Can't register pci 1 error handler: %d",
rc);
mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0);
mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, mask);
return 0;
}
arch_initcall(mv64360_register_hdlrs);

2392
arch/ppc/syslib/mv64x60.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
/*
* arch/ppc/syslib/mv64x60_dbg.c
*
* KGDB and progress routines for the Marvell/Galileo MV64x60 (Discovery).
*
* Author: Mark A. Greer <mgreer@mvista.com>
*
* 2003 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
*****************************************************************************
*
* Low-level MPSC/UART I/O routines
*
*****************************************************************************
*/
#include <linux/config.h>
#include <linux/irq.h>
#include <asm/delay.h>
#include <asm/mv64x60.h>
#if defined(CONFIG_SERIAL_TEXT_DEBUG)
#define MPSC_CHR_1 0x000c
#define MPSC_CHR_2 0x0010
static struct mv64x60_handle mv64x60_dbg_bh;
void
mv64x60_progress_init(u32 base)
{
mv64x60_dbg_bh.v_base = base;
return;
}
static void
mv64x60_polled_putc(int chan, char c)
{
u32 offset;
if (chan == 0)
offset = 0x8000;
else
offset = 0x9000;
mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_1, (u32)c);
mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_2, 0x200);
udelay(2000);
}
void
mv64x60_mpsc_progress(char *s, unsigned short hex)
{
volatile char c;
mv64x60_polled_putc(0, '\r');
while ((c = *s++) != 0)
mv64x60_polled_putc(0, c);
mv64x60_polled_putc(0, '\n');
mv64x60_polled_putc(0, '\r');
return;
}
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
#if defined(CONFIG_KGDB)
#if defined(CONFIG_KGDB_TTYS0)
#define KGDB_PORT 0
#elif defined(CONFIG_KGDB_TTYS1)
#define KGDB_PORT 1
#else
#error "Invalid kgdb_tty port"
#endif
void
putDebugChar(unsigned char c)
{
mv64x60_polled_putc(KGDB_PORT, (char)c);
}
int
getDebugChar(void)
{
unsigned char c;
while (!mv64x60_polled_getc(KGDB_PORT, &c));
return (int)c;
}
void
putDebugString(char* str)
{
while (*str != '\0') {
putDebugChar(*str);
str++;
}
putDebugChar('\r');
return;
}
void
kgdb_interruptible(int enable)
{
}
void
kgdb_map_scc(void)
{
if (ppc_md.early_serial_map)
ppc_md.early_serial_map();
}
#endif /* CONFIG_KGDB */

File diff suppressed because it is too large Load Diff

485
arch/ppc/syslib/ocp.c Normal file
View File

@@ -0,0 +1,485 @@
/*
* ocp.c
*
* (c) Benjamin Herrenschmidt (benh@kernel.crashing.org)
* Mipsys - France
*
* Derived from work (c) Armin Kuster akuster@pacbell.net
*
* Additional support and port to 2.6 LDM/sysfs by
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2004 MontaVista Software, Inc.
*
* 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.
*
* OCP (On Chip Peripheral) is a software emulated "bus" with a
* pseudo discovery method for dumb peripherals. Usually these type
* of peripherals are found on embedded SoC (System On a Chip)
* processors or highly integrated system controllers that have
* a host bridge and many peripherals. Common examples where
* this is already used include the PPC4xx, PPC85xx, MPC52xx,
* and MV64xxx parts.
*
* This subsystem creates a standard OCP bus type within the
* device model. The devices on the OCP bus are seeded by an
* an initial OCP device array created by the arch-specific
* Device entries can be added/removed/modified through OCP
* helper functions to accomodate system and board-specific
* parameters commonly found in embedded systems. OCP also
* provides a standard method for devices to describe extended
* attributes about themselves to the system. A standard access
* method allows OCP drivers to obtain the information, both
* SoC-specific and system/board-specific, needed for operation.
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/bootmem.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/ocp.h>
#include <asm/errno.h>
#include <asm/rwsem.h>
#include <asm/semaphore.h>
//#define DBG(x) printk x
#define DBG(x)
extern int mem_init_done;
extern struct ocp_def core_ocp[]; /* Static list of devices, provided by
CPU core */
LIST_HEAD(ocp_devices); /* List of all OCP devices */
DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */
static int ocp_inited;
/* Sysfs support */
#define OCP_DEF_ATTR(field, format_string) \
static ssize_t \
show_##field(struct device *dev, char *buf) \
{ \
struct ocp_device *odev = to_ocp_dev(dev); \
\
return sprintf(buf, format_string, odev->def->field); \
} \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
OCP_DEF_ATTR(vendor, "0x%04x\n");
OCP_DEF_ATTR(function, "0x%04x\n");
OCP_DEF_ATTR(index, "0x%04x\n");
#ifdef CONFIG_PTE_64BIT
OCP_DEF_ATTR(paddr, "0x%016Lx\n");
#else
OCP_DEF_ATTR(paddr, "0x%08lx\n");
#endif
OCP_DEF_ATTR(irq, "%d\n");
OCP_DEF_ATTR(pm, "%lu\n");
void ocp_create_sysfs_dev_files(struct ocp_device *odev)
{
struct device *dev = &odev->dev;
/* Current OCP device def attributes */
device_create_file(dev, &dev_attr_vendor);
device_create_file(dev, &dev_attr_function);
device_create_file(dev, &dev_attr_index);
device_create_file(dev, &dev_attr_paddr);
device_create_file(dev, &dev_attr_irq);
device_create_file(dev, &dev_attr_pm);
/* Current OCP device additions attributes */
if (odev->def->additions && odev->def->show)
odev->def->show(dev);
}
/**
* ocp_device_match - Match one driver to one device
* @drv: driver to match
* @dev: device to match
*
* This function returns 0 if the driver and device don't match
*/
static int
ocp_device_match(struct device *dev, struct device_driver *drv)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_drv(drv);
const struct ocp_device_id *ids = ocp_drv->id_table;
if (!ids)
return 0;
while (ids->vendor || ids->function) {
if ((ids->vendor == OCP_ANY_ID
|| ids->vendor == ocp_dev->def->vendor)
&& (ids->function == OCP_ANY_ID
|| ids->function == ocp_dev->def->function))
return 1;
ids++;
}
return 0;
}
static int
ocp_device_probe(struct device *dev)
{
int error = 0;
struct ocp_driver *drv;
struct ocp_device *ocp_dev;
drv = to_ocp_drv(dev->driver);
ocp_dev = to_ocp_dev(dev);
if (drv->probe) {
error = drv->probe(ocp_dev);
if (error >= 0) {
ocp_dev->driver = drv;
error = 0;
}
}
return error;
}
static int
ocp_device_remove(struct device *dev)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
if (ocp_dev->driver) {
if (ocp_dev->driver->remove)
ocp_dev->driver->remove(ocp_dev);
ocp_dev->driver = NULL;
}
return 0;
}
static int
ocp_device_suspend(struct device *dev, u32 state)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver);
if (dev->driver && ocp_drv->suspend)
return ocp_drv->suspend(ocp_dev, state);
return 0;
}
static int
ocp_device_resume(struct device *dev)
{
struct ocp_device *ocp_dev = to_ocp_dev(dev);
struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver);
if (dev->driver && ocp_drv->resume)
return ocp_drv->resume(ocp_dev);
return 0;
}
struct bus_type ocp_bus_type = {
.name = "ocp",
.match = ocp_device_match,
.suspend = ocp_device_suspend,
.resume = ocp_device_resume,
};
/**
* ocp_register_driver - Register an OCP driver
* @drv: pointer to statically defined ocp_driver structure
*
* The driver's probe() callback is called either recursively
* by this function or upon later call of ocp_driver_init
*
* NOTE: Detection of devices is a 2 pass step on this implementation,
* hotswap isn't supported. First, all OCP devices are put in the device
* list, _then_ all drivers are probed on each match.
*/
int
ocp_register_driver(struct ocp_driver *drv)
{
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &ocp_bus_type;
drv->driver.probe = ocp_device_probe;
drv->driver.remove = ocp_device_remove;
/* register with core */
return driver_register(&drv->driver);
}
/**
* ocp_unregister_driver - Unregister an OCP driver
* @drv: pointer to statically defined ocp_driver structure
*
* The driver's remove() callback is called recursively
* by this function for any device already registered
*/
void
ocp_unregister_driver(struct ocp_driver *drv)
{
DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name));
driver_unregister(&drv->driver);
DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name));
}
/* Core of ocp_find_device(). Caller must hold ocp_devices_sem */
static struct ocp_device *
__ocp_find_device(unsigned int vendor, unsigned int function, int index)
{
struct list_head *entry;
struct ocp_device *dev, *found = NULL;
DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index));
list_for_each(entry, &ocp_devices) {
dev = list_entry(entry, struct ocp_device, link);
if (vendor != OCP_ANY_ID && vendor != dev->def->vendor)
continue;
if (function != OCP_ANY_ID && function != dev->def->function)
continue;
if (index != OCP_ANY_INDEX && index != dev->def->index)
continue;
found = dev;
break;
}
DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index));
return found;
}
/**
* ocp_find_device - Find a device by function & index
* @vendor: vendor ID of the device (or OCP_ANY_ID)
* @function: function code of the device (or OCP_ANY_ID)
* @idx: index of the device (or OCP_ANY_INDEX)
*
* This function allows a lookup of a given function by it's
* index, it's typically used to find the MAL or ZMII associated
* with an EMAC or similar horrors.
* You can pass vendor, though you usually want OCP_ANY_ID there...
*/
struct ocp_device *
ocp_find_device(unsigned int vendor, unsigned int function, int index)
{
struct ocp_device *dev;
down_read(&ocp_devices_sem);
dev = __ocp_find_device(vendor, function, index);
up_read(&ocp_devices_sem);
return dev;
}
/**
* ocp_get_one_device - Find a def by function & index
* @vendor: vendor ID of the device (or OCP_ANY_ID)
* @function: function code of the device (or OCP_ANY_ID)
* @idx: index of the device (or OCP_ANY_INDEX)
*
* This function allows a lookup of a given ocp_def by it's
* vendor, function, and index. The main purpose for is to
* allow modification of the def before binding to the driver
*/
struct ocp_def *
ocp_get_one_device(unsigned int vendor, unsigned int function, int index)
{
struct ocp_device *dev;
struct ocp_def *found = NULL;
DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n",
vendor, function, index));
dev = ocp_find_device(vendor, function, index);
if (dev)
found = dev->def;
DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n",
vendor, function, index));
return found;
}
/**
* ocp_add_one_device - Add a device
* @def: static device definition structure
*
* This function adds a device definition to the
* device list. It may only be called before
* ocp_driver_init() and will return an error
* otherwise.
*/
int
ocp_add_one_device(struct ocp_def *def)
{
struct ocp_device *dev;
DBG(("ocp: ocp_add_one_device()...\n"));
/* Can't be called after ocp driver init */
if (ocp_inited)
return 1;
if (mem_init_done)
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
else
dev = alloc_bootmem(sizeof(*dev));
if (dev == NULL)
return 1;
memset(dev, 0, sizeof(*dev));
dev->def = def;
dev->current_state = 4;
sprintf(dev->name, "OCP device %04x:%04x:%04x",
dev->def->vendor, dev->def->function, dev->def->index);
down_write(&ocp_devices_sem);
list_add_tail(&dev->link, &ocp_devices);
up_write(&ocp_devices_sem);
DBG(("ocp: ocp_add_one_device()...done\n"));
return 0;
}
/**
* ocp_remove_one_device - Remove a device by function & index
* @vendor: vendor ID of the device (or OCP_ANY_ID)
* @function: function code of the device (or OCP_ANY_ID)
* @idx: index of the device (or OCP_ANY_INDEX)
*
* This function allows removal of a given function by its
* index. It may only be called before ocp_driver_init()
* and will return an error otherwise.
*/
int
ocp_remove_one_device(unsigned int vendor, unsigned int function, int index)
{
struct ocp_device *dev;
DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index));
/* Can't be called after ocp driver init */
if (ocp_inited)
return 1;
down_write(&ocp_devices_sem);
dev = __ocp_find_device(vendor, function, index);
list_del((struct list_head *)dev);
up_write(&ocp_devices_sem);
DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index));
return 0;
}
/**
* ocp_for_each_device - Iterate over OCP devices
* @callback: routine to execute for each ocp device.
* @arg: user data to be passed to callback routine.
*
* This routine holds the ocp_device semaphore, so the
* callback routine cannot modify the ocp_device list.
*/
void
ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg)
{
struct list_head *entry;
if (callback) {
down_read(&ocp_devices_sem);
list_for_each(entry, &ocp_devices)
callback(list_entry(entry, struct ocp_device, link),
arg);
up_read(&ocp_devices_sem);
}
}
/**
* ocp_early_init - Init OCP device management
*
* This function builds the list of devices before setup_arch.
* This allows platform code to modify the device lists before
* they are bound to drivers (changes to paddr, removing devices
* etc)
*/
int __init
ocp_early_init(void)
{
struct ocp_def *def;
DBG(("ocp: ocp_early_init()...\n"));
/* Fill the devices list */
for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++)
ocp_add_one_device(def);
DBG(("ocp: ocp_early_init()... done.\n"));
return 0;
}
/**
* ocp_driver_init - Init OCP device management
*
* This function is meant to be called via OCP bus registration.
*/
static int __init
ocp_driver_init(void)
{
int ret = 0, index = 0;
struct device *ocp_bus;
struct list_head *entry;
struct ocp_device *dev;
if (ocp_inited)
return ret;
ocp_inited = 1;
DBG(("ocp: ocp_driver_init()...\n"));
/* Allocate/register primary OCP bus */
ocp_bus = kmalloc(sizeof(struct device), GFP_KERNEL);
if (ocp_bus == NULL)
return 1;
memset(ocp_bus, 0, sizeof(struct device));
strcpy(ocp_bus->bus_id, "ocp");
bus_register(&ocp_bus_type);
device_register(ocp_bus);
/* Put each OCP device into global device list */
list_for_each(entry, &ocp_devices) {
dev = list_entry(entry, struct ocp_device, link);
sprintf(dev->dev.bus_id, "%2.2x", index);
dev->dev.parent = ocp_bus;
dev->dev.bus = &ocp_bus_type;
device_register(&dev->dev);
ocp_create_sysfs_dev_files(dev);
index++;
}
DBG(("ocp: ocp_driver_init()... done.\n"));
return 0;
}
postcore_initcall(ocp_driver_init);
EXPORT_SYMBOL(ocp_bus_type);
EXPORT_SYMBOL(ocp_find_device);
EXPORT_SYMBOL(ocp_register_driver);
EXPORT_SYMBOL(ocp_unregister_driver);

273
arch/ppc/syslib/of_device.c Normal file
View File

@@ -0,0 +1,273 @@
#include <linux/config.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/errno.h>
#include <asm/of_device.h>
/**
* of_match_device - Tell if an of_device structure has a matching
* of_match structure
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
* Used by a driver to check whether an of_device present in the
* system is in its list of supported devices.
*/
const struct of_match * of_match_device(const struct of_match *matches,
const struct of_device *dev)
{
if (!dev->node)
return NULL;
while (matches->name || matches->type || matches->compatible) {
int match = 1;
if (matches->name && matches->name != OF_ANY_MATCH)
match &= dev->node->name
&& !strcmp(matches->name, dev->node->name);
if (matches->type && matches->type != OF_ANY_MATCH)
match &= dev->node->type
&& !strcmp(matches->type, dev->node->type);
if (matches->compatible && matches->compatible != OF_ANY_MATCH)
match &= device_is_compatible(dev->node,
matches->compatible);
if (match)
return matches;
matches++;
}
return NULL;
}
static int of_platform_bus_match(struct device *dev, struct device_driver *drv)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * of_drv = to_of_platform_driver(drv);
const struct of_match * matches = of_drv->match_table;
if (!matches)
return 0;
return of_match_device(matches, of_dev) != NULL;
}
struct of_device *of_dev_get(struct of_device *dev)
{
struct device *tmp;
if (!dev)
return NULL;
tmp = get_device(&dev->dev);
if (tmp)
return to_of_device(tmp);
else
return NULL;
}
void of_dev_put(struct of_device *dev)
{
if (dev)
put_device(&dev->dev);
}
static int of_device_probe(struct device *dev)
{
int error = -ENODEV;
struct of_platform_driver *drv;
struct of_device *of_dev;
const struct of_match *match;
drv = to_of_platform_driver(dev->driver);
of_dev = to_of_device(dev);
if (!drv->probe)
return error;
of_dev_get(of_dev);
match = of_match_device(drv->match_table, of_dev);
if (match)
error = drv->probe(of_dev, match);
if (error)
of_dev_put(of_dev);
return error;
}
static int of_device_remove(struct device *dev)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
if (dev->driver && drv->remove)
drv->remove(of_dev);
return 0;
}
static int of_device_suspend(struct device *dev, u32 state)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
int error = 0;
if (dev->driver && drv->suspend)
error = drv->suspend(of_dev, state);
return error;
}
static int of_device_resume(struct device * dev)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
int error = 0;
if (dev->driver && drv->resume)
error = drv->resume(of_dev);
return error;
}
struct bus_type of_platform_bus_type = {
.name = "of_platform",
.match = of_platform_bus_match,
.suspend = of_device_suspend,
.resume = of_device_resume,
};
static int __init of_bus_driver_init(void)
{
return bus_register(&of_platform_bus_type);
}
postcore_initcall(of_bus_driver_init);
int of_register_driver(struct of_platform_driver *drv)
{
int count = 0;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &of_platform_bus_type;
drv->driver.probe = of_device_probe;
drv->driver.remove = of_device_remove;
/* register with core */
count = driver_register(&drv->driver);
return count ? count : 1;
}
void of_unregister_driver(struct of_platform_driver *drv)
{
driver_unregister(&drv->driver);
}
static ssize_t dev_show_devspec(struct device *dev, char *buf)
{
struct of_device *ofdev;
ofdev = to_of_device(dev);
return sprintf(buf, "%s", ofdev->node->full_name);
}
static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL);
/**
* of_release_dev - free an of device structure when all users of it are finished.
* @dev: device that's been disconnected
*
* Will be called only by the device core when all users of this of device are
* done.
*/
void of_release_dev(struct device *dev)
{
struct of_device *ofdev;
ofdev = to_of_device(dev);
of_node_put(ofdev->node);
kfree(ofdev);
}
int of_device_register(struct of_device *ofdev)
{
int rc;
struct of_device **odprop;
BUG_ON(ofdev->node == NULL);
odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL);
if (!odprop) {
struct property *new_prop;
new_prop = kmalloc(sizeof(struct property) + sizeof(struct of_device *),
GFP_KERNEL);
if (new_prop == NULL)
return -ENOMEM;
new_prop->name = "linux,device";
new_prop->length = sizeof(sizeof(struct of_device *));
new_prop->value = (unsigned char *)&new_prop[1];
odprop = (struct of_device **)new_prop->value;
*odprop = NULL;
prom_add_property(ofdev->node, new_prop);
}
*odprop = ofdev;
rc = device_register(&ofdev->dev);
if (rc)
return rc;
device_create_file(&ofdev->dev, &dev_attr_devspec);
return 0;
}
void of_device_unregister(struct of_device *ofdev)
{
struct of_device **odprop;
device_remove_file(&ofdev->dev, &dev_attr_devspec);
odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL);
if (odprop)
*odprop = NULL;
device_unregister(&ofdev->dev);
}
struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id)
{
struct of_device *dev;
u32 *reg;
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, sizeof(*dev));
dev->node = of_node_get(np);
dev->dma_mask = 0xffffffffUL;
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.parent = NULL;
dev->dev.bus = &of_platform_bus_type;
dev->dev.release = of_release_dev;
reg = (u32 *)get_property(np, "reg", NULL);
strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
if (of_device_register(dev) != 0) {
kfree(dev);
return NULL;
}
return dev;
}
EXPORT_SYMBOL(of_match_device);
EXPORT_SYMBOL(of_platform_bus_type);
EXPORT_SYMBOL(of_register_driver);
EXPORT_SYMBOL(of_unregister_driver);
EXPORT_SYMBOL(of_device_register);
EXPORT_SYMBOL(of_device_unregister);
EXPORT_SYMBOL(of_dev_get);
EXPORT_SYMBOL(of_dev_put);
EXPORT_SYMBOL(of_platform_device_create);
EXPORT_SYMBOL(of_release_dev);

1083
arch/ppc/syslib/open_pic.c Normal file

File diff suppressed because it is too large Load Diff

716
arch/ppc/syslib/open_pic2.c Normal file
View File

@@ -0,0 +1,716 @@
/*
* arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
*
* Copyright (C) 1997 Geert Uytterhoeven
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* This is a duplicate of open_pic.c that deals with U3s MPIC on
* G5 PowerMacs. It's the same file except it's using big endian
* register accesses
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sysdev.h>
#include <linux/errno.h>
#include <asm/ptrace.h>
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
#include <asm/sections.h>
#include <asm/open_pic.h>
#include <asm/i8259.h>
#include "open_pic_defs.h"
void *OpenPIC2_Addr;
static volatile struct OpenPIC *OpenPIC2 = NULL;
/*
* We define OpenPIC_InitSenses table thusly:
* bit 0x1: sense, 0 for edge and 1 for level.
* bit 0x2: polarity, 0 for negative, 1 for positive.
*/
extern u_int OpenPIC_NumInitSenses;
extern u_char *OpenPIC_InitSenses;
extern int use_of_interrupt_tree;
static u_int NumProcessors;
static u_int NumSources;
static int open_pic2_irq_offset;
static volatile OpenPIC_Source *ISR[NR_IRQS];
/* Global Operations */
static void openpic2_disable_8259_pass_through(void);
static void openpic2_set_priority(u_int pri);
static void openpic2_set_spurious(u_int vector);
/* Timer Interrupts */
static void openpic2_inittimer(u_int timer, u_int pri, u_int vector);
static void openpic2_maptimer(u_int timer, u_int cpumask);
/* Interrupt Sources */
static void openpic2_enable_irq(u_int irq);
static void openpic2_disable_irq(u_int irq);
static void openpic2_initirq(u_int irq, u_int pri, u_int vector, int polarity,
int is_level);
static void openpic2_mapirq(u_int irq, u_int cpumask, u_int keepmask);
/*
* These functions are not used but the code is kept here
* for completeness and future reference.
*/
static void openpic2_reset(void);
#ifdef notused
static void openpic2_enable_8259_pass_through(void);
static u_int openpic2_get_priority(void);
static u_int openpic2_get_spurious(void);
static void openpic2_set_sense(u_int irq, int sense);
#endif /* notused */
/*
* Description of the openpic for the higher-level irq code
*/
static void openpic2_end_irq(unsigned int irq_nr);
static void openpic2_ack_irq(unsigned int irq_nr);
struct hw_interrupt_type open_pic2 = {
" OpenPIC2 ",
NULL,
NULL,
openpic2_enable_irq,
openpic2_disable_irq,
openpic2_ack_irq,
openpic2_end_irq,
};
/*
* Accesses to the current processor's openpic registers
* On cascaded controller, this is only CPU 0
*/
#define THIS_CPU Processor[0]
#define DECL_THIS_CPU
#define CHECK_THIS_CPU
#if 1
#define check_arg_ipi(ipi) \
if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
printk("open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi);
#define check_arg_timer(timer) \
if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \
printk("open_pic.c:%d: illegal timer %d\n", __LINE__, timer);
#define check_arg_vec(vec) \
if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \
printk("open_pic.c:%d: illegal vector %d\n", __LINE__, vec);
#define check_arg_pri(pri) \
if (pri < 0 || pri >= OPENPIC_NUM_PRI) \
printk("open_pic.c:%d: illegal priority %d\n", __LINE__, pri);
/*
* Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's
* data has probably been corrupted and we're going to panic or deadlock later
* anyway --Troy
*/
extern unsigned long* _get_SP(void);
#define check_arg_irq(irq) \
if (irq < open_pic2_irq_offset || irq >= NumSources+open_pic2_irq_offset \
|| ISR[irq - open_pic2_irq_offset] == 0) { \
printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \
/*print_backtrace(_get_SP());*/ }
#define check_arg_cpu(cpu) \
if (cpu < 0 || cpu >= NumProcessors){ \
printk("open_pic2.c:%d: illegal cpu %d\n", __LINE__, cpu); \
/*print_backtrace(_get_SP());*/ }
#else
#define check_arg_ipi(ipi) do {} while (0)
#define check_arg_timer(timer) do {} while (0)
#define check_arg_vec(vec) do {} while (0)
#define check_arg_pri(pri) do {} while (0)
#define check_arg_irq(irq) do {} while (0)
#define check_arg_cpu(cpu) do {} while (0)
#endif
static u_int openpic2_read(volatile u_int *addr)
{
u_int val;
val = in_be32(addr);
return val;
}
static inline void openpic2_write(volatile u_int *addr, u_int val)
{
out_be32(addr, val);
}
static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask)
{
u_int val = openpic2_read(addr);
return val & mask;
}
inline void openpic2_writefield(volatile u_int *addr, u_int mask,
u_int field)
{
u_int val = openpic2_read(addr);
openpic2_write(addr, (val & ~mask) | (field & mask));
}
static inline void openpic2_clearfield(volatile u_int *addr, u_int mask)
{
openpic2_writefield(addr, mask, 0);
}
static inline void openpic2_setfield(volatile u_int *addr, u_int mask)
{
openpic2_writefield(addr, mask, mask);
}
static void openpic2_safe_writefield(volatile u_int *addr, u_int mask,
u_int field)
{
openpic2_setfield(addr, OPENPIC_MASK);
while (openpic2_read(addr) & OPENPIC_ACTIVITY);
openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
}
static void openpic2_reset(void)
{
openpic2_setfield(&OpenPIC2->Global.Global_Configuration0,
OPENPIC_CONFIG_RESET);
while (openpic2_readfield(&OpenPIC2->Global.Global_Configuration0,
OPENPIC_CONFIG_RESET))
mb();
}
void __init openpic2_set_sources(int first_irq, int num_irqs, void *first_ISR)
{
volatile OpenPIC_Source *src = first_ISR;
int i, last_irq;
last_irq = first_irq + num_irqs;
if (last_irq > NumSources)
NumSources = last_irq;
if (src == 0)
src = &((struct OpenPIC *)OpenPIC2_Addr)->Source[first_irq];
for (i = first_irq; i < last_irq; ++i, ++src)
ISR[i] = src;
}
/*
* The `offset' parameter defines where the interrupts handled by the
* OpenPIC start in the space of interrupt numbers that the kernel knows
* about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the
* kernel's interrupt numbering scheme.
* We assume there is only one OpenPIC.
*/
void __init openpic2_init(int offset)
{
u_int t, i;
u_int timerfreq;
const char *version;
if (!OpenPIC2_Addr) {
printk("No OpenPIC2 found !\n");
return;
}
OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr;
if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122);
t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0);
switch (t & OPENPIC_FEATURE_VERSION_MASK) {
case 1:
version = "1.0";
break;
case 2:
version = "1.2";
break;
case 3:
version = "1.3";
break;
default:
version = "?";
break;
}
NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
if (NumSources == 0)
openpic2_set_sources(0,
((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1,
NULL);
printk("OpenPIC (2) Version %s (%d CPUs and %d IRQ sources) at %p\n",
version, NumProcessors, NumSources, OpenPIC2);
timerfreq = openpic2_read(&OpenPIC2->Global.Timer_Frequency);
if (timerfreq)
printk("OpenPIC timer frequency is %d.%06d MHz\n",
timerfreq / 1000000, timerfreq % 1000000);
open_pic2_irq_offset = offset;
/* Initialize timer interrupts */
if ( ppc_md.progress ) ppc_md.progress("openpic2: timer",0x3ba);
for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
/* Disabled, Priority 0 */
openpic2_inittimer(i, 0, OPENPIC2_VEC_TIMER+i+offset);
/* No processor */
openpic2_maptimer(i, 0);
}
/* Initialize external interrupts */
if (ppc_md.progress) ppc_md.progress("openpic2: external",0x3bc);
openpic2_set_priority(0xf);
/* Init all external sources, including possibly the cascade. */
for (i = 0; i < NumSources; i++) {
int sense;
if (ISR[i] == 0)
continue;
/* the bootloader may have left it enabled (bad !) */
openpic2_disable_irq(i+offset);
sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE);
if (sense & IRQ_SENSE_MASK)
irq_desc[i+offset].status = IRQ_LEVEL;
/* Enabled, Priority 8 */
openpic2_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK),
(sense & IRQ_SENSE_MASK));
/* Processor 0 */
openpic2_mapirq(i, 1<<0, 0);
}
/* Init descriptors */
for (i = offset; i < NumSources + offset; i++)
irq_desc[i].handler = &open_pic2;
/* Initialize the spurious interrupt */
if (ppc_md.progress) ppc_md.progress("openpic2: spurious",0x3bd);
openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+offset);
openpic2_disable_8259_pass_through();
openpic2_set_priority(0);
if (ppc_md.progress) ppc_md.progress("openpic2: exit",0x222);
}
#ifdef notused
static void openpic2_enable_8259_pass_through(void)
{
openpic2_clearfield(&OpenPIC2->Global.Global_Configuration0,
OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
}
#endif /* notused */
/* This can't be __init, it is used in openpic_sleep_restore_intrs */
static void openpic2_disable_8259_pass_through(void)
{
openpic2_setfield(&OpenPIC2->Global.Global_Configuration0,
OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
}
/*
* Find out the current interrupt
*/
u_int openpic2_irq(void)
{
u_int vec;
DECL_THIS_CPU;
CHECK_THIS_CPU;
vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge,
OPENPIC_VECTOR_MASK);
return vec;
}
void openpic2_eoi(void)
{
DECL_THIS_CPU;
CHECK_THIS_CPU;
openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0);
/* Handle PCI write posting */
(void)openpic2_read(&OpenPIC2->THIS_CPU.EOI);
}
#ifdef notused
static u_int openpic2_get_priority(void)
{
DECL_THIS_CPU;
CHECK_THIS_CPU;
return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority,
OPENPIC_CURRENT_TASK_PRIORITY_MASK);
}
#endif /* notused */
static void __init openpic2_set_priority(u_int pri)
{
DECL_THIS_CPU;
CHECK_THIS_CPU;
check_arg_pri(pri);
openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority,
OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
}
/*
* Get/set the spurious vector
*/
#ifdef notused
static u_int openpic2_get_spurious(void)
{
return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector,
OPENPIC_VECTOR_MASK);
}
#endif /* notused */
/* This can't be __init, it is used in openpic_sleep_restore_intrs */
static void openpic2_set_spurious(u_int vec)
{
check_arg_vec(vec);
openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
vec);
}
static DEFINE_SPINLOCK(openpic2_setup_lock);
/*
* Initialize a timer interrupt (and disable it)
*
* timer: OpenPIC timer number
* pri: interrupt source priority
* vec: the vector it will produce
*/
static void __init openpic2_inittimer(u_int timer, u_int pri, u_int vec)
{
check_arg_timer(timer);
check_arg_pri(pri);
check_arg_vec(vec);
openpic2_safe_writefield(&OpenPIC2->Global.Timer[timer].Vector_Priority,
OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
(pri << OPENPIC_PRIORITY_SHIFT) | vec);
}
/*
* Map a timer interrupt to one or more CPUs
*/
static void __init openpic2_maptimer(u_int timer, u_int cpumask)
{
check_arg_timer(timer);
openpic2_write(&OpenPIC2->Global.Timer[timer].Destination,
cpumask);
}
/*
* Initalize the interrupt source which will generate an NMI.
* This raises the interrupt's priority from 8 to 9.
*
* irq: The logical IRQ which generates an NMI.
*/
void __init
openpic2_init_nmi_irq(u_int irq)
{
check_arg_irq(irq);
openpic2_safe_writefield(&ISR[irq - open_pic2_irq_offset]->Vector_Priority,
OPENPIC_PRIORITY_MASK,
9 << OPENPIC_PRIORITY_SHIFT);
}
/*
*
* All functions below take an offset'ed irq argument
*
*/
/*
* Enable/disable an external interrupt source
*
* Externally called, irq is an offseted system-wide interrupt number
*/
static void openpic2_enable_irq(u_int irq)
{
volatile u_int *vpp;
check_arg_irq(irq);
vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority;
openpic2_clearfield(vpp, OPENPIC_MASK);
/* make sure mask gets to controller before we return to user */
do {
mb(); /* sync is probably useless here */
} while (openpic2_readfield(vpp, OPENPIC_MASK));
}
static void openpic2_disable_irq(u_int irq)
{
volatile u_int *vpp;
u32 vp;
check_arg_irq(irq);
vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority;
openpic2_setfield(vpp, OPENPIC_MASK);
/* make sure mask gets to controller before we return to user */
do {
mb(); /* sync is probably useless here */
vp = openpic2_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY);
} while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK));
}
/*
* Initialize an interrupt source (and disable it!)
*
* irq: OpenPIC interrupt number
* pri: interrupt source priority
* vec: the vector it will produce
* pol: polarity (1 for positive, 0 for negative)
* sense: 1 for level, 0 for edge
*/
static void __init
openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
{
openpic2_safe_writefield(&ISR[irq]->Vector_Priority,
OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK,
(pri << OPENPIC_PRIORITY_SHIFT) | vec |
(pol ? OPENPIC_POLARITY_POSITIVE :
OPENPIC_POLARITY_NEGATIVE) |
(sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE));
}
/*
* Map an interrupt source to one or more CPUs
*/
static void openpic2_mapirq(u_int irq, u_int physmask, u_int keepmask)
{
if (ISR[irq] == 0)
return;
if (keepmask != 0)
physmask |= openpic2_read(&ISR[irq]->Destination) & keepmask;
openpic2_write(&ISR[irq]->Destination, physmask);
}
#ifdef notused
/*
* Set the sense for an interrupt source (and disable it!)
*
* sense: 1 for level, 0 for edge
*/
static void openpic2_set_sense(u_int irq, int sense)
{
if (ISR[irq] != 0)
openpic2_safe_writefield(&ISR[irq]->Vector_Priority,
OPENPIC_SENSE_LEVEL,
(sense ? OPENPIC_SENSE_LEVEL : 0));
}
#endif /* notused */
/* No spinlocks, should not be necessary with the OpenPIC
* (1 register = 1 interrupt and we have the desc lock).
*/
static void openpic2_ack_irq(unsigned int irq_nr)
{
openpic2_disable_irq(irq_nr);
openpic2_eoi();
}
static void openpic2_end_irq(unsigned int irq_nr)
{
if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
openpic2_enable_irq(irq_nr);
}
int
openpic2_get_irq(struct pt_regs *regs)
{
int irq = openpic2_irq();
if (irq == (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset))
irq = -1;
return irq;
}
#ifdef CONFIG_PM
/*
* We implement the IRQ controller as a sysdev and put it
* to sleep at powerdown stage (the callback is named suspend,
* but it's old semantics, for the Device Model, it's really
* powerdown). The possible problem is that another sysdev that
* happens to be suspend after this one will have interrupts off,
* that may be an issue... For now, this isn't an issue on pmac
* though...
*/
static u32 save_ipi_vp[OPENPIC_NUM_IPI];
static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES];
static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES];
static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS];
static int openpic_suspend_count;
static void openpic2_cached_enable_irq(u_int irq)
{
check_arg_irq(irq);
save_irq_src_vp[irq - open_pic2_irq_offset] &= ~OPENPIC_MASK;
}
static void openpic2_cached_disable_irq(u_int irq)
{
check_arg_irq(irq);
save_irq_src_vp[irq - open_pic2_irq_offset] |= OPENPIC_MASK;
}
/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
* we need something better to deal with that... Maybe switch to S1 for
* cpufreq changes
*/
int openpic2_suspend(struct sys_device *sysdev, u32 state)
{
int i;
unsigned long flags;
spin_lock_irqsave(&openpic2_setup_lock, flags);
if (openpic_suspend_count++ > 0) {
spin_unlock_irqrestore(&openpic2_setup_lock, flags);
return 0;
}
open_pic2.enable = openpic2_cached_enable_irq;
open_pic2.disable = openpic2_cached_disable_irq;
for (i=0; i<NumProcessors; i++) {
save_cpu_task_pri[i] = openpic2_read(&OpenPIC2->Processor[i].Current_Task_Priority);
openpic2_writefield(&OpenPIC2->Processor[i].Current_Task_Priority,
OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf);
}
for (i=0; i<OPENPIC_NUM_IPI; i++)
save_ipi_vp[i] = openpic2_read(&OpenPIC2->Global.IPI_Vector_Priority(i));
for (i=0; i<NumSources; i++) {
if (ISR[i] == 0)
continue;
save_irq_src_vp[i] = openpic2_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY;
save_irq_src_dest[i] = openpic2_read(&ISR[i]->Destination);
}
spin_unlock_irqrestore(&openpic2_setup_lock, flags);
return 0;
}
/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
* we need something better to deal with that... Maybe switch to S1 for
* cpufreq changes
*/
int openpic2_resume(struct sys_device *sysdev)
{
int i;
unsigned long flags;
u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK |
OPENPIC_MASK;
spin_lock_irqsave(&openpic2_setup_lock, flags);
if ((--openpic_suspend_count) > 0) {
spin_unlock_irqrestore(&openpic2_setup_lock, flags);
return 0;
}
openpic2_reset();
/* OpenPIC sometimes seem to need some time to be fully back up... */
do {
openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+open_pic2_irq_offset);
} while(openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK)
!= (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset));
openpic2_disable_8259_pass_through();
for (i=0; i<OPENPIC_NUM_IPI; i++)
openpic2_write(&OpenPIC2->Global.IPI_Vector_Priority(i),
save_ipi_vp[i]);
for (i=0; i<NumSources; i++) {
if (ISR[i] == 0)
continue;
openpic2_write(&ISR[i]->Destination, save_irq_src_dest[i]);
openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
/* make sure mask gets to controller before we return to user */
do {
openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
} while (openpic2_readfield(&ISR[i]->Vector_Priority, vppmask)
!= (save_irq_src_vp[i] & vppmask));
}
for (i=0; i<NumProcessors; i++)
openpic2_write(&OpenPIC2->Processor[i].Current_Task_Priority,
save_cpu_task_pri[i]);
open_pic2.enable = openpic2_enable_irq;
open_pic2.disable = openpic2_disable_irq;
spin_unlock_irqrestore(&openpic2_setup_lock, flags);
return 0;
}
#endif /* CONFIG_PM */
/* HACK ALERT */
static struct sysdev_class openpic2_sysclass = {
set_kset_name("openpic2"),
};
static struct sys_device device_openpic2 = {
.id = 0,
.cls = &openpic2_sysclass,
};
static struct sysdev_driver driver_openpic2 = {
#ifdef CONFIG_PM
.suspend = &openpic2_suspend,
.resume = &openpic2_resume,
#endif /* CONFIG_PM */
};
static int __init init_openpic2_sysfs(void)
{
int rc;
if (!OpenPIC2_Addr)
return -ENODEV;
printk(KERN_DEBUG "Registering openpic2 with sysfs...\n");
rc = sysdev_class_register(&openpic2_sysclass);
if (rc) {
printk(KERN_ERR "Failed registering openpic sys class\n");
return -ENODEV;
}
rc = sysdev_register(&device_openpic2);
if (rc) {
printk(KERN_ERR "Failed registering openpic sys device\n");
return -ENODEV;
}
rc = sysdev_driver_register(&openpic2_sysclass, &driver_openpic2);
if (rc) {
printk(KERN_ERR "Failed registering openpic sys driver\n");
return -ENODEV;
}
return 0;
}
subsys_initcall(init_openpic2_sysfs);

View File

@@ -0,0 +1,292 @@
/*
* arch/ppc/kernel/open_pic_defs.h -- OpenPIC definitions
*
* Copyright (C) 1997 Geert Uytterhoeven
*
* This file is based on the following documentation:
*
* The Open Programmable Interrupt Controller (PIC)
* Register Interface Specification Revision 1.2
*
* Issue Date: October 1995
*
* Issued jointly by Advanced Micro Devices and Cyrix Corporation
*
* AMD is a registered trademark of Advanced Micro Devices, Inc.
* Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc.
* All Rights Reserved.
*
* To receive a copy of this documentation, send an email to openpic@amd.com.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#ifndef _LINUX_OPENPIC_H
#define _LINUX_OPENPIC_H
#ifdef __KERNEL__
/*
* OpenPIC supports up to 2048 interrupt sources and up to 32 processors
*/
#define OPENPIC_MAX_SOURCES 2048
#define OPENPIC_MAX_PROCESSORS 32
#define OPENPIC_MAX_ISU 16
#define OPENPIC_NUM_TIMERS 4
#define OPENPIC_NUM_IPI 4
#define OPENPIC_NUM_PRI 16
#define OPENPIC_NUM_VECTORS 256
/*
* OpenPIC Registers are 32 bits and aligned on 128 bit boundaries
*/
typedef struct _OpenPIC_Reg {
u_int Reg; /* Little endian! */
char Pad[0xc];
} OpenPIC_Reg;
/*
* Per Processor Registers
*/
typedef struct _OpenPIC_Processor {
/*
* Private Shadow Registers (for SLiC backwards compatibility)
*/
u_int IPI0_Dispatch_Shadow; /* Write Only */
char Pad1[0x4];
u_int IPI0_Vector_Priority_Shadow; /* Read/Write */
char Pad2[0x34];
/*
* Interprocessor Interrupt Command Ports
*/
OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */
/*
* Current Task Priority Register
*/
OpenPIC_Reg _Current_Task_Priority; /* Read/Write */
char Pad3[0x10];
/*
* Interrupt Acknowledge Register
*/
OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */
/*
* End of Interrupt (EOI) Register
*/
OpenPIC_Reg _EOI; /* Read/Write */
char Pad5[0xf40];
} OpenPIC_Processor;
/*
* Timer Registers
*/
typedef struct _OpenPIC_Timer {
OpenPIC_Reg _Current_Count; /* Read Only */
OpenPIC_Reg _Base_Count; /* Read/Write */
OpenPIC_Reg _Vector_Priority; /* Read/Write */
OpenPIC_Reg _Destination; /* Read/Write */
} OpenPIC_Timer;
/*
* Global Registers
*/
typedef struct _OpenPIC_Global {
/*
* Feature Reporting Registers
*/
OpenPIC_Reg _Feature_Reporting0; /* Read Only */
OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */
/*
* Global Configuration Registers
*/
OpenPIC_Reg _Global_Configuration0; /* Read/Write */
OpenPIC_Reg _Global_Configuration1; /* Future Expansion */
/*
* Vendor Specific Registers
*/
OpenPIC_Reg _Vendor_Specific[4];
/*
* Vendor Identification Register
*/
OpenPIC_Reg _Vendor_Identification; /* Read Only */
/*
* Processor Initialization Register
*/
OpenPIC_Reg _Processor_Initialization; /* Read/Write */
/*
* IPI Vector/Priority Registers
*/
OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */
/*
* Spurious Vector Register
*/
OpenPIC_Reg _Spurious_Vector; /* Read/Write */
/*
* Global Timer Registers
*/
OpenPIC_Reg _Timer_Frequency; /* Read/Write */
OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS];
char Pad1[0xee00];
} OpenPIC_Global;
/*
* Interrupt Source Registers
*/
typedef struct _OpenPIC_Source {
OpenPIC_Reg _Vector_Priority; /* Read/Write */
OpenPIC_Reg _Destination; /* Read/Write */
} OpenPIC_Source, *OpenPIC_SourcePtr;
/*
* OpenPIC Register Map
*/
struct OpenPIC {
char Pad1[0x1000];
/*
* Global Registers
*/
OpenPIC_Global Global;
/*
* Interrupt Source Configuration Registers
*/
OpenPIC_Source Source[OPENPIC_MAX_SOURCES];
/*
* Per Processor Registers
*/
OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS];
};
extern volatile struct OpenPIC __iomem *OpenPIC;
/*
* Current Task Priority Register
*/
#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f
/*
* Who Am I Register
*/
#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f
/*
* Feature Reporting Register 0
*/
#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000
#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16
#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00
#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8
#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff
/*
* Global Configuration Register 0
*/
#define OPENPIC_CONFIG_RESET 0x80000000
#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000
#define OPENPIC_CONFIG_BASE_MASK 0x000fffff
/*
* Global Configuration Register 1
* This is the EICR on EPICs.
*/
#define OPENPIC_EICR_S_CLK_MASK 0x70000000
#define OPENPIC_EICR_SIE 0x08000000
/*
* Vendor Identification Register
*/
#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000
#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16
#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00
#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8
#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff
/*
* Vector/Priority Registers
*/
#define OPENPIC_MASK 0x80000000
#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */
#define OPENPIC_PRIORITY_MASK 0x000f0000
#define OPENPIC_PRIORITY_SHIFT 16
#define OPENPIC_VECTOR_MASK 0x000000ff
/*
* Interrupt Source Registers
*/
#define OPENPIC_POLARITY_POSITIVE 0x00800000
#define OPENPIC_POLARITY_NEGATIVE 0x00000000
#define OPENPIC_POLARITY_MASK 0x00800000
#define OPENPIC_SENSE_LEVEL 0x00400000
#define OPENPIC_SENSE_EDGE 0x00000000
#define OPENPIC_SENSE_MASK 0x00400000
/*
* Timer Registers
*/
#define OPENPIC_COUNT_MASK 0x7fffffff
#define OPENPIC_TIMER_TOGGLE 0x80000000
#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000
/*
* Aliases to make life simpler
*/
/* Per Processor Registers */
#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg
#define Current_Task_Priority _Current_Task_Priority.Reg
#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg
#define EOI _EOI.Reg
/* Global Registers */
#define Feature_Reporting0 _Feature_Reporting0.Reg
#define Feature_Reporting1 _Feature_Reporting1.Reg
#define Global_Configuration0 _Global_Configuration0.Reg
#define Global_Configuration1 _Global_Configuration1.Reg
#define Vendor_Specific(i) _Vendor_Specific[i].Reg
#define Vendor_Identification _Vendor_Identification.Reg
#define Processor_Initialization _Processor_Initialization.Reg
#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg
#define Spurious_Vector _Spurious_Vector.Reg
#define Timer_Frequency _Timer_Frequency.Reg
/* Timer Registers */
#define Current_Count _Current_Count.Reg
#define Base_Count _Base_Count.Reg
#define Vector_Priority _Vector_Priority.Reg
#define Destination _Destination.Reg
/* Interrupt Source Registers */
#define Vector_Priority _Vector_Priority.Reg
#define Destination _Destination.Reg
#endif /* __KERNEL__ */
#endif /* _LINUX_OPENPIC_H */

517
arch/ppc/syslib/pci_auto.c Normal file
View File

@@ -0,0 +1,517 @@
/*
* arch/ppc/syslib/pci_auto.c
*
* PCI autoconfiguration library
*
* Author: Matt Porter <mporter@mvista.com>
*
* 2001 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
* The CardBus support is very preliminary. Preallocating space is
* the way to go but will require some change in card services to
* make it useful. Eventually this will ensure that we can put
* multiple CB bridges behind multiple P2P bridges. For now, at
* least it ensures that we place the CB bridge BAR and assigned
* initial bus numbers. I definitely need to do something about
* the lack of 16-bit I/O support. -MDP
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#define PCIAUTO_IDE_MODE_MASK 0x05
#undef DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif /* DEBUG */
static int pciauto_upper_iospc;
static int pciauto_upper_memspc;
void __init pciauto_setup_bars(struct pci_controller *hose,
int current_bus,
int pci_devfn,
int bar_limit)
{
int bar_response, bar_size, bar_value;
int bar, addr_mask;
int * upper_limit;
int found_mem64 = 0;
DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n",
current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) );
for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) {
/* Tickle the BAR and get the response */
early_write_config_dword(hose,
current_bus,
pci_devfn,
bar,
0xffffffff);
early_read_config_dword(hose,
current_bus,
pci_devfn,
bar,
&bar_response);
/* If BAR is not implemented go to the next BAR */
if (!bar_response)
continue;
/* Check the BAR type and set our address mask */
if (bar_response & PCI_BASE_ADDRESS_SPACE) {
addr_mask = PCI_BASE_ADDRESS_IO_MASK;
upper_limit = &pciauto_upper_iospc;
DBG("PCI Autoconfig: BAR 0x%x, I/O, ", bar);
} else {
if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
PCI_BASE_ADDRESS_MEM_TYPE_64)
found_mem64 = 1;
addr_mask = PCI_BASE_ADDRESS_MEM_MASK;
upper_limit = &pciauto_upper_memspc;
DBG("PCI Autoconfig: BAR 0x%x, Mem ", bar);
}
/* Calculate requested size */
bar_size = ~(bar_response & addr_mask) + 1;
/* Allocate a base address */
bar_value = (*upper_limit - bar_size) & ~(bar_size - 1);
/* Write it out and update our limit */
early_write_config_dword(hose,
current_bus,
pci_devfn,
bar,
bar_value);
*upper_limit = bar_value;
/*
* If we are a 64-bit decoder then increment to the
* upper 32 bits of the bar and force it to locate
* in the lower 4GB of memory.
*/
if (found_mem64) {
bar += 4;
early_write_config_dword(hose,
current_bus,
pci_devfn,
bar,
0x00000000);
found_mem64 = 0;
}
DBG("size=0x%x, address=0x%x\n",
bar_size, bar_value);
}
}
void __init pciauto_prescan_setup_bridge(struct pci_controller *hose,
int current_bus,
int pci_devfn,
int sub_bus,
int *iosave,
int *memsave)
{
/* Configure bus number registers */
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_PRIMARY_BUS,
current_bus);
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_SECONDARY_BUS,
sub_bus + 1);
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_SUBORDINATE_BUS,
0xff);
/* Round memory allocator to 1MB boundary */
pciauto_upper_memspc &= ~(0x100000 - 1);
*memsave = pciauto_upper_memspc;
/* Round I/O allocator to 4KB boundary */
pciauto_upper_iospc &= ~(0x1000 - 1);
*iosave = pciauto_upper_iospc;
/* Set up memory and I/O filter limits, assume 32-bit I/O space */
early_write_config_word(hose,
current_bus,
pci_devfn,
PCI_MEMORY_LIMIT,
((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_IO_LIMIT,
((pciauto_upper_iospc - 1) & 0x0000f000) >> 8);
early_write_config_word(hose,
current_bus,
pci_devfn,
PCI_IO_LIMIT_UPPER16,
((pciauto_upper_iospc - 1) & 0xffff0000) >> 16);
/* Zero upper 32 bits of prefetchable base/limit */
early_write_config_dword(hose,
current_bus,
pci_devfn,
PCI_PREF_BASE_UPPER32,
0);
early_write_config_dword(hose,
current_bus,
pci_devfn,
PCI_PREF_LIMIT_UPPER32,
0);
}
void __init pciauto_postscan_setup_bridge(struct pci_controller *hose,
int current_bus,
int pci_devfn,
int sub_bus,
int *iosave,
int *memsave)
{
int cmdstat;
/* Configure bus number registers */
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_SUBORDINATE_BUS,
sub_bus);
/*
* Round memory allocator to 1MB boundary.
* If no space used, allocate minimum.
*/
pciauto_upper_memspc &= ~(0x100000 - 1);
if (*memsave == pciauto_upper_memspc)
pciauto_upper_memspc -= 0x00100000;
early_write_config_word(hose,
current_bus,
pci_devfn,
PCI_MEMORY_BASE,
pciauto_upper_memspc >> 16);
/* Allocate 1MB for pre-fretch */
early_write_config_word(hose,
current_bus,
pci_devfn,
PCI_PREF_MEMORY_LIMIT,
((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
pciauto_upper_memspc -= 0x100000;
early_write_config_word(hose,
current_bus,
pci_devfn,
PCI_PREF_MEMORY_BASE,
pciauto_upper_memspc >> 16);
/* Round I/O allocator to 4KB boundary */
pciauto_upper_iospc &= ~(0x1000 - 1);
if (*iosave == pciauto_upper_iospc)
pciauto_upper_iospc -= 0x1000;
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_IO_BASE,
(pciauto_upper_iospc & 0x0000f000) >> 8);
early_write_config_word(hose,
current_bus,
pci_devfn,
PCI_IO_BASE_UPPER16,
pciauto_upper_iospc >> 16);
/* Enable memory and I/O accesses, enable bus master */
early_read_config_dword(hose,
current_bus,
pci_devfn,
PCI_COMMAND,
&cmdstat);
early_write_config_dword(hose,
current_bus,
pci_devfn,
PCI_COMMAND,
cmdstat |
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER);
}
void __init pciauto_prescan_setup_cardbus_bridge(struct pci_controller *hose,
int current_bus,
int pci_devfn,
int sub_bus,
int *iosave,
int *memsave)
{
/* Configure bus number registers */
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_PRIMARY_BUS,
current_bus);
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_SECONDARY_BUS,
sub_bus + 1);
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_SUBORDINATE_BUS,
0xff);
/* Round memory allocator to 4KB boundary */
pciauto_upper_memspc &= ~(0x1000 - 1);
*memsave = pciauto_upper_memspc;
/* Round I/O allocator to 4 byte boundary */
pciauto_upper_iospc &= ~(0x4 - 1);
*iosave = pciauto_upper_iospc;
/* Set up memory and I/O filter limits, assume 32-bit I/O space */
early_write_config_dword(hose,
current_bus,
pci_devfn,
0x20,
pciauto_upper_memspc - 1);
early_write_config_dword(hose,
current_bus,
pci_devfn,
0x30,
pciauto_upper_iospc - 1);
}
void __init pciauto_postscan_setup_cardbus_bridge(struct pci_controller *hose,
int current_bus,
int pci_devfn,
int sub_bus,
int *iosave,
int *memsave)
{
int cmdstat;
/*
* Configure subordinate bus number. The PCI subsystem
* bus scan will renumber buses (reserving three additional
* for this PCI<->CardBus bridge for the case where a CardBus
* adapter contains a P2P or CB2CB bridge.
*/
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_SUBORDINATE_BUS,
sub_bus);
/*
* Reserve an additional 4MB for mem space and 16KB for
* I/O space. This should cover any additional space
* requirement of unusual CardBus devices with
* additional bridges that can consume more address space.
*
* Although pcmcia-cs currently will reprogram bridge
* windows, the goal is to add an option to leave them
* alone and use the bridge window ranges as the regions
* that are searched for free resources upon hot-insertion
* of a device. This will allow a PCI<->CardBus bridge
* configured by this routine to happily live behind a
* P2P bridge in a system.
*/
pciauto_upper_memspc -= 0x00400000;
pciauto_upper_iospc -= 0x00004000;
/* Round memory allocator to 4KB boundary */
pciauto_upper_memspc &= ~(0x1000 - 1);
early_write_config_dword(hose,
current_bus,
pci_devfn,
0x1c,
pciauto_upper_memspc);
/* Round I/O allocator to 4 byte boundary */
pciauto_upper_iospc &= ~(0x4 - 1);
early_write_config_dword(hose,
current_bus,
pci_devfn,
0x2c,
pciauto_upper_iospc);
/* Enable memory and I/O accesses, enable bus master */
early_read_config_dword(hose,
current_bus,
pci_devfn,
PCI_COMMAND,
&cmdstat);
early_write_config_dword(hose,
current_bus,
pci_devfn,
PCI_COMMAND,
cmdstat |
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER);
}
int __init pciauto_bus_scan(struct pci_controller *hose, int current_bus)
{
int sub_bus, pci_devfn, pci_class, cmdstat, found_multi = 0;
unsigned short vid;
unsigned char header_type;
/*
* Fetch our I/O and memory space upper boundaries used
* to allocated base addresses on this hose.
*/
if (current_bus == hose->first_busno) {
pciauto_upper_iospc = hose->io_space.end + 1;
pciauto_upper_memspc = hose->mem_space.end + 1;
}
sub_bus = current_bus;
for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
/* Skip our host bridge */
if ( (current_bus == hose->first_busno) && (pci_devfn == 0) )
continue;
if (PCI_FUNC(pci_devfn) && !found_multi)
continue;
/* If config space read fails from this device, move on */
if (early_read_config_byte(hose,
current_bus,
pci_devfn,
PCI_HEADER_TYPE,
&header_type))
continue;
if (!PCI_FUNC(pci_devfn))
found_multi = header_type & 0x80;
early_read_config_word(hose,
current_bus,
pci_devfn,
PCI_VENDOR_ID,
&vid);
if (vid != 0xffff) {
early_read_config_dword(hose,
current_bus,
pci_devfn,
PCI_CLASS_REVISION, &pci_class);
if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) {
int iosave, memsave;
DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn));
/* Allocate PCI I/O and/or memory space */
pciauto_setup_bars(hose,
current_bus,
pci_devfn,
PCI_BASE_ADDRESS_1);
pciauto_prescan_setup_bridge(hose,
current_bus,
pci_devfn,
sub_bus,
&iosave,
&memsave);
sub_bus = pciauto_bus_scan(hose, sub_bus+1);
pciauto_postscan_setup_bridge(hose,
current_bus,
pci_devfn,
sub_bus,
&iosave,
&memsave);
} else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) {
int iosave, memsave;
DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn));
/* Place CardBus Socket/ExCA registers */
pciauto_setup_bars(hose,
current_bus,
pci_devfn,
PCI_BASE_ADDRESS_0);
pciauto_prescan_setup_cardbus_bridge(hose,
current_bus,
pci_devfn,
sub_bus,
&iosave,
&memsave);
sub_bus = pciauto_bus_scan(hose, sub_bus+1);
pciauto_postscan_setup_cardbus_bridge(hose,
current_bus,
pci_devfn,
sub_bus,
&iosave,
&memsave);
} else {
if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
unsigned char prg_iface;
early_read_config_byte(hose,
current_bus,
pci_devfn,
PCI_CLASS_PROG,
&prg_iface);
if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n");
continue;
}
}
/* Allocate PCI I/O and/or memory space */
pciauto_setup_bars(hose,
current_bus,
pci_devfn,
PCI_BASE_ADDRESS_5);
/*
* Enable some standard settings
*/
early_read_config_dword(hose,
current_bus,
pci_devfn,
PCI_COMMAND,
&cmdstat);
early_write_config_dword(hose,
current_bus,
pci_devfn,
PCI_COMMAND,
cmdstat |
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER);
early_write_config_byte(hose,
current_bus,
pci_devfn,
PCI_LATENCY_TIMER,
0x80);
}
}
}
return sub_bus;
}

View File

@@ -0,0 +1,127 @@
/*
*
* Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
*
* Module name: ppc403_pic.c
*
* Description:
* Interrupt controller driver for PowerPC 403-based processors.
*/
/*
* The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has
* 32 possible interrupts, a majority of which are not implemented on
* all cores. There are six configurable, external interrupt pins and
* there are eight internal interrupts for the on-chip serial port
* (SPU), DMA controller, and JTAG controller.
*
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/stddef.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/ppc4xx_pic.h>
/* Function Prototypes */
static void ppc403_aic_enable(unsigned int irq);
static void ppc403_aic_disable(unsigned int irq);
static void ppc403_aic_disable_and_ack(unsigned int irq);
static struct hw_interrupt_type ppc403_aic = {
"403GC AIC",
NULL,
NULL,
ppc403_aic_enable,
ppc403_aic_disable,
ppc403_aic_disable_and_ack,
0
};
int
ppc403_pic_get_irq(struct pt_regs *regs)
{
int irq;
unsigned long bits;
/*
* Only report the status of those interrupts that are actually
* enabled.
*/
bits = mfdcr(DCRN_EXISR) & mfdcr(DCRN_EXIER);
/*
* Walk through the interrupts from highest priority to lowest, and
* report the first pending interrupt found.
* We want PPC, not C bit numbering, so just subtract the ffs()
* result from 32.
*/
irq = 32 - ffs(bits);
if (irq == NR_AIC_IRQS)
irq = -1;
return (irq);
}
static void
ppc403_aic_enable(unsigned int irq)
{
int bit, word;
bit = irq & 0x1f;
word = irq >> 5;
ppc_cached_irq_mask[word] |= (1 << (31 - bit));
mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
}
static void
ppc403_aic_disable(unsigned int irq)
{
int bit, word;
bit = irq & 0x1f;
word = irq >> 5;
ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
}
static void
ppc403_aic_disable_and_ack(unsigned int irq)
{
int bit, word;
bit = irq & 0x1f;
word = irq >> 5;
ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
mtdcr(DCRN_EXISR, (1 << (31 - bit)));
}
void __init
ppc4xx_pic_init(void)
{
int i;
/*
* Disable all external interrupts until they are
* explicity requested.
*/
ppc_cached_irq_mask[0] = 0;
mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]);
ppc_md.get_irq = ppc403_pic_get_irq;
for (i = 0; i < NR_IRQS; i++)
irq_desc[i].handler = &ppc403_aic;
}

View File

@@ -0,0 +1,177 @@
/*
* Authors: Frank Rowand <frank_rowand@mvista.com>,
* Debbie Chu <debbie_chu@mvista.com>, or source@mvista.com
* Further modifications by Armin Kuster <akuster@mvista.com>
*
* 2000 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*
* Based on arch/ppc/kernel/indirect.c, Copyright (C) 1998 Gabriel Paubert.
*/
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/machdep.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <asm/ocp.h>
#include <asm/ibm4xx.h>
#include <asm/pci-bridge.h>
#include <asm/ibm_ocp_pci.h>
extern void bios_fixup(struct pci_controller *, struct pcil0_regs *);
extern int ppc405_map_irq(struct pci_dev *dev, unsigned char idsel,
unsigned char pin);
void
ppc405_pcibios_fixup_resources(struct pci_dev *dev)
{
int i;
unsigned long max_host_addr;
unsigned long min_host_addr;
struct resource *res;
/*
* openbios puts some graphics cards in the same range as the host
* controller uses to map to SDRAM. Fix it.
*/
min_host_addr = 0;
max_host_addr = PPC405_PCI_MEM_BASE - 1;
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
res = dev->resource + i;
if (!res->start)
continue;
if ((res->flags & IORESOURCE_MEM) &&
(((res->start >= min_host_addr)
&& (res->start <= max_host_addr))
|| ((res->end >= min_host_addr)
&& (res->end <= max_host_addr))
|| ((res->start < min_host_addr)
&& (res->end > max_host_addr))
)
) {
/* force pcibios_assign_resources() to assign a new address */
res->end -= res->start;
res->start = 0;
}
}
}
static int
ppc4xx_exclude_device(unsigned char bus, unsigned char devfn)
{
/* We prevent us from seeing ourselves to avoid having
* the kernel try to remap our BAR #1 and fuck up bus
* master from external PCI devices
*/
return (bus == 0 && devfn == 0);
}
void
ppc4xx_find_bridges(void)
{
struct pci_controller *hose_a;
struct pcil0_regs *pcip;
unsigned int tmp_addr;
unsigned int tmp_size;
unsigned int reg_index;
unsigned int new_pmm_max = 0;
unsigned int new_pmm_min = 0;
isa_io_base = 0;
isa_mem_base = 0;
pci_dram_offset = 0;
#if (PSR_PCI_ARBIT_EN > 1)
/* Check if running in slave mode */
if ((mfdcr(DCRN_CHPSR) & PSR_PCI_ARBIT_EN) == 0) {
printk("Running as PCI slave, kernel PCI disabled !\n");
return;
}
#endif
/* Setup PCI32 hose */
hose_a = pcibios_alloc_controller();
if (!hose_a)
return;
setup_indirect_pci(hose_a, PPC405_PCI_CONFIG_ADDR,
PPC405_PCI_CONFIG_DATA);
pcip = ioremap(PPC4xx_PCI_LCFG_PADDR, PAGE_SIZE);
if (pcip != NULL) {
#if defined(CONFIG_BIOS_FIXUP)
bios_fixup(hose_a, pcip);
#endif
new_pmm_min = 0xffffffff;
for (reg_index = 0; reg_index < 3; reg_index++) {
tmp_size = in_le32(&pcip->pmm[reg_index].ma); // mask & attrs
/* test the enable bit */
if ((tmp_size & 0x1) == 0)
continue;
tmp_addr = in_le32(&pcip->pmm[reg_index].pcila); // PCI addr
if (tmp_addr < PPC405_PCI_PHY_MEM_BASE) {
printk(KERN_DEBUG
"Disabling mapping to PCI mem addr 0x%8.8x\n",
tmp_addr);
out_le32(&pcip->pmm[reg_index].ma, tmp_size & ~1); // *_PMMOMA
continue;
}
tmp_addr = in_le32(&pcip->pmm[reg_index].la); // *_PMMOLA
if (tmp_addr < new_pmm_min)
new_pmm_min = tmp_addr;
tmp_addr = tmp_addr +
(0xffffffff - (tmp_size & 0xffffc000));
if (tmp_addr > PPC405_PCI_UPPER_MEM) {
new_pmm_max = tmp_addr; // PPC405_PCI_UPPER_MEM
} else {
new_pmm_max = PPC405_PCI_UPPER_MEM;
}
} // for
iounmap(pcip);
}
hose_a->first_busno = 0;
hose_a->last_busno = 0xff;
hose_a->pci_mem_offset = 0;
/* Setup bridge memory/IO ranges & resources
* TODO: Handle firmwares setting up a legacy ISA mem base
*/
hose_a->io_space.start = PPC405_PCI_LOWER_IO;
hose_a->io_space.end = PPC405_PCI_UPPER_IO;
hose_a->mem_space.start = new_pmm_min;
hose_a->mem_space.end = new_pmm_max;
hose_a->io_base_phys = PPC405_PCI_PHY_IO_BASE;
hose_a->io_base_virt = ioremap(hose_a->io_base_phys, 0x10000);
hose_a->io_resource.start = 0;
hose_a->io_resource.end = PPC405_PCI_UPPER_IO - PPC405_PCI_LOWER_IO;
hose_a->io_resource.flags = IORESOURCE_IO;
hose_a->io_resource.name = "PCI I/O";
hose_a->mem_resources[0].start = new_pmm_min;
hose_a->mem_resources[0].end = new_pmm_max;
hose_a->mem_resources[0].flags = IORESOURCE_MEM;
hose_a->mem_resources[0].name = "PCI Memory";
isa_io_base = (int) hose_a->io_base_virt;
isa_mem_base = 0; /* ISA not implemented */
ISA_DMA_THRESHOLD = 0x00ffffff; /* ??? ISA not implemented */
/* Scan busses & initial setup by pci_auto */
hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno);
hose_a->last_busno = 0;
/* Setup ppc_md */
ppc_md.pcibios_fixup = NULL;
ppc_md.pci_exclude_device = ppc4xx_exclude_device;
ppc_md.pcibios_fixup_resources = ppc405_pcibios_fixup_resources;
ppc_md.pci_swizzle = common_swizzle;
ppc_md.pci_map_irq = ppc405_map_irq;
}

View File

@@ -0,0 +1,708 @@
/*
* arch/ppc/kernel/ppc4xx_dma.c
*
* IBM PPC4xx DMA engine core library
*
* Copyright 2000-2004 MontaVista Software Inc.
*
* Cleaned up and converted to new DCR access
* Matt Porter <mporter@kernel.crashing.org>
*
* Original code by Armin Kuster <akuster@mvista.com>
* and Pete Popov <ppopov@mvista.com>
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/ppc4xx_dma.h>
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
int
ppc4xx_get_dma_status(void)
{
return (mfdcr(DCRN_DMASR));
}
void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_src_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}
void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dst_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}
void
ppc4xx_enable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};
if (p_dma_ch->in_use) {
printk("enable_dma: channel %d in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("enable_dma: bad channel: %d\n", dmanr);
return;
}
if (p_dma_ch->mode == DMA_MODE_READ) {
/* peripheral to memory */
ppc4xx_set_src_addr(dmanr, 0);
ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
/* memory to peripheral */
ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
ppc4xx_set_dst_addr(dmanr, 0);
}
/* for other xfer modes, the addresses are already set */
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */
if (p_dma_ch->mode == DMA_MODE_MM) {
/* software initiated memory to memory */
control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
}
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/*
* Clear the CS, TS, RI bits for the channel from DMASR. This
* has been observed to happen correctly only after the mode and
* ETD/DCE bits in DMACRx are set above. Must do this before
* enabling the channel.
*/
mtdcr(DCRN_DMASR, status_bits[dmanr]);
/*
* For device-paced transfers, Terminal Count Enable apparently
* must be on, and this must be turned on after the mode, etc.
* bits are cleared above (at least on Redwood-6).
*/
if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
(p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
control |= DMA_TCE_ENABLE;
/*
* Now enable the channel.
*/
control |= (p_dma_ch->mode | DMA_CE_ENABLE);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 1;
}
void
ppc4xx_disable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (!p_dma_ch->in_use) {
printk("disable_dma: channel %d not in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("disable_dma: bad channel: %d\n", dmanr);
return;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CE_ENABLE;
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 0;
}
/*
* Sets the dma mode for single DMA transfers only.
* For scatter/gather transfers, the mode is passed to the
* alloc_dma_handle() function as one of the parameters.
*
* The mode is simply saved and used later. This allows
* the driver to call set_dma_mode() and set_dma_addr() in
* any order.
*
* Valid mode values are:
*
* DMA_MODE_READ peripheral to memory
* DMA_MODE_WRITE memory to peripheral
* DMA_MODE_MM memory to memory
* DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src
* DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst
*/
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dma_mode: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->mode = mode;
return DMA_STATUS_GOOD;
}
/*
* Sets the DMA Count register. Note that 'count' is in bytes.
* However, the DMA Count register counts the number of "transfers",
* where each transfer is equal to the bus width. Thus, count
* MUST be a multiple of the bus width.
*/
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (count & 0x1)
error = 1;
break;
case PW_32:
if (count & 0x3)
error = 1;
break;
case PW_64:
if (count & 0x7)
error = 1;
break;
default:
printk("set_dma_count: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: set_dma_count count 0x%x bus width %d\n",
count, p_dma_ch->pwidth);
}
#endif
count = count >> p_dma_ch->shift;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}
/*
* Returns the number of bytes left to be transfered.
* After a DMA transfer, this should return zero.
* Reading this while a DMA transfer is still in progress will return
* unpredictable results.
*/
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
unsigned int count;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));
return (count << p_dma_ch->shift);
}
/*
* Sets the DMA address for a memory to peripheral or peripheral
* to memory transfer. The address is just saved in the channel
* structure for now and used later in enable_dma().
*/
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if ((unsigned) addr & 0x1)
error = 1;
break;
case PW_32:
if ((unsigned) addr & 0x3)
error = 1;
break;
case PW_64:
if ((unsigned) addr & 0x7)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
addr, p_dma_ch->pwidth);
}
#endif
/* save dma address and program it later after we know the xfer mode */
p_dma_ch->addr = addr;
}
/*
* Sets both DMA addresses for a memory to memory transfer.
* For memory to peripheral or peripheral to memory transfers
* the function set_dma_addr() should be used instead.
*/
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
phys_addr_t dst_dma_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (((unsigned) src_dma_addr & 0x1) ||
((unsigned) dst_dma_addr & 0x1)
)
error = 1;
break;
case PW_32:
if (((unsigned) src_dma_addr & 0x3) ||
((unsigned) dst_dma_addr & 0x3)
)
error = 1;
break;
case PW_64:
if (((unsigned) src_dma_addr & 0x7) ||
((unsigned) dst_dma_addr & 0x7)
)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
}
#endif
ppc4xx_set_src_addr(dmanr, src_dma_addr);
ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}
/*
* Enables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be enabled, if
* they were previously disabled.
*/
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 1;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Disables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be disabled, if
* they were previously enabled.
*/
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 0;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Configures a DMA channel, including the peripheral bus width, if a
* peripheral is attached to the channel, the polarity of the DMAReq and
* DMAAck signals, etc. This information should really be setup by the boot
* code, since most likely the configuration won't change dynamically.
* If the kernel has to call this function, it's recommended that it's
* called from platform specific init code. The driver should not need to
* call this function.
*/
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
unsigned int polarity;
uint32_t control = 0;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */
DMA_MODE_WRITE = 0; /* Memory to Peripheral */
if (!p_init) {
printk("ppc4xx_init_dma_channel: NULL p_init\n");
return DMA_STATUS_NULL_POINTER;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
/* Setup the control register based on the values passed to
* us in p_init. Then, over-write the control register with this
* new value.
*/
control |= SET_DMA_CONTROL;
/* clear all polarity signals and then "or" in new signal levels */
polarity &= ~GET_DMA_POLARITY(dmanr);
polarity |= p_init->polarity;
#if DCRN_POL > 0
mtdcr(DCRN_POL, polarity);
#endif
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/* save these values in our dma channel structure */
memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));
/*
* The peripheral width values written in the control register are:
* PW_8 0
* PW_16 1
* PW_32 2
* PW_64 3
*
* Since the DMA count register takes the number of "transfers",
* we need to divide the count sent to us in certain
* functions by the appropriate number. It so happens that our
* right shift value is equal to the peripheral width value.
*/
p_dma_ch->shift = p_init->pwidth;
/*
* Save the control word for easy access.
*/
p_dma_ch->control = control;
mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */
return DMA_STATUS_GOOD;
}
/*
* This function returns the channel configuration.
*/
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
unsigned int polarity;
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
p_dma_ch->cp = GET_DMA_PRIORITY(control);
p_dma_ch->pwidth = GET_DMA_PW(control);
p_dma_ch->psc = GET_DMA_PSC(control);
p_dma_ch->pwc = GET_DMA_PWC(control);
p_dma_ch->phc = GET_DMA_PHC(control);
p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
p_dma_ch->shift = GET_DMA_PW(control);
#ifdef CONFIG_PPC4xx_EDMA
p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
p_dma_ch->ch_enable = GET_DMA_CH(control);
p_dma_ch->ece_enable = GET_DMA_ECE(control);
p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
return DMA_STATUS_GOOD;
}
/*
* Sets the priority for the DMA channel dmanr.
* Since this is setup by the hardware init function, this function
* can be used to dynamically change the priority of a channel.
*
* Acceptable priorities:
*
* PRIORITY_LOW
* PRIORITY_MID_LOW
* PRIORITY_MID_HIGH
* PRIORITY_HIGH
*
*/
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if ((priority != PRIORITY_LOW) &&
(priority != PRIORITY_MID_LOW) &&
(priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= SET_DMA_PRIORITY(priority);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Returns the width of the peripheral attached to this channel. This assumes
* that someone who knows the hardware configuration, boot code or some other
* init code, already set the width.
*
* The return value is one of:
* PW_8
* PW_16
* PW_32
* PW_64
*
* The function returns 0 on error.
*/
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
return (GET_DMA_PW(control));
}
/*
* Clears the channel status bits
*/
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
return DMA_STATUS_GOOD;
}
/*
* Enables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_enable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Disables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_disable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Sets the burst size (number of peripheral widths) for the channel
* (BSIZ bits in the control/count register))
* must be one of:
* DMA_CTC_BSIZ_2
* DMA_CTC_BSIZ_4
* DMA_CTC_BSIZ_8
* DMA_CTC_BSIZ_16
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
ctc |= (bsize & DMA_CTC_BSIZ_MSK);
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);
EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);

View File

@@ -0,0 +1,124 @@
#include <linux/config.h>
#include <linux/types.h>
#include <asm/ibm4xx.h>
#include <linux/kernel.h>
#define LSR_DR 0x01 /* Data ready */
#define LSR_OE 0x02 /* Overrun */
#define LSR_PE 0x04 /* Parity error */
#define LSR_FE 0x08 /* Framing error */
#define LSR_BI 0x10 /* Break */
#define LSR_THRE 0x20 /* Xmit holding register empty */
#define LSR_TEMT 0x40 /* Xmitter empty */
#define LSR_ERR 0x80 /* Error */
#include <platforms/4xx/ibm_ocp.h>
extern struct NS16550* COM_PORTS[];
#ifndef NULL
#define NULL 0x00
#endif
static volatile struct NS16550 *kgdb_debugport = NULL;
volatile struct NS16550 *
NS16550_init(int chan)
{
volatile struct NS16550 *com_port;
int quot;
#ifdef BASE_BAUD
quot = BASE_BAUD / 9600;
#else
quot = 0x000c; /* 0xc = 9600 baud (on a pc) */
#endif
com_port = (struct NS16550 *) COM_PORTS[chan];
com_port->lcr = 0x00;
com_port->ier = 0xFF;
com_port->ier = 0x00;
com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */
com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */
com_port->dlm = ( quot & 0xff00 ) >> 8;
com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */
com_port->mcr = 0x00; /* RTS/DTR */
com_port->fcr = 0x07; /* Clear & enable FIFOs */
return( com_port );
}
void
NS16550_putc(volatile struct NS16550 *com_port, unsigned char c)
{
while ((com_port->lsr & LSR_THRE) == 0)
;
com_port->thr = c;
return;
}
unsigned char
NS16550_getc(volatile struct NS16550 *com_port)
{
while ((com_port->lsr & LSR_DR) == 0)
;
return (com_port->rbr);
}
unsigned char
NS16550_tstc(volatile struct NS16550 *com_port)
{
return ((com_port->lsr & LSR_DR) != 0);
}
#if defined(CONFIG_KGDB_TTYS0)
#define KGDB_PORT 0
#elif defined(CONFIG_KGDB_TTYS1)
#define KGDB_PORT 1
#elif defined(CONFIG_KGDB_TTYS2)
#define KGDB_PORT 2
#elif defined(CONFIG_KGDB_TTYS3)
#define KGDB_PORT 3
#else
#error "invalid kgdb_tty port"
#endif
void putDebugChar( unsigned char c )
{
if ( kgdb_debugport == NULL )
kgdb_debugport = NS16550_init(KGDB_PORT);
NS16550_putc( kgdb_debugport, c );
}
int getDebugChar( void )
{
if (kgdb_debugport == NULL)
kgdb_debugport = NS16550_init(KGDB_PORT);
return(NS16550_getc(kgdb_debugport));
}
void kgdb_interruptible(int enable)
{
return;
}
void putDebugString(char* str)
{
while (*str != '\0') {
putDebugChar(*str);
str++;
}
putDebugChar('\r');
return;
}
void
kgdb_map_scc(void)
{
printk("kgdb init \n");
kgdb_debugport = NS16550_init(KGDB_PORT);
}

View File

@@ -0,0 +1,244 @@
/*
* arch/ppc/syslib/ppc4xx_pic.c
*
* Interrupt controller driver for PowerPC 4xx-based processors.
*
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* Copyright (c) 2004, 2005 Zultys Technologies
*
* Based on original code by
* Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
* Armin Custer <akuster@mvista.com>
*
* 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/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/stddef.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/ppc4xx_pic.h>
/* See comment in include/arch-ppc/ppc4xx_pic.h
* for more info about these two variables
*/
extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
__attribute__ ((weak));
extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
#define IRQ_MASK_UIC0(irq) (1 << (31 - (irq)))
#define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f)))
#define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq)
#define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq)
#define UIC_HANDLERS(n) \
static void ppc4xx_uic##n##_enable(unsigned int irq) \
{ \
ppc_cached_irq_mask[n] |= IRQ_MASK_UIC##n(irq); \
mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
} \
\
static void ppc4xx_uic##n##_disable(unsigned int irq) \
{ \
ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \
mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
ACK_UIC##n##_PARENT \
} \
\
static void ppc4xx_uic##n##_ack(unsigned int irq) \
{ \
u32 mask = IRQ_MASK_UIC##n(irq); \
ppc_cached_irq_mask[n] &= ~mask; \
mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
mtdcr(DCRN_UIC_SR(UIC##n), mask); \
ACK_UIC##n##_PARENT \
} \
\
static void ppc4xx_uic##n##_end(unsigned int irq) \
{ \
unsigned int status = irq_desc[irq].status; \
u32 mask = IRQ_MASK_UIC##n(irq); \
if (status & IRQ_LEVEL) { \
mtdcr(DCRN_UIC_SR(UIC##n), mask); \
ACK_UIC##n##_PARENT \
} \
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \
ppc_cached_irq_mask[n] |= mask; \
mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
} \
}
#define DECLARE_UIC(n) \
{ \
.typename = "UIC"#n, \
.enable = ppc4xx_uic##n##_enable, \
.disable = ppc4xx_uic##n##_disable, \
.ack = ppc4xx_uic##n##_ack, \
.end = ppc4xx_uic##n##_end, \
} \
#if NR_UICS == 3
#define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
UIC_HANDLERS(0);
UIC_HANDLERS(1);
UIC_HANDLERS(2);
static int ppc4xx_pic_get_irq(struct pt_regs *regs)
{
u32 uicb = mfdcr(DCRN_UIC_MSR(UICB));
if (uicb & UICB_UIC0NC)
return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0)));
else if (uicb & UICB_UIC1NC)
return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
else if (uicb & UICB_UIC2NC)
return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
else
return -1;
}
static void __init ppc4xx_pic_impl_init(void)
{
/* Configure Base UIC */
mtdcr(DCRN_UIC_CR(UICB), 0);
mtdcr(DCRN_UIC_TR(UICB), 0);
mtdcr(DCRN_UIC_PR(UICB), 0xffffffff);
mtdcr(DCRN_UIC_SR(UICB), 0xffffffff);
mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC);
}
#elif NR_UICS == 2
#define ACK_UIC0_PARENT
#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
UIC_HANDLERS(0);
UIC_HANDLERS(1);
static int ppc4xx_pic_get_irq(struct pt_regs *regs)
{
u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
if (uic0 & UIC0_UIC1NC)
return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
else
return uic0 ? 32 - ffs(uic0) : -1;
}
static void __init ppc4xx_pic_impl_init(void)
{
/* Enable cascade interrupt in UIC0 */
ppc_cached_irq_mask[0] |= UIC0_UIC1NC;
mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
}
#elif NR_UICS == 1
#define ACK_UIC0_PARENT
UIC_HANDLERS(0);
static int ppc4xx_pic_get_irq(struct pt_regs *regs)
{
u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
return uic0 ? 32 - ffs(uic0) : -1;
}
static inline void ppc4xx_pic_impl_init(void)
{
}
#endif
static struct ppc4xx_uic_impl {
struct hw_interrupt_type decl;
int base; /* Base DCR number */
} __uic[] = {
{ .decl = DECLARE_UIC(0), .base = UIC0 },
#if NR_UICS > 1
{ .decl = DECLARE_UIC(1), .base = UIC1 },
#if NR_UICS > 2
{ .decl = DECLARE_UIC(2), .base = UIC2 },
#endif
#endif
};
static inline int is_level_sensitive(int irq)
{
u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
return (tr & IRQ_MASK_UICx(irq)) == 0;
}
void __init ppc4xx_pic_init(void)
{
int i;
unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
for (i = 0; i < NR_UICS; ++i) {
int base = __uic[i].base;
/* Disable everything by default */
ppc_cached_irq_mask[i] = 0;
mtdcr(DCRN_UIC_ER(base), 0);
/* We don't use critical interrupts */
mtdcr(DCRN_UIC_CR(base), 0);
/* Configure polarity and triggering */
if (ppc4xx_core_uic_cfg) {
struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i;
u32 mask = p->ext_irq_mask;
u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask;
u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask;
/* "Fixed" interrupts (on-chip devices) */
pr |= p->polarity & ~mask;
tr |= p->triggering & ~mask;
/* Merge external IRQs settings if board port
* provided them
*/
if (eirqs && mask) {
pr &= ~mask;
tr &= ~mask;
while (mask) {
/* Extract current external IRQ mask */
u32 eirq_mask = 1 << __ilog2(mask);
if (!(*eirqs & IRQ_SENSE_LEVEL))
tr |= eirq_mask;
if (*eirqs & IRQ_POLARITY_POSITIVE)
pr |= eirq_mask;
mask &= ~eirq_mask;
++eirqs;
}
}
mtdcr(DCRN_UIC_PR(base), pr);
mtdcr(DCRN_UIC_TR(base), tr);
}
/* ACK any pending interrupts to prevent false
* triggering after first enable
*/
mtdcr(DCRN_UIC_SR(base), 0xffffffff);
}
/* Perform optional implementation specific setup
* (e.g. enable cascade interrupts for multi-UIC configurations)
*/
ppc4xx_pic_impl_init();
/* Attach low-level handlers */
for (i = 0; i < (NR_UICS << 5); ++i) {
irq_desc[i].handler = &__uic[i >> 5].decl;
if (is_level_sensitive(i))
irq_desc[i].status |= IRQ_LEVEL;
}
ppc_md.get_irq = ppc4xx_pic_get_irq;
}

View File

@@ -0,0 +1,47 @@
/*
* Author: Armin Kuster <akuster@mvista.com>
*
* 2002 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*
* This an attempt to get Power Management going for the IBM 4xx processor.
* This was derived from the ppc4xx._setup.c file
*/
#include <linux/config.h>
#include <linux/init.h>
#include <asm/ibm4xx.h>
void __init
ppc4xx_pm_init(void)
{
unsigned int value = 0;
/* turn off unused hardware to save power */
#ifdef CONFIG_405GP
value |= CPM_DCP; /* CodePack */
#endif
#if !defined(CONFIG_IBM_OCP_GPIO)
value |= CPM_GPIO0;
#endif
#if !defined(CONFIG_PPC405_I2C_ADAP)
value |= CPM_IIC0;
#ifdef CONFIG_STB03xxx
value |= CPM_IIC1;
#endif
#endif
#if !defined(CONFIG_405_DMA)
value |= CPM_DMA;
#endif
mtdcr(DCRN_CPMFR, value);
}

View File

@@ -0,0 +1,321 @@
/*
*
* Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
*
* Copyright 2000-2001 MontaVista Software Inc.
* Completed implementation.
* Author: MontaVista Software, Inc. <source@mvista.com>
* Frank Rowand <frank_rowand@mvista.com>
* Debbie Chu <debbie_chu@mvista.com>
* Further modifications by Armin Kuster
*
* Module name: ppc4xx_setup.c
*
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/threads.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/reboot.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/initrd.h>
#include <linux/pci.h>
#include <linux/rtc.h>
#include <linux/console.h>
#include <linux/ide.h>
#include <linux/serial_reg.h>
#include <linux/seq_file.h>
#include <asm/system.h>
#include <asm/processor.h>
#include <asm/machdep.h>
#include <asm/page.h>
#include <asm/kgdb.h>
#include <asm/ibm4xx.h>
#include <asm/time.h>
#include <asm/todc.h>
#include <asm/ppc4xx_pic.h>
#include <asm/pci-bridge.h>
#include <asm/bootinfo.h>
#include <syslib/gen550.h>
/* Function Prototypes */
extern void abort(void);
extern void ppc4xx_find_bridges(void);
extern void ppc4xx_wdt_heartbeat(void);
extern int wdt_enable;
extern unsigned long wdt_period;
/* Global Variables */
bd_t __res;
void __init
ppc4xx_setup_arch(void)
{
#if !defined(CONFIG_BDI_SWITCH)
/*
* The Abatron BDI JTAG debugger does not tolerate others
* mucking with the debug registers.
*/
mtspr(SPRN_DBCR0, (DBCR0_IDM));
mtspr(SPRN_DBSR, 0xffffffff);
#endif
/* Setup PCI host bridges */
#ifdef CONFIG_PCI
ppc4xx_find_bridges();
#endif
}
/*
* This routine pretty-prints the platform's internal CPU clock
* frequencies into the buffer for usage in /proc/cpuinfo.
*/
static int
ppc4xx_show_percpuinfo(struct seq_file *m, int i)
{
seq_printf(m, "clock\t\t: %ldMHz\n", (long)__res.bi_intfreq / 1000000);
return 0;
}
/*
* This routine pretty-prints the platform's internal bus clock
* frequencies into the buffer for usage in /proc/cpuinfo.
*/
static int
ppc4xx_show_cpuinfo(struct seq_file *m)
{
bd_t *bip = &__res;
seq_printf(m, "machine\t\t: %s\n", PPC4xx_MACHINE_NAME);
seq_printf(m, "plb bus clock\t: %ldMHz\n",
(long) bip->bi_busfreq / 1000000);
#ifdef CONFIG_PCI
seq_printf(m, "pci bus clock\t: %dMHz\n",
bip->bi_pci_busfreq / 1000000);
#endif
return 0;
}
/*
* Return the virtual address representing the top of physical RAM.
*/
static unsigned long __init
ppc4xx_find_end_of_memory(void)
{
return ((unsigned long) __res.bi_memsize);
}
void __init
ppc4xx_map_io(void)
{
io_block_mapping(PPC4xx_ONB_IO_VADDR,
PPC4xx_ONB_IO_PADDR, PPC4xx_ONB_IO_SIZE, _PAGE_IO);
#ifdef CONFIG_PCI
io_block_mapping(PPC4xx_PCI_IO_VADDR,
PPC4xx_PCI_IO_PADDR, PPC4xx_PCI_IO_SIZE, _PAGE_IO);
io_block_mapping(PPC4xx_PCI_CFG_VADDR,
PPC4xx_PCI_CFG_PADDR, PPC4xx_PCI_CFG_SIZE, _PAGE_IO);
io_block_mapping(PPC4xx_PCI_LCFG_VADDR,
PPC4xx_PCI_LCFG_PADDR, PPC4xx_PCI_LCFG_SIZE, _PAGE_IO);
#endif
}
void __init
ppc4xx_init_IRQ(void)
{
ppc4xx_pic_init();
}
static void
ppc4xx_restart(char *cmd)
{
printk("%s\n", cmd);
abort();
}
static void
ppc4xx_power_off(void)
{
printk("System Halted\n");
local_irq_disable();
while (1) ;
}
static void
ppc4xx_halt(void)
{
printk("System Halted\n");
local_irq_disable();
while (1) ;
}
/*
* This routine retrieves the internal processor frequency from the board
* information structure, sets up the kernel timer decrementer based on
* that value, enables the 4xx programmable interval timer (PIT) and sets
* it up for auto-reload.
*/
static void __init
ppc4xx_calibrate_decr(void)
{
unsigned int freq;
bd_t *bip = &__res;
#if defined(CONFIG_WALNUT) || defined(CONFIG_ASH) || defined(CONFIG_SYCAMORE)
/* Walnut boot rom sets DCR CHCR1 (aka CPC0_CR1) bit CETE to 1 */
mtdcr(DCRN_CHCR1, mfdcr(DCRN_CHCR1) & ~CHR1_CETE);
#endif
freq = bip->bi_tbfreq;
tb_ticks_per_jiffy = freq / HZ;
tb_to_us = mulhwu_scale_factor(freq, 1000000);
/* Set the time base to zero.
** At 200 Mhz, time base will rollover in ~2925 years.
*/
mtspr(SPRN_TBWL, 0);
mtspr(SPRN_TBWU, 0);
/* Clear any pending timer interrupts */
mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS);
mtspr(SPRN_TCR, TCR_PIE | TCR_ARE);
/* Set the PIT reload value and just let it run. */
mtspr(SPRN_PIT, tb_ticks_per_jiffy);
}
/*
* IDE stuff.
* should be generic for every IDE PCI chipset
*/
#if defined(CONFIG_PCI) && defined(CONFIG_IDE)
static void
ppc4xx_ide_init_hwif_ports(hw_regs_t * hw, unsigned long data_port,
unsigned long ctrl_port, int *irq)
{
int i;
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
}
#endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */
TODC_ALLOC();
/*
* Input(s):
* r3 - Optional pointer to a board information structure.
* r4 - Optional pointer to the physical starting address of the init RAM
* disk.
* r5 - Optional pointer to the physical ending address of the init RAM
* disk.
* r6 - Optional pointer to the physical starting address of any kernel
* command-line parameters.
* r7 - Optional pointer to the physical ending address of any kernel
* command-line parameters.
*/
void __init
ppc4xx_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
{
parse_bootinfo(find_bootinfo());
/*
* If we were passed in a board information, copy it into the
* residual data area.
*/
if (r3)
__res = *(bd_t *)(r3 + KERNELBASE);
#if defined(CONFIG_BLK_DEV_INITRD)
/*
* If the init RAM disk has been configured in, and there's a valid
* starting address for it, set it up.
*/
if (r4) {
initrd_start = r4 + KERNELBASE;
initrd_end = r5 + KERNELBASE;
}
#endif /* CONFIG_BLK_DEV_INITRD */
/* Copy the kernel command line arguments to a safe place. */
if (r6) {
*(char *) (r7 + KERNELBASE) = 0;
strcpy(cmd_line, (char *) (r6 + KERNELBASE));
}
#if defined(CONFIG_PPC405_WDT)
/* Look for wdt= option on command line */
if (strstr(cmd_line, "wdt=")) {
int valid_wdt = 0;
char *p, *q;
for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) {
q = p + 4;
if (p > cmd_line && p[-1] != ' ')
continue;
wdt_period = simple_strtoul(q, &q, 0);
valid_wdt = 1;
++q;
}
wdt_enable = valid_wdt;
}
#endif
/* Initialize machine-dependent vectors */
ppc_md.setup_arch = ppc4xx_setup_arch;
ppc_md.show_percpuinfo = ppc4xx_show_percpuinfo;
ppc_md.show_cpuinfo = ppc4xx_show_cpuinfo;
ppc_md.init_IRQ = ppc4xx_init_IRQ;
ppc_md.restart = ppc4xx_restart;
ppc_md.power_off = ppc4xx_power_off;
ppc_md.halt = ppc4xx_halt;
ppc_md.calibrate_decr = ppc4xx_calibrate_decr;
#ifdef CONFIG_PPC405_WDT
ppc_md.heartbeat = ppc4xx_wdt_heartbeat;
#endif
ppc_md.heartbeat_count = 0;
ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory;
ppc_md.setup_io_mappings = ppc4xx_map_io;
#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_IDE)
ppc_ide_md.ide_init_hwif = ppc4xx_ide_init_hwif_ports;
#endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */
}
/* Called from MachineCheckException */
void platform_machine_check(struct pt_regs *regs)
{
#if defined(DCRN_PLB0_BEAR)
printk("PLB0: BEAR= 0x%08x ACR= 0x%08x BESR= 0x%08x\n",
mfdcr(DCRN_PLB0_BEAR), mfdcr(DCRN_PLB0_ACR),
mfdcr(DCRN_PLB0_BESR));
#endif
#if defined(DCRN_POB0_BEAR)
printk("PLB0 to OPB: BEAR= 0x%08x BESR0= 0x%08x BESR1= 0x%08x\n",
mfdcr(DCRN_POB0_BEAR), mfdcr(DCRN_POB0_BESR0),
mfdcr(DCRN_POB0_BESR1));
#endif
}

View File

@@ -0,0 +1,467 @@
/*
* arch/ppc/kernel/ppc4xx_sgdma.c
*
* IBM PPC4xx DMA engine scatter/gather library
*
* Copyright 2002-2003 MontaVista Software Inc.
*
* Cleaned up and converted to new DCR access
* Matt Porter <mporter@kernel.crashing.org>
*
* Original code by Armin Kuster <akuster@mvista.com>
* and Pete Popov <ppopov@mvista.com>
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/ppc4xx_dma.h>
void
ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_sg_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_ASGH0 + (dmanr * 0x8), (u32)(sg_addr >> 32));
#endif
mtdcr(DCRN_ASG0 + (dmanr * 0x8), (u32)sg_addr);
}
/*
* Add a new sgl descriptor to the end of a scatter/gather list
* which was created by alloc_dma_handle().
*
* For a memory to memory transfer, both dma addresses must be
* valid. For a peripheral to memory transfer, one of the addresses
* must be set to NULL, depending on the direction of the transfer:
* memory to peripheral: set dst_addr to NULL,
* peripheral to memory: set src_addr to NULL.
*/
int
ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr,
unsigned int count)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
ppc_dma_ch_t *p_dma_ch;
if (!handle) {
printk("ppc4xx_add_dma_sgl: null handle\n");
return DMA_STATUS_BAD_HANDLE;
}
if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch = &dma_channels[psgl->dmanr];
#ifdef DEBUG_4xxDMA
{
int error = 0;
unsigned int aligned =
(unsigned) src_addr | (unsigned) dst_addr | count;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (aligned & 0x1)
error = 1;
break;
case PW_32:
if (aligned & 0x3)
error = 1;
break;
case PW_64:
if (aligned & 0x7)
error = 1;
break;
default:
printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return DMA_STATUS_GENERAL_ERROR;
}
if (error)
printk
("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n",
src_addr, dst_addr, count, p_dma_ch->pwidth);
}
#endif
if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) {
printk("sgl handle out of memory \n");
return DMA_STATUS_OUT_OF_MEMORY;
}
if (!psgl->ptail) {
psgl->phead = (ppc_sgl_t *)
((unsigned) psgl + sizeof (sgl_list_info_t));
psgl->phead_dma = psgl->dma_addr + sizeof(sgl_list_info_t);
psgl->ptail = psgl->phead;
psgl->ptail_dma = psgl->phead_dma;
} else {
if(p_dma_ch->int_on_final_sg) {
/* mask out all dma interrupts, except error, on tail
before adding new tail. */
psgl->ptail->control_count &=
~(SG_TCI_ENABLE | SG_ETI_ENABLE);
}
psgl->ptail->next = psgl->ptail_dma + sizeof(ppc_sgl_t);
psgl->ptail++;
psgl->ptail_dma += sizeof(ppc_sgl_t);
}
psgl->ptail->control = psgl->control;
psgl->ptail->src_addr = src_addr;
psgl->ptail->dst_addr = dst_addr;
psgl->ptail->control_count = (count >> p_dma_ch->shift) |
psgl->sgl_control;
psgl->ptail->next = (uint32_t) NULL;
return DMA_STATUS_GOOD;
}
/*
* Enable (start) the DMA described by the sgl handle.
*/
void
ppc4xx_enable_dma_sgl(sgl_handle_t handle)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
ppc_dma_ch_t *p_dma_ch;
uint32_t sg_command;
if (!handle) {
printk("ppc4xx_enable_dma_sgl: null handle\n");
return;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n",
psgl->dmanr);
return;
} else if (!psgl->phead) {
printk("ppc4xx_enable_dma_sgl: sg list empty\n");
return;
}
p_dma_ch = &dma_channels[psgl->dmanr];
psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */
sg_command = mfdcr(DCRN_ASGC);
ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma);
sg_command |= SSG_ENABLE(psgl->dmanr);
mtdcr(DCRN_ASGC, sg_command); /* start transfer */
}
/*
* Halt an active scatter/gather DMA operation.
*/
void
ppc4xx_disable_dma_sgl(sgl_handle_t handle)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
uint32_t sg_command;
if (!handle) {
printk("ppc4xx_enable_dma_sgl: null handle\n");
return;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n",
psgl->dmanr);
return;
}
sg_command = mfdcr(DCRN_ASGC);
sg_command &= ~SSG_ENABLE(psgl->dmanr);
mtdcr(DCRN_ASGC, sg_command); /* stop transfer */
}
/*
* Returns number of bytes left to be transferred from the entire sgl list.
* *src_addr and *dst_addr get set to the source/destination address of
* the sgl descriptor where the DMA stopped.
*
* An sgl transfer must NOT be active when this function is called.
*/
int
ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr,
phys_addr_t * dst_addr)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
ppc_dma_ch_t *p_dma_ch;
ppc_sgl_t *pnext, *sgl_addr;
uint32_t count_left;
if (!handle) {
printk("ppc4xx_get_dma_sgl_residue: null handle\n");
return DMA_STATUS_BAD_HANDLE;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n",
psgl->dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
sgl_addr = (ppc_sgl_t *) __va(mfdcr(DCRN_ASG0 + (psgl->dmanr * 0x8)));
count_left = mfdcr(DCRN_DMACT0 + (psgl->dmanr * 0x8)) & SG_COUNT_MASK;
if (!sgl_addr) {
printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n");
goto error;
}
pnext = psgl->phead;
while (pnext &&
((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) &&
(pnext != sgl_addr))
) {
pnext++;
}
if (pnext == sgl_addr) { /* found the sgl descriptor */
*src_addr = pnext->src_addr;
*dst_addr = pnext->dst_addr;
/*
* Now search the remaining descriptors and add their count.
* We already have the remaining count from this descriptor in
* count_left.
*/
pnext++;
while ((pnext != psgl->ptail) &&
((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE))
) {
count_left += pnext->control_count & SG_COUNT_MASK;
}
if (pnext != psgl->ptail) { /* should never happen */
printk
("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n",
(unsigned int) psgl->ptail, (unsigned int) handle);
goto error;
}
/* success */
p_dma_ch = &dma_channels[psgl->dmanr];
return (count_left << p_dma_ch->shift); /* count in bytes */
} else {
/* this shouldn't happen */
printk
("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n",
(unsigned int) sgl_addr, (unsigned int) handle);
}
error:
*src_addr = (phys_addr_t) NULL;
*dst_addr = (phys_addr_t) NULL;
return 0;
}
/*
* Returns the address(es) of the buffer(s) contained in the head element of
* the scatter/gather list. The element is removed from the scatter/gather
* list and the next element becomes the head.
*
* This function should only be called when the DMA is not active.
*/
int
ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr,
phys_addr_t * dst_dma_addr)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
if (!handle) {
printk("ppc4xx_delete_sgl_element: null handle\n");
return DMA_STATUS_BAD_HANDLE;
} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n",
psgl->dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if (!psgl->phead) {
printk("ppc4xx_delete_sgl_element: sgl list empty\n");
*src_dma_addr = (phys_addr_t) NULL;
*dst_dma_addr = (phys_addr_t) NULL;
return DMA_STATUS_SGL_LIST_EMPTY;
}
*src_dma_addr = (phys_addr_t) psgl->phead->src_addr;
*dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr;
if (psgl->phead == psgl->ptail) {
/* last descriptor on the list */
psgl->phead = NULL;
psgl->ptail = NULL;
} else {
psgl->phead++;
psgl->phead_dma += sizeof(ppc_sgl_t);
}
return DMA_STATUS_GOOD;
}
/*
* Create a scatter/gather list handle. This is simply a structure which
* describes a scatter/gather list.
*
* A handle is returned in "handle" which the driver should save in order to
* be able to access this list later. A chunk of memory will be allocated
* to be used by the API for internal management purposes, including managing
* the sg list and allocating memory for the sgl descriptors. One page should
* be more than enough for that purpose. Perhaps it's a bit wasteful to use
* a whole page for a single sg list, but most likely there will be only one
* sg list per channel.
*
* Interrupt notes:
* Each sgl descriptor has a copy of the DMA control word which the DMA engine
* loads in the control register. The control word has a "global" interrupt
* enable bit for that channel. Interrupts are further qualified by a few bits
* in the sgl descriptor count register. In order to setup an sgl, we have to
* know ahead of time whether or not interrupts will be enabled at the completion
* of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST
* be called before calling alloc_dma_handle(). If the interrupt mode will never
* change after powerup, then enable_dma_interrupt()/disable_dma_interrupt()
* do not have to be called -- interrupts will be enabled or disabled based
* on how the channel was configured after powerup by the hw_init_dma_channel()
* function. Each sgl descriptor will be setup to interrupt if an error occurs;
* however, only the last descriptor will be setup to interrupt. Thus, an
* interrupt will occur (if interrupts are enabled) only after the complete
* sgl transfer is done.
*/
int
ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr)
{
sgl_list_info_t *psgl=NULL;
dma_addr_t dma_addr;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
uint32_t sg_command;
uint32_t ctc_settings;
void *ret;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if (!phandle) {
printk("ppc4xx_alloc_dma_handle: null handle pointer\n");
return DMA_STATUS_NULL_POINTER;
}
/* Get a page of memory, which is zeroed out by consistent_alloc() */
ret = dma_alloc_coherent(NULL, DMA_PPC4xx_SIZE, &dma_addr, GFP_KERNEL);
if (ret != NULL) {
memset(ret, 0, DMA_PPC4xx_SIZE);
psgl = (sgl_list_info_t *) ret;
}
if (psgl == NULL) {
*phandle = (sgl_handle_t) NULL;
return DMA_STATUS_OUT_OF_MEMORY;
}
psgl->dma_addr = dma_addr;
psgl->dmanr = dmanr;
/*
* Modify and save the control word. These words will be
* written to each sgl descriptor. The DMA engine then
* loads this control word into the control register
* every time it reads a new descriptor.
*/
psgl->control = p_dma_ch->control;
/* Clear all mode bits */
psgl->control &= ~(DMA_TM_MASK | DMA_TD);
/* Save control word and mode */
psgl->control |= (mode | DMA_CE_ENABLE);
/* In MM mode, we must set ETD/TCE */
if (mode == DMA_MODE_MM)
psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
if (p_dma_ch->int_enable) {
/* Enable channel interrupt */
psgl->control |= DMA_CIE_ENABLE;
} else {
psgl->control &= ~DMA_CIE_ENABLE;
}
sg_command = mfdcr(DCRN_ASGC);
sg_command |= SSG_MASK_ENABLE(dmanr);
/* Enable SGL control access */
mtdcr(DCRN_ASGC, sg_command);
psgl->sgl_control = SG_ERI_ENABLE | SG_LINK;
/* keep control count register settings */
ctc_settings = mfdcr(DCRN_DMACT0 + (dmanr * 0x8))
& (DMA_CTC_BSIZ_MSK | DMA_CTC_BTEN); /*burst mode settings*/
psgl->sgl_control |= ctc_settings;
if (p_dma_ch->int_enable) {
if (p_dma_ch->tce_enable)
psgl->sgl_control |= SG_TCI_ENABLE;
else
psgl->sgl_control |= SG_ETI_ENABLE;
}
*phandle = (sgl_handle_t) psgl;
return DMA_STATUS_GOOD;
}
/*
* Destroy a scatter/gather list handle that was created by alloc_dma_handle().
* The list must be empty (contain no elements).
*/
void
ppc4xx_free_dma_handle(sgl_handle_t handle)
{
sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
if (!handle) {
printk("ppc4xx_free_dma_handle: got NULL\n");
return;
} else if (psgl->phead) {
printk("ppc4xx_free_dma_handle: list not empty\n");
return;
} else if (!psgl->dma_addr) { /* should never happen */
printk("ppc4xx_free_dma_handle: no dma address\n");
return;
}
dma_free_coherent(NULL, DMA_PPC4xx_SIZE, (void *) psgl, 0);
}
EXPORT_SYMBOL(ppc4xx_alloc_dma_handle);
EXPORT_SYMBOL(ppc4xx_free_dma_handle);
EXPORT_SYMBOL(ppc4xx_add_dma_sgl);
EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element);
EXPORT_SYMBOL(ppc4xx_enable_dma_sgl);
EXPORT_SYMBOL(ppc4xx_disable_dma_sgl);
EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue);

View File

@@ -0,0 +1,138 @@
/*
* arch/ppc/syslib/ppc83xx_setup.c
*
* MPC83XX common board code
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/serial.h>
#include <linux/tty.h> /* for linux/serial_core.h */
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <asm/prom.h>
#include <asm/time.h>
#include <asm/mpc83xx.h>
#include <asm/mmu.h>
#include <asm/ppc_sys.h>
#include <asm/kgdb.h>
#include <syslib/ppc83xx_setup.h>
phys_addr_t immrbar;
/* Return the amount of memory */
unsigned long __init
mpc83xx_find_end_of_memory(void)
{
bd_t *binfo;
binfo = (bd_t *) __res;
return binfo->bi_memsize;
}
long __init
mpc83xx_time_init(void)
{
#define SPCR_OFFS 0x00000110
#define SPCR_TBEN 0x00400000
bd_t *binfo = (bd_t *)__res;
u32 *spcr = ioremap(binfo->bi_immr_base + SPCR_OFFS, 4);
*spcr |= SPCR_TBEN;
iounmap(spcr);
return 0;
}
/* The decrementer counts at the system (internal) clock freq divided by 4 */
void __init
mpc83xx_calibrate_decr(void)
{
bd_t *binfo = (bd_t *) __res;
unsigned int freq, divisor;
freq = binfo->bi_busfreq;
divisor = 4;
tb_ticks_per_jiffy = freq / HZ / divisor;
tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
}
#ifdef CONFIG_SERIAL_8250
void __init
mpc83xx_early_serial_map(void)
{
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
struct uart_port serial_req;
#endif
struct plat_serial8250_port *pdata;
bd_t *binfo = (bd_t *) __res;
pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC83xx_DUART);
/* Setup serial port access */
pdata[0].uartclk = binfo->bi_busfreq;
pdata[0].mapbase += binfo->bi_immr_base;
pdata[0].membase = ioremap(pdata[0].mapbase, 0x100);
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
memset(&serial_req, 0, sizeof (serial_req));
serial_req.iotype = SERIAL_IO_MEM;
serial_req.mapbase = pdata[0].mapbase;
serial_req.membase = pdata[0].membase;
serial_req.regshift = 0;
gen550_init(0, &serial_req);
#endif
pdata[1].uartclk = binfo->bi_busfreq;
pdata[1].mapbase += binfo->bi_immr_base;
pdata[1].membase = ioremap(pdata[1].mapbase, 0x100);
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
/* Assume gen550_init() doesn't modify serial_req */
serial_req.mapbase = pdata[1].mapbase;
serial_req.membase = pdata[1].membase;
gen550_init(1, &serial_req);
#endif
}
#endif
void
mpc83xx_restart(char *cmd)
{
local_irq_disable();
for(;;);
}
void
mpc83xx_power_off(void)
{
local_irq_disable();
for(;;);
}
void
mpc83xx_halt(void)
{
local_irq_disable();
for(;;);
}
/* PCI SUPPORT DOES NOT EXIT, MODEL after ppc85xx_setup.c */

View File

@@ -0,0 +1,53 @@
/*
* arch/ppc/syslib/ppc83xx_setup.h
*
* MPC83XX common board definitions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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.
*
*/
#ifndef __PPC_SYSLIB_PPC83XX_SETUP_H
#define __PPC_SYSLIB_PPC83XX_SETUP_H
#include <linux/config.h>
#include <linux/init.h>
#include <asm/ppcboot.h>
extern unsigned long mpc83xx_find_end_of_memory(void) __init;
extern long mpc83xx_time_init(void) __init;
extern void mpc83xx_calibrate_decr(void) __init;
extern void mpc83xx_early_serial_map(void) __init;
extern void mpc83xx_restart(char *cmd);
extern void mpc83xx_power_off(void);
extern void mpc83xx_halt(void);
extern void mpc83xx_setup_hose(void) __init;
/* PCI config */
#if 0
#define PCI1_CFG_ADDR_OFFSET (FIXME)
#define PCI1_CFG_DATA_OFFSET (FIXME)
#define PCI2_CFG_ADDR_OFFSET (FIXME)
#define PCI2_CFG_DATA_OFFSET (FIXME)
#endif
/* Serial Config */
#ifdef CONFIG_SERIAL_MANY_PORTS
#define RS_TABLE_SIZE 64
#else
#define RS_TABLE_SIZE 2
#endif
#ifndef BASE_BAUD
#define BASE_BAUD 115200
#endif
#endif /* __PPC_SYSLIB_PPC83XX_SETUP_H */

View File

@@ -0,0 +1,33 @@
/*
* arch/ppc/syslib/ppc85xx_common.c
*
* MPC85xx support routines
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2004 Freescale Semiconductor Inc.
*
* 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/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/mpc85xx.h>
#include <asm/mmu.h>
/* ************************************************************************ */
/* Return the value of CCSRBAR for the current board */
phys_addr_t
get_ccsrbar(void)
{
return BOARD_CCSRBAR;
}
EXPORT_SYMBOL(get_ccsrbar);

View File

@@ -0,0 +1,25 @@
/*
* arch/ppc/syslib/ppc85xx_common.h
*
* MPC85xx support routines
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2004 Freescale Semiconductor Inc.
*
* 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.
*/
#ifndef __PPC_SYSLIB_PPC85XX_COMMON_H
#define __PPC_SYSLIB_PPC85XX_COMMON_H
#include <linux/config.h>
#include <linux/init.h>
/* Provide access to ccsrbar for any modules, etc */
phys_addr_t get_ccsrbar(void);
#endif /* __PPC_SYSLIB_PPC85XX_COMMON_H */

View File

@@ -0,0 +1,354 @@
/*
* arch/ppc/syslib/ppc85xx_setup.c
*
* MPC85XX common board code
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2004 Freescale Semiconductor Inc.
*
* 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/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/serial.h>
#include <linux/tty.h> /* for linux/serial_core.h */
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <asm/prom.h>
#include <asm/time.h>
#include <asm/mpc85xx.h>
#include <asm/immap_85xx.h>
#include <asm/mmu.h>
#include <asm/ppc_sys.h>
#include <asm/kgdb.h>
#include <syslib/ppc85xx_setup.h>
/* Return the amount of memory */
unsigned long __init
mpc85xx_find_end_of_memory(void)
{
bd_t *binfo;
binfo = (bd_t *) __res;
return binfo->bi_memsize;
}
/* The decrementer counts at the system (internal) clock freq divided by 8 */
void __init
mpc85xx_calibrate_decr(void)
{
bd_t *binfo = (bd_t *) __res;
unsigned int freq, divisor;
/* get the core frequency */
freq = binfo->bi_busfreq;
/* The timebase is updated every 8 bus clocks, HID0[SEL_TBCLK] = 0 */
divisor = 8;
tb_ticks_per_jiffy = freq / divisor / HZ;
tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
/* Set the time base to zero */
mtspr(SPRN_TBWL, 0);
mtspr(SPRN_TBWU, 0);
/* Clear any pending timer interrupts */
mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
/* Enable decrementer interrupt */
mtspr(SPRN_TCR, TCR_DIE);
}
#ifdef CONFIG_SERIAL_8250
void __init
mpc85xx_early_serial_map(void)
{
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
struct uart_port serial_req;
#endif
struct plat_serial8250_port *pdata;
bd_t *binfo = (bd_t *) __res;
pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC85xx_DUART);
/* Setup serial port access */
pdata[0].uartclk = binfo->bi_busfreq;
pdata[0].mapbase += binfo->bi_immr_base;
pdata[0].membase = ioremap(pdata[0].mapbase, MPC85xx_UART0_SIZE);
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
memset(&serial_req, 0, sizeof (serial_req));
serial_req.iotype = SERIAL_IO_MEM;
serial_req.mapbase = pdata[0].mapbase;
serial_req.membase = pdata[0].membase;
serial_req.regshift = 0;
gen550_init(0, &serial_req);
#endif
pdata[1].uartclk = binfo->bi_busfreq;
pdata[1].mapbase += binfo->bi_immr_base;
pdata[1].membase = ioremap(pdata[1].mapbase, MPC85xx_UART0_SIZE);
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
/* Assume gen550_init() doesn't modify serial_req */
serial_req.mapbase = pdata[1].mapbase;
serial_req.membase = pdata[1].membase;
gen550_init(1, &serial_req);
#endif
}
#endif
void
mpc85xx_restart(char *cmd)
{
local_irq_disable();
abort();
}
void
mpc85xx_power_off(void)
{
local_irq_disable();
for(;;);
}
void
mpc85xx_halt(void)
{
local_irq_disable();
for(;;);
}
#ifdef CONFIG_PCI
static void __init
mpc85xx_setup_pci1(struct pci_controller *hose)
{
volatile struct ccsr_pci *pci;
volatile struct ccsr_guts *guts;
unsigned short temps;
bd_t *binfo = (bd_t *) __res;
pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI1_OFFSET,
MPC85xx_PCI1_SIZE);
guts = ioremap(binfo->bi_immr_base + MPC85xx_GUTS_OFFSET,
MPC85xx_GUTS_SIZE);
early_read_config_word(hose, 0, 0, PCI_COMMAND, &temps);
temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
early_write_config_word(hose, 0, 0, PCI_COMMAND, temps);
#define PORDEVSR_PCI (0x00800000) /* PCI Mode */
if (guts->pordevsr & PORDEVSR_PCI) {
early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80);
} else {
/* PCI-X init */
temps = PCI_X_CMD_MAX_SPLIT | PCI_X_CMD_MAX_READ
| PCI_X_CMD_ERO | PCI_X_CMD_DPERR_E;
early_write_config_word(hose, 0, 0, PCIX_COMMAND, temps);
}
/* Disable all windows (except powar0 since its ignored) */
pci->powar1 = 0;
pci->powar2 = 0;
pci->powar3 = 0;
pci->powar4 = 0;
pci->piwar1 = 0;
pci->piwar2 = 0;
pci->piwar3 = 0;
/* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI1_LOWER_MEM */
pci->potar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff;
pci->potear1 = 0x00000000;
pci->powbar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff;
/* Enable, Mem R/W */
pci->powar1 = 0x80044000 |
(__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
/* Setup outboud IO windows @ MPC85XX_PCI1_IO_BASE */
pci->potar2 = 0x00000000;
pci->potear2 = 0x00000000;
pci->powbar2 = (MPC85XX_PCI1_IO_BASE >> 12) & 0x000fffff;
/* Enable, IO R/W */
pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
/* Setup 2G inbound Memory Window @ 0 */
pci->pitar1 = 0x00000000;
pci->piwbar1 = 0x00000000;
pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local
Mem, Snoop R/W, 2G */
}
extern int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin);
extern int mpc85xx_exclude_device(u_char bus, u_char devfn);
#ifdef CONFIG_85xx_PCI2
static void __init
mpc85xx_setup_pci2(struct pci_controller *hose)
{
volatile struct ccsr_pci *pci;
unsigned short temps;
bd_t *binfo = (bd_t *) __res;
pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI2_OFFSET,
MPC85xx_PCI2_SIZE);
early_read_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, &temps);
temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
early_write_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, temps);
early_write_config_byte(hose, hose->bus_offset, 0, PCI_LATENCY_TIMER, 0x80);
/* Disable all windows (except powar0 since its ignored) */
pci->powar1 = 0;
pci->powar2 = 0;
pci->powar3 = 0;
pci->powar4 = 0;
pci->piwar1 = 0;
pci->piwar2 = 0;
pci->piwar3 = 0;
/* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI2_LOWER_MEM */
pci->potar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
pci->potear1 = 0x00000000;
pci->powbar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
/* Enable, Mem R/W */
pci->powar1 = 0x80044000 |
(__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
/* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */
pci->potar2 = 0x00000000;
pci->potear2 = 0x00000000;
pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff;
/* Enable, IO R/W */
pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
/* Setup 2G inbound Memory Window @ 0 */
pci->pitar1 = 0x00000000;
pci->piwbar1 = 0x00000000;
pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local
Mem, Snoop R/W, 2G */
}
#endif /* CONFIG_85xx_PCI2 */
int mpc85xx_pci1_last_busno = 0;
void __init
mpc85xx_setup_hose(void)
{
struct pci_controller *hose_a;
#ifdef CONFIG_85xx_PCI2
struct pci_controller *hose_b;
#endif
bd_t *binfo = (bd_t *) __res;
hose_a = pcibios_alloc_controller();
if (!hose_a)
return;
ppc_md.pci_swizzle = common_swizzle;
ppc_md.pci_map_irq = mpc85xx_map_irq;
hose_a->first_busno = 0;
hose_a->bus_offset = 0;
hose_a->last_busno = 0xff;
setup_indirect_pci(hose_a, binfo->bi_immr_base + PCI1_CFG_ADDR_OFFSET,
binfo->bi_immr_base + PCI1_CFG_DATA_OFFSET);
hose_a->set_cfg_type = 1;
mpc85xx_setup_pci1(hose_a);
hose_a->pci_mem_offset = MPC85XX_PCI1_MEM_OFFSET;
hose_a->mem_space.start = MPC85XX_PCI1_LOWER_MEM;
hose_a->mem_space.end = MPC85XX_PCI1_UPPER_MEM;
hose_a->io_space.start = MPC85XX_PCI1_LOWER_IO;
hose_a->io_space.end = MPC85XX_PCI1_UPPER_IO;
hose_a->io_base_phys = MPC85XX_PCI1_IO_BASE;
#ifdef CONFIG_85xx_PCI2
isa_io_base =
(unsigned long) ioremap(MPC85XX_PCI1_IO_BASE,
MPC85XX_PCI1_IO_SIZE +
MPC85XX_PCI2_IO_SIZE);
#else
isa_io_base =
(unsigned long) ioremap(MPC85XX_PCI1_IO_BASE,
MPC85XX_PCI1_IO_SIZE);
#endif
hose_a->io_base_virt = (void *) isa_io_base;
/* setup resources */
pci_init_resource(&hose_a->mem_resources[0],
MPC85XX_PCI1_LOWER_MEM,
MPC85XX_PCI1_UPPER_MEM,
IORESOURCE_MEM, "PCI1 host bridge");
pci_init_resource(&hose_a->io_resource,
MPC85XX_PCI1_LOWER_IO,
MPC85XX_PCI1_UPPER_IO,
IORESOURCE_IO, "PCI1 host bridge");
ppc_md.pci_exclude_device = mpc85xx_exclude_device;
hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno);
#ifdef CONFIG_85xx_PCI2
hose_b = pcibios_alloc_controller();
if (!hose_b)
return;
hose_b->bus_offset = hose_a->last_busno + 1;
hose_b->first_busno = hose_a->last_busno + 1;
hose_b->last_busno = 0xff;
setup_indirect_pci(hose_b, binfo->bi_immr_base + PCI2_CFG_ADDR_OFFSET,
binfo->bi_immr_base + PCI2_CFG_DATA_OFFSET);
hose_b->set_cfg_type = 1;
mpc85xx_setup_pci2(hose_b);
hose_b->pci_mem_offset = MPC85XX_PCI2_MEM_OFFSET;
hose_b->mem_space.start = MPC85XX_PCI2_LOWER_MEM;
hose_b->mem_space.end = MPC85XX_PCI2_UPPER_MEM;
hose_b->io_space.start = MPC85XX_PCI2_LOWER_IO;
hose_b->io_space.end = MPC85XX_PCI2_UPPER_IO;
hose_b->io_base_phys = MPC85XX_PCI2_IO_BASE;
hose_b->io_base_virt = (void *) isa_io_base + MPC85XX_PCI1_IO_SIZE;
/* setup resources */
pci_init_resource(&hose_b->mem_resources[0],
MPC85XX_PCI2_LOWER_MEM,
MPC85XX_PCI2_UPPER_MEM,
IORESOURCE_MEM, "PCI2 host bridge");
pci_init_resource(&hose_b->io_resource,
MPC85XX_PCI2_LOWER_IO,
MPC85XX_PCI2_UPPER_IO,
IORESOURCE_IO, "PCI2 host bridge");
hose_b->last_busno = pciauto_bus_scan(hose_b, hose_b->first_busno);
/* let board code know what the last bus number was on PCI1 */
mpc85xx_pci1_last_busno = hose_a->last_busno;
#endif
return;
}
#endif /* CONFIG_PCI */

View File

@@ -0,0 +1,59 @@
/*
* arch/ppc/syslib/ppc85xx_setup.h
*
* MPC85XX common board definitions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2004 Freescale Semiconductor Inc.
*
* 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.
*
*/
#ifndef __PPC_SYSLIB_PPC85XX_SETUP_H
#define __PPC_SYSLIB_PPC85XX_SETUP_H
#include <linux/config.h>
#include <linux/init.h>
#include <asm/ppcboot.h>
extern unsigned long mpc85xx_find_end_of_memory(void) __init;
extern void mpc85xx_calibrate_decr(void) __init;
extern void mpc85xx_early_serial_map(void) __init;
extern void mpc85xx_restart(char *cmd);
extern void mpc85xx_power_off(void);
extern void mpc85xx_halt(void);
extern void mpc85xx_setup_hose(void) __init;
/* PCI config */
#define PCI1_CFG_ADDR_OFFSET (0x8000)
#define PCI1_CFG_DATA_OFFSET (0x8004)
#define PCI2_CFG_ADDR_OFFSET (0x9000)
#define PCI2_CFG_DATA_OFFSET (0x9004)
/* Additional register for PCI-X configuration */
#define PCIX_NEXT_CAP 0x60
#define PCIX_CAP_ID 0x61
#define PCIX_COMMAND 0x62
#define PCIX_STATUS 0x64
/* Serial Config */
#ifdef CONFIG_SERIAL_MANY_PORTS
#define RS_TABLE_SIZE 64
#else
#define RS_TABLE_SIZE 2
#endif
#ifndef BASE_BAUD
#define BASE_BAUD 115200
#endif
/* Offset of CPM register space */
#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET)
#endif /* __PPC_SYSLIB_PPC85XX_SETUP_H */

View File

@@ -0,0 +1,130 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/8xx_immap.h>
#include <asm/mpc8xx.h>
#include "ppc8xx_pic.h"
extern int cpm_get_irq(struct pt_regs *regs);
/* The 8xx internal interrupt controller. It is usually
* the only interrupt controller. Some boards, like the MBX and
* Sandpoint have the 8259 as a secondary controller. Depending
* upon the processor type, the internal controller can have as
* few as 16 interrups or as many as 64. We could use the
* "clear_bit()" and "set_bit()" functions like other platforms,
* but they are overkill for us.
*/
static void m8xx_mask_irq(unsigned int irq_nr)
{
int bit, word;
bit = irq_nr & 0x1f;
word = irq_nr >> 5;
ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
ppc_cached_irq_mask[word];
}
static void m8xx_unmask_irq(unsigned int irq_nr)
{
int bit, word;
bit = irq_nr & 0x1f;
word = irq_nr >> 5;
ppc_cached_irq_mask[word] |= (1 << (31-bit));
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
ppc_cached_irq_mask[word];
}
static void m8xx_end_irq(unsigned int irq_nr)
{
if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
&& irq_desc[irq_nr].action) {
int bit, word;
bit = irq_nr & 0x1f;
word = irq_nr >> 5;
ppc_cached_irq_mask[word] |= (1 << (31-bit));
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
ppc_cached_irq_mask[word];
}
}
static void m8xx_mask_and_ack(unsigned int irq_nr)
{
int bit, word;
bit = irq_nr & 0x1f;
word = irq_nr >> 5;
ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
ppc_cached_irq_mask[word];
((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-bit);
}
struct hw_interrupt_type ppc8xx_pic = {
.typename = " 8xx SIU ",
.enable = m8xx_unmask_irq,
.disable = m8xx_mask_irq,
.ack = m8xx_mask_and_ack,
.end = m8xx_end_irq,
};
/*
* We either return a valid interrupt or -1 if there is nothing pending
*/
int
m8xx_get_irq(struct pt_regs *regs)
{
int irq;
/* For MPC8xx, read the SIVEC register and shift the bits down
* to get the irq number.
*/
irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26;
/*
* When we read the sivec without an interrupt to process, we will
* get back SIU_LEVEL7. In this case, return -1
*/
if (irq == CPM_INTERRUPT)
irq = CPM_IRQ_OFFSET + cpm_get_irq(regs);
#if defined(CONFIG_PCI)
else if (irq == ISA_BRIDGE_INT) {
int isa_irq;
if ((isa_irq = i8259_poll(regs)) >= 0)
irq = I8259_IRQ_OFFSET + isa_irq;
}
#endif /* CONFIG_PCI */
else if (irq == SIU_LEVEL7)
irq = -1;
return irq;
}
#if defined(CONFIG_MBX) && defined(CONFIG_PCI)
/* Only the MBX uses the external 8259. This allows us to catch standard
* drivers that may mess up the internal interrupt controllers, and also
* allow them to run without modification on the MBX.
*/
void mbx_i8259_action(int irq, void *dev_id, struct pt_regs *regs)
{
/* This interrupt handler never actually gets called. It is
* installed only to unmask the 8259 cascade interrupt in the SIU
* and to make the 8259 cascade interrupt visible in /proc/interrupts.
*/
}
#endif /* CONFIG_PCI */

View File

@@ -0,0 +1,21 @@
#ifndef _PPC_KERNEL_PPC8xx_H
#define _PPC_KERNEL_PPC8xx_H
#include <linux/config.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
extern struct hw_interrupt_type ppc8xx_pic;
void m8xx_pic_init(void);
void m8xx_do_IRQ(struct pt_regs *regs,
int cpu);
int m8xx_get_irq(struct pt_regs *regs);
#ifdef CONFIG_MBX
#include <asm/i8259.h>
#include <asm/io.h>
void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs);
#endif
#endif /* _PPC_KERNEL_PPC8xx_H */

103
arch/ppc/syslib/ppc_sys.c Normal file
View File

@@ -0,0 +1,103 @@
/*
* arch/ppc/syslib/ppc_sys.c
*
* PPC System library functions
*
* Maintainer: Kumar Gala <kumar.gala@freescale.com>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
* 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 <asm/ppc_sys.h>
int (*ppc_sys_device_fixup) (struct platform_device * pdev);
static int ppc_sys_inited;
void __init identify_ppc_sys_by_id(u32 id)
{
unsigned int i = 0;
while (1) {
if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value)
break;
i++;
}
cur_ppc_sys_spec = &ppc_sys_specs[i];
return;
}
void __init identify_ppc_sys_by_name(char *name)
{
/* TODO */
return;
}
/* Update all memory resources by paddr, call before platform_device_register */
void __init
ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr)
{
int i;
for (i = 0; i < pdev->num_resources; i++) {
struct resource *r = &pdev->resource[i];
if ((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) {
r->start += paddr;
r->end += paddr;
}
}
}
/* Get platform_data pointer out of platform device, call before platform_device_register */
void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev)
{
return ppc_sys_platform_devices[dev].dev.platform_data;
}
void ppc_sys_device_remove(enum ppc_sys_devices dev)
{
unsigned int i;
if (ppc_sys_inited) {
platform_device_unregister(&ppc_sys_platform_devices[dev]);
} else {
if (cur_ppc_sys_spec == NULL)
return;
for (i = 0; i < cur_ppc_sys_spec->num_devices; i++)
if (cur_ppc_sys_spec->device_list[i] == dev)
cur_ppc_sys_spec->device_list[i] = -1;
}
}
static int __init ppc_sys_init(void)
{
unsigned int i, dev_id, ret = 0;
BUG_ON(cur_ppc_sys_spec == NULL);
for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) {
dev_id = cur_ppc_sys_spec->device_list[i];
if (dev_id != -1) {
if (ppc_sys_device_fixup != NULL)
ppc_sys_device_fixup(&ppc_sys_platform_devices
[dev_id]);
if (platform_device_register
(&ppc_sys_platform_devices[dev_id])) {
ret = 1;
printk(KERN_ERR
"unable to register device %d\n",
dev_id);
}
}
}
ppc_sys_inited = 1;
return ret;
}
subsys_initcall(ppc_sys_init);

View File

@@ -0,0 +1,141 @@
/*
* arch/ppc/kernel/prep_nvram.c
*
* Copyright (C) 1998 Corey Minyard
*
* This reads the NvRAM on PReP compliant machines (generally from IBM or
* Motorola). Motorola kept the format of NvRAM in their ROM, PPCBUG, the
* same, long after they had stopped producing PReP compliant machines. So
* this code is useful in those cases as well.
*
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <asm/sections.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/machdep.h>
#include <asm/prep_nvram.h>
static char nvramData[MAX_PREP_NVRAM];
static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0];
unsigned char __prep prep_nvram_read_val(int addr)
{
outb(addr, PREP_NVRAM_AS0);
outb(addr>>8, PREP_NVRAM_AS1);
return inb(PREP_NVRAM_DATA);
}
void __prep prep_nvram_write_val(int addr,
unsigned char val)
{
outb(addr, PREP_NVRAM_AS0);
outb(addr>>8, PREP_NVRAM_AS1);
outb(val, PREP_NVRAM_DATA);
}
void __init init_prep_nvram(void)
{
unsigned char *nvp;
int i;
int nvramSize;
/*
* The following could fail if the NvRAM were corrupt but
* we expect the boot firmware to have checked its checksum
* before boot
*/
nvp = (char *) &nvram->Header;
for (i=0; i<sizeof(HEADER); i++)
{
*nvp = ppc_md.nvram_read_val(i);
nvp++;
}
/*
* The PReP NvRAM may be any size so read in the header to
* determine how much we must read in order to get the complete
* GE area
*/
nvramSize=(int)nvram->Header.GEAddress+nvram->Header.GELength;
if(nvramSize>MAX_PREP_NVRAM)
{
/*
* NvRAM is too large
*/
nvram->Header.GELength=0;
return;
}
/*
* Read the remainder of the PReP NvRAM
*/
nvp = (char *) &nvram->GEArea[0];
for (i=sizeof(HEADER); i<nvramSize; i++)
{
*nvp = ppc_md.nvram_read_val(i);
nvp++;
}
}
__prep
char __prep *prep_nvram_get_var(const char *name)
{
char *cp;
int namelen;
namelen = strlen(name);
cp = prep_nvram_first_var();
while (cp != NULL) {
if ((strncmp(name, cp, namelen) == 0)
&& (cp[namelen] == '='))
{
return cp+namelen+1;
}
cp = prep_nvram_next_var(cp);
}
return NULL;
}
__prep
char __prep *prep_nvram_first_var(void)
{
if (nvram->Header.GELength == 0) {
return NULL;
} else {
return (((char *)nvram)
+ ((unsigned int) nvram->Header.GEAddress));
}
}
__prep
char __prep *prep_nvram_next_var(char *name)
{
char *cp;
cp = name;
while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength)
&& (*cp != '\0'))
{
cp++;
}
/* Skip over any null characters. */
while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength)
&& (*cp == '\0'))
{
cp++;
}
if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) {
return cp;
} else {
return NULL;
}
}

1447
arch/ppc/syslib/prom.c Normal file

File diff suppressed because it is too large Load Diff

1002
arch/ppc/syslib/prom_init.c Normal file

File diff suppressed because it is too large Load Diff

381
arch/ppc/syslib/qspan_pci.c Normal file
View File

@@ -0,0 +1,381 @@
/*
* QSpan pci routines.
* Most 8xx boards use the QSpan PCI bridge. The config address register
* is located 0x500 from the base of the bridge control/status registers.
* The data register is located at 0x504.
* This is a two step operation. First, the address register is written,
* then the data register is read/written as required.
* I don't know what to do about interrupts (yet).
*
* The RPX Classic implementation shares a chip select for normal
* PCI access and QSpan control register addresses. The selection is
* further selected by a bit setting in a board control register.
* Although it should happen, we disable interrupts during this operation
* to make sure some driver doesn't accidentally access the PCI while
* we have switched the chip select.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/mpc8xx.h>
#include <asm/system.h>
#include <asm/machdep.h>
#include <asm/pci-bridge.h>
/*
* This blows......
* When reading the configuration space, if something does not respond
* the bus times out and we get a machine check interrupt. So, the
* good ol' exception tables come to mind to trap it and return some
* value.
*
* On an error we just return a -1, since that is what the caller wants
* returned if nothing is present. I copied this from __get_user_asm,
* with the only difference of returning -1 instead of EFAULT.
* There is an associated hack in the machine check trap code.
*
* The QSPAN is also a big endian device, that is it makes the PCI
* look big endian to us. This presents a problem for the Linux PCI
* functions, which assume little endian. For example, we see the
* first 32-bit word like this:
* ------------------------
* | Device ID | Vendor ID |
* ------------------------
* If we read/write as a double word, that's OK. But in our world,
* when read as a word, device ID is at location 0, not location 2 as
* the little endian PCI would believe. We have to switch bits in
* the PCI addresses given to us to get the data to/from the correct
* byte lanes.
*
* The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5.
* It always forces the MS bit to zero. Therefore, dev_fn values
* greater than 128 are returned as "no device found" errors.
*
* The QSPAN can only perform long word (32-bit) configuration cycles.
* The "offset" must have the two LS bits set to zero. Read operations
* require we read the entire word and then sort out what should be
* returned. Write operations other than long word require that we
* read the long word, update the proper word or byte, then write the
* entire long word back.
*
* PCI Bridge hack. We assume (correctly) that bus 0 is the primary
* PCI bus from the QSPAN. If we are called with a bus number other
* than zero, we create a Type 1 configuration access that a downstream
* PCI bridge will interpret.
*/
#define __get_qspan_pci_config(x, addr, op) \
__asm__ __volatile__( \
"1: "op" %0,0(%1)\n" \
" eieio\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: li %0,-1\n" \
" b 2b\n" \
".section __ex_table,\"a\"\n" \
" .align 2\n" \
" .long 1b,3b\n" \
".text" \
: "=r"(x) : "r"(addr) : " %0")
#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500))
#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504))
#define mk_config_addr(bus, dev, offset) \
(((bus)<<16) | ((dev)<<8) | (offset & 0xfc))
#define mk_config_type1(bus, dev, offset) \
mk_config_addr(bus, dev, offset) | 1;
static spinlock_t pcibios_lock = SPIN_LOCK_UNLOCKED;
int qspan_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned char *val)
{
uint temp;
u_char *cp;
#ifdef CONFIG_RPXCLASSIC
unsigned long flags;
#endif
if ((bus > 7) || (dev_fn > 127)) {
*val = 0xff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
#ifdef CONFIG_RPXCLASSIC
/* disable interrupts */
spin_lock_irqsave(&pcibios_lock, flags);
*((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
eieio();
#endif
if (bus == 0)
*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
else
*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
__get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz");
#ifdef CONFIG_RPXCLASSIC
*((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
eieio();
spin_unlock_irqrestore(&pcibios_lock, flags);
#endif
offset ^= 0x03;
cp = ((u_char *)&temp) + (offset & 0x03);
*val = *cp;
return PCIBIOS_SUCCESSFUL;
}
int qspan_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned short *val)
{
uint temp;
ushort *sp;
#ifdef CONFIG_RPXCLASSIC
unsigned long flags;
#endif
if ((bus > 7) || (dev_fn > 127)) {
*val = 0xffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
#ifdef CONFIG_RPXCLASSIC
/* disable interrupts */
spin_lock_irqsave(&pcibios_lock, flags);
*((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
eieio();
#endif
if (bus == 0)
*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
else
*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
__get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz");
offset ^= 0x02;
#ifdef CONFIG_RPXCLASSIC
*((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
eieio();
spin_unlock_irqrestore(&pcibios_lock, flags);
#endif
sp = ((ushort *)&temp) + ((offset >> 1) & 1);
*val = *sp;
return PCIBIOS_SUCCESSFUL;
}
int qspan_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned int *val)
{
#ifdef CONFIG_RPXCLASSIC
unsigned long flags;
#endif
if ((bus > 7) || (dev_fn > 127)) {
*val = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
#ifdef CONFIG_RPXCLASSIC
/* disable interrupts */
spin_lock_irqsave(&pcibios_lock, flags);
*((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
eieio();
#endif
if (bus == 0)
*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
else
*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
__get_qspan_pci_config(*val, QS_CONFIG_DATA, "lwz");
#ifdef CONFIG_RPXCLASSIC
*((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
eieio();
spin_unlock_irqrestore(&pcibios_lock, flags);
#endif
return PCIBIOS_SUCCESSFUL;
}
int qspan_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned char val)
{
uint temp;
u_char *cp;
#ifdef CONFIG_RPXCLASSIC
unsigned long flags;
#endif
if ((bus > 7) || (dev_fn > 127))
return PCIBIOS_DEVICE_NOT_FOUND;
qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
offset ^= 0x03;
cp = ((u_char *)&temp) + (offset & 0x03);
*cp = val;
#ifdef CONFIG_RPXCLASSIC
/* disable interrupts */
spin_lock_irqsave(&pcibios_lock, flags);
*((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
eieio();
#endif
if (bus == 0)
*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
else
*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
*QS_CONFIG_DATA = temp;
#ifdef CONFIG_RPXCLASSIC
*((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
eieio();
spin_unlock_irqrestore(&pcibios_lock, flags);
#endif
return PCIBIOS_SUCCESSFUL;
}
int qspan_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned short val)
{
uint temp;
ushort *sp;
#ifdef CONFIG_RPXCLASSIC
unsigned long flags;
#endif
if ((bus > 7) || (dev_fn > 127))
return PCIBIOS_DEVICE_NOT_FOUND;
qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
offset ^= 0x02;
sp = ((ushort *)&temp) + ((offset >> 1) & 1);
*sp = val;
#ifdef CONFIG_RPXCLASSIC
/* disable interrupts */
spin_lock_irqsave(&pcibios_lock, flags);
*((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
eieio();
#endif
if (bus == 0)
*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
else
*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
*QS_CONFIG_DATA = temp;
#ifdef CONFIG_RPXCLASSIC
*((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
eieio();
spin_unlock_irqrestore(&pcibios_lock, flags);
#endif
return PCIBIOS_SUCCESSFUL;
}
int qspan_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned int val)
{
#ifdef CONFIG_RPXCLASSIC
unsigned long flags;
#endif
if ((bus > 7) || (dev_fn > 127))
return PCIBIOS_DEVICE_NOT_FOUND;
#ifdef CONFIG_RPXCLASSIC
/* disable interrupts */
spin_lock_irqsave(&pcibios_lock, flags);
*((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
eieio();
#endif
if (bus == 0)
*QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
else
*QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
*(unsigned int *)QS_CONFIG_DATA = val;
#ifdef CONFIG_RPXCLASSIC
*((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
eieio();
spin_unlock_irqrestore(&pcibios_lock, flags);
#endif
return PCIBIOS_SUCCESSFUL;
}
int qspan_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
unsigned short index, unsigned char *bus_ptr,
unsigned char *dev_fn_ptr)
{
int num, devfn;
unsigned int x, vendev;
if (vendor == 0xffff)
return PCIBIOS_BAD_VENDOR_ID;
vendev = (dev_id << 16) + vendor;
num = 0;
for (devfn = 0; devfn < 32; devfn++) {
qspan_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
if (x == vendev) {
if (index == num) {
*bus_ptr = 0;
*dev_fn_ptr = devfn<<3;
return PCIBIOS_SUCCESSFUL;
}
++num;
}
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
int qspan_pcibios_find_class(unsigned int class_code, unsigned short index,
unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
{
int devnr, x, num;
num = 0;
for (devnr = 0; devnr < 32; devnr++) {
qspan_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
if ((x>>8) == class_code) {
if (index == num) {
*bus_ptr = 0;
*dev_fn_ptr = devnr<<3;
return PCIBIOS_SUCCESSFUL;
}
++num;
}
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
void __init
m8xx_pcibios_fixup(void))
{
/* Lots to do here, all board and configuration specific. */
}
void __init
m8xx_setup_pci_ptrs(void))
{
set_config_access_method(qspan);
ppc_md.pcibios_fixup = m8xx_pcibios_fixup;
}

513
arch/ppc/syslib/todc_time.c Normal file
View File

@@ -0,0 +1,513 @@
/*
* arch/ppc/syslib/todc_time.c
*
* Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818
* Real Time Clocks/Timekeepers.
*
* Author: Mark A. Greer
* mgreer@mvista.com
*
* 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/bcd.h>
#include <linux/mc146818rtc.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/time.h>
#include <asm/todc.h>
/*
* Depending on the hardware on your board and your board design, the
* RTC/NVRAM may be accessed either directly (like normal memory) or via
* address/data registers. If your board uses the direct method, set
* 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and
* 'nvram_as1' NULL. If your board uses address/data regs to access nvram,
* set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the
* address of the upper byte (leave NULL if using mc146818), and set
* 'nvram_data' to the address of the 8-bit data register.
*
* In order to break the assumption that the RTC and NVRAM are accessed by
* the same mechanism, you need to explicitly set 'ppc_md.rtc_read_val' and
* 'ppc_md.rtc_write_val', otherwise the values of 'ppc_md.rtc_read_val'
* and 'ppc_md.rtc_write_val' will be used.
*
* Note: Even though the documentation for the various RTC chips say that it
* take up to a second before it starts updating once the 'R' bit is
* cleared, they always seem to update even though we bang on it many
* times a second. This is true, except for the Dallas Semi 1746/1747
* (possibly others). Those chips seem to have a real problem whenever
* we set the 'R' bit before reading them, they basically stop counting.
* --MAG
*/
/*
* 'todc_info' should be initialized in your *_setup.c file to
* point to a fully initialized 'todc_info_t' structure.
* This structure holds all the register offsets for your particular
* TODC/RTC chip.
* TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you.
*/
#ifdef RTC_FREQ_SELECT
#undef RTC_FREQ_SELECT
#define RTC_FREQ_SELECT control_b /* Register A */
#endif
#ifdef RTC_CONTROL
#undef RTC_CONTROL
#define RTC_CONTROL control_a /* Register B */
#endif
#ifdef RTC_INTR_FLAGS
#undef RTC_INTR_FLAGS
#define RTC_INTR_FLAGS watchdog /* Register C */
#endif
#ifdef RTC_VALID
#undef RTC_VALID
#define RTC_VALID interrupts /* Register D */
#endif
/* Access routines when RTC accessed directly (like normal memory) */
u_char
todc_direct_read_val(int addr)
{
return readb((void __iomem *)(todc_info->nvram_data + addr));
}
void
todc_direct_write_val(int addr, unsigned char val)
{
writeb(val, (void __iomem *)(todc_info->nvram_data + addr));
return;
}
/* Access routines for accessing m48txx type chips via addr/data regs */
u_char
todc_m48txx_read_val(int addr)
{
outb(addr, todc_info->nvram_as0);
outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
return inb(todc_info->nvram_data);
}
void
todc_m48txx_write_val(int addr, unsigned char val)
{
outb(addr, todc_info->nvram_as0);
outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
outb(val, todc_info->nvram_data);
return;
}
/* Access routines for accessing mc146818 type chips via addr/data regs */
u_char
todc_mc146818_read_val(int addr)
{
outb_p(addr, todc_info->nvram_as0);
return inb_p(todc_info->nvram_data);
}
void
todc_mc146818_write_val(int addr, unsigned char val)
{
outb_p(addr, todc_info->nvram_as0);
outb_p(val, todc_info->nvram_data);
}
/*
* Routines to make RTC chips with NVRAM buried behind an addr/data pair
* have the NVRAM and clock regs appear at the same level.
* The NVRAM will appear to start at addr 0 and the clock regs will appear
* to start immediately after the NVRAM (actually, start at offset
* todc_info->nvram_size).
*/
static inline u_char
todc_read_val(int addr)
{
u_char val;
if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
if (addr < todc_info->nvram_size) { /* NVRAM */
ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
val = ppc_md.rtc_read_val(todc_info->nvram_data_reg);
}
else { /* Clock Reg */
addr -= todc_info->nvram_size;
val = ppc_md.rtc_read_val(addr);
}
}
else {
val = ppc_md.rtc_read_val(addr);
}
return val;
}
static inline void
todc_write_val(int addr, u_char val)
{
if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
if (addr < todc_info->nvram_size) { /* NVRAM */
ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
ppc_md.rtc_write_val(todc_info->nvram_data_reg, val);
}
else { /* Clock Reg */
addr -= todc_info->nvram_size;
ppc_md.rtc_write_val(addr, val);
}
}
else {
ppc_md.rtc_write_val(addr, val);
}
}
/*
* TODC routines
*
* There is some ugly stuff in that there are assumptions for the mc146818.
*
* Assumptions:
* - todc_info->control_a has the offset as mc146818 Register B reg
* - todc_info->control_b has the offset as mc146818 Register A reg
* - m48txx control reg's write enable or 'W' bit is same as
* mc146818 Register B 'SET' bit (i.e., 0x80)
*
* These assumptions were made to make the code simpler.
*/
long __init
todc_time_init(void)
{
u_char cntl_b;
if (!ppc_md.rtc_read_val)
ppc_md.rtc_read_val = ppc_md.nvram_read_val;
if (!ppc_md.rtc_write_val)
ppc_md.rtc_write_val = ppc_md.nvram_write_val;
cntl_b = todc_read_val(todc_info->control_b);
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
if ((cntl_b & 0x70) != 0x20) {
printk(KERN_INFO "TODC %s %s\n",
"real-time-clock was stopped.",
"Now starting...");
cntl_b &= ~0x70;
cntl_b |= 0x20;
}
todc_write_val(todc_info->control_b, cntl_b);
} else if (todc_info->rtc_type == TODC_TYPE_DS17285) {
u_char mode;
mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A);
/* Make sure countdown clear is not set */
mode &= ~0x40;
/* Enable oscillator, extended register set */
mode |= 0x30;
todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode);
} else if (todc_info->rtc_type == TODC_TYPE_DS1501) {
u_char month;
todc_info->enable_read = TODC_DS1501_CNTL_B_TE;
todc_info->enable_write = TODC_DS1501_CNTL_B_TE;
month = todc_read_val(todc_info->month);
if ((month & 0x80) == 0x80) {
printk(KERN_INFO "TODC %s %s\n",
"real-time-clock was stopped.",
"Now starting...");
month &= ~0x80;
todc_write_val(todc_info->month, month);
}
cntl_b &= ~TODC_DS1501_CNTL_B_TE;
todc_write_val(todc_info->control_b, cntl_b);
} else { /* must be a m48txx type */
u_char cntl_a;
todc_info->enable_read = TODC_MK48TXX_CNTL_A_R;
todc_info->enable_write = TODC_MK48TXX_CNTL_A_W;
cntl_a = todc_read_val(todc_info->control_a);
/* Check & clear STOP bit in control B register */
if (cntl_b & TODC_MK48TXX_DAY_CB) {
printk(KERN_INFO "TODC %s %s\n",
"real-time-clock was stopped.",
"Now starting...");
cntl_a |= todc_info->enable_write;
cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */
todc_write_val(todc_info->control_a, cntl_a);
todc_write_val(todc_info->control_b, cntl_b);
}
/* Make sure READ & WRITE bits are cleared. */
cntl_a &= ~(todc_info->enable_write |
todc_info->enable_read);
todc_write_val(todc_info->control_a, cntl_a);
}
return 0;
}
/*
* There is some ugly stuff in that there are assumptions that for a mc146818,
* the todc_info->control_a has the offset of the mc146818 Register B reg and
* that the register'ss 'SET' bit is the same as the m48txx's write enable
* bit in the control register of the m48txx (i.e., 0x80).
*
* It was done to make the code look simpler.
*/
ulong
todc_get_rtc_time(void)
{
uint year = 0, mon = 0, day = 0, hour = 0, min = 0, sec = 0;
uint limit, i;
u_char save_control, uip = 0;
spin_lock(&rtc_lock);
save_control = todc_read_val(todc_info->control_a);
if (todc_info->rtc_type != TODC_TYPE_MC146818) {
limit = 1;
switch (todc_info->rtc_type) {
case TODC_TYPE_DS1553:
case TODC_TYPE_DS1557:
case TODC_TYPE_DS1743:
case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
case TODC_TYPE_DS1747:
case TODC_TYPE_DS17285:
break;
default:
todc_write_val(todc_info->control_a,
(save_control | todc_info->enable_read));
}
}
else {
limit = 100000000;
}
for (i=0; i<limit; i++) {
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
uip = todc_read_val(todc_info->RTC_FREQ_SELECT);
}
sec = todc_read_val(todc_info->seconds) & 0x7f;
min = todc_read_val(todc_info->minutes) & 0x7f;
hour = todc_read_val(todc_info->hours) & 0x3f;
day = todc_read_val(todc_info->day_of_month) & 0x3f;
mon = todc_read_val(todc_info->month) & 0x1f;
year = todc_read_val(todc_info->year) & 0xff;
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
uip |= todc_read_val(todc_info->RTC_FREQ_SELECT);
if ((uip & RTC_UIP) == 0) break;
}
}
if (todc_info->rtc_type != TODC_TYPE_MC146818) {
switch (todc_info->rtc_type) {
case TODC_TYPE_DS1553:
case TODC_TYPE_DS1557:
case TODC_TYPE_DS1743:
case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
case TODC_TYPE_DS1747:
case TODC_TYPE_DS17285:
break;
default:
save_control &= ~(todc_info->enable_read);
todc_write_val(todc_info->control_a,
save_control);
}
}
spin_unlock(&rtc_lock);
if ((todc_info->rtc_type != TODC_TYPE_MC146818) ||
((save_control & RTC_DM_BINARY) == 0) ||
RTC_ALWAYS_BCD) {
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
year = year + 1900;
if (year < 1970) {
year += 100;
}
return mktime(year, mon, day, hour, min, sec);
}
int
todc_set_rtc_time(unsigned long nowtime)
{
struct rtc_time tm;
u_char save_control, save_freq_select = 0;
spin_lock(&rtc_lock);
to_tm(nowtime, &tm);
save_control = todc_read_val(todc_info->control_a);
/* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */
todc_write_val(todc_info->control_a,
(save_control | todc_info->enable_write));
save_control &= ~(todc_info->enable_write); /* in case it was set */
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT);
todc_write_val(todc_info->RTC_FREQ_SELECT,
save_freq_select | RTC_DIV_RESET2);
}
tm.tm_year = (tm.tm_year - 1900) % 100;
if ((todc_info->rtc_type != TODC_TYPE_MC146818) ||
((save_control & RTC_DM_BINARY) == 0) ||
RTC_ALWAYS_BCD) {
BIN_TO_BCD(tm.tm_sec);
BIN_TO_BCD(tm.tm_min);
BIN_TO_BCD(tm.tm_hour);
BIN_TO_BCD(tm.tm_mon);
BIN_TO_BCD(tm.tm_mday);
BIN_TO_BCD(tm.tm_year);
}
todc_write_val(todc_info->seconds, tm.tm_sec);
todc_write_val(todc_info->minutes, tm.tm_min);
todc_write_val(todc_info->hours, tm.tm_hour);
todc_write_val(todc_info->month, tm.tm_mon);
todc_write_val(todc_info->day_of_month, tm.tm_mday);
todc_write_val(todc_info->year, tm.tm_year);
todc_write_val(todc_info->control_a, save_control);
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select);
}
spin_unlock(&rtc_lock);
return 0;
}
/*
* Manipulates read bit to reliably read seconds at a high rate.
*/
static unsigned char __init todc_read_timereg(int addr)
{
unsigned char save_control = 0, val;
switch (todc_info->rtc_type) {
case TODC_TYPE_DS1553:
case TODC_TYPE_DS1557:
case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
case TODC_TYPE_DS1747:
case TODC_TYPE_DS17285:
case TODC_TYPE_MC146818:
break;
default:
save_control = todc_read_val(todc_info->control_a);
todc_write_val(todc_info->control_a,
(save_control | todc_info->enable_read));
}
val = todc_read_val(addr);
switch (todc_info->rtc_type) {
case TODC_TYPE_DS1553:
case TODC_TYPE_DS1557:
case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
case TODC_TYPE_DS1747:
case TODC_TYPE_DS17285:
case TODC_TYPE_MC146818:
break;
default:
save_control &= ~(todc_info->enable_read);
todc_write_val(todc_info->control_a, save_control);
}
return val;
}
/*
* This was taken from prep_setup.c
* Use the NVRAM RTC to time a second to calibrate the decrementer.
*/
void __init
todc_calibrate_decr(void)
{
ulong freq;
ulong tbl, tbu;
long i, loop_count;
u_char sec;
todc_time_init();
/*
* Actually this is bad for precision, we should have a loop in
* which we only read the seconds counter. todc_read_val writes
* the address bytes on every call and this takes a lot of time.
* Perhaps an nvram_wait_change method returning a time
* stamp with a loop count as parameter would be the solution.
*/
/*
* Need to make sure the tbl doesn't roll over so if tbu increments
* during this test, we need to do it again.
*/
loop_count = 0;
sec = todc_read_timereg(todc_info->seconds) & 0x7f;
do {
tbu = get_tbu();
for (i = 0 ; i < 10000000 ; i++) {/* may take up to 1 second */
tbl = get_tbl();
if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) {
break;
}
}
sec = todc_read_timereg(todc_info->seconds) & 0x7f;
for (i = 0 ; i < 10000000 ; i++) { /* Should take 1 second */
freq = get_tbl();
if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) {
break;
}
}
freq -= tbl;
} while ((get_tbu() != tbu) && (++loop_count < 2));
printk("time_init: decrementer frequency = %lu.%.6lu MHz\n",
freq/1000000, freq%1000000);
tb_ticks_per_jiffy = freq / HZ;
tb_to_us = mulhwu_scale_factor(freq, 1000000);
return;
}

View File

@@ -0,0 +1,157 @@
/*
* arch/ppc/syslib/xilinx_pic.c
*
* Interrupt controller driver for Xilinx Virtex-II Pro.
*
* Author: MontaVista Software, Inc.
* source@mvista.com
*
* 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/xparameters.h>
#include <asm/ibm4xx.h>
/* No one else should require these constants, so define them locally here. */
#define ISR 0 /* Interrupt Status Register */
#define IPR 1 /* Interrupt Pending Register */
#define IER 2 /* Interrupt Enable Register */
#define IAR 3 /* Interrupt Acknowledge Register */
#define SIE 4 /* Set Interrupt Enable bits */
#define CIE 5 /* Clear Interrupt Enable bits */
#define IVR 6 /* Interrupt Vector Register */
#define MER 7 /* Master Enable Register */
#if XPAR_XINTC_USE_DCR == 0
static volatile u32 *intc;
#define intc_out_be32(addr, mask) out_be32((addr), (mask))
#define intc_in_be32(addr) in_be32((addr))
#else
#define intc XPAR_INTC_0_BASEADDR
#define intc_out_be32(addr, mask) mtdcr((addr), (mask))
#define intc_in_be32(addr) mfdcr((addr))
#endif
static void
xilinx_intc_enable(unsigned int irq)
{
unsigned long mask = (0x00000001 << (irq & 31));
pr_debug("enable: %d\n", irq);
intc_out_be32(intc + SIE, mask);
}
static void
xilinx_intc_disable(unsigned int irq)
{
unsigned long mask = (0x00000001 << (irq & 31));
pr_debug("disable: %d\n", irq);
intc_out_be32(intc + CIE, mask);
}
static void
xilinx_intc_disable_and_ack(unsigned int irq)
{
unsigned long mask = (0x00000001 << (irq & 31));
pr_debug("disable_and_ack: %d\n", irq);
intc_out_be32(intc + CIE, mask);
if (!(irq_desc[irq].status & IRQ_LEVEL))
intc_out_be32(intc + IAR, mask); /* ack edge triggered intr */
}
static void
xilinx_intc_end(unsigned int irq)
{
unsigned long mask = (0x00000001 << (irq & 31));
pr_debug("end: %d\n", irq);
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
intc_out_be32(intc + SIE, mask);
/* ack level sensitive intr */
if (irq_desc[irq].status & IRQ_LEVEL)
intc_out_be32(intc + IAR, mask);
}
}
static struct hw_interrupt_type xilinx_intc = {
"Xilinx Interrupt Controller",
NULL,
NULL,
xilinx_intc_enable,
xilinx_intc_disable,
xilinx_intc_disable_and_ack,
xilinx_intc_end,
0
};
int
xilinx_pic_get_irq(struct pt_regs *regs)
{
int irq;
/*
* NOTE: This function is the one that needs to be improved in
* order to handle multiple interrupt controllers. It currently
* is hardcoded to check for interrupts only on the first INTC.
*/
irq = intc_in_be32(intc + IVR);
if (irq != -1)
irq = irq;
pr_debug("get_irq: %d\n", irq);
return (irq);
}
void __init
ppc4xx_pic_init(void)
{
int i;
/*
* NOTE: The assumption here is that NR_IRQS is 32 or less
* (NR_IRQS is 32 for PowerPC 405 cores by default).
*/
#if (NR_IRQS > 32)
#error NR_IRQS > 32 not supported
#endif
#if XPAR_XINTC_USE_DCR == 0
intc = ioremap(XPAR_INTC_0_BASEADDR, 32);
printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n",
(unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc);
#else
printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n",
(unsigned long) XPAR_INTC_0_BASEADDR);
#endif
/*
* Disable all external interrupts until they are
* explicity requested.
*/
intc_out_be32(intc + IER, 0);
/* Acknowledge any pending interrupts just in case. */
intc_out_be32(intc + IAR, ~(u32) 0);
/* Turn on the Master Enable. */
intc_out_be32(intc + MER, 0x3UL);
ppc_md.get_irq = xilinx_pic_get_irq;
for (i = 0; i < NR_IRQS; ++i) {
irq_desc[i].handler = &xilinx_intc;
if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i))
irq_desc[i].status &= ~IRQ_LEVEL;
else
irq_desc[i].status |= IRQ_LEVEL;
}
}