diff options
| author | Daniel Hellstrom <daniel@gaisler.com> | 2008-03-26 22:51:29 +0100 | 
|---|---|---|
| committer | Daniel Hellstrom <daniel@gaisler.com> | 2008-04-08 07:58:32 +0000 | 
| commit | 1e9a164e22976933002c5e4b0b79b09fcede9cd4 (patch) | |
| tree | 2fb31cf40420f1eb37bcaf664f33d0b5d0070ade | |
| parent | bf3d8b31169546fcddb4737391e1893fb12d033a (diff) | |
| download | olio-uboot-2014.01-1e9a164e22976933002c5e4b0b79b09fcede9cd4.tar.xz olio-uboot-2014.01-1e9a164e22976933002c5e4b0b79b09fcede9cd4.zip | |
SPARC: Added support for SPARC LEON3 SOC processor.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | README | 1 | ||||
| -rw-r--r-- | cpu/leon3/Makefile | 54 | ||||
| -rw-r--r-- | cpu/leon3/ambapp.c | 339 | ||||
| -rw-r--r-- | cpu/leon3/config.mk | 26 | ||||
| -rw-r--r-- | cpu/leon3/cpu.c | 67 | ||||
| -rw-r--r-- | cpu/leon3/cpu_init.c | 254 | ||||
| -rw-r--r-- | cpu/leon3/interrupts.c | 219 | ||||
| -rw-r--r-- | cpu/leon3/prom.c | 1078 | ||||
| -rw-r--r-- | cpu/leon3/serial.c | 139 | ||||
| -rw-r--r-- | cpu/leon3/start.S | 616 | ||||
| -rw-r--r-- | cpu/leon3/usb_uhci.c | 1313 | ||||
| -rw-r--r-- | cpu/leon3/usb_uhci.h | 184 | ||||
| -rw-r--r-- | include/ambapp.h | 380 | ||||
| -rw-r--r-- | include/asm-sparc/arch-leon3/asi.h | 36 | ||||
| -rw-r--r-- | include/asm-sparc/leon.h | 38 | ||||
| -rw-r--r-- | include/asm-sparc/leon3.h | 37 | ||||
| -rw-r--r-- | include/asm-sparc/processor.h | 9 | 
18 files changed, 4792 insertions, 1 deletions
| @@ -2898,6 +2898,9 @@ r2dplus_config  :   unconfig  #========================================================================  # SPARC  #======================================================================== +######################################################################### +## LEON3 +#########################################################################  #########################################################################  ######################################################################### @@ -153,6 +153,7 @@ Directory Hierarchy:    - at32ap	Files specific to Atmel AVR32 AP CPUs    - i386	Files specific to i386 CPUs    - ixp		Files specific to Intel XScale IXP CPUs +  - leon3	Files specific to Gaisler LEON3 SPARC CPU    - mcf52x2	Files specific to Freescale ColdFire MCF52x2 CPUs    - mcf5227x	Files specific to Freescale ColdFire MCF5227x CPUs    - mcf532x	Files specific to Freescale ColdFire MCF5329 CPUs diff --git a/cpu/leon3/Makefile b/cpu/leon3/Makefile new file mode 100644 index 000000000..182543dd1 --- /dev/null +++ b/cpu/leon3/Makefile @@ -0,0 +1,54 @@ +# +# (C) Copyright 2003-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# 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 $(TOPDIR)/config.mk + +LIB	= $(obj)lib$(CPU).a + +START	= start.o +SOBJS	= +COBJS	= cpu_init.o serial.o cpu.o ambapp.o interrupts.o prom.o usb_uhci.o + +SRCS	:= $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS)) +START	:= $(addprefix $(obj),$(START)) + +all:	$(obj).depend $(START) $(LIB) + +$(LIB):	$(OBJS) +	$(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +$(START): $(START:.o=.S) +	$(CC) -D__ASSEMBLY__ $(DBGFLAGS) $(OPTFLAGS) -D__KERNEL__ -DTEXT_BASE=$(TEXT_BASE) \ +	-I$(TOPDIR)/include -fno-builtin -ffreestanding -nostdinc -isystem $(gccincdir) -pipe \ +	$(PLATFORM_CPPFLAGS) -Wall -Wstrict-prototypes \ +	-I$(TOPDIR)/board -c -o $(START) $(START:.o=.S) + +sinclude $(obj).depend + +######################################################################### diff --git a/cpu/leon3/ambapp.c b/cpu/leon3/ambapp.c new file mode 100644 index 000000000..60ff1a2ae --- /dev/null +++ b/cpu/leon3/ambapp.c @@ -0,0 +1,339 @@ +/* Gaisler AMBA Plug&Play bus scanning. Functions + * ending on _nomem is inteded to be used only during + * initialization, only registers are used (no ram). + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * 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 <common.h> +#include <command.h> +#include <ambapp.h> + +static int ambapp_apb_scan(unsigned int vendor,	/* Plug&Play Vendor ID */ +			   unsigned int driver,	/* Plug&Play Device ID */ +			   ambapp_apbdev * dev,	/* Result(s) is placed here */ +			   int index,	/* Index of device to start copying Plug&Play +					 * info into dev +					 */ +			   int max_cnt	/* Maximal count that dev can hold, if dev +					 * is NULL function will stop scanning after +					 * max_cnt devices are found. +					 */ +    ) +{ +	int i, cnt = 0; +	unsigned int apbmst_base; +	ambapp_ahbdev apbmst; +	apbctrl_pp_dev *apb; + +	if (max_cnt == 0) +		return 0; + +	/* Get AMBA APB Master */ +	if (ambapp_ahbslv_first(VENDOR_GAISLER, GAISLER_APBMST, &apbmst) != 1) { +		return 0; +	} + +	/* Get APB CTRL Plug&Play info area */ +	apbmst_base = apbmst.address[0] & LEON3_IO_AREA; +	apb = (apbctrl_pp_dev *) (apbmst_base | LEON3_CONF_AREA); + +	for (i = 0; i < LEON3_APB_SLAVES; i++) { +		if ((amba_vendor(apb->conf) == vendor) && +		    (amba_device(apb->conf) == driver) && ((index < 0) +							   || (index-- == 0))) { +			/* Convert Plug&Play info into a more readable format */ +			cnt++; +			if (dev) { +				dev->irq = amba_irq(apb->conf); +				dev->ver = amba_ver(apb->conf); +				dev->address = +				    (apbmst_base | +				     (((apb-> +					bar & 0xfff00000) >> 12))) & (((apb-> +									bar & +									0x0000fff0) +								       << 4) | +								      0xfff00000); +				dev++; +			} +			/* found max devices? */ +			if (cnt >= max_cnt) +				return cnt; +		} +		/* Get next Plug&Play entry */ +		apb++; +	} +	return cnt; +} + +unsigned int ambapp_apb_next_nomem(register unsigned int vendor,	/* Plug&Play Vendor ID */ +				   register unsigned int driver,	/* Plug&Play Device ID */ +				   register int index) +{ +	register int i; +	register ahbctrl_pp_dev *apbmst; +	register apbctrl_pp_dev *apb; +	register unsigned int apbmst_base; + +	/* APBMST is a AHB Slave */ +	apbmst = ambapp_ahb_next_nomem(VENDOR_GAISLER, GAISLER_APBMST, 1, 0); +	if (!apbmst) +		return 0; + +	apbmst_base = amba_membar_start(apbmst->bars[0]); +	if (amba_membar_type(apbmst->bars[0]) == AMBA_TYPE_AHBIO) +		apbmst_base = AMBA_TYPE_AHBIO_ADDR(apbmst_base); +	apbmst_base &= LEON3_IO_AREA; + +	/* Find the vendor/driver device on the first APB bus */ +	apb = (apbctrl_pp_dev *) (apbmst_base | LEON3_CONF_AREA); + +	for (i = 0; i < LEON3_APB_SLAVES; i++) { +		if ((amba_vendor(apb->conf) == vendor) && +		    (amba_device(apb->conf) == driver) && ((index < 0) +							   || (index-- == 0))) { +			/* Convert Plug&Play info info a more readable format */ +			return (apbmst_base | (((apb->bar & 0xfff00000) >> 12))) +			    & (((apb->bar & 0x0000fff0) << 4) | 0xfff00000); +		} +		/* Get next Plug&Play entry */ +		apb++; +	} +	return 0; +} + +/****************************** APB SLAVES ******************************/ + +int ambapp_apb_count(unsigned int vendor, unsigned int driver) +{ +	return ambapp_apb_scan(vendor, driver, NULL, 0, LEON3_APB_SLAVES); +} + +int ambapp_apb_first(unsigned int vendor, +		     unsigned int driver, ambapp_apbdev * dev) +{ +	return ambapp_apb_scan(vendor, driver, dev, 0, 1); +} + +int ambapp_apb_next(unsigned int vendor, +		    unsigned int driver, ambapp_apbdev * dev, int index) +{ +	return ambapp_apb_scan(vendor, driver, dev, index, 1); +} + +int ambapp_apbs_first(unsigned int vendor, +		      unsigned int driver, ambapp_apbdev * dev, int max_cnt) +{ +	return ambapp_apb_scan(vendor, driver, dev, 0, max_cnt); +} + +enum { +	AHB_SCAN_MASTER = 0, +	AHB_SCAN_SLAVE = 1 +}; + +/* Scan AMBA Plug&Play bus for AMBA AHB Masters or AHB Slaves + * for a certain matching Vendor and Device ID. + * + * Return number of devices found. + * + * Compact edition... + */ +static int ambapp_ahb_scan(unsigned int vendor,	/* Plug&Play Vendor ID */ +			   unsigned int driver,	/* Plug&Play Device ID */ +			   ambapp_ahbdev * dev,	/* Result(s) is placed here */ +			   int index,	/* Index of device to start copying Plug&Play +					 * info into dev +					 */ +			   int max_cnt,	/* Maximal count that dev can hold, if dev +					 * is NULL function will stop scanning after +					 * max_cnt devices are found. +					 */ +			   int type	/* Selectes what type of devices to scan. +					 * 0=AHB Masters +					 * 1=AHB Slaves +					 */ +    ) +{ +	int i, j, cnt = 0, max_pp_devs; +	unsigned int addr; +	ahbctrl_info *info = (ahbctrl_info *) (LEON3_IO_AREA | LEON3_CONF_AREA); +	ahbctrl_pp_dev *ahb; + +	if (max_cnt == 0) +		return 0; + +	if (type == 0) { +		max_pp_devs = LEON3_AHB_MASTERS; +		ahb = info->masters; +	} else { +		max_pp_devs = LEON3_AHB_SLAVES; +		ahb = info->slaves; +	} + +	for (i = 0; i < max_pp_devs; i++) { +		if ((amba_vendor(ahb->conf) == vendor) && +		    (amba_device(ahb->conf) == driver) && +		    ((index < 0) || (index-- == 0))) { +			/* Convert Plug&Play info info a more readable format */ +			cnt++; +			if (dev) { +				dev->irq = amba_irq(ahb->conf); +				dev->ver = amba_ver(ahb->conf); +				dev->userdef[0] = ahb->userdef[0]; +				dev->userdef[1] = ahb->userdef[1]; +				dev->userdef[2] = ahb->userdef[2]; +				for (j = 0; j < 4; j++) { +					addr = amba_membar_start(ahb->bars[j]); +					if (amba_membar_type(ahb->bars[j]) == +					    AMBA_TYPE_AHBIO) +						addr = +						    AMBA_TYPE_AHBIO_ADDR(addr); +					dev->address[j] = addr; +				} +				dev++; +			} +			/* found max devices? */ +			if (cnt >= max_cnt) +				return cnt; +		} +		/* Get next Plug&Play entry */ +		ahb++; +	} +	return cnt; +} + +unsigned int ambapp_ahb_get_info(ahbctrl_pp_dev * ahb, int info) +{ +	register unsigned int ret; + +	if (!ahb) +		return 0; + +	switch (info) { +	default: +		info = 0; +	case 0: +	case 1: +	case 2: +	case 3: +		/* Get Address from PnP Info */ +		ret = amba_membar_start(ahb->bars[info]); +		if (amba_membar_type(ahb->bars[info]) == AMBA_TYPE_AHBIO) +			ret = AMBA_TYPE_AHBIO_ADDR(ret); +		return ret; +	} +	return 0; + +} + +ahbctrl_pp_dev *ambapp_ahb_next_nomem(register unsigned int vendor,	/* Plug&Play Vendor ID */ +				      register unsigned int driver,	/* Plug&Play Device ID */ +				      register unsigned int opts,	/* 1=slave, 0=master */ +				      register int index) +{ +	register ahbctrl_pp_dev *ahb; +	register ahbctrl_info *info = +	    (ahbctrl_info *) (LEON3_IO_AREA | LEON3_CONF_AREA); +	register int i; +	register int max_pp_devs; + +	if (opts == 0) { +		max_pp_devs = LEON3_AHB_MASTERS; +		ahb = info->masters; +	} else { +		max_pp_devs = LEON3_AHB_SLAVES; +		ahb = info->slaves; +	} + +	for (i = 0; i < max_pp_devs; i++) { +		if ((amba_vendor(ahb->conf) == vendor) && +		    (amba_device(ahb->conf) == driver) && +		    ((index < 0) || (index-- == 0))) { +			/* Convert Plug&Play info info a more readable format */ +			return ahb; +		} +		/* Get next Plug&Play entry */ +		ahb++; +	} +	return 0; +} + +/****************************** AHB MASTERS ******************************/ +int ambapp_ahbmst_count(unsigned int vendor, unsigned int driver) +{ +	/* Get number of devices of this vendor&device ID */ +	return ambapp_ahb_scan(vendor, driver, NULL, 0, LEON3_AHB_MASTERS, +			       AHB_SCAN_MASTER); +} + +int ambapp_ahbmst_first(unsigned int vendor, unsigned int driver, +			ambapp_ahbdev * dev) +{ +	/* find first device of this */ +	return ambapp_ahb_scan(vendor, driver, dev, 0, 1, AHB_SCAN_MASTER); +} + +int ambapp_ahbmst_next(unsigned int vendor, +		       unsigned int driver, ambapp_ahbdev * dev, int index) +{ +	/* find first device of this */ +	return ambapp_ahb_scan(vendor, driver, dev, index, 1, AHB_SCAN_MASTER); +} + +int ambapp_ahbmsts_first(unsigned int vendor, +			 unsigned int driver, ambapp_ahbdev * dev, int max_cnt) +{ +	/* find first device of this */ +	return ambapp_ahb_scan(vendor, driver, dev, 0, max_cnt, +			       AHB_SCAN_MASTER); +} + +/****************************** AHB SLAVES ******************************/ +int ambapp_ahbslv_count(unsigned int vendor, unsigned int driver) +{ +	/* Get number of devices of this vendor&device ID */ +	return ambapp_ahb_scan(vendor, driver, NULL, 0, LEON3_AHB_SLAVES, +			       AHB_SCAN_SLAVE); +} + +int ambapp_ahbslv_first(unsigned int vendor, unsigned int driver, +			ambapp_ahbdev * dev) +{ +	/* find first device of this */ +	return ambapp_ahb_scan(vendor, driver, dev, 0, 1, AHB_SCAN_SLAVE); +} + +int ambapp_ahbslv_next(unsigned int vendor, +		       unsigned int driver, ambapp_ahbdev * dev, int index) +{ +	/* find first device of this */ +	return ambapp_ahb_scan(vendor, driver, dev, index, 1, AHB_SCAN_SLAVE); +} + +int ambapp_ahbslvs_first(unsigned int vendor, +			 unsigned int driver, ambapp_ahbdev * dev, int max_cnt) +{ +	/* find first device of this */ +	return ambapp_ahb_scan(vendor, driver, dev, 0, max_cnt, AHB_SCAN_SLAVE); +} diff --git a/cpu/leon3/config.mk b/cpu/leon3/config.mk new file mode 100644 index 000000000..30b224a06 --- /dev/null +++ b/cpu/leon3/config.mk @@ -0,0 +1,26 @@ +# +# (C) Copyright 2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# 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 +# + +PLATFORM_RELFLAGS += -fPIC + +PLATFORM_CPPFLAGS += -DCONFIG_LEON diff --git a/cpu/leon3/cpu.c b/cpu/leon3/cpu.c new file mode 100644 index 000000000..306a21004 --- /dev/null +++ b/cpu/leon3/cpu.c @@ -0,0 +1,67 @@ +/* CPU specific code for the LEON3 CPU + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * 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 <common.h> +#include <watchdog.h> +#include <command.h> + +#include <asm/io.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern void _reset_reloc(void); + +int checkcpu(void) +{ +	/* check LEON version here */ +	printf("CPU: LEON3\n"); +	return 0; +} + +/* ------------------------------------------------------------------------- */ + +void cpu_reset(void) +{ +	/* Interrupts off */ +	disable_interrupts(); + +	/* jump to restart in flash */ +	_reset_reloc(); +} + +int do_reset(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	cpu_reset(); + +	return 1; + +} + +u64 flash_read64(void *addr) +{ +	return __raw_readq(addr); +} + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/leon3/cpu_init.c b/cpu/leon3/cpu_init.c new file mode 100644 index 000000000..4fe7d4b8d --- /dev/null +++ b/cpu/leon3/cpu_init.c @@ -0,0 +1,254 @@ +/* Initializes CPU and basic hardware such as memory + * controllers, IRQ controller and system timer 0. + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * 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 <common.h> +#include <asm/asi.h> +#include <asm/leon.h> +#include <ambapp.h> + +#include <config.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* reset CPU (jump to 0, without reset) */ +void start(void); + +/* find & initialize the memory controller */ +int init_memory_ctrl(void); + +ambapp_dev_irqmp *irqmp = NULL; +ambapp_dev_mctrl memctrl; +ambapp_dev_gptimer *gptimer = NULL; +unsigned int gptimer_irq = 0; +int leon3_snooping_avail = 0; + +struct { +	gd_t gd_area; +	bd_t bd; +} global_data; + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers. + * + * Run from FLASH/PROM: + *  - until memory controller is set up, only registers avaiable + *  - no global variables available for writing + *  - constants avaiable + */ + +void cpu_init_f(void) +{ +	/* these varaiable must not be initialized */ +	ambapp_dev_irqmp *irqmp; +	ambapp_apbdev apbdev; +	register unsigned int apbmst; + +	/* find AMBA APB Master */ +	apbmst = (unsigned int) +	    ambapp_ahb_next_nomem(VENDOR_GAISLER, GAISLER_APBMST, 1, 0); +	if (!apbmst) { +		/* +		 * no AHB/APB bridge, something is wrong +		 * ==> jump to start (or hang) +		 */ +		while (1) ; +	} +	/* Init memory controller */ +	if (init_memory_ctrl()) { +		while (1) ; +	} + +	/**************************************************** +	 * From here we can use the main memory and the stack. +	 */ + +	/* Find AMBA APB IRQMP Controller */ +	if (ambapp_apb_first(VENDOR_GAISLER, GAISLER_IRQMP, &apbdev) != 1) { +		/* no IRQ controller, something is wrong +		 * ==> jump to start (or hang) +		 */ +		while (1) ; +	} +	irqmp = (ambapp_dev_irqmp *) apbdev.address; + +	/* initialize the IRQMP */ +	irqmp->ilevel = 0xf;	/* all IRQ off */ +	irqmp->iforce = 0; +	irqmp->ipend = 0; +	irqmp->iclear = 0xfffe;	/* clear all old pending interrupts */ +	irqmp->cpu_mask[0] = 0;	/* mask all IRQs on CPU 0 */ +	irqmp->cpu_force[0] = 0;	/* no force IRQ on CPU 0 */ + +	/* cache */ +} + +void cpu_init_f2(void) +{ + +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r(void) +{ +	ambapp_apbdev apbdev; + +	/* +	 * Find AMBA APB IRQMP Controller, +	 * When we come so far we know there is a IRQMP available +	 */ +	ambapp_apb_first(VENDOR_GAISLER, GAISLER_IRQMP, &apbdev); +	irqmp = (ambapp_dev_irqmp *) apbdev.address; + +	/* timer */ +	if (ambapp_apb_first(VENDOR_GAISLER, GAISLER_GPTIMER, &apbdev) != 1) { +		printf("cpu_init_r: gptimer not found!\n"); +		return 1; +	} +	gptimer = (ambapp_dev_gptimer *) apbdev.address; +	gptimer_irq = apbdev.irq; + +	/* initialize prescaler common to all timers to 1MHz */ +	gptimer->scalar = gptimer->scalar_reload = +	    (((CONFIG_SYS_CLK_FREQ / 1000) + 500) / 1000) - 1; + +	return (0); +} + +/* find & setup memory controller */ +int init_memory_ctrl() +{ +	register ambapp_dev_mctrl *mctrl; +	register ambapp_dev_sdctrl *sdctrl; +	register ambapp_dev_ddrspa *ddrspa; +	register ambapp_dev_ddr2spa *ddr2spa; +	register ahbctrl_pp_dev *ahb; +	register unsigned int base; +	register int not_found_mctrl = -1; + +	/* find ESA Memory controller */ +	base = ambapp_apb_next_nomem(VENDOR_ESA, ESA_MCTRL, 0); +	if (base) { +		mctrl = (ambapp_dev_mctrl *) base; + +		/* config MCTRL memory controller */ +		mctrl->mcfg1 = CFG_GRLIB_MEMCFG1 | (mctrl->mcfg1 & 0x300); +		mctrl->mcfg2 = CFG_GRLIB_MEMCFG2; +		mctrl->mcfg3 = CFG_GRLIB_MEMCFG3; +		not_found_mctrl = 0; +	} + +	/* find Gaisler Fault Tolerant Memory controller */ +	base = ambapp_apb_next_nomem(VENDOR_GAISLER, GAISLER_FTMCTRL, 0); +	if (base) { +		mctrl = (ambapp_dev_mctrl *) base; + +		/* config MCTRL memory controller */ +		mctrl->mcfg1 = CFG_GRLIB_FT_MEMCFG1 | (mctrl->mcfg1 & 0x300); +		mctrl->mcfg2 = CFG_GRLIB_FT_MEMCFG2; +		mctrl->mcfg3 = CFG_GRLIB_FT_MEMCFG3; +		not_found_mctrl = 0; +	} + +	/* find SDRAM controller */ +	base = ambapp_apb_next_nomem(VENDOR_GAISLER, GAISLER_SDCTRL, 0); +	if (base) { +		sdctrl = (ambapp_dev_sdctrl *) base; + +		/* config memory controller */ +		sdctrl->sdcfg = CFG_GRLIB_SDRAM; +		not_found_mctrl = 0; +	} + +	ahb = ambapp_ahb_next_nomem(VENDOR_GAISLER, GAISLER_DDR2SPA, 1, 0); +	if (ahb) { +		ddr2spa = (ambapp_dev_ddr2spa *) ambapp_ahb_get_info(ahb, 1); + +		/* Config DDR2 memory controller */ +		ddr2spa->cfg1 = CFG_GRLIB_DDR2_CFG1; +		ddr2spa->cfg3 = CFG_GRLIB_DDR2_CFG3; +		not_found_mctrl = 0; +	} + +	ahb = ambapp_ahb_next_nomem(VENDOR_GAISLER, GAISLER_DDRSPA, 1, 0); +	if (ahb) { +		ddrspa = (ambapp_dev_ddrspa *) ambapp_ahb_get_info(ahb, 1); + +		/* Config DDR memory controller */ +		ddrspa->ctrl = CFG_GRLIB_DDR_CFG; +		not_found_mctrl = 0; +	} + +	/* failed to find any memory controller */ +	return not_found_mctrl; +} + +/* Uses Timer 0 to get accurate + * pauses. Max 2 raised to 32 ticks + * + */ +void cpu_wait_ticks(unsigned long ticks) +{ +	unsigned long start = get_timer(0); +	while (get_timer(start) < ticks) ; +} + +/* initiate and setup timer0 interrupt to 1MHz + * Return irq number for timer int or a negative number for + * dealing with self + */ +int timer_interrupt_init_cpu(void) +{ +	/* 1ms ticks */ +	gptimer->e[0].val = 0; +	gptimer->e[0].rld = 999;	/* (((1000000 / 100) - 1)) */ +	gptimer->e[0].ctrl = +	    (LEON3_GPTIMER_EN | +	     LEON3_GPTIMER_RL | LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN); + +	return gptimer_irq; +} + +/* + * This function is intended for SHORT delays only. + */ +unsigned long cpu_usec2ticks(unsigned long usec) +{ +	/* timer set to 1kHz ==> 1 clk tick = 1 msec */ +	if (usec < 1000) +		return 1; +	return (usec / 1000); +} + +unsigned long cpu_ticks2usec(unsigned long ticks) +{ +	/* 1tick = 1usec */ +	return ticks * 1000; +} diff --git a/cpu/leon3/interrupts.c b/cpu/leon3/interrupts.c new file mode 100644 index 000000000..26926321a --- /dev/null +++ b/cpu/leon3/interrupts.c @@ -0,0 +1,219 @@ +/* + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * (C) Copyright 2006 + * Detlev Zundel, DENX Software Engineering, dzu@denx.de + * + * (C) Copyright -2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, 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 <asm/stack.h> +#include <common.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <command.h> +#include <asm/irq.h> + +#include <asm/leon.h> +#include <ambapp.h> + +/* 15 normal irqs and a non maskable interrupt */ +#define NR_IRQS 15 + +struct irq_action { +	interrupt_handler_t *handler; +	void *arg; +	unsigned int count; +}; + +extern ambapp_dev_irqmp *irqmp; +extern ambapp_dev_gptimer *gptimer; + +static struct irq_action irq_handlers[NR_IRQS] = { {0}, }; +static int spurious_irq_cnt = 0; +static int spurious_irq = 0; + +static inline unsigned int irqmp_get_irqmask(unsigned int irq) +{ +	if ((irq < 0) || (irq >= NR_IRQS)) { +		return 0; +	} else { +		return (1 << irq); +	} + +} + +static void leon3_ic_disable(unsigned int irq) +{ +	unsigned int mask, pil; +	if (!irqmp) +		return; + +	pil = intLock(); + +	/* get mask of interrupt */ +	mask = irqmp_get_irqmask(irq); + +	/* set int level */ +	irqmp->cpu_mask[0] = SPARC_NOCACHE_READ(&irqmp->cpu_mask[0]) & (~mask); + +	intUnlock(pil); +} + +static void leon3_ic_enable(unsigned int irq) +{ +	unsigned int mask, pil; +	if (!irqmp) +		return; + +	pil = intLock(); + +	/* get mask of interrupt */ +	mask = irqmp_get_irqmask(irq); + +	/* set int level */ +	irqmp->cpu_mask[0] = SPARC_NOCACHE_READ(&irqmp->cpu_mask[0]) | mask; + +	intUnlock(pil); + +} + +void handler_irq(int irq, struct pt_regs *regs) +{ +	if (irq_handlers[irq].handler) { +		if (((unsigned int)irq_handlers[irq].handler > CFG_RAM_END) || +		    ((unsigned int)irq_handlers[irq].handler < CFG_RAM_BASE) +		    ) { +			printf("handler_irq: bad handler: %x, irq number %d\n", +			       (unsigned int)irq_handlers[irq].handler, irq); +			return; +		} +		irq_handlers[irq].handler(irq_handlers[irq].arg); +		irq_handlers[irq].count++; +	} else { +		spurious_irq_cnt++; +		spurious_irq = irq; +	} +} + +void leon3_force_int(int irq) +{ +	if (!irqmp || (irq >= NR_IRQS) || (irq < 0)) +		return; +	printf("Forcing interrupt %d\n", irq); + +	irqmp->iforce = SPARC_NOCACHE_READ(&irqmp->iforce) | (1 << irq); +} + +/****************************************************************************/ + +int interrupt_init_cpu(void) +{ + +	return (0); +} + +/****************************************************************************/ + +/* Handle Timer 0 IRQ */ +void timer_interrupt_cpu(void *arg) +{ +	gptimer->e[0].ctrl = (LEON3_GPTIMER_EN | +			      LEON3_GPTIMER_RL | +			      LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN); +	/* nothing to do here */ +	return; +} + +/****************************************************************************/ + +/* + * Install and free a interrupt handler. + */ + +void irq_install_handler(int irq, interrupt_handler_t * handler, void *arg) +{ +	if (irq < 0 || irq >= NR_IRQS) { +		printf("irq_install_handler: bad irq number %d\n", irq); +		return; +	} + +	if (irq_handlers[irq].handler != NULL) +		printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n", +		       (ulong) handler, (ulong) irq_handlers[irq].handler); + +	if (((unsigned int)handler > CFG_RAM_END) || +	    ((unsigned int)handler < CFG_RAM_BASE) +	    ) { +		printf("irq_install_handler: bad handler: %x, irq number %d\n", +		       (unsigned int)handler, irq); +		return; +	} +	irq_handlers[irq].handler = handler; +	irq_handlers[irq].arg = arg; + +	/* enable irq on IRQMP hardware */ +	leon3_ic_enable(irq); + +} + +void irq_free_handler(int irq) +{ +	if (irq < 0 || irq >= NR_IRQS) { +		printf("irq_free_handler: bad irq number %d\n", irq); +		return; +	} + +	/* disable irq on IRQMP hardware */ +	leon3_ic_disable(irq); + +	irq_handlers[irq].handler = NULL; +	irq_handlers[irq].arg = NULL; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_IRQ) +void do_irqinfo(cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ +	int irq; +	unsigned int pil = get_pil(); +	printf("PIL level: %u\n\r", pil); +	printf("Spurious IRQ: %u, last unknown IRQ: %d\n", +	       spurious_irq_cnt, spurious_irq); + +	puts("\nInterrupt-Information:\n" "Nr  Routine   Arg       Count\n"); + +	for (irq = 0; irq < NR_IRQS; irq++) { +		if (irq_handlers[irq].handler != NULL) { +			printf("%02d  %08lx  %08lx  %ld\n", irq, +			       (unsigned int)irq_handlers[irq].handler, +			       (unsigned int)irq_handlers[irq].arg, +			       irq_handlers[irq].count); +		} +	} +} +#endif diff --git a/cpu/leon3/prom.c b/cpu/leon3/prom.c new file mode 100644 index 000000000..9fa2d040e --- /dev/null +++ b/cpu/leon3/prom.c @@ -0,0 +1,1078 @@ +/* prom.c - emulates a sparc v0 PROM for the linux kernel. + * + * Copyright (C) 2003 Konrad Eisele <eiselekd@web.de> + * Copyright (C) 2004 Stefan Holst <mail@s-holst.de> + * Copyright (C) 2007 Daniel Hellstrom <daniel@gaisler.com> + * + * 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 <common.h> +#include <asm/prom.h> +#include <asm/machines.h> +#include <asm/srmmu.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/leon.h> +#include <ambapp.h> + +#include <config.h> +/* +#define PRINT_ROM_VEC +*/ +extern struct linux_romvec *kernel_arg_promvec; +extern ambapp_dev_apbuart *leon3_apbuart; + +#define PROM_PGT __attribute__ ((__section__ (".prom.pgt"))) +#define PROM_TEXT __attribute__ ((__section__ (".prom.text"))) +#define PROM_DATA __attribute__ ((__section__ (".prom.data"))) + +ambapp_dev_gptimer *gptimer; + +/* for __va */ +extern int __prom_start; +#define PAGE_OFFSET 0xf0000000 +#define phys_base CFG_SDRAM_BASE +#define PROM_OFFS 8192 +#define PROM_SIZE_MASK (PROM_OFFS-1) +#define __va(x) ( \ +	(void *)( ((unsigned long)(x))-PROM_OFFS+ \ +	(CFG_PROM_OFFSET-phys_base)+PAGE_OFFSET-TEXT_BASE ) \ +	) +#define __phy(x) ((void *)(((unsigned long)(x))-PROM_OFFS+CFG_PROM_OFFSET-TEXT_BASE)) + +struct property { +	char *name; +	char *value; +	int length; +}; + +struct node { +	int level; +	struct property *properties; +}; + +static void leon_reboot(char *bcommand); +static void leon_halt(void); +static int leon_nbputchar(int c); +static int leon_nbgetchar(void); + +static int no_nextnode(int node); +static int no_child(int node); +static int no_proplen(int node, char *name); +static int no_getprop(int node, char *name, char *value); +static int no_setprop(int node, char *name, char *value, int len); +static char *no_nextprop(int node, char *name); + +static struct property PROM_TEXT *find_property(int node, char *name); +static int PROM_TEXT leon_strcmp(const char *s1, const char *s2); +static void *PROM_TEXT leon_memcpy(void *dest, const void *src, size_t n); +static void PROM_TEXT leon_reboot_physical(char *bcommand); + +void __inline__ leon_flush_cache_all(void) +{ +	__asm__ __volatile__(" flush "); +      __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t"::"i"(ASI_DFLUSH):"memory"); +} + +void __inline__ leon_flush_tlb_all(void) +{ +	leon_flush_cache_all(); +	__asm__ __volatile__("sta %%g0, [%0] %1\n\t"::"r"(0x400), +			     "i"(ASI_MMUFLUSH):"memory"); +} + +typedef struct { +	unsigned int ctx_table[256]; +	unsigned int pgd_table[256]; +} sparc_srmmu_setup; + +sparc_srmmu_setup srmmu_tables PROM_PGT = { +	{0}, +	{0x1e, +	 0x10001e, +	 0x20001e, +	 0x30001e, +	 0x40001e, +	 0x50001e, +	 0x60001e, +	 0x70001e, +	 0x80001e, +	 0x90001e, +	 0xa0001e, +	 0xb0001e, +	 0xc0001e, +	 0xd0001e, +	 0xe0001e, +	 0xf0001e, +	 0x100001e, +	 0x110001e, +	 0x120001e, +	 0x130001e, +	 0x140001e, +	 0x150001e, +	 0x160001e, +	 0x170001e, +	 0x180001e, +	 0x190001e, +	 0x1a0001e, +	 0x1b0001e, +	 0x1c0001e, +	 0x1d0001e, +	 0x1e0001e, +	 0x1f0001e, +	 0x200001e, +	 0x210001e, +	 0x220001e, +	 0x230001e, +	 0x240001e, +	 0x250001e, +	 0x260001e, +	 0x270001e, +	 0x280001e, +	 0x290001e, +	 0x2a0001e, +	 0x2b0001e, +	 0x2c0001e, +	 0x2d0001e, +	 0x2e0001e, +	 0x2f0001e, +	 0x300001e, +	 0x310001e, +	 0x320001e, +	 0x330001e, +	 0x340001e, +	 0x350001e, +	 0x360001e, +	 0x370001e, +	 0x380001e, +	 0x390001e, +	 0x3a0001e, +	 0x3b0001e, +	 0x3c0001e, +	 0x3d0001e, +	 0x3e0001e, +	 0x3f0001e, +	 0x400001e, +	 0x410001e, +	 0x420001e, +	 0x430001e, +	 0x440001e, +	 0x450001e, +	 0x460001e, +	 0x470001e, +	 0x480001e, +	 0x490001e, +	 0x4a0001e, +	 0x4b0001e, +	 0x4c0001e, +	 0x4d0001e, +	 0x4e0001e, +	 0x4f0001e, +	 0x500001e, +	 0x510001e, +	 0x520001e, +	 0x530001e, +	 0x540001e, +	 0x550001e, +	 0x560001e, +	 0x570001e, +	 0x580001e, +	 0x590001e, +	 0x5a0001e, +	 0x5b0001e, +	 0x5c0001e, +	 0x5d0001e, +	 0x5e0001e, +	 0x5f0001e, +	 0x600001e, +	 0x610001e, +	 0x620001e, +	 0x630001e, +	 0x640001e, +	 0x650001e, +	 0x660001e, +	 0x670001e, +	 0x680001e, +	 0x690001e, +	 0x6a0001e, +	 0x6b0001e, +	 0x6c0001e, +	 0x6d0001e, +	 0x6e0001e, +	 0x6f0001e, +	 0x700001e, +	 0x710001e, +	 0x720001e, +	 0x730001e, +	 0x740001e, +	 0x750001e, +	 0x760001e, +	 0x770001e, +	 0x780001e, +	 0x790001e, +	 0x7a0001e, +	 0x7b0001e, +	 0x7c0001e, +	 0x7d0001e, +	 0x7e0001e, +	 0x7f0001e, +	 0x800001e, +	 0x810001e, +	 0x820001e, +	 0x830001e, +	 0x840001e, +	 0x850001e, +	 0x860001e, +	 0x870001e, +	 0x880001e, +	 0x890001e, +	 0x8a0001e, +	 0x8b0001e, +	 0x8c0001e, +	 0x8d0001e, +	 0x8e0001e, +	 0x8f0001e, +	 0x900001e, +	 0x910001e, +	 0x920001e, +	 0x930001e, +	 0x940001e, +	 0x950001e, +	 0x960001e, +	 0x970001e, +	 0x980001e, +	 0x990001e, +	 0x9a0001e, +	 0x9b0001e, +	 0x9c0001e, +	 0x9d0001e, +	 0x9e0001e, +	 0x9f0001e, +	 0xa00001e, +	 0xa10001e, +	 0xa20001e, +	 0xa30001e, +	 0xa40001e, +	 0xa50001e, +	 0xa60001e, +	 0xa70001e, +	 0xa80001e, +	 0xa90001e, +	 0xaa0001e, +	 0xab0001e, +	 0xac0001e, +	 0xad0001e, +	 0xae0001e, +	 0xaf0001e, +	 0xb00001e, +	 0xb10001e, +	 0xb20001e, +	 0xb30001e, +	 0xb40001e, +	 0xb50001e, +	 0xb60001e, +	 0xb70001e, +	 0xb80001e, +	 0xb90001e, +	 0xba0001e, +	 0xbb0001e, +	 0xbc0001e, +	 0xbd0001e, +	 0xbe0001e, +	 0xbf0001e, +	 0xc00001e, +	 0xc10001e, +	 0xc20001e, +	 0xc30001e, +	 0xc40001e, +	 0xc50001e, +	 0xc60001e, +	 0xc70001e, +	 0xc80001e, +	 0xc90001e, +	 0xca0001e, +	 0xcb0001e, +	 0xcc0001e, +	 0xcd0001e, +	 0xce0001e, +	 0xcf0001e, +	 0xd00001e, +	 0xd10001e, +	 0xd20001e, +	 0xd30001e, +	 0xd40001e, +	 0xd50001e, +	 0xd60001e, +	 0xd70001e, +	 0xd80001e, +	 0xd90001e, +	 0xda0001e, +	 0xdb0001e, +	 0xdc0001e, +	 0xdd0001e, +	 0xde0001e, +	 0xdf0001e, +	 0xe00001e, +	 0xe10001e, +	 0xe20001e, +	 0xe30001e, +	 0xe40001e, +	 0xe50001e, +	 0xe60001e, +	 0xe70001e, +	 0xe80001e, +	 0xe90001e, +	 0xea0001e, +	 0xeb0001e, +	 0xec0001e, +	 0xed0001e, +	 0xee0001e, +	 0xef0001e, +	 0x400001e		/* default */ +	 } +}; + +/* a self contained prom info structure */ +struct leon_reloc_func { +	struct property *(*find_property) (int node, char *name); +	int (*strcmp) (char *s1, char *s2); +	void *(*memcpy) (void *dest, const void *src, size_t n); +	void (*reboot_physical) (char *cmd); +	ambapp_dev_apbuart *leon3_apbuart; +}; + +struct leon_prom_info { +	int freq_khz; +	int leon_nctx; +	int mids[32]; +	int baudrates[2]; +	struct leon_reloc_func reloc_funcs; +	struct property root_properties[4]; +	struct property cpu_properties[7]; +#undef  CPUENTRY +#define CPUENTRY(idx) struct property cpu_properties##idx[4] +	 CPUENTRY(1); +	 CPUENTRY(2); +	 CPUENTRY(3); +	 CPUENTRY(4); +	 CPUENTRY(5); +	 CPUENTRY(6); +	 CPUENTRY(7); +	 CPUENTRY(8); +	 CPUENTRY(9); +	 CPUENTRY(10); +	 CPUENTRY(11); +	 CPUENTRY(12); +	 CPUENTRY(13); +	 CPUENTRY(14); +	 CPUENTRY(15); +	 CPUENTRY(16); +	 CPUENTRY(17); +	 CPUENTRY(18); +	 CPUENTRY(19); +	 CPUENTRY(20); +	 CPUENTRY(21); +	 CPUENTRY(22); +	 CPUENTRY(23); +	 CPUENTRY(24); +	 CPUENTRY(25); +	 CPUENTRY(26); +	 CPUENTRY(27); +	 CPUENTRY(28); +	 CPUENTRY(29); +	 CPUENTRY(30); +	 CPUENTRY(31); +	struct idprom idprom; +	struct linux_nodeops nodeops; +	struct linux_mlist_v0 *totphys_p; +	struct linux_mlist_v0 totphys; +	struct linux_mlist_v0 *avail_p; +	struct linux_mlist_v0 avail; +	struct linux_mlist_v0 *prommap_p; +	void (*synchook) (void); +	struct linux_arguments_v0 *bootargs_p; +	struct linux_arguments_v0 bootargs; +	struct linux_romvec romvec; +	struct node nodes[35]; +	char s_device_type[12]; +	char s_cpu[4]; +	char s_mid[4]; +	char s_idprom[7]; +	char s_compatability[14]; +	char s_leon2[6]; +	char s_mmu_nctx[9]; +	char s_frequency[16]; +	char s_uart1_baud[11]; +	char s_uart2_baud[11]; +	char arg[256]; +}; + +/* static prom info */ +static struct leon_prom_info PROM_DATA spi = { +	CONFIG_SYS_CLK_FREQ / 1000, +	256, +	{ +#undef	CPUENTRY +#define	CPUENTRY(idx) idx +	 CPUENTRY(0), +	 CPUENTRY(1), +	 CPUENTRY(2), +	 CPUENTRY(3), +	 CPUENTRY(4), +	 CPUENTRY(5), +	 CPUENTRY(6), +	 CPUENTRY(7), +	 CPUENTRY(8), +	 CPUENTRY(9), +	 CPUENTRY(10), +	 CPUENTRY(11), +	 CPUENTRY(12), +	 CPUENTRY(13), +	 CPUENTRY(14), +	 CPUENTRY(15), +	 CPUENTRY(16), +	 CPUENTRY(17), +	 CPUENTRY(18), +	 CPUENTRY(19), +	 CPUENTRY(20), +	 CPUENTRY(21), +	 CPUENTRY(22), +	 CPUENTRY(23), +	 CPUENTRY(24), +	 CPUENTRY(25), +	 CPUENTRY(26), +	 CPUENTRY(27), +	 CPUENTRY(28), +	 CPUENTRY(29), +	 CPUENTRY(30), +	 31}, +	{38400, 38400}, +	{ +	 __va(find_property), +	 __va(leon_strcmp), +	 __va(leon_memcpy), +	 __phy(leon_reboot_physical), +	 }, +	{ +	 {__va(spi.s_device_type), __va(spi.s_idprom), 4}, +	 {__va(spi.s_idprom), (char *)__va(&spi.idprom), sizeof(struct idprom)}, +	 {__va(spi.s_compatability), __va(spi.s_leon2), 5}, +	 {NULL, NULL, -1} +	 }, +	{ +	 {__va(spi.s_device_type), __va(spi.s_cpu), 4}, +	 {__va(spi.s_mid), __va(&spi.mids[0]), 4}, +	 {__va(spi.s_mmu_nctx), (char *)__va(&spi.leon_nctx), 4}, +	 {__va(spi.s_frequency), (char *)__va(&spi.freq_khz), 4}, +	 {__va(spi.s_uart1_baud), (char *)__va(&spi.baudrates[0]), 4}, +	 {__va(spi.s_uart2_baud), (char *)__va(&spi.baudrates[1]), 4}, +	 {NULL, NULL, -1} +	 }, +#undef  CPUENTRY +#define CPUENTRY(idx) \ +	{ /* cpu_properties */						\ +		{__va(spi.s_device_type), __va(spi.s_cpu), 4},		\ +		{__va(spi.s_mid), __va(&spi.mids[idx]), 4},			\ +		{__va(spi.s_frequency), (char *)__va(&spi.freq_khz), 4},	\ +		{NULL, NULL, -1}						\ +	} +	CPUENTRY(1), +	CPUENTRY(2), +	CPUENTRY(3), +	CPUENTRY(4), +	CPUENTRY(5), +	CPUENTRY(6), +	CPUENTRY(7), +	CPUENTRY(8), +	CPUENTRY(9), +	CPUENTRY(10), +	CPUENTRY(11), +	CPUENTRY(12), +	CPUENTRY(13), +	CPUENTRY(14), +	CPUENTRY(15), +	CPUENTRY(16), +	CPUENTRY(17), +	CPUENTRY(18), +	CPUENTRY(19), +	CPUENTRY(20), +	CPUENTRY(21), +	CPUENTRY(22), +	CPUENTRY(23), +	CPUENTRY(24), +	CPUENTRY(25), +	CPUENTRY(26), +	CPUENTRY(27), +	CPUENTRY(28), +	CPUENTRY(29), +	CPUENTRY(30), +	CPUENTRY(31), +	{ +	 0x01,			/* format */ +	 M_LEON2 | M_LEON2_SOC,	/* machine type */ +	 {0, 0, 0, 0, 0, 0},	/* eth */ +	 0,			/* date */ +	 0,			/* sernum */ +	 0,			/* checksum */ +	 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}	/* reserved */ +	 }, +	{ +	 __va(no_nextnode), +	 __va(no_child), +	 __va(no_proplen), +	 __va(no_getprop), +	 __va(no_setprop), +	 __va(no_nextprop) +	 }, +	__va(&spi.totphys), +	{ +	 NULL, +	 (char *)CFG_SDRAM_BASE, +	 0, +	 }, +	__va(&spi.avail), +	{ +	 NULL, +	 (char *)CFG_SDRAM_BASE, +	 0, +	 }, +	NULL,			/* prommap_p */ +	NULL, +	__va(&spi.bootargs), +	{ +	 {NULL, __va(spi.arg), NULL /*... */ }, +	 /*... */ +	 }, +	{ +	 0, +	 0,			/* sun4c v0 prom */ +	 0, 0, +	 {__va(&spi.totphys_p), __va(&spi.prommap_p), __va(&spi.avail_p)}, +	 __va(&spi.nodeops), +	 NULL, {NULL /* ... */ }, +	 NULL, NULL, +	 NULL, NULL,		/* pv_getchar, pv_putchar */ +	 __va(leon_nbgetchar), __va(leon_nbputchar), +	 NULL, +	 __va(leon_reboot), +	 NULL, +	 NULL, +	 NULL, +	 __va(leon_halt), +	 __va(&spi.synchook), +	 {NULL}, +	 __va(&spi.bootargs_p) +	 /*... */ +	 }, +	{ +	 {0, __va(spi.root_properties + 3) /* NULL, NULL, -1 */ }, +	 {0, __va(spi.root_properties)}, +	 /* cpu 0, must be spi.nodes[2] see leon_prom_init() */ +	 {1, __va(spi.cpu_properties)}, + +#undef  CPUENTRY +#define CPUENTRY(idx) \ +	  {1, __va(spi.cpu_properties##idx) }	/* cpu <idx> */ +	 CPUENTRY(1), +	 CPUENTRY(2), +	 CPUENTRY(3), +	 CPUENTRY(4), +	 CPUENTRY(5), +	 CPUENTRY(6), +	 CPUENTRY(7), +	 CPUENTRY(8), +	 CPUENTRY(9), +	 CPUENTRY(10), +	 CPUENTRY(11), +	 CPUENTRY(12), +	 CPUENTRY(13), +	 CPUENTRY(14), +	 CPUENTRY(15), +	 CPUENTRY(16), +	 CPUENTRY(17), +	 CPUENTRY(18), +	 CPUENTRY(19), +	 CPUENTRY(20), +	 CPUENTRY(21), +	 CPUENTRY(22), +	 CPUENTRY(23), +	 CPUENTRY(24), +	 CPUENTRY(25), +	 CPUENTRY(26), +	 CPUENTRY(27), +	 CPUENTRY(28), +	 CPUENTRY(29), +	 CPUENTRY(30), +	 CPUENTRY(31), +	 {-1, __va(spi.root_properties + 3) /* NULL, NULL, -1 */ } +	 }, +	"device_type", +	"cpu", +	"mid", +	"idprom", +	"compatability", +	"leon2", +	"mmu-nctx", +	"clock-frequency", +	"uart1_baud", +	"uart2_baud", +	CONFIG_DEFAULT_KERNEL_COMMAND_LINE +}; + +/* from arch/sparc/kernel/setup.c */ +#define RAMDISK_LOAD_FLAG 0x4000 +extern unsigned short root_flags; +extern unsigned short root_dev; +extern unsigned short ram_flags; +extern unsigned int sparc_ramdisk_image; +extern unsigned int sparc_ramdisk_size; +extern int root_mountflags; + +extern char initrd_end, initrd_start; + +/* Reboot the CPU = jump to beginning of flash again. + * + * Make sure that all function are inlined here. + */ +static void PROM_TEXT leon_reboot(char *bcommand) +{ +	register char *arg = bcommand; +	void __attribute__ ((noreturn)) (*reboot_physical) (char *cmd); + +	/* get physical address */ +	struct leon_prom_info *pspi = +	    (void *)(CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	unsigned int *srmmu_ctx_table; + +	/* Turn of Interrupts */ +	set_pil(0xf); + +	/* Set kernel's context, context zero */ +	srmmu_set_context(0); + +	/* Get physical address of the MMU shutdown routine */ +	reboot_physical = (void *) +	    SPARC_BYPASS_READ(&pspi->reloc_funcs.reboot_physical); + +	/* Now that we know the physical address of the function +	 * we can make the MMU allow jumping to it. +	 */ +	srmmu_ctx_table = (unsigned int *)srmmu_get_ctable_ptr(); + +	srmmu_ctx_table = (unsigned int *)SPARC_BYPASS_READ(srmmu_ctx_table); + +	/* get physical address of kernel's context table (assume ptd) */ +	srmmu_ctx_table = (unsigned int *) +	    (((unsigned int)srmmu_ctx_table & 0xfffffffc) << 4); + +	/* enable access to physical address of MMU shutdown function */ +	SPARC_BYPASS_WRITE(&srmmu_ctx_table +			   [((unsigned int)reboot_physical) >> 24], +			   (((unsigned int)reboot_physical & 0xff000000) >> 4) | +			   0x1e); + +	/* flush TLB cache */ +	leon_flush_tlb_all(); + +	/* flash instruction & data cache */ +	sparc_icache_flush_all(); +	sparc_dcache_flush_all(); + +	/* jump to physical address function +	 * so that when the MMU is disabled +	 * we can continue to execute +	 */ +	reboot_physical(arg); +} + +static void PROM_TEXT leon_reboot_physical(char *bcommand) +{ +	void __attribute__ ((noreturn)) (*reset) (void); + +	/* Turn off MMU */ +	srmmu_set_mmureg(0); + +	/* Hardcoded start address */ +	reset = CFG_MONITOR_BASE; + +	/* flush data cache */ +	sparc_dcache_flush_all(); + +	/* flush instruction cache */ +	sparc_icache_flush_all(); + +	/* Jump to start in Flash */ +	reset(); +} + +static void PROM_TEXT leon_halt(void) +{ +	while (1) ; +} + +/* get single char, don't care for blocking*/ +static int PROM_TEXT leon_nbgetchar(void) +{ +	return -1; +} + +/* put single char, don't care for blocking*/ +static int PROM_TEXT leon_nbputchar(int c) +{ +	ambapp_dev_apbuart *uart; + +	/* get physical address */ +	struct leon_prom_info *pspi = +	    (void *)(CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	uart = (ambapp_dev_apbuart *) +	    SPARC_BYPASS_READ(&pspi->reloc_funcs.leon3_apbuart); + +	/* no UART? */ +	if (!uart) +		return 0; + +	/***** put char in buffer... *********** +	 * Make sure all functions are inline! * +	 ***************************************/ + +	/* Wait for last character to go. */ +	while (!(SPARC_BYPASS_READ(&uart->status) +		 & LEON_REG_UART_STATUS_THE)) ; + +	/* Send data */ +	SPARC_BYPASS_WRITE(&uart->data, c); + +	/* Wait for data to be sent */ +	while (!(SPARC_BYPASS_READ(&uart->status) +		 & LEON_REG_UART_STATUS_TSE)) ; + +	return 0; +} + +/* node ops */ + +/*#define nodes ((struct node *)__va(&pspi->nodes))*/ +#define nodes ((struct node *)(pspi->nodes)) + +static int PROM_TEXT no_nextnode(int node) +{ +	/* get physical address */ +	struct leon_prom_info *pspi = +	    (void *)(CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	/* convert into virtual address */ +	pspi = (struct leon_prom_info *) +	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET); + +	if (nodes[node].level == nodes[node + 1].level) +		return node + 1; +	return -1; +} + +static int PROM_TEXT no_child(int node) +{ +	/* get physical address */ +	struct leon_prom_info *pspi = (struct leon_prom_info *) +	    (CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	/* convert into virtual address */ +	pspi = (struct leon_prom_info *) +	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET); + +	if (nodes[node].level == nodes[node + 1].level - 1) +		return node + 1; +	return -1; +} + +static struct property PROM_TEXT *find_property(int node, char *name) +{ +	/* get physical address */ +	struct leon_prom_info *pspi = (struct leon_prom_info *) +	    (CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	/* convert into virtual address */ +	pspi = (struct leon_prom_info *) +	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET); + +	struct property *prop = &nodes[node].properties[0]; +	while (prop && prop->name) { +		if (pspi->reloc_funcs.strcmp(prop->name, name) == 0) +			return prop; +		prop++; +	} +	return NULL; +} + +static int PROM_TEXT no_proplen(int node, char *name) +{ +	/* get physical address */ +	struct leon_prom_info *pspi = (struct leon_prom_info *) +	    (CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	/* convert into virtual address */ +	pspi = (struct leon_prom_info *) +	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET); + +	struct property *prop = pspi->reloc_funcs.find_property(node, name); +	if (prop) +		return prop->length; +	return -1; +} + +static int PROM_TEXT no_getprop(int node, char *name, char *value) +{ +	/* get physical address */ +	struct leon_prom_info *pspi = (struct leon_prom_info *) +	    (CFG_PROM_OFFSET + sizeof(srmmu_tables)); + +	/* convert into virtual address */ +	pspi = (struct leon_prom_info *) +	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET); + +	struct property *prop = pspi->reloc_funcs.find_property(node, name); +	if (prop) { +		pspi->reloc_funcs.memcpy(value, prop->value, prop->length); +		return 1; +	} +	return -1; +} + +static int PROM_TEXT no_setprop(int node, char *name, char *value, int len) +{ +	return -1; +} + +static char PROM_TEXT *no_nextprop(int node, char *name) +{ +	/* get physical address */ +	struct leon_prom_info *pspi = (struct leon_prom_info *) +	    (CFG_PROM_OFFSET + sizeof(srmmu_tables)); +	struct property *prop; + +	/* convert into virtual address */ +	pspi = (struct leon_prom_info *) +	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET); + +	if (!name || !name[0]) +		return nodes[node].properties[0].name; + +	prop = pspi->reloc_funcs.find_property(node, name); +	if (prop) +		return prop[1].name; +	return NULL; +} + +static int PROM_TEXT leon_strcmp(const char *s1, const char *s2) +{ +	register char result; + +	while (1) { +		result = *s1 - *s2; +		if (result || !*s1) +			break; +		s2++; +		s1++; +	} + +	return result; +} + +static void *PROM_TEXT leon_memcpy(void *dest, const void *src, size_t n) +{ +	char *dst = (char *)dest, *source = (char *)src; + +	while (n--) { +		*dst = *source; +		dst++; +		source++; +	} +	return dest; +} + +#define GETREGSP(sp) __asm__ __volatile__("mov %%sp, %0" : "=r" (sp)) + +void leon_prom_init(struct leon_prom_info *pspi) +{ +	unsigned long i; +	unsigned char cksum, *ptr; +	char *addr_str, *end; +	unsigned long sp; +	GETREGSP(sp); + +	pspi->freq_khz = CONFIG_SYS_CLK_FREQ / 1000; + +	/* Set Available main memory size */ +	pspi->totphys.num_bytes = CFG_PROM_OFFSET - CFG_SDRAM_BASE; +	pspi->avail.num_bytes = pspi->totphys.num_bytes; + +	/* Set the pointer to the Console UART in romvec */ +	pspi->reloc_funcs.leon3_apbuart = leon3_apbuart; + +	{ +		int j = 1; +#ifdef CONFIG_SMP +		ambapp_dev_irqmp *b; +		b = (ambapp_dev_irqmp *) leon3_getapbbase(VENDOR_GAISLER, +							  GAISLER_IRQMP); +		if (b) { +			j = 1 + ((LEON3_BYPASS_LOAD_PA(&(b->mpstatus)) +				  >> LEON3_IRQMPSTATUS_CPUNR) & 0xf); +		} +#endif +#undef nodes +		pspi->nodes[2 + j].level = -1; +		pspi->nodes[2 + j].properties = __va(spi.root_properties + 3); +	} + +	/* Set Ethernet MAC address from environment */ +	if ((addr_str = getenv("ethaddr")) != NULL) { +		for (i = 0; i < 6; i++) { +			pspi->idprom.id_ethaddr[i] = addr_str ? +			    simple_strtoul(addr_str, &end, 16) : 0; +			if (addr_str) { +				addr_str = (*end) ? end + 1 : end; +			} +		} +	} else { +		/* HW Address not found in environment, +		 * Set default HW address +		 */ +		pspi->idprom.id_ethaddr[0] = 0; +		pspi->idprom.id_ethaddr[1] = 0; +		pspi->idprom.id_ethaddr[2] = 0; +		pspi->idprom.id_ethaddr[3] = 0; +		pspi->idprom.id_ethaddr[4] = 0; +		pspi->idprom.id_ethaddr[5] = 0; +	} + +	ptr = (unsigned char *)&pspi->idprom; +	for (i = cksum = 0; i <= 0x0E; i++) +		cksum ^= *ptr++; +	pspi->idprom.id_cksum = cksum; +} + +static inline void set_cache(unsigned long regval) +{ +	asm volatile ("sta %0, [%%g0] %1\n\t":: "r" (regval), "i"(2):"memory"); +} + +extern unsigned short bss_start, bss_end; + +/* mark as section .img.main.text, to be referenced in linker script */ +int prom_init(void) +{ +	struct leon_prom_info *pspi = (void *) +	    ((((unsigned int)&spi) & PROM_SIZE_MASK) + CFG_PROM_OFFSET); + +	/* disable mmu */ +	srmmu_set_mmureg(0x00000000); +	__asm__ __volatile__("flush\n\t"); + +	/* init prom info struct */ +	leon_prom_init(pspi); + +	kernel_arg_promvec = &pspi->romvec; +#ifdef PRINT_ROM_VEC +	printf("Kernel rom vec: 0x%lx\n", (unsigned int)(&pspi->romvec)); +#endif +	return 0; +} + +/* Copy current kernel boot argument to ROMvec */ +void prepare_bootargs(char *bootargs) +{ +	struct leon_prom_info *pspi; +	char *src, *dst; +	int left; + +	/* if no bootargs set, skip copying ==> default bootline */ +	if (bootargs && (*bootargs != '\0')) { +		pspi = (void *)((((unsigned int)&spi) & PROM_SIZE_MASK) + +				CFG_PROM_OFFSET); +		src = bootargs; +		dst = &pspi->arg[0]; +		left = 255;	/* max len */ +		while (*src && left > 0) { +			*dst++ = *src++; +			left--; +		} +		/* terminate kernel command line string */ +		*dst = 0; +	} +} + +void srmmu_init_cpu(unsigned int entry) +{ +	sparc_srmmu_setup *psrmmu_tables = (void *) +	    ((((unsigned int)&srmmu_tables) & PROM_SIZE_MASK) + +	     CFG_PROM_OFFSET); + +	/* Make context 0 (kernel's context) point +	 * to our prepared memory mapping +	 */ +#define PTD 1 +	psrmmu_tables->ctx_table[0] = +	    ((unsigned int)&psrmmu_tables->pgd_table[0x00]) >> 4 | PTD; + +	/* Set virtual kernel address 0xf0000000 +	 * to SRAM/SDRAM address. +	 * Make it READ/WRITE/EXEC to SuperUser +	 */ +#define PTE 2 +#define ACC_SU_ALL 0x1c +	psrmmu_tables->pgd_table[0xf0] = +	    (CFG_SDRAM_BASE >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf1] = +	    ((CFG_SDRAM_BASE + 0x1000000) >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf2] = +	    ((CFG_SDRAM_BASE + 0x2000000) >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf3] = +	    ((CFG_SDRAM_BASE + 0x3000000) >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf4] = +	    ((CFG_SDRAM_BASE + 0x4000000) >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf5] = +	    ((CFG_SDRAM_BASE + 0x5000000) >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf6] = +	    ((CFG_SDRAM_BASE + 0x6000000) >> 4) | ACC_SU_ALL | PTE; +	psrmmu_tables->pgd_table[0xf7] = +	    ((CFG_SDRAM_BASE + 0x7000000) >> 4) | ACC_SU_ALL | PTE; + +	/* convert rom vec pointer to virtual address */ +	kernel_arg_promvec = (struct linux_romvec *) +	    (((unsigned int)kernel_arg_promvec & 0x0fffffff) | 0xf0000000); + +	/* Set Context pointer to point to context table +	 * 256 contexts supported. +	 */ +	srmmu_set_ctable_ptr((unsigned int)&psrmmu_tables->ctx_table[0]); + +	/* Set kernel's context, context zero */ +	srmmu_set_context(0); + +	/* Invalidate all Cache */ +	__asm__ __volatile__("flush\n\t"); + +	srmmu_set_mmureg(0x00000001); +	leon_flush_tlb_all(); +	leon_flush_cache_all(); +} diff --git a/cpu/leon3/serial.c b/cpu/leon3/serial.c new file mode 100644 index 000000000..27d5cd380 --- /dev/null +++ b/cpu/leon3/serial.c @@ -0,0 +1,139 @@ +/* GRLIB APBUART Serial controller driver + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com. + * + * 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 <common.h> +#include <asm/processor.h> +#include <asm/leon.h> +#include <ambapp.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Force cache miss each time a serial controller reg is read */ +#define CACHE_BYPASS 1 + +#ifdef CACHE_BYPASS +#define READ_BYTE(var)  SPARC_NOCACHE_READ_BYTE((unsigned int)&(var)) +#define READ_HWORD(var) SPARC_NOCACHE_READ_HWORD((unsigned int)&(var)) +#define READ_WORD(var)  SPARC_NOCACHE_READ((unsigned int)&(var)) +#define READ_DWORD(var) SPARC_NOCACHE_READ_DWORD((unsigned int)&(var)) +#endif + +ambapp_dev_apbuart *leon3_apbuart = NULL; + +int serial_init(void) +{ +	ambapp_apbdev apbdev; +	unsigned int tmp; + +	/* find UART */ +	if (ambapp_apb_first(VENDOR_GAISLER, GAISLER_APBUART, &apbdev) == 1) { + +		leon3_apbuart = (ambapp_dev_apbuart *) apbdev.address; + +		/* found apbuart, let's init... +		 * +		 * Set scaler / baud rate +		 * +		 * Receiver & transmitter enable +		 */ +		leon3_apbuart->scaler = CFG_GRLIB_APBUART_SCALER; + +		/* Let bit 11 be unchanged (debug bit for GRMON) */ +		tmp = READ_WORD(leon3_apbuart->ctrl); + +		leon3_apbuart->ctrl = ((tmp & LEON_REG_UART_CTRL_DBG) | +				       LEON_REG_UART_CTRL_RE | +				       LEON_REG_UART_CTRL_TE); + +		return 0; +	} +	return -1;		/* didn't find hardware */ +} + +void serial_putc(const char c) +{ +	if (c == '\n') +		serial_putc_raw('\r'); + +	serial_putc_raw(c); +} + +void serial_putc_raw(const char c) +{ +	if (!leon3_apbuart) +		return; + +	/* Wait for last character to go. */ +	while (!(READ_WORD(leon3_apbuart->status) & LEON_REG_UART_STATUS_THE)) ; + +	/* Send data */ +	leon3_apbuart->data = c; + +#ifdef LEON_DEBUG +	/* Wait for data to be sent */ +	while (!(READ_WORD(leon3_apbuart->status) & LEON_REG_UART_STATUS_TSE)) ; +#endif +} + +void serial_puts(const char *s) +{ +	while (*s) { +		serial_putc(*s++); +	} +} + +int serial_getc(void) +{ +	if (!leon3_apbuart) +		return 0; + +	/* Wait for a character to arrive. */ +	while (!(READ_WORD(leon3_apbuart->status) & LEON_REG_UART_STATUS_DR)) ; + +	/* read data */ +	return READ_WORD(leon3_apbuart->data); +} + +int serial_tstc(void) +{ +	if (leon3_apbuart) +		return (READ_WORD(leon3_apbuart->status) & +			LEON_REG_UART_STATUS_DR); +	return 0; +} + +/* set baud rate for uart */ +void serial_setbrg(void) +{ +	/* update baud rate settings, read it from gd->baudrate */ +	unsigned int scaler; +	if (leon3_apbuart && (gd->baudrate > 0)) { +		scaler = +		    (((CONFIG_SYS_CLK_FREQ * 10) / (gd->baudrate * 8)) - +		     5) / 10; +		leon3_apbuart->scaler = scaler; +	} +	return; +} diff --git a/cpu/leon3/start.S b/cpu/leon3/start.S new file mode 100644 index 000000000..807f7e8da --- /dev/null +++ b/cpu/leon3/start.S @@ -0,0 +1,616 @@ +/* This is where the SPARC/LEON3 starts + * Copyright (C) 2007, + * Daniel Hellstrom, daniel@gaisler.com + * + * 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 <config.h> +#include <asm/asmmacro.h> +#include <asm/winmacro.h> +#include <asm/psr.h> +#include <asm/stack.h> +#include <asm/leon.h> +#include <version.h> + +/* Entry for traps which jump to a programmer-specified trap handler.  */ +#define TRAPR(H)  \ +	wr 	%g0, 0xfe0, %psr; \ +	mov 	%g0, %tbr; \ +	ba 	(H); \ +	mov 	%g0, %wim; + +#define TRAP(H) \ +	mov	%psr, %l0; \ +	ba	(H); \ +	nop; nop; + +#define TRAPI(ilevel) \ +	mov 	ilevel, %l7; \ +	mov 	%psr, %l0; \ +	b 	_irq_entry; \ +	mov 	%wim, %l3 + +/* Unexcpected trap will halt the processor by forcing it to error state */ +#undef BAD_TRAP +#define BAD_TRAP ta 0; nop; nop; nop; + +/* Software trap. Treat as BAD_TRAP for the time being... */ +#define SOFT_TRAP TRAP(_hwerr) + +#define PSR_INIT   0x1FC0	/* Disable traps, set s and ps */ +#define WIM_INIT   2 + +/* All traps low-level code here must end with this macro. */ +#define RESTORE_ALL b ret_trap_entry; clr %l6; + +#define WRITE_PAUSE nop;nop;nop + +WINDOWSIZE = (16 * 4) +ARGPUSHSIZE = (6 * 4) +ARGPUSH = (WINDOWSIZE + 4) +MINFRAME = (WINDOWSIZE + ARGPUSHSIZE + 4) + +/* Number of register windows */ +#ifndef CFG_SPARC_NWINDOWS +#error Must define number of SPARC register windows, default is 8 +#endif + +#define STACK_ALIGN	8 +#define SA(X)	(((X)+(STACK_ALIGN-1)) & ~(STACK_ALIGN-1)) + +	.section ".start", "ax" +	.globl 	_start, start, _trap_table +	.globl  _irq_entry, nmi_trap +	.globl  _reset_reloc + +/* at address 0 + * Hardware traps + */ +start: +_start: +_trap_table: +	TRAPR(_hardreset);		! 00 reset trap +	BAD_TRAP;			! 01 instruction_access_exception +	BAD_TRAP;			! 02 illegal_instruction +	BAD_TRAP;			! 03 priveleged_instruction +	BAD_TRAP;			! 04 fp_disabled +	TRAP(_window_overflow);		! 05 window_overflow +	TRAP(_window_underflow);	! 06 window_underflow +	BAD_TRAP;			! 07 Memory Address Not Aligned +	BAD_TRAP;			! 08 Floating Point Exception +	BAD_TRAP;			! 09 Data Miss Exception +	BAD_TRAP;			! 0a Tagged Instruction Ovrflw +	BAD_TRAP;			! 0b Watchpoint Detected +	BAD_TRAP;			! 0c +	BAD_TRAP;			! 0d +	BAD_TRAP;			! 0e +	BAD_TRAP;			! 0f +	BAD_TRAP;			! 10 +	TRAPI(1);			! 11 IRQ level 1 +	TRAPI(2);			! 12 IRQ level 2 +	TRAPI(3);			! 13 IRQ level 3 +	TRAPI(4);			! 14 IRQ level 4 +	TRAPI(5);			! 15 IRQ level 5 +	TRAPI(6);			! 16 IRQ level 6 +	TRAPI(7);			! 17 IRQ level 7 +	TRAPI(8);			! 18 IRQ level 8 +	TRAPI(9);			! 19 IRQ level 9 +	TRAPI(10);			! 1a IRQ level 10 +	TRAPI(11);			! 1b IRQ level 11 +	TRAPI(12);			! 1c IRQ level 12 +	TRAPI(13);			! 1d IRQ level 13 +	TRAPI(14);			! 1e IRQ level 14 +	TRAP(_nmi_trap);		! 1f IRQ level 15 / +	                		! NMI (non maskable interrupt) +	BAD_TRAP;			! 20 r_register_access_error +	BAD_TRAP;			! 21 instruction access error +	BAD_TRAP;			! 22 +	BAD_TRAP;			! 23 +	BAD_TRAP;			! 24 co-processor disabled +	BAD_TRAP;			! 25 uniplemented FLUSH +	BAD_TRAP;			! 26 +	BAD_TRAP;			! 27 +	BAD_TRAP;			! 28 co-processor exception +	BAD_TRAP;			! 29 data access error +	BAD_TRAP;			! 2a division by zero +	BAD_TRAP;			! 2b data store error +	BAD_TRAP;			! 2c data access MMU miss +	BAD_TRAP;			! 2d +	BAD_TRAP;			! 2e +	BAD_TRAP;			! 2f +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 30-33 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 34-37 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 38-3b +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 3c-3f +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 40-43 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 44-47 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 48-4b +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 4c-4f +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 50-53 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 54-57 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 58-5b +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 5c-5f + +	/* implementaion dependent */ +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 60-63 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 64-67 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 68-6b +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 6c-6f +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 70-73 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 74-77 +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 78-7b +	BAD_TRAP; BAD_TRAP; BAD_TRAP; BAD_TRAP; ! 7c-7f + +	/* Software traps, not handled */ +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 80-83 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 84-87 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 88-8b +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 8c-8f +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 90-93 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 94-97 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 98-9b +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! 9c-9f +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! a0-a3 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! a4-a7 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! a8-ab +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! ac-af +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! b0-b3 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! b4-b7 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! b8-bb +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! bc-bf +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! c0-c3 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! c4-c7 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! c8-cb +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! cc-cf +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! d0-d3 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! d4-d7 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! d8-db +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! dc-df +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! e0-e3 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! e4-e7 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! e8-eb +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! ec-ef +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! f0-f3 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! f4-f7 +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! f8-fb +	SOFT_TRAP; SOFT_TRAP; SOFT_TRAP; SOFT_TRAP;	! fc-ff +/* + * Version string + */ + +	.data +	.extern leon3_snooping_avail +	.globl	version_string +version_string: +	.ascii U_BOOT_VERSION +	.ascii " (", __DATE__, " - ", __TIME__, ")" +	.ascii CONFIG_IDENT_STRING, "\0" + +	.section	".text" +	.align 4 + +_hardreset: +1000: +	flush + +	/* Enable I/D-Cache and Snooping */ +	set	0x0081000f, %g2 +	sta	%g2, [%g0] 2 + +	mov	%g0, %y +	clr	%g1 +	clr	%g2 +	clr	%g3 +	clr	%g4 +	clr	%g5 +	clr	%g6 +	clr	%g7 + +	mov	%asr17, %g3 +	and	%g3, 0x1f, %g3 +clear_window: +	mov	%g0, %l0 +	mov	%g0, %l1 +	mov	%g0, %l2 +	mov	%g0, %l3 +	mov	%g0, %l4 +	mov	%g0, %l5 +	mov	%g0, %l6 +	mov	%g0, %l7 +	mov	%g0, %o0 +	mov	%g0, %o1 +	mov	%g0, %o2 +	mov	%g0, %o3 +	mov	%g0, %o4 +	mov	%g0, %o5 +	mov	%g0, %o6 +	mov	%g0, %o7 +	subcc	%g3, 1, %g3 +	bge	clear_window +	save + +wininit: +	set	WIM_INIT, %g3 +	mov	%g3, %wim + +stackp: +	set	CFG_INIT_SP_OFFSET, %fp +	andn	%fp, 0x0f, %fp +	sub	%fp, 64, %sp + +cpu_init_unreloc: +	call	cpu_init_f +	nop + +/* un relocated start address of monitor */ +#define TEXT_START _text + +/* un relocated end address of monitor */ +#define DATA_END __init_end + +reloc: +	set	TEXT_START,%g2 +	set	DATA_END,%g3 +	set	CFG_RELOC_MONITOR_BASE,%g4 +reloc_loop: +	ldd	[%g2],%l0 +	ldd	[%g2+8],%l2 +	std	%l0,[%g4] +	std	%l2,[%g4+8] +	inc	16,%g2 +	subcc	%g3,%g2,%g0 +	bne	reloc_loop +	inc	16,%g4 + +	clr	%l0 +	clr	%l1 +	clr	%l2 +	clr	%l3 +	clr	%g2 + +/* register g4 contain address to start + * This means that BSS must be directly after data and code segments + * + * g3 is length of bss = (__bss_end-__bss_start) + * + */ + +clr_bss: +/* clear bss area (the relocated) */ +	set	__bss_start,%g2 +	set	__bss_end,%g3 +	sub	%g3,%g2,%g3 +	add	%g3,%g4,%g3 +	clr	%g1	/* std %g0 uses g0 and g1 */ +/* clearing 16byte a time ==> linker script need to align to 16 byte offset */ +clr_bss_16: +	std	%g0,[%g4] +	std	%g0,[%g4+8] +	inc	16,%g4 +	cmp	%g3,%g4 +	bne	clr_bss_16 +	nop + +/* add offsets to GOT table */ +fixup_got: +	set	__got_start,%g4 +	set	__got_end,%g3 +/* + * new got offset = (old GOT-PTR (read with ld) - + *   CFG_RELOC_MONITOR_BASE(from define) ) + + *   Destination Address (from define) + */ +	set	CFG_RELOC_MONITOR_BASE,%g2 +	set	TEXT_START, %g1 +	add	%g4,%g2,%g4 +	sub	%g4,%g1,%g4 +	add	%g3,%g2,%g3 +	sub	%g3,%g1,%g3 +	sub	%g2,%g1,%g2	! prepare register with (new base address) - +				!  (old base address) +got_loop: +	ld	[%g4],%l0	! load old GOT-PTR +	add	%l0,%g2,%l0	! increase with (new base address) - +				!  (old base) +	st	%l0,[%g4] +	inc	4,%g4 +	cmp	%g3,%g4 +	bne	got_loop +	nop + +prom_relocate: +	set	__prom_start, %g2 +	set	__prom_end, %g3 +	set	CFG_PROM_OFFSET, %g4 + +prom_relocate_loop: +	ldd	[%g2],%l0 +	ldd	[%g2+8],%l2 +	std	%l0,[%g4] +	std	%l2,[%g4+8] +	inc	16,%g2 +	subcc	%g3,%g2,%g0 +	bne	prom_relocate_loop +	inc	16,%g4 + +/* Trap table has been moved, lets tell CPU about + * the new trap table address + */ + +	set	CFG_RELOC_MONITOR_BASE, %g2 +	wr	%g0, %g2, %tbr +	nop +	nop +	nop +	 +/* If CACHE snooping is available in hardware the  + * variable leon3_snooping_avail will be set to + * 0x800000 else 0. + */ +snoop_detect: +	sethi	%hi(0x00800000), %o0 +	lda	[%g0] 2, %o1 +	and	%o0, %o1, %o0 +	sethi	%hi(leon3_snooping_avail+CFG_RELOC_MONITOR_BASE-TEXT_BASE), %o1 +	st	%o0, [%lo(leon3_snooping_avail+CFG_RELOC_MONITOR_BASE-TEXT_BASE)+%o1] + +/*	call	relocate*/ +	nop +/* Call relocated init functions */ +jump: +	set	cpu_init_f2,%o1 +	set	CFG_RELOC_MONITOR_BASE,%o2 +	add	%o1,%o2,%o1 +	sub	%o1,%g1,%o1 +	call	%o1 +	clr	%o0 + +	set	board_init_f,%o1 +	set	CFG_RELOC_MONITOR_BASE,%o2 +	add	%o1,%o2,%o1 +	sub	%o1,%g1,%o1 +	call	%o1 +	clr	%o0 + +dead:	ta 0				! if call returns... +	nop + +/* Interrupt handler caller, + * reg L7: interrupt number + * reg L0: psr after interrupt + * reg L1: PC + * reg L2: next PC + * reg L3: wim + */ +_irq_entry: +	SAVE_ALL + +	or	%l0, PSR_PIL, %g2 +	wr	%g2, 0x0, %psr +	WRITE_PAUSE +	wr	%g2, PSR_ET, %psr +	WRITE_PAUSE +	mov	%l7, %o0		! irq level +	set	handler_irq, %o1 +	set	(CFG_RELOC_MONITOR_BASE-TEXT_BASE), %o2 +	add	%o1, %o2, %o1 +	call	%o1 +	add	%sp, SF_REGS_SZ, %o1	! pt_regs ptr +	or	%l0, PSR_PIL, %g2	! restore PIL after handler_irq +	wr	%g2, PSR_ET, %psr	! keep ET up +	WRITE_PAUSE + +	RESTORE_ALL + +!Window overflow trap handler. +	.global _window_overflow + +_window_overflow: + +	mov	%wim, %l3		! Calculate next WIM +	mov	%g1, %l7 +	srl	%l3, 1, %g1 +	sll	%l3, (CFG_SPARC_NWINDOWS-1) , %l4 +	or	%l4, %g1, %g1 + +	save				! Get into window to be saved. +	mov	%g1, %wim +	nop; +	nop; +	nop +	st	%l0, [%sp + 0]; +	st	%l1, [%sp + 4]; +	st	%l2, [%sp + 8]; +	st	%l3, [%sp + 12]; +	st	%l4, [%sp + 16]; +	st	%l5, [%sp + 20]; +	st	%l6, [%sp + 24]; +	st	%l7, [%sp + 28]; +	st	%i0, [%sp + 32]; +	st	%i1, [%sp + 36]; +	st	%i2, [%sp + 40]; +	st	%i3, [%sp + 44]; +	st	%i4, [%sp + 48]; +	st	%i5, [%sp + 52]; +	st	%i6, [%sp + 56]; +	st	%i7, [%sp + 60]; +	restore				! Go back to trap window. +	mov	%l7, %g1 +	jmp	%l1			! Re-execute save. +	rett	%l2 + +/* Window underflow trap handler.  */ + +	.global  _window_underflow + +_window_underflow: + +	mov  %wim, %l3			! Calculate next WIM +	sll  %l3, 1, %l4 +	srl  %l3, (CFG_SPARC_NWINDOWS-1), %l5 +	or   %l5, %l4, %l5 +	mov  %l5, %wim +	nop; nop; nop +	restore				! Two restores to get into the +	restore				! window to restore +	ld	[%sp + 0], %l0; 	! Restore window from the stack +	ld	[%sp + 4], %l1; +	ld	[%sp + 8], %l2; +	ld	[%sp + 12], %l3; +	ld	[%sp + 16], %l4; +	ld	[%sp + 20], %l5; +	ld	[%sp + 24], %l6; +	ld	[%sp + 28], %l7; +	ld	[%sp + 32], %i0; +	ld	[%sp + 36], %i1; +	ld	[%sp + 40], %i2; +	ld	[%sp + 44], %i3; +	ld	[%sp + 48], %i4; +	ld	[%sp + 52], %i5; +	ld	[%sp + 56], %i6; +	ld	[%sp + 60], %i7; +	save				! Get back to the trap window. +	save +	jmp	%l1			! Re-execute restore. +	rett	%l2 + +	retl + +_nmi_trap: +	nop +	jmp %l1 +	rett %l2 + +_hwerr: +	ta 0 +	nop +	nop +	b _hwerr 			! loop infinite +	nop + +/* Registers to not touch at all. */ +#define t_psr      l0 /* Set by caller */ +#define t_pc       l1 /* Set by caller */ +#define t_npc      l2 /* Set by caller */ +#define t_wim      l3 /* Set by caller */ +#define t_twinmask l4 /* Set at beginning of this entry routine. */ +#define t_kstack   l5 /* Set right before pt_regs frame is built */ +#define t_retpc    l6 /* If you change this, change winmacro.h header file */ +#define t_systable l7 /* Never touch this, could be the syscall table ptr. */ +#define curptr     g6 /* Set after pt_regs frame is built */ + +trap_setup: +/* build a pt_regs trap frame. */ +	sub	%fp, (SF_REGS_SZ + PT_REGS_SZ), %t_kstack +	PT_STORE_ALL(t_kstack, t_psr, t_pc, t_npc, g2) + +	/* See if we are in the trap window. */ +	mov	1, %t_twinmask +	sll	%t_twinmask, %t_psr, %t_twinmask ! t_twinmask = (1 << psr) +	andcc	%t_twinmask, %t_wim, %g0 +	beq	1f		! in trap window, clean up +	nop + +	/*------------------------------------------------- +	 * Spill , adjust %wim and go. +	 */ +	srl	%t_wim, 0x1, %g2		! begin computation of new %wim + +	set	(CFG_SPARC_NWINDOWS-1), %g3	!NWINDOWS-1 + +	sll	%t_wim, %g3, %t_wim	! NWINDOWS-1 +	or	%t_wim, %g2, %g2 +	and	%g2, 0xff, %g2 + +	save	%g0, %g0, %g0           ! get in window to be saved + +	/* Set new %wim value */ +	wr	%g2, 0x0, %wim + +	/* Save the kernel window onto the corresponding stack. */ +	RW_STORE(sp) + +	restore	%g0, %g0, %g0 +	/*-------------------------------------------------*/ + +1: +	/* Trap from kernel with a window available. +	 * Just do it... +	 */ +	jmpl	%t_retpc + 0x8, %g0	! return to caller +	 mov	%t_kstack, %sp		! jump onto new stack + +#define twin_tmp1 l4 +#define glob_tmp  g4 +#define curptr    g6 +ret_trap_entry: +	wr	%t_psr, 0x0, %psr       ! enable nesting again, clear ET + +	/* Will the rett land us in the invalid window? */ +	mov	2, %g1 +	sll	%g1, %t_psr, %g1 + +	set	CFG_SPARC_NWINDOWS, %g2	!NWINDOWS + +	srl	%g1, %g2, %g2 +	or	%g1, %g2, %g1 +	rd	%wim, %g2 +	andcc	%g2, %g1, %g0 +	be	1f		! Nope, just return from the trap +	 sll	%g2, 0x1, %g1 + +	/* We have to grab a window before returning. */ +	set	(CFG_SPARC_NWINDOWS-1), %g3	!NWINDOWS-1 + +	srl	%g2, %g3,  %g2 +	or	%g1, %g2, %g1 +	and	%g1, 0xff, %g1 + +	wr	%g1, 0x0, %wim + +	/* Grrr, make sure we load from the right %sp... */ +	PT_LOAD_ALL(sp, t_psr, t_pc, t_npc, g1) + +	restore	%g0, %g0, %g0 +	RW_LOAD(sp) +	b	2f +	save	%g0, %g0, %g0 + +	/* Reload the entire frame in case this is from a +	 * kernel system call or whatever... +	 */ +1: +	PT_LOAD_ALL(sp, t_psr, t_pc, t_npc, g1) +2: +	wr	%t_psr, 0x0, %psr +	nop; +	nop; +	nop + +	jmp	%t_pc +	rett	%t_npc + +/* This is called from relocated C-code. + * It resets the system by jumping to _start + */ +_reset_reloc: +	set	start, %l0 +	call	%l0 +	nop diff --git a/cpu/leon3/usb_uhci.c b/cpu/leon3/usb_uhci.c new file mode 100644 index 000000000..7910bebe1 --- /dev/null +++ b/cpu/leon3/usb_uhci.c @@ -0,0 +1,1313 @@ +/* + * Part of this code has been derived from linux: + * Universal Host Controller Interface driver for USB (take II). + * + * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar) + *               Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + *               Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + *               Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter) + * (c) 2000      Yggdrasil Computing, Inc. (port of new PCI interface support + *               from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) 2000      David Brownell, david-b@pacbell.net (usb-ohci.c) + * + * HW-initalization based on material of + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Gregory P. Smith + * + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * (C) Copyright 2008, Daniel Hellström, daniel@gaisler.com + *     Added AMBA Plug&Play detection of GRUSB, modified interrupt handler. + *     Added cache flushes where needed. + * + * 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 + * + * + */ + +/********************************************************************** + * How it works: + * ------------- + * The framelist / Transfer descriptor / Queue Heads are similar like + * in the linux usb_uhci.c. + * + * During initialization, the following skeleton is allocated in init_skel: + * + *         framespecific           |           common chain + * + * framelist[] + * [  0 ]-----> TD ---------\ + * [  1 ]-----> TD ----------> TD ------> QH -------> QH -------> QH ---> NULL + *   ...        TD ---------/ + * [1023]-----> TD --------/ + * + *              ^^             ^^         ^^          ^^          ^^ + *              7 TDs for      1 TD for   Start of    Start of    End Chain + *              INT (2-128ms)  1ms-INT    CTRL Chain  BULK Chain + * + * + * Since this is a bootloader, the isochronous transfer descriptor have been removed. + * + * Interrupt Transfers. + * -------------------- + * For Interupt transfers USB_MAX_TEMP_INT_TD Transfer descriptor are available. They + * will be inserted after the appropriate (depending the interval setting) skeleton TD. + * If an interrupt has been detected the dev->irqhandler is called. The status and number + * of transfered bytes is stored in dev->irq_status resp. dev->irq_act_len. If the + * dev->irqhandler returns 0, the interrupt TD is removed and disabled. If an 1 is returned, + * the interrupt TD will be reactivated. + * + * Control Transfers + * ----------------- + * Control Transfers are issued by filling the tmp_td with the appropriate data and connect + * them to the qh_cntrl queue header. Before other control/bulk transfers can be issued, + * the programm has to wait for completion. This does not allows asynchronous data transfer. + * + * Bulk Transfers + * -------------- + * Bulk Transfers are issued by filling the tmp_td with the appropriate data and connect + * them to the qh_bulk queue header. Before other control/bulk transfers can be issued, + * the programm has to wait for completion. This does not allows asynchronous data transfer. + * + * + */ + +#include <common.h> +#include <ambapp.h> +#include <asm/leon.h> +#include <asm/leon3.h> +#include <asm/processor.h> + +#ifdef CONFIG_USB_UHCI + +#include <usb.h> +#include "usb_uhci.h" + +#define USB_MAX_TEMP_TD      128	/* number of temporary TDs for bulk and control transfers */ +#define USB_MAX_TEMP_INT_TD  32	/* number of temporary TDs for Interrupt transfers */ + +extern int leon3_snooping_avail; +/* +#define out16r(address,data) (*(unsigned short *)(address) = \ + (unsigned short)( \ + (((unsigned short)(data)&0xff)<<8) | \ + (((unsigned short)(data)&0xff00)>>8) \ + )) + */ +#define out16r(address,data) _out16r((unsigned int)(address), (unsigned short)(data)) +void _out16r(unsigned int address, unsigned short data) +{ +	unsigned short val = (unsigned short)((((unsigned short)(data) & 0xff) +					       << 8) | (((unsigned short)(data) +							 & 0xff00) >> 8)); +#ifdef UHCI_DEBUG_REGS +	printf("out16r(0x%lx,0x%04x = 0x%04x)\n", address, val, data); +#endif +	*(unsigned short *)(address) = val; +} + +#define out32r(address,data) _out32r((unsigned int)(address), (unsigned int)(data)) +void _out32r(unsigned int address, unsigned int data) +{ +	unsigned int val = (unsigned int)((((unsigned int)(data) & 0x000000ff) +					   << 24) | (((unsigned int)(data) & +						      0x0000ff00) << 8) | +					  (((unsigned int)(data) & 0x00ff0000) +					   >> 8) | (((unsigned int)(data) & +						     0xff000000) >> 24)); +#ifdef UHCI_DEBUG_REGS +	printf("out32r(0x%lx,0x%lx = 0x%lx)\n", address, val, data); +#endif +	*(unsigned int *)address = val; +} + +#define in16r(address) _in16r((unsigned int)(address)) +unsigned short _in16r(unsigned int address) +{ +	unsigned short val = sparc_load_reg_cachemiss_word(address); +	val = ((val << 8) & 0xff00) | ((val >> 8) & 0xff); +#ifdef UHCI_DEBUG_REGS +	printf("in16r(0x%lx): 0x%04x\n", address, val); +#endif +	return val; +} + +#define in32r(address) _in32r((unsigned int)(address)) +unsigned int _in32r(unsigned int address) +{ +	unsigned int val = sparc_load_reg_cachemiss(address); +	val = +	    ((val << 24) & 0xff000000) | ((val << 8) & 0xff0000) | ((val >> 8) & +								    0xff00) | +	    ((val >> 24) & 0xff); +#ifdef UHCI_DEBUG_REGS +	printf("in32r(0x%lx): 0x%08x\n", address, val); +#endif +	return val; +} + +#define READ32(address) sparc_load_reg_cachemiss((unsigned int)(address)) + +/*#define USB_UHCI_DEBUG*/ +#undef USB_UHCI_DEBUG + +void usb_show_td(int max); +#ifdef	USB_UHCI_DEBUG +void grusb_show_regs(void); +#define	USB_UHCI_PRINTF(fmt,args...)	printf (fmt ,##args) +#else +#define USB_UHCI_PRINTF(fmt,args...) +#endif + +static int grusb_irq = -1;	/* irq vector, if -1 uhci is stopped / reseted */ +unsigned int usb_base_addr;	/* base address */ + +static uhci_td_t td_int[8] __attribute__ ((aligned(16)));	/* Interrupt Transfer descriptors */ +static uhci_qh_t qh_cntrl __attribute__ ((aligned(16)));	/* control Queue Head */ +static uhci_qh_t qh_bulk __attribute__ ((aligned(16)));	/*  bulk Queue Head */ +static uhci_qh_t qh_end __attribute__ ((aligned(16)));	/* end Queue Head */ +static uhci_td_t td_last __attribute__ ((aligned(16)));	/* last TD (linked with end chain) */ + +/* temporary tds */ +static uhci_td_t tmp_td[USB_MAX_TEMP_TD] __attribute__ ((aligned(16)));	/* temporary bulk/control td's  */ +static uhci_td_t tmp_int_td[USB_MAX_TEMP_INT_TD] __attribute__ ((aligned(16)));	/* temporary interrupt td's  */ + +static unsigned long framelist[1024] __attribute__ ((aligned(0x1000)));	/* frame list */ + +static struct virt_root_hub rh;	/* struct for root hub */ + +/********************************************************************** + * some forward decleration + */ +int uhci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, +		       void *buffer, int transfer_len, +		       struct devrequest *setup); + +/* fill a td with the approproiate data. Link, status, info and buffer + * are used by the USB controller itselfes, dev is used to identify the + * "connected" device + */ +void usb_fill_td(uhci_td_t * td, unsigned long link, unsigned long status, +		 unsigned long info, unsigned long buffer, unsigned long dev) +{ +	td->link = swap_32(link); +	td->status = swap_32(status); +	if ((info & UHCI_PID) == 0) +		info |= USB_PID_OUT; +	td->info = swap_32(info); +	td->buffer = swap_32(buffer); +	td->dev_ptr = dev; +} + +/* fill a qh with the approproiate data. Head and element are used by the USB controller + * itselfes. As soon as a valid dev_ptr is filled, a td chain is connected to the qh. + * Please note, that after completion of the td chain, the entry element is removed / + * marked invalid by the USB controller. + */ +void usb_fill_qh(uhci_qh_t * qh, unsigned long head, unsigned long element) +{ +	qh->head = swap_32(head); +	qh->element = swap_32(element); +	qh->dev_ptr = 0L; +} + +/* get the status of a td->status + */ +unsigned long usb_uhci_td_stat(unsigned long status) +{ +	unsigned long result = 0; +	result |= (status & TD_CTRL_NAK) ? USB_ST_NAK_REC : 0; +	result |= (status & TD_CTRL_STALLED) ? USB_ST_STALLED : 0; +	result |= (status & TD_CTRL_DBUFERR) ? USB_ST_BUF_ERR : 0; +	result |= (status & TD_CTRL_BABBLE) ? USB_ST_BABBLE_DET : 0; +	result |= (status & TD_CTRL_CRCTIMEO) ? USB_ST_CRC_ERR : 0; +	result |= (status & TD_CTRL_BITSTUFF) ? USB_ST_BIT_ERR : 0; +	result |= (status & TD_CTRL_ACTIVE) ? USB_ST_NOT_PROC : 0; +	return result; +} + +/* get the status and the transfered len of a td chain. + * called from the completion handler + */ +int usb_get_td_status(uhci_td_t * td, struct usb_device *dev) +{ +	unsigned long temp, info; +	unsigned long stat; +	uhci_td_t *mytd = td; + +	if (dev->devnum == rh.devnum) +		return 0; +	dev->act_len = 0; +	stat = 0; +	do { +		temp = swap_32((unsigned long)READ32(&mytd->status)); +		stat = usb_uhci_td_stat(temp); +		info = swap_32((unsigned long)READ32(&mytd->info)); +		if (((info & 0xff) != USB_PID_SETUP) && (((info >> 21) & 0x7ff) != 0x7ff) && (temp & 0x7FF) != 0x7ff) {	/* if not setup and not null data pack */ +			dev->act_len += (temp & 0x7FF) + 1;	/* the transfered len is act_len + 1 */ +		} +		if (stat) {	/* status no ok */ +			dev->status = stat; +			return -1; +		} +		temp = swap_32((unsigned long)READ32(&mytd->link)); +		mytd = (uhci_td_t *) (temp & 0xfffffff0); +	} while ((temp & 0x1) == 0);	/* process all TDs */ +	dev->status = stat; +	return 0;		/* Ok */ +} + +/*------------------------------------------------------------------- + *                         LOW LEVEL STUFF + *          assembles QHs und TDs for control, bulk and iso + *-------------------------------------------------------------------*/ +int dummy(void) +{ +	USB_UHCI_PRINTF("DUMMY\n"); +	return 0; +} + +/* Submits a control message. That is a Setup, Data and Status transfer. + * Routine does not wait for completion. + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		       int transfer_len, struct devrequest *setup) +{ +	unsigned long destination, status; +	int maxsze = usb_maxpacket(dev, pipe); +	unsigned long dataptr; +	int len; +	int pktsze; +	int i = 0; + +	if (!maxsze) { +		USB_UHCI_PRINTF +		    ("uhci_submit_control_urb: pipesize for pipe %lx is zero\n", +		     pipe); +		return -1; +	} +	if (((pipe >> 8) & 0x7f) == rh.devnum) { +		/* this is the root hub -> redirect it */ +		return uhci_submit_rh_msg(dev, pipe, buffer, transfer_len, +					  setup); +	} +	USB_UHCI_PRINTF("uhci_submit_control start len %x, maxsize %x\n", +			transfer_len, maxsze); +	/* The "pipe" thing contains the destination in bits 8--18 */ +	destination = (pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;	/* Setup stage */ +	/* 3 errors */ +	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); +	/* (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD); */ +	/*  Build the TD for the control request, try forever, 8 bytes of data */ +	usb_fill_td(&tmp_td[i], UHCI_PTR_TERM, status, destination | (7 << 21), +		    (unsigned long)setup, (unsigned long)dev); +#ifdef DEBUG_EXTRA +	{ +		char *sp = (char *)setup; +		printf("SETUP to pipe %lx: %x %x %x %x %x %x %x %x\n", pipe, +		       sp[0], sp[1], sp[2], sp[3], sp[4], sp[5], sp[6], sp[7]); +	} +#endif +	dataptr = (unsigned long)buffer; +	len = transfer_len; + +	/* If direction is "send", change the frame from SETUP (0x2D) +	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */ +	destination = +	    (pipe & PIPE_DEVEP_MASK) | ((pipe & USB_DIR_IN) == +					0 ? USB_PID_OUT : USB_PID_IN); +	while (len > 0) { +		/* data stage */ +		pktsze = len; +		i++; +		if (pktsze > maxsze) +			pktsze = maxsze; +		destination ^= 1 << TD_TOKEN_TOGGLE;	/* toggle DATA0/1 */ +		usb_fill_td(&tmp_td[i], UHCI_PTR_TERM, status, destination | ((pktsze - 1) << 21), dataptr, (unsigned long)dev);	/* Status, pktsze bytes of data */ +		tmp_td[i - 1].link = swap_32((unsigned long)&tmp_td[i]); + +		dataptr += pktsze; +		len -= pktsze; +	} + +	/*  Build the final TD for control status */ +	/* It's only IN if the pipe is out AND we aren't expecting data */ + +	destination &= ~UHCI_PID; +	if (((pipe & USB_DIR_IN) == 0) || (transfer_len == 0)) +		destination |= USB_PID_IN; +	else +		destination |= USB_PID_OUT; +	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */ +	i++; +	status &= ~TD_CTRL_SPD; +	/* no limit on errors on final packet , 0 bytes of data */ +	usb_fill_td(&tmp_td[i], UHCI_PTR_TERM, status | TD_CTRL_IOC, +		    destination | (UHCI_NULL_DATA_SIZE << 21), 0, +		    (unsigned long)dev); +	tmp_td[i - 1].link = swap_32((unsigned long)&tmp_td[i]);	/* queue status td */ +	/* usb_show_td(i+1); */ +	USB_UHCI_PRINTF("uhci_submit_control end (%d tmp_tds used)\n", i); +	/* first mark the control QH element terminated */ +	qh_cntrl.element = 0xffffffffL; +	/* set qh active */ +	qh_cntrl.dev_ptr = (unsigned long)dev; +	/* fill in tmp_td_chain */ +	dummy(); +	qh_cntrl.element = swap_32((unsigned long)&tmp_td[0]); +	return 0; +} + +/*------------------------------------------------------------------- + * Prepare TDs for bulk transfers. + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		    int transfer_len) +{ +	unsigned long destination, status, info; +	unsigned long dataptr; +	int maxsze = usb_maxpacket(dev, pipe); +	int len; +	int i = 0; + +	if (transfer_len < 0) { +		printf("Negative transfer length in submit_bulk\n"); +		return -1; +	} +	if (!maxsze) +		return -1; +	/* The "pipe" thing contains the destination in bits 8--18. */ +	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid(pipe); +	/* 3 errors */ +	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); +	/*      ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); */ +	/* Build the TDs for the bulk request */ +	len = transfer_len; +	dataptr = (unsigned long)buffer; +	do { +		int pktsze = len; +		if (pktsze > maxsze) +			pktsze = maxsze; +		/* pktsze bytes of data  */ +		info = +		    destination | (((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) | +		    (usb_gettoggle +		     (dev, usb_pipeendpoint(pipe), +		      usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); + +		if ((len - pktsze) == 0) +			status |= TD_CTRL_IOC;	/* last one generates INT */ + +		usb_fill_td(&tmp_td[i], UHCI_PTR_TERM, status, info, dataptr, (unsigned long)dev);	/* Status, pktsze bytes of data */ +		if (i > 0) +			tmp_td[i - 1].link = swap_32((unsigned long)&tmp_td[i]); +		i++; +		dataptr += pktsze; +		len -= pktsze; +		usb_dotoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); +	} while (len > 0); +	/* first mark the bulk QH element terminated */ +	qh_bulk.element = 0xffffffffL; +	/* set qh active */ +	qh_bulk.dev_ptr = (unsigned long)dev; +	/* fill in tmp_td_chain */ +	qh_bulk.element = swap_32((unsigned long)&tmp_td[0]); +	return 0; +} + +/* search a free interrupt td + */ +uhci_td_t *uhci_alloc_int_td(void) +{ +	int i; +	for (i = 0; i < USB_MAX_TEMP_INT_TD; i++) { +		if (tmp_int_td[i].dev_ptr == 0)	/* no device assigned -> free TD */ +			return &tmp_int_td[i]; +	} +	return NULL; +} + +/*------------------------------------------------------------------- + * submits USB interrupt (ie. polling ;-) + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		   int transfer_len, int interval) +{ +	int nint, n; +	unsigned long status, destination; +	unsigned long info, tmp; +	uhci_td_t *mytd; +	if (interval < 0 || interval >= 256) +		return -1; + +	if (interval == 0) +		nint = 0; +	else { +		for (nint = 0, n = 1; nint <= 8; nint++, n += n) {	/* round interval down to 2^n */ +			if (interval < n) { +				interval = n / 2; +				break; +			} +		} +		nint--; +	} + +	USB_UHCI_PRINTF("Rounded interval to %i, chain  %i\n", interval, nint); +	mytd = uhci_alloc_int_td(); +	if (mytd == NULL) { +		printf("No free INT TDs found\n"); +		return -1; +	} +	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | (3 << 27); +/*		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); +*/ + +	destination = +	    (pipe & PIPE_DEVEP_MASK) | usb_packetid(pipe) | +	    (((transfer_len - 1) & 0x7ff) << 21); + +	info = +	    destination | +	    (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << +	     TD_TOKEN_TOGGLE); +	tmp = swap_32(td_int[nint].link); +	usb_fill_td(mytd, tmp, status, info, (unsigned long)buffer, +		    (unsigned long)dev); +	/* Link it */ +	tmp = swap_32((unsigned long)mytd); +	td_int[nint].link = tmp; + +	usb_dotoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + +	return 0; +} + +/********************************************************************** + * Low Level functions + */ + +void reset_hc(void) +{ + +	/* Global reset for 100ms */ +	out16r(usb_base_addr + USBPORTSC1, 0x0204); +	out16r(usb_base_addr + USBPORTSC2, 0x0204); +	out16r(usb_base_addr + USBCMD, USBCMD_GRESET | USBCMD_RS); +	/* Turn off all interrupts */ +	out16r(usb_base_addr + USBINTR, 0); +	wait_ms(50); +	out16r(usb_base_addr + USBCMD, 0); +	wait_ms(10); +} + +void start_hc(void) +{ +	int timeout = 1000; + +	while (in16r(usb_base_addr + USBCMD) & USBCMD_HCRESET) { +		if (!--timeout) { +			printf("USBCMD_HCRESET timed out!\n"); +			break; +		} +	} +	/* Turn on all interrupts */ +	out16r(usb_base_addr + USBINTR, +	       USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP); +	/* Start at frame 0 */ +	out16r(usb_base_addr + USBFRNUM, 0); +	/* set Framebuffer base address */ +	out32r(usb_base_addr + USBFLBASEADD, (unsigned long)&framelist); +	/* Run and mark it configured with a 64-byte max packet */ +	out16r(usb_base_addr + USBCMD, USBCMD_RS | USBCMD_CF | USBCMD_MAXP); +} + +/* Initialize the skeleton + */ +void usb_init_skel(void) +{ +	unsigned long temp; +	int n; + +	for (n = 0; n < USB_MAX_TEMP_INT_TD; n++) +		tmp_int_td[n].dev_ptr = 0L;	/* no devices connected */ +	/* last td */ +	usb_fill_td(&td_last, UHCI_PTR_TERM, TD_CTRL_IOC, USB_PID_OUT, 0, 0L); +	/* usb_fill_td(&td_last,UHCI_PTR_TERM,0,0,0); */ +	/* End Queue Header */ +	usb_fill_qh(&qh_end, UHCI_PTR_TERM, (unsigned long)&td_last); +	/* Bulk Queue Header */ +	temp = (unsigned long)&qh_end; +	usb_fill_qh(&qh_bulk, temp | UHCI_PTR_QH, UHCI_PTR_TERM); +	/* Control Queue Header */ +	temp = (unsigned long)&qh_bulk; +	usb_fill_qh(&qh_cntrl, temp | UHCI_PTR_QH, UHCI_PTR_TERM); +	/* 1ms Interrupt td */ +	temp = (unsigned long)&qh_cntrl; +	usb_fill_td(&td_int[0], temp | UHCI_PTR_QH, 0, USB_PID_OUT, 0, 0L); +	temp = (unsigned long)&td_int[0]; +	for (n = 1; n < 8; n++) +		usb_fill_td(&td_int[n], temp, 0, USB_PID_OUT, 0, 0L); +	for (n = 0; n < 1024; n++) { +		/* link all framelist pointers to one of the interrupts */ +		int m, o; +		if ((n & 127) == 127) +			framelist[n] = swap_32((unsigned long)&td_int[0]); +		else +			for (o = 1, m = 2; m <= 128; o++, m += m) +				if ((n & (m - 1)) == ((m - 1) / 2)) +					framelist[n] = +					    swap_32((unsigned long)&td_int[o]); + +	} +} + +/* check the common skeleton for completed transfers, and update the status + * of the "connected" device. Called from the IRQ routine. + */ +void usb_check_skel(void) +{ +	struct usb_device *dev; +	/* start with the control qh */ +	if (qh_cntrl.dev_ptr != 0) {	/* it's a device assigned check if this caused IRQ */ +		dev = (struct usb_device *)qh_cntrl.dev_ptr; +		/* Flush cache now that hardware updated DATA and TDs/QHs */ +		if (!leon3_snooping_avail) +			sparc_dcache_flush_all(); +		usb_get_td_status(&tmp_td[0], dev);	/* update status */ +		if (!(dev->status & USB_ST_NOT_PROC)) {	/* is not active anymore, disconnect devices */ +			qh_cntrl.dev_ptr = 0; +		} +	} +	/* now process the bulk */ +	if (qh_bulk.dev_ptr != 0) {	/* it's a device assigned check if this caused IRQ */ +		dev = (struct usb_device *)qh_bulk.dev_ptr; +		/* Flush cache now that hardware updated DATA and TDs/QHs */ +		if (!leon3_snooping_avail) +			sparc_dcache_flush_all(); +		usb_get_td_status(&tmp_td[0], dev);	/* update status */ +		if (!(dev->status & USB_ST_NOT_PROC)) {	/* is not active anymore, disconnect devices */ +			qh_bulk.dev_ptr = 0; +		} +	} +} + +/* check the interrupt chain, ubdate the status of the appropriate device, + * call the appropriate irqhandler and reactivate the TD if the irqhandler + * returns with 1 + */ +void usb_check_int_chain(void) +{ +	int i, res; +	unsigned long link, status; +	struct usb_device *dev; +	uhci_td_t *td, *prevtd; + +	for (i = 0; i < 8; i++) { +		prevtd = &td_int[i];	/* the first previous td is the skeleton td */ +		link = swap_32(READ32(&td_int[i].link)) & 0xfffffff0;	/* next in chain */ +		td = (uhci_td_t *) link;	/* assign it */ +		/* all interrupt TDs are finally linked to the td_int[0]. +		 * so we process all until we find the td_int[0]. +		 * if int0 chain points to a QH, we're also done +		 */ +		while (((i > 0) && (link != (unsigned long)&td_int[0])) || +		       ((i == 0) +			&& !(swap_32(READ32(&td->link)) & UHCI_PTR_QH))) { +			/* check if a device is assigned with this td */ +			status = swap_32(READ32(&td->status)); +			if ((td->dev_ptr != 0L) && !(status & TD_CTRL_ACTIVE)) { +				/* td is not active and a device is assigned -> call irqhandler */ +				dev = (struct usb_device *)td->dev_ptr; +				dev->irq_act_len = ((status & 0x7FF) == 0x7FF) ? 0 : (status & 0x7FF) + 1;	/* transfered length */ +				dev->irq_status = usb_uhci_td_stat(status);	/* get status */ +				res = dev->irq_handle(dev);	/* call irqhandler */ +				if (res == 1) { +					/* reactivate */ +					status |= TD_CTRL_ACTIVE; +					td->status = swap_32(status); +					prevtd = td;	/* previous td = this td */ +				} else { +					prevtd->link = READ32(&td->link);	/* link previous td directly to the nex td -> unlinked */ +					/* remove device pointer */ +					td->dev_ptr = 0L; +				} +			}	/* if we call the irq handler */ +			link = swap_32(READ32(&td->link)) & 0xfffffff0;	/* next in chain */ +			td = (uhci_td_t *) link;	/* assign it */ +		}		/* process all td in this int chain */ +	}			/* next interrupt chain */ +} + +/* usb interrupt service routine. + */ +void handle_usb_interrupt(void) +{ +	unsigned short status; +	static int error = 0; + +	/* +	 * Read the interrupt status, and write it back to clear the +	 * interrupt cause +	 */ + +	status = in16r(usb_base_addr + USBSTS); + +	if (!status)		/* shared interrupt, not mine */ +		return; +	if (status != 1) { +		/* remove host controller halted state */ +		if ((status & (USBSTS_HCPE | USBSTS_HCH)) == +		    (USBSTS_HCPE | USBSTS_HCH)) { +			/* Stop due to bug in driver, or hardware */ +			out16r(usb_base_addr + USBSTS, status); +			out16r(usb_base_addr + USBCMD, +			       USBCMD_HCRESET | USBCMD_GRESET); +			printf +			    ("GRUSB: HW detected error(s) in USB Descriptors (STS: 0x%x)\n", +			     status); +			usb_show_td(8); +			return; +		} else if ((status & 0x20) +			   && ((in16r(usb_base_addr + USBCMD) & USBCMD_RS) == +			       0)) { +			if (error < 10) { +				out16r(usb_base_addr + USBCMD, +				       USBCMD_RS | in16r(usb_base_addr + +							 USBCMD)); +				error++; +			} +		} else +			error = 0; +	} +	usb_check_int_chain();	/* call interrupt handlers for int tds */ +	usb_check_skel();	/* call completion handler for common transfer routines */ +	out16r(usb_base_addr + USBSTS, status); +} + +/* init uhci + */ +int usb_lowlevel_init(void) +{ +	unsigned char temp; +	ambapp_ahbdev ahbdev; + +	/* Find GRUSB core using AMBA Plug&Play information */ +	if (ambapp_ahbslv_first(VENDOR_GAISLER, GAISLER_UHCI, &ahbdev) != 1) { +		printf("USB UHCI: Failed to find GRUSB controller\n"); +		return -1; +	} +	usb_base_addr = ahbdev.address[0]; +	grusb_irq = ahbdev.irq; +	/* +	   usb_base_addr = 0xfffa0000; +	   grusb_irq = 10; +	 */ +#ifdef USB_UHCI_DEBUG +	grusb_show_regs(); +#endif +	memset(td_int, 0, sizeof(td_int)); +	memset(tmp_td, 0, sizeof(tmp_td)); +	memset(tmp_int_td, 0, sizeof(tmp_int_td)); +	memset(&qh_cntrl, 0, sizeof(qh_cntrl)); +	memset(&qh_end, 0, sizeof(qh_end)); +	memset(&td_last, 0, sizeof(td_last)); + +	irq_free_handler(grusb_irq); +	USB_UHCI_PRINTF("GRUSB: at 0x%lx irq %d\n", usb_base_addr, grusb_irq); +	rh.devnum = 0; +	usb_init_skel(); +	reset_hc(); +	start_hc(); +	irq_install_handler(grusb_irq, +			    (interrupt_handler_t *) handle_usb_interrupt, NULL); +	return 0; +} + +/* stop uhci + */ +int usb_lowlevel_stop(void) +{ +	if (grusb_irq == -1) +		return 1; +	irq_free_handler(grusb_irq); +	reset_hc(); +	grusb_irq = -1; +	return 0; +} + +/******************************************************************************************* + * Virtual Root Hub + * Since the uhci does not have a real HUB, we simulate one ;-) + */ +#undef	USB_RH_DEBUG + +#ifdef	USB_RH_DEBUG +#define	USB_RH_PRINTF(fmt,args...)	printf (fmt ,##args) +static void usb_display_wValue(unsigned short wValue, unsigned short wIndex); +static void usb_display_Req(unsigned short req); +#else +#define USB_RH_PRINTF(fmt,args...) +static void usb_display_wValue(unsigned short wValue, unsigned short wIndex) +{ +} +static void usb_display_Req(unsigned short req) +{ +} +#endif + +static unsigned char root_hub_dev_des[] = { +	0x12,			/*  __u8  bLength; */ +	0x01,			/*  __u8  bDescriptorType; Device */ +	0x00,			/*  __u16 bcdUSB; v1.0 */ +	0x01, +	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */ +	0x00,			/*  __u8  bDeviceSubClass; */ +	0x00,			/*  __u8  bDeviceProtocol; */ +	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */ +	0x00,			/*  __u16 idVendor; */ +	0x00, +	0x00,			/*  __u16 idProduct; */ +	0x00, +	0x00,			/*  __u16 bcdDevice; */ +	0x00, +	0x01,			/*  __u8  iManufacturer; */ +	0x00,			/*  __u8  iProduct; */ +	0x00,			/*  __u8  iSerialNumber; */ +	0x01			/*  __u8  bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static unsigned char root_hub_config_des[] = { +	0x09,			/*  __u8  bLength; */ +	0x02,			/*  __u8  bDescriptorType; Configuration */ +	0x19,			/*  __u16 wTotalLength; */ +	0x00, +	0x01,			/*  __u8  bNumInterfaces; */ +	0x01,			/*  __u8  bConfigurationValue; */ +	0x00,			/*  __u8  iConfiguration; */ +	0x40,			/*  __u8  bmAttributes; +				   Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ +	0x00,			/*  __u8  MaxPower; */ + +	/* interface */ +	0x09,			/*  __u8  if_bLength; */ +	0x04,			/*  __u8  if_bDescriptorType; Interface */ +	0x00,			/*  __u8  if_bInterfaceNumber; */ +	0x00,			/*  __u8  if_bAlternateSetting; */ +	0x01,			/*  __u8  if_bNumEndpoints; */ +	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */ +	0x00,			/*  __u8  if_bInterfaceSubClass; */ +	0x00,			/*  __u8  if_bInterfaceProtocol; */ +	0x00,			/*  __u8  if_iInterface; */ + +	/* endpoint */ +	0x07,			/*  __u8  ep_bLength; */ +	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */ +	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */ +	0x03,			/*  __u8  ep_bmAttributes; Interrupt */ +	0x08,			/*  __u16 ep_wMaxPacketSize; 8 Bytes */ +	0x00, +	0xff			/*  __u8  ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_hub_des[] = { +	0x09,			/*  __u8  bLength; */ +	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */ +	0x02,			/*  __u8  bNbrPorts; */ +	0x00,			/* __u16  wHubCharacteristics; */ +	0x00, +	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */ +	0x00,			/*  __u8  bHubContrCurrent; 0 mA */ +	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */ +	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */ +}; + +static unsigned char root_hub_str_index0[] = { +	0x04,			/*  __u8  bLength; */ +	0x03,			/*  __u8  bDescriptorType; String-descriptor */ +	0x09,			/*  __u8  lang ID */ +	0x04,			/*  __u8  lang ID */ +}; + +static unsigned char root_hub_str_index1[] = { +	28,			/*  __u8  bLength; */ +	0x03,			/*  __u8  bDescriptorType; String-descriptor */ +	'U',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'H',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'C',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'I',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	' ',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'R',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'o',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'o',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	't',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	' ',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'H',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'u',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +	'b',			/*  __u8  Unicode */ +	0,			/*  __u8  Unicode */ +}; + +/* + * Root Hub Control Pipe (interrupt Pipes are not supported) + */ + +int uhci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		       int transfer_len, struct devrequest *cmd) +{ +	void *data = buffer; +	int leni = transfer_len; +	int len = 0; +	int status = 0; +	int stat = 0; +	int i; + +	unsigned short cstatus; + +	unsigned short bmRType_bReq; +	unsigned short wValue; +	unsigned short wIndex; +	unsigned short wLength; + +	if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { +		printf("Root-Hub submit IRQ: NOT implemented\n"); +		return 0; +	} +	bmRType_bReq = cmd->requesttype | cmd->request << 8; +	wValue = swap_16(cmd->value); +	wIndex = swap_16(cmd->index); +	wLength = swap_16(cmd->length); +	usb_display_Req(bmRType_bReq); +	for (i = 0; i < 8; i++) +		rh.c_p_r[i] = 0; +	USB_RH_PRINTF("Root-Hub: adr: %2x cmd(%1x): %02x%02x %04x %04x %04x\n", +		      dev->devnum, 8, cmd->requesttype, cmd->request, wValue, +		      wIndex, wLength); + +	switch (bmRType_bReq) { +		/* Request Destination: +		   without flags: Device, +		   RH_INTERFACE: interface, +		   RH_ENDPOINT: endpoint, +		   RH_CLASS means HUB here, +		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here +		 */ + +	case RH_GET_STATUS: +		*(unsigned short *)data = swap_16(1); +		len = 2; +		break; +	case RH_GET_STATUS | RH_INTERFACE: +		*(unsigned short *)data = swap_16(0); +		len = 2; +		break; +	case RH_GET_STATUS | RH_ENDPOINT: +		*(unsigned short *)data = swap_16(0); +		len = 2; +		break; +	case RH_GET_STATUS | RH_CLASS: +		*(unsigned long *)data = swap_32(0); +		len = 4; +		break;		/* hub power ** */ +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: + +		status = in16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1)); +		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | +		    ((status & USBPORTSC_PEC) >> (3 - 1)) | +		    (rh.c_p_r[wIndex - 1] << (0 + 4)); +		status = (status & USBPORTSC_CCS) | ((status & USBPORTSC_PE) >> (2 - 1)) | ((status & USBPORTSC_SUSP) >> (12 - 2)) | ((status & USBPORTSC_PR) >> (9 - 4)) | (1 << 8) |	/* power on ** */ +		    ((status & USBPORTSC_LSDA) << (-8 + 9)); + +		*(unsigned short *)data = swap_16(status); +		*(unsigned short *)(data + 2) = swap_16(cstatus); +		len = 4; +		break; +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +		switch (wValue) { +		case (RH_ENDPOINT_STALL): +			len = 0; +			break; +		} +		break; + +	case RH_CLEAR_FEATURE | RH_CLASS: +		switch (wValue) { +		case (RH_C_HUB_OVER_CURRENT): +			len = 0;	/* hub power over current ** */ +			break; +		} +		break; + +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		usb_display_wValue(wValue, wIndex); +		switch (wValue) { +		case (RH_PORT_ENABLE): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) & ~USBPORTSC_PE; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		case (RH_PORT_SUSPEND): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) & ~USBPORTSC_SUSP; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		case (RH_PORT_POWER): +			len = 0;	/* port power ** */ +			break; +		case (RH_C_PORT_CONNECTION): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) | USBPORTSC_CSC; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		case (RH_C_PORT_ENABLE): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) | USBPORTSC_PEC; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		case (RH_C_PORT_SUSPEND): +/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ +			len = 0; +			break; +		case (RH_C_PORT_OVER_CURRENT): +			len = 0; +			break; +		case (RH_C_PORT_RESET): +			rh.c_p_r[wIndex - 1] = 0; +			len = 0; +			break; +		} +		break; +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		usb_display_wValue(wValue, wIndex); +		switch (wValue) { +		case (RH_PORT_SUSPEND): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) | USBPORTSC_SUSP; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		case (RH_PORT_RESET): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) | USBPORTSC_PR; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			wait_ms(10); +			status = (status & 0xfff5) & ~USBPORTSC_PR; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			udelay(10); +			status = (status & 0xfff5) | USBPORTSC_PE; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			wait_ms(10); +			status = (status & 0xfff5) | 0xa; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		case (RH_PORT_POWER): +			len = 0;	/* port power ** */ +			break; +		case (RH_PORT_ENABLE): +			status = +			    in16r(usb_base_addr + USBPORTSC1 + +				  2 * (wIndex - 1)); +			status = (status & 0xfff5) | USBPORTSC_PE; +			out16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1), +			       status); +			len = 0; +			break; +		} +		break; + +	case RH_SET_ADDRESS: +		rh.devnum = wValue; +		len = 0; +		break; +	case RH_GET_DESCRIPTOR: +		switch ((wValue & 0xff00) >> 8) { +		case (0x01):	/* device descriptor */ +			i = sizeof(root_hub_config_des); +			status = i > wLength ? wLength : i; +			len = leni > status ? status : leni; +			memcpy(data, root_hub_dev_des, len); +			break; +		case (0x02):	/* configuration descriptor */ +			i = sizeof(root_hub_config_des); +			status = i > wLength ? wLength : i; +			len = leni > status ? status : leni; +			memcpy(data, root_hub_config_des, len); +			break; +		case (0x03):	/*string descriptors */ +			if (wValue == 0x0300) { +				i = sizeof(root_hub_str_index0); +				status = i > wLength ? wLength : i; +				len = leni > status ? status : leni; +				memcpy(data, root_hub_str_index0, len); +				break; +			} +			if (wValue == 0x0301) { +				i = sizeof(root_hub_str_index1); +				status = i > wLength ? wLength : i; +				len = leni > status ? status : leni; +				memcpy(data, root_hub_str_index1, len); +				break; +			} +			stat = USB_ST_STALLED; +		} +		break; + +	case RH_GET_DESCRIPTOR | RH_CLASS: +		root_hub_hub_des[2] = 2; +		i = sizeof(root_hub_hub_des); +		status = i > wLength ? wLength : i; +		len = leni > status ? status : leni; +		memcpy(data, root_hub_hub_des, len); +		break; +	case RH_GET_CONFIGURATION: +		*(unsigned char *)data = 0x01; +		len = 1; +		break; +	case RH_SET_CONFIGURATION: +		len = 0; +		break; +	default: +		stat = USB_ST_STALLED; +	} +	USB_RH_PRINTF("Root-Hub stat %lx port1: %x port2: %x\n\n", stat, +		      in16r(usb_base_addr + USBPORTSC1), +		      in16r(usb_base_addr + USBPORTSC2)); +	dev->act_len = len; +	dev->status = stat; +	return stat; + +} + +/******************************************************************************** + * Some Debug Routines + */ + +#ifdef	USB_RH_DEBUG + +static void usb_display_Req(unsigned short req) +{ +	USB_RH_PRINTF("- Root-Hub Request: "); +	switch (req) { +	case RH_GET_STATUS: +		USB_RH_PRINTF("Get Status "); +		break; +	case RH_GET_STATUS | RH_INTERFACE: +		USB_RH_PRINTF("Get Status Interface "); +		break; +	case RH_GET_STATUS | RH_ENDPOINT: +		USB_RH_PRINTF("Get Status Endpoint "); +		break; +	case RH_GET_STATUS | RH_CLASS: +		USB_RH_PRINTF("Get Status Class"); +		break;		/* hub power ** */ +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: +		USB_RH_PRINTF("Get Status Class Others"); +		break; +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +		USB_RH_PRINTF("Clear Feature Endpoint "); +		break; +	case RH_CLEAR_FEATURE | RH_CLASS: +		USB_RH_PRINTF("Clear Feature Class "); +		break; +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		USB_RH_PRINTF("Clear Feature Other Class "); +		break; +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		USB_RH_PRINTF("Set Feature Other Class "); +		break; +	case RH_SET_ADDRESS: +		USB_RH_PRINTF("Set Address "); +		break; +	case RH_GET_DESCRIPTOR: +		USB_RH_PRINTF("Get Descriptor "); +		break; +	case RH_GET_DESCRIPTOR | RH_CLASS: +		USB_RH_PRINTF("Get Descriptor Class "); +		break; +	case RH_GET_CONFIGURATION: +		USB_RH_PRINTF("Get Configuration "); +		break; +	case RH_SET_CONFIGURATION: +		USB_RH_PRINTF("Get Configuration "); +		break; +	default: +		USB_RH_PRINTF("****UNKNOWN**** 0x%04X ", req); +	} +	USB_RH_PRINTF("\n"); + +} + +static void usb_display_wValue(unsigned short wValue, unsigned short wIndex) +{ +	switch (wValue) { +	case (RH_PORT_ENABLE): +		USB_RH_PRINTF("Root-Hub: Enable Port %d\n", wIndex); +		break; +	case (RH_PORT_SUSPEND): +		USB_RH_PRINTF("Root-Hub: Suspend Port %d\n", wIndex); +		break; +	case (RH_PORT_POWER): +		USB_RH_PRINTF("Root-Hub: Port Power %d\n", wIndex); +		break; +	case (RH_C_PORT_CONNECTION): +		USB_RH_PRINTF("Root-Hub: C Port Connection Port %d\n", wIndex); +		break; +	case (RH_C_PORT_ENABLE): +		USB_RH_PRINTF("Root-Hub: C Port Enable Port %d\n", wIndex); +		break; +	case (RH_C_PORT_SUSPEND): +		USB_RH_PRINTF("Root-Hub: C Port Suspend Port %d\n", wIndex); +		break; +	case (RH_C_PORT_OVER_CURRENT): +		USB_RH_PRINTF("Root-Hub: C Port Over Current Port %d\n", +			      wIndex); +		break; +	case (RH_C_PORT_RESET): +		USB_RH_PRINTF("Root-Hub: C Port reset Port %d\n", wIndex); +		break; +	default: +		USB_RH_PRINTF("Root-Hub: unknown %x %x\n", wValue, wIndex); +		break; +	} +} + +#endif + +/*#ifdef	USB_UHCI_DEBUG*/ + +static int usb_display_td(uhci_td_t * td) +{ +	unsigned long tmp; +	int valid; + +	printf("TD at %p:\n", td); + +	tmp = swap_32(READ32(&td->link)); +	printf("Link points to 0x%08lX, %s first, %s, %s\n", tmp & 0xfffffff0, +	       ((tmp & 0x4) == 0x4) ? "Depth" : "Breath", +	       ((tmp & 0x2) == 0x2) ? "QH" : "TD", +	       ((tmp & 0x1) == 0x1) ? "invalid" : "valid"); +	valid = ((tmp & 0x1) == 0x0); +	tmp = swap_32(READ32(&td->status)); +	printf +	    ("     %s %ld Errors %s %s %s \n     %s %s %s %s %s %s\n     Len 0x%lX\n", +	     (((tmp >> 29) & 0x1) == 0x1) ? "SPD Enable" : "SPD Disable", +	     ((tmp >> 28) & 0x3), +	     (((tmp >> 26) & 0x1) == 0x1) ? "Low Speed" : "Full Speed", +	     (((tmp >> 25) & 0x1) == 0x1) ? "ISO " : "", +	     (((tmp >> 24) & 0x1) == 0x1) ? "IOC " : "", +	     (((tmp >> 23) & 0x1) == 0x1) ? "Active " : "Inactive ", +	     (((tmp >> 22) & 0x1) == 0x1) ? "Stalled" : "", +	     (((tmp >> 21) & 0x1) == 0x1) ? "Data Buffer Error" : "", +	     (((tmp >> 20) & 0x1) == 0x1) ? "Babble" : "", +	     (((tmp >> 19) & 0x1) == 0x1) ? "NAK" : "", +	     (((tmp >> 18) & 0x1) == 0x1) ? "Bitstuff Error" : "", +	     (tmp & 0x7ff)); +	tmp = swap_32(READ32(&td->info)); +	printf("     MaxLen 0x%lX\n", ((tmp >> 21) & 0x7FF)); +	printf("     %sEndpoint 0x%lX Dev Addr 0x%lX PID 0x%lX\n", +	       ((tmp >> 19) & 0x1) == 0x1 ? "TOGGLE " : "", ((tmp >> 15) & 0xF), +	       ((tmp >> 8) & 0x7F), tmp & 0xFF); +	tmp = swap_32(READ32(&td->buffer)); +	printf("     Buffer 0x%08lX\n", tmp); +	printf("     DEV %08lX\n", td->dev_ptr); +	return valid; +} + +void usb_show_td(int max) +{ +	int i; +	if (max > 0) { +		for (i = 0; i < max; i++) { +			usb_display_td(&tmp_td[i]); +		} +	} else { +		i = 0; +		do { +			printf("tmp_td[%d]\n", i); +		} while (usb_display_td(&tmp_td[i++])); +	} +} + +void grusb_show_regs(void) +{ +	unsigned int tmp; + +	tmp = in16r(usb_base_addr + USBCMD); +	printf(" USBCMD:   0x%04x\n", tmp); +	tmp = in16r(usb_base_addr + USBSTS); +	printf(" USBSTS:   0x%04x\n", tmp); +	tmp = in16r(usb_base_addr + USBINTR); +	printf(" USBINTR:   0x%04x\n", tmp); +	tmp = in16r(usb_base_addr + USBFRNUM); +	printf(" FRNUM:   0x%04x\n", tmp); +	tmp = in32r(usb_base_addr + USBFLBASEADD); +	printf(" FLBASEADD:   0x%08x\n", tmp); +	tmp = in16r(usb_base_addr + USBSOF); +	printf(" SOFMOD:   0x%04x\n", tmp); +	tmp = in16r(usb_base_addr + USBPORTSC1); +	printf(" PORTSC1:   0x%04x\n", tmp); +} + +/*#endif*/ +#endif				/* CONFIG_USB_UHCI */ + +/* EOF */ diff --git a/cpu/leon3/usb_uhci.h b/cpu/leon3/usb_uhci.h new file mode 100644 index 000000000..bf572a661 --- /dev/null +++ b/cpu/leon3/usb_uhci.h @@ -0,0 +1,184 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * 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 + * + * Note: Part of this code has been derived from linux + * + */ +#ifndef _USB_UHCI_H_ +#define _USB_UHCI_H_ + +/* Command register */ +#define USBCMD		0 +#define   USBCMD_RS       0x0001	/* Run/Stop */ +#define   USBCMD_HCRESET  0x0002	/* Host reset */ +#define   USBCMD_GRESET   0x0004	/* Global reset */ +#define   USBCMD_EGSM     0x0008	/* Global Suspend Mode */ +#define   USBCMD_FGR      0x0010	/* Force Global Resume */ +#define   USBCMD_SWDBG    0x0020	/* SW Debug mode */ +#define   USBCMD_CF       0x0040	/* Config Flag (sw only) */ +#define   USBCMD_MAXP     0x0080	/* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS		2 +#define   USBSTS_USBINT   0x0001	/* Interrupt due to IOC */ +#define   USBSTS_ERROR    0x0002	/* Interrupt due to error */ +#define   USBSTS_RD       0x0004	/* Resume Detect */ +#define   USBSTS_HSE      0x0008	/* Host System Error - basically PCI problems */ +#define   USBSTS_HCPE     0x0010	/* Host Controller Process Error - the scripts were buggy */ +#define   USBSTS_HCH      0x0020	/* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR		4 +#define   USBINTR_TIMEOUT 0x0001	/* Timeout/CRC error enable */ +#define   USBINTR_RESUME  0x0002	/* Resume interrupt enable */ +#define   USBINTR_IOC     0x0004	/* Interrupt On Complete enable */ +#define   USBINTR_SP      0x0008	/* Short packet interrupt enable */ + +#define USBFRNUM      6 +#define USBFLBASEADD  8 +#define USBSOF        12 + +/* USB port status and control registers */ +#define USBPORTSC1	16 +#define USBPORTSC2	18 +#define   USBPORTSC_CCS   0x0001	/* Current Connect Status ("device present") */ +#define   USBPORTSC_CSC   0x0002	/* Connect Status Change */ +#define   USBPORTSC_PE    0x0004	/* Port Enable */ +#define   USBPORTSC_PEC   0x0008	/* Port Enable Change */ +#define   USBPORTSC_LS    0x0030	/* Line Status */ +#define   USBPORTSC_RD    0x0040	/* Resume Detect */ +#define   USBPORTSC_LSDA  0x0100	/* Low Speed Device Attached */ +#define   USBPORTSC_PR    0x0200	/* Port Reset */ +#define   USBPORTSC_SUSP  0x1000	/* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000	/* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7ff	/* for UHCI controller TD */ +#define UHCI_PID            0xff	/* PID MASK */ + +#define UHCI_PTR_BITS       0x000F +#define UHCI_PTR_TERM       0x0001 +#define UHCI_PTR_QH         0x0002 +#define UHCI_PTR_DEPTH      0x0004 + +/* for TD <status>: */ +#define TD_CTRL_SPD         (1 << 29)	/* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK  (3 << 27)	/* Error Counter bits */ +#define TD_CTRL_LS          (1 << 26)	/* Low Speed Device */ +#define TD_CTRL_IOS         (1 << 25)	/* Isochronous Select */ +#define TD_CTRL_IOC         (1 << 24)	/* Interrupt on Complete */ +#define TD_CTRL_ACTIVE      (1 << 23)	/* TD Active */ +#define TD_CTRL_STALLED     (1 << 22)	/* TD Stalled */ +#define TD_CTRL_DBUFERR     (1 << 21)	/* Data Buffer Error */ +#define TD_CTRL_BABBLE      (1 << 20)	/* Babble Detected */ +#define TD_CTRL_NAK         (1 << 19)	/* NAK Received */ +#define TD_CTRL_CRCTIMEO    (1 << 18)	/* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF    (1 << 17)	/* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7ff	/* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ +				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define TD_TOKEN_TOGGLE		19 + +/* ------------------------------------------------------------------------------------ +   Virtual Root HUB +   ------------------------------------------------------------------------------------ */ +/* destination of request */ +#define RH_INTERFACE               0x01 +#define RH_ENDPOINT                0x02 +#define RH_OTHER                   0x03 + +#define RH_CLASS                   0x20 +#define RH_VENDOR                  0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS           0x0080 +#define RH_CLEAR_FEATURE        0x0100 +#define RH_SET_FEATURE          0x0300 +#define RH_SET_ADDRESS          0x0500 +#define RH_GET_DESCRIPTOR       0x0680 +#define RH_SET_DESCRIPTOR       0x0700 +#define RH_GET_CONFIGURATION    0x0880 +#define RH_SET_CONFIGURATION    0x0900 +#define RH_GET_STATE            0x0280 +#define RH_GET_INTERFACE        0x0A80 +#define RH_SET_INTERFACE        0x0B00 +#define RH_SYNC_FRAME           0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP               0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION         0x00 +#define RH_PORT_ENABLE             0x01 +#define RH_PORT_SUSPEND            0x02 +#define RH_PORT_OVER_CURRENT       0x03 +#define RH_PORT_RESET              0x04 +#define RH_PORT_POWER              0x08 +#define RH_PORT_LOW_SPEED          0x09 +#define RH_C_PORT_CONNECTION       0x10 +#define RH_C_PORT_ENABLE           0x11 +#define RH_C_PORT_SUSPEND          0x12 +#define RH_C_PORT_OVER_CURRENT     0x13 +#define RH_C_PORT_RESET            0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER       0x00 +#define RH_C_HUB_OVER_CURRENT      0x01 + +#define RH_DEVICE_REMOTE_WAKEUP    0x00 +#define RH_ENDPOINT_STALL          0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP               0x00 + +#define RH_ACK                     0x01 +#define RH_REQ_ERR                 -1 +#define RH_NACK                    0x00 + +/* Transfer descriptor structure */ +typedef struct { +	unsigned long link;	/* next td/qh (LE) */ +	unsigned long status;	/* status of the td */ +	unsigned long info;	/* Max Lenght / Endpoint / device address and PID */ +	unsigned long buffer;	/* pointer to data buffer (LE) */ +	unsigned long dev_ptr;	/* pointer to the assigned device (BE) */ +	unsigned long res[3];	/* reserved (TDs must be 8Byte aligned) */ +} uhci_td_t, *puhci_td_t; + +/* Queue Header structure */ +typedef struct { +	unsigned long head;	/* Next QH (LE) */ +	unsigned long element;	/* Queue element pointer (LE) */ +	unsigned long res[5];	/* reserved */ +	unsigned long dev_ptr;	/* if 0 no tds have been assigned to this qh */ +} uhci_qh_t, *puhci_qh_t; + +struct virt_root_hub { +	int devnum;		/* Address of Root Hub endpoint */ +	int numports;		/* number of ports */ +	int c_p_r[8];		/* C_PORT_RESET */ +}; + +#endif				/* _USB_UHCI_H_ */ diff --git a/include/ambapp.h b/include/ambapp.h new file mode 100644 index 000000000..1e49d896c --- /dev/null +++ b/include/ambapp.h @@ -0,0 +1,380 @@ +/* Interface for accessing Gaisler AMBA Plug&Play Bus. + * The AHB bus can be interfaced with a simpler bus - + * the APB bus, also freely available in GRLIB at + * www.gaisler.com. + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com. + * + * 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 + * + */ + +#ifndef __AMBAPP_H__ +#define __AMBAPP_H__ + +/* Default location of Plug&Play info + * normally 0xfffff000 for AHB masters + * and 0xfffff800 for AHB slaves. + * Normally no need to change this. + */ +#define LEON3_IO_AREA 0xfff00000 +#define LEON3_CONF_AREA  0xff000 +#define LEON3_AHB_SLAVE_CONF_AREA (1 << 11) + +/* Max devices this software will support */ +#define LEON3_AHB_MASTERS 16 +#define LEON3_AHB_SLAVES 16 +				/*#define LEON3_APB_MASTERS 1*//* Number of APB buses that has Plug&Play */ +#define LEON3_APB_SLAVES 16	/* Total number of APB slaves per APB bus */ + +/* Vendor codes */ +#define VENDOR_GAISLER       1 +#define VENDOR_PENDER        2 +#define VENDOR_ESA           4 +#define VENDOR_ASTRIUM       6 +#define VENDOR_OPENCHIP      7 +#define VENDOR_OPENCORES     8 +#define VENDOR_CONTRIB       9 +#define VENDOR_EONIC         11 +#define VENDOR_RADIONOR      15 +#define VENDOR_GLEICHMANN    16 +#define VENDOR_MENTA         17 +#define VENDOR_SUN           19 +#define VENDOR_EMBEDDIT      234 +#define VENDOR_CAL           202 + +/* Gaisler Research device id's */ +#define GAISLER_LEON3    0x003 +#define GAISLER_LEON3DSU 0x004 +#define GAISLER_ETHAHB   0x005 +#define GAISLER_APBMST   0x006 +#define GAISLER_AHBUART  0x007 +#define GAISLER_SRCTRL   0x008 +#define GAISLER_SDCTRL   0x009 +#define GAISLER_APBUART  0x00C +#define GAISLER_IRQMP    0x00D +#define GAISLER_AHBRAM   0x00E +#define GAISLER_GPTIMER  0x011 +#define GAISLER_PCITRG   0x012 +#define GAISLER_PCISBRG  0x013 +#define GAISLER_PCIFBRG  0x014 +#define GAISLER_PCITRACE 0x015 +#define GAISLER_PCIDMA   0x016 +#define GAISLER_AHBTRACE 0x017 +#define GAISLER_ETHDSU   0x018 +#define GAISLER_PIOPORT  0x01A +#define GAISLER_AHBJTAG  0x01c +#define GAISLER_SPW      0x01f +#define GAISLER_ATACTRL  0x024 +#define GAISLER_VGA      0x061 +#define GAISLER_KBD      0X060 +#define GAISLER_ETHMAC   0x01D +#define GAISLER_DDRSPA   0x025 +#define GAISLER_EHCI     0x026 +#define GAISLER_UHCI     0x027 +#define GAISLER_SPW2     0x029 +#define GAISLER_DDR2SPA  0x02E +#define GAISLER_AHBSTAT  0x052 +#define GAISLER_FTMCTRL  0x054 + +#define GAISLER_L2TIME   0xffd	/* internal device: leon2 timer */ +#define GAISLER_L2C      0xffe	/* internal device: leon2compat */ +#define GAISLER_PLUGPLAY 0xfff	/* internal device: plug & play configarea */ + +/* European Space Agency device id's */ +#define ESA_LEON2        0x2 +#define ESA_MCTRL        0xF + +/* Opencores device id's */ +#define OPENCORES_PCIBR  0x4 +#define OPENCORES_ETHMAC 0x5 + +/* Vendor codes */ + +/* + * + * Macros for manipulating Configuration registers + * + */ + +#define amba_vendor(x) (((x) >> 24) & 0xff) + +#define amba_device(x) (((x) >> 12) & 0xfff) + +#define amba_membar_start(mbar) \ + (((mbar) & 0xfff00000) & (((mbar) & 0xfff0) << 16)) + +#define amba_iobar_start(base, iobar) \ + ((base) | ((((iobar) & 0xfff00000)>>12) & (((iobar) & 0xfff0)<<4)) ) + +#define amba_irq(conf) ((conf) & 0xf) + +#define amba_ver(conf) (((conf)>>5) & 0x1f) + +#define amba_membar_type(mbar) ((mbar) & 0xf) + +#define amba_membar_mask(mbar) (((mbar)>>4) & 0xfff) + +#define AMBA_TYPE_APBIO 0x1 +#define AMBA_TYPE_MEM   0x2 +#define AMBA_TYPE_AHBIO 0x3 + +#define AMBA_TYPE_AHBIO_ADDR(addr) (LEON3_IO_AREA | ((addr) >> 12)) + +#ifndef __ASSEMBLER__ + +/* + *  Types and structure used for AMBA Plug & Play bus scanning + */ + +/* AMBA Plug&Play AHB information layout */ +typedef struct { +	unsigned int conf; +	unsigned int userdef[3]; +	unsigned int bars[4]; +} ahbctrl_pp_dev; + +/* Prototypes for scanning AMBA Plug&Play bus for AMBA + *  i)   AHB Masters + *  ii)  AHB Slaves + *  iii) APB Slaves (APB MST is a AHB Slave) + */ + +typedef struct { +	unsigned char irq; +	unsigned char ver; +	unsigned int address; +} ambapp_apbdev; + +typedef struct { +	unsigned char irq; +	unsigned char ver; +	unsigned int userdef[3]; +	unsigned int address[4]; +} ambapp_ahbdev; + +/* AMBA Plug&Play AHB Masters & Slaves information locations + * Max devices is 64 supported by HW, however often only 8 + * are used. + */ +typedef struct { +	ahbctrl_pp_dev masters[64]; +	ahbctrl_pp_dev slaves[64]; +} ahbctrl_info; + +/* AMBA Plug&Play AHB information layout */ +typedef struct { +	unsigned int conf; +	unsigned int bar; +} apbctrl_pp_dev; + +/* All functions return the number of found devices + * 0 = no devices found + */ + +/****************************** APB SLAVES ******************************/ +int ambapp_apb_count(unsigned int vendor, unsigned int driver); + +int ambapp_apb_first(unsigned int vendor, +		     unsigned int driver, ambapp_apbdev * dev); + +int ambapp_apb_next(unsigned int vendor, +		    unsigned int driver, ambapp_apbdev * dev, int index); + +int ambapp_apbs_first(unsigned int vendor, +		      unsigned int driver, ambapp_apbdev * dev, int max_cnt); + +/****************************** AHB MASTERS ******************************/ +int ambapp_ahbmst_count(unsigned int vendor, unsigned int driver); + +int ambapp_ahbmst_first(unsigned int vendor, +			unsigned int driver, ambapp_ahbdev * dev); + +int ambapp_ahbmst_next(unsigned int vendor, +		       unsigned int driver, ambapp_ahbdev * dev, int index); + +int ambapp_ahbmsts_first(unsigned int vendor, +			 unsigned int driver, ambapp_ahbdev * dev, int max_cnt); + +/****************************** AHB SLAVES ******************************/ +int ambapp_ahbslv_count(unsigned int vendor, unsigned int driver); + +int ambapp_ahbslv_first(unsigned int vendor, +			unsigned int driver, ambapp_ahbdev * dev); + +int ambapp_ahbslv_next(unsigned int vendor, +		       unsigned int driver, ambapp_ahbdev * dev, int index); + +int ambapp_ahbslvs_first(unsigned int vendor, +			 unsigned int driver, ambapp_ahbdev * dev, int max_cnt); + +/*************************** AHB/APB only regs functions ************************* + * During start up, no memory is available we can use the simplified functions + * to get to the memory controller. + * + * Functions uses no stack/memory, only registers. + */ +unsigned int ambapp_apb_next_nomem(register unsigned int vendor,	/* Plug&Play Vendor ID */ +				   register unsigned int driver,	/* Plug&Play Device ID */ +				   register int index); + +ahbctrl_pp_dev *ambapp_ahb_next_nomem(register unsigned int vendor,	/* Plug&Play Vendor ID */ +				      register unsigned int driver,	/* Plug&Play Device ID */ +				      register unsigned int opts,	/* scan for AHB 1=slave, 0=masters */ +				      register int index); + +unsigned int ambapp_ahb_get_info(ahbctrl_pp_dev * ahb, int info); + +/*************************** AMBA Plug&Play device register MAPS *****************/ + +/* + *  The following defines the bits in the LEON UART Status Registers. + */ + +#define LEON_REG_UART_STATUS_DR   0x00000001	/* Data Ready */ +#define LEON_REG_UART_STATUS_TSE  0x00000002	/* TX Send Register Empty */ +#define LEON_REG_UART_STATUS_THE  0x00000004	/* TX Hold Register Empty */ +#define LEON_REG_UART_STATUS_BR   0x00000008	/* Break Error */ +#define LEON_REG_UART_STATUS_OE   0x00000010	/* RX Overrun Error */ +#define LEON_REG_UART_STATUS_PE   0x00000020	/* RX Parity Error */ +#define LEON_REG_UART_STATUS_FE   0x00000040	/* RX Framing Error */ +#define LEON_REG_UART_STATUS_ERR  0x00000078	/* Error Mask */ + +/* + *  The following defines the bits in the LEON UART Ctrl Registers. + */ + +#define LEON_REG_UART_CTRL_RE     0x00000001	/* Receiver enable */ +#define LEON_REG_UART_CTRL_TE     0x00000002	/* Transmitter enable */ +#define LEON_REG_UART_CTRL_RI     0x00000004	/* Receiver interrupt enable */ +#define LEON_REG_UART_CTRL_TI     0x00000008	/* Transmitter interrupt enable */ +#define LEON_REG_UART_CTRL_PS     0x00000010	/* Parity select */ +#define LEON_REG_UART_CTRL_PE     0x00000020	/* Parity enable */ +#define LEON_REG_UART_CTRL_FL     0x00000040	/* Flow control enable */ +#define LEON_REG_UART_CTRL_LB     0x00000080	/* Loop Back enable */ +#define LEON_REG_UART_CTRL_DBG    (1<<11)	/* Debug Bit used by GRMON */ + +#define LEON3_GPTIMER_EN 1 +#define LEON3_GPTIMER_RL 2 +#define LEON3_GPTIMER_LD 4 +#define LEON3_GPTIMER_IRQEN 8 + +/* + *  The following defines the bits in the LEON PS/2 Status Registers. + */ + +#define LEON_REG_PS2_STATUS_DR   0x00000001	/* Data Ready */ +#define LEON_REG_PS2_STATUS_PE   0x00000002	/* Parity error */ +#define LEON_REG_PS2_STATUS_FE   0x00000004	/* Framing error */ +#define LEON_REG_PS2_STATUS_KI   0x00000008	/* Keyboard inhibit */ + +/* + *  The following defines the bits in the LEON PS/2 Ctrl Registers. + */ + +#define LEON_REG_PS2_CTRL_RE     0x00000001	/* Receiver enable */ +#define LEON_REG_PS2_CTRL_TE     0x00000002	/* Transmitter enable */ +#define LEON_REG_PS2_CTRL_RI     0x00000004	/* Keyboard receive interrupt  */ +#define LEON_REG_PS2_CTRL_TI     0x00000008	/* Keyboard transmit interrupt */ + +typedef struct { +	volatile unsigned int ilevel; +	volatile unsigned int ipend; +	volatile unsigned int iforce; +	volatile unsigned int iclear; +	volatile unsigned int mstatus; +	volatile unsigned int notused[11]; +	volatile unsigned int cpu_mask[16]; +	volatile unsigned int cpu_force[16]; +} ambapp_dev_irqmp; + +typedef struct { +	volatile unsigned int data; +	volatile unsigned int status; +	volatile unsigned int ctrl; +	volatile unsigned int scaler; +} ambapp_dev_apbuart; + +typedef struct { +	volatile unsigned int val; +	volatile unsigned int rld; +	volatile unsigned int ctrl; +	volatile unsigned int unused; +} ambapp_dev_gptimer_element; + +#define LEON3_GPTIMER_CTRL_EN	0x1	/* Timer enable */ +#define LEON3_GPTIMER_CTRL_RS	0x2	/* Timer reStart  */ +#define LEON3_GPTIMER_CTRL_LD	0x4	/* Timer reLoad */ +#define LEON3_GPTIMER_CTRL_IE	0x8	/* interrupt enable */ +#define LEON3_GPTIMER_CTRL_IP	0x10	/* interrupt flag/pending */ +#define LEON3_GPTIMER_CTRL_CH	0x20	/* Chain with previous timer */ + +typedef struct { +	volatile unsigned int scalar; +	volatile unsigned int scalar_reload; +	volatile unsigned int config; +	volatile unsigned int unused; +	volatile ambapp_dev_gptimer_element e[8]; +} ambapp_dev_gptimer; + +typedef struct { +	volatile unsigned int iodata; +	volatile unsigned int ioout; +	volatile unsigned int iodir; +	volatile unsigned int irqmask; +	volatile unsigned int irqpol; +	volatile unsigned int irqedge; +} ambapp_dev_ioport; + +typedef struct { +	volatile unsigned int write; +	volatile unsigned int dummy; +	volatile unsigned int txcolor; +	volatile unsigned int bgcolor; +} ambapp_dev_textvga; + +typedef struct { +	volatile unsigned int data; +	volatile unsigned int status; +	volatile unsigned int ctrl; +} ambapp_dev_apbps2; + +typedef struct { +	unsigned int mcfg1, mcfg2, mcfg3; +} ambapp_dev_mctrl; + +typedef struct { +	unsigned int sdcfg; +} ambapp_dev_sdctrl; + +typedef struct { +	unsigned int cfg1; +	unsigned int cfg2; +	unsigned int cfg3; +} ambapp_dev_ddr2spa; + +typedef struct { +	unsigned int ctrl; +	unsigned int cfg; +} ambapp_dev_ddrspa; + +#endif + +#endif diff --git a/include/asm-sparc/arch-leon3/asi.h b/include/asm-sparc/arch-leon3/asi.h new file mode 100644 index 000000000..700b3caa5 --- /dev/null +++ b/include/asm-sparc/arch-leon3/asi.h @@ -0,0 +1,36 @@ +/* asi.h:  Address Space Identifier values for the LEON3 sparc. + * + * Copyright (C) 2008 Daniel Hellstrom (daniel@gaisler.com) + * + * 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 + * + */ + +#ifndef _LEON3_ASI_H +#define _LEON3_ASI_H + +#define ASI_CACHEMISS		0x01	/* Force D-Cache miss on load (lda) */ +#define ASI_M_FLUSH_PROBE	0x03	/* MMU Flush/Probe */ +#define ASI_IFLUSH		0x10	/* Flush I-Cache */ +#define ASI_DFLUSH		0x11	/* Flush D-Cache */ +#define ASI_BYPASS		0x1c	/* Bypass MMU (Physical address) */ +#define ASI_MMUFLUSH		0x18	/* FLUSH TLB */ +#define ASI_M_MMUREGS		0x19	/* READ/Write MMU Registers */ + +#endif	/* _LEON3_ASI_H */ diff --git a/include/asm-sparc/leon.h b/include/asm-sparc/leon.h new file mode 100644 index 000000000..6036fb5e7 --- /dev/null +++ b/include/asm-sparc/leon.h @@ -0,0 +1,38 @@ +/* LEON Header File select + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.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. + * + * 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 + * + */ + +#ifndef __ASM_LEON_H__ +#define __ASM_LEON_H__ + +#if defined(CONFIG_LEON3) + +#include <asm/leon3.h> + +#else + +#error Unknown LEON processor + +#endif + +/* Common stuff */ + +#endif diff --git a/include/asm-sparc/leon3.h b/include/asm-sparc/leon3.h new file mode 100644 index 000000000..84d0e2ed2 --- /dev/null +++ b/include/asm-sparc/leon3.h @@ -0,0 +1,37 @@ +/* LEON3 header file. LEON3 is a free GPL SOC processor available + * at www.gaisler.com. + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.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. + * + * 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 + * + */ + +#ifndef __LEON3_H__ +#define __LEON3_H__ + +#ifndef CONFIG_LEON3 +#error Include LEON3 header file only if LEON3 processor +#endif + +/* Not much to define, most is Plug and Play and GRLIB dependent  + * not LEON3 dependent. See <ambapp.h> for GRLIB timers, interrupt  + * ctrl, memory controllers etc. + */ + + +#endif diff --git a/include/asm-sparc/processor.h b/include/asm-sparc/processor.h index 31b44c546..d518389ad 100644 --- a/include/asm-sparc/processor.h +++ b/include/asm-sparc/processor.h @@ -26,8 +26,15 @@  #include <asm/arch/asi.h> -/* Includeprocessor specific header file here */ +#ifdef CONFIG_LEON + +/* All LEON processors supported */ +#include <asm/leon.h> + +#else +/* other processors */  #error Unknown SPARC Processor +#endif  #ifndef __ASSEMBLY__ |