diff options
Diffstat (limited to 'roms/u-boot/arch/nios2/cpu')
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/Makefile | 11 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/cpu.c | 49 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/epcs.c | 717 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/exceptions.S | 139 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/fdt.c | 36 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/interrupts.c | 264 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/start.S | 186 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/sysid.c | 42 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/traps.c | 26 | ||||
| -rw-r--r-- | roms/u-boot/arch/nios2/cpu/u-boot.lds | 117 | 
10 files changed, 1587 insertions, 0 deletions
diff --git a/roms/u-boot/arch/nios2/cpu/Makefile b/roms/u-boot/arch/nios2/cpu/Makefile new file mode 100644 index 00000000..bdd983d3 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/Makefile @@ -0,0 +1,11 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +extra-y	= start.o +obj-y	= exceptions.o +obj-y	+= cpu.o interrupts.o sysid.o traps.o epcs.o +obj-y	+= fdt.o diff --git a/roms/u-boot/arch/nios2/cpu/cpu.c b/roms/u-boot/arch/nios2/cpu/cpu.c new file mode 100644 index 00000000..e0dcbc20 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/cpu.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <nios2.h> +#include <nios2-io.h> +#include <asm/cache.h> + +#if defined (CONFIG_SYS_NIOS_SYSID_BASE) +extern void display_sysid (void); +#endif /* CONFIG_SYS_NIOS_SYSID_BASE */ + +int checkcpu (void) +{ +	printf ("CPU   : Nios-II\n"); +#if !defined(CONFIG_SYS_NIOS_SYSID_BASE) +	printf ("SYSID : <unknown>\n"); +#else +	display_sysid (); +#endif +	return (0); +} + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	disable_interrupts(); +	/* indirect call to go beyond 256MB limitation of toolchain */ +	nios2_callr(CONFIG_SYS_RESET_ADDR); +	return 0; +} + +int dcache_status(void) +{ +	return 1; +} + +void dcache_enable(void) +{ +	flush_dcache(CONFIG_SYS_DCACHE_SIZE, CONFIG_SYS_DCACHELINE_SIZE); +} + +void dcache_disable(void) +{ +	flush_dcache(CONFIG_SYS_DCACHE_SIZE, CONFIG_SYS_DCACHELINE_SIZE); +} diff --git a/roms/u-boot/arch/nios2/cpu/epcs.c b/roms/u-boot/arch/nios2/cpu/epcs.c new file mode 100644 index 00000000..97585524 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/epcs.c @@ -0,0 +1,717 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#if defined(CONFIG_SYS_NIOS_EPCSBASE) +#include <command.h> +#include <asm/io.h> +#include <nios2-io.h> +#include <nios2-epcs.h> + + +/*-----------------------------------------------------------------------*/ +#define SHORT_HELP\ +	"epcs    - read/write Cyclone EPCS configuration device.\n" + +#define LONG_HELP\ +	"\n"\ +	"epcs erase start [end]\n"\ +	"    - erase sector start or sectors start through end.\n"\ +	"epcs info\n"\ +	"    - display EPCS device information.\n"\ +	"epcs protect on | off\n"\ +	"    - turn device protection on or off.\n"\ +	"epcs read addr offset count\n"\ +	"    - read count bytes from offset to addr.\n"\ +	"epcs write addr offset count\n"\ +	"    - write count bytes to offset from addr.\n"\ +	"epcs verify addr offset count\n"\ +	"    - verify count bytes at offset from addr." + + +/*-----------------------------------------------------------------------*/ +/* Operation codes for serial configuration devices + */ +#define EPCS_WRITE_ENA		0x06	/* Write enable */ +#define EPCS_WRITE_DIS		0x04	/* Write disable */ +#define EPCS_READ_STAT		0x05	/* Read status */ +#define EPCS_READ_BYTES		0x03	/* Read bytes */ +#define EPCS_READ_ID		0xab	/* Read silicon id */ +#define EPCS_WRITE_STAT		0x01	/* Write status */ +#define EPCS_WRITE_BYTES	0x02	/* Write bytes */ +#define EPCS_ERASE_BULK		0xc7	/* Erase entire device */ +#define EPCS_ERASE_SECT		0xd8	/* Erase sector */ + +/* Device status register bits + */ +#define EPCS_STATUS_WIP		(1<<0)	/* Write in progress */ +#define EPCS_STATUS_WEL		(1<<1)	/* Write enable latch */ + +/* Misc + */ +#define EPCS_TIMEOUT		100	/* 100 msec timeout */ + +static nios_spi_t *epcs = (nios_spi_t *)CONFIG_SYS_NIOS_EPCSBASE; + +/*********************************************************************** + * Device access + ***********************************************************************/ +static int epcs_cs (int assert) +{ +	ulong start; +	unsigned tmp; + + +	if (assert) { +		tmp = readl (&epcs->control); +		writel (tmp | NIOS_SPI_SSO, &epcs->control); +	} else { +		/* Let all bits shift out */ +		start = get_timer (0); +		while ((readl (&epcs->status) & NIOS_SPI_TMT) == 0) +			if (get_timer (start) > EPCS_TIMEOUT) +				return (-1); +		tmp = readl (&epcs->control); +		writel (tmp & ~NIOS_SPI_SSO, &epcs->control); +	} +	return (0); +} + +static int epcs_tx (unsigned char c) +{ +	ulong start; + +	start = get_timer (0); +	while ((readl (&epcs->status) & NIOS_SPI_TRDY) == 0) +		if (get_timer (start) > EPCS_TIMEOUT) +			return (-1); +	writel (c, &epcs->txdata); +	return (0); +} + +static int epcs_rx (void) +{ +	ulong start; + +	start = get_timer (0); +	while ((readl (&epcs->status) & NIOS_SPI_RRDY) == 0) +		if (get_timer (start) > EPCS_TIMEOUT) +			return (-1); +	return (readl (&epcs->rxdata)); +} + +static unsigned char bitrev[] = { +	0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, +	0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f +}; + +static unsigned char epcs_bitrev (unsigned char c) +{ +	unsigned char val; + +	val  = bitrev[c>>4]; +	val |= bitrev[c & 0x0f]<<4; +	return (val); +} + +static void epcs_rcv (unsigned char *dst, int len) +{ +	while (len--) { +		epcs_tx (0); +		*dst++ = epcs_rx (); +	} +} + +static void epcs_rrcv (unsigned char *dst, int len) +{ +	while (len--) { +		epcs_tx (0); +		*dst++ = epcs_bitrev (epcs_rx ()); +	} +} + +static void epcs_snd (unsigned char *src, int len) +{ +	while (len--) { +		epcs_tx (*src++); +		epcs_rx (); +	} +} + +static void epcs_rsnd (unsigned char *src, int len) +{ +	while (len--) { +		epcs_tx (epcs_bitrev (*src++)); +		epcs_rx (); +	} +} + +static void epcs_wr_enable (void) +{ +	epcs_cs (1); +	epcs_tx (EPCS_WRITE_ENA); +	epcs_rx (); +	epcs_cs (0); +} + +static unsigned char epcs_status_rd (void) +{ +	unsigned char status; + +	epcs_cs (1); +	epcs_tx (EPCS_READ_STAT); +	epcs_rx (); +	epcs_tx (0); +	status = epcs_rx (); +	epcs_cs (0); +	return (status); +} + +static void epcs_status_wr (unsigned char status) +{ +	epcs_wr_enable (); +	epcs_cs (1); +	epcs_tx (EPCS_WRITE_STAT); +	epcs_rx (); +	epcs_tx (status); +	epcs_rx (); +	epcs_cs (0); +	return; +} + +/*********************************************************************** + * Device information + ***********************************************************************/ + +static struct epcs_devinfo_t devinfo[] = { +	{ "EPCS1 ", 0x10, 17, 4, 15, 8, 0x0c }, +	{ "EPCS4 ", 0x12, 19, 8, 16, 8, 0x1c }, +	{ "EPCS16", 0x14, 21, 32, 16, 8, 0x1c }, +	{ "EPCS64", 0x16, 23,128, 16, 8, 0x1c }, +	{ 0, 0, 0, 0, 0, 0 } +}; + +int epcs_reset (void) +{ +	/* When booting from an epcs controller, the epcs bootrom +	 * code may leave the slave select in an asserted state. +	 * This causes two problems: (1) The initial epcs access +	 * will fail -- not a big deal, and (2) a software reset +	 * will cause the bootrom code to hang since it does not +	 * ensure the select is negated prior to first access -- a +	 * big deal. Here we just negate chip select and everything +	 * gets better :-) +	 */ +	epcs_cs (0); /* Negate chip select */ +	return (0); +} + +epcs_devinfo_t *epcs_dev_find (void) +{ +	unsigned char buf[4]; +	unsigned char id; +	int i; +	struct epcs_devinfo_t *dev = NULL; + +	/* Read silicon id requires 3 "dummy bytes" before it's put +	 * on the wire. +	 */ +	buf[0] = EPCS_READ_ID; +	buf[1] = 0; +	buf[2] = 0; +	buf[3] = 0; + +	epcs_cs (1); +	epcs_snd (buf,4); +	epcs_rcv (buf,1); +	if (epcs_cs (0) == -1) +		return (NULL); +	id = buf[0]; + +	/* Find the info struct */ +	i = 0; +	while (devinfo[i].name) { +		if (id == devinfo[i].id) { +			dev = &devinfo[i]; +			break; +		} +		i++; +	} + +	return (dev); +} + +/*********************************************************************** + * Misc Utilities + ***********************************************************************/ +int epcs_cfgsz (void) +{ +	int sz = 0; +	unsigned char buf[128]; +	unsigned char *p; +	struct epcs_devinfo_t *dev = epcs_dev_find (); + +	if (!dev) +		return (-1); + +	/* Read in the first 128 bytes of the device */ +	buf[0] = EPCS_READ_BYTES; +	buf[1] = 0; +	buf[2] = 0; +	buf[3] = 0; + +	epcs_cs (1); +	epcs_snd (buf,4); +	epcs_rrcv (buf, sizeof(buf)); +	epcs_cs (0); + +	/* Search for the starting 0x6a which is followed by the +	 * 4-byte 'register' and 4-byte bit-count. +	 */ +	p = buf; +	while (p < buf + sizeof(buf)-8) { +		if ( *p == 0x6a ) { +			/* Point to bit count and extract */ +			p += 5; +			sz = *p++; +			sz |= *p++ << 8; +			sz |= *p++ << 16; +			sz |= *p++ << 24; +			/* Convert to byte count */ +			sz += 7; +			sz >>= 3; +		} else if (*p == 0xff) { +			/* 0xff is ok ... just skip */ +			p++; +			continue; +		} else { +			/* Not 0xff or 0x6a ... something's not +			 * right ... report 'unknown' (sz=0). +			 */ +			break; +		} +	} +	return (sz); +} + +int epcs_erase (unsigned start, unsigned end) +{ +	unsigned off, sectsz; +	unsigned char buf[4]; +	struct epcs_devinfo_t *dev = epcs_dev_find (); + +	if (!dev || (start>end)) +		return (-1); + +	/* Erase the requested sectors. An address is required +	 * that lies within the requested sector -- we'll just +	 * use the first address in the sector. +	 */ +	printf ("epcs erasing sector %d ", start); +	if (start != end) +		printf ("to %d ", end); +	sectsz = (1 << dev->sz_sect); +	while (start <= end) { +		off = start * sectsz; +		start++; + +		buf[0] = EPCS_ERASE_SECT; +		buf[1] = off >> 16; +		buf[2] = off >> 8; +		buf[3] = off; + +		epcs_wr_enable (); +		epcs_cs (1); +		epcs_snd (buf,4); +		epcs_cs (0); + +		printf ("."); /* Some user feedback */ + +		/* Wait for erase to complete */ +		while (epcs_status_rd() & EPCS_STATUS_WIP) +			; +	} +	printf (" done.\n"); +	return (0); +} + +int epcs_read (ulong addr, ulong off, ulong cnt) +{ +	unsigned char buf[4]; +	struct epcs_devinfo_t *dev = epcs_dev_find (); + +	if (!dev) +		return (-1); + +	buf[0] = EPCS_READ_BYTES; +	buf[1] = off >> 16; +	buf[2] = off >> 8; +	buf[3] = off; + +	epcs_cs (1); +	epcs_snd (buf,4); +	epcs_rrcv ((unsigned char *)addr, cnt); +	epcs_cs (0); + +	return (0); +} + +int epcs_write (ulong addr, ulong off, ulong cnt) +{ +	ulong wrcnt; +	unsigned pgsz; +	unsigned char buf[4]; +	struct epcs_devinfo_t *dev = epcs_dev_find (); + +	if (!dev) +		return (-1); + +	pgsz = (1<<dev->sz_page); +	while (cnt) { +		if (off % pgsz) +			wrcnt = pgsz - (off % pgsz); +		else +			wrcnt = pgsz; +		wrcnt = (wrcnt > cnt) ? cnt : wrcnt; + +		buf[0] = EPCS_WRITE_BYTES; +		buf[1] = off >> 16; +		buf[2] = off >> 8; +		buf[3] = off; + +		epcs_wr_enable (); +		epcs_cs (1); +		epcs_snd (buf,4); +		epcs_rsnd ((unsigned char *)addr, wrcnt); +		epcs_cs (0); + +		/* Wait for write to complete */ +		while (epcs_status_rd() & EPCS_STATUS_WIP) +			; + +		cnt -= wrcnt; +		off += wrcnt; +		addr += wrcnt; +	} + +	return (0); +} + +int epcs_verify (ulong addr, ulong off, ulong cnt, ulong *err) +{ +	ulong rdcnt; +	unsigned char buf[256]; +	unsigned char *start,*end; +	int i; + +	start = end = (unsigned char *)addr; +	while (cnt) { +		rdcnt = (cnt>sizeof(buf)) ? sizeof(buf) : cnt; +		epcs_read ((ulong)buf, off, rdcnt); +		for (i=0; i<rdcnt; i++) { +			if (*end != buf[i]) { +				*err = end - start; +				return(-1); +			} +			end++; +		} +		cnt -= rdcnt; +		off += rdcnt; +	} +	return (0); +} + +static int epcs_sect_erased (int sect, unsigned *offset, +		struct epcs_devinfo_t *dev) +{ +	unsigned char buf[128]; +	unsigned off, end; +	unsigned sectsz; +	int i; + +	sectsz = (1 << dev->sz_sect); +	off = sectsz * sect; +	end = off + sectsz; + +	while (off < end) { +		epcs_read ((ulong)buf, off, sizeof(buf)); +		for (i=0; i < sizeof(buf); i++) { +			if (buf[i] != 0xff) { +				*offset = off + i; +				return (0); +			} +		} +		off += sizeof(buf); +	} +	return (1); +} + + +/*********************************************************************** + * Commands + ***********************************************************************/ +static +void do_epcs_info (struct epcs_devinfo_t *dev, int argc, char * const argv[]) +{ +	int i; +	unsigned char stat; +	unsigned tmp; +	int erased; + +	/* Basic device info */ +	printf ("%s: %d kbytes (%d sectors x %d kbytes," +		" %d bytes/page)\n", +		dev->name, 1 << (dev->size-10), +		dev->num_sects, 1 << (dev->sz_sect-10), +		1 << dev->sz_page ); + +	/* Status -- for now protection is all-or-nothing */ +	stat = epcs_status_rd(); +	printf ("status: 0x%02x (WIP:%d, WEL:%d, PROT:%s)\n", +		stat, +		(stat & EPCS_STATUS_WIP) ? 1 : 0, +		(stat & EPCS_STATUS_WEL) ? 1 : 0, +		(stat & dev->prot_mask) ? "on" : "off" ); + +	/* Configuration  */ +	tmp = epcs_cfgsz (); +	if (tmp) { +		printf ("config: 0x%06x (%d) bytes\n", tmp, tmp ); +	} else { +		printf ("config: unknown\n" ); +	} + +	/* Sector info */ +	for (i=0; (i < dev->num_sects) && (argc > 1); i++) { +		erased = epcs_sect_erased (i, &tmp, dev); +		if ((i & 0x03) == 0) printf ("\n"); +		printf ("%4d: %07x ", +			i, i*(1<<dev->sz_sect) ); +		if (erased) +			printf ("E "); +		else +			printf ("  "); +	} +	printf ("\n"); + +	return; +} + +static +void do_epcs_erase (struct epcs_devinfo_t *dev, int argc, char * const argv[]) +{ +	unsigned start,end; + +	if ((argc < 3) || (argc > 4)) { +		printf ("USAGE: epcs erase sect [end]\n"); +		return; +	} +	if ((epcs_status_rd() & dev->prot_mask) != 0) { +		printf ( "epcs: device protected.\n"); +		return; +	} + +	start = simple_strtoul (argv[2], NULL, 10); +	if (argc > 3) +		end = simple_strtoul (argv[3], NULL, 10); +	else +		end = start; +	if ((start >= dev->num_sects) || (start > end)) { +		printf ("epcs: invalid sector range: [%d:%d]\n", +			start, end ); +		return; +	} + +	epcs_erase (start, end); + +	return; +} + +static +void do_epcs_protect (struct epcs_devinfo_t *dev, int argc, char * const argv[]) +{ +	unsigned char stat; + +	/* For now protection is all-or-nothing to keep things +	 * simple. The protection bits don't map in a linear +	 * fashion ... and we would rather protect the bottom +	 * of the device since it contains the config data and +	 * leave the top unprotected for app use. But unfortunately +	 * protection works from top-to-bottom so it does +	 * really help very much from a software app point-of-view. +	 */ +	if (argc < 3) { +		printf ("USAGE: epcs protect on | off\n"); +		return; +	} +	if (!dev) +		return; + +	/* Protection on/off is just a matter of setting/clearing +	 * all protection bits in the status register. +	 */ +	stat = epcs_status_rd (); +	if (strcmp ("on", argv[2]) == 0) { +		stat |= dev->prot_mask; +	} else if (strcmp ("off", argv[2]) == 0 ) { +		stat &= ~dev->prot_mask; +	} else { +		printf ("epcs: unknown protection: %s\n", argv[2]); +		return; +	} +	epcs_status_wr (stat); +	return; +} + +static +void do_epcs_read (struct epcs_devinfo_t *dev, int argc, char * const argv[]) +{ +	ulong addr,off,cnt; +	ulong sz; + +	if (argc < 5) { +		printf ("USAGE: epcs read addr offset count\n"); +		return; +	} + +	sz = 1 << dev->size; +	addr = simple_strtoul (argv[2], NULL, 16); +	off  = simple_strtoul (argv[3], NULL, 16); +	cnt  = simple_strtoul (argv[4], NULL, 16); +	if (off > sz) { +		printf ("offset is greater than device size" +			"... aborting.\n"); +		return; +	} +	if ((off + cnt) > sz) { +		printf ("request exceeds device size" +			"... truncating.\n"); +		cnt = sz - off; +	} +	printf ("epcs: read %08lx <- %06lx (0x%lx bytes)\n", +			addr, off, cnt); +	epcs_read (addr, off, cnt); + +	return; +} + +static +void do_epcs_write (struct epcs_devinfo_t *dev, int argc, char * const argv[]) +{ +	ulong addr,off,cnt; +	ulong sz; +	ulong err; + +	if (argc < 5) { +		printf ("USAGE: epcs write addr offset count\n"); +		return; +	} +	if ((epcs_status_rd() & dev->prot_mask) != 0) { +		printf ( "epcs: device protected.\n"); +		return; +	} + +	sz = 1 << dev->size; +	addr = simple_strtoul (argv[2], NULL, 16); +	off  = simple_strtoul (argv[3], NULL, 16); +	cnt  = simple_strtoul (argv[4], NULL, 16); +	if (off > sz) { +		printf ("offset is greater than device size" +			"... aborting.\n"); +		return; +	} +	if ((off + cnt) > sz) { +		printf ("request exceeds device size" +			"... truncating.\n"); +		cnt = sz - off; +	} +	printf ("epcs: write %08lx -> %06lx (0x%lx bytes)\n", +			addr, off, cnt); +	epcs_write (addr, off, cnt); +	if (epcs_verify (addr, off, cnt, &err) != 0) +		printf ("epcs: write error at offset %06lx\n", err); + +	return; +} + +static +void do_epcs_verify (struct epcs_devinfo_t *dev, int argc, char * const argv[]) +{ +	ulong addr,off,cnt; +	ulong sz; +	ulong err; + +	if (argc < 5) { +		printf ("USAGE: epcs verify addr offset count\n"); +		return; +	} + +	sz = 1 << dev->size; +	addr = simple_strtoul (argv[2], NULL, 16); +	off  = simple_strtoul (argv[3], NULL, 16); +	cnt  = simple_strtoul (argv[4], NULL, 16); +	if (off > sz) { +		printf ("offset is greater than device size" +			"... aborting.\n"); +		return; +	} +	if ((off + cnt) > sz) { +		printf ("request exceeds device size" +			"... truncating.\n"); +		cnt = sz - off; +	} +	printf ("epcs: verify %08lx -> %06lx (0x%lx bytes)\n", +			addr, off, cnt); +	if (epcs_verify (addr, off, cnt, &err) != 0) +		printf ("epcs: verify error at offset %06lx\n", err); + +	return; +} + +/*-----------------------------------------------------------------------*/ +int do_epcs (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	int len; +	struct epcs_devinfo_t *dev = epcs_dev_find (); + +	if (!dev) { +		printf ("epcs: device not found.\n"); +		return (-1); +	} + +	if (argc < 2) { +		do_epcs_info (dev, argc, argv); +		return (0); +	} + +	len = strlen (argv[1]); +	if (strncmp ("info", argv[1], len) == 0) { +		do_epcs_info (dev, argc, argv); +	} else if (strncmp ("erase", argv[1], len) == 0) { +		do_epcs_erase (dev, argc, argv); +	} else if (strncmp ("protect", argv[1], len) == 0) { +		do_epcs_protect (dev, argc, argv); +	} else if (strncmp ("read", argv[1], len) == 0) { +		do_epcs_read (dev, argc, argv); +	} else if (strncmp ("write", argv[1], len) == 0) { +		do_epcs_write (dev, argc, argv); +	} else if (strncmp ("verify", argv[1], len) == 0) { +		do_epcs_verify (dev, argc, argv); +	} else { +		printf ("epcs: unknown operation: %s\n", argv[1]); +	} + +	return (0); +} + +/*-----------------------------------------------------------------------*/ + + +U_BOOT_CMD( epcs, 5, 0, do_epcs, SHORT_HELP, LONG_HELP ); + +#endif /* CONFIG_NIOS_EPCS */ diff --git a/roms/u-boot/arch/nios2/cpu/exceptions.S b/roms/u-boot/arch/nios2/cpu/exceptions.S new file mode 100644 index 00000000..f5fa8d74 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/exceptions.S @@ -0,0 +1,139 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <asm/opcodes.h> + + +	.text +	.align	4 + +	.global _exception + +	.set noat +	.set nobreak + +_exception: +	/* SAVE ALL REGS -- this allows trap and unimplemented +	 * instruction handlers to be coded conveniently in C +	 */ +	addi	sp, sp, -(33*4) +	stw	r0, 0(sp) +	stw	r1, 4(sp) +	stw	r2, 8(sp) +	stw	r3, 12(sp) +	stw	r4, 16(sp) +	stw	r5, 20(sp) +	stw	r6, 24(sp) +	stw	r7, 28(sp) +	stw	r8, 32(sp) +	stw	r9, 36(sp) +	stw	r10, 40(sp) +	stw	r11, 44(sp) +	stw	r12, 48(sp) +	stw	r13, 52(sp) +	stw	r14, 56(sp) +	stw	r15, 60(sp) +	stw	r16, 64(sp) +	stw	r17, 68(sp) +	stw	r19, 72(sp) +	stw	r19, 76(sp) +	stw	r20, 80(sp) +	stw	r21, 84(sp) +	stw	r22, 88(sp) +	stw	r23, 92(sp) +	stw	r24, 96(sp) +	stw	r25, 100(sp) +	stw	r26, 104(sp) +	stw	r27, 108(sp) +	stw	r28, 112(sp) +	stw	r29, 116(sp) +	stw	r30, 120(sp) +	stw	r31, 124(sp) +	rdctl	et, estatus +	stw	et, 128(sp) + +	/* If interrupts are disabled -- software interrupt */ +	rdctl	et, estatus +	andi	et, et, 1 +	beq	et, r0, 0f + +	/* If no interrupts are pending -- software interrupt */ +	rdctl	et, ipending +	beq	et, r0, 0f + +	/* HARDWARE INTERRUPT: Call interrupt handler */ +	movhi	r3, %hi(external_interrupt) +	ori	r3, r3, %lo(external_interrupt) +	mov	r4, sp		/* ptr to regs */ +	callr	r3 + +	/* Return address fixup: execution resumes by re-issue of +	 * interrupted instruction at ea-4 (ea == r29). Here we do +	 * simple fixup to allow common exception return. +	 */ +	ldw	r3, 116(sp) +	addi	r3, r3, -4 +	stw	r3, 116(sp) +	br	_exception_return + +0: +	/* TRAP EXCEPTION */ +	movhi	r3, %hi(OPC_TRAP) +	ori	r3, r3, %lo(OPC_TRAP) +	addi	r1, ea, -4 +	ldw	r1, 0(r1) +	bne	r1, r3, 1f +	movhi	r3, %hi(trap_handler) +	ori	r3, r3, %lo(trap_handler) +	mov	r4, sp		/* ptr to regs */ +	callr	r3 +	br	_exception_return + +1: +	/* UNIMPLEMENTED INSTRUCTION EXCEPTION */ +	movhi	r3, %hi(soft_emulation) +	ori	r3, r3, %lo(soft_emulation) +	mov	r4, sp		/* ptr to regs */ +	callr	r3 + +	/* Restore regsisters and return from exception*/ +_exception_return: +	ldw	r1, 4(sp) +	ldw	r2, 8(sp) +	ldw	r3, 12(sp) +	ldw	r4, 16(sp) +	ldw	r5, 20(sp) +	ldw	r6, 24(sp) +	ldw	r7, 28(sp) +	ldw	r8, 32(sp) +	ldw	r9, 36(sp) +	ldw	r10, 40(sp) +	ldw	r11, 44(sp) +	ldw	r12, 48(sp) +	ldw	r13, 52(sp) +	ldw	r14, 56(sp) +	ldw	r15, 60(sp) +	ldw	r16, 64(sp) +	ldw	r17, 68(sp) +	ldw	r19, 72(sp) +	ldw	r19, 76(sp) +	ldw	r20, 80(sp) +	ldw	r21, 84(sp) +	ldw	r22, 88(sp) +	ldw	r23, 92(sp) +	ldw	r24, 96(sp) +	ldw	r25, 100(sp) +	ldw	r26, 104(sp) +	ldw	r27, 108(sp) +	ldw	r28, 112(sp) +	ldw	r29, 116(sp) +	ldw	r30, 120(sp) +	ldw	r31, 124(sp) +	addi	sp, sp, (33*4) +	eret +/*-------------------------------------------------------------*/ diff --git a/roms/u-boot/arch/nios2/cpu/fdt.c b/roms/u-boot/arch/nios2/cpu/fdt.c new file mode 100644 index 00000000..4d88f169 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/fdt.c @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2011, Missing Link Electronics + *                     Joachim Foerster <joachim@missinglinkelectronics.com> + * + * Taken from arch/powerpc/cpu/ppc4xx/fdt.c: + * + * (C) Copyright 2007-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) +#include <libfdt.h> +#include <fdt_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +void __ft_board_setup(void *blob, bd_t *bd) +{ +	ft_cpu_setup(blob, bd); +} +void ft_board_setup(void *blob, bd_t *bd) \ +	__attribute__((weak, alias("__ft_board_setup"))); + +void ft_cpu_setup(void *blob, bd_t *bd) +{ +	/* +	 * Fixup all ethernet nodes +	 * Note: aliases in the dts are required for this +	 */ +	fdt_fixup_ethernet(blob); +} +#endif /* CONFIG_OF_LIBFDT && CONFIG_OF_BOARD_SETUP */ diff --git a/roms/u-boot/arch/nios2/cpu/interrupts.c b/roms/u-boot/arch/nios2/cpu/interrupts.c new file mode 100644 index 00000000..c4bed225 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/interrupts.c @@ -0,0 +1,264 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + + +#include <nios2.h> +#include <nios2-io.h> +#include <asm/types.h> +#include <asm/io.h> +#include <asm/ptrace.h> +#include <common.h> +#include <command.h> +#include <watchdog.h> +#ifdef CONFIG_STATUS_LED +#include <status_led.h> +#endif + +#if defined(CONFIG_SYS_NIOS_TMRBASE) && !defined(CONFIG_SYS_NIOS_TMRIRQ) +#error CONFIG_SYS_NIOS_TMRIRQ not defined (see documentation) +#endif + +/****************************************************************************/ + +struct	irq_action { +	interrupt_handler_t *handler; +	void *arg; +	int count; +}; + +static struct irq_action vecs[32]; + +/*************************************************************************/ +volatile ulong timestamp = 0; + +void reset_timer (void) +{ +	nios_timer_t *tmr =(nios_timer_t *)CONFIG_SYS_NIOS_TMRBASE; + +	/* From Embedded Peripherals Handbook: +	 * +	 * "When the hardware is configured with Writeable period +	 * disabled, writing to one of the period_n registers causes +	 * the counter to reset to the fixed Timeout Period specified +	 * at system generation time." +	 * +	 * Here we force a reload to prevent early timeouts from +	 * get_timer() when the interrupt period is greater than +	 * than 1 msec. +	 * +	 * Simply write to periodl with its own value to force an +	 * internal counter reload, THEN reset the timestamp. +	 */ +	writel (readl (&tmr->periodl), &tmr->periodl); +	timestamp = 0; + +	/* From Embedded Peripherals Handbook: +	 * +	 * "Writing to one of the period_n registers stops the internal +	 * counter, except when the hardware is configured with Start/Stop +	 * control bits off. If Start/Stop control bits is off, writing +	 * either register does not stop the counter." +	 * +	 * In order to accomodate either configuration, the control +	 * register is re-written. If the counter is stopped, it will +	 * be restarted. If it is running, the write is essentially +	 * a nop. +	 */ +	writel (NIOS_TIMER_ITO | NIOS_TIMER_CONT | NIOS_TIMER_START, +			&tmr->control); + +} + +ulong get_timer (ulong base) +{ +	WATCHDOG_RESET (); +	return (timestamp - base); +} + +/* + * This function is derived from Blackfin code (read timebase as long long). + * On Nios2 it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ +	return get_timer(0); +} + +/* + * This function is derived from Blackfin code. + * On Nios2 it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ +	ulong tbclk; + +	tbclk = CONFIG_SYS_HZ; +	return tbclk; +} + +/* The board must handle this interrupt if a timer is not + * provided. + */ +#if defined(CONFIG_SYS_NIOS_TMRBASE) +void tmr_isr (void *arg) +{ +	nios_timer_t *tmr = (nios_timer_t *)arg; +	/* Interrupt is cleared by writing anything to the +	 * status register. +	 */ +	writel (0, &tmr->status); +	timestamp += CONFIG_SYS_NIOS_TMRMS; +#ifdef CONFIG_STATUS_LED +	status_led_tick(timestamp); +#endif +} + +static void tmr_init (void) +{ +	nios_timer_t *tmr =(nios_timer_t *)CONFIG_SYS_NIOS_TMRBASE; + +	writel (0, &tmr->status); +	writel (0, &tmr->control); +	writel (NIOS_TIMER_STOP, &tmr->control); + +#if defined(CONFIG_SYS_NIOS_TMRCNT) +	writel (CONFIG_SYS_NIOS_TMRCNT & 0xffff, &tmr->periodl); +	writel ((CONFIG_SYS_NIOS_TMRCNT >> 16) & 0xffff, &tmr->periodh); +#endif +	writel (NIOS_TIMER_ITO | NIOS_TIMER_CONT | NIOS_TIMER_START, +			&tmr->control); +	irq_install_handler (CONFIG_SYS_NIOS_TMRIRQ, tmr_isr, (void *)tmr); +} + +#endif /* CONFIG_SYS_NIOS_TMRBASE */ + +/*************************************************************************/ +int disable_interrupts (void) +{ +	int val = rdctl (CTL_STATUS); +	wrctl (CTL_STATUS, val & ~STATUS_IE); +	return (val & STATUS_IE); +} + +void enable_interrupts( void ) +{ +	int val = rdctl (CTL_STATUS); +	wrctl (CTL_STATUS, val | STATUS_IE); +} + +void external_interrupt (struct pt_regs *regs) +{ +	unsigned irqs; +	struct irq_action *act; + +	/* Evaluate only irqs that are both enabled AND pending */ +	irqs = rdctl (CTL_IENABLE) & rdctl (CTL_IPENDING); +	act = vecs; + +	/* Assume (as does the Nios2 HAL) that bit 0 is highest +	 * priority. NOTE: There is ALWAYS a handler assigned +	 * (the default if no other). +	 */ +	while (irqs) { +		if (irqs & 1) { +			act->handler (act->arg); +			act->count++; +		} +		irqs >>=1; +		act++; +	} +} + +static void def_hdlr (void *arg) +{ +	unsigned irqs = rdctl (CTL_IENABLE); + +	/* Disable the individual interrupt -- with gratuitous +	 * warning. +	 */ +	irqs &= ~(1 << (int)arg); +	wrctl (CTL_IENABLE, irqs); +	printf ("WARNING: Disabling unhandled interrupt: %d\n", +			(int)arg); +} + +/*************************************************************************/ +void irq_install_handler (int irq, interrupt_handler_t *hdlr, void *arg) +{ + +	int flag; +	struct irq_action *act; +	unsigned ena = rdctl (CTL_IENABLE); + +	if ((irq < 0) || (irq > 31)) +		return; +	act = &vecs[irq]; + +	flag = disable_interrupts (); +	if (hdlr) { +		act->handler = hdlr; +		act->arg = arg; +		ena |= (1 << irq);		/* enable */ +	} else { +		act->handler = def_hdlr; +		act->arg = (void *)irq; +		ena &= ~(1 << irq);		/* disable */ +	} +	wrctl (CTL_IENABLE, ena); +	if (flag) enable_interrupts (); +} + + +int interrupt_init (void) +{ +	int i; + +	/* Assign the default handler to all */ +	for (i = 0; i < 32; i++) { +		vecs[i].handler = def_hdlr; +		vecs[i].arg = (void *)i; +		vecs[i].count = 0; +	} + +#if defined(CONFIG_SYS_NIOS_TMRBASE) +	tmr_init (); +#endif + +	enable_interrupts (); +	return (0); +} + + +/*************************************************************************/ +#if defined(CONFIG_CMD_IRQ) +int do_irqinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	int i; +	struct irq_action *act = vecs; + +	printf ("\nInterrupt-Information:\n\n"); +	printf ("Nr  Routine   Arg       Count\n"); +	printf ("-----------------------------\n"); + +	for (i=0; i<32; i++) { +		if (act->handler != def_hdlr) { +			printf ("%02d  %08lx  %08lx  %d\n", +				i, +				(ulong)act->handler, +				(ulong)act->arg, +				act->count); +		} +		act++; +	} +	printf ("\n"); + +	return (0); +} +#endif diff --git a/roms/u-boot/arch/nios2/cpu/start.S b/roms/u-boot/arch/nios2/cpu/start.S new file mode 100644 index 00000000..7ce0d34d --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/start.S @@ -0,0 +1,186 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <asm-offsets.h> +#include <config.h> +#include <version.h> + +/************************************************************************* + * RESTART + ************************************************************************/ + +	.text +	.global _start + +_start: +	wrctl	status, r0		/* Disable interrupts */ +	/* ICACHE INIT -- only the icache line at the reset address +	 * is invalidated at reset. So the init must stay within +	 * the cache line size (8 words). If GERMS is used, we'll +	 * just be invalidating the cache a second time. If cache +	 * is not implemented initi behaves as nop. +	 */ +	ori	r4, r0, %lo(CONFIG_SYS_ICACHELINE_SIZE) +	movhi	r5, %hi(CONFIG_SYS_ICACHE_SIZE) +	ori	r5, r5, %lo(CONFIG_SYS_ICACHE_SIZE) +0:	initi	r5 +	sub	r5, r5, r4 +	bgt	r5, r0, 0b +	br	_except_end	/* Skip the tramp */ + +	/* EXCEPTION TRAMPOLINE -- the following gets copied +	 * to the exception address (below), but is otherwise at the +	 * default exception vector offset (0x0020). +	 */ +_except_start: +	movhi	et, %hi(_exception) +	ori	et, et, %lo(_exception) +	jmp	et +_except_end: + +	/* INTERRUPTS -- for now, all interrupts masked and globally +	 * disabled. +	 */ +	wrctl	ienable, r0		/* All disabled	*/ + +	/* DCACHE INIT -- if dcache not implemented, initd behaves as +	 * nop. +	 */ +	movhi	r4, %hi(CONFIG_SYS_DCACHELINE_SIZE) +	ori	r4, r4, %lo(CONFIG_SYS_DCACHELINE_SIZE) +	movhi	r5, %hi(CONFIG_SYS_DCACHE_SIZE) +	ori	r5, r5, %lo(CONFIG_SYS_DCACHE_SIZE) +	mov	r6, r0 +1:	initd	0(r6) +	add	r6, r6, r4 +	bltu	r6, r5, 1b + +	/* RELOCATE CODE, DATA & COMMAND TABLE -- the following code +	 * assumes code, data and the command table are all +	 * contiguous. This lets us relocate everything as a single +	 * block. Make sure the linker script matches this ;-) +	 */ +	nextpc	r4 +_cur:	movhi	r5, %hi(_cur - _start) +	ori	r5, r5, %lo(_cur - _start) +	sub	r4, r4, r5		/* r4 <- cur _start */ +	mov	r8, r4 +	movhi	r5, %hi(_start) +	ori	r5, r5, %lo(_start)	/* r5 <- linked _start */ +	beq	r4, r5, 3f + +	movhi	r6, %hi(_edata) +	ori	r6, r6, %lo(_edata) +2:	ldwio	r7, 0(r4) +	addi	r4, r4, 4 +	stwio	r7, 0(r5) +	addi	r5, r5, 4 +	bne	r5, r6, 2b +3: + +	/* ZERO BSS/SBSS -- bss and sbss are assumed to be adjacent +	 * and between __bss_start and __bss_end. +	 */ +	 movhi	r5, %hi(__bss_start) +	 ori	r5, r5, %lo(__bss_start) +	 movhi	r6, %hi(__bss_end) +	 ori	r6, r6, %lo(__bss_end) +	 beq	r5, r6, 5f + +4:	stwio	r0, 0(r5) +	 addi	r5, r5, 4 +	 bne	r5, r6, 4b +5: + +	/* JUMP TO RELOC ADDR */ +	movhi	r4, %hi(_reloc) +	ori	r4, r4, %lo(_reloc) +	jmp	r4 +_reloc: + +	/* COPY EXCEPTION TRAMPOLINE -- copy the tramp to the +	 * exception address. Define CONFIG_ROM_STUBS to prevent +	 * the copy (e.g. exception in flash or in other +	 * softare/firmware component). +	 */ +#if !defined(CONFIG_ROM_STUBS) +	movhi	r4, %hi(_except_start) +	ori	r4, r4, %lo(_except_start) +	movhi	r5, %hi(_except_end) +	ori	r5, r5, %lo(_except_end) +	movhi	r6, %hi(CONFIG_SYS_EXCEPTION_ADDR) +	ori	r6, r6, %lo(CONFIG_SYS_EXCEPTION_ADDR) +	beq	r4, r6, 7f	/* Skip if at proper addr */ + +6:	ldwio	r7, 0(r4) +	stwio	r7, 0(r6) +	addi	r4, r4, 4 +	addi	r6, r6, 4 +	bne	r4, r5, 6b +7: +#endif + +	/* STACK INIT -- zero top two words for call back chain. +	 */ +	movhi	sp, %hi(CONFIG_SYS_INIT_SP) +	ori	sp, sp, %lo(CONFIG_SYS_INIT_SP) +	addi	sp, sp, -8 +	stw	r0, 0(sp) +	stw	r0, 4(sp) +	mov	fp, sp + +	/* +	 * Call board_init -- never returns +	 */ +	movhi	r4, %hi(board_init@h) +	ori	r4, r4, %lo(board_init@h) +	callr	r4 + +	/* NEVER RETURNS -- but branch to the _start just +	 * in case ;-) +	 */ +	br	_start + + +/* + * dly_clks -- Nios2 (like Nios1) doesn't have a timebase in + * the core. For simple delay loops, we do our best by counting + * instruction cycles. + * + * Instruction performance varies based on the core. For cores + * with icache and static/dynamic branch prediction (II/f, II/s): + * + *	Normal ALU (e.g. add, cmp, etc):	1 cycle + *	Branch (correctly predicted, taken):	2 cycles + *	Negative offset is predicted (II/s). + * + * For cores without icache and no branch prediction (II/e): + * + *	Normal ALU (e.g. add, cmp, etc):	6 cycles + *	Branch (no prediction):			6 cycles + * + * For simplicity, if an instruction cache is implemented we + * assume II/f or II/s. Otherwise, we use the II/e. + * + */ +	.globl dly_clks + +dly_clks: + +#if (CONFIG_SYS_ICACHE_SIZE > 0) +	subi	r4, r4, 3		/* 3 clocks/loop	*/ +#else +	subi	r4, r4, 12		/* 12 clocks/loop	*/ +#endif +	bge	r4, r0, dly_clks +	ret + +	.data +	.globl	version_string + +version_string: +	.ascii U_BOOT_VERSION_STRING, "\0" diff --git a/roms/u-boot/arch/nios2/cpu/sysid.c b/roms/u-boot/arch/nios2/cpu/sysid.c new file mode 100644 index 00000000..943bff8d --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/sysid.c @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#if defined (CONFIG_SYS_NIOS_SYSID_BASE) + +#include <command.h> +#include <asm/io.h> +#include <nios2-io.h> +#include <linux/time.h> + +void display_sysid (void) +{ +	struct nios_sysid_t *sysid = (struct nios_sysid_t *)CONFIG_SYS_NIOS_SYSID_BASE; +	struct tm t; +	char asc[32]; +	time_t stamp; + +	stamp = readl (&sysid->timestamp); +	localtime_r (&stamp, &t); +	asctime_r (&t, asc); +	printf ("SYSID : %08lx, %s", readl (&sysid->id), asc); + +} + +int do_sysid (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	display_sysid (); +	return (0); +} + +U_BOOT_CMD( +	sysid,	1,	1,	do_sysid, +	"display Nios-II system id", +	"" +); +#endif /* CONFIG_SYS_NIOS_SYSID_BASE */ diff --git a/roms/u-boot/arch/nios2/cpu/traps.c b/roms/u-boot/arch/nios2/cpu/traps.c new file mode 100644 index 00000000..07ed6e44 --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/traps.c @@ -0,0 +1,26 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <asm/ptrace.h> +#include <common.h> + +void trap_handler (struct pt_regs *regs) +{ +	/* Just issue warning */ +	printf ("\n\n*** WARNING: unimplemented trap @ %08x\n\n", +			regs->reg[29] - 4); +} + +void soft_emulation (struct pt_regs *regs) +{ +	/* TODO: Software emulation of mul/div etc. Until this is +	 * implemented, generate warning and hang. +	 */ +	printf ("\n\n*** ERROR: unimplemented instruction @ %08x\n", +			regs->reg[29] - 4); +	hang (); +} diff --git a/roms/u-boot/arch/nios2/cpu/u-boot.lds b/roms/u-boot/arch/nios2/cpu/u-boot.lds new file mode 100644 index 00000000..be92e8ed --- /dev/null +++ b/roms/u-boot/arch/nios2/cpu/u-boot.lds @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + + +OUTPUT_FORMAT("elf32-littlenios2") +OUTPUT_ARCH(nios2) +ENTRY(_start) + +SECTIONS +{ +	.text : +	{ +	  arch/nios2/cpu/start.o (.text) +	  *(.text) +	  *(.text.*) +	  *(.gnu.linkonce.t*) +	  *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) +	  *(.gnu.linkonce.r*) +	} +	. = ALIGN (4); +	_etext = .; +	PROVIDE (etext = .); + +	/* CMD TABLE - sandwich this in between text and data so +	 * the initialization code relocates the command table as +	 * well -- admittedly, this is just pure laziness ;-) +	 */ + +	. = ALIGN(4); +	.u_boot_list : { +		KEEP(*(SORT(.u_boot_list*))); +	} + +	/* INIT DATA sections - "Small" data (see the gcc -G option) +	 * is always gp-relative. Here we make all init data sections +	 * adjacent to simplify the startup code -- and provide +	 * the global pointer for gp-relative access. +	 */ +	_data = .; +	.data : +	{ +	  *(.data) +	  *(.data.*) +	  *(.gnu.linkonce.d*) +	} + +	. = ALIGN(16); +	_gp = .;			/* Global pointer addr */ +	PROVIDE (gp = .); + +	.sdata : +	{ +	  *(.sdata) +	  *(.sdata.*) +	  *(.gnu.linkonce.s.*) +	} +	. = ALIGN(4); + +	_edata = .; +	PROVIDE (edata = .); + +	/* UNINIT DATA - Small uninitialized data is first so it's +	 * adjacent to sdata and can be referenced via gp. The normal +	 * bss follows. We keep it adjacent to simplify init code. +	 */ +	__bss_start = .; +	.sbss (NOLOAD) : +	{ +	  *(.sbss) +	  *(.sbss.*) +	  *(.gnu.linkonce.sb.*) +	  *(.scommon) +	} +	. = ALIGN(4); +	.bss (NOLOAD) : +	{ +	  *(.bss) +	  *(.bss.*) +	  *(.dynbss) +	  *(COMMON) +	  *(.scommon) +	} +	. = ALIGN(4); +	__bss_end = .; +	PROVIDE (end = .); + +	/* DEBUG -- symbol table, string table, etc. etc. +	 */ +	.stab 0 : { *(.stab) } +	.stabstr 0 : { *(.stabstr) } +	.stab.excl 0 : { *(.stab.excl) } +	.stab.exclstr 0 : { *(.stab.exclstr) } +	.stab.index 0 : { *(.stab.index) } +	.stab.indexstr 0 : { *(.stab.indexstr) } +	.comment 0 : { *(.comment) } +	.debug		0 : { *(.debug) } +	.line		0 : { *(.line) } +	.debug_srcinfo	0 : { *(.debug_srcinfo) } +	.debug_sfnames	0 : { *(.debug_sfnames) } +	.debug_aranges	0 : { *(.debug_aranges) } +	.debug_pubnames 0 : { *(.debug_pubnames) } +	.debug_info	0 : { *(.debug_info) } +	.debug_abbrev	0 : { *(.debug_abbrev) } +	.debug_line	0 : { *(.debug_line) } +	.debug_frame	0 : { *(.debug_frame) } +	.debug_str	0 : { *(.debug_str) } +	.debug_loc	0 : { *(.debug_loc) } +	.debug_macinfo	0 : { *(.debug_macinfo) } +	.debug_weaknames 0 : { *(.debug_weaknames) } +	.debug_funcnames 0 : { *(.debug_funcnames) } +	.debug_typenames 0 : { *(.debug_typenames) } +	.debug_varnames	 0 : { *(.debug_varnames) } +}  | 
