summaryrefslogtreecommitdiff
path: root/arch/nios2/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/nios2/cpu')
-rw-r--r--arch/nios2/cpu/Makefile2
-rw-r--r--arch/nios2/cpu/cpu.c128
-rw-r--r--arch/nios2/cpu/interrupts.c144
-rw-r--r--arch/nios2/cpu/start.S186
-rw-r--r--arch/nios2/cpu/sysid.c46
-rw-r--r--arch/nios2/cpu/u-boot.lds14
6 files changed, 193 insertions, 327 deletions
diff --git a/arch/nios2/cpu/Makefile b/arch/nios2/cpu/Makefile
index 3fe7847..185ca3c 100644
--- a/arch/nios2/cpu/Makefile
+++ b/arch/nios2/cpu/Makefile
@@ -7,5 +7,5 @@
extra-y = start.o
obj-y = exceptions.o
-obj-y += cpu.o interrupts.o sysid.o traps.o
+obj-y += cpu.o interrupts.o traps.o
obj-y += fdt.o
diff --git a/arch/nios2/cpu/cpu.c b/arch/nios2/cpu/cpu.c
index 39ae972..ff0fa20 100644
--- a/arch/nios2/cpu/cpu.c
+++ b/arch/nios2/cpu/cpu.c
@@ -6,25 +6,18 @@
*/
#include <common.h>
-#include <asm/nios2.h>
+#include <cpu.h>
+#include <dm.h>
+#include <errno.h>
#include <asm/cache.h>
DECLARE_GLOBAL_DATA_PTR;
-#if defined (CONFIG_SYS_NIOS_SYSID_BASE)
-extern void display_sysid (void);
-#endif /* CONFIG_SYS_NIOS_SYSID_BASE */
-
#ifdef CONFIG_DISPLAY_CPUINFO
int print_cpuinfo(void)
{
- printf ("CPU : Nios-II\n");
-#if !defined(CONFIG_SYS_NIOS_SYSID_BASE)
- printf ("SYSID : <unknown>\n");
-#else
- display_sysid ();
-#endif
- return (0);
+ printf("CPU: Nios-II\n");
+ return 0;
}
#endif /* CONFIG_DISPLAY_CPUINFO */
@@ -32,29 +25,120 @@ 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);
+ nios2_callr(gd->arch.reset_addr);
return 0;
}
-int dcache_status(void)
+/*
+ * 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).
+ */
+#ifndef CONFIG_ROM_STUBS
+static void copy_exception_trampoline(void)
{
- return 1;
+ extern int _except_start, _except_end;
+ void *except_target = (void *)gd->arch.exception_addr;
+
+ if (&_except_start != except_target) {
+ memcpy(except_target, &_except_start,
+ &_except_end - &_except_start);
+ flush_cache(gd->arch.exception_addr,
+ &_except_end - &_except_start);
+ }
}
+#endif
-void dcache_enable(void)
+int arch_cpu_init_dm(void)
{
- flush_dcache(CONFIG_SYS_DCACHE_SIZE, CONFIG_SYS_DCACHELINE_SIZE);
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_first_device(UCLASS_CPU, &dev);
+ if (ret)
+ return ret;
+ if (!dev)
+ return -ENODEV;
+
+ gd->ram_size = CONFIG_SYS_SDRAM_SIZE;
+#ifndef CONFIG_ROM_STUBS
+ copy_exception_trampoline();
+#endif
+
+ return 0;
}
-void dcache_disable(void)
+static int altera_nios2_get_desc(struct udevice *dev, char *buf, int size)
{
- flush_dcache(CONFIG_SYS_DCACHE_SIZE, CONFIG_SYS_DCACHELINE_SIZE);
+ const char *cpu_name = "Nios-II";
+
+ if (size < strlen(cpu_name))
+ return -ENOSPC;
+ strcpy(buf, cpu_name);
+
+ return 0;
}
-int arch_cpu_init(void)
+static int altera_nios2_get_info(struct udevice *dev, struct cpu_info *info)
{
- gd->cpu_clk = CONFIG_SYS_CLK_FREQ;
- gd->ram_size = CONFIG_SYS_SDRAM_SIZE;
+ info->cpu_freq = gd->cpu_clk;
+ info->features = (1 << CPU_FEAT_L1_CACHE) |
+ (gd->arch.has_mmu ? (1 << CPU_FEAT_MMU) : 0);
return 0;
}
+
+static int altera_nios2_get_count(struct udevice *dev)
+{
+ return 1;
+}
+
+static int altera_nios2_probe(struct udevice *dev)
+{
+ const void *blob = gd->fdt_blob;
+ int node = dev->of_offset;
+
+ gd->cpu_clk = fdtdec_get_int(blob, node,
+ "clock-frequency", 0);
+ gd->arch.dcache_line_size = fdtdec_get_int(blob, node,
+ "dcache-line-size", 0);
+ gd->arch.icache_line_size = fdtdec_get_int(blob, node,
+ "icache-line-size", 0);
+ gd->arch.dcache_size = fdtdec_get_int(blob, node,
+ "dcache-size", 0);
+ gd->arch.icache_size = fdtdec_get_int(blob, node,
+ "icache-size", 0);
+ gd->arch.reset_addr = fdtdec_get_int(blob, node,
+ "altr,reset-addr", 0);
+ gd->arch.exception_addr = fdtdec_get_int(blob, node,
+ "altr,exception-addr", 0);
+ gd->arch.has_initda = fdtdec_get_int(blob, node,
+ "altr,has-initda", 0);
+ gd->arch.has_mmu = fdtdec_get_int(blob, node,
+ "altr,has-mmu", 0);
+ gd->arch.io_region_base = gd->arch.has_mmu ? 0xe0000000 : 0x8000000;
+
+ return 0;
+}
+
+static const struct cpu_ops altera_nios2_ops = {
+ .get_desc = altera_nios2_get_desc,
+ .get_info = altera_nios2_get_info,
+ .get_count = altera_nios2_get_count,
+};
+
+static const struct udevice_id altera_nios2_ids[] = {
+ { .compatible = "altr,nios2-1.0" },
+ { .compatible = "altr,nios2-1.1" },
+ { }
+};
+
+U_BOOT_DRIVER(altera_nios2) = {
+ .name = "altera_nios2",
+ .id = UCLASS_CPU,
+ .of_match = altera_nios2_ids,
+ .probe = altera_nios2_probe,
+ .ops = &altera_nios2_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/arch/nios2/cpu/interrupts.c b/arch/nios2/cpu/interrupts.c
index 9d7e193..1599674 100644
--- a/arch/nios2/cpu/interrupts.c
+++ b/arch/nios2/cpu/interrupts.c
@@ -8,43 +8,14 @@
* SPDX-License-Identifier: GPL-2.0+
*/
-
+#include <common.h>
+#include <command.h>
#include <asm/nios2.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
-
-typedef volatile struct {
- unsigned status; /* Timer status reg */
- unsigned control; /* Timer control reg */
- unsigned periodl; /* Timeout period low */
- unsigned periodh; /* Timeout period high */
- unsigned snapl; /* Snapshot low */
- unsigned snaph; /* Snapshot high */
-} nios_timer_t;
-
-/* status register */
-#define NIOS_TIMER_TO (1 << 0) /* Timeout */
-#define NIOS_TIMER_RUN (1 << 1) /* Timer running */
-
-/* control register */
-#define NIOS_TIMER_ITO (1 << 0) /* Timeout int ena */
-#define NIOS_TIMER_CONT (1 << 1) /* Continuous mode */
-#define NIOS_TIMER_START (1 << 2) /* Start timer */
-#define NIOS_TIMER_STOP (1 << 3) /* Stop timer */
-
-#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;
@@ -53,111 +24,6 @@ struct irq_action {
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);
@@ -245,10 +111,6 @@ int interrupt_init (void)
vecs[i].count = 0;
}
-#if defined(CONFIG_SYS_NIOS_TMRBASE)
- tmr_init ();
-#endif
-
enable_interrupts ();
return (0);
}
diff --git a/arch/nios2/cpu/start.S b/arch/nios2/cpu/start.S
index 6af9b4e..8758e7e 100644
--- a/arch/nios2/cpu/start.S
+++ b/arch/nios2/cpu/start.S
@@ -9,30 +9,38 @@
#include <config.h>
#include <version.h>
-/*************************************************************************
- * RESTART
- ************************************************************************/
+/*
+ * icache and dcache configuration used only for start.S.
+ * the values are chosen so that it will work for all configuration.
+ */
+#define ICACHE_LINE_SIZE 32 /* fixed 32 */
+#define ICACHE_SIZE_MAX 0x10000 /* 64k max */
+#define DCACHE_LINE_SIZE_MIN 4 /* 4, 16, 32 */
+#define DCACHE_SIZE_MAX 0x10000 /* 64k max */
+ /* RESTART */
.text
- .global _start
+ .global _start, _except_start, _except_end
_start:
wrctl status, r0 /* Disable interrupts */
- /* ICACHE INIT -- only the icache line at the reset address
+ /*
+ * 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)
+ ori r4, r0, %lo(ICACHE_LINE_SIZE)
+ movhi r5, %hi(ICACHE_SIZE_MAX)
+ ori r5, r5, %lo(ICACHE_SIZE_MAX)
0: initi r5
sub r5, r5, r4
bgt r5, r0, 0b
br _except_end /* Skip the tramp */
- /* EXCEPTION TRAMPOLINE -- the following gets copied
+ /*
+ * EXCEPTION TRAMPOLINE -- the following gets copied
* to the exception address (below), but is otherwise at the
* default exception vector offset (0x0020).
*/
@@ -42,24 +50,26 @@ _except_start:
jmp et
_except_end:
- /* INTERRUPTS -- for now, all interrupts masked and globally
+ /*
+ * INTERRUPTS -- for now, all interrupts masked and globally
* disabled.
*/
wrctl ienable, r0 /* All disabled */
- /* DCACHE INIT -- if dcache not implemented, initd behaves as
+ /*
+ * 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)
+ ori r4, r0, %lo(DCACHE_LINE_SIZE_MIN)
+ movhi r5, %hi(DCACHE_SIZE_MAX)
+ ori r5, r5, %lo(DCACHE_SIZE_MAX)
mov r6, r0
1: initd 0(r6)
add r6, r6, r4
bltu r6, r5, 1b
- /* RELOCATE CODE, DATA & COMMAND TABLE -- the following code
+ /*
+ * 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 ;-)
@@ -73,8 +83,9 @@ _cur: movhi r5, %hi(_cur - _start)
ori r5, r5, %lo(_start) /* r5 <- linked _start */
beq r4, r5, 3f
- movhi r6, %hi(_edata)
- ori r6, r6, %lo(_edata)
+ movhi r6, %hi(CONFIG_SYS_MONITOR_LEN)
+ ori r6, r6, %lo(CONFIG_SYS_MONITOR_LEN)
+ add r6, r6, r5
2: ldwio r7, 0(r4)
addi r4, r4, 4
stwio r7, 0(r5)
@@ -82,50 +93,13 @@ _cur: movhi r5, %hi(_cur - _start)
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.
- */
+ /* 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
@@ -133,80 +107,64 @@ _reloc:
stw r0, 4(sp)
mov fp, sp
- /*
- * Call board_init_f -- never returns
- */
+ /* Allocate and zero GD, update SP */
+ mov r4, sp
+ movhi r2, %hi(board_init_f_mem@h)
+ ori r2, r2, %lo(board_init_f_mem@h)
+ callr r2
+
+ /* Update stack- and frame-pointers */
+ mov sp, r2
+ mov fp, sp
+
+ /* Call board_init_f -- never returns */
mov r4, r0
movhi r2, %hi(board_init_f@h)
ori r2, r2, %lo(board_init_f@h)
callr r2
- /* NEVER RETURNS -- but branch to the _start just
+ /*
+ * NEVER RETURNS -- but branch to the _start just
* in case ;-)
*/
br _start
-
-
-/*
- * relocate_code -- Nios2 handles the relocation above. But
- * the generic board code monkeys with the heap, stack, etc.
- * (it makes some assumptions that may not be appropriate
- * for Nios). Nevertheless, we capitulate here.
- *
- * We'll call the board_init_r from here since this isn't
- * supposed to return.
- *
- * void relocate_code (ulong sp, gd_t *global_data,
- * ulong reloc_addr)
- * __attribute__ ((noreturn));
- */
+ /*
+ * relocate_code -- Nios2 handles the relocation above. But
+ * the generic board code monkeys with the heap, stack, etc.
+ * (it makes some assumptions that may not be appropriate
+ * for Nios). Nevertheless, we capitulate here.
+ *
+ * We'll call the board_init_r from here since this isn't
+ * supposed to return.
+ *
+ * void relocate_code (ulong sp, gd_t *global_data,
+ * ulong reloc_addr)
+ * __attribute__ ((noreturn));
+ */
.text
.global relocate_code
relocate_code:
mov sp, r4 /* Set the new sp */
mov r4, r5
- movhi r8, %hi(board_init_r@h)
- ori r8, r8, %lo(board_init_r@h)
- callr r8
- ret
-/*
- * 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
+ /*
+ * 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
-dly_clks:
+4: stwio r0, 0(r5)
+ addi r5, r5, 4
+ bne r5, r6, 4b
+5:
-#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
+ movhi r8, %hi(board_init_r@h)
+ ori r8, r8, %lo(board_init_r@h)
+ callr r8
ret
-
- .data
- .globl version_string
-
-version_string:
- .ascii U_BOOT_VERSION_STRING, "\0"
diff --git a/arch/nios2/cpu/sysid.c b/arch/nios2/cpu/sysid.c
deleted file mode 100644
index 50819b2..0000000
--- a/arch/nios2/cpu/sysid.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * (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 <linux/time.h>
-
-typedef volatile struct {
- unsigned id; /* The system build id */
- unsigned timestamp; /* Timestamp */
-} nios_sysid_t;
-
-void display_sysid (void)
-{
- nios_sysid_t *sysid = (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/arch/nios2/cpu/u-boot.lds b/arch/nios2/cpu/u-boot.lds
index 6e174be..3bd3f2c 100644
--- a/arch/nios2/cpu/u-boot.lds
+++ b/arch/nios2/cpu/u-boot.lds
@@ -50,9 +50,11 @@ SECTIONS
*(.gnu.linkonce.d*)
}
- . = ALIGN(16);
- _gp = .; /* Global pointer addr */
- PROVIDE (gp = .);
+ /*
+ * gp - Since we don't use gp for small data with option "-G0",
+ * we will use gp as global data pointer. The _gp location is
+ * not needed.
+ */
.sdata :
{
@@ -65,6 +67,12 @@ SECTIONS
_edata = .;
PROVIDE (edata = .);
+ /*
+ * _end - This is end of u-boot.bin image.
+ * dtb will be appended here to make u-boot-dtb.bin
+ */
+ _end = .;
+
/* 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.