diff options
Diffstat (limited to 'arch/ppc/syslib')
79 files changed, 23351 insertions, 0 deletions
diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile new file mode 100644 index 00000000000..dd418ea3426 --- /dev/null +++ b/arch/ppc/syslib/Makefile @@ -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 diff --git a/arch/ppc/syslib/btext.c b/arch/ppc/syslib/btext.c new file mode 100644 index 00000000000..7734f683617 --- /dev/null +++ b/arch/ppc/syslib/btext.c @@ -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, +}; diff --git a/arch/ppc/syslib/cpc700.h b/arch/ppc/syslib/cpc700.h new file mode 100644 index 00000000000..f2c00253101 --- /dev/null +++ b/arch/ppc/syslib/cpc700.h @@ -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__ */ diff --git a/arch/ppc/syslib/cpc700_pic.c b/arch/ppc/syslib/cpc700_pic.c new file mode 100644 index 00000000000..77470980753 --- /dev/null +++ b/arch/ppc/syslib/cpc700_pic.c @@ -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); +} diff --git a/arch/ppc/syslib/cpc710.h b/arch/ppc/syslib/cpc710.h new file mode 100644 index 00000000000..cc0afd80402 --- /dev/null +++ b/arch/ppc/syslib/cpc710.h @@ -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 */ diff --git a/arch/ppc/syslib/cpm2_common.c b/arch/ppc/syslib/cpm2_common.c new file mode 100644 index 00000000000..ea5e77080e8 --- /dev/null +++ b/arch/ppc/syslib/cpm2_common.c @@ -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); diff --git a/arch/ppc/syslib/cpm2_pic.c b/arch/ppc/syslib/cpm2_pic.c new file mode 100644 index 00000000000..954b07fc1df --- /dev/null +++ b/arch/ppc/syslib/cpm2_pic.c @@ -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; +	} +} diff --git a/arch/ppc/syslib/cpm2_pic.h b/arch/ppc/syslib/cpm2_pic.h new file mode 100644 index 00000000000..97cab8f13a1 --- /dev/null +++ b/arch/ppc/syslib/cpm2_pic.h @@ -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 */ diff --git a/arch/ppc/syslib/dcr.S b/arch/ppc/syslib/dcr.S new file mode 100644 index 00000000000..895f10243a4 --- /dev/null +++ b/arch/ppc/syslib/dcr.S @@ -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 diff --git a/arch/ppc/syslib/gen550.h b/arch/ppc/syslib/gen550.h new file mode 100644 index 00000000000..039d249e19a --- /dev/null +++ b/arch/ppc/syslib/gen550.h @@ -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); diff --git a/arch/ppc/syslib/gen550_dbg.c b/arch/ppc/syslib/gen550_dbg.c new file mode 100644 index 00000000000..9ef0113c83d --- /dev/null +++ b/arch/ppc/syslib/gen550_dbg.c @@ -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 */ diff --git a/arch/ppc/syslib/gen550_kgdb.c b/arch/ppc/syslib/gen550_kgdb.c new file mode 100644 index 00000000000..7239d5d7ddc --- /dev/null +++ b/arch/ppc/syslib/gen550_kgdb.c @@ -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); +} diff --git a/arch/ppc/syslib/gt64260_pic.c b/arch/ppc/syslib/gt64260_pic.c new file mode 100644 index 00000000000..44aa8738545 --- /dev/null +++ b/arch/ppc/syslib/gt64260_pic.c @@ -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 = >64260_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); diff --git a/arch/ppc/syslib/harrier.c b/arch/ppc/syslib/harrier.c new file mode 100644 index 00000000000..a6b3f864579 --- /dev/null +++ b/arch/ppc/syslib/harrier.c @@ -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; +} diff --git a/arch/ppc/syslib/hawk_common.c b/arch/ppc/syslib/hawk_common.c new file mode 100644 index 00000000000..a9911dc3a82 --- /dev/null +++ b/arch/ppc/syslib/hawk_common.c @@ -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; +} diff --git a/arch/ppc/syslib/i8259.c b/arch/ppc/syslib/i8259.c new file mode 100644 index 00000000000..b9391e65014 --- /dev/null +++ b/arch/ppc/syslib/i8259.c @@ -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); +} diff --git a/arch/ppc/syslib/ibm440gp_common.c b/arch/ppc/syslib/ibm440gp_common.c new file mode 100644 index 00000000000..0d6be2d6dd6 --- /dev/null +++ b/arch/ppc/syslib/ibm440gp_common.c @@ -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; +	} +} diff --git a/arch/ppc/syslib/ibm440gp_common.h b/arch/ppc/syslib/ibm440gp_common.h new file mode 100644 index 00000000000..a054d83cb1a --- /dev/null +++ b/arch/ppc/syslib/ibm440gp_common.h @@ -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__ */ diff --git a/arch/ppc/syslib/ibm440gx_common.c b/arch/ppc/syslib/ibm440gx_common.c new file mode 100644 index 00000000000..4ad85e0e023 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.c @@ -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; +} + diff --git a/arch/ppc/syslib/ibm440gx_common.h b/arch/ppc/syslib/ibm440gx_common.h new file mode 100644 index 00000000000..e73aa0411d3 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.h @@ -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__ */ diff --git a/arch/ppc/syslib/ibm440sp_common.c b/arch/ppc/syslib/ibm440sp_common.c new file mode 100644 index 00000000000..417d4cff77a --- /dev/null +++ b/arch/ppc/syslib/ibm440sp_common.c @@ -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; +} diff --git a/arch/ppc/syslib/ibm440sp_common.h b/arch/ppc/syslib/ibm440sp_common.h new file mode 100644 index 00000000000..a21a9906dcc --- /dev/null +++ b/arch/ppc/syslib/ibm440sp_common.h @@ -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__ */ diff --git a/arch/ppc/syslib/ibm44x_common.c b/arch/ppc/syslib/ibm44x_common.c new file mode 100644 index 00000000000..7612e0623f9 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.c @@ -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)); +} diff --git a/arch/ppc/syslib/ibm44x_common.h b/arch/ppc/syslib/ibm44x_common.h new file mode 100644 index 00000000000..b14eb603ce0 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.h @@ -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__ */ diff --git a/arch/ppc/syslib/ibm_ocp.c b/arch/ppc/syslib/ibm_ocp.c new file mode 100644 index 00000000000..3f6e55c7918 --- /dev/null +++ b/arch/ppc/syslib/ibm_ocp.c @@ -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); diff --git a/arch/ppc/syslib/indirect_pci.c b/arch/ppc/syslib/indirect_pci.c new file mode 100644 index 00000000000..a5a752609e2 --- /dev/null +++ b/arch/ppc/syslib/indirect_pci.c @@ -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); +} diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c new file mode 100644 index 00000000000..acb2cde3171 --- /dev/null +++ b/arch/ppc/syslib/ipic.c @@ -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); diff --git a/arch/ppc/syslib/ipic.h b/arch/ppc/syslib/ipic.h new file mode 100644 index 00000000000..2b56a4fcf37 --- /dev/null +++ b/arch/ppc/syslib/ipic.h @@ -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__ */ diff --git a/arch/ppc/syslib/m8260_pci.c b/arch/ppc/syslib/m8260_pci.c new file mode 100644 index 00000000000..bd564fb35ab --- /dev/null +++ b/arch/ppc/syslib/m8260_pci.c @@ -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"); +} diff --git a/arch/ppc/syslib/m8260_pci.h b/arch/ppc/syslib/m8260_pci.h new file mode 100644 index 00000000000..d1352120acd --- /dev/null +++ b/arch/ppc/syslib/m8260_pci.h @@ -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 */ diff --git a/arch/ppc/syslib/m8260_pci_erratum9.c b/arch/ppc/syslib/m8260_pci_erratum9.c new file mode 100644 index 00000000000..9c0582d639e --- /dev/null +++ b/arch/ppc/syslib/m8260_pci_erratum9.c @@ -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); +} diff --git a/arch/ppc/syslib/m8260_setup.c b/arch/ppc/syslib/m8260_setup.c new file mode 100644 index 00000000000..23ea3f694de --- /dev/null +++ b/arch/ppc/syslib/m8260_setup.c @@ -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(); +} diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c new file mode 100644 index 00000000000..c1db2ab1d15 --- /dev/null +++ b/arch/ppc/syslib/m8xx_setup.c @@ -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 +} diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c new file mode 100644 index 00000000000..7838a44bfd1 --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.c @@ -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; +} diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h new file mode 100644 index 00000000000..0d81a9f8155 --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.h @@ -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 */ diff --git a/arch/ppc/syslib/mpc10x_common.c b/arch/ppc/syslib/mpc10x_common.c new file mode 100644 index 00000000000..fd93adfd464 --- /dev/null +++ b/arch/ppc/syslib/mpc10x_common.c @@ -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 diff --git a/arch/ppc/syslib/mpc52xx_devices.c b/arch/ppc/syslib/mpc52xx_devices.c new file mode 100644 index 00000000000..ad5182efca1 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_devices.c @@ -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); diff --git a/arch/ppc/syslib/mpc52xx_pci.c b/arch/ppc/syslib/mpc52xx_pci.c new file mode 100644 index 00000000000..c723efd954a --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pci.c @@ -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"); + +} diff --git a/arch/ppc/syslib/mpc52xx_pci.h b/arch/ppc/syslib/mpc52xx_pci.h new file mode 100644 index 00000000000..04b509a0253 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pci.h @@ -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__ */ diff --git a/arch/ppc/syslib/mpc52xx_pic.c b/arch/ppc/syslib/mpc52xx_pic.c new file mode 100644 index 00000000000..4c4497e6251 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pic.c @@ -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; +} + diff --git a/arch/ppc/syslib/mpc52xx_setup.c b/arch/ppc/syslib/mpc52xx_setup.c new file mode 100644 index 00000000000..bb2374585a7 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_setup.c @@ -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; +} diff --git a/arch/ppc/syslib/mpc52xx_sys.c b/arch/ppc/syslib/mpc52xx_sys.c new file mode 100644 index 00000000000..9a0f90aa8aa --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_sys.c @@ -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, +	}, +}; diff --git a/arch/ppc/syslib/mpc83xx_devices.c b/arch/ppc/syslib/mpc83xx_devices.c new file mode 100644 index 00000000000..5c1a919eaab --- /dev/null +++ b/arch/ppc/syslib/mpc83xx_devices.c @@ -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); diff --git a/arch/ppc/syslib/mpc83xx_sys.c b/arch/ppc/syslib/mpc83xx_sys.c new file mode 100644 index 00000000000..29aa6335002 --- /dev/null +++ b/arch/ppc/syslib/mpc83xx_sys.c @@ -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, +	}, +}; diff --git a/arch/ppc/syslib/mpc85xx_devices.c b/arch/ppc/syslib/mpc85xx_devices.c new file mode 100644 index 00000000000..a231795ee26 --- /dev/null +++ b/arch/ppc/syslib/mpc85xx_devices.c @@ -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); diff --git a/arch/ppc/syslib/mpc85xx_sys.c b/arch/ppc/syslib/mpc85xx_sys.c new file mode 100644 index 00000000000..d806a92a940 --- /dev/null +++ b/arch/ppc/syslib/mpc85xx_sys.c @@ -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, +	}, +}; diff --git a/arch/ppc/syslib/mv64360_pic.c b/arch/ppc/syslib/mv64360_pic.c new file mode 100644 index 00000000000..74d8996418e --- /dev/null +++ b/arch/ppc/syslib/mv64360_pic.c @@ -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); diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c new file mode 100644 index 00000000000..7b241e7876b --- /dev/null +++ b/arch/ppc/syslib/mv64x60.c @@ -0,0 +1,2392 @@ +/* + * arch/ppc/syslib/mv64x60.c + * + * Common routines for the Marvell/Galileo Discovery line of host bridges + * (gt64260, mv64360, mv64460, ...). + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/bootmem.h> +#include <linux/spinlock.h> +#include <linux/mv643xx.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/delay.h> +#include <asm/mv64x60.h> + + +u8		mv64x60_pci_exclude_bridge = 1; +spinlock_t	mv64x60_lock = SPIN_LOCK_UNLOCKED; + +static phys_addr_t 	mv64x60_bridge_pbase = 0; +static void 		*mv64x60_bridge_vbase = 0; +static u32		mv64x60_bridge_type = MV64x60_TYPE_INVALID; +static u32		mv64x60_bridge_rev = 0; + +static u32 gt64260_translate_size(u32 base, u32 size, u32 num_bits); +static u32 gt64260_untranslate_size(u32 base, u32 size, u32 num_bits); +static void gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, +	u32 window, u32 base); +static void gt64260_set_pci2regs_window(struct mv64x60_handle *bh, +	struct pci_controller *hose, u32 bus, u32 base); +static u32 gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_disable_all_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si); +static void gt64260a_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si); +static void gt64260b_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si); + +static u32 mv64360_translate_size(u32 base, u32 size, u32 num_bits); +static u32 mv64360_untranslate_size(u32 base, u32 size, u32 num_bits); +static void mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, +	u32 window, u32 base); +static void mv64360_set_pci2regs_window(struct mv64x60_handle *bh, +	struct pci_controller *hose, u32 bus, u32 base); +static u32 mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_disable_all_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si); +static void mv64360_config_io2mem_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si, +	u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]); +static void mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base); +static void mv64360_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si); +static void mv64460_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si); + + +/* + * Define tables that have the chip-specific info for each type of + * Marvell bridge chip. + */ +static struct mv64x60_chip_info gt64260a_ci __initdata = { /* GT64260A */ +	.translate_size		= gt64260_translate_size, +	.untranslate_size	= gt64260_untranslate_size, +	.set_pci2mem_window	= gt64260_set_pci2mem_window, +	.set_pci2regs_window	= gt64260_set_pci2regs_window, +	.is_enabled_32bit	= gt64260_is_enabled_32bit, +	.enable_window_32bit	= gt64260_enable_window_32bit, +	.disable_window_32bit	= gt64260_disable_window_32bit, +	.enable_window_64bit	= gt64260_enable_window_64bit, +	.disable_window_64bit	= gt64260_disable_window_64bit, +	.disable_all_windows	= gt64260_disable_all_windows, +	.chip_specific_init	= gt64260a_chip_specific_init, +	.window_tab_32bit	= gt64260_32bit_windows, +	.window_tab_64bit	= gt64260_64bit_windows, +}; + +static struct mv64x60_chip_info gt64260b_ci __initdata = { /* GT64260B */ +	.translate_size		= gt64260_translate_size, +	.untranslate_size	= gt64260_untranslate_size, +	.set_pci2mem_window	= gt64260_set_pci2mem_window, +	.set_pci2regs_window	= gt64260_set_pci2regs_window, +	.is_enabled_32bit	= gt64260_is_enabled_32bit, +	.enable_window_32bit	= gt64260_enable_window_32bit, +	.disable_window_32bit	= gt64260_disable_window_32bit, +	.enable_window_64bit	= gt64260_enable_window_64bit, +	.disable_window_64bit	= gt64260_disable_window_64bit, +	.disable_all_windows	= gt64260_disable_all_windows, +	.chip_specific_init	= gt64260b_chip_specific_init, +	.window_tab_32bit	= gt64260_32bit_windows, +	.window_tab_64bit	= gt64260_64bit_windows, +}; + +static struct mv64x60_chip_info mv64360_ci __initdata = { /* MV64360 */ +	.translate_size		= mv64360_translate_size, +	.untranslate_size	= mv64360_untranslate_size, +	.set_pci2mem_window	= mv64360_set_pci2mem_window, +	.set_pci2regs_window	= mv64360_set_pci2regs_window, +	.is_enabled_32bit	= mv64360_is_enabled_32bit, +	.enable_window_32bit	= mv64360_enable_window_32bit, +	.disable_window_32bit	= mv64360_disable_window_32bit, +	.enable_window_64bit	= mv64360_enable_window_64bit, +	.disable_window_64bit	= mv64360_disable_window_64bit, +	.disable_all_windows	= mv64360_disable_all_windows, +	.config_io2mem_windows	= mv64360_config_io2mem_windows, +	.set_mpsc2regs_window	= mv64360_set_mpsc2regs_window, +	.chip_specific_init	= mv64360_chip_specific_init, +	.window_tab_32bit	= mv64360_32bit_windows, +	.window_tab_64bit	= mv64360_64bit_windows, +}; + +static struct mv64x60_chip_info mv64460_ci __initdata = { /* MV64460 */ +	.translate_size		= mv64360_translate_size, +	.untranslate_size	= mv64360_untranslate_size, +	.set_pci2mem_window	= mv64360_set_pci2mem_window, +	.set_pci2regs_window	= mv64360_set_pci2regs_window, +	.is_enabled_32bit	= mv64360_is_enabled_32bit, +	.enable_window_32bit	= mv64360_enable_window_32bit, +	.disable_window_32bit	= mv64360_disable_window_32bit, +	.enable_window_64bit	= mv64360_enable_window_64bit, +	.disable_window_64bit	= mv64360_disable_window_64bit, +	.disable_all_windows	= mv64360_disable_all_windows, +	.config_io2mem_windows	= mv64360_config_io2mem_windows, +	.set_mpsc2regs_window	= mv64360_set_mpsc2regs_window, +	.chip_specific_init	= mv64460_chip_specific_init, +	.window_tab_32bit	= mv64360_32bit_windows, +	.window_tab_64bit	= mv64360_64bit_windows, +}; + +/* + ***************************************************************************** + * + *	Platform Device Definitions + * + ***************************************************************************** + */ +#ifdef CONFIG_SERIAL_MPSC +static struct mpsc_shared_pdata mv64x60_mpsc_shared_pdata = { +	.mrr_val		= 0x3ffffe38, +	.rcrr_val		= 0, +	.tcrr_val		= 0, +	.intr_cause_val		= 0, +	.intr_mask_val		= 0, +}; + +static struct resource mv64x60_mpsc_shared_resources[] = { +	/* Do not change the order of the IORESOURCE_MEM resources */ +	[0] = { +		.name	= "mpsc routing base", +		.start	= MV64x60_MPSC_ROUTING_OFFSET, +		.end	= MV64x60_MPSC_ROUTING_OFFSET + +			MPSC_ROUTING_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.name	= "sdma intr base", +		.start	= MV64x60_SDMA_INTR_OFFSET, +		.end	= MV64x60_SDMA_INTR_OFFSET + +			MPSC_SDMA_INTR_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +}; + +static struct platform_device mpsc_shared_device = { /* Shared device */ +	.name		= MPSC_SHARED_NAME, +	.id		= 0, +	.num_resources	= ARRAY_SIZE(mv64x60_mpsc_shared_resources), +	.resource	= mv64x60_mpsc_shared_resources, +	.dev = { +		.platform_data = &mv64x60_mpsc_shared_pdata, +	}, +}; + +static struct mpsc_pdata mv64x60_mpsc0_pdata = { +	.mirror_regs		= 0, +	.cache_mgmt		= 0, +	.max_idle		= 0, +	.default_baud		= 9600, +	.default_bits		= 8, +	.default_parity		= 'n', +	.default_flow		= 'n', +	.chr_1_val		= 0x00000000, +	.chr_2_val		= 0x00000000, +	.chr_10_val		= 0x00000003, +	.mpcr_val		= 0, +	.bcr_val		= 0, +	.brg_can_tune		= 0, +	.brg_clk_src		= 8,		/* Default to TCLK */ +	.brg_clk_freq		= 100000000,	/* Default to 100 MHz */ +}; + +static struct resource mv64x60_mpsc0_resources[] = { +	/* Do not change the order of the IORESOURCE_MEM resources */ +	[0] = { +		.name	= "mpsc 0 base", +		.start	= MV64x60_MPSC_0_OFFSET, +		.end	= MV64x60_MPSC_0_OFFSET + MPSC_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.name	= "sdma 0 base", +		.start	= MV64x60_SDMA_0_OFFSET, +		.end	= MV64x60_SDMA_0_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[2] = { +		.name	= "brg 0 base", +		.start	= MV64x60_BRG_0_OFFSET, +		.end	= MV64x60_BRG_0_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[3] = { +		.name	= "sdma 0 irq", +		.start	= MV64x60_IRQ_SDMA_0, +		.end	= MV64x60_IRQ_SDMA_0, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct platform_device mpsc0_device = { +	.name		= MPSC_CTLR_NAME, +	.id		= 0, +	.num_resources	= ARRAY_SIZE(mv64x60_mpsc0_resources), +	.resource	= mv64x60_mpsc0_resources, +	.dev = { +		.platform_data = &mv64x60_mpsc0_pdata, +	}, +}; + +static struct mpsc_pdata mv64x60_mpsc1_pdata = { +	.mirror_regs		= 0, +	.cache_mgmt		= 0, +	.max_idle		= 0, +	.default_baud		= 9600, +	.default_bits		= 8, +	.default_parity		= 'n', +	.default_flow		= 'n', +	.chr_1_val		= 0x00000000, +	.chr_1_val		= 0x00000000, +	.chr_2_val		= 0x00000000, +	.chr_10_val		= 0x00000003, +	.mpcr_val		= 0, +	.bcr_val		= 0, +	.brg_can_tune		= 0, +	.brg_clk_src		= 8,		/* Default to TCLK */ +	.brg_clk_freq		= 100000000,	/* Default to 100 MHz */ +}; + +static struct resource mv64x60_mpsc1_resources[] = { +	/* Do not change the order of the IORESOURCE_MEM resources */ +	[0] = { +		.name	= "mpsc 1 base", +		.start	= MV64x60_MPSC_1_OFFSET, +		.end	= MV64x60_MPSC_1_OFFSET + MPSC_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.name	= "sdma 1 base", +		.start	= MV64x60_SDMA_1_OFFSET, +		.end	= MV64x60_SDMA_1_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[2] = { +		.name	= "brg 1 base", +		.start	= MV64x60_BRG_1_OFFSET, +		.end	= MV64x60_BRG_1_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[3] = { +		.name	= "sdma 1 irq", +		.start	= MV64360_IRQ_SDMA_1, +		.end	= MV64360_IRQ_SDMA_1, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct platform_device mpsc1_device = { +	.name		= MPSC_CTLR_NAME, +	.id		= 1, +	.num_resources	= ARRAY_SIZE(mv64x60_mpsc1_resources), +	.resource	= mv64x60_mpsc1_resources, +	.dev = { +		.platform_data = &mv64x60_mpsc1_pdata, +	}, +}; +#endif + +#ifdef CONFIG_MV643XX_ETH +static struct resource mv64x60_eth_shared_resources[] = { +	[0] = { +		.name	= "ethernet shared base", +		.start	= MV643XX_ETH_SHARED_REGS, +		.end	= MV643XX_ETH_SHARED_REGS + +					MV643XX_ETH_SHARED_REGS_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +}; + +static struct platform_device mv64x60_eth_shared_device = { +	.name		= MV643XX_ETH_SHARED_NAME, +	.id		= 0, +	.num_resources	= ARRAY_SIZE(mv64x60_eth_shared_resources), +	.resource	= mv64x60_eth_shared_resources, +}; + +#ifdef CONFIG_MV643XX_ETH_0 +static struct resource mv64x60_eth0_resources[] = { +	[0] = { +		.name	= "eth0 irq", +		.start	= MV64x60_IRQ_ETH_0, +		.end	= MV64x60_IRQ_ETH_0, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct mv643xx_eth_platform_data eth0_pd; + +static struct platform_device eth0_device = { +	.name		= MV643XX_ETH_NAME, +	.id		= 0, +	.num_resources	= ARRAY_SIZE(mv64x60_eth0_resources), +	.resource	= mv64x60_eth0_resources, +	.dev = { +		.platform_data = ð0_pd, +	}, +}; +#endif + +#ifdef CONFIG_MV643XX_ETH_1 +static struct resource mv64x60_eth1_resources[] = { +	[0] = { +		.name	= "eth1 irq", +		.start	= MV64x60_IRQ_ETH_1, +		.end	= MV64x60_IRQ_ETH_1, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct mv643xx_eth_platform_data eth1_pd; + +static struct platform_device eth1_device = { +	.name		= MV643XX_ETH_NAME, +	.id		= 1, +	.num_resources	= ARRAY_SIZE(mv64x60_eth1_resources), +	.resource	= mv64x60_eth1_resources, +	.dev = { +		.platform_data = ð1_pd, +	}, +}; +#endif + +#ifdef CONFIG_MV643XX_ETH_2 +static struct resource mv64x60_eth2_resources[] = { +	[0] = { +		.name	= "eth2 irq", +		.start	= MV64x60_IRQ_ETH_2, +		.end	= MV64x60_IRQ_ETH_2, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct mv643xx_eth_platform_data eth2_pd; + +static struct platform_device eth2_device = { +	.name		= MV643XX_ETH_NAME, +	.id		= 2, +	.num_resources	= ARRAY_SIZE(mv64x60_eth2_resources), +	.resource	= mv64x60_eth2_resources, +	.dev = { +		.platform_data = ð2_pd, +	}, +}; +#endif +#endif + +#ifdef	CONFIG_I2C_MV64XXX +static struct mv64xxx_i2c_pdata mv64xxx_i2c_pdata = { +	.freq_m			= 8, +	.freq_n			= 3, +	.timeout		= 1000, /* Default timeout of 1 second */ +	.retries		= 1, +}; + +static struct resource mv64xxx_i2c_resources[] = { +	/* Do not change the order of the IORESOURCE_MEM resources */ +	[0] = { +		.name	= "mv64xxx i2c base", +		.start	= MV64XXX_I2C_OFFSET, +		.end	= MV64XXX_I2C_OFFSET + MV64XXX_I2C_REG_BLOCK_SIZE - 1, +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.name	= "mv64xxx i2c irq", +		.start	= MV64x60_IRQ_I2C, +		.end	= MV64x60_IRQ_I2C, +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct platform_device i2c_device = { +	.name		= MV64XXX_I2C_CTLR_NAME, +	.id		= 0, +	.num_resources	= ARRAY_SIZE(mv64xxx_i2c_resources), +	.resource	= mv64xxx_i2c_resources, +	.dev = { +		.platform_data = &mv64xxx_i2c_pdata, +	}, +}; +#endif + +static struct platform_device *mv64x60_pd_devs[] __initdata = { +#ifdef CONFIG_SERIAL_MPSC +	&mpsc_shared_device, +	&mpsc0_device, +	&mpsc1_device, +#endif +#ifdef CONFIG_MV643XX_ETH +	&mv64x60_eth_shared_device, +#endif +#ifdef CONFIG_MV643XX_ETH_0 +	ð0_device, +#endif +#ifdef CONFIG_MV643XX_ETH_1 +	ð1_device, +#endif +#ifdef CONFIG_MV643XX_ETH_2 +	ð2_device, +#endif +#ifdef	CONFIG_I2C_MV64XXX +	&i2c_device, +#endif +}; + +/* + ***************************************************************************** + * + *	Bridge Initialization Routines + * + ***************************************************************************** + */ +/* + * mv64x60_init() + * + * Initialze the bridge based on setting passed in via 'si'.  The bridge + * handle, 'bh', will be set so that it can be used to make subsequent + * calls to routines in this file. + */ +int __init +mv64x60_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si) +{ +	u32	mem_windows[MV64x60_CPU2MEM_WINDOWS][2]; + +	if (ppc_md.progress) +		ppc_md.progress("mv64x60 initialization", 0x0); + +	spin_lock_init(&mv64x60_lock); +	mv64x60_early_init(bh, si); + +	if (mv64x60_get_type(bh) || mv64x60_setup_for_chip(bh)) { +		iounmap(bh->v_base); +		bh->v_base = 0; +		if (ppc_md.progress) +			ppc_md.progress("mv64x60_init: Can't determine chip",0); +		return -1; +	} + +	bh->ci->disable_all_windows(bh, si); +	mv64x60_get_mem_windows(bh, mem_windows); +	mv64x60_config_cpu2mem_windows(bh, si, mem_windows); + +	if (bh->ci->config_io2mem_windows) +		bh->ci->config_io2mem_windows(bh, si, mem_windows); +	if (bh->ci->set_mpsc2regs_window) +		bh->ci->set_mpsc2regs_window(bh, si->phys_reg_base); + +	if (si->pci_1.enable_bus) { +		bh->io_base_b = (u32)ioremap(si->pci_1.pci_io.cpu_base, +			si->pci_1.pci_io.size); +		isa_io_base = bh->io_base_b; +	} + +	if (si->pci_0.enable_bus) { +		bh->io_base_a = (u32)ioremap(si->pci_0.pci_io.cpu_base, +			si->pci_0.pci_io.size); +		isa_io_base = bh->io_base_a; + +		mv64x60_alloc_hose(bh, MV64x60_PCI0_CONFIG_ADDR, +			MV64x60_PCI0_CONFIG_DATA, &bh->hose_a); +		mv64x60_config_resources(bh->hose_a, &si->pci_0, bh->io_base_a); +		mv64x60_config_pci_params(bh->hose_a, &si->pci_0); + +		mv64x60_config_cpu2pci_windows(bh, &si->pci_0, 0); +		mv64x60_config_pci2mem_windows(bh, bh->hose_a, &si->pci_0, 0, +			mem_windows); +		bh->ci->set_pci2regs_window(bh, bh->hose_a, 0, +			si->phys_reg_base); +	} + +	if (si->pci_1.enable_bus) { +		mv64x60_alloc_hose(bh, MV64x60_PCI1_CONFIG_ADDR, +			MV64x60_PCI1_CONFIG_DATA, &bh->hose_b); +		mv64x60_config_resources(bh->hose_b, &si->pci_1, bh->io_base_b); +		mv64x60_config_pci_params(bh->hose_b, &si->pci_1); + +		mv64x60_config_cpu2pci_windows(bh, &si->pci_1, 1); +		mv64x60_config_pci2mem_windows(bh, bh->hose_b, &si->pci_1, 1, +			mem_windows); +		bh->ci->set_pci2regs_window(bh, bh->hose_b, 1, +			si->phys_reg_base); +	} + +	bh->ci->chip_specific_init(bh, si); +	mv64x60_pd_fixup(bh, mv64x60_pd_devs, ARRAY_SIZE(mv64x60_pd_devs)); + +	return 0; +} + +/* + * mv64x60_early_init() + * + * Do some bridge work that must take place before we start messing with + * the bridge for real. + */ +void __init +mv64x60_early_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si) +{ +	struct pci_controller	hose_a, hose_b; + +	memset(bh, 0, sizeof(*bh)); + +	bh->p_base = si->phys_reg_base; +	bh->v_base = ioremap(bh->p_base, MV64x60_INTERNAL_SPACE_SIZE); + +	mv64x60_bridge_pbase = bh->p_base; +	mv64x60_bridge_vbase = bh->v_base; + +	/* Assuming pci mode [reserved] bits 4:5 on 64260 are 0 */ +	bh->pci_mode_a = mv64x60_read(bh, MV64x60_PCI0_MODE) & +		MV64x60_PCIMODE_MASK; +	bh->pci_mode_b = mv64x60_read(bh, MV64x60_PCI1_MODE) & +		MV64x60_PCIMODE_MASK; + +	/* Need temporary hose structs to call mv64x60_set_bus() */ +	memset(&hose_a, 0, sizeof(hose_a)); +	memset(&hose_b, 0, sizeof(hose_b)); +	setup_indirect_pci_nomap(&hose_a, bh->v_base + MV64x60_PCI0_CONFIG_ADDR, +		bh->v_base + MV64x60_PCI0_CONFIG_DATA); +	setup_indirect_pci_nomap(&hose_b, bh->v_base + MV64x60_PCI1_CONFIG_ADDR, +		bh->v_base + MV64x60_PCI1_CONFIG_DATA); +	bh->hose_a = &hose_a; +	bh->hose_b = &hose_b; + +	mv64x60_set_bus(bh, 0, 0); +	mv64x60_set_bus(bh, 1, 0); + +	bh->hose_a = NULL; +	bh->hose_b = NULL; + +	/* Clear bit 0 of PCI addr decode control so PCI->CPU remap 1:1 */ +	mv64x60_clr_bits(bh, MV64x60_PCI0_PCI_DECODE_CNTL, 0x00000001); +	mv64x60_clr_bits(bh, MV64x60_PCI1_PCI_DECODE_CNTL, 0x00000001); + +	/* Bit 12 MUST be 0; set bit 27--don't auto-update cpu remap regs */ +	mv64x60_clr_bits(bh, MV64x60_CPU_CONFIG, (1<<12)); +	mv64x60_set_bits(bh, MV64x60_CPU_CONFIG, (1<<27)); + +	mv64x60_set_bits(bh, MV64x60_PCI0_TO_RETRY, 0xffff); +	mv64x60_set_bits(bh, MV64x60_PCI1_TO_RETRY, 0xffff); + +	return; +} + +/* + ***************************************************************************** + * + *	Window Config Routines + * + ***************************************************************************** + */ +/* + * mv64x60_get_32bit_window() + * + * Determine the base address and size of a 32-bit window on the bridge. + */ +void __init +mv64x60_get_32bit_window(struct mv64x60_handle *bh, u32 window, +	u32 *base, u32 *size) +{ +	u32	val, base_reg, size_reg, base_bits, size_bits; +	u32	(*get_from_field)(u32 val, u32 num_bits); + +	base_reg = bh->ci->window_tab_32bit[window].base_reg; + +	if (base_reg != 0) { +		size_reg  = bh->ci->window_tab_32bit[window].size_reg; +		base_bits = bh->ci->window_tab_32bit[window].base_bits; +		size_bits = bh->ci->window_tab_32bit[window].size_bits; +		get_from_field= bh->ci->window_tab_32bit[window].get_from_field; + +		val = mv64x60_read(bh, base_reg); +		*base = get_from_field(val, base_bits); + +		if (size_reg != 0) { +			val = mv64x60_read(bh, size_reg); +			val = get_from_field(val, size_bits); +			*size = bh->ci->untranslate_size(*base, val, size_bits); +		} +		else +			*size = 0; +	} +	else { +		*base = 0; +		*size = 0; +	} + +	pr_debug("get 32bit window: %d, base: 0x%x, size: 0x%x\n", +		window, *base, *size); + +	return; +} + +/* + * mv64x60_set_32bit_window() + * + * Set the base address and size of a 32-bit window on the bridge. + */ +void __init +mv64x60_set_32bit_window(struct mv64x60_handle *bh, u32 window, +	u32 base, u32 size, u32 other_bits) +{ +	u32	val, base_reg, size_reg, base_bits, size_bits; +	u32	(*map_to_field)(u32 val, u32 num_bits); + +	pr_debug("set 32bit window: %d, base: 0x%x, size: 0x%x, other: 0x%x\n", +		window, base, size, other_bits); + +	base_reg = bh->ci->window_tab_32bit[window].base_reg; + +	if (base_reg != 0) { +		size_reg  = bh->ci->window_tab_32bit[window].size_reg; +		base_bits = bh->ci->window_tab_32bit[window].base_bits; +		size_bits = bh->ci->window_tab_32bit[window].size_bits; +		map_to_field = bh->ci->window_tab_32bit[window].map_to_field; + +		val = map_to_field(base, base_bits) | other_bits; +		mv64x60_write(bh, base_reg, val); + +		if (size_reg != 0) { +			val = bh->ci->translate_size(base, size, size_bits); +			val = map_to_field(val, size_bits); +			mv64x60_write(bh, size_reg, val); +		} + +		(void)mv64x60_read(bh, base_reg); /* Flush FIFO */ +	} + +	return; +} + +/* + * mv64x60_get_64bit_window() + * + * Determine the base address and size of a 64-bit window on the bridge. + */ +void __init +mv64x60_get_64bit_window(struct mv64x60_handle *bh, u32 window, +	u32 *base_hi, u32 *base_lo, u32 *size) +{ +	u32	val, base_lo_reg, size_reg, base_lo_bits, size_bits; +	u32	(*get_from_field)(u32 val, u32 num_bits); + +	base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg; + +	if (base_lo_reg != 0) { +		size_reg = bh->ci->window_tab_64bit[window].size_reg; +		base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits; +		size_bits = bh->ci->window_tab_64bit[window].size_bits; +		get_from_field= bh->ci->window_tab_64bit[window].get_from_field; + +		*base_hi = mv64x60_read(bh, +			bh->ci->window_tab_64bit[window].base_hi_reg); + +		val = mv64x60_read(bh, base_lo_reg); +		*base_lo = get_from_field(val, base_lo_bits); + +		if (size_reg != 0) { +			val = mv64x60_read(bh, size_reg); +			val = get_from_field(val, size_bits); +			*size = bh->ci->untranslate_size(*base_lo, val, +								size_bits); +		} +		else +			*size = 0; +	} +	else { +		*base_hi = 0; +		*base_lo = 0; +		*size = 0; +	} + +	pr_debug("get 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " +		"size: 0x%x\n", window, *base_hi, *base_lo, *size); + +	return; +} + +/* + * mv64x60_set_64bit_window() + * + * Set the base address and size of a 64-bit window on the bridge. + */ +void __init +mv64x60_set_64bit_window(struct mv64x60_handle *bh, u32 window, +	u32 base_hi, u32 base_lo, u32 size, u32 other_bits) +{ +	u32	val, base_lo_reg, size_reg, base_lo_bits, size_bits; +	u32	(*map_to_field)(u32 val, u32 num_bits); + +	pr_debug("set 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " +		"size: 0x%x, other: 0x%x\n", +		window, base_hi, base_lo, size, other_bits); + +	base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg; + +	if (base_lo_reg != 0) { +		size_reg = bh->ci->window_tab_64bit[window].size_reg; +		base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits; +		size_bits = bh->ci->window_tab_64bit[window].size_bits; +		map_to_field = bh->ci->window_tab_64bit[window].map_to_field; + +		mv64x60_write(bh, bh->ci->window_tab_64bit[window].base_hi_reg, +			base_hi); + +		val = map_to_field(base_lo, base_lo_bits) | other_bits; +		mv64x60_write(bh, base_lo_reg, val); + +		if (size_reg != 0) { +			val = bh->ci->translate_size(base_lo, size, size_bits); +			val = map_to_field(val, size_bits); +			mv64x60_write(bh, size_reg, val); +		} + +		(void)mv64x60_read(bh, base_lo_reg); /* Flush FIFO */ +	} + +	return; +} + +/* + * mv64x60_mask() + * + * Take the high-order 'num_bits' of 'val' & mask off low bits. + */ +u32 __init +mv64x60_mask(u32 val, u32 num_bits) +{ +	return val & (0xffffffff << (32 - num_bits)); +} + +/* + * mv64x60_shift_left() + * + * Take the low-order 'num_bits' of 'val', shift left to align at bit 31 (MSB). + */ +u32 __init +mv64x60_shift_left(u32 val, u32 num_bits) +{ +	return val << (32 - num_bits); +} + +/* + * mv64x60_shift_right() + * + * Take the high-order 'num_bits' of 'val', shift right to align at bit 0 (LSB). + */ +u32 __init +mv64x60_shift_right(u32 val, u32 num_bits) +{ +	return val >> (32 - num_bits); +} + +/* + ***************************************************************************** + * + *	Chip Identification Routines + * + ***************************************************************************** + */ +/* + * mv64x60_get_type() + * + * Determine the type of bridge chip we have. + */ +int __init +mv64x60_get_type(struct mv64x60_handle *bh) +{ +	struct pci_controller hose; +	u16	val; +	u8	save_exclude; + +	memset(&hose, 0, sizeof(hose)); +	setup_indirect_pci_nomap(&hose, bh->v_base + MV64x60_PCI0_CONFIG_ADDR, +		bh->v_base + MV64x60_PCI0_CONFIG_DATA); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	/* Sanity check of bridge's Vendor ID */ +	early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID, &val); + +	if (val != PCI_VENDOR_ID_MARVELL) { +		mv64x60_pci_exclude_bridge = save_exclude; +		return -1; +	} + +	/* Get the revision of the chip */ +	early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_CLASS_REVISION, +		&val); +	bh->rev = (u32)(val & 0xff); + +	/* Figure out the type of Marvell bridge it is */ +	early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_DEVICE_ID, &val); +	mv64x60_pci_exclude_bridge = save_exclude; + +	switch (val) { +	case PCI_DEVICE_ID_MARVELL_GT64260: +		switch (bh->rev) { +		case GT64260_REV_A: +			bh->type = MV64x60_TYPE_GT64260A; +			break; + +		default: +			printk(KERN_WARNING "Unsupported GT64260 rev %04x\n", +				bh->rev); +			/* Assume its similar to a 'B' rev and fallthru */ +		case GT64260_REV_B: +			bh->type = MV64x60_TYPE_GT64260B; +			break; +		} +		break; + +	case PCI_DEVICE_ID_MARVELL_MV64360: +		/* Marvell won't tell me how to distinguish a 64361 & 64362 */ +		bh->type = MV64x60_TYPE_MV64360; +		break; + +	case PCI_DEVICE_ID_MARVELL_MV64460: +		bh->type = MV64x60_TYPE_MV64460; +		break; + +	default: +		printk(KERN_ERR "Unknown Marvell bridge type %04x\n", val); +		return -1; +	} + +	/* Hang onto bridge type & rev for PIC code */ +	mv64x60_bridge_type = bh->type; +	mv64x60_bridge_rev = bh->rev; + +	return 0; +} + +/* + * mv64x60_setup_for_chip() + * + * Set 'bh' to use the proper set of routine for the bridge chip that we have. + */ +int __init +mv64x60_setup_for_chip(struct mv64x60_handle *bh) +{ +	int	rc = 0; + +	/* Set up chip-specific info based on the chip/bridge type */ +	switch(bh->type) { +	case MV64x60_TYPE_GT64260A: +		bh->ci = >64260a_ci; +		break; + +	case MV64x60_TYPE_GT64260B: +		bh->ci = >64260b_ci; +		break; + +	case MV64x60_TYPE_MV64360: +		bh->ci = &mv64360_ci; +		break; + +	case MV64x60_TYPE_MV64460: +		bh->ci = &mv64460_ci; +		break; + +	case MV64x60_TYPE_INVALID: +	default: +		if (ppc_md.progress) +			ppc_md.progress("mv64x60: Unsupported bridge", 0x0); +		printk(KERN_ERR "mv64x60: Unsupported bridge\n"); +		rc = -1; +	} + +	return rc; +} + +/* + * mv64x60_get_bridge_vbase() + * + * Return the virtual address of the bridge's registers. + */ +void * +mv64x60_get_bridge_vbase(void) +{ +	return mv64x60_bridge_vbase; +} + +/* + * mv64x60_get_bridge_type() + * + * Return the type of bridge on the platform. + */ +u32 +mv64x60_get_bridge_type(void) +{ +	return mv64x60_bridge_type; +} + +/* + * mv64x60_get_bridge_rev() + * + * Return the revision of the bridge on the platform. + */ +u32 +mv64x60_get_bridge_rev(void) +{ +	return mv64x60_bridge_rev; +} + +/* + ***************************************************************************** + * + *	System Memory Window Related Routines + * + ***************************************************************************** + */ +/* + * mv64x60_get_mem_size() + * + * Calculate the amount of memory that the memory controller is set up for. + * This should only be used by board-specific code if there is no other + * way to determine the amount of memory in the system. + */ +u32 __init +mv64x60_get_mem_size(u32 bridge_base, u32 chip_type) +{ +	struct mv64x60_handle	bh; +	u32	mem_windows[MV64x60_CPU2MEM_WINDOWS][2]; +	u32	rc = 0; + +	memset(&bh, 0, sizeof(bh)); + +	bh.type = chip_type; +	bh.v_base = (void *)bridge_base; + +	if (!mv64x60_setup_for_chip(&bh)) { +		mv64x60_get_mem_windows(&bh, mem_windows); +		rc = mv64x60_calc_mem_size(&bh, mem_windows); +	} + +	return rc; +} + +/* + * mv64x60_get_mem_windows() + * + * Get the values in the memory controller & return in the 'mem_windows' array. + */ +void __init +mv64x60_get_mem_windows(struct mv64x60_handle *bh, +	u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ +	u32	i, win; + +	for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) +		if (bh->ci->is_enabled_32bit(bh, win)) +			mv64x60_get_32bit_window(bh, win, +				&mem_windows[i][0], &mem_windows[i][1]); +		else { +			mem_windows[i][0] = 0; +			mem_windows[i][1] = 0; +		} + +	return; +} + +/* + * mv64x60_calc_mem_size() + * + * Using the memory controller register values in 'mem_windows', determine + * how much memory it is set up for. + */ +u32 __init +mv64x60_calc_mem_size(struct mv64x60_handle *bh, +	u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ +	u32	i, total = 0; + +	for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) +		total += mem_windows[i][1]; + +	return total; +} + +/* + ***************************************************************************** + * + *	CPU->System MEM, PCI Config Routines + * + ***************************************************************************** + */ +/* + * mv64x60_config_cpu2mem_windows() + * + * Configure CPU->Memory windows on the bridge. + */ +static u32 prot_tab[] __initdata = { +	MV64x60_CPU_PROT_0_WIN, MV64x60_CPU_PROT_1_WIN, +	MV64x60_CPU_PROT_2_WIN, MV64x60_CPU_PROT_3_WIN +}; + +static u32 cpu_snoop_tab[] __initdata = { +	MV64x60_CPU_SNOOP_0_WIN, MV64x60_CPU_SNOOP_1_WIN, +	MV64x60_CPU_SNOOP_2_WIN, MV64x60_CPU_SNOOP_3_WIN +}; + +void __init +mv64x60_config_cpu2mem_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si, +	u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ +	u32	i, win; + +	/* Set CPU protection & snoop windows */ +	for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) +		if (bh->ci->is_enabled_32bit(bh, win)) { +			mv64x60_set_32bit_window(bh, prot_tab[i], +				mem_windows[i][0], mem_windows[i][1], +				si->cpu_prot_options[i]); +			bh->ci->enable_window_32bit(bh, prot_tab[i]); + +			if (bh->ci->window_tab_32bit[cpu_snoop_tab[i]]. +								base_reg != 0) { +				mv64x60_set_32bit_window(bh, cpu_snoop_tab[i], +					mem_windows[i][0], mem_windows[i][1], +					si->cpu_snoop_options[i]); +				bh->ci->enable_window_32bit(bh, +					cpu_snoop_tab[i]); +			} + +		} + +	return; +} + +/* + * mv64x60_config_cpu2pci_windows() + * + * Configure the CPU->PCI windows for one of the PCI buses. + */ +static u32 win_tab[2][4] __initdata = { +	{ MV64x60_CPU2PCI0_IO_WIN, MV64x60_CPU2PCI0_MEM_0_WIN, +	  MV64x60_CPU2PCI0_MEM_1_WIN, MV64x60_CPU2PCI0_MEM_2_WIN }, +	{ MV64x60_CPU2PCI1_IO_WIN, MV64x60_CPU2PCI1_MEM_0_WIN, +	  MV64x60_CPU2PCI1_MEM_1_WIN, MV64x60_CPU2PCI1_MEM_2_WIN }, +}; + +static u32 remap_tab[2][4] __initdata = { +	{ MV64x60_CPU2PCI0_IO_REMAP_WIN, MV64x60_CPU2PCI0_MEM_0_REMAP_WIN, +	  MV64x60_CPU2PCI0_MEM_1_REMAP_WIN, MV64x60_CPU2PCI0_MEM_2_REMAP_WIN }, +	{ MV64x60_CPU2PCI1_IO_REMAP_WIN, MV64x60_CPU2PCI1_MEM_0_REMAP_WIN, +	  MV64x60_CPU2PCI1_MEM_1_REMAP_WIN, MV64x60_CPU2PCI1_MEM_2_REMAP_WIN } +}; + +void __init +mv64x60_config_cpu2pci_windows(struct mv64x60_handle *bh, +	struct mv64x60_pci_info *pi, u32 bus) +{ +	int	i; + +	if (pi->pci_io.size > 0) { +		mv64x60_set_32bit_window(bh, win_tab[bus][0], +			pi->pci_io.cpu_base, pi->pci_io.size, pi->pci_io.swap); +		mv64x60_set_32bit_window(bh, remap_tab[bus][0], +			pi->pci_io.pci_base_lo, 0, 0); +		bh->ci->enable_window_32bit(bh, win_tab[bus][0]); +	} +	else /* Actually, the window should already be disabled */ +		bh->ci->disable_window_32bit(bh, win_tab[bus][0]); + +	for (i=0; i<3; i++) +		if (pi->pci_mem[i].size > 0) { +			mv64x60_set_32bit_window(bh, win_tab[bus][i+1], +				pi->pci_mem[i].cpu_base, pi->pci_mem[i].size, +				pi->pci_mem[i].swap); +			mv64x60_set_64bit_window(bh, remap_tab[bus][i+1], +				pi->pci_mem[i].pci_base_hi, +				pi->pci_mem[i].pci_base_lo, 0, 0); +			bh->ci->enable_window_32bit(bh, win_tab[bus][i+1]); +		} +		else /* Actually, the window should already be disabled */ +			bh->ci->disable_window_32bit(bh, win_tab[bus][i+1]); + +	return; +} + +/* + ***************************************************************************** + * + *	PCI->System MEM Config Routines + * + ***************************************************************************** + */ +/* + * mv64x60_config_pci2mem_windows() + * + * Configure the PCI->Memory windows on the bridge. + */ +static u32 pci_acc_tab[2][4] __initdata = { +	{ MV64x60_PCI02MEM_ACC_CNTL_0_WIN, MV64x60_PCI02MEM_ACC_CNTL_1_WIN, +	  MV64x60_PCI02MEM_ACC_CNTL_2_WIN, MV64x60_PCI02MEM_ACC_CNTL_3_WIN }, +	{ MV64x60_PCI12MEM_ACC_CNTL_0_WIN, MV64x60_PCI12MEM_ACC_CNTL_1_WIN, +	  MV64x60_PCI12MEM_ACC_CNTL_2_WIN, MV64x60_PCI12MEM_ACC_CNTL_3_WIN } +}; + +static u32 pci_snoop_tab[2][4] __initdata = { +	{ MV64x60_PCI02MEM_SNOOP_0_WIN, MV64x60_PCI02MEM_SNOOP_1_WIN, +	  MV64x60_PCI02MEM_SNOOP_2_WIN, MV64x60_PCI02MEM_SNOOP_3_WIN }, +	{ MV64x60_PCI12MEM_SNOOP_0_WIN, MV64x60_PCI12MEM_SNOOP_1_WIN, +	  MV64x60_PCI12MEM_SNOOP_2_WIN, MV64x60_PCI12MEM_SNOOP_3_WIN } +}; + +static u32 pci_size_tab[2][4] __initdata = { +	{ MV64x60_PCI0_MEM_0_SIZE, MV64x60_PCI0_MEM_1_SIZE, +	  MV64x60_PCI0_MEM_2_SIZE, MV64x60_PCI0_MEM_3_SIZE }, +	{ MV64x60_PCI1_MEM_0_SIZE, MV64x60_PCI1_MEM_1_SIZE, +	  MV64x60_PCI1_MEM_2_SIZE, MV64x60_PCI1_MEM_3_SIZE } +}; + +void __init +mv64x60_config_pci2mem_windows(struct mv64x60_handle *bh, +	struct pci_controller *hose, struct mv64x60_pci_info *pi, +	u32 bus, u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ +	u32	i, win; + +	/* +	 * Set the access control, snoop, BAR size, and window base addresses. +	 * PCI->MEM windows base addresses will match exactly what the +	 * CPU->MEM windows are. +	 */ +	for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) +		if (bh->ci->is_enabled_32bit(bh, win)) { +			mv64x60_set_64bit_window(bh, +				pci_acc_tab[bus][i], 0, +				mem_windows[i][0], mem_windows[i][1], +				pi->acc_cntl_options[i]); +			bh->ci->enable_window_64bit(bh, pci_acc_tab[bus][i]); + +			if (bh->ci->window_tab_64bit[ +				pci_snoop_tab[bus][i]].base_lo_reg != 0) { + +				mv64x60_set_64bit_window(bh, +					pci_snoop_tab[bus][i], 0, +					mem_windows[i][0], mem_windows[i][1], +					pi->snoop_options[i]); +				bh->ci->enable_window_64bit(bh, +					pci_snoop_tab[bus][i]); +			} + +			bh->ci->set_pci2mem_window(hose, bus, i, +				mem_windows[i][0]); +			mv64x60_write(bh, pci_size_tab[bus][i], +				mv64x60_mask(mem_windows[i][1] - 1, 20)); + +			/* Enable the window */ +			mv64x60_clr_bits(bh, ((bus == 0) ? +				MV64x60_PCI0_BAR_ENABLE : +				MV64x60_PCI1_BAR_ENABLE), (1 << i)); +		} + +	return; +} + +/* + ***************************************************************************** + * + *	Hose & Resource Alloc/Init Routines + * + ***************************************************************************** + */ +/* + * mv64x60_alloc_hoses() + * + * Allocate the PCI hose structures for the bridge's PCI buses. + */ +void __init +mv64x60_alloc_hose(struct mv64x60_handle *bh, u32 cfg_addr, u32 cfg_data, +	struct pci_controller **hose) +{ +	*hose = pcibios_alloc_controller(); +	setup_indirect_pci_nomap(*hose, bh->v_base + cfg_addr, +		bh->v_base + cfg_data); +	return; +} + +/* + * mv64x60_config_resources() + * + * Calculate the offsets, etc. for the hose structures to reflect all of + * the address remapping that happens as you go from CPU->PCI and PCI->MEM. + */ +void __init +mv64x60_config_resources(struct pci_controller *hose, +	struct mv64x60_pci_info *pi, u32 io_base) +{ +	int		i; +	/* 2 hoses; 4 resources/hose; string <= 64 bytes */ +	static char	s[2][4][64]; + +	if (pi->pci_io.size != 0) { +		sprintf(s[hose->index][0], "PCI hose %d I/O Space", +			hose->index); +		pci_init_resource(&hose->io_resource, io_base - isa_io_base, +			io_base - isa_io_base + pi->pci_io.size - 1, +			IORESOURCE_IO, s[hose->index][0]); +		hose->io_space.start = pi->pci_io.pci_base_lo; +		hose->io_space.end = pi->pci_io.pci_base_lo + pi->pci_io.size-1; +		hose->io_base_phys = pi->pci_io.cpu_base; +		hose->io_base_virt = (void *)isa_io_base; +	} + +	for (i=0; i<3; i++) +		if (pi->pci_mem[i].size != 0) { +			sprintf(s[hose->index][i+1], "PCI hose %d MEM Space %d", +				hose->index, i); +			pci_init_resource(&hose->mem_resources[i], +				pi->pci_mem[i].cpu_base, +				pi->pci_mem[i].cpu_base + pi->pci_mem[i].size-1, +				IORESOURCE_MEM, s[hose->index][i+1]); +		} + +	hose->mem_space.end = pi->pci_mem[0].pci_base_lo + +						pi->pci_mem[0].size - 1; +	hose->pci_mem_offset = pi->pci_mem[0].cpu_base - +						pi->pci_mem[0].pci_base_lo; +	return; +} + +/* + * mv64x60_config_pci_params() + * + * Configure a hose's PCI config space parameters. + */ +void __init +mv64x60_config_pci_params(struct pci_controller *hose, +	struct mv64x60_pci_info *pi) +{ +	u32	devfn; +	u16	u16_val; +	u8	save_exclude; + +	devfn = PCI_DEVFN(0,0); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; + +	/* Set class code to indicate host bridge */ +	u16_val = PCI_CLASS_BRIDGE_HOST; /* 0x0600 (host bridge) */ +	early_write_config_word(hose, 0, devfn, PCI_CLASS_DEVICE, u16_val); + +	/* Enable bridge to be PCI master & respond to PCI MEM cycles */ +	early_read_config_word(hose, 0, devfn, PCI_COMMAND, &u16_val); +	u16_val &= ~(PCI_COMMAND_IO | PCI_COMMAND_INVALIDATE | +		PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK); +	u16_val |= pi->pci_cmd_bits | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; +	early_write_config_word(hose, 0, devfn, PCI_COMMAND, u16_val); + +	/* Set latency timer, cache line size, clear BIST */ +	u16_val = (pi->latency_timer << 8) | (L1_CACHE_LINE_SIZE >> 2); +	early_write_config_word(hose, 0, devfn, PCI_CACHE_LINE_SIZE, u16_val); + +	mv64x60_pci_exclude_bridge = save_exclude; +	return; +} + +/* + ***************************************************************************** + * + *	PCI Related Routine + * + ***************************************************************************** + */ +/* + * mv64x60_set_bus() + * + * Set the bus number for the hose directly under the bridge. + */ +void __init +mv64x60_set_bus(struct mv64x60_handle *bh, u32 bus, u32 child_bus) +{ +	struct pci_controller	*hose; +	u32	pci_mode, p2p_cfg, pci_cfg_offset, val; +	u8	save_exclude; + +	if (bus == 0) { +		pci_mode = bh->pci_mode_a; +		p2p_cfg = MV64x60_PCI0_P2P_CONFIG; +		pci_cfg_offset = 0x64; +		hose = bh->hose_a; +	} +	else { +		pci_mode = bh->pci_mode_b; +		p2p_cfg = MV64x60_PCI1_P2P_CONFIG; +		pci_cfg_offset = 0xe4; +		hose = bh->hose_b; +	} + +	child_bus &= 0xff; +	val = mv64x60_read(bh, p2p_cfg); + +	if (pci_mode == MV64x60_PCIMODE_CONVENTIONAL) { +		val &= 0xe0000000; /* Force dev num to 0, turn off P2P bridge */ +		val |= (child_bus << 16) | 0xff; +		mv64x60_write(bh, p2p_cfg, val); +		(void)mv64x60_read(bh, p2p_cfg); /* Flush FIFO */ +	} +	else { /* PCI-X */ +		/* +		 * Need to use the current bus/dev number (that's in the +		 * P2P CONFIG reg) to access the bridge's pci config space. +		 */ +		save_exclude = mv64x60_pci_exclude_bridge; +		mv64x60_pci_exclude_bridge = 0; +		early_write_config_dword(hose, (val & 0x00ff0000) >> 16, +			PCI_DEVFN(((val & 0x1f000000) >> 24), 0), +			pci_cfg_offset, child_bus << 8); +		mv64x60_pci_exclude_bridge = save_exclude; +	} + +	return; +} + +/* + * mv64x60_pci_exclude_device() + * + * This routine is used to make the bridge not appear when the + * PCI subsystem is accessing PCI devices (in PCI config space). + */ +int +mv64x60_pci_exclude_device(u8 bus, u8 devfn) +{ +	struct pci_controller	*hose; + +	hose = pci_bus_to_hose(bus); + +	/* Skip slot 0 on both hoses */ +	if ((mv64x60_pci_exclude_bridge == 1) && (PCI_SLOT(devfn) == 0) && +		(hose->first_busno == bus)) + +		return PCIBIOS_DEVICE_NOT_FOUND; +	else +		return PCIBIOS_SUCCESSFUL; +} /* mv64x60_pci_exclude_device() */ + +/* + ***************************************************************************** + * + *	Platform Device Routines + * + ***************************************************************************** + */ + +/* + * mv64x60_pd_fixup() + * + * Need to add the base addr of where the bridge's regs are mapped in the + * physical addr space so drivers can ioremap() them. + */ +void __init +mv64x60_pd_fixup(struct mv64x60_handle *bh, struct platform_device *pd_devs[], +	u32 entries) +{ +	struct resource	*r; +	u32		i, j; + +	for (i=0; i<entries; i++) { +		j = 0; + +		while ((r = platform_get_resource(pd_devs[i],IORESOURCE_MEM,j)) +			!= NULL) { + +			r->start += bh->p_base; +			r->end += bh->p_base; +			j++; +		} +	} + +	return; +} + +/* + * mv64x60_add_pds() + * + * Add the mv64x60 platform devices to the list of platform devices. + */ +static int __init +mv64x60_add_pds(void) +{ +	return platform_add_devices(mv64x60_pd_devs, +		ARRAY_SIZE(mv64x60_pd_devs)); +} +arch_initcall(mv64x60_add_pds); + +/* + ***************************************************************************** + * + *	GT64260-Specific Routines + * + ***************************************************************************** + */ +/* + * gt64260_translate_size() + * + * On the GT64260, the size register is really the "top" address of the window. + */ +static u32 __init +gt64260_translate_size(u32 base, u32 size, u32 num_bits) +{ +	return base + mv64x60_mask(size - 1, num_bits); +} + +/* + * gt64260_untranslate_size() + * + * Translate the top address of a window into a window size. + */ +static u32 __init +gt64260_untranslate_size(u32 base, u32 size, u32 num_bits) +{ +	if (size >= base) +		size = size - base + (1 << (32 - num_bits)); +	else +		size = 0; + +	return size; +} + +/* + * gt64260_set_pci2mem_window() + * + * The PCI->MEM window registers are actually in PCI config space so need + * to set them by setting the correct config space BARs. + */ +static u32 gt64260_reg_addrs[2][4] __initdata = { +	{ 0x10, 0x14, 0x18, 0x1c }, { 0x90, 0x94, 0x98, 0x9c } +}; + +static void __init +gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window, +	u32 base) +{ +	u8	save_exclude; + +	pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window, +		hose->index, base); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	early_write_config_dword(hose, 0, PCI_DEVFN(0, 0), +		gt64260_reg_addrs[bus][window], mv64x60_mask(base, 20) | 0x8); +	mv64x60_pci_exclude_bridge = save_exclude; + +	return; +} + +/* + * gt64260_set_pci2regs_window() + * + * Set where the bridge's registers appear in PCI MEM space. + */ +static u32 gt64260_offset[2] __initdata = {0x20, 0xa0}; + +static void __init +gt64260_set_pci2regs_window(struct mv64x60_handle *bh, +	struct pci_controller *hose, u32 bus, u32 base) +{ +	u8	save_exclude; + +	pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index, +		base); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	early_write_config_dword(hose, 0, PCI_DEVFN(0,0), gt64260_offset[bus], +		(base << 16)); +	mv64x60_pci_exclude_bridge = save_exclude; + +	return; +} + +/* + * gt64260_is_enabled_32bit() + * + * On a GT64260, a window is enabled iff its top address is >= to its base + * address. + */ +static u32 __init +gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window) +{ +	u32	rc = 0; + +	if ((gt64260_32bit_windows[window].base_reg != 0) && +		(gt64260_32bit_windows[window].size_reg != 0) && +		((mv64x60_read(bh, gt64260_32bit_windows[window].size_reg) & +			((1 << gt64260_32bit_windows[window].size_bits) - 1)) >= +		 (mv64x60_read(bh, gt64260_32bit_windows[window].base_reg) & +			((1 << gt64260_32bit_windows[window].base_bits) - 1)))) + +		rc = 1; + +	return rc; +} + +/* + * gt64260_enable_window_32bit() + * + * On the GT64260, a window is enabled iff the top address is >= to the base + * address of the window.  Since the window has already been configured by + * the time this routine is called, we have nothing to do here. + */ +static void __init +gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ +	pr_debug("enable 32bit window: %d\n", window); +	return; +} + +/* + * gt64260_disable_window_32bit() + * + * On a GT64260, you disable a window by setting its top address to be less + * than its base address. + */ +static void __init +gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ +	pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", +		window, gt64260_32bit_windows[window].base_reg, +		gt64260_32bit_windows[window].size_reg); + +	if ((gt64260_32bit_windows[window].base_reg != 0) && +		(gt64260_32bit_windows[window].size_reg != 0)) { + +		/* To disable, make bottom reg higher than top reg */ +		mv64x60_write(bh, gt64260_32bit_windows[window].base_reg,0xfff); +		mv64x60_write(bh, gt64260_32bit_windows[window].size_reg, 0); +	} + +	return; +} + +/* + * gt64260_enable_window_64bit() + * + * On the GT64260, a window is enabled iff the top address is >= to the base + * address of the window.  Since the window has already been configured by + * the time this routine is called, we have nothing to do here. + */ +static void __init +gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ +	pr_debug("enable 64bit window: %d\n", window); +	return;	/* Enabled when window configured (i.e., when top >= base) */ +} + +/* + * gt64260_disable_window_64bit() + * + * On a GT64260, you disable a window by setting its top address to be less + * than its base address. + */ +static void __init +gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ +	pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", +		window, gt64260_64bit_windows[window].base_lo_reg, +		gt64260_64bit_windows[window].size_reg); + +	if ((gt64260_64bit_windows[window].base_lo_reg != 0) && +		(gt64260_64bit_windows[window].size_reg != 0)) { + +		/* To disable, make bottom reg higher than top reg */ +		mv64x60_write(bh, gt64260_64bit_windows[window].base_lo_reg, +									0xfff); +		mv64x60_write(bh, gt64260_64bit_windows[window].base_hi_reg, 0); +		mv64x60_write(bh, gt64260_64bit_windows[window].size_reg, 0); +	} + +	return; +} + +/* + * gt64260_disable_all_windows() + * + * The GT64260 has several windows that aren't represented in the table of + * windows at the top of this file.  This routine turns all of them off + * except for the memory controller windows, of course. + */ +static void __init +gt64260_disable_all_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si) +{ +	u32	i, preserve; + +	/* Disable 32bit windows (don't disable cpu->mem windows) */ +	for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) { +		if (i < 32) +			preserve = si->window_preserve_mask_32_lo & (1 << i); +		else +			preserve = si->window_preserve_mask_32_hi & (1<<(i-32)); + +		if (!preserve) +			gt64260_disable_window_32bit(bh, i); +	} + +	/* Disable 64bit windows */ +	for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++) +		if (!(si->window_preserve_mask_64 & (1<<i))) +			gt64260_disable_window_64bit(bh, i); + +	/* Turn off cpu protection windows not in gt64260_32bit_windows[] */ +	mv64x60_write(bh, GT64260_CPU_PROT_BASE_4, 0xfff); +	mv64x60_write(bh, GT64260_CPU_PROT_SIZE_4, 0); +	mv64x60_write(bh, GT64260_CPU_PROT_BASE_5, 0xfff); +	mv64x60_write(bh, GT64260_CPU_PROT_SIZE_5, 0); +	mv64x60_write(bh, GT64260_CPU_PROT_BASE_6, 0xfff); +	mv64x60_write(bh, GT64260_CPU_PROT_SIZE_6, 0); +	mv64x60_write(bh, GT64260_CPU_PROT_BASE_7, 0xfff); +	mv64x60_write(bh, GT64260_CPU_PROT_SIZE_7, 0); + +	/* Turn off PCI->MEM access cntl wins not in gt64260_64bit_windows[] */ +	mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0xfff); +	mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_HI, 0); +	mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_SIZE, 0); +	mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0xfff); +	mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_HI, 0); +	mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_SIZE, 0); +	mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_LO, 0xfff); +	mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_HI, 0); +	mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_SIZE, 0); +	mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_LO, 0xfff); +	mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_HI, 0); +	mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_SIZE, 0); + +	mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0xfff); +	mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_HI, 0); +	mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_SIZE, 0); +	mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0xfff); +	mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_HI, 0); +	mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_SIZE, 0); +	mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_LO, 0xfff); +	mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_HI, 0); +	mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_SIZE, 0); +	mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_LO, 0xfff); +	mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_HI, 0); +	mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_SIZE, 0); + +	/* Disable all PCI-><whatever> windows */ +	mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x07fffdff); +	mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x07fffdff); + +	/* +	 * Some firmwares enable a bunch of intr sources +	 * for the PCI INT output pins. +	 */ +	mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_LO, 0); +	mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_HI, 0); +	mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_LO, 0); +	mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_HI, 0); +	mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_LO, 0); +	mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_HI, 0); +	mv64x60_write(bh, GT64260_IC_CPU_INT_0_MASK, 0); +	mv64x60_write(bh, GT64260_IC_CPU_INT_1_MASK, 0); +	mv64x60_write(bh, GT64260_IC_CPU_INT_2_MASK, 0); +	mv64x60_write(bh, GT64260_IC_CPU_INT_3_MASK, 0); + +	return; +} + +/* + * gt64260a_chip_specific_init() + * + * Implement errata work arounds for the GT64260A. + */ +static void __init +gt64260a_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC +	struct resource	*r; +#endif +#if !defined(CONFIG_NOT_COHERENT_CACHE) +	u32	val; +	u8	save_exclude; +#endif + +	if (si->pci_0.enable_bus) +		mv64x60_set_bits(bh, MV64x60_PCI0_CMD, +			((1<<4) | (1<<5) | (1<<9) | (1<<13))); + +	if (si->pci_1.enable_bus) +		mv64x60_set_bits(bh, MV64x60_PCI1_CMD, +			((1<<4) | (1<<5) | (1<<9) | (1<<13))); + +	/* +	 * Dave Wilhardt found that bit 4 in the PCI Command registers must +	 * be set if you are using cache coherency. +	 */ +#if !defined(CONFIG_NOT_COHERENT_CACHE) +	/* Res #MEM-4 -- cpu read buffer to buffer 1 */ +	if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40) +		mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26)); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	if (si->pci_0.enable_bus) { +		early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, &val); +		val |= PCI_COMMAND_INVALIDATE; +		early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, val); +	} + +	if (si->pci_1.enable_bus) { +		early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, &val); +		val |= PCI_COMMAND_INVALIDATE; +		early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, val); +	} +	mv64x60_pci_exclude_bridge = save_exclude; +#endif + +	/* Disable buffer/descriptor snooping */ +	mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); +	mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); + +#ifdef CONFIG_SERIAL_MPSC +	mv64x60_mpsc0_pdata.mirror_regs = 1; +	mv64x60_mpsc0_pdata.cache_mgmt = 1; +	mv64x60_mpsc1_pdata.mirror_regs = 1; +	mv64x60_mpsc1_pdata.cache_mgmt = 1; + +	if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) +		!= NULL) { + +		r->start = MV64x60_IRQ_SDMA_0; +		r->end = MV64x60_IRQ_SDMA_0; +	} +#endif + +	return; +} + +/* + * gt64260b_chip_specific_init() + * + * Implement errata work arounds for the GT64260B. + */ +static void __init +gt64260b_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC +	struct resource	*r; +#endif +#if !defined(CONFIG_NOT_COHERENT_CACHE) +	u32	val; +	u8	save_exclude; +#endif + +	if (si->pci_0.enable_bus) +		mv64x60_set_bits(bh, MV64x60_PCI0_CMD, +			((1<<4) | (1<<5) | (1<<9) | (1<<13))); + +	if (si->pci_1.enable_bus) +		mv64x60_set_bits(bh, MV64x60_PCI1_CMD, +			((1<<4) | (1<<5) | (1<<9) | (1<<13))); + +	/* +	 * Dave Wilhardt found that bit 4 in the PCI Command registers must +	 * be set if you are using cache coherency. +	 */ +#if !defined(CONFIG_NOT_COHERENT_CACHE) +	mv64x60_set_bits(bh, GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH, 0xf); + +	/* Res #MEM-4 -- cpu read buffer to buffer 1 */ +	if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40) +		mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26)); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	if (si->pci_0.enable_bus) { +		early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, &val); +		val |= PCI_COMMAND_INVALIDATE; +		early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, val); +	} + +	if (si->pci_1.enable_bus) { +		early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, &val); +		val |= PCI_COMMAND_INVALIDATE; +		early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), +			PCI_COMMAND, val); +	} +	mv64x60_pci_exclude_bridge = save_exclude; +#endif + +	/* Disable buffer/descriptor snooping */ +	mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); +	mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); + +#ifdef CONFIG_SERIAL_MPSC +	/* +	 * The 64260B is not supposed to have the bug where the MPSC & ENET +	 * can't access cache coherent regions.  However, testing has shown +	 * that the MPSC, at least, still has this bug. +	 */ +	mv64x60_mpsc0_pdata.cache_mgmt = 1; +	mv64x60_mpsc1_pdata.cache_mgmt = 1; + +	if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) +		!= NULL) { + +		r->start = MV64x60_IRQ_SDMA_0; +		r->end = MV64x60_IRQ_SDMA_0; +	} +#endif + +	return; +} + +/* + ***************************************************************************** + * + *	MV64360-Specific Routines + * + ***************************************************************************** + */ +/* + * mv64360_translate_size() + * + * On the MV64360, the size register is set similar to the size you get + * from a pci config space BAR register.  That is, programmed from LSB to MSB + * as a sequence of 1's followed by a sequence of 0's. IOW, "size -1" with the + * assumption that the size is a power of 2. + */ +static u32 __init +mv64360_translate_size(u32 base_addr, u32 size, u32 num_bits) +{ +	return mv64x60_mask(size - 1, num_bits); +} + +/* + * mv64360_untranslate_size() + * + * Translate the size register value of a window into a window size. + */ +static u32 __init +mv64360_untranslate_size(u32 base_addr, u32 size, u32 num_bits) +{ +	if (size > 0) { +		size >>= (32 - num_bits); +		size++; +		size <<= (32 - num_bits); +	} + +	return size; +} + +/* + * mv64360_set_pci2mem_window() + * + * The PCI->MEM window registers are actually in PCI config space so need + * to set them by setting the correct config space BARs. + */ +struct { +	u32	fcn; +	u32	base_hi_bar; +	u32	base_lo_bar; +} static mv64360_reg_addrs[2][4] __initdata = { +	{{ 0, 0x14, 0x10 }, { 0, 0x1c, 0x18 }, +	 { 1, 0x14, 0x10 }, { 1, 0x1c, 0x18 }}, +	{{ 0, 0x94, 0x90 }, { 0, 0x9c, 0x98 }, +	 { 1, 0x94, 0x90 }, { 1, 0x9c, 0x98 }} +}; + +static void __init +mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window, +	u32 base) +{ +	u8 save_exclude; + +	pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window, +		hose->index, base); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	early_write_config_dword(hose, 0, +		PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn), +		mv64360_reg_addrs[bus][window].base_hi_bar, 0); +	early_write_config_dword(hose, 0, +		PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn), +		mv64360_reg_addrs[bus][window].base_lo_bar, +		mv64x60_mask(base,20) | 0xc); +	mv64x60_pci_exclude_bridge = save_exclude; + +	return; +} + +/* + * mv64360_set_pci2regs_window() + * + * Set where the bridge's registers appear in PCI MEM space. + */ +static u32 mv64360_offset[2][2] __initdata = {{0x20, 0x24}, {0xa0, 0xa4}}; + +static void __init +mv64360_set_pci2regs_window(struct mv64x60_handle *bh, +	struct pci_controller *hose, u32 bus, u32 base) +{ +	u8	save_exclude; + +	pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index, +		base); + +	save_exclude = mv64x60_pci_exclude_bridge; +	mv64x60_pci_exclude_bridge = 0; +	early_write_config_dword(hose, 0, PCI_DEVFN(0,0), +		mv64360_offset[bus][0], (base << 16)); +	early_write_config_dword(hose, 0, PCI_DEVFN(0,0), +		mv64360_offset[bus][1], 0); +	mv64x60_pci_exclude_bridge = save_exclude; + +	return; +} + +/* + * mv64360_is_enabled_32bit() + * + * On a MV64360, a window is enabled by either clearing a bit in the + * CPU BAR Enable reg or setting a bit in the window's base reg. + * Note that this doesn't work for windows on the PCI slave side but we don't + * check those so its okay. + */ +static u32 __init +mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window) +{ +	u32	extra, rc = 0; + +	if (((mv64360_32bit_windows[window].base_reg != 0) && +		(mv64360_32bit_windows[window].size_reg != 0)) || +		(window == MV64x60_CPU2SRAM_WIN)) { + +		extra = mv64360_32bit_windows[window].extra; + +		switch (extra & MV64x60_EXTRA_MASK) { +		case MV64x60_EXTRA_CPUWIN_ENAB: +			rc = (mv64x60_read(bh, MV64360_CPU_BAR_ENABLE) & +				(1 << (extra & 0x1f))) == 0; +			break; + +		case MV64x60_EXTRA_CPUPROT_ENAB: +			rc = (mv64x60_read(bh, +				mv64360_32bit_windows[window].base_reg) & +					(1 << (extra & 0x1f))) != 0; +			break; + +		case MV64x60_EXTRA_ENET_ENAB: +			rc = (mv64x60_read(bh, MV64360_ENET2MEM_BAR_ENABLE) & +				(1 << (extra & 0x7))) == 0; +			break; + +		case MV64x60_EXTRA_MPSC_ENAB: +			rc = (mv64x60_read(bh, MV64360_MPSC2MEM_BAR_ENABLE) & +				(1 << (extra & 0x3))) == 0; +			break; + +		case MV64x60_EXTRA_IDMA_ENAB: +			rc = (mv64x60_read(bh, MV64360_IDMA2MEM_BAR_ENABLE) & +				(1 << (extra & 0x7))) == 0; +			break; + +		default: +			printk(KERN_ERR "mv64360_is_enabled: %s\n", +				"32bit table corrupted"); +		} +	} + +	return rc; +} + +/* + * mv64360_enable_window_32bit() + * + * On a MV64360, a window is enabled by either clearing a bit in the + * CPU BAR Enable reg or setting a bit in the window's base reg. + */ +static void __init +mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ +	u32	extra; + +	pr_debug("enable 32bit window: %d\n", window); + +	if (((mv64360_32bit_windows[window].base_reg != 0) && +		(mv64360_32bit_windows[window].size_reg != 0)) || +		(window == MV64x60_CPU2SRAM_WIN)) { + +		extra = mv64360_32bit_windows[window].extra; + +		switch (extra & MV64x60_EXTRA_MASK) { +		case MV64x60_EXTRA_CPUWIN_ENAB: +			mv64x60_clr_bits(bh, MV64360_CPU_BAR_ENABLE, +				(1 << (extra & 0x1f))); +			break; + +		case MV64x60_EXTRA_CPUPROT_ENAB: +			mv64x60_set_bits(bh, +				mv64360_32bit_windows[window].base_reg, +				(1 << (extra & 0x1f))); +			break; + +		case MV64x60_EXTRA_ENET_ENAB: +			mv64x60_clr_bits(bh, MV64360_ENET2MEM_BAR_ENABLE, +				(1 << (extra & 0x7))); +			break; + +		case MV64x60_EXTRA_MPSC_ENAB: +			mv64x60_clr_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE, +				(1 << (extra & 0x3))); +			break; + +		case MV64x60_EXTRA_IDMA_ENAB: +			mv64x60_clr_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE, +				(1 << (extra & 0x7))); +			break; + +		default: +			printk(KERN_ERR "mv64360_enable: %s\n", +				"32bit table corrupted"); +		} +	} + +	return; +} + +/* + * mv64360_disable_window_32bit() + * + * On a MV64360, a window is disabled by either setting a bit in the + * CPU BAR Enable reg or clearing a bit in the window's base reg. + */ +static void __init +mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ +	u32	extra; + +	pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", +		window, mv64360_32bit_windows[window].base_reg, +		mv64360_32bit_windows[window].size_reg); + +	if (((mv64360_32bit_windows[window].base_reg != 0) && +		(mv64360_32bit_windows[window].size_reg != 0)) || +		(window == MV64x60_CPU2SRAM_WIN)) { + +		extra = mv64360_32bit_windows[window].extra; + +		switch (extra & MV64x60_EXTRA_MASK) { +		case MV64x60_EXTRA_CPUWIN_ENAB: +			mv64x60_set_bits(bh, MV64360_CPU_BAR_ENABLE, +				(1 << (extra & 0x1f))); +			break; + +		case MV64x60_EXTRA_CPUPROT_ENAB: +			mv64x60_clr_bits(bh, +				mv64360_32bit_windows[window].base_reg, +				(1 << (extra & 0x1f))); +			break; + +		case MV64x60_EXTRA_ENET_ENAB: +			mv64x60_set_bits(bh, MV64360_ENET2MEM_BAR_ENABLE, +				(1 << (extra & 0x7))); +			break; + +		case MV64x60_EXTRA_MPSC_ENAB: +			mv64x60_set_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE, +				(1 << (extra & 0x3))); +			break; + +		case MV64x60_EXTRA_IDMA_ENAB: +			mv64x60_set_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE, +				(1 << (extra & 0x7))); +			break; + +		default: +			printk(KERN_ERR "mv64360_disable: %s\n", +				"32bit table corrupted"); +		} +	} + +	return; +} + +/* + * mv64360_enable_window_64bit() + * + * On the MV64360, a 64-bit window is enabled by setting a bit in the window's + * base reg. + */ +static void __init +mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ +	pr_debug("enable 64bit window: %d\n", window); + +	if ((mv64360_64bit_windows[window].base_lo_reg!= 0) && +		(mv64360_64bit_windows[window].size_reg != 0)) { + +		if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK) +			== MV64x60_EXTRA_PCIACC_ENAB) + +			mv64x60_set_bits(bh, +				mv64360_64bit_windows[window].base_lo_reg, +				(1 << (mv64360_64bit_windows[window].extra & +									0x1f))); +		else +			printk(KERN_ERR "mv64360_enable: %s\n", +				"64bit table corrupted"); +	} + +	return; +} + +/* + * mv64360_disable_window_64bit() + * + * On a MV64360, a 64-bit window is disabled by clearing a bit in the window's + * base reg. + */ +static void __init +mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ +	pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", +		window, mv64360_64bit_windows[window].base_lo_reg, +		mv64360_64bit_windows[window].size_reg); + +	if ((mv64360_64bit_windows[window].base_lo_reg != 0) && +		(mv64360_64bit_windows[window].size_reg != 0)) { + +		if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK) +			== MV64x60_EXTRA_PCIACC_ENAB) + +			mv64x60_clr_bits(bh, +				mv64360_64bit_windows[window].base_lo_reg, +				(1 << (mv64360_64bit_windows[window].extra & +									0x1f))); +		else +			printk(KERN_ERR "mv64360_disable: %s\n", +				"64bit table corrupted"); +	} + +	return; +} + +/* + * mv64360_disable_all_windows() + * + * The MV64360 has a few windows that aren't represented in the table of + * windows at the top of this file.  This routine turns all of them off + * except for the memory controller windows, of course. + */ +static void __init +mv64360_disable_all_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si) +{ +	u32	preserve, i; + +	/* Disable 32bit windows (don't disable cpu->mem windows) */ +	for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) { +		if (i < 32) +			preserve = si->window_preserve_mask_32_lo & (1 << i); +		else +			preserve = si->window_preserve_mask_32_hi & (1<<(i-32)); + +		if (!preserve) +			mv64360_disable_window_32bit(bh, i); +	} + +	/* Disable 64bit windows */ +	for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++) +		if (!(si->window_preserve_mask_64 & (1<<i))) +			mv64360_disable_window_64bit(bh, i); + +	/* Turn off PCI->MEM access cntl wins not in mv64360_64bit_windows[] */ +	mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0); +	mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0); +	mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0); +	mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0); + +	/* Disable all PCI-><whatever> windows */ +	mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x0000f9ff); +	mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x0000f9ff); + +	return; +} + +/* + * mv64360_config_io2mem_windows() + * + * ENET, MPSC, and IDMA ctlrs on the MV64[34]60 have separate windows that + * must be set up so that the respective ctlr can access system memory. + */ +static u32 enet_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { +	MV64x60_ENET2MEM_0_WIN, MV64x60_ENET2MEM_1_WIN, +	MV64x60_ENET2MEM_2_WIN, MV64x60_ENET2MEM_3_WIN, +}; + +static u32 mpsc_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { +	MV64x60_MPSC2MEM_0_WIN, MV64x60_MPSC2MEM_1_WIN, +	MV64x60_MPSC2MEM_2_WIN, MV64x60_MPSC2MEM_3_WIN, +}; + +static u32 idma_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { +	MV64x60_IDMA2MEM_0_WIN, MV64x60_IDMA2MEM_1_WIN, +	MV64x60_IDMA2MEM_2_WIN, MV64x60_IDMA2MEM_3_WIN, +}; + +static u32 dram_selects[MV64x60_CPU2MEM_WINDOWS] __initdata = +	{ 0xe, 0xd, 0xb, 0x7 }; + +static void __init +mv64360_config_io2mem_windows(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si, +	u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ +	u32	i, win; + +	pr_debug("config_io2regs_windows: enet, mpsc, idma -> bridge regs\n"); + +	mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_0, 0); +	mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_1, 0); +	mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_2, 0); + +	mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_0, 0); +	mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_1, 0); + +	mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_0, 0); +	mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_1, 0); +	mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_2, 0); +	mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_3, 0); + +	/* Assume that mem ctlr has no more windows than embedded I/O ctlr */ +	for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) +		if (bh->ci->is_enabled_32bit(bh, win)) { +			mv64x60_set_32bit_window(bh, enet_tab[i], +				mem_windows[i][0], mem_windows[i][1], +				(dram_selects[i] << 8) | +				(si->enet_options[i] & 0x3000)); +			bh->ci->enable_window_32bit(bh, enet_tab[i]); + +			/* Give enet r/w access to memory region */ +			mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_0, +				(0x3 << (i << 1))); +			mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_1, +				(0x3 << (i << 1))); +			mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_2, +				(0x3 << (i << 1))); + +			mv64x60_set_32bit_window(bh, mpsc_tab[i], +				mem_windows[i][0], mem_windows[i][1], +				(dram_selects[i] << 8) | +				(si->mpsc_options[i] & 0x3000)); +			bh->ci->enable_window_32bit(bh, mpsc_tab[i]); + +			/* Give mpsc r/w access to memory region */ +			mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_0, +				(0x3 << (i << 1))); +			mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_1, +				(0x3 << (i << 1))); + +			mv64x60_set_32bit_window(bh, idma_tab[i], +				mem_windows[i][0], mem_windows[i][1], +				(dram_selects[i] << 8) | +				(si->idma_options[i] & 0x3000)); +			bh->ci->enable_window_32bit(bh, idma_tab[i]); + +			/* Give idma r/w access to memory region */ +			mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_0, +				(0x3 << (i << 1))); +			mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_1, +				(0x3 << (i << 1))); +			mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_2, +				(0x3 << (i << 1))); +			mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_3, +				(0x3 << (i << 1))); +		} + +	return; +} + +/* + * mv64360_set_mpsc2regs_window() + * + * MPSC has a window to the bridge's internal registers.  Call this routine + * to change that window so it doesn't conflict with the windows mapping the + * mpsc to system memory. + */ +static void __init +mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base) +{ +	pr_debug("set mpsc->internal regs, base: 0x%x\n", base); + +	mv64x60_write(bh, MV64360_MPSC2REGS_BASE, base & 0xffff0000); +	return; +} + +/* + * mv64360_chip_specific_init() + * + * No errata work arounds for the MV64360 implemented at this point. + */ +static void __init +mv64360_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC +	mv64x60_mpsc0_pdata.brg_can_tune = 1; +	mv64x60_mpsc0_pdata.cache_mgmt = 1; +	mv64x60_mpsc1_pdata.brg_can_tune = 1; +	mv64x60_mpsc1_pdata.cache_mgmt = 1; +#endif + +	return; +} + +/* + * mv64460_chip_specific_init() + * + * No errata work arounds for the MV64460 implemented at this point. + */ +static void __init +mv64460_chip_specific_init(struct mv64x60_handle *bh, +	struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC +	mv64x60_mpsc0_pdata.brg_can_tune = 1; +	mv64x60_mpsc1_pdata.brg_can_tune = 1; +#endif +	return; +} diff --git a/arch/ppc/syslib/mv64x60_dbg.c b/arch/ppc/syslib/mv64x60_dbg.c new file mode 100644 index 00000000000..2927c7adf5e --- /dev/null +++ b/arch/ppc/syslib/mv64x60_dbg.c @@ -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 */ diff --git a/arch/ppc/syslib/mv64x60_win.c b/arch/ppc/syslib/mv64x60_win.c new file mode 100644 index 00000000000..b6f0f5dcf6e --- /dev/null +++ b/arch/ppc/syslib/mv64x60_win.c @@ -0,0 +1,1168 @@ +/* + * arch/ppc/syslib/mv64x60_win.c + * + * Tables with info on how to manipulate the 32 & 64 bit windows on the + * various types of Marvell bridge chips. + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/bootmem.h> +#include <linux/mv643xx.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/delay.h> +#include <asm/mv64x60.h> + + +/* + ***************************************************************************** + * + *	Tables describing how to set up windows on each type of bridge + * + ***************************************************************************** + */ +struct mv64x60_32bit_window +	gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = { +	/* CPU->MEM Windows */ +	[MV64x60_CPU2MEM_0_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_0_BASE, +		.size_reg		= MV64x60_CPU2MEM_0_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2MEM_1_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_1_BASE, +		.size_reg		= MV64x60_CPU2MEM_1_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2MEM_2_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_2_BASE, +		.size_reg		= MV64x60_CPU2MEM_2_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2MEM_3_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_3_BASE, +		.size_reg		= MV64x60_CPU2MEM_3_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->Device Windows */ +	[MV64x60_CPU2DEV_0_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_0_BASE, +		.size_reg		= MV64x60_CPU2DEV_0_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2DEV_1_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_1_BASE, +		.size_reg		= MV64x60_CPU2DEV_1_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2DEV_2_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_2_BASE, +		.size_reg		= MV64x60_CPU2DEV_2_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2DEV_3_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_3_BASE, +		.size_reg		= MV64x60_CPU2DEV_3_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->Boot Window */ +	[MV64x60_CPU2BOOT_WIN] = { +		.base_reg		= MV64x60_CPU2BOOT_0_BASE, +		.size_reg		= MV64x60_CPU2BOOT_0_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->PCI 0 Windows */ +	[MV64x60_CPU2PCI0_IO_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_IO_BASE, +		.size_reg		= MV64x60_CPU2PCI0_IO_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_0_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_0_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_0_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_1_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_1_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_1_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_2_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_2_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_2_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_3_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_3_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_3_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->PCI 1 Windows */ +	[MV64x60_CPU2PCI1_IO_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_IO_BASE, +		.size_reg		= MV64x60_CPU2PCI1_IO_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_0_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_0_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_0_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_1_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_1_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_1_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_2_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_2_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_2_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_3_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_3_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_3_SIZE, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->SRAM Window (64260 has no integrated SRAM) */ +	/* CPU->PCI 0 Remap I/O Window */ +	[MV64x60_CPU2PCI0_IO_REMAP_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_IO_REMAP, +		.size_reg		= 0, +		.base_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->PCI 1 Remap I/O Window */ +	[MV64x60_CPU2PCI1_IO_REMAP_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_IO_REMAP, +		.size_reg		= 0, +		.base_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU Memory Protection Windows */ +	[MV64x60_CPU_PROT_0_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_0, +		.size_reg		= MV64x60_CPU_PROT_SIZE_0, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU_PROT_1_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_1, +		.size_reg		= MV64x60_CPU_PROT_SIZE_1, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU_PROT_2_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_2, +		.size_reg		= MV64x60_CPU_PROT_SIZE_2, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU_PROT_3_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_3, +		.size_reg		= MV64x60_CPU_PROT_SIZE_3, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU Snoop Windows */ +	[MV64x60_CPU_SNOOP_0_WIN] = { +		.base_reg		= GT64260_CPU_SNOOP_BASE_0, +		.size_reg		= GT64260_CPU_SNOOP_SIZE_0, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU_SNOOP_1_WIN] = { +		.base_reg		= GT64260_CPU_SNOOP_BASE_1, +		.size_reg		= GT64260_CPU_SNOOP_SIZE_1, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU_SNOOP_2_WIN] = { +		.base_reg		= GT64260_CPU_SNOOP_BASE_2, +		.size_reg		= GT64260_CPU_SNOOP_SIZE_2, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU_SNOOP_3_WIN] = { +		.base_reg		= GT64260_CPU_SNOOP_BASE_3, +		.size_reg		= GT64260_CPU_SNOOP_SIZE_3, +		.base_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* PCI 0->System Memory Remap Windows */ +	[MV64x60_PCI02MEM_REMAP_0_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_0_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_REMAP_1_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_REMAP_2_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_REMAP_3_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	/* PCI 1->System Memory Remap Windows */ +	[MV64x60_PCI12MEM_REMAP_0_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_0_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_REMAP_1_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_REMAP_2_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_REMAP_3_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	/* ENET->SRAM Window (64260 doesn't have separate windows) */ +	/* MPSC->SRAM Window (64260 doesn't have separate windows) */ +	/* IDMA->SRAM Window (64260 doesn't have separate windows) */ +}; + +struct mv64x60_64bit_window +	gt64260_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = { +	/* CPU->PCI 0 MEM Remap Windows */ +	[MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_0_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_0_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_1_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_1_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_2_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_2_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_3_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_3_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->PCI 1 MEM Remap Windows */ +	[MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_0_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_0_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_1_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_1_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_2_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_2_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_3_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_3_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 12, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* PCI 0->MEM Access Control Windows */ +	[MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_0_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_0_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_0_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_1_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_1_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_1_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_2_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_2_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_2_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_3_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_3_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_3_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* PCI 1->MEM Access Control Windows */ +	[MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_0_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_0_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_0_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_1_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_1_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_1_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_2_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_2_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_2_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_3_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_3_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_3_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* PCI 0->MEM Snoop Windows */ +	[MV64x60_PCI02MEM_SNOOP_0_WIN] = { +		.base_hi_reg		= GT64260_PCI0_SNOOP_0_BASE_HI, +		.base_lo_reg		= GT64260_PCI0_SNOOP_0_BASE_LO, +		.size_reg		= GT64260_PCI0_SNOOP_0_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_SNOOP_1_WIN] = { +		.base_hi_reg		= GT64260_PCI0_SNOOP_1_BASE_HI, +		.base_lo_reg		= GT64260_PCI0_SNOOP_1_BASE_LO, +		.size_reg		= GT64260_PCI0_SNOOP_1_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_SNOOP_2_WIN] = { +		.base_hi_reg		= GT64260_PCI0_SNOOP_2_BASE_HI, +		.base_lo_reg		= GT64260_PCI0_SNOOP_2_BASE_LO, +		.size_reg		= GT64260_PCI0_SNOOP_2_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_SNOOP_3_WIN] = { +		.base_hi_reg		= GT64260_PCI0_SNOOP_3_BASE_HI, +		.base_lo_reg		= GT64260_PCI0_SNOOP_3_BASE_LO, +		.size_reg		= GT64260_PCI0_SNOOP_3_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* PCI 1->MEM Snoop Windows */ +	[MV64x60_PCI12MEM_SNOOP_0_WIN] = { +		.base_hi_reg		= GT64260_PCI1_SNOOP_0_BASE_HI, +		.base_lo_reg		= GT64260_PCI1_SNOOP_0_BASE_LO, +		.size_reg		= GT64260_PCI1_SNOOP_0_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_SNOOP_1_WIN] = { +		.base_hi_reg		= GT64260_PCI1_SNOOP_1_BASE_HI, +		.base_lo_reg		= GT64260_PCI1_SNOOP_1_BASE_LO, +		.size_reg		= GT64260_PCI1_SNOOP_1_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_SNOOP_2_WIN] = { +		.base_hi_reg		= GT64260_PCI1_SNOOP_2_BASE_HI, +		.base_lo_reg		= GT64260_PCI1_SNOOP_2_BASE_LO, +		.size_reg		= GT64260_PCI1_SNOOP_2_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_SNOOP_3_WIN] = { +		.base_hi_reg		= GT64260_PCI1_SNOOP_3_BASE_HI, +		.base_lo_reg		= GT64260_PCI1_SNOOP_3_BASE_LO, +		.size_reg		= GT64260_PCI1_SNOOP_3_SIZE, +		.base_lo_bits		= 12, +		.size_bits		= 12, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +}; + +struct mv64x60_32bit_window +	mv64360_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = { +	/* CPU->MEM Windows */ +	[MV64x60_CPU2MEM_0_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_0_BASE, +		.size_reg		= MV64x60_CPU2MEM_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 0 }, +	[MV64x60_CPU2MEM_1_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_1_BASE, +		.size_reg		= MV64x60_CPU2MEM_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 1 }, +	[MV64x60_CPU2MEM_2_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_2_BASE, +		.size_reg		= MV64x60_CPU2MEM_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 2 }, +	[MV64x60_CPU2MEM_3_WIN] = { +		.base_reg		= MV64x60_CPU2MEM_3_BASE, +		.size_reg		= MV64x60_CPU2MEM_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 3 }, +	/* CPU->Device Windows */ +	[MV64x60_CPU2DEV_0_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_0_BASE, +		.size_reg		= MV64x60_CPU2DEV_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 4 }, +	[MV64x60_CPU2DEV_1_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_1_BASE, +		.size_reg		= MV64x60_CPU2DEV_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 5 }, +	[MV64x60_CPU2DEV_2_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_2_BASE, +		.size_reg		= MV64x60_CPU2DEV_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 6 }, +	[MV64x60_CPU2DEV_3_WIN] = { +		.base_reg		= MV64x60_CPU2DEV_3_BASE, +		.size_reg		= MV64x60_CPU2DEV_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 7 }, +	/* CPU->Boot Window */ +	[MV64x60_CPU2BOOT_WIN] = { +		.base_reg		= MV64x60_CPU2BOOT_0_BASE, +		.size_reg		= MV64x60_CPU2BOOT_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 8 }, +	/* CPU->PCI 0 Windows */ +	[MV64x60_CPU2PCI0_IO_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_IO_BASE, +		.size_reg		= MV64x60_CPU2PCI0_IO_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 9 }, +	[MV64x60_CPU2PCI0_MEM_0_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_0_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 10 }, +	[MV64x60_CPU2PCI0_MEM_1_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_1_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 11 }, +	[MV64x60_CPU2PCI0_MEM_2_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_2_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 12 }, +	[MV64x60_CPU2PCI0_MEM_3_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_MEM_3_BASE, +		.size_reg		= MV64x60_CPU2PCI0_MEM_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 13 }, +	/* CPU->PCI 1 Windows */ +	[MV64x60_CPU2PCI1_IO_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_IO_BASE, +		.size_reg		= MV64x60_CPU2PCI1_IO_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 14 }, +	[MV64x60_CPU2PCI1_MEM_0_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_0_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 15 }, +	[MV64x60_CPU2PCI1_MEM_1_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_1_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 16 }, +	[MV64x60_CPU2PCI1_MEM_2_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_2_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 17 }, +	[MV64x60_CPU2PCI1_MEM_3_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_MEM_3_BASE, +		.size_reg		= MV64x60_CPU2PCI1_MEM_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 18 }, +	/* CPU->SRAM Window */ +	[MV64x60_CPU2SRAM_WIN] = { +		.base_reg		= MV64360_CPU2SRAM_BASE, +		.size_reg		= 0, +		.base_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUWIN_ENAB | 19 }, +	/* CPU->PCI 0 Remap I/O Window */ +	[MV64x60_CPU2PCI0_IO_REMAP_WIN] = { +		.base_reg		= MV64x60_CPU2PCI0_IO_REMAP, +		.size_reg		= 0, +		.base_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->PCI 1 Remap I/O Window */ +	[MV64x60_CPU2PCI1_IO_REMAP_WIN] = { +		.base_reg		= MV64x60_CPU2PCI1_IO_REMAP, +		.size_reg		= 0, +		.base_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU Memory Protection Windows */ +	[MV64x60_CPU_PROT_0_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_0, +		.size_reg		= MV64x60_CPU_PROT_SIZE_0, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUPROT_ENAB | 31 }, +	[MV64x60_CPU_PROT_1_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_1, +		.size_reg		= MV64x60_CPU_PROT_SIZE_1, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUPROT_ENAB | 31 }, +	[MV64x60_CPU_PROT_2_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_2, +		.size_reg		= MV64x60_CPU_PROT_SIZE_2, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUPROT_ENAB | 31 }, +	[MV64x60_CPU_PROT_3_WIN] = { +		.base_reg		= MV64x60_CPU_PROT_BASE_3, +		.size_reg		= MV64x60_CPU_PROT_SIZE_3, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= MV64x60_EXTRA_CPUPROT_ENAB | 31 }, +	/* CPU Snoop Windows -- don't exist on 64360 */ +	/* PCI 0->System Memory Remap Windows */ +	[MV64x60_PCI02MEM_REMAP_0_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_0_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_REMAP_1_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_REMAP_2_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI02MEM_REMAP_3_WIN] = { +		.base_reg		= MV64x60_PCI0_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	/* PCI 1->System Memory Remap Windows */ +	[MV64x60_PCI12MEM_REMAP_0_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_0_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_REMAP_1_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_REMAP_2_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	[MV64x60_PCI12MEM_REMAP_3_WIN] = { +		.base_reg		= MV64x60_PCI1_SLAVE_MEM_1_REMAP, +		.size_reg		= 0, +		.base_bits		= 20, +		.size_bits		= 0, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= 0 }, +	/* ENET->System Memory Windows */ +	[MV64x60_ENET2MEM_0_WIN] = { +		.base_reg		= MV64360_ENET2MEM_0_BASE, +		.size_reg		= MV64360_ENET2MEM_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_ENET_ENAB | 0 }, +	[MV64x60_ENET2MEM_1_WIN] = { +		.base_reg		= MV64360_ENET2MEM_1_BASE, +		.size_reg		= MV64360_ENET2MEM_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_ENET_ENAB | 1 }, +	[MV64x60_ENET2MEM_2_WIN] = { +		.base_reg		= MV64360_ENET2MEM_2_BASE, +		.size_reg		= MV64360_ENET2MEM_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_ENET_ENAB | 2 }, +	[MV64x60_ENET2MEM_3_WIN] = { +		.base_reg		= MV64360_ENET2MEM_3_BASE, +		.size_reg		= MV64360_ENET2MEM_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_ENET_ENAB | 3 }, +	[MV64x60_ENET2MEM_4_WIN] = { +		.base_reg		= MV64360_ENET2MEM_4_BASE, +		.size_reg		= MV64360_ENET2MEM_4_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_ENET_ENAB | 4 }, +	[MV64x60_ENET2MEM_5_WIN] = { +		.base_reg		= MV64360_ENET2MEM_5_BASE, +		.size_reg		= MV64360_ENET2MEM_5_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_ENET_ENAB | 5 }, +	/* MPSC->System Memory Windows */ +	[MV64x60_MPSC2MEM_0_WIN] = { +		.base_reg		= MV64360_MPSC2MEM_0_BASE, +		.size_reg		= MV64360_MPSC2MEM_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_MPSC_ENAB | 0 }, +	[MV64x60_MPSC2MEM_1_WIN] = { +		.base_reg		= MV64360_MPSC2MEM_1_BASE, +		.size_reg		= MV64360_MPSC2MEM_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_MPSC_ENAB | 1 }, +	[MV64x60_MPSC2MEM_2_WIN] = { +		.base_reg		= MV64360_MPSC2MEM_2_BASE, +		.size_reg		= MV64360_MPSC2MEM_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_MPSC_ENAB | 2 }, +	[MV64x60_MPSC2MEM_3_WIN] = { +		.base_reg		= MV64360_MPSC2MEM_3_BASE, +		.size_reg		= MV64360_MPSC2MEM_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_MPSC_ENAB | 3 }, +	/* IDMA->System Memory Windows */ +	[MV64x60_IDMA2MEM_0_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_0_BASE, +		.size_reg		= MV64360_IDMA2MEM_0_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 0 }, +	[MV64x60_IDMA2MEM_1_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_1_BASE, +		.size_reg		= MV64360_IDMA2MEM_1_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 1 }, +	[MV64x60_IDMA2MEM_2_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_2_BASE, +		.size_reg		= MV64360_IDMA2MEM_2_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 2 }, +	[MV64x60_IDMA2MEM_3_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_3_BASE, +		.size_reg		= MV64360_IDMA2MEM_3_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 3 }, +	[MV64x60_IDMA2MEM_4_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_4_BASE, +		.size_reg		= MV64360_IDMA2MEM_4_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 4 }, +	[MV64x60_IDMA2MEM_5_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_5_BASE, +		.size_reg		= MV64360_IDMA2MEM_5_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 5 }, +	[MV64x60_IDMA2MEM_6_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_6_BASE, +		.size_reg		= MV64360_IDMA2MEM_6_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 6 }, +	[MV64x60_IDMA2MEM_7_WIN] = { +		.base_reg		= MV64360_IDMA2MEM_7_BASE, +		.size_reg		= MV64360_IDMA2MEM_7_SIZE, +		.base_bits		= 16, +		.size_bits		= 16, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_IDMA_ENAB | 7 }, +}; + +struct mv64x60_64bit_window +	mv64360_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = { +	/* CPU->PCI 0 MEM Remap Windows */ +	[MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_0_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_0_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_1_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_1_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_2_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_2_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI0_MEM_3_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI0_MEM_3_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* CPU->PCI 1 MEM Remap Windows */ +	[MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_0_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_0_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_1_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_1_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_2_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_2_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	[MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = { +		.base_hi_reg		= MV64x60_CPU2PCI1_MEM_3_REMAP_HI, +		.base_lo_reg		= MV64x60_CPU2PCI1_MEM_3_REMAP_LO, +		.size_reg		= 0, +		.base_lo_bits		= 16, +		.size_bits		= 0, +		.get_from_field		= mv64x60_shift_left, +		.map_to_field		= mv64x60_shift_right, +		.extra			= 0 }, +	/* PCI 0->MEM Access Control Windows */ +	[MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_0_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_0_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_0_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	[MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_1_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_1_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_1_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	[MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_2_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_2_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_2_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	[MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = { +		.base_hi_reg		= MV64x60_PCI0_ACC_CNTL_3_BASE_HI, +		.base_lo_reg		= MV64x60_PCI0_ACC_CNTL_3_BASE_LO, +		.size_reg		= MV64x60_PCI0_ACC_CNTL_3_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	/* PCI 1->MEM Access Control Windows */ +	[MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_0_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_0_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_0_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	[MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_1_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_1_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_1_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	[MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_2_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_2_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_2_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	[MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = { +		.base_hi_reg		= MV64x60_PCI1_ACC_CNTL_3_BASE_HI, +		.base_lo_reg		= MV64x60_PCI1_ACC_CNTL_3_BASE_LO, +		.size_reg		= MV64x60_PCI1_ACC_CNTL_3_SIZE, +		.base_lo_bits		= 20, +		.size_bits		= 20, +		.get_from_field		= mv64x60_mask, +		.map_to_field		= mv64x60_mask, +		.extra			= MV64x60_EXTRA_PCIACC_ENAB | 0 }, +	/* PCI 0->MEM Snoop Windows -- don't exist on 64360 */ +	/* PCI 1->MEM Snoop Windows -- don't exist on 64360 */ +}; diff --git a/arch/ppc/syslib/ocp.c b/arch/ppc/syslib/ocp.c new file mode 100644 index 00000000000..a5156c5179a --- /dev/null +++ b/arch/ppc/syslib/ocp.c @@ -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); diff --git a/arch/ppc/syslib/of_device.c b/arch/ppc/syslib/of_device.c new file mode 100644 index 00000000000..46269ed21ae --- /dev/null +++ b/arch/ppc/syslib/of_device.c @@ -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); diff --git a/arch/ppc/syslib/open_pic.c b/arch/ppc/syslib/open_pic.c new file mode 100644 index 00000000000..406f36a8a68 --- /dev/null +++ b/arch/ppc/syslib/open_pic.c @@ -0,0 +1,1083 @@ +/* + *  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. + */ + +#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" + +#if defined(CONFIG_PRPMC800) || defined(CONFIG_85xx) +#define OPENPIC_BIG_ENDIAN +#endif + +void __iomem *OpenPIC_Addr; +static volatile struct OpenPIC __iomem *OpenPIC = 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. + */ +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; +extern int use_of_interrupt_tree; + +static u_int NumProcessors; +static u_int NumSources; +static int open_pic_irq_offset; +static volatile OpenPIC_Source __iomem *ISR[NR_IRQS]; +static int openpic_cascade_irq = -1; +static int (*openpic_cascade_fn)(struct pt_regs *); + +/* Global Operations */ +static void openpic_disable_8259_pass_through(void); +static void openpic_set_spurious(u_int vector); + +#ifdef CONFIG_SMP +/* Interprocessor Interrupts */ +static void openpic_initipi(u_int ipi, u_int pri, u_int vector); +static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *); +#endif + +/* Timer Interrupts */ +static void openpic_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic_maptimer(u_int timer, cpumask_t cpumask); + +/* Interrupt Sources */ +static void openpic_enable_irq(u_int irq); +static void openpic_disable_irq(u_int irq); +static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, +			    int is_level); +static void openpic_mapirq(u_int irq, cpumask_t cpumask, cpumask_t keepmask); + +/* + * These functions are not used but the code is kept here + * for completeness and future reference. + */ +#ifdef notused +static void openpic_enable_8259_pass_through(void); +static u_int openpic_get_priority(void); +static u_int openpic_get_spurious(void); +static void openpic_set_sense(u_int irq, int sense); +#endif /* notused */ + +/* + * Description of the openpic for the higher-level irq code + */ +static void openpic_end_irq(unsigned int irq_nr); +static void openpic_ack_irq(unsigned int irq_nr); +static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask); + +struct hw_interrupt_type open_pic = { +	.typename	= " OpenPIC  ", +	.enable		= openpic_enable_irq, +	.disable	= openpic_disable_irq, +	.ack		= openpic_ack_irq, +	.end		= openpic_end_irq, +	.set_affinity	= openpic_set_affinity, +}; + +#ifdef CONFIG_SMP +static void openpic_end_ipi(unsigned int irq_nr); +static void openpic_ack_ipi(unsigned int irq_nr); +static void openpic_enable_ipi(unsigned int irq_nr); +static void openpic_disable_ipi(unsigned int irq_nr); + +struct hw_interrupt_type open_pic_ipi = { +	.typename	= " OpenPIC  ", +	.enable		= openpic_enable_ipi, +	.disable	= openpic_disable_ipi, +	.ack		= openpic_ack_ipi, +	.end		= openpic_end_ipi, +}; +#endif /* CONFIG_SMP */ + +/* + *  Accesses to the current processor's openpic registers + */ +#ifdef CONFIG_SMP +#define THIS_CPU		Processor[cpu] +#define DECL_THIS_CPU		int cpu = smp_hw_index[smp_processor_id()] +#define CHECK_THIS_CPU		check_arg_cpu(cpu) +#else +#define THIS_CPU		Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU +#endif /* CONFIG_SMP */ + +#if 1 +#define check_arg_ipi(ipi) \ +    if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ +	printk("open_pic.c:%d: invalid ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ +    if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ +	printk("open_pic.c:%d: invalid timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ +    if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ +	printk("open_pic.c:%d: invalid vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ +    if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ +	printk("open_pic.c:%d: invalid 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 + */ +#define check_arg_irq(irq) \ +    if (irq < open_pic_irq_offset || irq >= NumSources+open_pic_irq_offset \ +	|| ISR[irq - open_pic_irq_offset] == 0) { \ +      printk("open_pic.c:%d: invalid irq %d\n", __LINE__, irq); \ +      dump_stack(); } +#define check_arg_cpu(cpu) \ +    if (cpu < 0 || cpu >= NumProcessors){ \ +	printk("open_pic.c:%d: invalid cpu %d\n", __LINE__, cpu); \ +	dump_stack(); } +#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 + +u_int openpic_read(volatile u_int __iomem *addr) +{ +	u_int val; + +#ifdef OPENPIC_BIG_ENDIAN +	val = in_be32(addr); +#else +	val = in_le32(addr); +#endif +	return val; +} + +static inline void openpic_write(volatile u_int __iomem *addr, u_int val) +{ +#ifdef OPENPIC_BIG_ENDIAN +	out_be32(addr, val); +#else +	out_le32(addr, val); +#endif +} + +static inline u_int openpic_readfield(volatile u_int __iomem *addr, u_int mask) +{ +	u_int val = openpic_read(addr); +	return val & mask; +} + +inline void openpic_writefield(volatile u_int __iomem *addr, u_int mask, +			       u_int field) +{ +	u_int val = openpic_read(addr); +	openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int __iomem *addr, u_int mask) +{ +	openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int __iomem *addr, u_int mask) +{ +	openpic_writefield(addr, mask, mask); +} + +static void openpic_safe_writefield(volatile u_int __iomem *addr, u_int mask, +				    u_int field) +{ +	openpic_setfield(addr, OPENPIC_MASK); +	while (openpic_read(addr) & OPENPIC_ACTIVITY); +	openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +#ifdef CONFIG_SMP +/* yes this is right ... bug, feature, you decide! -- tgall */ +u_int openpic_read_IPI(volatile u_int __iomem * addr) +{ +         u_int val = 0; +#if defined(OPENPIC_BIG_ENDIAN) || defined(CONFIG_POWER3) +        val = in_be32(addr); +#else +        val = in_le32(addr); +#endif +        return val; +} + +/* because of the power3 be / le above, this is needed */ +inline void openpic_writefield_IPI(volatile u_int __iomem * addr, u_int mask, u_int field) +{ +        u_int  val = openpic_read_IPI(addr); +        openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield_IPI(volatile u_int __iomem *addr, u_int mask) +{ +        openpic_writefield_IPI(addr, mask, 0); +} + +static inline void openpic_setfield_IPI(volatile u_int __iomem *addr, u_int mask) +{ +        openpic_writefield_IPI(addr, mask, mask); +} + +static void openpic_safe_writefield_IPI(volatile u_int __iomem *addr, u_int mask, u_int field) +{ +        openpic_setfield_IPI(addr, OPENPIC_MASK); + +        /* wait until it's not in use */ +        /* BenH: Is this code really enough ? I would rather check the result +         *       and eventually retry ... +         */ +        while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY); + +        openpic_writefield_IPI(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_EPIC_SERIAL_MODE +/* On platforms that may use EPIC serial mode, the default is enabled. */ +int epic_serial_mode = 1; + +static void __init openpic_eicr_set_clk(u_int clkval) +{ +	openpic_writefield(&OpenPIC->Global.Global_Configuration1, +			OPENPIC_EICR_S_CLK_MASK, (clkval << 28)); +} + +static void __init openpic_enable_sie(void) +{ +	openpic_setfield(&OpenPIC->Global.Global_Configuration1, +			OPENPIC_EICR_SIE); +} +#endif + +#if defined(CONFIG_EPIC_SERIAL_MODE) || defined(CONFIG_PM) +static void openpic_reset(void) +{ +	openpic_setfield(&OpenPIC->Global.Global_Configuration0, +			 OPENPIC_CONFIG_RESET); +	while (openpic_readfield(&OpenPIC->Global.Global_Configuration0, +				 OPENPIC_CONFIG_RESET)) +		mb(); +} +#endif + +void __init openpic_set_sources(int first_irq, int num_irqs, void __iomem *first_ISR) +{ +	volatile OpenPIC_Source __iomem *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 __iomem *)OpenPIC_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 openpic_init(int offset) +{ +	u_int t, i; +	u_int timerfreq; +	const char *version; + +	if (!OpenPIC_Addr) { +		printk("No OpenPIC found !\n"); +		return; +	} +	OpenPIC = (volatile struct OpenPIC __iomem *)OpenPIC_Addr; + +#ifdef CONFIG_EPIC_SERIAL_MODE +	/* Have to start from ground zero. +	*/ +	openpic_reset(); +#endif + +	if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); + +	t = openpic_read(&OpenPIC->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) +		openpic_set_sources(0, +				    ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> +				     OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, +				    NULL); +	printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", +	       version, NumProcessors, NumSources, OpenPIC); +	timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); +	if (timerfreq) +		printk("OpenPIC timer frequency is %d.%06d MHz\n", +		       timerfreq / 1000000, timerfreq % 1000000); + +	open_pic_irq_offset = offset; + +	/* Initialize timer interrupts */ +	if ( ppc_md.progress ) ppc_md.progress("openpic: timer",0x3ba); +	for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { +		/* Disabled, Priority 0 */ +		openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i+offset); +		/* No processor */ +		openpic_maptimer(i, CPU_MASK_NONE); +	} + +#ifdef CONFIG_SMP +	/* Initialize IPI interrupts */ +	if ( ppc_md.progress ) ppc_md.progress("openpic: ipi",0x3bb); +	for (i = 0; i < OPENPIC_NUM_IPI; i++) { +		/* Disabled, Priority 10..13 */ +		openpic_initipi(i, 10+i, OPENPIC_VEC_IPI+i+offset); +		/* IPIs are per-CPU */ +		irq_desc[OPENPIC_VEC_IPI+i+offset].status |= IRQ_PER_CPU; +		irq_desc[OPENPIC_VEC_IPI+i+offset].handler = &open_pic_ipi; +	} +#endif + +	/* Initialize external interrupts */ +	if (ppc_md.progress) ppc_md.progress("openpic: external",0x3bc); + +	openpic_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 !) */ +		openpic_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 */ +		openpic_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), +				(sense & IRQ_SENSE_MASK)); +		/* Processor 0 */ +		openpic_mapirq(i, CPU_MASK_CPU0, CPU_MASK_NONE); +	} + +	/* Init descriptors */ +	for (i = offset; i < NumSources + offset; i++) +		irq_desc[i].handler = &open_pic; + +	/* Initialize the spurious interrupt */ +	if (ppc_md.progress) ppc_md.progress("openpic: spurious",0x3bd); +	openpic_set_spurious(OPENPIC_VEC_SPURIOUS); +	openpic_disable_8259_pass_through(); +#ifdef CONFIG_EPIC_SERIAL_MODE +	if (epic_serial_mode) { +		openpic_eicr_set_clk(7);	/* Slowest value until we know better */ +		openpic_enable_sie(); +	} +#endif +	openpic_set_priority(0); + +	if (ppc_md.progress) ppc_md.progress("openpic: exit",0x222); +} + +#ifdef notused +static void openpic_enable_8259_pass_through(void) +{ +	openpic_clearfield(&OpenPIC->Global.Global_Configuration0, +			   OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} +#endif /* notused */ + +static void openpic_disable_8259_pass_through(void) +{ +	openpic_setfield(&OpenPIC->Global.Global_Configuration0, +			 OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + *  Find out the current interrupt + */ +u_int openpic_irq(void) +{ +	u_int vec; +	DECL_THIS_CPU; + +	CHECK_THIS_CPU; +	vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, +				OPENPIC_VECTOR_MASK); +	return vec; +} + +void openpic_eoi(void) +{ +	DECL_THIS_CPU; + +	CHECK_THIS_CPU; +	openpic_write(&OpenPIC->THIS_CPU.EOI, 0); +	/* Handle PCI write posting */ +	(void)openpic_read(&OpenPIC->THIS_CPU.EOI); +} + +#ifdef notused +static u_int openpic_get_priority(void) +{ +	DECL_THIS_CPU; + +	CHECK_THIS_CPU; +	return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, +				 OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} +#endif /* notused */ + +void openpic_set_priority(u_int pri) +{ +	DECL_THIS_CPU; + +	CHECK_THIS_CPU; +	check_arg_pri(pri); +	openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, +			   OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + *  Get/set the spurious vector + */ +#ifdef notused +static u_int openpic_get_spurious(void) +{ +	return openpic_readfield(&OpenPIC->Global.Spurious_Vector, +				 OPENPIC_VECTOR_MASK); +} +#endif /* notused */ + +static void openpic_set_spurious(u_int vec) +{ +	check_arg_vec(vec); +	openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, +			   vec); +} + +#ifdef CONFIG_SMP +/* + * Convert a cpu mask from logical to physical cpu numbers. + */ +static inline cpumask_t physmask(cpumask_t cpumask) +{ +	int i; +	cpumask_t mask = CPU_MASK_NONE; + +	cpus_and(cpumask, cpu_online_map, cpumask); + +	for (i = 0; i < NR_CPUS; i++) +		if (cpu_isset(i, cpumask)) +			cpu_set(smp_hw_index[i], mask); + +	return mask; +} +#else +#define physmask(cpumask)	(cpumask) +#endif + +void openpic_reset_processor_phys(u_int mask) +{ +	openpic_write(&OpenPIC->Global.Processor_Initialization, mask); +} + +#if defined(CONFIG_SMP) || defined(CONFIG_PM) +static DEFINE_SPINLOCK(openpic_setup_lock); +#endif + +#ifdef CONFIG_SMP +/* + *  Initialize an interprocessor interrupt (and disable it) + * + *  ipi: OpenPIC interprocessor interrupt number + *  pri: interrupt source priority + *  vec: the vector it will produce + */ +static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ +	check_arg_ipi(ipi); +	check_arg_pri(pri); +	check_arg_vec(vec); +	openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi), +				OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, +				(pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + *  Send an IPI to one or more CPUs + * + *  Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) + *  and not a system-wide interrupt number + */ +void openpic_cause_IPI(u_int ipi, cpumask_t cpumask) +{ +	cpumask_t phys; +	DECL_THIS_CPU; + +	CHECK_THIS_CPU; +	check_arg_ipi(ipi); +	phys = physmask(cpumask); +	openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), +		      cpus_addr(physmask(cpumask))[0]); +} + +void openpic_request_IPIs(void) +{ +	int i; + +	/* +	 * Make sure this matches what is defined in smp.c for +	 * smp_message_{pass|recv}() or what shows up in +	 * /proc/interrupts will be wrong!!! --Troy */ + +	if (OpenPIC == NULL) +		return; + +	/* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */ +	request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset, +		    openpic_ipi_action, SA_INTERRUPT, +		    "IPI0 (call function)", NULL); +	request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+1, +		    openpic_ipi_action, SA_INTERRUPT, +		    "IPI1 (reschedule)", NULL); +	request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+2, +		    openpic_ipi_action, SA_INTERRUPT, +		    "IPI2 (invalidate tlb)", NULL); +	request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+3, +		    openpic_ipi_action, SA_INTERRUPT, +		    "IPI3 (xmon break)", NULL); + +	for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) +		openpic_enable_ipi(OPENPIC_VEC_IPI+open_pic_irq_offset+i); +} + +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + *   -- Cort + */ + +void __devinit do_openpic_setup_cpu(void) +{ +#ifdef CONFIG_IRQ_ALL_CPUS + 	int i; +	cpumask_t msk = CPU_MASK_NONE; +#endif +	spin_lock(&openpic_setup_lock); + +#ifdef CONFIG_IRQ_ALL_CPUS +	cpu_set(smp_hw_index[smp_processor_id()], msk); + + 	/* let the openpic know we want intrs. default affinity + 	 * is 0xffffffff until changed via /proc + 	 * That's how it's done on x86. If we want it differently, then + 	 * we should make sure we also change the default values of irq_affinity + 	 * in irq.c. + 	 */ + 	for (i = 0; i < NumSources; i++) +		openpic_mapirq(i, msk, CPU_MASK_ALL); +#endif /* CONFIG_IRQ_ALL_CPUS */ + 	openpic_set_priority(0); + +	spin_unlock(&openpic_setup_lock); +} +#endif /* CONFIG_SMP */ + +/* + *  Initialize a timer interrupt (and disable it) + * + *  timer: OpenPIC timer number + *  pri: interrupt source priority + *  vec: the vector it will produce + */ +static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ +	check_arg_timer(timer); +	check_arg_pri(pri); +	check_arg_vec(vec); +	openpic_safe_writefield(&OpenPIC->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 openpic_maptimer(u_int timer, cpumask_t cpumask) +{ +	cpumask_t phys = physmask(cpumask); +	check_arg_timer(timer); +	openpic_write(&OpenPIC->Global.Timer[timer].Destination, +		      cpus_addr(phys)[0]); +} + +/* + * 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 +openpic_init_nmi_irq(u_int irq) +{ +	check_arg_irq(irq); +	openpic_safe_writefield(&ISR[irq - open_pic_irq_offset]->Vector_Priority, +				OPENPIC_PRIORITY_MASK, +				9 << OPENPIC_PRIORITY_SHIFT); +} + +/* + * + * All functions below take an offset'ed irq argument + * + */ + +/* + * Hookup a cascade to the OpenPIC. + */ + +static struct irqaction openpic_cascade_irqaction = { +	.handler = no_action, +	.flags = SA_INTERRUPT, +	.mask = CPU_MASK_NONE, +}; + +void __init +openpic_hookup_cascade(u_int irq, char *name, +	int (*cascade_fn)(struct pt_regs *)) +{ +	openpic_cascade_irq = irq; +	openpic_cascade_fn = cascade_fn; + +	if (setup_irq(irq, &openpic_cascade_irqaction)) +		printk("Unable to get OpenPIC IRQ %d for cascade\n", +				irq - open_pic_irq_offset); +} + +/* + *  Enable/disable an external interrupt source + * + *  Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic_enable_irq(u_int irq) +{ +	volatile u_int __iomem *vpp; + +	check_arg_irq(irq); +	vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; +	openpic_clearfield(vpp, OPENPIC_MASK); +	/* make sure mask gets to controller before we return to user */ +	do { +		mb(); /* sync is probably useless here */ +	} while (openpic_readfield(vpp, OPENPIC_MASK)); +} + +static void openpic_disable_irq(u_int irq) +{ +	volatile u_int __iomem *vpp; +	u32 vp; + +	check_arg_irq(irq); +	vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; +	openpic_setfield(vpp, OPENPIC_MASK); +	/* make sure mask gets to controller before we return to user */ +	do { +		mb();  /* sync is probably useless here */ +		vp = openpic_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); +	} while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + +#ifdef CONFIG_SMP +/* + *  Enable/disable an IPI interrupt source + * + *  Externally called, irq is an offseted system-wide interrupt number + */ +void openpic_enable_ipi(u_int irq) +{ +	irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset); +	check_arg_ipi(irq); +	openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); + +} + +void openpic_disable_ipi(u_int irq) +{ +	irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset); +	check_arg_ipi(irq); +	openpic_setfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); +} +#endif + +/* + *  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 +openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ +	openpic_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 openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask) +{ +	if (ISR[irq] == 0) +		return; +	if (!cpus_empty(keepmask)) { +		cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) }; +		cpus_and(irqdest, irqdest, keepmask); +		cpus_or(physmask, physmask, irqdest); +	} +	openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]); +} + +#ifdef notused +/* + *  Set the sense for an interrupt source (and disable it!) + * + *  sense: 1 for level, 0 for edge + */ +static void openpic_set_sense(u_int irq, int sense) +{ +	if (ISR[irq] != 0) +		openpic_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 openpic_ack_irq(unsigned int irq_nr) +{ +#ifdef __SLOW_VERSION__ +	openpic_disable_irq(irq_nr); +	openpic_eoi(); +#else +	if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) +		openpic_eoi(); +#endif +} + +static void openpic_end_irq(unsigned int irq_nr) +{ +#ifdef __SLOW_VERSION__ +	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) +	    && irq_desc[irq_nr].action) +		openpic_enable_irq(irq_nr); +#else +	if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) +		openpic_eoi(); +#endif +} + +static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask) +{ +	openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE); +} + +#ifdef CONFIG_SMP +static void openpic_ack_ipi(unsigned int irq_nr) +{ +	openpic_eoi(); +} + +static void openpic_end_ipi(unsigned int irq_nr) +{ +} + +static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ +	smp_message_recv(cpl-OPENPIC_VEC_IPI-open_pic_irq_offset, regs); +	return IRQ_HANDLED; +} + +#endif /* CONFIG_SMP */ + +int +openpic_get_irq(struct pt_regs *regs) +{ +	int irq = openpic_irq(); + +	/* +	 * Check for the cascade interrupt and call the cascaded +	 * interrupt controller function (usually i8259_irq) if so. +	 * This should move to irq.c eventually.  -- paulus +	 */ +	if (irq == openpic_cascade_irq && openpic_cascade_fn != NULL) { +		int cirq = openpic_cascade_fn(regs); + +		/* Allow for the cascade being shared with other devices */ +		if (cirq != -1) { +			irq = cirq; +			openpic_eoi(); +		} +	} else if (irq == OPENPIC_VEC_SPURIOUS) +		irq = -1; +	return irq; +} + +#ifdef CONFIG_SMP +void +smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) +{ +	cpumask_t mask = CPU_MASK_ALL; +	/* make sure we're sending something that translates to an IPI */ +	if (msg > 0x3) { +		printk("SMP %d: smp_message_pass: unknown msg %d\n", +		       smp_processor_id(), msg); +		return; +	} +	switch (target) { +	case MSG_ALL: +		openpic_cause_IPI(msg, mask); +		break; +	case MSG_ALL_BUT_SELF: +		cpu_clear(smp_processor_id(), mask); +		openpic_cause_IPI(msg, mask); +		break; +	default: +		openpic_cause_IPI(msg, cpumask_of_cpu(target)); +		break; +	} +} +#endif /* CONFIG_SMP */ + +#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 openpic_cached_enable_irq(u_int irq) +{ +	check_arg_irq(irq); +	save_irq_src_vp[irq - open_pic_irq_offset] &= ~OPENPIC_MASK; +} + +static void openpic_cached_disable_irq(u_int irq) +{ +	check_arg_irq(irq); +	save_irq_src_vp[irq - open_pic_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 openpic_suspend(struct sys_device *sysdev, u32 state) +{ +	int	i; +	unsigned long flags; + +	spin_lock_irqsave(&openpic_setup_lock, flags); + +	if (openpic_suspend_count++ > 0) { +		spin_unlock_irqrestore(&openpic_setup_lock, flags); +		return 0; +	} + + 	openpic_set_priority(0xf); + +	open_pic.enable = openpic_cached_enable_irq; +	open_pic.disable = openpic_cached_disable_irq; + +	for (i=0; i<NumProcessors; i++) { +		save_cpu_task_pri[i] = openpic_read(&OpenPIC->Processor[i].Current_Task_Priority); +		openpic_writefield(&OpenPIC->Processor[i].Current_Task_Priority, +				   OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); +	} + +	for (i=0; i<OPENPIC_NUM_IPI; i++) +		save_ipi_vp[i] = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(i)); +	for (i=0; i<NumSources; i++) { +		if (ISR[i] == 0) +			continue; +		save_irq_src_vp[i] = openpic_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY; +		save_irq_src_dest[i] = openpic_read(&ISR[i]->Destination); +	} + +	spin_unlock_irqrestore(&openpic_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 openpic_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(&openpic_setup_lock, flags); + +	if ((--openpic_suspend_count) > 0) { +		spin_unlock_irqrestore(&openpic_setup_lock, flags); +		return 0; +	} + +	openpic_reset(); + +	/* OpenPIC sometimes seem to need some time to be fully back up... */ +	do { +		openpic_set_spurious(OPENPIC_VEC_SPURIOUS); +	} while(openpic_readfield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) +			!= OPENPIC_VEC_SPURIOUS); +	 +	openpic_disable_8259_pass_through(); + +	for (i=0; i<OPENPIC_NUM_IPI; i++) +		openpic_write(&OpenPIC->Global.IPI_Vector_Priority(i), +			      save_ipi_vp[i]); +	for (i=0; i<NumSources; i++) { +		if (ISR[i] == 0) +			continue; +		openpic_write(&ISR[i]->Destination, save_irq_src_dest[i]); +		openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); +		/* make sure mask gets to controller before we return to user */ +		do { +			openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); +		} while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask) +			 != (save_irq_src_vp[i] & vppmask)); +	} +	for (i=0; i<NumProcessors; i++) +		openpic_write(&OpenPIC->Processor[i].Current_Task_Priority, +			      save_cpu_task_pri[i]); + +	open_pic.enable = openpic_enable_irq; +	open_pic.disable = openpic_disable_irq; + + 	openpic_set_priority(0); + +	spin_unlock_irqrestore(&openpic_setup_lock, flags); + +	return 0; +} + +#endif /* CONFIG_PM */ + +static struct sysdev_class openpic_sysclass = { +	set_kset_name("openpic"), +}; + +static struct sys_device device_openpic = { +	.id		= 0, +	.cls		= &openpic_sysclass, +}; + +static struct sysdev_driver driver_openpic = { +#ifdef CONFIG_PM +	.suspend	= &openpic_suspend, +	.resume		= &openpic_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_openpic_sysfs(void) +{ +	int rc; + +	if (!OpenPIC_Addr) +		return -ENODEV; +	printk(KERN_DEBUG "Registering openpic with sysfs...\n"); +	rc = sysdev_class_register(&openpic_sysclass); +	if (rc) { +		printk(KERN_ERR "Failed registering openpic sys class\n"); +		return -ENODEV; +	} +	rc = sysdev_register(&device_openpic); +	if (rc) { +		printk(KERN_ERR "Failed registering openpic sys device\n"); +		return -ENODEV; +	} +	rc = sysdev_driver_register(&openpic_sysclass, &driver_openpic); +	if (rc) { +		printk(KERN_ERR "Failed registering openpic sys driver\n"); +		return -ENODEV; +	} +	return 0; +} + +subsys_initcall(init_openpic_sysfs); + diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c new file mode 100644 index 00000000000..ea26da0d8b6 --- /dev/null +++ b/arch/ppc/syslib/open_pic2.c @@ -0,0 +1,716 @@ +/* + *  arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + *  Copyright (C) 1997 Geert Uytterhoeven + * + *  This file is subject to the terms and conditions of the GNU General Public + *  License.  See the file COPYING in the main directory of this archive + *  for more details. + * + *  This is a duplicate of open_pic.c that deals with U3s MPIC on + *  G5 PowerMacs. It's the same file except it's using big endian + *  register accesses + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/sysdev.h> +#include <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); + diff --git a/arch/ppc/syslib/open_pic_defs.h b/arch/ppc/syslib/open_pic_defs.h new file mode 100644 index 00000000000..4f05624b249 --- /dev/null +++ b/arch/ppc/syslib/open_pic_defs.h @@ -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 */ diff --git a/arch/ppc/syslib/pci_auto.c b/arch/ppc/syslib/pci_auto.c new file mode 100644 index 00000000000..d64207c2a97 --- /dev/null +++ b/arch/ppc/syslib/pci_auto.c @@ -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; +} diff --git a/arch/ppc/syslib/ppc403_pic.c b/arch/ppc/syslib/ppc403_pic.c new file mode 100644 index 00000000000..06cb0af2a58 --- /dev/null +++ b/arch/ppc/syslib/ppc403_pic.c @@ -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; +} diff --git a/arch/ppc/syslib/ppc405_pci.c b/arch/ppc/syslib/ppc405_pci.c new file mode 100644 index 00000000000..81c83bf98df --- /dev/null +++ b/arch/ppc/syslib/ppc405_pci.c @@ -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; +} diff --git a/arch/ppc/syslib/ppc4xx_dma.c b/arch/ppc/syslib/ppc4xx_dma.c new file mode 100644 index 00000000000..5015ab99afd --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_dma.c @@ -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); diff --git a/arch/ppc/syslib/ppc4xx_kgdb.c b/arch/ppc/syslib/ppc4xx_kgdb.c new file mode 100644 index 00000000000..fe8668bf813 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_kgdb.c @@ -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); +} diff --git a/arch/ppc/syslib/ppc4xx_pic.c b/arch/ppc/syslib/ppc4xx_pic.c new file mode 100644 index 00000000000..08f06dd17e7 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_pic.c @@ -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; +} diff --git a/arch/ppc/syslib/ppc4xx_pm.c b/arch/ppc/syslib/ppc4xx_pm.c new file mode 100644 index 00000000000..60a47920488 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_pm.c @@ -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); + +} diff --git a/arch/ppc/syslib/ppc4xx_setup.c b/arch/ppc/syslib/ppc4xx_setup.c new file mode 100644 index 00000000000..e170aebeb69 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_setup.c @@ -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 + +} diff --git a/arch/ppc/syslib/ppc4xx_sgdma.c b/arch/ppc/syslib/ppc4xx_sgdma.c new file mode 100644 index 00000000000..9f76e8ee39e --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_sgdma.c @@ -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); diff --git a/arch/ppc/syslib/ppc83xx_setup.c b/arch/ppc/syslib/ppc83xx_setup.c new file mode 100644 index 00000000000..c28f9d67948 --- /dev/null +++ b/arch/ppc/syslib/ppc83xx_setup.c @@ -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 */ diff --git a/arch/ppc/syslib/ppc83xx_setup.h b/arch/ppc/syslib/ppc83xx_setup.h new file mode 100644 index 00000000000..683f179b746 --- /dev/null +++ b/arch/ppc/syslib/ppc83xx_setup.h @@ -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 */ diff --git a/arch/ppc/syslib/ppc85xx_common.c b/arch/ppc/syslib/ppc85xx_common.c new file mode 100644 index 00000000000..e83f2f8686d --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_common.c @@ -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); diff --git a/arch/ppc/syslib/ppc85xx_common.h b/arch/ppc/syslib/ppc85xx_common.h new file mode 100644 index 00000000000..2c8f304441b --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_common.h @@ -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 */ diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c new file mode 100644 index 00000000000..81f1968c326 --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_setup.c @@ -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 */ + + diff --git a/arch/ppc/syslib/ppc85xx_setup.h b/arch/ppc/syslib/ppc85xx_setup.h new file mode 100644 index 00000000000..6e6cfe162fa --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_setup.h @@ -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 */ diff --git a/arch/ppc/syslib/ppc8xx_pic.c b/arch/ppc/syslib/ppc8xx_pic.c new file mode 100644 index 00000000000..d3b01c6c97d --- /dev/null +++ b/arch/ppc/syslib/ppc8xx_pic.c @@ -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 */ diff --git a/arch/ppc/syslib/ppc8xx_pic.h b/arch/ppc/syslib/ppc8xx_pic.h new file mode 100644 index 00000000000..784935eac36 --- /dev/null +++ b/arch/ppc/syslib/ppc8xx_pic.h @@ -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 */ diff --git a/arch/ppc/syslib/ppc_sys.c b/arch/ppc/syslib/ppc_sys.c new file mode 100644 index 00000000000..87920235256 --- /dev/null +++ b/arch/ppc/syslib/ppc_sys.c @@ -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); diff --git a/arch/ppc/syslib/prep_nvram.c b/arch/ppc/syslib/prep_nvram.c new file mode 100644 index 00000000000..2bcf8a16d1c --- /dev/null +++ b/arch/ppc/syslib/prep_nvram.c @@ -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; +	} +} diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c new file mode 100644 index 00000000000..2c64ed62747 --- /dev/null +++ b/arch/ppc/syslib/prom.c @@ -0,0 +1,1447 @@ +/* + * Procedures for interfacing to the Open Firmware PROM on + * Power Macintosh computers. + * + * In particular, we are interested in the device tree + * and in using some of its services (exit, write to stdout). + * + * Paul Mackerras	August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include <stdarg.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/version.h> +#include <linux/threads.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/bitops.h> + +#include <asm/sections.h> +#include <asm/prom.h> +#include <asm/page.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/bootx.h> +#include <asm/system.h> +#include <asm/mmu.h> +#include <asm/pgtable.h> +#include <asm/bootinfo.h> +#include <asm/btext.h> +#include <asm/pci-bridge.h> +#include <asm/open_pic.h> + + +struct pci_address { +	unsigned a_hi; +	unsigned a_mid; +	unsigned a_lo; +}; + +struct pci_reg_property { +	struct pci_address addr; +	unsigned size_hi; +	unsigned size_lo; +}; + +struct isa_reg_property { +	unsigned space; +	unsigned address; +	unsigned size; +}; + +typedef unsigned long interpret_func(struct device_node *, unsigned long, +				     int, int); +static interpret_func interpret_pci_props; +static interpret_func interpret_dbdma_props; +static interpret_func interpret_isa_props; +static interpret_func interpret_macio_props; +static interpret_func interpret_root_props; + +extern char *klimit; + +/* Set for a newworld or CHRP machine */ +int use_of_interrupt_tree; +struct device_node *dflt_interrupt_controller; +int num_interrupt_controllers; + +int pmac_newworld; + +extern unsigned int rtas_entry;  /* physical pointer */ + +extern struct device_node *allnodes; + +static unsigned long finish_node(struct device_node *, unsigned long, +				 interpret_func *, int, int); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); +static struct device_node *find_phandle(phandle); + +extern void enter_rtas(void *); +void phys_call_rtas(int, int, int, ...); + +extern char cmd_line[512];	/* XXX */ +extern boot_infos_t *boot_infos; +unsigned long dev_tree_size; + +void __openfirmware +phys_call_rtas(int service, int nargs, int nret, ...) +{ +	va_list list; +	union { +		unsigned long words[16]; +		double align; +	} u; +	void (*rtas)(void *, unsigned long); +	int i; + +	u.words[0] = service; +	u.words[1] = nargs; +	u.words[2] = nret; +	va_start(list, nret); +	for (i = 0; i < nargs; ++i) +		u.words[i+3] = va_arg(list, unsigned long); +	va_end(list); + +	rtas = (void (*)(void *, unsigned long)) rtas_entry; +	rtas(&u, rtas_data); +} + +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ +void __init +finish_device_tree(void) +{ +	unsigned long mem = (unsigned long) klimit; +	struct device_node *np; + +	/* All newworld pmac machines and CHRPs now use the interrupt tree */ +	for (np = allnodes; np != NULL; np = np->allnext) { +		if (get_property(np, "interrupt-parent", NULL)) { +			use_of_interrupt_tree = 1; +			break; +		} +	} +	if (_machine == _MACH_Pmac && use_of_interrupt_tree) +		pmac_newworld = 1; + +#ifdef CONFIG_BOOTX_TEXT +	if (boot_infos && pmac_newworld) { +		prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n"); +		prom_print("          You should use an Open Firmware bootloader\n"); +	} +#endif /* CONFIG_BOOTX_TEXT */ + +	if (use_of_interrupt_tree) { +		/* +		 * We want to find out here how many interrupt-controller +		 * nodes there are, and if we are booted from BootX, +		 * we need a pointer to the first (and hopefully only) +		 * such node.  But we can't use find_devices here since +		 * np->name has not been set yet.  -- paulus +		 */ +		int n = 0; +		char *name, *ic; +		int iclen; + +		for (np = allnodes; np != NULL; np = np->allnext) { +			ic = get_property(np, "interrupt-controller", &iclen); +			name = get_property(np, "name", NULL); +			/* checking iclen makes sure we don't get a false +			   match on /chosen.interrupt_controller */ +			if ((name != NULL +			     && strcmp(name, "interrupt-controller") == 0) +			    || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) { +				if (n == 0) +					dflt_interrupt_controller = np; +				++n; +			} +		} +		num_interrupt_controllers = n; +	} + +	mem = finish_node(allnodes, mem, NULL, 1, 1); +	dev_tree_size = mem - (unsigned long) allnodes; +	klimit = (char *) mem; +} + +static unsigned long __init +finish_node(struct device_node *np, unsigned long mem_start, +	    interpret_func *ifunc, int naddrc, int nsizec) +{ +	struct device_node *child; +	int *ip; + +	np->name = get_property(np, "name", NULL); +	np->type = get_property(np, "device_type", NULL); + +	if (!np->name) +		np->name = "<NULL>"; +	if (!np->type) +		np->type = "<NULL>"; + +	/* get the device addresses and interrupts */ +	if (ifunc != NULL) +		mem_start = ifunc(np, mem_start, naddrc, nsizec); + +	if (use_of_interrupt_tree) +		mem_start = finish_node_interrupts(np, mem_start); + +	/* Look for #address-cells and #size-cells properties. */ +	ip = (int *) get_property(np, "#address-cells", NULL); +	if (ip != NULL) +		naddrc = *ip; +	ip = (int *) get_property(np, "#size-cells", NULL); +	if (ip != NULL) +		nsizec = *ip; + +	if (np->parent == NULL) +		ifunc = interpret_root_props; +	else if (np->type == 0) +		ifunc = NULL; +	else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) +		ifunc = interpret_pci_props; +	else if (!strcmp(np->type, "dbdma")) +		ifunc = interpret_dbdma_props; +	else if (!strcmp(np->type, "mac-io") +		 || ifunc == interpret_macio_props) +		ifunc = interpret_macio_props; +	else if (!strcmp(np->type, "isa")) +		ifunc = interpret_isa_props; +	else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) +		ifunc = interpret_root_props; +	else if (!((ifunc == interpret_dbdma_props +		    || ifunc == interpret_macio_props) +		   && (!strcmp(np->type, "escc") +		       || !strcmp(np->type, "media-bay")))) +		ifunc = NULL; + +	/* if we were booted from BootX, convert the full name */ +	if (boot_infos +	    && strncmp(np->full_name, "Devices:device-tree", 19) == 0) { +		if (np->full_name[19] == 0) { +			strcpy(np->full_name, "/"); +		} else if (np->full_name[19] == ':') { +			char *p = np->full_name + 19; +			np->full_name = p; +			for (; *p; ++p) +				if (*p == ':') +					*p = '/'; +		} +	} + +	for (child = np->child; child != NULL; child = child->sibling) +		mem_start = finish_node(child, mem_start, ifunc, +					naddrc, nsizec); + +	return mem_start; +} + +/* + * Find the interrupt parent of a node. + */ +static struct device_node * __init +intr_parent(struct device_node *p) +{ +	phandle *parp; + +	parp = (phandle *) get_property(p, "interrupt-parent", NULL); +	if (parp == NULL) +		return p->parent; +	p = find_phandle(*parp); +	if (p != NULL) +		return p; +	/* +	 * On a powermac booted with BootX, we don't get to know the +	 * phandles for any nodes, so find_phandle will return NULL. +	 * Fortunately these machines only have one interrupt controller +	 * so there isn't in fact any ambiguity.  -- paulus +	 */ +	if (num_interrupt_controllers == 1) +		p = dflt_interrupt_controller; +	return p; +} + +/* + * Find out the size of each entry of the interrupts property + * for a node. + */ +static int __init +prom_n_intr_cells(struct device_node *np) +{ +	struct device_node *p; +	unsigned int *icp; + +	for (p = np; (p = intr_parent(p)) != NULL; ) { +		icp = (unsigned int *) +			get_property(p, "#interrupt-cells", NULL); +		if (icp != NULL) +			return *icp; +		if (get_property(p, "interrupt-controller", NULL) != NULL +		    || get_property(p, "interrupt-map", NULL) != NULL) { +			printk("oops, node %s doesn't have #interrupt-cells\n", +			       p->full_name); +			return 1; +		} +	} +	printk("prom_n_intr_cells failed for %s\n", np->full_name); +	return 1; +} + +/* + * Map an interrupt from a device up to the platform interrupt + * descriptor. + */ +static int __init +map_interrupt(unsigned int **irq, struct device_node **ictrler, +	      struct device_node *np, unsigned int *ints, int nintrc) +{ +	struct device_node *p, *ipar; +	unsigned int *imap, *imask, *ip; +	int i, imaplen, match; +	int newintrc = 1, newaddrc = 1; +	unsigned int *reg; +	int naddrc; + +	reg = (unsigned int *) get_property(np, "reg", NULL); +	naddrc = prom_n_addr_cells(np); +	p = intr_parent(np); +	while (p != NULL) { +		if (get_property(p, "interrupt-controller", NULL) != NULL) +			/* this node is an interrupt controller, stop here */ +			break; +		imap = (unsigned int *) +			get_property(p, "interrupt-map", &imaplen); +		if (imap == NULL) { +			p = intr_parent(p); +			continue; +		} +		imask = (unsigned int *) +			get_property(p, "interrupt-map-mask", NULL); +		if (imask == NULL) { +			printk("oops, %s has interrupt-map but no mask\n", +			       p->full_name); +			return 0; +		} +		imaplen /= sizeof(unsigned int); +		match = 0; +		ipar = NULL; +		while (imaplen > 0 && !match) { +			/* check the child-interrupt field */ +			match = 1; +			for (i = 0; i < naddrc && match; ++i) +				match = ((reg[i] ^ imap[i]) & imask[i]) == 0; +			for (; i < naddrc + nintrc && match; ++i) +				match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0; +			imap += naddrc + nintrc; +			imaplen -= naddrc + nintrc; +			/* grab the interrupt parent */ +			ipar = find_phandle((phandle) *imap++); +			--imaplen; +			if (ipar == NULL && num_interrupt_controllers == 1) +				/* cope with BootX not giving us phandles */ +				ipar = dflt_interrupt_controller; +			if (ipar == NULL) { +				printk("oops, no int parent %x in map of %s\n", +				       imap[-1], p->full_name); +				return 0; +			} +			/* find the parent's # addr and intr cells */ +			ip = (unsigned int *) +				get_property(ipar, "#interrupt-cells", NULL); +			if (ip == NULL) { +				printk("oops, no #interrupt-cells on %s\n", +				       ipar->full_name); +				return 0; +			} +			newintrc = *ip; +			ip = (unsigned int *) +				get_property(ipar, "#address-cells", NULL); +			newaddrc = (ip == NULL)? 0: *ip; +			imap += newaddrc + newintrc; +			imaplen -= newaddrc + newintrc; +		} +		if (imaplen < 0) { +			printk("oops, error decoding int-map on %s, len=%d\n", +			       p->full_name, imaplen); +			return 0; +		} +		if (!match) { +			printk("oops, no match in %s int-map for %s\n", +			       p->full_name, np->full_name); +			return 0; +		} +		p = ipar; +		naddrc = newaddrc; +		nintrc = newintrc; +		ints = imap - nintrc; +		reg = ints - naddrc; +	} +	if (p == NULL) +		printk("hmmm, int tree for %s doesn't have ctrler\n", +		       np->full_name); +	*irq = ints; +	*ictrler = p; +	return nintrc; +} + +/* + * New version of finish_node_interrupts. + */ +static unsigned long __init +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ +	unsigned int *ints; +	int intlen, intrcells; +	int i, j, n, offset; +	unsigned int *irq; +	struct device_node *ic; + +	ints = (unsigned int *) get_property(np, "interrupts", &intlen); +	if (ints == NULL) +		return mem_start; +	intrcells = prom_n_intr_cells(np); +	intlen /= intrcells * sizeof(unsigned int); +	np->n_intrs = intlen; +	np->intrs = (struct interrupt_info *) mem_start; +	mem_start += intlen * sizeof(struct interrupt_info); + +	for (i = 0; i < intlen; ++i) { +		np->intrs[i].line = 0; +		np->intrs[i].sense = 1; +		n = map_interrupt(&irq, &ic, np, ints, intrcells); +		if (n <= 0) +			continue; +		offset = 0; +		/* +		 * On a CHRP we have an 8259 which is subordinate to +		 * the openpic in the interrupt tree, but we want the +		 * openpic's interrupt numbers offsetted, not the 8259's. +		 * So we apply the offset if the controller is at the +		 * root of the interrupt tree, i.e. has no interrupt-parent. +		 * This doesn't cope with the general case of multiple +		 * cascaded interrupt controllers, but then neither will +		 * irq.c at the moment either.  -- paulus +		 * The G5 triggers that code, I add a machine test. On +		 * those machines, we want to offset interrupts from the +		 * second openpic by 128 -- BenH +		 */ +		if (_machine != _MACH_Pmac && num_interrupt_controllers > 1 +		    && ic != NULL +		    && get_property(ic, "interrupt-parent", NULL) == NULL) +			offset = 16; +		else if (_machine == _MACH_Pmac && num_interrupt_controllers > 1 +			 && ic != NULL && ic->parent != NULL) { +			char *name = get_property(ic->parent, "name", NULL); +			if (name && !strcmp(name, "u3")) +				offset = 128; +		} + +		np->intrs[i].line = irq[0] + offset; +		if (n > 1) +			np->intrs[i].sense = irq[1]; +		if (n > 2) { +			printk("hmmm, got %d intr cells for %s:", n, +			       np->full_name); +			for (j = 0; j < n; ++j) +				printk(" %d", irq[j]); +			printk("\n"); +		} +		ints += intrcells; +	} + +	return mem_start; +} + +/* + * When BootX makes a copy of the device tree from the MacOS + * Name Registry, it is in the format we use but all of the pointers + * are offsets from the start of the tree. + * This procedure updates the pointers. + */ +void __init +relocate_nodes(void) +{ +	unsigned long base; +	struct device_node *np; +	struct property *pp; + +#define ADDBASE(x)	(x = (typeof (x))((x)? ((unsigned long)(x) + base): 0)) + +	base = (unsigned long) boot_infos + boot_infos->deviceTreeOffset; +	allnodes = (struct device_node *)(base + 4); +	for (np = allnodes; np != 0; np = np->allnext) { +		ADDBASE(np->full_name); +		ADDBASE(np->properties); +		ADDBASE(np->parent); +		ADDBASE(np->child); +		ADDBASE(np->sibling); +		ADDBASE(np->allnext); +		for (pp = np->properties; pp != 0; pp = pp->next) { +			ADDBASE(pp->name); +			ADDBASE(pp->value); +			ADDBASE(pp->next); +		} +	} +} + +int +prom_n_addr_cells(struct device_node* np) +{ +	int* ip; +	do { +		if (np->parent) +			np = np->parent; +		ip = (int *) get_property(np, "#address-cells", NULL); +		if (ip != NULL) +			return *ip; +	} while (np->parent); +	/* No #address-cells property for the root node, default to 1 */ +	return 1; +} + +int +prom_n_size_cells(struct device_node* np) +{ +	int* ip; +	do { +		if (np->parent) +			np = np->parent; +		ip = (int *) get_property(np, "#size-cells", NULL); +		if (ip != NULL) +			return *ip; +	} while (np->parent); +	/* No #size-cells property for the root node, default to 1 */ +	return 1; +} + +static unsigned long __init +map_addr(struct device_node *np, unsigned long space, unsigned long addr) +{ +	int na; +	unsigned int *ranges; +	int rlen = 0; +	unsigned int type; + +	type = (space >> 24) & 3; +	if (type == 0) +		return addr; + +	while ((np = np->parent) != NULL) { +		if (strcmp(np->type, "pci") != 0) +			continue; +		/* PCI bridge: map the address through the ranges property */ +		na = prom_n_addr_cells(np); +		ranges = (unsigned int *) get_property(np, "ranges", &rlen); +		while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) { +			if (((ranges[0] >> 24) & 3) == type +			    && ranges[2] <= addr +			    && addr - ranges[2] < ranges[na+4]) { +				/* ok, this matches, translate it */ +				addr += ranges[na+2] - ranges[2]; +				break; +			} +			ranges += na + 5; +		} +	} +	return addr; +} + +static unsigned long __init +interpret_pci_props(struct device_node *np, unsigned long mem_start, +		    int naddrc, int nsizec) +{ +	struct address_range *adr; +	struct pci_reg_property *pci_addrs; +	int i, l, *ip; + +	pci_addrs = (struct pci_reg_property *) +		get_property(np, "assigned-addresses", &l); +	if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { +		i = 0; +		adr = (struct address_range *) mem_start; +		while ((l -= sizeof(struct pci_reg_property)) >= 0) { +			adr[i].space = pci_addrs[i].addr.a_hi; +			adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi, +						  pci_addrs[i].addr.a_lo); +			adr[i].size = pci_addrs[i].size_lo; +			++i; +		} +		np->addrs = adr; +		np->n_addrs = i; +		mem_start += i * sizeof(struct address_range); +	} + +	if (use_of_interrupt_tree) +		return mem_start; + +	ip = (int *) get_property(np, "AAPL,interrupts", &l); +	if (ip == 0 && np->parent) +		ip = (int *) get_property(np->parent, "AAPL,interrupts", &l); +	if (ip == 0) +		ip = (int *) get_property(np, "interrupts", &l); +	if (ip != 0) { +		np->intrs = (struct interrupt_info *) mem_start; +		np->n_intrs = l / sizeof(int); +		mem_start += np->n_intrs * sizeof(struct interrupt_info); +		for (i = 0; i < np->n_intrs; ++i) { +			np->intrs[i].line = *ip++; +			np->intrs[i].sense = 1; +		} +	} + +	return mem_start; +} + +static unsigned long __init +interpret_dbdma_props(struct device_node *np, unsigned long mem_start, +		      int naddrc, int nsizec) +{ +	struct reg_property *rp; +	struct address_range *adr; +	unsigned long base_address; +	int i, l, *ip; +	struct device_node *db; + +	base_address = 0; +	for (db = np->parent; db != NULL; db = db->parent) { +		if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { +			base_address = db->addrs[0].address; +			break; +		} +	} + +	rp = (struct reg_property *) get_property(np, "reg", &l); +	if (rp != 0 && l >= sizeof(struct reg_property)) { +		i = 0; +		adr = (struct address_range *) mem_start; +		while ((l -= sizeof(struct reg_property)) >= 0) { +			adr[i].space = 2; +			adr[i].address = rp[i].address + base_address; +			adr[i].size = rp[i].size; +			++i; +		} +		np->addrs = adr; +		np->n_addrs = i; +		mem_start += i * sizeof(struct address_range); +	} + +	if (use_of_interrupt_tree) +		return mem_start; + +	ip = (int *) get_property(np, "AAPL,interrupts", &l); +	if (ip == 0) +		ip = (int *) get_property(np, "interrupts", &l); +	if (ip != 0) { +		np->intrs = (struct interrupt_info *) mem_start; +		np->n_intrs = l / sizeof(int); +		mem_start += np->n_intrs * sizeof(struct interrupt_info); +		for (i = 0; i < np->n_intrs; ++i) { +			np->intrs[i].line = *ip++; +			np->intrs[i].sense = 1; +		} +	} + +	return mem_start; +} + +static unsigned long __init +interpret_macio_props(struct device_node *np, unsigned long mem_start, +		      int naddrc, int nsizec) +{ +	struct reg_property *rp; +	struct address_range *adr; +	unsigned long base_address; +	int i, l, *ip; +	struct device_node *db; + +	base_address = 0; +	for (db = np->parent; db != NULL; db = db->parent) { +		if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { +			base_address = db->addrs[0].address; +			break; +		} +	} + +	rp = (struct reg_property *) get_property(np, "reg", &l); +	if (rp != 0 && l >= sizeof(struct reg_property)) { +		i = 0; +		adr = (struct address_range *) mem_start; +		while ((l -= sizeof(struct reg_property)) >= 0) { +			adr[i].space = 2; +			adr[i].address = rp[i].address + base_address; +			adr[i].size = rp[i].size; +			++i; +		} +		np->addrs = adr; +		np->n_addrs = i; +		mem_start += i * sizeof(struct address_range); +	} + +	if (use_of_interrupt_tree) +		return mem_start; + +	ip = (int *) get_property(np, "interrupts", &l); +	if (ip == 0) +		ip = (int *) get_property(np, "AAPL,interrupts", &l); +	if (ip != 0) { +		np->intrs = (struct interrupt_info *) mem_start; +		np->n_intrs = l / sizeof(int); +		for (i = 0; i < np->n_intrs; ++i) { +			np->intrs[i].line = *ip++; +			np->intrs[i].sense = 1; +		} +		mem_start += np->n_intrs * sizeof(struct interrupt_info); +	} + +	return mem_start; +} + +static unsigned long __init +interpret_isa_props(struct device_node *np, unsigned long mem_start, +		    int naddrc, int nsizec) +{ +	struct isa_reg_property *rp; +	struct address_range *adr; +	int i, l, *ip; + +	rp = (struct isa_reg_property *) get_property(np, "reg", &l); +	if (rp != 0 && l >= sizeof(struct isa_reg_property)) { +		i = 0; +		adr = (struct address_range *) mem_start; +		while ((l -= sizeof(struct reg_property)) >= 0) { +			adr[i].space = rp[i].space; +			adr[i].address = rp[i].address +				+ (adr[i].space? 0: _ISA_MEM_BASE); +			adr[i].size = rp[i].size; +			++i; +		} +		np->addrs = adr; +		np->n_addrs = i; +		mem_start += i * sizeof(struct address_range); +	} + +	if (use_of_interrupt_tree) +		return mem_start; + +	ip = (int *) get_property(np, "interrupts", &l); +	if (ip != 0) { +		np->intrs = (struct interrupt_info *) mem_start; +		np->n_intrs = l / (2 * sizeof(int)); +		mem_start += np->n_intrs * sizeof(struct interrupt_info); +		for (i = 0; i < np->n_intrs; ++i) { +			np->intrs[i].line = *ip++; +			np->intrs[i].sense = *ip++; +		} +	} + +	return mem_start; +} + +static unsigned long __init +interpret_root_props(struct device_node *np, unsigned long mem_start, +		     int naddrc, int nsizec) +{ +	struct address_range *adr; +	int i, l, *ip; +	unsigned int *rp; +	int rpsize = (naddrc + nsizec) * sizeof(unsigned int); + +	rp = (unsigned int *) get_property(np, "reg", &l); +	if (rp != 0 && l >= rpsize) { +		i = 0; +		adr = (struct address_range *) mem_start; +		while ((l -= rpsize) >= 0) { +			adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2); +			adr[i].address = rp[naddrc - 1]; +			adr[i].size = rp[naddrc + nsizec - 1]; +			++i; +			rp += naddrc + nsizec; +		} +		np->addrs = adr; +		np->n_addrs = i; +		mem_start += i * sizeof(struct address_range); +	} + +	if (use_of_interrupt_tree) +		return mem_start; + +	ip = (int *) get_property(np, "AAPL,interrupts", &l); +	if (ip == 0) +		ip = (int *) get_property(np, "interrupts", &l); +	if (ip != 0) { +		np->intrs = (struct interrupt_info *) mem_start; +		np->n_intrs = l / sizeof(int); +		mem_start += np->n_intrs * sizeof(struct interrupt_info); +		for (i = 0; i < np->n_intrs; ++i) { +			np->intrs[i].line = *ip++; +			np->intrs[i].sense = 1; +		} +	} + +	return mem_start; +} + +/* + * Work out the sense (active-low level / active-high edge) + * of each interrupt from the device tree. + */ +void __init +prom_get_irq_senses(unsigned char *senses, int off, int max) +{ +	struct device_node *np; +	int i, j; + +	/* default to level-triggered */ +	memset(senses, 1, max - off); +	if (!use_of_interrupt_tree) +		return; + +	for (np = allnodes; np != 0; np = np->allnext) { +		for (j = 0; j < np->n_intrs; j++) { +			i = np->intrs[j].line; +			if (i >= off && i < max) { +				if (np->intrs[j].sense == 1) +					senses[i-off] = (IRQ_SENSE_LEVEL +						| IRQ_POLARITY_NEGATIVE); +				else +					senses[i-off] = (IRQ_SENSE_EDGE +						| IRQ_POLARITY_POSITIVE); +			} +		} +	} +} + +/* + * Construct and return a list of the device_nodes with a given name. + */ +struct device_node * +find_devices(const char *name) +{ +	struct device_node *head, **prevp, *np; + +	prevp = &head; +	for (np = allnodes; np != 0; np = np->allnext) { +		if (np->name != 0 && strcasecmp(np->name, name) == 0) { +			*prevp = np; +			prevp = &np->next; +		} +	} +	*prevp = NULL; +	return head; +} + +/* + * Construct and return a list of the device_nodes with a given type. + */ +struct device_node * +find_type_devices(const char *type) +{ +	struct device_node *head, **prevp, *np; + +	prevp = &head; +	for (np = allnodes; np != 0; np = np->allnext) { +		if (np->type != 0 && strcasecmp(np->type, type) == 0) { +			*prevp = np; +			prevp = &np->next; +		} +	} +	*prevp = NULL; +	return head; +} + +/* + * Returns all nodes linked together + */ +struct device_node * __openfirmware +find_all_nodes(void) +{ +	struct device_node *head, **prevp, *np; + +	prevp = &head; +	for (np = allnodes; np != 0; np = np->allnext) { +		*prevp = np; +		prevp = &np->next; +	} +	*prevp = NULL; +	return head; +} + +/* Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +int +device_is_compatible(struct device_node *device, const char *compat) +{ +	const char* cp; +	int cplen, l; + +	cp = (char *) get_property(device, "compatible", &cplen); +	if (cp == NULL) +		return 0; +	while (cplen > 0) { +		if (strncasecmp(cp, compat, strlen(compat)) == 0) +			return 1; +		l = strlen(cp) + 1; +		cp += l; +		cplen -= l; +	} + +	return 0; +} + + +/* + * Indicates whether the root node has a given value in its + * compatible property. + */ +int +machine_is_compatible(const char *compat) +{ +	struct device_node *root; + +	root = find_path_device("/"); +	if (root == 0) +		return 0; +	return device_is_compatible(root, compat); +} + +/* + * Construct and return a list of the device_nodes with a given type + * and compatible property. + */ +struct device_node * +find_compatible_devices(const char *type, const char *compat) +{ +	struct device_node *head, **prevp, *np; + +	prevp = &head; +	for (np = allnodes; np != 0; np = np->allnext) { +		if (type != NULL +		    && !(np->type != 0 && strcasecmp(np->type, type) == 0)) +			continue; +		if (device_is_compatible(np, compat)) { +			*prevp = np; +			prevp = &np->next; +		} +	} +	*prevp = NULL; +	return head; +} + +/* + * Find the device_node with a given full_name. + */ +struct device_node * +find_path_device(const char *path) +{ +	struct device_node *np; + +	for (np = allnodes; np != 0; np = np->allnext) +		if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) +			return np; +	return NULL; +} + +/******* + * + * New implementation of the OF "find" APIs, return a refcounted + * object, call of_node_put() when done. Currently, still lacks + * locking as old implementation, this is beeing done for ppc64. + * + * Note that property management will need some locking as well, + * this isn't dealt with yet + * + *******/ + +/** + *	of_find_node_by_name - Find a node by it's "name" property + *	@from:	The node to start searching from or NULL, the node + *		you pass will not be searched, only the next one + *		will; typically, you pass what the previous call + *		returned. of_node_put() will be called on it + *	@name:	The name string to match against + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_find_node_by_name(struct device_node *from, +	const char *name) +{ +	struct device_node *np = from ? from->allnext : allnodes; + +	for (; np != 0; np = np->allnext) +		if (np->name != 0 && strcasecmp(np->name, name) == 0) +			break; +	if (from) +		of_node_put(from); +	return of_node_get(np); +} + +/** + *	of_find_node_by_type - Find a node by it's "device_type" property + *	@from:	The node to start searching from or NULL, the node + *		you pass will not be searched, only the next one + *		will; typically, you pass what the previous call + *		returned. of_node_put() will be called on it + *	@name:	The type string to match against + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_find_node_by_type(struct device_node *from, +	const char *type) +{ +	struct device_node *np = from ? from->allnext : allnodes; + +	for (; np != 0; np = np->allnext) +		if (np->type != 0 && strcasecmp(np->type, type) == 0) +			break; +	if (from) +		of_node_put(from); +	return of_node_get(np); +} + +/** + *	of_find_compatible_node - Find a node based on type and one of the + *                                tokens in it's "compatible" property + *	@from:		The node to start searching from or NULL, the node + *			you pass will not be searched, only the next one + *			will; typically, you pass what the previous call + *			returned. of_node_put() will be called on it + *	@type:		The type string to match "device_type" or NULL to ignore + *	@compatible:	The string to match to one of the tokens in the device + *			"compatible" list. + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_find_compatible_node(struct device_node *from, +	const char *type, const char *compatible) +{ +	struct device_node *np = from ? from->allnext : allnodes; + +	for (; np != 0; np = np->allnext) { +		if (type != NULL +		    && !(np->type != 0 && strcasecmp(np->type, type) == 0)) +			continue; +		if (device_is_compatible(np, compatible)) +			break; +	} +	if (from) +		of_node_put(from); +	return of_node_get(np); +} + +/** + *	of_find_node_by_path - Find a node matching a full OF path + *	@path:	The full path to match + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_find_node_by_path(const char *path) +{ +	struct device_node *np = allnodes; + +	for (; np != 0; np = np->allnext) +		if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) +			break; +	return of_node_get(np); +} + +/** + *	of_find_all_nodes - Get next node in global list + *	@prev:	Previous node or NULL to start iteration + *		of_node_put() will be called on it + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_find_all_nodes(struct device_node *prev) +{ +	return of_node_get(prev ? prev->allnext : allnodes); +} + +/** + *	of_get_parent - Get a node's parent if any + *	@node:	Node to get parent + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_get_parent(const struct device_node *node) +{ +	return node ? of_node_get(node->parent) : NULL; +} + +/** + *	of_get_next_child - Iterate a node childs + *	@node:	parent node + *	@prev:	previous child of the parent node, or NULL to get first + * + *	Returns a node pointer with refcount incremented, use + *	of_node_put() on it when done. + */ +struct device_node *of_get_next_child(const struct device_node *node, +				      struct device_node *prev) +{ +	struct device_node *next = prev ? prev->sibling : node->child; + +	for (; next != 0; next = next->sibling) +		if (of_node_get(next)) +			break; +	if (prev) +		of_node_put(prev); +	return next; +} + +/** + *	of_node_get - Increment refcount of a node + *	@node:	Node to inc refcount, NULL is supported to + *		simplify writing of callers + * + *	Returns the node itself or NULL if gone. Current implementation + *	does nothing as we don't yet do dynamic node allocation on ppc32 + */ +struct device_node *of_node_get(struct device_node *node) +{ +	return node; +} + +/** + *	of_node_put - Decrement refcount of a node + *	@node:	Node to dec refcount, NULL is supported to + *		simplify writing of callers + * + *	Current implementation does nothing as we don't yet do dynamic node + *	allocation on ppc32 + */ +void  of_node_put(struct device_node *node) +{ +} + +/* + * Find the device_node with a given phandle. + */ +static struct device_node * __init +find_phandle(phandle ph) +{ +	struct device_node *np; + +	for (np = allnodes; np != 0; np = np->allnext) +		if (np->node == ph) +			return np; +	return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +unsigned char * +get_property(struct device_node *np, const char *name, int *lenp) +{ +	struct property *pp; + +	for (pp = np->properties; pp != 0; pp = pp->next) +		if (pp->name != NULL && strcmp(pp->name, name) == 0) { +			if (lenp != 0) +				*lenp = pp->length; +			return pp->value; +		} +	return NULL; +} + +/* + * Add a property to a node + */ +void __openfirmware +prom_add_property(struct device_node* np, struct property* prop) +{ +	struct property **next = &np->properties; + +	prop->next = NULL; +	while (*next) +		next = &(*next)->next; +	*next = prop; +} + +/* I quickly hacked that one, check against spec ! */ +static inline unsigned long __openfirmware +bus_space_to_resource_flags(unsigned int bus_space) +{ +	u8 space = (bus_space >> 24) & 0xf; +	if (space == 0) +		space = 0x02; +	if (space == 0x02) +		return IORESOURCE_MEM; +	else if (space == 0x01) +		return IORESOURCE_IO; +	else { +		printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", +		    	bus_space); +		return 0; +	} +} + +static struct resource* __openfirmware +find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range) +{ +	unsigned long mask; +	int i; + +	/* Check this one */ +	mask = bus_space_to_resource_flags(range->space); +	for (i=0; i<DEVICE_COUNT_RESOURCE; i++) { +		if ((pdev->resource[i].flags & mask) == mask && +			pdev->resource[i].start <= range->address && +			pdev->resource[i].end > range->address) { +				if ((range->address + range->size - 1) > pdev->resource[i].end) { +					/* Add better message */ +					printk(KERN_WARNING "PCI/OF resource overlap !\n"); +					return NULL; +				} +				break; +			} +	} +	if (i == DEVICE_COUNT_RESOURCE) +		return NULL; +	return &pdev->resource[i]; +} + +/* + * Request an OF device resource. Currently handles child of PCI devices, + * or other nodes attached to the root node. Ultimately, put some + * link to resources in the OF node. + */ +struct resource* __openfirmware +request_OF_resource(struct device_node* node, int index, const char* name_postfix) +{ +	struct pci_dev* pcidev; +	u8 pci_bus, pci_devfn; +	unsigned long iomask; +	struct device_node* nd; +	struct resource* parent; +	struct resource *res = NULL; +	int nlen, plen; + +	if (index >= node->n_addrs) +		goto fail; + +	/* Sanity check on bus space */ +	iomask = bus_space_to_resource_flags(node->addrs[index].space); +	if (iomask & IORESOURCE_MEM) +		parent = &iomem_resource; +	else if (iomask & IORESOURCE_IO) +		parent = &ioport_resource; +	else +		goto fail; + +	/* Find a PCI parent if any */ +	nd = node; +	pcidev = NULL; +	while(nd) { +		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) +			pcidev = pci_find_slot(pci_bus, pci_devfn); +		if (pcidev) break; +		nd = nd->parent; +	} +	if (pcidev) +		parent = find_parent_pci_resource(pcidev, &node->addrs[index]); +	if (!parent) { +		printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", +			node->name); +		goto fail; +	} + +	res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL); +	if (!res) +		goto fail; +	nlen = strlen(node->name); +	plen = name_postfix ? strlen(name_postfix) : 0; +	res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); +	if (res->name) { +		strcpy((char *)res->name, node->name); +		if (plen) +			strcpy((char *)res->name+nlen, name_postfix); +	} +	return res; +fail: +	return NULL; +} + +int __openfirmware +release_OF_resource(struct device_node* node, int index) +{ +	struct pci_dev* pcidev; +	u8 pci_bus, pci_devfn; +	unsigned long iomask, start, end; +	struct device_node* nd; +	struct resource* parent; +	struct resource *res = NULL; + +	if (index >= node->n_addrs) +		return -EINVAL; + +	/* Sanity check on bus space */ +	iomask = bus_space_to_resource_flags(node->addrs[index].space); +	if (iomask & IORESOURCE_MEM) +		parent = &iomem_resource; +	else if (iomask & IORESOURCE_IO) +		parent = &ioport_resource; +	else +		return -EINVAL; + +	/* Find a PCI parent if any */ +	nd = node; +	pcidev = NULL; +	while(nd) { +		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) +			pcidev = pci_find_slot(pci_bus, pci_devfn); +		if (pcidev) break; +		nd = nd->parent; +	} +	if (pcidev) +		parent = find_parent_pci_resource(pcidev, &node->addrs[index]); +	if (!parent) { +		printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", +			node->name); +		return -ENODEV; +	} + +	/* Find us in the parent and its childs */ +	res = parent->child; +	start = node->addrs[index].address; +	end = start + node->addrs[index].size - 1; +	while (res) { +		if (res->start == start && res->end == end && +		    (res->flags & IORESOURCE_BUSY)) +		    	break; +		if (res->start <= start && res->end >= end) +			res = res->child; +		else +			res = res->sibling; +	} +	if (!res) +		return -ENODEV; + +	if (res->name) { +		kfree(res->name); +		res->name = NULL; +	} +	release_resource(res); +	kfree(res); + +	return 0; +} + +#if 0 +void __openfirmware +print_properties(struct device_node *np) +{ +	struct property *pp; +	char *cp; +	int i, n; + +	for (pp = np->properties; pp != 0; pp = pp->next) { +		printk(KERN_INFO "%s", pp->name); +		for (i = strlen(pp->name); i < 16; ++i) +			printk(" "); +		cp = (char *) pp->value; +		for (i = pp->length; i > 0; --i, ++cp) +			if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) +			    || (i == 1 && *cp != 0)) +				break; +		if (i == 0 && pp->length > 1) { +			/* looks like a string */ +			printk(" %s\n", (char *) pp->value); +		} else { +			/* dump it in hex */ +			n = pp->length; +			if (n > 64) +				n = 64; +			if (pp->length % 4 == 0) { +				unsigned int *p = (unsigned int *) pp->value; + +				n /= 4; +				for (i = 0; i < n; ++i) { +					if (i != 0 && (i % 4) == 0) +						printk("\n                "); +					printk(" %08x", *p++); +				} +			} else { +				unsigned char *bp = pp->value; + +				for (i = 0; i < n; ++i) { +					if (i != 0 && (i % 16) == 0) +						printk("\n                "); +					printk(" %02x", *bp++); +				} +			} +			printk("\n"); +			if (pp->length > 64) +				printk("                 ... (length = %d)\n", +				       pp->length); +		} +	} +} +#endif + +static DEFINE_SPINLOCK(rtas_lock); + +/* this can be called after setup -- Cort */ +int __openfirmware +call_rtas(const char *service, int nargs, int nret, +	  unsigned long *outputs, ...) +{ +	va_list list; +	int i; +	unsigned long s; +	struct device_node *rtas; +	int *tokp; +	union { +		unsigned long words[16]; +		double align; +	} u; + +	rtas = find_devices("rtas"); +	if (rtas == NULL) +		return -1; +	tokp = (int *) get_property(rtas, service, NULL); +	if (tokp == NULL) { +		printk(KERN_ERR "No RTAS service called %s\n", service); +		return -1; +	} +	u.words[0] = *tokp; +	u.words[1] = nargs; +	u.words[2] = nret; +	va_start(list, outputs); +	for (i = 0; i < nargs; ++i) +		u.words[i+3] = va_arg(list, unsigned long); +	va_end(list); + +	/* +	 * RTAS doesn't use floating point. +	 * Or at least, according to the CHRP spec we enter RTAS +	 * with FP disabled, and it doesn't change the FP registers. +	 *  -- paulus. +	 */ +	spin_lock_irqsave(&rtas_lock, s); +	enter_rtas((void *)__pa(&u)); +	spin_unlock_irqrestore(&rtas_lock, s); + +	if (nret > 1 && outputs != NULL) +		for (i = 0; i < nret-1; ++i) +			outputs[i] = u.words[i+nargs+4]; +	return u.words[nargs+3]; +} diff --git a/arch/ppc/syslib/prom_init.c b/arch/ppc/syslib/prom_init.c new file mode 100644 index 00000000000..2cee87137f2 --- /dev/null +++ b/arch/ppc/syslib/prom_init.c @@ -0,0 +1,1002 @@ +/* + * Note that prom_init() and anything called from prom_init() + * may be running at an address that is different from the address + * that it was linked at.  References to static data items are + * handled by compiling this file with -mrelocatable-lib. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/version.h> +#include <linux/threads.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/bitops.h> + +#include <asm/sections.h> +#include <asm/prom.h> +#include <asm/page.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/bootx.h> +#include <asm/system.h> +#include <asm/mmu.h> +#include <asm/pgtable.h> +#include <asm/bootinfo.h> +#include <asm/btext.h> +#include <asm/pci-bridge.h> +#include <asm/open_pic.h> +#include <asm/cacheflush.h> + +#ifdef CONFIG_LOGO_LINUX_CLUT224 +#include <linux/linux_logo.h> +extern const struct linux_logo logo_linux_clut224; +#endif + +/* + * Properties whose value is longer than this get excluded from our + * copy of the device tree.  This way we don't waste space storing + * things like "driver,AAPL,MacOS,PowerPC" properties.  But this value + * does need to be big enough to ensure that we don't lose things + * like the interrupt-map property on a PCI-PCI bridge. + */ +#define MAX_PROPERTY_LENGTH	4096 + +#ifndef FB_MAX			/* avoid pulling in all of the fb stuff */ +#define FB_MAX	8 +#endif + +#define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) + +typedef u32 prom_arg_t; + +struct prom_args { +	const char *service; +	int nargs; +	int nret; +	prom_arg_t args[10]; +}; + +struct pci_address { +	unsigned a_hi; +	unsigned a_mid; +	unsigned a_lo; +}; + +struct pci_reg_property { +	struct pci_address addr; +	unsigned size_hi; +	unsigned size_lo; +}; + +struct pci_range { +	struct pci_address addr; +	unsigned phys; +	unsigned size_hi; +	unsigned size_lo; +}; + +struct isa_reg_property { +	unsigned space; +	unsigned address; +	unsigned size; +}; + +struct pci_intr_map { +	struct pci_address addr; +	unsigned dunno; +	phandle int_ctrler; +	unsigned intr; +}; + +static void prom_exit(void); +static int  call_prom(const char *service, int nargs, int nret, ...); +static int  call_prom_ret(const char *service, int nargs, int nret, +			  prom_arg_t *rets, ...); +static void prom_print_hex(unsigned int v); +static int  prom_set_color(ihandle ih, int i, int r, int g, int b); +static int  prom_next_node(phandle *nodep); +static unsigned long check_display(unsigned long mem); +static void setup_disp_fake_bi(ihandle dp); +static unsigned long copy_device_tree(unsigned long mem_start, +				unsigned long mem_end); +static unsigned long inspect_node(phandle node, struct device_node *dad, +				unsigned long mem_start, unsigned long mem_end, +				struct device_node ***allnextpp); +static void prom_hold_cpus(unsigned long mem); +static void prom_instantiate_rtas(void); +static void * early_get_property(unsigned long base, unsigned long node, +				char *prop); + +prom_entry prom __initdata; +ihandle prom_chosen __initdata; +ihandle prom_stdout __initdata; + +static char *prom_display_paths[FB_MAX] __initdata; +static phandle prom_display_nodes[FB_MAX] __initdata; +static unsigned int prom_num_displays __initdata; +static ihandle prom_disp_node __initdata; +char *of_stdout_device __initdata; + +unsigned int rtas_data;   /* physical pointer */ +unsigned int rtas_entry;  /* physical pointer */ +unsigned int rtas_size; +unsigned int old_rtas; + +boot_infos_t *boot_infos; +char *bootpath; +char *bootdevice; +struct device_node *allnodes; + +extern char *klimit; + +static void __init +prom_exit(void) +{ +	struct prom_args args; + +	args.service = "exit"; +	args.nargs = 0; +	args.nret = 0; +	prom(&args); +	for (;;)			/* should never get here */ +		; +} + +static int __init +call_prom(const char *service, int nargs, int nret, ...) +{ +	va_list list; +	int i; +	struct prom_args prom_args; + +	prom_args.service = service; +	prom_args.nargs = nargs; +	prom_args.nret = nret; +	va_start(list, nret); +	for (i = 0; i < nargs; ++i) +		prom_args.args[i] = va_arg(list, prom_arg_t); +	va_end(list); +	for (i = 0; i < nret; ++i) +		prom_args.args[i + nargs] = 0; +	prom(&prom_args); +	return prom_args.args[nargs]; +} + +static int __init +call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...) +{ +	va_list list; +	int i; +	struct prom_args prom_args; + +	prom_args.service = service; +	prom_args.nargs = nargs; +	prom_args.nret = nret; +	va_start(list, rets); +	for (i = 0; i < nargs; ++i) +		prom_args.args[i] = va_arg(list, int); +	va_end(list); +	for (i = 0; i < nret; ++i) +		prom_args.args[i + nargs] = 0; +	prom(&prom_args); +	for (i = 1; i < nret; ++i) +		rets[i-1] = prom_args.args[nargs + i]; +	return prom_args.args[nargs]; +} + +void __init +prom_print(const char *msg) +{ +	const char *p, *q; + +	if (prom_stdout == 0) +		return; + +	for (p = msg; *p != 0; p = q) { +		for (q = p; *q != 0 && *q != '\n'; ++q) +			; +		if (q > p) +			call_prom("write", 3, 1, prom_stdout, p, q - p); +		if (*q != 0) { +			++q; +			call_prom("write", 3, 1, prom_stdout, "\r\n", 2); +		} +	} +} + +static void __init +prom_print_hex(unsigned int v) +{ +	char buf[16]; +	int i, c; + +	for (i = 0; i < 8; ++i) { +		c = (v >> ((7-i)*4)) & 0xf; +		c += (c >= 10)? ('a' - 10): '0'; +		buf[i] = c; +	} +	buf[i] = ' '; +	buf[i+1] = 0; +	prom_print(buf); +} + +static int __init +prom_set_color(ihandle ih, int i, int r, int g, int b) +{ +	return call_prom("call-method", 6, 1, "color!", ih, i, b, g, r); +} + +static int __init +prom_next_node(phandle *nodep) +{ +	phandle node; + +	if ((node = *nodep) != 0 +	    && (*nodep = call_prom("child", 1, 1, node)) != 0) +		return 1; +	if ((*nodep = call_prom("peer", 1, 1, node)) != 0) +		return 1; +	for (;;) { +		if ((node = call_prom("parent", 1, 1, node)) == 0) +			return 0; +		if ((*nodep = call_prom("peer", 1, 1, node)) != 0) +			return 1; +	} +} + +#ifdef CONFIG_POWER4 +/* + * Set up a hash table with a set of entries in it to map the + * first 64MB of RAM.  This is used on 64-bit machines since + * some of them don't have BATs. + */ + +static inline void make_pte(unsigned long htab, unsigned int hsize, +			    unsigned int va, unsigned int pa, int mode) +{ +	unsigned int *pteg; +	unsigned int hash, i, vsid; + +	vsid = ((va >> 28) * 0x111) << 12; +	hash = ((va ^ vsid) >> 5) & 0x7fff80; +	pteg = (unsigned int *)(htab + (hash & (hsize - 1))); +	for (i = 0; i < 8; ++i, pteg += 4) { +		if ((pteg[1] & 1) == 0) { +			pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; +			pteg[3] = pa | mode; +			break; +		} +	} +} + +extern unsigned long _SDR1; +extern PTE *Hash; +extern unsigned long Hash_size; + +static void __init +prom_alloc_htab(void) +{ +	unsigned int hsize; +	unsigned long htab; +	unsigned int addr; + +	/* +	 * Because of OF bugs we can't use the "claim" client +	 * interface to allocate memory for the hash table. +	 * This code is only used on 64-bit PPCs, and the only +	 * 64-bit PPCs at the moment are RS/6000s, and their +	 * OF is based at 0xc00000 (the 12M point), so we just +	 * arbitrarily use the 0x800000 - 0xc00000 region for the +	 * hash table. +	 *  -- paulus. +	 */ +	hsize = 4 << 20;	/* POWER4 has no BATs */ +	htab = (8 << 20); +	call_prom("claim", 3, 1, htab, hsize, 0); +	Hash = (void *)(htab + KERNELBASE); +	Hash_size = hsize; +	_SDR1 = htab + __ilog2(hsize) - 18; + +	/* +	 * Put in PTEs for the first 64MB of RAM +	 */ +	memset((void *)htab, 0, hsize); +	for (addr = 0; addr < 0x4000000; addr += 0x1000) +		make_pte(htab, hsize, addr + KERNELBASE, addr, +			 _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); +#if 0 /* DEBUG stuff mapping the SCC */ +	make_pte(htab, hsize, 0x80013000, 0x80013000, +		 _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); +#endif +} +#endif /* CONFIG_POWER4 */ + + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later.  However, OF will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static unsigned long __init +check_display(unsigned long mem) +{ +	phandle node; +	ihandle ih; +	int i, j; +	char type[16], *path; +	static unsigned char default_colors[] = { +		0x00, 0x00, 0x00, +		0x00, 0x00, 0xaa, +		0x00, 0xaa, 0x00, +		0x00, 0xaa, 0xaa, +		0xaa, 0x00, 0x00, +		0xaa, 0x00, 0xaa, +		0xaa, 0xaa, 0x00, +		0xaa, 0xaa, 0xaa, +		0x55, 0x55, 0x55, +		0x55, 0x55, 0xff, +		0x55, 0xff, 0x55, +		0x55, 0xff, 0xff, +		0xff, 0x55, 0x55, +		0xff, 0x55, 0xff, +		0xff, 0xff, 0x55, +		0xff, 0xff, 0xff +	}; +	const unsigned char *clut; + +	prom_disp_node = 0; + +	for (node = 0; prom_next_node(&node); ) { +		type[0] = 0; +		call_prom("getprop", 4, 1, node, "device_type", +			  type, sizeof(type)); +		if (strcmp(type, "display") != 0) +			continue; +		/* It seems OF doesn't null-terminate the path :-( */ +		path = (char *) mem; +		memset(path, 0, 256); +		if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) +			continue; + +		/* +		 * If this display is the device that OF is using for stdout, +		 * move it to the front of the list. +		 */ +		mem += strlen(path) + 1; +		i = prom_num_displays++; +		if (of_stdout_device != 0 && i > 0 +		    && strcmp(of_stdout_device, path) == 0) { +			for (; i > 0; --i) { +				prom_display_paths[i] +					= prom_display_paths[i-1]; +				prom_display_nodes[i] +					= prom_display_nodes[i-1]; +			} +		} +		prom_display_paths[i] = path; +		prom_display_nodes[i] = node; +		if (i == 0) +			prom_disp_node = node; +		if (prom_num_displays >= FB_MAX) +			break; +	} + +	for (j=0; j<prom_num_displays; j++) { +		path = prom_display_paths[j]; +		node = prom_display_nodes[j]; +		prom_print("opening display "); +		prom_print(path); +		ih = call_prom("open", 1, 1, path); +		if (ih == 0 || ih == (ihandle) -1) { +			prom_print("... failed\n"); +			for (i=j+1; i<prom_num_displays; i++) { +				prom_display_paths[i-1] = prom_display_paths[i]; +				prom_display_nodes[i-1] = prom_display_nodes[i]; +			} +			if (--prom_num_displays > 0) { +				prom_disp_node = prom_display_nodes[j]; +				j--; +			} else +				prom_disp_node = 0; +			continue; +		} else { +			prom_print("... ok\n"); +			call_prom("setprop", 4, 1, node, "linux,opened", 0, 0); + +			/* +			 * Setup a usable color table when the appropriate +			 * method is available. +			 * Should update this to use set-colors. +			 */ +			clut = default_colors; +			for (i = 0; i < 32; i++, clut += 3) +				if (prom_set_color(ih, i, clut[0], clut[1], +						   clut[2]) != 0) +					break; + +#ifdef CONFIG_LOGO_LINUX_CLUT224 +			clut = PTRRELOC(logo_linux_clut224.clut); +			for (i = 0; i < logo_linux_clut224.clutsize; +			     i++, clut += 3) +				if (prom_set_color(ih, i + 32, clut[0], +						   clut[1], clut[2]) != 0) +					break; +#endif /* CONFIG_LOGO_LINUX_CLUT224 */ +		} +	} +	 +	if (prom_stdout) { +		phandle p; +		p = call_prom("instance-to-package", 1, 1, prom_stdout); +		if (p && p != -1) { +			type[0] = 0; +			call_prom("getprop", 4, 1, p, "device_type", +				  type, sizeof(type)); +			if (strcmp(type, "display") == 0) +				call_prom("setprop", 4, 1, p, "linux,boot-display", +					  0, 0); +		} +	} + +	return ALIGNUL(mem); +} + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +static void __init +setup_disp_fake_bi(ihandle dp) +{ +#ifdef CONFIG_BOOTX_TEXT +	int width = 640, height = 480, depth = 8, pitch; +	unsigned address; +	struct pci_reg_property addrs[8]; +	int i, naddrs; +	char name[32]; +	char *getprop = "getprop"; + +	prom_print("Initializing fake screen: "); + +	memset(name, 0, sizeof(name)); +	call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); +	name[sizeof(name)-1] = 0; +	prom_print(name); +	prom_print("\n"); +	call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); +	call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); +	call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); +	pitch = width * ((depth + 7) / 8); +	call_prom(getprop, 4, 1, dp, "linebytes", +		  &pitch, sizeof(pitch)); +	if (pitch == 1) +		pitch = 0x1000;		/* for strange IBM display */ +	address = 0; +	call_prom(getprop, 4, 1, dp, "address", +		  &address, sizeof(address)); +	if (address == 0) { +		/* look for an assigned address with a size of >= 1MB */ +		naddrs = call_prom(getprop, 4, 1, dp, "assigned-addresses", +				   addrs, sizeof(addrs)); +		naddrs /= sizeof(struct pci_reg_property); +		for (i = 0; i < naddrs; ++i) { +			if (addrs[i].size_lo >= (1 << 20)) { +				address = addrs[i].addr.a_lo; +				/* use the BE aperture if possible */ +				if (addrs[i].size_lo >= (16 << 20)) +					address += (8 << 20); +				break; +			} +		} +		if (address == 0) { +			prom_print("Failed to get address\n"); +			return; +		} +	} +	/* kludge for valkyrie */ +	if (strcmp(name, "valkyrie") == 0) +		address += 0x1000; + +#ifdef CONFIG_POWER4 +#if CONFIG_TASK_SIZE > 0x80000000 +#error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5 +#endif +	{ +		extern boot_infos_t disp_bi; +		unsigned long va, pa, i, offset; +       		va = 0x90000000; +		pa = address & 0xfffff000ul; +		offset = address & 0x00000fff; + +		for (i=0; i<0x4000; i++) {   +			make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa,  +				 _PAGE_ACCESSED | _PAGE_NO_CACHE | +				 _PAGE_GUARDED | PP_RWXX); +			va += 0x1000; +			pa += 0x1000; +		} +		btext_setup_display(width, height, depth, pitch, 0x90000000 | offset); +		disp_bi.dispDeviceBase = (u8 *)address; +	} +#else /* CONFIG_POWER4 */ +	btext_setup_display(width, height, depth, pitch, address); +	btext_prepare_BAT(); +#endif /* CONFIG_POWER4 */ +#endif /* CONFIG_BOOTX_TEXT */ +} + +/* + * Make a copy of the device tree from the PROM. + */ +static unsigned long __init +copy_device_tree(unsigned long mem_start, unsigned long mem_end) +{ +	phandle root; +	unsigned long new_start; +	struct device_node **allnextp; + +	root = call_prom("peer", 1, 1, (phandle)0); +	if (root == (phandle)0) { +		prom_print("couldn't get device tree root\n"); +		prom_exit(); +	} +	allnextp = &allnodes; +	mem_start = ALIGNUL(mem_start); +	new_start = inspect_node(root, NULL, mem_start, mem_end, &allnextp); +	*allnextp = NULL; +	return new_start; +} + +static unsigned long __init +inspect_node(phandle node, struct device_node *dad, +	     unsigned long mem_start, unsigned long mem_end, +	     struct device_node ***allnextpp) +{ +	int l; +	phandle child; +	struct device_node *np; +	struct property *pp, **prev_propp; +	char *prev_name, *namep; +	unsigned char *valp; + +	np = (struct device_node *) mem_start; +	mem_start += sizeof(struct device_node); +	memset(np, 0, sizeof(*np)); +	np->node = node; +	**allnextpp = PTRUNRELOC(np); +	*allnextpp = &np->allnext; +	if (dad != 0) { +		np->parent = PTRUNRELOC(dad); +		/* we temporarily use the `next' field as `last_child'. */ +		if (dad->next == 0) +			dad->child = PTRUNRELOC(np); +		else +			dad->next->sibling = PTRUNRELOC(np); +		dad->next = np; +	} + +	/* get and store all properties */ +	prev_propp = &np->properties; +	prev_name = ""; +	for (;;) { +		pp = (struct property *) mem_start; +		namep = (char *) (pp + 1); +		pp->name = PTRUNRELOC(namep); +		if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0) +			break; +		mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); +		prev_name = namep; +		valp = (unsigned char *) mem_start; +		pp->value = PTRUNRELOC(valp); +		pp->length = call_prom("getprop", 4, 1, node, namep, +				       valp, mem_end - mem_start); +		if (pp->length < 0) +			continue; +#ifdef MAX_PROPERTY_LENGTH +		if (pp->length > MAX_PROPERTY_LENGTH) +			continue; /* ignore this property */ +#endif +		mem_start = ALIGNUL(mem_start + pp->length); +		*prev_propp = PTRUNRELOC(pp); +		prev_propp = &pp->next; +	} +	if (np->node != 0) { +		/* Add a "linux,phandle" property" */ +		pp = (struct property *) mem_start; +		*prev_propp = PTRUNRELOC(pp); +		prev_propp = &pp->next; +		namep = (char *) (pp + 1); +		pp->name = PTRUNRELOC(namep); +		strcpy(namep, "linux,phandle"); +		mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); +		pp->value = (unsigned char *) PTRUNRELOC(&np->node); +		pp->length = sizeof(np->node); +	} +	*prev_propp = NULL; + +	/* get the node's full name */ +	l = call_prom("package-to-path", 3, 1, node, +		      mem_start, mem_end - mem_start); +	if (l >= 0) { +		np->full_name = PTRUNRELOC((char *) mem_start); +		*(char *)(mem_start + l) = 0; +		mem_start = ALIGNUL(mem_start + l + 1); +	} + +	/* do all our children */ +	child = call_prom("child", 1, 1, node); +	while (child != 0) { +		mem_start = inspect_node(child, np, mem_start, mem_end, +					 allnextpp); +		child = call_prom("peer", 1, 1, child); +	} + +	return mem_start; +} + +unsigned long smp_chrp_cpu_nr __initdata = 0; + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of high memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something.  We do that by using + * physical address 0x0.  The holding pattern checks that address + * until its cpu # is there, when it is that cpu jumps to + * __secondary_start().  smp_boot_cpus() takes care of setting those + * values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * -- Cort + * + * Note that we have to do this if we have more than one CPU, + * even if this is a UP kernel.  Otherwise when we trash OF + * the other CPUs will start executing some random instructions + * and crash the system.  -- paulus + */ +static void __init +prom_hold_cpus(unsigned long mem) +{ +	extern void __secondary_hold(void); +	unsigned long i; +	int cpu; +	phandle node; +	char type[16], *path; +	unsigned int reg; + +	/* +	 * XXX: hack to make sure we're chrp, assume that if we're +	 *      chrp we have a device_type property -- Cort +	 */ +	node = call_prom("finddevice", 1, 1, "/"); +	if (call_prom("getprop", 4, 1, node, +		      "device_type", type, sizeof(type)) <= 0) +		return; + +	/* copy the holding pattern code to someplace safe (0) */ +	/* the holding pattern is now within the first 0x100 +	   bytes of the kernel image -- paulus */ +	memcpy((void *)0, _stext, 0x100); +	flush_icache_range(0, 0x100); + +	/* look for cpus */ +	*(unsigned long *)(0x0) = 0; +	asm volatile("dcbf 0,%0": : "r" (0) : "memory"); +	for (node = 0; prom_next_node(&node); ) { +		type[0] = 0; +		call_prom("getprop", 4, 1, node, "device_type", +			  type, sizeof(type)); +		if (strcmp(type, "cpu") != 0) +			continue; +		path = (char *) mem; +		memset(path, 0, 256); +		if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) +			continue; +		reg = -1; +		call_prom("getprop", 4, 1, node, "reg", ®, sizeof(reg)); +		cpu = smp_chrp_cpu_nr++; +#ifdef CONFIG_SMP +		smp_hw_index[cpu] = reg; +#endif /* CONFIG_SMP */ +		/* XXX: hack - don't start cpu 0, this cpu -- Cort */ +		if (cpu == 0) +			continue; +		prom_print("starting cpu "); +		prom_print(path); +		*(ulong *)(0x4) = 0; +		call_prom("start-cpu", 3, 0, node, +			  (char *)__secondary_hold - _stext, cpu); +		prom_print("..."); +		for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) +			; +		if (*(ulong *)(0x4) == cpu) +			prom_print("ok\n"); +		else { +			prom_print("failed: "); +			prom_print_hex(*(ulong *)0x4); +			prom_print("\n"); +		} +	} +} + +static void __init +prom_instantiate_rtas(void) +{ +	ihandle prom_rtas; +	prom_arg_t result; + +	prom_rtas = call_prom("finddevice", 1, 1, "/rtas"); +	if (prom_rtas == -1) +		return; + +	rtas_size = 0; +	call_prom("getprop", 4, 1, prom_rtas, +		  "rtas-size", &rtas_size, sizeof(rtas_size)); +	prom_print("instantiating rtas"); +	if (rtas_size == 0) { +		rtas_data = 0; +	} else { +		/* +		 * Ask OF for some space for RTAS. +		 * Actually OF has bugs so we just arbitrarily +		 * use memory at the 6MB point. +		 */ +		rtas_data = 6 << 20; +		prom_print(" at "); +		prom_print_hex(rtas_data); +	} + +	prom_rtas = call_prom("open", 1, 1, "/rtas"); +	prom_print("..."); +	rtas_entry = 0; +	if (call_prom_ret("call-method", 3, 2, &result, +			  "instantiate-rtas", prom_rtas, rtas_data) == 0) +		rtas_entry = result; +	if ((rtas_entry == -1) || (rtas_entry == 0)) +		prom_print(" failed\n"); +	else +		prom_print(" done\n"); +} + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ +unsigned long __init +prom_init(int r3, int r4, prom_entry pp) +{ +	unsigned long mem; +	ihandle prom_mmu; +	unsigned long offset = reloc_offset(); +	int i, l; +	char *p, *d; + 	unsigned long phys; +	prom_arg_t result[3]; +	char model[32]; +	phandle node; +	int rc; + + 	/* Default */ + 	phys = (unsigned long) &_stext; + +	/* First get a handle for the stdout device */ +	prom = pp; +	prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); +	if (prom_chosen == -1) +		prom_exit(); +	if (call_prom("getprop", 4, 1, prom_chosen, "stdout", +		      &prom_stdout, sizeof(prom_stdout)) <= 0) +		prom_exit(); + +	/* Get the full OF pathname of the stdout device */ +	mem = (unsigned long) klimit + offset; +	p = (char *) mem; +	memset(p, 0, 256); +	call_prom("instance-to-path", 3, 1, prom_stdout, p, 255); +	of_stdout_device = p; +	mem += strlen(p) + 1; + +	/* Get the boot device and translate it to a full OF pathname. */ +	p = (char *) mem; +	l = call_prom("getprop", 4, 1, prom_chosen, "bootpath", p, 1<<20); +	if (l > 0) { +		p[l] = 0;	/* should already be null-terminated */ +		bootpath = PTRUNRELOC(p); +		mem += l + 1; +		d = (char *) mem; +		*d = 0; +		call_prom("canon", 3, 1, p, d, 1<<20); +		bootdevice = PTRUNRELOC(d); +		mem = ALIGNUL(mem + strlen(d) + 1); +	} + +	prom_instantiate_rtas(); + +#ifdef CONFIG_POWER4 +	/* +	 * Find out how much memory we have and allocate a +	 * suitably-sized hash table. +	 */ +	prom_alloc_htab(); +#endif +	mem = check_display(mem); + +	prom_print("copying OF device tree..."); +	mem = copy_device_tree(mem, mem + (1<<20)); +	prom_print("done\n"); + +	prom_hold_cpus(mem); + +	klimit = (char *) (mem - offset); + +	node = call_prom("finddevice", 1, 1, "/"); +	rc = call_prom("getprop", 4, 1, node, "model", model, sizeof(model)); +	if (rc > 0 && !strncmp (model, "Pegasos", 7) +		&& strncmp (model, "Pegasos2", 8)) { +		/* Pegasos 1 has a broken translate method in the OF, +		 * and furthermore the BATs are mapped 1:1 so the phys +		 * address calculated above is correct, so let's use +		 * it directly. +		 */ +	} else if (offset == 0) { +		/* If we are already running at 0xc0000000, we assume we were +	 	 * loaded by an OF bootloader which did set a BAT for us. +	 	 * This breaks OF translate so we force phys to be 0. +	 	 */ +		prom_print("(already at 0xc0000000) phys=0\n"); +		phys = 0; +	} else if (call_prom("getprop", 4, 1, prom_chosen, "mmu", +			     &prom_mmu, sizeof(prom_mmu)) <= 0) { +		prom_print(" no MMU found\n"); +	} else if (call_prom_ret("call-method", 4, 4, result, "translate", +				 prom_mmu, &_stext, 1) != 0) { +		prom_print(" (translate failed)\n"); +	} else { +		/* We assume the phys. address size is 3 cells */ +		phys = result[2]; +	} + +	if (prom_disp_node != 0) +		setup_disp_fake_bi(prom_disp_node); + +	/* Use quiesce call to get OF to shut down any devices it's using */ +	prom_print("Calling quiesce ...\n"); +	call_prom("quiesce", 0, 0); + +	/* Relocate various pointers which will be used once the +	   kernel is running at the address it was linked at. */ +	for (i = 0; i < prom_num_displays; ++i) +		prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]); + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +	/* Relocate the of stdout for console autodetection */ +	of_stdout_device = PTRUNRELOC(of_stdout_device); +#endif + +	prom_print("returning 0x"); +	prom_print_hex(phys); +	prom_print("from prom_init\n"); +	prom_stdout = 0; + +	return phys; +} + +/* + * early_get_property is used to access the device tree image prepared + * by BootX very early on, before the pointers in it have been relocated. + */ +static void * __init +early_get_property(unsigned long base, unsigned long node, char *prop) +{ +	struct device_node *np = (struct device_node *)(base + node); +	struct property *pp; + +	for (pp = np->properties; pp != 0; pp = pp->next) { +		pp = (struct property *) (base + (unsigned long)pp); +		if (strcmp((char *)((unsigned long)pp->name + base), +			   prop) == 0) { +			return (void *)((unsigned long)pp->value + base); +		} +	} +	return NULL; +} + +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi)		((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi)	((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi)	((bi)->version >= 4) + +void __init +bootx_init(unsigned long r4, unsigned long phys) +{ +	boot_infos_t *bi = (boot_infos_t *) r4; +	unsigned long space; +	unsigned long ptr, x; +	char *model; + +	boot_infos = PTRUNRELOC(bi); +	if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) +		bi->logicalDisplayBase = NULL; + +#ifdef CONFIG_BOOTX_TEXT +	btext_init(bi); + +	/* +	 * Test if boot-info is compatible.  Done only in config +	 * CONFIG_BOOTX_TEXT since there is nothing much we can do +	 * with an incompatible version, except display a message +	 * and eventually hang the processor... +	 * +	 * I'll try to keep enough of boot-info compatible in the +	 * future to always allow display of this message; +	 */ +	if (!BOOT_INFO_IS_COMPATIBLE(bi)) { +		btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"); +		btext_flushscreen(); +	} +#endif	/* CONFIG_BOOTX_TEXT */ + +	/* New BootX enters kernel with MMU off, i/os are not allowed +	   here. This hack will have been done by the boostrap anyway. +	*/ +	if (bi->version < 4) { +		/* +		 * XXX If this is an iMac, turn off the USB controller. +		 */ +		model = (char *) early_get_property +			(r4 + bi->deviceTreeOffset, 4, "model"); +		if (model +		    && (strcmp(model, "iMac,1") == 0 +			|| strcmp(model, "PowerMac1,1") == 0)) { +			out_le32((unsigned *)0x80880008, 1);	/* XXX */ +		} +	} + +	/* Move klimit to enclose device tree, args, ramdisk, etc... */ +	if (bi->version < 5) { +		space = bi->deviceTreeOffset + bi->deviceTreeSize; +		if (bi->ramDisk) +			space = bi->ramDisk + bi->ramDiskSize; +	} else +		space = bi->totalParamsSize; +	klimit = PTRUNRELOC((char *) bi + space); + +	/* New BootX will have flushed all TLBs and enters kernel with +	   MMU switched OFF, so this should not be useful anymore. +	*/ +	if (bi->version < 4) { +		/* +		 * Touch each page to make sure the PTEs for them +		 * are in the hash table - the aim is to try to avoid +		 * getting DSI exceptions while copying the kernel image. +		 */ +		for (ptr = ((unsigned long) &_stext) & PAGE_MASK; +		     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) +			x = *(volatile unsigned long *)ptr; +	} + +#ifdef CONFIG_BOOTX_TEXT +	/* +	 * Note that after we call btext_prepare_BAT, we can't do +	 * prom_draw*, flushscreen or clearscreen until we turn the MMU +	 * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase +	 * to a virtual address. +	 */ +	btext_prepare_BAT(); +#endif +} diff --git a/arch/ppc/syslib/qspan_pci.c b/arch/ppc/syslib/qspan_pci.c new file mode 100644 index 00000000000..57f4ed5e5ae --- /dev/null +++ b/arch/ppc/syslib/qspan_pci.c @@ -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; +} + diff --git a/arch/ppc/syslib/todc_time.c b/arch/ppc/syslib/todc_time.c new file mode 100644 index 00000000000..1323c641c19 --- /dev/null +++ b/arch/ppc/syslib/todc_time.c @@ -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; +} diff --git a/arch/ppc/syslib/xilinx_pic.c b/arch/ppc/syslib/xilinx_pic.c new file mode 100644 index 00000000000..e0bd66f0847 --- /dev/null +++ b/arch/ppc/syslib/xilinx_pic.c @@ -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; +	} +}  |