diff options
Diffstat (limited to 'examples/standalone')
23 files changed, 4357 insertions, 0 deletions
diff --git a/examples/standalone/.gitignore b/examples/standalone/.gitignore new file mode 100644 index 0000000..0d1864c --- /dev/null +++ b/examples/standalone/.gitignore @@ -0,0 +1,11 @@ +/82559_eeprom +/hello_world +/interrupt +/mem_to_mem_idma2intr +/test_burst +/timer +/sched +/smc91111_eeprom +/smc911x_eeprom +*.bin +*.srec diff --git a/examples/standalone/82559_eeprom.c b/examples/standalone/82559_eeprom.c new file mode 100644 index 0000000..5e2eee9 --- /dev/null +++ b/examples/standalone/82559_eeprom.c @@ -0,0 +1,357 @@ + +/* + * Copyright 1998-2001 by Donald Becker. + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by reference. + * Contact the author for use under other terms. + * + * This program must be compiled with "-O"! + * See the bottom of this file for the suggested compile-command. + * + * The author may be reached as becker@scyld.com, or C/O + * Scyld Computing Corporation + * 410 Severn Ave., Suite 210 + * Annapolis MD 21403 + * + * Common-sense licensing statement: Using any portion of this program in + * your own program means that you must give credit to the original author + * and release the resulting code under the GPL. + */ + +#define _PPC_STRING_H_ /* avoid unnecessary str/mem functions */ + +#include <common.h> +#include <exports.h> +#include <asm/io.h> + + +/* Default EEPROM for i82559 */ +static unsigned short default_eeprom[64] = { + 0x0100, 0x0302, 0x0504, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0x40c0, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + +static unsigned short eeprom[256]; + +static int eeprom_size = 64; +static int eeprom_addr_size = 6; + +static int debug = 0; + +static inline unsigned short swap16(unsigned short x) +{ + return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); +} + + +void * memcpy(void * dest,const void *src,size_t count) +{ + char *tmp = (char *) dest, *s = (char *) src; + + while (count--) + *tmp++ = *s++; + + return dest; +} + + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +/* Serial EEPROM section. */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) +#define EE_WRITE_0 0x4802 +#define EE_WRITE_1 0x4806 +#define EE_OFFSET 14 + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay(ee_addr) inw(ee_addr) + +/* Wait for the EEPROM to finish the previous operation. */ +static int eeprom_busy_poll(long ee_ioaddr) +{ + int i; + outw(EE_ENB, ee_ioaddr); + for (i = 0; i < 10000; i++) /* Typical 2000 ticks */ + if (inw(ee_ioaddr) & EE_DATA_READ) + break; + return i; +} + +/* This executes a generic EEPROM command, typically a write or write enable. + It returns the data output from the EEPROM, and thus may also be used for + reads. */ +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len) +{ + unsigned retval = 0; + long ee_addr = ioaddr + EE_OFFSET; + + if (debug > 1) + printf(" EEPROM op 0x%x: ", cmd); + + outw(EE_ENB | EE_SHIFT_CLK, ee_addr); + + /* Shift the command bits out. */ + do { + short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; + outw(dataval, ee_addr); + eeprom_delay(ee_addr); + if (debug > 2) + printf("%X", inw(ee_addr) & 15); + outw(dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); + } while (--cmd_len >= 0); +#if 0 + outw(EE_ENB, ee_addr); +#endif + /* Terminate the EEPROM access. */ + outw(EE_ENB & ~EE_CS, ee_addr); + if (debug > 1) + printf(" EEPROM result is 0x%5.5x.\n", retval); + return retval; +} + +static int read_eeprom(long ioaddr, int location, int addr_len) +{ + return do_eeprom_cmd(ioaddr, ((EE_READ_CMD << addr_len) | location) + << 16 , 3 + addr_len + 16) & 0xffff; +} + +static void write_eeprom(long ioaddr, int index, int value, int addr_len) +{ + long ee_ioaddr = ioaddr + EE_OFFSET; + int i; + + /* Poll for previous op finished. */ + eeprom_busy_poll(ee_ioaddr); /* Typical 0 ticks */ + /* Enable programming modes. */ + do_eeprom_cmd(ioaddr, (0x4f << (addr_len-4)), 3 + addr_len); + /* Do the actual write. */ + do_eeprom_cmd(ioaddr, + (((EE_WRITE_CMD<<addr_len) | index)<<16) | (value & 0xffff), + 3 + addr_len + 16); + /* Poll for write finished. */ + i = eeprom_busy_poll(ee_ioaddr); /* Typical 2000 ticks */ + if (debug) + printf(" Write finished after %d ticks.\n", i); + /* Disable programming. This command is not instantaneous, so we check + for busy before the next op. */ + do_eeprom_cmd(ioaddr, (0x40 << (addr_len-4)), 3 + addr_len); + eeprom_busy_poll(ee_ioaddr); +} + +static int reset_eeprom(unsigned long ioaddr, unsigned char *hwaddr) +{ + unsigned short checksum = 0; + int size_test; + int i; + + printf("Resetting i82559 EEPROM @ 0x%08lx ... ", ioaddr); + + size_test = do_eeprom_cmd(ioaddr, (EE_READ_CMD << 8) << 16, 27); + eeprom_addr_size = (size_test & 0xffe0000) == 0xffe0000 ? 8 : 6; + eeprom_size = 1 << eeprom_addr_size; + + memcpy(eeprom, default_eeprom, sizeof default_eeprom); + + for (i = 0; i < 3; i++) + eeprom[i] = (hwaddr[i*2+1]<<8) + hwaddr[i*2]; + + /* Recalculate the checksum. */ + for (i = 0; i < eeprom_size - 1; i++) + checksum += eeprom[i]; + eeprom[i] = 0xBABA - checksum; + + for (i = 0; i < eeprom_size; i++) + write_eeprom(ioaddr, i, eeprom[i], eeprom_addr_size); + + for (i = 0; i < eeprom_size; i++) + if (read_eeprom(ioaddr, i, eeprom_addr_size) != eeprom[i]) { + printf("failed\n"); + return 1; + } + + printf("done\n"); + return 0; +} + +static unsigned int hatoi(char *p, char **errp) +{ + unsigned int res = 0; + + while (1) { + switch (*p) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + res |= (*p - 'a' + 10); + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + res |= (*p - 'A' + 10); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + res |= (*p - '0'); + break; + default: + if (errp) { + *errp = p; + } + return res; + } + p++; + if (*p == 0) { + break; + } + res <<= 4; + } + + if (errp) { + *errp = NULL; + } + + return res; +} + +static unsigned char *gethwaddr(char *in, unsigned char *out) +{ + char tmp[3]; + int i; + char *err; + + for (i=0;i<6;i++) { + if (in[i*3+2] == 0 && i == 5) { + out[i] = hatoi(&in[i*3], &err); + if (err) { + return NULL; + } + } else if (in[i*3+2] == ':' && i < 5) { + tmp[0] = in[i*3]; + tmp[1] = in[i*3+1]; + tmp[2] = 0; + out[i] = hatoi(tmp, &err); + if (err) { + return NULL; + } + } else { + return NULL; + } + } + + return out; +} + +static u32 +read_config_dword(int bus, int dev, int func, int reg) +{ + u32 res; + + outl(0x80000000|(bus&0xff)<<16|(dev&0x1f)<<11|(func&7)<<8|(reg&0xfc), + 0xcf8); + res = inl(0xcfc); + outl(0, 0xcf8); + return res; +} + +static u16 +read_config_word(int bus, int dev, int func, int reg) +{ + u32 res; + + outl(0x80000000|(bus&0xff)<<16|(dev&0x1f)<<11|(func&7)<<8|(reg&0xfc), + 0xcf8); + res = inw(0xcfc + (reg & 2)); + outl(0, 0xcf8); + return res; +} + +static void +write_config_word(int bus, int dev, int func, int reg, u16 data) +{ + + outl(0x80000000|(bus&0xff)<<16|(dev&0x1f)<<11|(func&7)<<8|(reg&0xfc), + 0xcf8); + outw(data, 0xcfc + (reg & 2)); + outl(0, 0xcf8); +} + + +int main (int argc, char *argv[]) +{ + unsigned char *eth_addr; + uchar buf[6]; + int instance; + + app_startup(argv); + if (argc != 2) { + printf ("call with base Ethernet address\n"); + return 1; + } + + + eth_addr = gethwaddr(argv[1], buf); + if (NULL == eth_addr) { + printf ("Can not parse ethernet address\n"); + return 1; + } + if (eth_addr[5] & 0x01) { + printf("Base Ethernet address must be even\n"); + } + + + for (instance = 0; instance < 2; instance ++) { + unsigned int io_addr; + unsigned char mac[6]; + int bar1 = read_config_dword(0, 6+instance, 0, 0x14); + if (! (bar1 & 1)) { + printf("ETH%d is disabled %x\n", instance, bar1); + } else { + printf("ETH%d IO=0x%04x\n", instance, bar1 & ~3); + } + io_addr = (bar1 & (~3L)); + + + write_config_word(0, 6+instance, 0, 4, + read_config_word(0, 6+instance, 0, 4) | 1); + printf("ETH%d CMD %04x\n", instance, + read_config_word(0, 6+instance, 0, 4)); + + memcpy(mac, eth_addr, 6); + mac[5] += instance; + + printf("got io=%04x, ha=%02x:%02x:%02x:%02x:%02x:%02x\n", + io_addr, mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + reset_eeprom(io_addr, mac); + } + return 0; +} diff --git a/examples/standalone/Makefile b/examples/standalone/Makefile new file mode 100644 index 0000000..dbcfa92 --- /dev/null +++ b/examples/standalone/Makefile @@ -0,0 +1,198 @@ +# +# (C) Copyright 2000-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 +# + +ifeq ($(ARCH),ppc) +LOAD_ADDR = 0x40000 +endif + +ifeq ($(ARCH),i386) +LOAD_ADDR = 0x40000 +endif + +ifeq ($(ARCH),arm) +ifeq ($(BOARD),omap2420h4) +LOAD_ADDR = 0x80300000 +else +ifeq ($(CPU),omap3) +LOAD_ADDR = 0x80300000 +else +LOAD_ADDR = 0xc100000 +endif +endif +endif + +ifeq ($(ARCH),mips) +LOAD_ADDR = 0x80200000 -T mips.lds +endif + +ifeq ($(ARCH),nios) +LOAD_ADDR = 0x00800000 -L $(gcclibdir)/m32 -T nios.lds +endif + +ifeq ($(ARCH),nios2) +LOAD_ADDR = 0x02000000 -L $(gcclibdir) -T nios2.lds +endif + +ifeq ($(ARCH),m68k) +LOAD_ADDR = 0x20000 -L $(clibdir) +endif + +ifeq ($(ARCH),microblaze) +LOAD_ADDR = 0x80F00000 +endif + +ifeq ($(ARCH),blackfin) +LOAD_ADDR = 0x1000 +endif + +ifeq ($(ARCH),avr32) +LOAD_ADDR = 0x00000000 +endif + +ifeq ($(ARCH),sh) +LOAD_ADDR = 0x8C000000 +ifeq ($(CPU),sh2) +BIG_ENDIAN=y +endif +endif + +ifeq ($(ARCH),sparc) +LOAD_ADDR = 0x00000000 -L $(gcclibdir) -T sparc.lds +endif + +include $(TOPDIR)/config.mk + +ELF = hello_world +SREC = hello_world.srec +BIN = hello_world.bin + +ifeq ($(CPU),mpc8xx) +ELF += test_burst +SREC += test_burst.srec +BIN += test_burst.bin +endif + +ifeq ($(ARCH),i386) +ELF += 82559_eeprom +SREC += 82559_eeprom.srec +BIN += 82559_eeprom.bin +endif + +ifeq ($(ARCH),ppc) +ELF += sched +SREC += sched.srec +BIN += sched.bin +endif + +ifeq ($(ARCH),blackfin) +BFIN_BIN = smc91111_eeprom smc911x_eeprom +ELF += $(BFIN_BIN) +SREC += $(addsuffix .srec,$(BFIN_BIN)) +BIN += $(addsuffix .bin,$(BFIN_BIN)) +endif + +# The following example is pretty 8xx specific... +ifeq ($(CPU),mpc8xx) +ELF += timer +SREC += timer.srec +BIN += timer.bin +endif + +# The following example is 8260 specific... +ifeq ($(CPU),mpc8260) +ELF += mem_to_mem_idma2intr +SREC += mem_to_mem_idma2intr.srec +BIN += mem_to_mem_idma2intr.bin +endif + +# Demo for 52xx IRQs +ifeq ($(CPU),mpc5xxx) +ELF += interrupt +SREC += interrupt.srec +BIN += interrupt.bin +endif + +# Utility for resetting i82559 EEPROM +ifeq ($(BOARD),oxc) +ELF += eepro100_eeprom +SREC += eepro100_eeprom.srec +BIN += eepro100_eeprom.bin +endif + +ifeq ($(BIG_ENDIAN),y) +EX_LDFLAGS += -EB +endif + +COBJS := $(SREC:.srec=.o) + +LIB = $(obj)libstubs.a +LIBAOBJS= +ifeq ($(ARCH),ppc) +LIBAOBJS+= $(ARCH)_longjmp.o $(ARCH)_setjmp.o +endif +ifeq ($(CPU),mpc8xx) +LIBAOBJS+= test_burst_lib.o +endif +LIBCOBJS= stubs.o + +LIBOBJS = $(addprefix $(obj),$(LIBAOBJS) $(LIBCOBJS)) + +SRCS := $(COBJS:.o=.c) $(LIBCOBJS:.o=.c) $(if $(LIBAOBJS),$(LIBAOBJS:.o=.S)) +OBJS := $(addprefix $(obj),$(COBJS)) +ELF := $(addprefix $(obj),$(ELF)) +BIN := $(addprefix $(obj),$(BIN)) +SREC := $(addprefix $(obj),$(SREC)) + +gcclibdir := $(shell dirname `$(CC) -print-libgcc-file-name`) +clibdir := $(shell dirname `$(CC) $(CFLAGS) -print-file-name=libc.a`) + +CPPFLAGS += -I.. + +all: $(obj).depend $(OBJS) $(LIB) $(SREC) $(BIN) $(ELF) + +######################################################################### +$(LIB): $(obj).depend $(LIBOBJS) + $(AR) $(ARFLAGS) $@ $(LIBOBJS) + +$(ELF): +$(obj)%: $(obj)%.o $(LIB) + $(LD) -g $(EX_LDFLAGS) -Ttext $(LOAD_ADDR) \ + -o $@ -e $(SYM_PREFIX)$(notdir $(<:.o=)) $< $(LIB) \ + -L$(gcclibdir) -lgcc + +$(SREC): +$(obj)%.srec: $(obj)% + $(OBJCOPY) -O srec $< $@ 2>/dev/null + +$(BIN): +$(obj)%.bin: $(obj)% + $(OBJCOPY) -O binary $< $@ 2>/dev/null + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/examples/standalone/README.smc91111_eeprom b/examples/standalone/README.smc91111_eeprom new file mode 100644 index 0000000..28e7e69 --- /dev/null +++ b/examples/standalone/README.smc91111_eeprom @@ -0,0 +1,246 @@ +This is the readme for the Das U-Boot standalone program smc91111 + +The main purpose of this is to manage MAC addresses on platforms +which include the SMC91111 integrated 10/100 MAC Phy, with attached +EEPROMs. + + +Contents: +------------------------ +1. Ensuring U-boot's MAC address can be set in hardware +2. Running the smc91111_eeprom program +3. Setting MAC addresses +4. Other things you can do with this +5. Things to be done. + + +1. Ensuring U-boot's MAC address can be set in hardware +-------------------------------------------------------------------------- + +On the Internet - MAC addresses are very important. Short for Media +Access Control address, a hardware address that uniquely identifies +each node of a network. When things are not unique - bad things +can happen. This is why U-Boot makes it difficult to change MAC +addresses. + +To find out who has a MAC address, or to purchase MAC addresses, goto +the IEEE, at: +http://standards.ieee.org/regauth/oui/index.shtml + +To change your MAC address, there can not be a MAC address predefined in +U-Boot. To ensure that this does not occur, check your +include/configs/<board_name>.h file, and check to see that the following +settings are _not_ or commented out there. + +#define HARDCODE_MAC 1 +#define CONFIG_ETHADDR 02:80:ad:20:31:b8 + +The purpose of HARDCODE_MAC is to hardcode the MAC address in software, +(not what we want), or to preset it to 02:80:ad:20:31:b8 (not what we +want either). + +You can check this in a running U-Boot, by doing a power cycle, then +before U-Boot tries to do any networking, running the 'printenv' command + + BOOT> printenv + + ethaddr=02:80:ad:20:31:b8 + +If you see the 'ethaddr' variable show up, like the above, you need to +recompile U-Boot, with the above settings commented out of the +include/configs/<board_name>.h file. + +2. Running the smc91111_eeprom program +--------------------------------------------------------------------- + +After Uboot is compiled, there should be three files of interest: +-rwxr-xr-x 1 8806 2004-10-11 14:00 smc91111_eeprom <- ELF +-rwxr-xr-x 1 3440 2004-10-11 14:00 smc91111_eeprom.bin <- BIN +-rwxr-xr-x 1 9524 2004-10-11 14:00 smc91111_eeprom.srec <- SREC + +if there is not, check the examples/Makefile, and ensure there is something +like for your architecture: + + ifeq ($(ARCH),blackfin) + SREC += smc91111_eeprom.srec + BIN += smc91111_eeprom.bin smc91111_eeprom + endif + +To load the files: there are two methods: a) serial or b) network. Since +it is not a good idea to start doing things on the network before the +MAC address is set, this example will do things over serial. + +a) Loading the elf file via the serial port +-------------------------------------------- +Loading the elf is very easy - just ensure that the location +you specify things to load as is not the load address specified +in the Makefile. + +BOOT> loadb 0x1000000 + +## Ready for binary (kermit) download to 0x01000000 at 57600 bps... + +(type CNTL-\ then C) +(Back at local machine) +---------------------------------------------------- +Kermit>send ~/u-boot_1.1.1/examples/smc91111_eeprom +Kermit>connect + +Connecting to /dev/ttyS0, speed 57600 + Escape character: Ctrl-\ (ASCII 28, FS): enabled +Type the escape character followed by C to get back, +or followed by ? to see other options. +---------------------------------------------------- +## Total Size = 0x00002266 = 8806 Bytes +## Start Addr = 0x01000000 + +BOOT> bootelf 0x1000000 + +Loading .text @ 0x00001000 (3440 bytes) +## Starting application at 0x000010d8 ... + +SMC91111> + +b) Loading the binary file via the serial port +----------------------------------------------- +For many toolchains, the entry point is not the load point. +The Load point is a hard coded address from the +examples/Makefile. The entry point can be found by doing something +like: + + u-boot_1.1.1/examples> bfin-elf-objdump -d smc91111_eeprom |less + + smc91111_eeprom: file format elf32-bfin + + Disassembly of section .text: + + 00001000 <smc91111_eeprom-0xd8>: + 1000: + 000010d8 <smc91111_eeprom>: + +You can see that the entry point (or the address that should be +jumped to is 0x10d8). This is also the same as the entry point +of the elf file. + +Now we load it to the actual load location: + +BOOT> loadb 0x1000 + +## Ready for binary (kermit) download to 0x00001000 at 57600 bps... + +(Back at pinky.dsl-only.net) +---------------------------------------------------- +Kermit>send /tftpboot/eeprom.bin +Kermit>connect + +Connecting to /dev/ttyS0, speed 57600 + Escape character: Ctrl-\ (ASCII 28, FS): enabled +Type the escape character followed by C to get back, +or followed by ? to see other options. +---------------------------------------------------- +## Total Size = 0x00000d70 = 3440 Bytes +## Start Addr = 0x00001000 + +BOOT> go 0x10D8 + +## Starting application at 0x000010D8 ... + +SMC91111> + +3. Setting MAC addresses +-------------------------------------------------------------------------- + +The MAC address can be stored in four locations: + +-Boot environmental variable in Flash <- can not change, without + re-flashing U-boot. +U-Boot environental variable <- can not change, without + resetting board/U-Boot +LAN91C111 Registers <- volitle +LAN91C111 EEPROM <- Non Volitle + +If you have not activated the network, and do not have a hardcoded +or pre-assigned MAC address in U-boot, the environmental variables +should be blank, and allow you to set things one time. + +To set the EEPROM MAC address to 12:34:56:78:9A:BC + +SMC91111> W E 20 3412 + +Writing EEPROM register 20 with 3412 +SMC91111> W E 21 7856 + +Writing EEPROM register 21 with 7856 +SMC91111> W E 22 BC9A + +Writing EEPROM register 22 with bc9a +EEPROM contents copied to MAC +SMC91111> P + +Current MAC Address in SMSC91111 12:34:56:78:9a:bc +Current MAC Address in EEPROM 12:34:56:78:9a:bc + +(CNTRL-C to exit) +SMC91111> ## Application terminated, rc = 0x0 + +BOOT> reset +U-Boot 1.1.1 (gcc version: 3.3.3) +Release Version Beta released on Oct 10 2004 - 00:34:35 +Blackfin support by LG Soft India +For further information please check this link http://www.blackfin.uclinux.org +BOOT> ping 192.168.0.4 + +Using MAC Address 12:34:56:78:9A:BC +host 192.168.0.4 is alive + + +4. Other things that you can do +-------------------------------------------------------------------------- +After the stand alone application is running, there are a few options: + - P : Print the MAC + - D : Dump the LAN91C111 EEPROM contents + - M : Dump the LAN91C111 MAC contents + - C : Copies the MAC address from the EEPROM to the LAN91C111 + - W : Write a register in the EEPROM or in the MAC + +SMC91111> P + +Current MAC Address in SMSC91111 12:34:56:78:9a:bc +Current MAC Address in EEPROM 12:34:56:78:9a:bc + +SMC91111> D + +IOS2-0 000 001 002 003 004 005 006 007 +CONFIG 00:ffff 04:ffff 08:ffff 0c:ffff 10:ffff 14:ffff 18:ffff 1c:ffff +BASE 01:ffff 05:ffff 09:ffff 0d:ffff 11:ffff 15:ffff 19:ffff 1d:ffff + 02:ffff 06:ffff 0a:ffff 0e:0020 12:ffff 16:ffff 1a:ffff 1e:ffff + 03:ffff 07:ffff 0b:ffff 0f:ffff 13:ffff 17:ffff 1b:ffff 1f:ffff + +20:3412 21:7856 22:bc9a 23:ffff 24:ffff 25:ffff 26:ffff 27:ffff +28:ffff 29:ffff 2a:ffff 2b:ffff 2c:ffff 2d:ffff 2e:ffff 2f:ffff +30:ffff 31:ffff 32:ffff 33:ffff 34:ffff 35:ffff 36:ffff 37:ffff +38:ffff 39:ffff 3a:ffff 3b:ffff 3c:ffff 3d:ffff 3e:ffff 3f:ffff + +SMC91111> M + + Bank0 Bank1 Bank2 Bank3 +00 0000 a0b1 3332 0000 +02 0000 1801 8000 0000 +04 0000 3412 8080 0000 +06 0000 7856 003f 0000 +08 0404 bc9a 02df 3332 +0a 0000 ffff 02df 3391 +0c 0000 1214 0004 001f +0e 3300 3301 3302 3303 + +SMC91111> C + +EEPROM contents copied to MAC + +SMC91111> W E 2A ABCD + +Writing EEPROM register 2a with abcd + +SMC91111> W M 14 FF00 + +Writing MAC register bank 1, reg 04 with ff00 diff --git a/examples/standalone/eepro100_eeprom.c b/examples/standalone/eepro100_eeprom.c new file mode 100644 index 0000000..2b15d05 --- /dev/null +++ b/examples/standalone/eepro100_eeprom.c @@ -0,0 +1,215 @@ +/* + * Copyright 1998-2001 by Donald Becker. + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by reference. + * Contact the author for use under other terms. + * + * This program must be compiled with "-O"! + * See the bottom of this file for the suggested compile-command. + * + * The author may be reached as becker@scyld.com, or C/O + * Scyld Computing Corporation + * 410 Severn Ave., Suite 210 + * Annapolis MD 21403 + * + * Common-sense licensing statement: Using any portion of this program in + * your own program means that you must give credit to the original author + * and release the resulting code under the GPL. + */ + +/* avoid unnecessary memcpy function */ +#define __HAVE_ARCH_MEMCPY +#define _PPC_STRING_H_ + +#include <common.h> +#include <exports.h> + +static int reset_eeprom(unsigned long ioaddr, unsigned char *hwaddr); + +int eepro100_eeprom(int argc, char *argv[]) +{ + int ret = 0; + + unsigned char hwaddr1[6] = { 0x00, 0x00, 0x02, 0x03, 0x04, 0x05 }; + unsigned char hwaddr2[6] = { 0x00, 0x00, 0x02, 0x03, 0x04, 0x06 }; + + app_startup(argv); + +#if defined(CONFIG_OXC) + ret |= reset_eeprom(0x80000000, hwaddr1); + ret |= reset_eeprom(0x81000000, hwaddr2); +#endif + + return ret; +} + +/* Default EEPROM for i82559 */ +static unsigned short default_eeprom[64] = { + 0x0100, 0x0302, 0x0504, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0x40c0, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + +static unsigned short eeprom[256]; + +static int eeprom_size = 64; +static int eeprom_addr_size = 6; + +static int debug = 0; + +static inline unsigned short swap16(unsigned short x) +{ + return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); +} + +static inline void outw(short data, long addr) +{ + *(volatile short *)(addr) = swap16(data); +} + +static inline short inw(long addr) +{ + return swap16(*(volatile short *)(addr)); +} + +static inline void *memcpy(void *dst, const void *src, unsigned int len) +{ + char *ret = dst; + while (len-- > 0) { + *ret++ = *((char *)src); + src++; + } + return (void *)ret; +} + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +/* Serial EEPROM section. */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) +#define EE_WRITE_0 0x4802 +#define EE_WRITE_1 0x4806 +#define EE_OFFSET 14 + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay(ee_addr) inw(ee_addr) + +/* Wait for the EEPROM to finish the previous operation. */ +static int eeprom_busy_poll(long ee_ioaddr) +{ + int i; + outw(EE_ENB, ee_ioaddr); + for (i = 0; i < 10000; i++) /* Typical 2000 ticks */ + if (inw(ee_ioaddr) & EE_DATA_READ) + break; + return i; +} + +/* This executes a generic EEPROM command, typically a write or write enable. + It returns the data output from the EEPROM, and thus may also be used for + reads. */ +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len) +{ + unsigned retval = 0; + long ee_addr = ioaddr + EE_OFFSET; + + if (debug > 1) + printf(" EEPROM op 0x%x: ", cmd); + + outw(EE_ENB | EE_SHIFT_CLK, ee_addr); + + /* Shift the command bits out. */ + do { + short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; + outw(dataval, ee_addr); + eeprom_delay(ee_addr); + if (debug > 2) + printf("%X", inw(ee_addr) & 15); + outw(dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); + } while (--cmd_len >= 0); +#if 0 + outw(EE_ENB, ee_addr); +#endif + /* Terminate the EEPROM access. */ + outw(EE_ENB & ~EE_CS, ee_addr); + if (debug > 1) + printf(" EEPROM result is 0x%5.5x.\n", retval); + return retval; +} + +static int read_eeprom(long ioaddr, int location, int addr_len) +{ + return do_eeprom_cmd(ioaddr, ((EE_READ_CMD << addr_len) | location) + << 16 , 3 + addr_len + 16) & 0xffff; +} + +static void write_eeprom(long ioaddr, int index, int value, int addr_len) +{ + long ee_ioaddr = ioaddr + EE_OFFSET; + int i; + + /* Poll for previous op finished. */ + eeprom_busy_poll(ee_ioaddr); /* Typical 0 ticks */ + /* Enable programming modes. */ + do_eeprom_cmd(ioaddr, (0x4f << (addr_len-4)), 3 + addr_len); + /* Do the actual write. */ + do_eeprom_cmd(ioaddr, + (((EE_WRITE_CMD<<addr_len) | index)<<16) | (value & 0xffff), + 3 + addr_len + 16); + /* Poll for write finished. */ + i = eeprom_busy_poll(ee_ioaddr); /* Typical 2000 ticks */ + if (debug) + printf(" Write finished after %d ticks.\n", i); + /* Disable programming. This command is not instantaneous, so we check + for busy before the next op. */ + do_eeprom_cmd(ioaddr, (0x40 << (addr_len-4)), 3 + addr_len); + eeprom_busy_poll(ee_ioaddr); +} + +static int reset_eeprom(unsigned long ioaddr, unsigned char *hwaddr) +{ + unsigned short checksum = 0; + int size_test; + int i; + + printf("Resetting i82559 EEPROM @ 0x%08lX ... ", ioaddr); + + size_test = do_eeprom_cmd(ioaddr, (EE_READ_CMD << 8) << 16, 27); + eeprom_addr_size = (size_test & 0xffe0000) == 0xffe0000 ? 8 : 6; + eeprom_size = 1 << eeprom_addr_size; + + memcpy(eeprom, default_eeprom, sizeof default_eeprom); + + for (i = 0; i < 3; i++) + eeprom[i] = (hwaddr[i*2+1]<<8) + hwaddr[i*2]; + + /* Recalculate the checksum. */ + for (i = 0; i < eeprom_size - 1; i++) + checksum += eeprom[i]; + eeprom[i] = 0xBABA - checksum; + + for (i = 0; i < eeprom_size; i++) + write_eeprom(ioaddr, i, eeprom[i], eeprom_addr_size); + + for (i = 0; i < eeprom_size; i++) + if (read_eeprom(ioaddr, i, eeprom_addr_size) != eeprom[i]) { + printf("failed\n"); + return 1; + } + + printf("done\n"); + return 0; +} diff --git a/examples/standalone/hello_world.c b/examples/standalone/hello_world.c new file mode 100644 index 0000000..9317f6d --- /dev/null +++ b/examples/standalone/hello_world.c @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2000 + * 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 <common.h> +#include <exports.h> + +int hello_world (int argc, char *argv[]) +{ + int i; + + /* Print the ABI version */ + app_startup(argv); + printf ("Example expects ABI version %d\n", XF_VERSION); + printf ("Actual U-Boot ABI version %d\n", (int)get_version()); + + printf ("Hello World\n"); + + printf ("argc = %d\n", argc); + + for (i=0; i<=argc; ++i) { + printf ("argv[%d] = \"%s\"\n", + i, + argv[i] ? argv[i] : "<NULL>"); + } + + printf ("Hit any key to exit ... "); + while (!tstc()) + ; + /* consume input */ + (void) getc(); + + printf ("\n\n"); + return (0); +} diff --git a/examples/standalone/interrupt.c b/examples/standalone/interrupt.c new file mode 100644 index 0000000..f3061d1 --- /dev/null +++ b/examples/standalone/interrupt.c @@ -0,0 +1,81 @@ +/* + * (C) Copyright 2006 + * Detlev Zundel, DENX Software Engineering, dzu@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 + * + * This is a very simple standalone application demonstrating + * catching IRQs on the MPC52xx architecture. + * + * The interrupt to be intercepted can be specified as an argument + * to the application. Specifying nothing will intercept IRQ1 on the + * MPC5200 platform. On the CR825 carrier board from MicroSys this + * maps to the ABORT switch :) + * + * Note that the specified vector is only a logical number specified + * by the respective header file. + */ + +#include <common.h> +#include <exports.h> +#include <config.h> + +#if defined(CONFIG_MPC5xxx) +#define DFL_IRQ MPC5XXX_IRQ1 +#else +#define DFL_IRQ 0 +#endif + +static void irq_handler (void *arg); + +int interrupt (int argc, char *argv[]) +{ + int c, irq = -1; + + app_startup (argv); + + if (argc > 1) + irq = simple_strtoul (argv[1], NULL, 0); + if ((irq < 0) || (irq > NR_IRQS)) + irq = DFL_IRQ; + + printf ("Installing handler for irq vector %d and doing busy wait\n", + irq); + printf ("Press 'q' to quit\n"); + + /* Install interrupt handler */ + install_hdlr (irq, irq_handler, NULL); + while ((c = getc ()) != 'q') { + printf ("Ok, ok, I am still alive!\n"); + } + + free_hdlr (irq); + printf ("\nInterrupt handler has been uninstalled\n"); + + return (0); +} + +/* + * Handler for interrupt + */ +static void irq_handler (void *arg) +{ + /* just for demonstration */ + printf ("+"); +} diff --git a/examples/standalone/mem_to_mem_idma2intr.c b/examples/standalone/mem_to_mem_idma2intr.c new file mode 100644 index 0000000..15779d0 --- /dev/null +++ b/examples/standalone/mem_to_mem_idma2intr.c @@ -0,0 +1,384 @@ +/* The dpalloc function used and implemented in this file was derieved + * from PPCBoot/U-Boot file "cpu/mpc8260/commproc.c". + */ + +/* Author: Arun Dharankar <ADharankar@ATTBI.Com> + * This example is meant to only demonstrate how the IDMA could be used. + */ + +/* + * This file is based on "arch/ppc/8260_io/commproc.c" - here is it's + * copyright notice: + * + * General Purpose functions for the global management of the + * 8260 Communication Processor Module. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2.3.99 Updates + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + */ + + +#include <common.h> +#include <exports.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define STANDALONE + +#ifndef STANDALONE /* Linked into/Part of PPCBoot */ +#include <command.h> +#include <watchdog.h> +#else /* Standalone app of PPCBoot */ +#define WATCHDOG_RESET() { \ + *(ushort *)(CONFIG_SYS_IMMR + 0x1000E) = 0x556c; \ + *(ushort *)(CONFIG_SYS_IMMR + 0x1000E) = 0xaa39; \ + } +#endif /* STANDALONE */ + +static int debug = 1; + +#define DEBUG(fmt, args...) { \ + if(debug != 0) { \ + printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__); \ + printf(fmt, ##args); \ + } \ +} + +#define CPM_CR_IDMA1_SBLOCK (0x14) +#define CPM_CR_IDMA2_SBLOCK (0x15) +#define CPM_CR_IDMA3_SBLOCK (0x16) +#define CPM_CR_IDMA4_SBLOCK (0x17) +#define CPM_CR_IDMA1_PAGE (0x07) +#define CPM_CR_IDMA2_PAGE (0x08) +#define CPM_CR_IDMA3_PAGE (0x09) +#define CPM_CR_IDMA4_PAGE (0x0a) +#define PROFF_IDMA1_BASE ((uint)0x87fe) +#define PROFF_IDMA2_BASE ((uint)0x88fe) +#define PROFF_IDMA3_BASE ((uint)0x89fe) +#define PROFF_IDMA4_BASE ((uint)0x8afe) + +#define CPM_CR_INIT_TRX ((ushort)0x0000) +#define CPM_CR_FLG ((ushort)0x0001) + +#define mk_cr_cmd(PG, SBC, MCN, OP) \ + ((PG << 26) | (SBC << 21) | (MCN << 6) | OP) + + +#pragma pack(1) +typedef struct ibdbits { + unsigned b_valid:1; + unsigned b_resv1:1; + unsigned b_wrap:1; + unsigned b_interrupt:1; + unsigned b_last:1; + unsigned b_resv2:1; + unsigned b_cm:1; + unsigned b_resv3:2; + unsigned b_sdn:1; + unsigned b_ddn:1; + unsigned b_dgbl:1; + unsigned b_dbo:2; + unsigned b_resv4:1; + unsigned b_ddtb:1; + unsigned b_resv5:2; + unsigned b_sgbl:1; + unsigned b_sbo:2; + unsigned b_resv6:1; + unsigned b_sdtb:1; + unsigned b_resv7:9; +} ibdbits_t; + +#pragma pack(1) +typedef union ibdbitsu { + ibdbits_t b; + uint i; +} ibdbitsu_t; + +#pragma pack(1) +typedef struct idma_buf_desc { + ibdbitsu_t ibd_bits; /* Status and Control */ + uint ibd_datlen; /* Data length in buffer */ + uint ibd_sbuf; /* Source buffer addr in host mem */ + uint ibd_dbuf; /* Destination buffer addr in host mem */ +} ibd_t; + + +#pragma pack(1) +typedef struct dcmbits { + unsigned b_fb:1; + unsigned b_lp:1; + unsigned b_resv1:3; + unsigned b_tc2:1; + unsigned b_resv2:1; + unsigned b_wrap:3; + unsigned b_sinc:1; + unsigned b_dinc:1; + unsigned b_erm:1; + unsigned b_dt:1; + unsigned b_sd:2; +} dcmbits_t; + +#pragma pack(1) +typedef union dcmbitsu { + dcmbits_t b; + ushort i; +} dcmbitsu_t; + +#pragma pack(1) +typedef struct pram_idma { + ushort pi_ibase; + dcmbitsu_t pi_dcmbits; + ushort pi_ibdptr; + ushort pi_dprbuf; + ushort pi_bufinv; /* internal to CPM */ + ushort pi_ssmax; + ushort pi_dprinptr; /* internal to CPM */ + ushort pi_sts; + ushort pi_dproutptr; /* internal to CPM */ + ushort pi_seob; + ushort pi_deob; + ushort pi_dts; + ushort pi_retadd; + ushort pi_resv1; /* internal to CPM */ + uint pi_bdcnt; + uint pi_sptr; + uint pi_dptr; + uint pi_istate; +} pram_idma_t; + + +volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +volatile ibd_t *bdf; +volatile pram_idma_t *piptr; + +volatile int dmadone; +volatile int *dmadonep = &dmadone; +void dmadone_handler (void *); + +int idma_init (void); +void idma_start (int, int, int, uint, uint, int); +uint dpalloc (uint, uint); + + +uint dpinit_done = 0; + + +#ifdef STANDALONE +int ctrlc (void) +{ + if (tstc()) { + switch (getc ()) { + case 0x03: /* ^C - Control C */ + return 1; + default: + break; + } + } + return 0; +} +void * memset(void * s,int c,size_t count) +{ + char *xs = (char *) s; + while (count--) + *xs++ = c; + return s; +} +int memcmp(const void * cs,const void * ct,size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} +#endif /* STANDALONE */ + +#ifdef STANDALONE +int mem_to_mem_idma2intr (int argc, char *argv[]) +#else +int do_idma (bd_t * bd, int argc, char *argv[]) +#endif /* STANDALONE */ +{ + int i; + + app_startup(argv); + dpinit_done = 0; + + idma_init (); + + DEBUG ("Installing dma handler\n"); + install_hdlr (7, dmadone_handler, (void *) bdf); + + memset ((void *) 0x100000, 'a', 512); + memset ((void *) 0x200000, 'b', 512); + + for (i = 0; i < 32; i++) { + printf ("Startin IDMA, iteration=%d\n", i); + idma_start (1, 1, 512, 0x100000, 0x200000, 3); + } + + DEBUG ("Uninstalling dma handler\n"); + free_hdlr (7); + + return 0; +} + +void +idma_start (int sinc, int dinc, int sz, uint sbuf, uint dbuf, int ttype) +{ + /* ttype is for M-M, M-P, P-M or P-P: not used for now */ + + piptr->pi_istate = 0; /* manual says: clear it before every START_IDMA */ + piptr->pi_dcmbits.b.b_resv1 = 0; + + if (sinc == 1) + piptr->pi_dcmbits.b.b_sinc = 1; + else + piptr->pi_dcmbits.b.b_sinc = 0; + + if (dinc == 1) + piptr->pi_dcmbits.b.b_dinc = 1; + else + piptr->pi_dcmbits.b.b_dinc = 0; + + piptr->pi_dcmbits.b.b_erm = 0; + piptr->pi_dcmbits.b.b_sd = 0x00; /* M-M */ + + bdf->ibd_sbuf = sbuf; + bdf->ibd_dbuf = dbuf; + bdf->ibd_bits.b.b_cm = 0; + bdf->ibd_bits.b.b_interrupt = 1; + bdf->ibd_bits.b.b_wrap = 1; + bdf->ibd_bits.b.b_last = 1; + bdf->ibd_bits.b.b_sdn = 0; + bdf->ibd_bits.b.b_ddn = 0; + bdf->ibd_bits.b.b_dgbl = 0; + bdf->ibd_bits.b.b_ddtb = 0; + bdf->ibd_bits.b.b_sgbl = 0; + bdf->ibd_bits.b.b_sdtb = 0; + bdf->ibd_bits.b.b_dbo = 1; + bdf->ibd_bits.b.b_sbo = 1; + bdf->ibd_bits.b.b_valid = 1; + bdf->ibd_datlen = 512; + + *dmadonep = 0; + + immap->im_sdma.sdma_idmr2 = (uchar) 0xf; + + immap->im_cpm.cp_cpcr = mk_cr_cmd (CPM_CR_IDMA2_PAGE, + CPM_CR_IDMA2_SBLOCK, 0x0, + 0x9) | 0x00010000; + + while (*dmadonep != 1) { + if (ctrlc ()) { + DEBUG ("\nInterrupted waiting for DMA interrupt.\n"); + goto done; + } + printf ("Waiting for DMA interrupt (dmadone=%d b_valid = %d)...\n", + dmadone, bdf->ibd_bits.b.b_valid); + udelay (1000000); + } + printf ("DMA complete notification received!\n"); + + done: + DEBUG ("memcmp(0x%08x, 0x%08x, 512) = %d\n", + sbuf, dbuf, memcmp ((void *) sbuf, (void *) dbuf, 512)); + + return; +} + +#define MAX_INT_BUFSZ 64 +#define DCM_WRAP 0 /* MUST be consistant with MAX_INT_BUFSZ */ + +int idma_init (void) +{ + uint memaddr; + + immap->im_cpm.cp_rccr &= ~0x00F3FFFF; + immap->im_cpm.cp_rccr |= 0x00A00A00; + + memaddr = dpalloc (sizeof (pram_idma_t), 64); + + *(volatile ushort *) &immap->im_dprambase[PROFF_IDMA2_BASE] = memaddr; + piptr = (volatile pram_idma_t *) ((uint) (immap) + memaddr); + + piptr->pi_resv1 = 0; /* manual says: clear it */ + piptr->pi_dcmbits.b.b_fb = 0; + piptr->pi_dcmbits.b.b_lp = 1; + piptr->pi_dcmbits.b.b_erm = 0; + piptr->pi_dcmbits.b.b_dt = 0; + + memaddr = (uint) dpalloc (sizeof (ibd_t), 64); + piptr->pi_ibase = piptr->pi_ibdptr = (volatile short) memaddr; + bdf = (volatile ibd_t *) ((uint) (immap) + memaddr); + bdf->ibd_bits.b.b_valid = 0; + + memaddr = (uint) dpalloc (64, 64); + piptr->pi_dprbuf = (volatile ushort) memaddr; + piptr->pi_dcmbits.b.b_wrap = 4; + piptr->pi_ssmax = 32; + + piptr->pi_sts = piptr->pi_ssmax; + piptr->pi_dts = piptr->pi_ssmax; + + return 1; +} + +void dmadone_handler (void *arg) +{ + immap->im_sdma.sdma_idmr2 = (uchar) 0x0; + + *dmadonep = 1; + + return; +} + + +static uint dpbase = 0; + +uint dpalloc (uint size, uint align) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + uint retloc; + uint align_mask, off; + uint savebase; + + /* Pointer to initial global data area */ + + if (dpinit_done == 0) { + dpbase = gd->dp_alloc_base; + dpinit_done = 1; + } + + align_mask = align - 1; + savebase = dpbase; + + if ((off = (dpbase & align_mask)) != 0) + dpbase += (align - off); + + if ((off = size & align_mask) != 0) + size += align - off; + + if ((dpbase + size) >= gd->dp_alloc_top) { + dpbase = savebase; + printf ("dpalloc: ran out of dual port ram!"); + return 0; + } + + retloc = dpbase; + dpbase += size; + + memset ((void *) &immr->im_dprambase[retloc], 0, size); + + return (retloc); +} diff --git a/examples/standalone/mips.lds b/examples/standalone/mips.lds new file mode 100644 index 0000000..717b201 --- /dev/null +++ b/examples/standalone/mips.lds @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk 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 + */ + +/* +OUTPUT_FORMAT("elf32-bigmips", "elf32-bigmips", "elf32-bigmips") +*/ +OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradbigmips") +OUTPUT_ARCH(mips) +SECTIONS +{ + .text : + { + *(.text) + } + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + + . = ALIGN(4); + .data : { *(.data) } + + . = .; + _gp = ALIGN(16) + 0x7ff0; + + .got : { + __got_start = .; + *(.got) + __got_end = .; + } + + .sdata : { *(.sdata) } + + . = ALIGN(4); + __bss_start = .; + .sbss (NOLOAD) : { *(.sbss) } + .bss (NOLOAD) : { *(.bss) . = ALIGN(4); } + + _end = .; +} diff --git a/examples/standalone/nios.lds b/examples/standalone/nios.lds new file mode 100644 index 0000000..4c1080b --- /dev/null +++ b/examples/standalone/nios.lds @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2003, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.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 + */ + + +OUTPUT_FORMAT("elf32-nios") +OUTPUT_ARCH(nios) +ENTRY(_start) + +SECTIONS +{ + .text : + { + *(.text) + } + __text_end = .; + + . = ALIGN(4); + .rodata : + { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + __rodata_end = .; + + . = ALIGN(4); + .data : + { + *(.data) + } + . = ALIGN(4); + __data_end = .; + + __bss_start = .; + . = ALIGN(4); + .bss (NOLOAD) : + { + *(.bss) + } + . = ALIGN(4); + __bss_end = .; + _end = .; +} diff --git a/examples/standalone/nios2.lds b/examples/standalone/nios2.lds new file mode 100644 index 0000000..a3e5ea8 --- /dev/null +++ b/examples/standalone/nios2.lds @@ -0,0 +1,133 @@ +/* + * (C) Copyright 2004, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.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 + */ + + +OUTPUT_FORMAT("elf32-littlenios2") +OUTPUT_ARCH(nios2) +ENTRY(_start) + +SECTIONS +{ + .text : + { + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + *(.gnu.linkonce.r*) + } + . = ALIGN (4); + _etext = .; + PROVIDE (etext = .); + + /* 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); + _end = .; + PROVIDE (end = .); + + /* CMD TABLE - uboot command sections + */ + . = .; + __uboot_cmd_start = .; + .u_boot_cmd : + { + *(.u_boot_cmd) + } + . = ALIGN(4); + __u_boot_cmd_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) } +} diff --git a/examples/standalone/ppc_longjmp.S b/examples/standalone/ppc_longjmp.S new file mode 100644 index 0000000..5ca6f6f --- /dev/null +++ b/examples/standalone/ppc_longjmp.S @@ -0,0 +1,79 @@ +/* longjmp for PowerPC. + Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <ppc_asm.tmpl> + +# define JB_GPR1 0 /* Also known as the stack pointer */ +# define JB_GPR2 1 +# define JB_LR 2 /* The address we will return to */ +# define JB_GPRS 3 /* GPRs 14 through 31 are saved, 18 in total */ +# define JB_CR 21 /* Condition code registers. */ +# define JB_FPRS 22 /* FPRs 14 through 31 are saved, 18*2 words total */ +# define JB_SIZE (58*4) + +#define FP(x...) x +#define FP(x...) x + +.globl ppc_longjmp; + +ppc_longjmp: + lwz r1,(JB_GPR1*4)(r3) + lwz r2,(JB_GPR2*4)(r3) + lwz r0,(JB_LR*4)(r3) + lwz r14,((JB_GPRS+0)*4)(r3) +FP( lfd 14,((JB_FPRS+0*2)*4)(r3)) + lwz r15,((JB_GPRS+1)*4)(r3) +FP( lfd 15,((JB_FPRS+1*2)*4)(r3)) + lwz r16,((JB_GPRS+2)*4)(r3) +FP( lfd 16,((JB_FPRS+2*2)*4)(r3)) + lwz r17,((JB_GPRS+3)*4)(r3) +FP( lfd 17,((JB_FPRS+3*2)*4)(r3)) + lwz r18,((JB_GPRS+4)*4)(r3) +FP( lfd 18,((JB_FPRS+4*2)*4)(r3)) + lwz r19,((JB_GPRS+5)*4)(r3) +FP( lfd 19,((JB_FPRS+5*2)*4)(r3)) + lwz r20,((JB_GPRS+6)*4)(r3) +FP( lfd 20,((JB_FPRS+6*2)*4)(r3)) + mtlr r0 + lwz r21,((JB_GPRS+7)*4)(r3) +FP( lfd 21,((JB_FPRS+7*2)*4)(r3)) + lwz r22,((JB_GPRS+8)*4)(r3) +FP( lfd 22,((JB_FPRS+8*2)*4)(r3)) + lwz r0,(JB_CR*4)(r3) + lwz r23,((JB_GPRS+9)*4)(r3) +FP( lfd 23,((JB_FPRS+9*2)*4)(r3)) + lwz r24,((JB_GPRS+10)*4)(r3) +FP( lfd 24,((JB_FPRS+10*2)*4)(r3)) + lwz r25,((JB_GPRS+11)*4)(r3) +FP( lfd 25,((JB_FPRS+11*2)*4)(r3)) + mtcrf 0xFF,r0 + lwz r26,((JB_GPRS+12)*4)(r3) +FP( lfd 26,((JB_FPRS+12*2)*4)(r3)) + lwz r27,((JB_GPRS+13)*4)(r3) +FP( lfd 27,((JB_FPRS+13*2)*4)(r3)) + lwz r28,((JB_GPRS+14)*4)(r3) +FP( lfd 28,((JB_FPRS+14*2)*4)(r3)) + lwz r29,((JB_GPRS+15)*4)(r3) +FP( lfd 29,((JB_FPRS+15*2)*4)(r3)) + lwz r30,((JB_GPRS+16)*4)(r3) +FP( lfd 30,((JB_FPRS+16*2)*4)(r3)) + lwz r31,((JB_GPRS+17)*4)(r3) +FP( lfd 31,((JB_FPRS+17*2)*4)(r3)) + mr r3,r4 + blr diff --git a/examples/standalone/ppc_setjmp.S b/examples/standalone/ppc_setjmp.S new file mode 100644 index 0000000..421abfd --- /dev/null +++ b/examples/standalone/ppc_setjmp.S @@ -0,0 +1,83 @@ +/* setjmp for PowerPC. + Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <ppc_asm.tmpl> + +# define JB_GPR1 0 /* Also known as the stack pointer */ +# define JB_GPR2 1 +# define JB_LR 2 /* The address we will return to */ +# define JB_GPRS 3 /* GPRs 14 through 31 are saved, 18 in total */ +# define JB_CR 21 /* Condition code registers. */ +# define JB_FPRS 22 /* FPRs 14 through 31 are saved, 18*2 words total */ +# define JB_SIZE (58*4) + +#define FP(x...) x + +.globl setctxsp; +setctxsp: + mr r1, r3 + blr + +.globl ppc_setjmp; +ppc_setjmp: + stw r1,(JB_GPR1*4)(3) + mflr r0 + stw r2,(JB_GPR2*4)(3) + stw r14,((JB_GPRS+0)*4)(3) +FP( stfd 14,((JB_FPRS+0*2)*4)(3)) + stw r0,(JB_LR*4)(3) + stw r15,((JB_GPRS+1)*4)(3) +FP( stfd 15,((JB_FPRS+1*2)*4)(3)) + mfcr r0 + stw r16,((JB_GPRS+2)*4)(3) +FP( stfd 16,((JB_FPRS+2*2)*4)(3)) + stw r0,(JB_CR*4)(3) + stw r17,((JB_GPRS+3)*4)(3) +FP( stfd 17,((JB_FPRS+3*2)*4)(3)) + stw r18,((JB_GPRS+4)*4)(3) +FP( stfd 18,((JB_FPRS+4*2)*4)(3)) + stw r19,((JB_GPRS+5)*4)(3) +FP( stfd 19,((JB_FPRS+5*2)*4)(3)) + stw r20,((JB_GPRS+6)*4)(3) +FP( stfd 20,((JB_FPRS+6*2)*4)(3)) + stw r21,((JB_GPRS+7)*4)(3) +FP( stfd 21,((JB_FPRS+7*2)*4)(3)) + stw r22,((JB_GPRS+8)*4)(3) +FP( stfd 22,((JB_FPRS+8*2)*4)(3)) + stw r23,((JB_GPRS+9)*4)(3) +FP( stfd 23,((JB_FPRS+9*2)*4)(3)) + stw r24,((JB_GPRS+10)*4)(3) +FP( stfd 24,((JB_FPRS+10*2)*4)(3)) + stw r25,((JB_GPRS+11)*4)(3) +FP( stfd 25,((JB_FPRS+11*2)*4)(3)) + stw r26,((JB_GPRS+12)*4)(3) +FP( stfd 26,((JB_FPRS+12*2)*4)(3)) + stw r27,((JB_GPRS+13)*4)(3) +FP( stfd 27,((JB_FPRS+13*2)*4)(3)) + stw r28,((JB_GPRS+14)*4)(3) +FP( stfd 28,((JB_FPRS+14*2)*4)(3)) + stw r29,((JB_GPRS+15)*4)(3) +FP( stfd 29,((JB_FPRS+15*2)*4)(3)) + stw r30,((JB_GPRS+16)*4)(3) +FP( stfd 30,((JB_FPRS+16*2)*4)(3)) + stw r31,((JB_GPRS+17)*4)(3) +FP( stfd 31,((JB_FPRS+17*2)*4)(3)) + + li 3, 0 + blr diff --git a/examples/standalone/sched.c b/examples/standalone/sched.c new file mode 100644 index 0000000..b32766f --- /dev/null +++ b/examples/standalone/sched.c @@ -0,0 +1,369 @@ +/* + * 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 <exports.h> + +/* + * Author: Arun Dharankar <ADharankar@ATTBI.Com> + * + * A very simple thread/schedular model: + * - only one master thread, and no parent child relation maintained + * - parent thread cannot be stopped or deleted + * - no permissions or credentials + * - no elaborate safety checks + * - cooperative multi threading + * - Simple round-robin scheduleing with no priorities + * - no metering/statistics collection + * + * Basic idea of implementing this is to allow more than one tests to + * execute "simultaneously". + * + * This may be modified such thread_yield may be called in syscalls, and + * timer interrupts. + */ + + +#define MAX_THREADS 8 + +#define CTX_SIZE 512 +#define STK_SIZE 8*1024 + +#define STATE_EMPTY 0 +#define STATE_RUNNABLE 1 +#define STATE_STOPPED 2 +#define STATE_TERMINATED 2 + +#define MASTER_THREAD 0 + +#define RC_FAILURE (-1) +#define RC_SUCCESS (0) + +typedef vu_char *jmp_ctx; +unsigned long setctxsp (vu_char *sp); +int ppc_setjmp(jmp_ctx env); +void ppc_longjmp(jmp_ctx env, int val); +#define setjmp ppc_setjmp +#define longjmp ppc_longjmp + +struct lthread { + int state; + int retval; + char stack[STK_SIZE]; + uchar context[CTX_SIZE]; + int (*func) (void *); + void *arg; +}; +static volatile struct lthread lthreads[MAX_THREADS]; +static volatile int current_tid = MASTER_THREAD; + + +static uchar dbg = 0; + +#define PDEBUG(fmt, args...) { \ + if(dbg != 0) { \ + printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\ + printf(fmt, ##args); \ + printf("\n"); \ + } \ +} + +static int testthread (void *); +static void sched_init (void); +static int thread_create (int (*func) (void *), void *arg); +static int thread_start (int id); +static void thread_yield (void); +static int thread_delete (int id); +static int thread_join (int *ret); + +#if 0 /* not used yet */ +static int thread_stop (int id); +#endif /* not used yet */ + +/* An example of schedular test */ + +#define NUMTHREADS 7 +int sched (int ac, char *av[]) +{ + int i, j; + int tid[NUMTHREADS]; + int names[NUMTHREADS]; + + app_startup(av); + + sched_init (); + + for (i = 0; i < NUMTHREADS; i++) { + names[i] = i; + j = thread_create (testthread, (void *) &names[i]); + if (j == RC_FAILURE) + printf ("schedtest: Failed to create thread %d\n", i); + if (j > 0) { + printf ("schedtest: Created thread with id %d, name %d\n", + j, i); + tid[i] = j; + } + } + printf ("schedtest: Threads created\n"); + + printf ("sched_test: function=0x%08x\n", (unsigned)testthread); + for (i = 0; i < NUMTHREADS; i++) { + printf ("schedtest: Setting thread %d runnable\n", tid[i]); + thread_start (tid[i]); + thread_yield (); + } + printf ("schedtest: Started %d threads\n", NUMTHREADS); + + while (1) { + printf ("schedtest: Waiting for threads to complete\n"); + if (tstc () && getc () == 0x3) { + printf ("schedtest: Aborting threads...\n"); + for (i = 0; i < NUMTHREADS; i++) { + printf ("schedtest: Deleting thread %d\n", tid[i]); + thread_delete (tid[i]); + } + return RC_SUCCESS; + } + j = -1; + i = thread_join (&j); + if (i == RC_FAILURE) { + printf ("schedtest: No threads pending, " + "exiting schedular test\n"); + return RC_SUCCESS; + } + printf ("schedtest: thread is %d returned %d\n", i, j); + thread_yield (); + } + + return RC_SUCCESS; +} + +static int testthread (void *name) +{ + int i; + + printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n", + *(int *) name, (unsigned)&i); + + printf ("Thread %02d, i=%d\n", *(int *) name, i); + + for (i = 0; i < 0xffff * (*(int *) name + 1); i++) { + if (tstc () && getc () == 0x3) { + printf ("testthread: myname %d terminating.\n", + *(int *) name); + return *(int *) name + 1; + } + + if (i % 100 == 0) + thread_yield (); + } + + printf ("testthread: returning %d, i=0x%x\n", + *(int *) name + 1, i); + + return *(int *) name + 1; +} + + +static void sched_init (void) +{ + int i; + + for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) + lthreads[i].state = STATE_EMPTY; + + current_tid = MASTER_THREAD; + lthreads[current_tid].state = STATE_RUNNABLE; + PDEBUG ("sched_init: master context = 0x%08x", + (unsigned)lthreads[current_tid].context); + return; +} + +static void thread_yield (void) +{ + static int i; + + PDEBUG ("thread_yield: current tid=%d", current_tid); + +#define SWITCH(new) \ + if(lthreads[new].state == STATE_RUNNABLE) { \ + PDEBUG("thread_yield: %d match, ctx=0x%08x", \ + new, \ + (unsigned)lthreads[current_tid].context); \ + if(setjmp(lthreads[current_tid].context) == 0) { \ + current_tid = new; \ + PDEBUG("thread_yield: tid %d returns 0", \ + new); \ + longjmp(lthreads[new].context, 1); \ + } else { \ + PDEBUG("thread_yield: tid %d returns 1", \ + new); \ + return; \ + } \ + } + + for (i = current_tid + 1; i < MAX_THREADS; i++) { + SWITCH (i); + } + + if (current_tid != 0) { + for (i = 0; i <= current_tid; i++) { + SWITCH (i); + } + } + + PDEBUG ("thread_yield: returning from thread_yield"); + return; +} + +static int thread_create (int (*func) (void *), void *arg) +{ + int i; + + for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { + if (lthreads[i].state == STATE_EMPTY) { + lthreads[i].state = STATE_STOPPED; + lthreads[i].func = func; + lthreads[i].arg = arg; + PDEBUG ("thread_create: returns new tid %d", i); + return i; + } + } + + PDEBUG ("thread_create: returns failure"); + return RC_FAILURE; +} + +static int thread_delete (int id) +{ + if (id <= MASTER_THREAD || id > MAX_THREADS) + return RC_FAILURE; + + if (current_tid == id) + return RC_FAILURE; + + lthreads[id].state = STATE_EMPTY; + return RC_SUCCESS; +} + +static void thread_launcher (void) +{ + PDEBUG ("thread_launcher: invoking func=0x%08x", + (unsigned)lthreads[current_tid].func); + + lthreads[current_tid].retval = + lthreads[current_tid].func (lthreads[current_tid].arg); + + PDEBUG ("thread_launcher: tid %d terminated", current_tid); + + lthreads[current_tid].state = STATE_TERMINATED; + thread_yield (); + printf ("thread_launcher: should NEVER get here!\n"); + + return; +} + +static int thread_start (int id) +{ + PDEBUG ("thread_start: id=%d", id); + if (id <= MASTER_THREAD || id > MAX_THREADS) { + return RC_FAILURE; + } + + if (lthreads[id].state != STATE_STOPPED) + return RC_FAILURE; + + if (setjmp (lthreads[current_tid].context) == 0) { + lthreads[id].state = STATE_RUNNABLE; + current_tid = id; + PDEBUG ("thread_start: to be stack=0%08x", + (unsigned)lthreads[id].stack); + setctxsp ((vu_char *)<hreads[id].stack[STK_SIZE]); + thread_launcher (); + } + + PDEBUG ("thread_start: Thread id=%d started, parent returns", id); + + return RC_SUCCESS; +} + +#if 0 /* not used so far */ +static int thread_stop (int id) +{ + if (id <= MASTER_THREAD || id >= MAX_THREADS) + return RC_FAILURE; + + if (current_tid == id) + return RC_FAILURE; + + lthreads[id].state = STATE_STOPPED; + return RC_SUCCESS; +} +#endif /* not used so far */ + +static int thread_join (int *ret) +{ + int i, j = 0; + + PDEBUG ("thread_join: *ret = %d", *ret); + + if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) { + PDEBUG ("thread_join: invalid tid %d", *ret); + return RC_FAILURE; + } + + if (*ret == -1) { + PDEBUG ("Checking for tid = -1"); + while (1) { + /* PDEBUG("thread_join: start while-loopn"); */ + j = 0; + for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { + if (lthreads[i].state == STATE_TERMINATED) { + *ret = lthreads[i].retval; + lthreads[i].state = STATE_EMPTY; + /* PDEBUG("thread_join: returning retval %d of tid %d", + ret, i); */ + return RC_SUCCESS; + } + + if (lthreads[i].state != STATE_EMPTY) { + PDEBUG ("thread_join: %d used slots tid %d state=%d", + j, i, lthreads[i].state); + j++; + } + } + if (j == 0) { + PDEBUG ("thread_join: all slots empty!"); + return RC_FAILURE; + } + /* PDEBUG("thread_join: yielding"); */ + thread_yield (); + /* PDEBUG("thread_join: back from yield"); */ + } + } + + if (lthreads[*ret].state == STATE_TERMINATED) { + i = *ret; + *ret = lthreads[*ret].retval; + lthreads[*ret].state = STATE_EMPTY; + PDEBUG ("thread_join: returing %d for tid %d", *ret, i); + return RC_SUCCESS; + } + + PDEBUG ("thread_join: thread %d is not terminated!", *ret); + return RC_FAILURE; +} diff --git a/examples/standalone/smc91111_eeprom.c b/examples/standalone/smc91111_eeprom.c new file mode 100644 index 0000000..39e5306 --- /dev/null +++ b/examples/standalone/smc91111_eeprom.c @@ -0,0 +1,395 @@ +/* + * (C) Copyright 2004 + * Robin Getz rgetz@blacfin.uclinux.org + * + * 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 + * + * Heavily borrowed from the following peoples GPL'ed software: + * - Wolfgang Denk, DENX Software Engineering, wd@denx.de + * Das U-boot + * - Ladislav Michl ladis@linux-mips.org + * A rejected patch on the U-Boot mailing list + */ + +#include <common.h> +#include <exports.h> +#include "../drivers/net/smc91111.h" + +#ifdef CONFIG_DRIVER_SMC91111 + +#ifndef SMC91111_EEPROM_INIT +# define SMC91111_EEPROM_INIT() +#endif + +#define SMC_BASE_ADDRESS CONFIG_SMC91111_BASE +#define EEPROM 0x1 +#define MAC 0x2 +#define UNKNOWN 0x4 + +void dump_reg (void); +void dump_eeprom (void); +int write_eeprom_reg (int, int); +void copy_from_eeprom (void); +void print_MAC (void); +int read_eeprom_reg (int); +void print_macaddr (void); + +int smc91111_eeprom (int argc, char *argv[]) +{ + int c, i, j, done, line, reg, value, start, what; + char input[50]; + + /* Print the ABI version */ + app_startup (argv); + if (XF_VERSION != (int) get_version ()) { + printf ("Expects ABI version %d\n", XF_VERSION); + printf ("Actual U-Boot ABI version %d\n", + (int) get_version ()); + printf ("Can't run\n\n"); + return (0); + } + + SMC91111_EEPROM_INIT(); + + if ((SMC_inw (BANK_SELECT) & 0xFF00) != 0x3300) { + printf ("Can't find SMSC91111\n"); + return (0); + } + + done = 0; + what = UNKNOWN; + printf ("\n"); + while (!done) { + /* print the prompt */ + printf ("SMC91111> "); + line = 0; + i = 0; + start = 1; + while (!line) { + /* Wait for a keystroke */ + while (!tstc ()); + + c = getc (); + /* Make Uppercase */ + if (c >= 'Z') + c -= ('a' - 'A'); + /* printf(" |%02x| ",c); */ + + switch (c) { + case '\r': /* Enter */ + case '\n': + input[i] = 0; + puts ("\r\n"); + line = 1; + break; + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + input[0] = 0; + i = 0; + line = 1; + done = 1; + break; + + case 0x5F: + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + if (i > 0) { + puts ("\b \b"); + i--; + } + break; + default: + if (start) { + if ((c == 'W') || (c == 'D') + || (c == 'M') || (c == 'C') + || (c == 'P')) { + putc (c); + input[i] = c; + if (i <= 45) + i++; + start = 0; + } + } else { + if ((c >= '0' && c <= '9') + || (c >= 'A' && c <= 'F') + || (c == 'E') || (c == 'M') + || (c == ' ')) { + putc (c); + input[i] = c; + if (i <= 45) + i++; + break; + } + } + break; + } + } + + for (; i < 49; i++) + input[i] = 0; + + switch (input[0]) { + case ('W'): + /* Line should be w reg value */ + i = 0; + reg = 0; + value = 0; + /* Skip to the next space or end) */ + while ((input[i] != ' ') && (input[i] != 0)) + i++; + + if (input[i] != 0) + i++; + + /* Are we writing to EEPROM or MAC */ + switch (input[i]) { + case ('E'): + what = EEPROM; + break; + case ('M'): + what = MAC; + break; + default: + what = UNKNOWN; + break; + } + + /* skip to the next space or end */ + while ((input[i] != ' ') && (input[i] != 0)) + i++; + if (input[i] != 0) + i++; + + /* Find register to write into */ + j = 0; + while ((input[i] != ' ') && (input[i] != 0)) { + j = input[i] - 0x30; + if (j >= 0xA) { + j -= 0x07; + } + reg = (reg * 0x10) + j; + i++; + } + + while ((input[i] != ' ') && (input[i] != 0)) + i++; + + if (input[i] != 0) + i++; + else + what = UNKNOWN; + + /* Get the value to write */ + j = 0; + while ((input[i] != ' ') && (input[i] != 0)) { + j = input[i] - 0x30; + if (j >= 0xA) { + j -= 0x07; + } + value = (value * 0x10) + j; + i++; + } + + switch (what) { + case 1: + printf ("Writing EEPROM register %02x with %04x\n", reg, value); + write_eeprom_reg (value, reg); + break; + case 2: + printf ("Writing MAC register bank %i, reg %02x with %04x\n", reg >> 4, reg & 0xE, value); + SMC_SELECT_BANK (reg >> 4); + SMC_outw (value, reg & 0xE); + break; + default: + printf ("Wrong\n"); + break; + } + break; + case ('D'): + dump_eeprom (); + break; + case ('M'): + dump_reg (); + break; + case ('C'): + copy_from_eeprom (); + break; + case ('P'): + print_macaddr (); + break; + default: + break; + } + + } + + return (0); +} + +void copy_from_eeprom (void) +{ + int i; + + SMC_SELECT_BANK (1); + SMC_outw ((SMC_inw (CTL_REG) & !CTL_EEPROM_SELECT) | CTL_RELOAD, + CTL_REG); + i = 100; + while ((SMC_inw (CTL_REG) & CTL_RELOAD) && --i) + udelay (100); + if (i == 0) { + printf ("Timeout Refreshing EEPROM registers\n"); + } else { + printf ("EEPROM contents copied to MAC\n"); + } + +} + +void print_macaddr (void) +{ + int i, j, k, mac[6]; + + printf ("Current MAC Address in SMSC91111 "); + SMC_SELECT_BANK (1); + for (i = 0; i < 5; i++) { + printf ("%02x:", SMC_inb (ADDR0_REG + i)); + } + + printf ("%02x\n", SMC_inb (ADDR0_REG + 5)); + + i = 0; + for (j = 0x20; j < 0x23; j++) { + k = read_eeprom_reg (j); + mac[i] = k & 0xFF; + i++; + mac[i] = k >> 8; + i++; + } + + printf ("Current MAC Address in EEPROM "); + for (i = 0; i < 5; i++) + printf ("%02x:", mac[i]); + printf ("%02x\n", mac[5]); + +} +void dump_eeprom (void) +{ + int j, k; + + printf ("IOS2-0 "); + for (j = 0; j < 8; j++) { + printf ("%03x ", j); + } + printf ("\n"); + + for (k = 0; k < 4; k++) { + if (k == 0) + printf ("CONFIG "); + if (k == 1) + printf ("BASE "); + if ((k == 2) || (k == 3)) + printf (" "); + for (j = 0; j < 0x20; j += 4) { + printf ("%02x:%04x ", j + k, read_eeprom_reg (j + k)); + } + printf ("\n"); + } + + for (j = 0x20; j < 0x40; j++) { + if ((j & 0x07) == 0) + printf ("\n"); + printf ("%02x:%04x ", j, read_eeprom_reg (j)); + } + printf ("\n"); + +} + +int read_eeprom_reg (int reg) +{ + int timeout; + + SMC_SELECT_BANK (2); + SMC_outw (reg, PTR_REG); + + SMC_SELECT_BANK (1); + SMC_outw (SMC_inw (CTL_REG) | CTL_EEPROM_SELECT | CTL_RELOAD, + CTL_REG); + timeout = 100; + while ((SMC_inw (CTL_REG) & CTL_RELOAD) && --timeout) + udelay (100); + if (timeout == 0) { + printf ("Timeout Reading EEPROM register %02x\n", reg); + return 0; + } + + return SMC_inw (GP_REG); + +} + +int write_eeprom_reg (int value, int reg) +{ + int timeout; + + SMC_SELECT_BANK (2); + SMC_outw (reg, PTR_REG); + + SMC_SELECT_BANK (1); + SMC_outw (value, GP_REG); + SMC_outw (SMC_inw (CTL_REG) | CTL_EEPROM_SELECT | CTL_STORE, CTL_REG); + timeout = 100; + while ((SMC_inw (CTL_REG) & CTL_STORE) && --timeout) + udelay (100); + if (timeout == 0) { + printf ("Timeout Writing EEPROM register %02x\n", reg); + return 0; + } + + return 1; + +} + +void dump_reg (void) +{ + int i, j; + + printf (" "); + for (j = 0; j < 4; j++) { + printf ("Bank%i ", j); + } + printf ("\n"); + for (i = 0; i < 0xF; i += 2) { + printf ("%02x ", i); + for (j = 0; j < 4; j++) { + SMC_SELECT_BANK (j); + printf ("%04x ", SMC_inw (i)); + } + printf ("\n"); + } +} + +#else + +int smc91111_eeprom (int argc, char *argv[]) +{ + printf("Not supported for this board\n"); + return 1; +} + +#endif diff --git a/examples/standalone/smc911x_eeprom.c b/examples/standalone/smc911x_eeprom.c new file mode 100644 index 0000000..bf22f0a --- /dev/null +++ b/examples/standalone/smc911x_eeprom.c @@ -0,0 +1,383 @@ +/* + * smc911x_eeprom.c - EEPROM interface to SMC911x parts. + * Only tested on SMSC9118 though ... + * + * Copyright 2004-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + * Based on smc91111_eeprom.c which: + * Heavily borrowed from the following peoples GPL'ed software: + * - Wolfgang Denk, DENX Software Engineering, wd@denx.de + * Das U-boot + * - Ladislav Michl ladis@linux-mips.org + * A rejected patch on the U-Boot mailing list + */ + +#include <common.h> +#include <exports.h> + +#ifdef CONFIG_DRIVER_SMC911X + +#include "../drivers/net/smc911x.h" + +/** + * smsc_ctrlc - detect press of CTRL+C (common ctrlc() isnt exported!?) + */ +static int smsc_ctrlc(void) +{ + return (tstc() && getc() == 0x03); +} + +/** + * usage - dump usage information + */ +static void usage(void) +{ + puts( + "MAC/EEPROM Commands:\n" + " P : Print the MAC addresses\n" + " D : Dump the EEPROM contents\n" + " M : Dump the MAC contents\n" + " C : Copy the MAC address from the EEPROM to the MAC\n" + " W : Write a register in the EEPROM or in the MAC\n" + " Q : Quit\n" + "\n" + "Some commands take arguments:\n" + " W <E|M> <register> <value>\n" + " E: EEPROM M: MAC\n" + ); +} + +/** + * dump_regs - dump the MAC registers + * + * Registers 0x00 - 0x50 are FIFOs. The 0x50+ are the control registers + * and they're all 32bits long. 0xB8+ are reserved, so don't bother. + */ +static void dump_regs(void) +{ + u8 i, j = 0; + for (i = 0x50; i < 0xB8; i += sizeof(u32)) + printf("%02x: 0x%08x %c", i, + smc911x_reg_read(CONFIG_DRIVER_SMC911X_BASE + i), + (j++ % 2 ? '\n' : ' ')); +} + +/** + * do_eeprom_cmd - handle eeprom communication + */ +static int do_eeprom_cmd(int cmd, u8 reg) +{ + if (smc911x_reg_read(E2P_CMD) & E2P_CMD_EPC_BUSY) { + printf("eeprom_cmd: busy at start (E2P_CMD = 0x%08x)\n", + smc911x_reg_read(E2P_CMD)); + return -1; + } + + smc911x_reg_write(E2P_CMD, E2P_CMD_EPC_BUSY | cmd | reg); + + while (smc911x_reg_read(E2P_CMD) & E2P_CMD_EPC_BUSY) + if (smsc_ctrlc()) { + printf("eeprom_cmd: timeout (E2P_CMD = 0x%08x)\n", + smc911x_reg_read(E2P_CMD)); + return -1; + } + + return 0; +} + +/** + * read_eeprom_reg - read specified register in EEPROM + */ +static u8 read_eeprom_reg(u8 reg) +{ + int ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_READ, reg); + return (ret ? : smc911x_reg_read(E2P_DATA)); +} + +/** + * write_eeprom_reg - write specified value into specified register in EEPROM + */ +static int write_eeprom_reg(u8 value, u8 reg) +{ + int ret; + + /* enable erasing/writing */ + ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_EWEN, reg); + if (ret) + goto done; + + /* erase the eeprom reg */ + ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_ERASE, reg); + if (ret) + goto done; + + /* write the eeprom reg */ + smc911x_reg_write(E2P_DATA, value); + ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_WRITE, reg); + if (ret) + goto done; + + /* disable erasing/writing */ + ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_EWDS, reg); + + done: + return ret; +} + +/** + * skip_space - find first non-whitespace in given pointer + */ +static char *skip_space(char *buf) +{ + while (buf[0] == ' ' || buf[0] == '\t') + ++buf; + return buf; +} + +/** + * write_stuff - handle writing of MAC registers / eeprom + */ +static void write_stuff(char *line) +{ + char dest; + char *endp; + u8 reg; + u32 value; + + /* Skip over the "W " part of the command */ + line = skip_space(line + 1); + + /* Figure out destination */ + switch (line[0]) { + case 'E': + case 'M': + dest = line[0]; + break; + default: + invalid_usage: + printf("ERROR: Invalid write usage\n"); + usage(); + return; + } + + /* Get the register to write */ + line = skip_space(line + 1); + reg = simple_strtoul(line, &endp, 16); + if (line == endp) + goto invalid_usage; + + /* Get the value to write */ + line = skip_space(endp); + value = simple_strtoul(line, &endp, 16); + if (line == endp) + goto invalid_usage; + + /* Check for trailing cruft */ + line = skip_space(endp); + if (line[0]) + goto invalid_usage; + + /* Finally, execute the command */ + if (dest == 'E') { + printf("Writing EEPROM register %02x with %02x\n", reg, value); + write_eeprom_reg(value, reg); + } else { + printf("Writing MAC register %02x with %08x\n", reg, value); + smc911x_reg_write(CONFIG_DRIVER_SMC911X_BASE + reg, value); + } +} + +/** + * copy_from_eeprom - copy MAC address in eeprom to address registers + */ +static void copy_from_eeprom(void) +{ + ulong addrl = + read_eeprom_reg(0x01) | + read_eeprom_reg(0x02) << 8 | + read_eeprom_reg(0x03) << 16 | + read_eeprom_reg(0x04) << 24; + ulong addrh = + read_eeprom_reg(0x05) | + read_eeprom_reg(0x06) << 8; + smc911x_set_mac_csr(ADDRL, addrl); + smc911x_set_mac_csr(ADDRH, addrh); + puts("EEPROM contents copied to MAC\n"); +} + +/** + * print_macaddr - print MAC address registers and MAC address in eeprom + */ +static void print_macaddr(void) +{ + puts("Current MAC Address in MAC: "); + ulong addrl = smc911x_get_mac_csr(ADDRL); + ulong addrh = smc911x_get_mac_csr(ADDRH); + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + (u8)(addrl), (u8)(addrl >> 8), (u8)(addrl >> 16), + (u8)(addrl >> 24), (u8)(addrh), (u8)(addrh >> 8)); + + puts("Current MAC Address in EEPROM: "); + int i; + for (i = 1; i < 6; ++i) + printf("%02x:", read_eeprom_reg(i)); + printf("%02x\n", read_eeprom_reg(i)); +} + +/** + * dump_eeprom - dump the whole content of the EEPROM + */ +static void dump_eeprom(void) +{ + int i; + puts("EEPROM:\n"); + for (i = 0; i < 7; ++i) + printf("%02x: 0x%02x\n", i, read_eeprom_reg(i)); +} + +/** + * smc911x_init - get the MAC/EEPROM up and ready for use + */ +static int smc911x_init(void) +{ + /* See if there is anything there */ + if (!smc911x_detect_chip()) + return 1; + + smc911x_reset(); + + /* Make sure we set EEDIO/EECLK to the EEPROM */ + if (smc911x_reg_read(GPIO_CFG) & GPIO_CFG_EEPR_EN) { + while (smc911x_reg_read(E2P_CMD) & E2P_CMD_EPC_BUSY) + if (smsc_ctrlc()) { + printf("init: timeout (E2P_CMD = 0x%08x)\n", + smc911x_reg_read(E2P_CMD)); + return 1; + } + smc911x_reg_write(GPIO_CFG, smc911x_reg_read(GPIO_CFG) & ~GPIO_CFG_EEPR_EN); + } + + return 0; +} + +/** + * getline - consume a line of input and handle some escape sequences + */ +static char *getline(void) +{ + static char buffer[100]; + char c; + size_t i; + + i = 0; + while (1) { + buffer[i] = '\0'; + while (!tstc()) + continue; + + c = getc(); + /* Convert to uppercase */ + if (c >= 'a' && c <= 'z') + c -= ('a' - 'A'); + + switch (c) { + case '\r': /* Enter/Return key */ + case '\n': + puts("\n"); + return buffer; + + case 0x03: /* ^C - break */ + return NULL; + + case 0x5F: + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + if (i) { + puts("\b \b"); + i--; + } + break; + + default: + /* Ignore control characters */ + if (c < 0x20) + break; + /* Queue up all other characters */ + buffer[i++] = c; + printf("%c", c); + break; + } + } +} + +/** + * smc911x_eeprom - our application's main() function + */ +int smc911x_eeprom(int argc, char *argv[]) +{ + /* Print the ABI version */ + app_startup(argv); + if (XF_VERSION != get_version()) { + printf("Expects ABI version %d\n", XF_VERSION); + printf("Actual U-Boot ABI version %lu\n", get_version()); + printf("Can't run\n\n"); + return 1; + } + + /* Initialize the MAC/EEPROM somewhat */ + puts("\n"); + if (smc911x_init()) + return 1; + + /* Dump helpful usage information */ + puts("\n"); + usage(); + puts("\n"); + + while (1) { + char *line; + + /* Send the prompt and wait for a line */ + puts("eeprom> "); + line = getline(); + + /* Got a ctrl+c */ + if (!line) + return 0; + + /* Eat leading space */ + line = skip_space(line); + + /* Empty line, try again */ + if (!line[0]) + continue; + + /* Only accept 1 letter commands */ + if (line[0] && line[1] && line[1] != ' ' && line[1] != '\t') + goto unknown_cmd; + + /* Now parse the command */ + switch (line[0]) { + case 'W': write_stuff(line); break; + case 'D': dump_eeprom(); break; + case 'M': dump_regs(); break; + case 'C': copy_from_eeprom(); break; + case 'P': print_macaddr(); break; + unknown_cmd: + default: puts("ERROR: Unknown command!\n\n"); + case '?': + case 'H': usage(); break; + case 'Q': return 0; + } + } +} + +#else +int smc911x_eeprom(int argc, char *argv[]) +{ + puts("Not supported for this board\n"); + return 1; +} +#endif diff --git a/examples/standalone/sparc.lds b/examples/standalone/sparc.lds new file mode 100644 index 0000000..9733daa --- /dev/null +++ b/examples/standalone/sparc.lds @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2003, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.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 + */ + + +OUTPUT_FORMAT("elf32-sparc", "elf32-sparc", "elf32-sparc") +OUTPUT_ARCH(sparc) +ENTRY(_start) + +SECTIONS +{ + .text : + { + *(.text) + } + __text_end = .; + + . = ALIGN(4); + .rodata : + { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + __rodata_end = .; + + . = ALIGN(4); + .data : + { + *(.data) + } + . = ALIGN(4); + __data_end = .; + + __bss_start = .; + . = ALIGN(4); + .bss : + { + *(.bss) + } + . = ALIGN(4); + __bss_end = .; + _end = .; +} diff --git a/examples/standalone/stubs.c b/examples/standalone/stubs.c new file mode 100644 index 0000000..a8cb954 --- /dev/null +++ b/examples/standalone/stubs.c @@ -0,0 +1,225 @@ +#include <exports.h> + +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif /* GCC_VERSION */ + +#if defined(CONFIG_I386) +/* + * x86 does not have a dedicated register to store the pointer to + * the global_data. Thus the jump table address is stored in a + * global variable, but such approach does not allow for execution + * from flash memory. The global_data address is passed as argv[-1] + * to the application program. + */ +static void **jt; +gd_t *global_data; + +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" movl %0, %%eax\n" \ +" movl jt, %%ecx\n" \ +" jmp *(%%ecx, %%eax)\n" \ + : : "i"(XF_ ## x * sizeof(void *)) : "eax", "ecx"); +#elif defined(CONFIG_PPC) +/* + * r2 holds the pointer to the global_data, r11 is a call-clobbered + * register + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" lwz %%r11, %0(%%r2)\n" \ +" lwz %%r11, %1(%%r11)\n" \ +" mtctr %%r11\n" \ +" bctr\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "r11"); +#elif defined(CONFIG_ARM) +/* + * r8 holds the pointer to the global_data, ip is a call-clobbered + * register + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" ldr ip, [r8, %0]\n" \ +" ldr pc, [ip, %1]\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "ip"); +#elif defined(CONFIG_MIPS) +/* + * k0 ($26) holds the pointer to the global_data; t9 ($25) is a call- + * clobbered register that is also used to set gp ($26). Note that the + * jr instruction also executes the instruction immediately following + * it; however, GCC/mips generates an additional `nop' after each asm + * statement + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" lw $25, %0($26)\n" \ +" lw $25, %1($25)\n" \ +" jr $25\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "t9"); +#elif defined(CONFIG_NIOS) +/* + * %g7 holds the pointer to the global_data. %g0 is call clobbered. + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" pfx %%hi(%0)\n" \ +" movi %%g0, %%lo(%0)\n" \ +" add %%g0, %%g7\n" \ +" ld %%g0, [%%g0]\n" \ +" pfx %1\n" \ +" ld %%g0, [%%g0]\n" \ +" jmp %%g0\n" \ +" nop \n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x) : "r0"); +#elif defined(CONFIG_NIOS2) +/* + * r15 holds the pointer to the global_data, r8 is call-clobbered + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" movhi r8, %%hi(%0)\n" \ +" ori r8, r0, %%lo(%0)\n" \ +" add r8, r8, r15\n" \ +" ldw r8, 0(r8)\n" \ +" ldw r8, %1(r8)\n" \ +" jmp r8\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "r15"); +#elif defined(CONFIG_M68K) +/* + * d7 holds the pointer to the global_data, a0 is a call-clobbered + * register + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" move.l %%d7, %%a0\n" \ +" adda.l %0, %%a0\n" \ +" move.l (%%a0), %%a0\n" \ +" adda.l %1, %%a0\n" \ +" move.l (%%a0), %%a0\n" \ +" jmp (%%a0)\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "a0"); +#elif defined(CONFIG_MICROBLAZE) +/* + * r31 holds the pointer to the global_data. r5 is a call-clobbered. + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" lwi r5, r31, %0\n" \ +" lwi r5, r5, %1\n" \ +" bra r5\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "r5"); +#elif defined(CONFIG_BLACKFIN) +/* + * P5 holds the pointer to the global_data, P0 is a call-clobbered + * register + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ +" .globl _" #x "\n_" \ +#x ":\n" \ +" P0 = [P5 + %0]\n" \ +" P0 = [P0 + %1]\n" \ +" JUMP (P0)\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "P0"); +#elif defined(CONFIG_AVR32) +/* + * r6 holds the pointer to the global_data. r8 is call clobbered. + */ +#define EXPORT_FUNC(x) \ + asm volatile( \ + " .globl\t" #x "\n" \ + #x ":\n" \ + " ld.w r8, r6[%0]\n" \ + " ld.w pc, r8[%1]\n" \ + : \ + : "i"(offsetof(gd_t, jt)), "i"(XF_ ##x) \ + : "r8"); +#elif defined(CONFIG_SH) +/* + * r13 holds the pointer to the global_data. r1 is a call clobbered. + */ +#define EXPORT_FUNC(x) \ + asm volatile ( \ + " .align 2\n" \ + " .globl " #x "\n" \ + #x ":\n" \ + " mov r13, r1\n" \ + " add %0, r1\n" \ + " mov.l @r1, r2\n" \ + " add %1, r2\n" \ + " mov.l @r2, r1\n" \ + " jmp @r1\n" \ + " nop\n" \ + " nop\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x * sizeof(void *)) : "r1", "r2"); +#elif defined(CONFIG_SPARC) +/* + * g7 holds the pointer to the global_data. g1 is call clobbered. + */ +#define EXPORT_FUNC(x) \ + asm volatile( \ +" .globl\t" #x "\n" \ +#x ":\n" \ +" set %0, %%g1\n" \ +" or %%g1, %%g7, %%g1\n" \ +" ld [%%g1], %%g1\n" \ +" ld [%%g1 + %1], %%g1\n" \ +" call %%g1\n" \ +" nop\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(XF_ ## x) : "g1" ); + +#else +#error stubs definition missing for this architecture +#endif + +/* This function is necessary to prevent the compiler from + * generating prologue/epilogue, preparing stack frame etc. + * The stub functions are special, they do not use the stack + * frame passed to them, but pass it intact to the actual + * implementation. On the other hand, asm() statements with + * arguments can be used only inside the functions (gcc limitation) + */ +#if GCC_VERSION < 3004 +static +#endif /* GCC_VERSION */ +void __attribute__((unused)) dummy(void) +{ +#include <_exports.h> +} + +extern unsigned long __bss_start, _end; + +void app_startup(char **argv) +{ + unsigned char * cp = (unsigned char *) &__bss_start; + + /* Zero out BSS */ + while (cp < (unsigned char *)&_end) { + *cp++ = 0; + } + +#if defined(CONFIG_I386) + /* x86 does not have a dedicated register for passing global_data */ + global_data = (gd_t *)argv[-1]; + jt = global_data->jt; +#endif +} + +#undef EXPORT_FUNC diff --git a/examples/standalone/test_burst.c b/examples/standalone/test_burst.c new file mode 100644 index 0000000..7109c09 --- /dev/null +++ b/examples/standalone/test_burst.c @@ -0,0 +1,319 @@ +/* + * (C) Copyright 2005 + * 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 + * + * The test exercises SDRAM accesses in burst mode + */ + +#include <common.h> +#include <exports.h> + +#include <commproc.h> +#include <asm/mmu.h> +#include <asm/processor.h> + +#include <serial.h> +#include <watchdog.h> + +#include "test_burst.h" + +/* 8 MB test region of physical RAM */ +#define TEST_PADDR 0x00800000 +/* The uncached virtual region */ +#define TEST_VADDR_NC 0x00800000 +/* The cached virtual region */ +#define TEST_VADDR_C 0x01000000 +/* When an error is detected, the address where the error has been found, + and also the current and the expected data will be written to + the following flash address +*/ +#define TEST_FLASH_ADDR 0x40100000 + +/* Define GPIO ports to signal start of burst transfers and errors */ +#ifdef CONFIG_LWMON +/* Use PD.8 to signal start of burst transfers */ +#define GPIO1_DAT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddat) +#define GPIO1_BIT 0x0080 +/* Configure PD.8 as general purpose output */ +#define GPIO1_INIT \ + ((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pdpar &= ~GPIO1_BIT; \ + ((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddir |= GPIO1_BIT; +/* Use PD.9 to signal error */ +#define GPIO2_DAT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddat) +#define GPIO2_BIT 0x0040 +/* Configure PD.9 as general purpose output */ +#define GPIO2_INIT \ + ((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pdpar &= ~GPIO2_BIT; \ + ((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddir |= GPIO2_BIT; +#endif /* CONFIG_LWMON */ + + +static void test_prepare (void); +static int test_burst_start (unsigned long size, unsigned long pattern); +static void test_map_8M (unsigned long paddr, unsigned long vaddr, int cached); +static int test_mmu_is_on(void); +static void test_desc(unsigned long size); +static void test_error(char * step, volatile void * addr, unsigned long val, unsigned long pattern); +static void signal_init(void); +static void signal_start(void); +static void signal_error(void); +static void test_usage(void); + +static unsigned long test_pattern [] = { + 0x00000000, + 0xffffffff, + 0x55555555, + 0xaaaaaaaa, +}; + + +int test_burst (int argc, char *argv[]) +{ + unsigned long size = CACHE_LINE_SIZE; + unsigned int pass = 0; + int res = 0; + int i, j; + + if (argc == 3) { + char * d; + for (size = 0, d = argv[1]; *d >= '0' && *d <= '9'; d++) { + size *= 10; + size += *d - '0'; + } + if (size == 0 || *d) { + test_usage(); + return 1; + } + for (d = argv[2]; *d >= '0' && *d <= '9'; d++) { + pass *= 10; + pass += *d - '0'; + } + if (*d) { + test_usage(); + return 1; + } + } else if (argc > 3) { + test_usage(); + return 1; + } + + size += (CACHE_LINE_SIZE - 1); + size &= ~(CACHE_LINE_SIZE - 1); + + if (!test_mmu_is_on()) { + test_prepare(); + } + + test_desc(size); + + for (j = 0; !pass || j < pass; j++) { + for (i = 0; i < sizeof(test_pattern) / sizeof(test_pattern[0]); + i++) { + res = test_burst_start(size, test_pattern[i]); + if (res != 0) { + goto Done; + } + } + + printf ("Iteration #%d passed\n", j + 1); + + if (tstc() && 0x03 == getc()) + break; + } +Done: + return res; +} + +static void test_prepare (void) +{ + printf ("\n"); + + caches_init(); + disable_interrupts(); + mmu_init(); + + printf ("Interrupts are disabled\n"); + printf ("I-Cache is ON\n"); + printf ("D-Cache is ON\n"); + printf ("MMU is ON\n"); + + printf ("\n"); + + test_map_8M (TEST_PADDR, TEST_VADDR_NC, 0); + test_map_8M (TEST_PADDR, TEST_VADDR_C, 1); + + test_map_8M (TEST_FLASH_ADDR & 0xFF800000, TEST_FLASH_ADDR & 0xFF800000, 0); + + /* Configure GPIO ports */ + signal_init(); +} + +static int test_burst_start (unsigned long size, unsigned long pattern) +{ + volatile unsigned long * vaddr_c = (unsigned long *)TEST_VADDR_C; + volatile unsigned long * vaddr_nc = (unsigned long *)TEST_VADDR_NC; + int i, n; + int res = 1; + + printf ("Test pattern %08lx ...", pattern); + + n = size / 4; + + for (i = 0; i < n; i ++) { + vaddr_c [i] = pattern; + } + signal_start(); + flush_dcache_range((unsigned long)vaddr_c, (unsigned long)(vaddr_c + n) - 1); + + for (i = 0; i < n; i ++) { + register unsigned long tmp = vaddr_nc [i]; + if (tmp != pattern) { + test_error("2a", vaddr_nc + i, tmp, pattern); + goto Done; + } + } + + for (i = 0; i < n; i ++) { + register unsigned long tmp = vaddr_c [i]; + if (tmp != pattern) { + test_error("2b", vaddr_c + i, tmp, pattern); + goto Done; + } + } + + for (i = 0; i < n; i ++) { + vaddr_nc [i] = pattern; + } + + for (i = 0; i < n; i ++) { + register unsigned long tmp = vaddr_nc [i]; + if (tmp != pattern) { + test_error("3a", vaddr_nc + i, tmp, pattern); + goto Done; + } + } + + signal_start(); + for (i = 0; i < n; i ++) { + register unsigned long tmp = vaddr_c [i]; + if (tmp != pattern) { + test_error("3b", vaddr_c + i, tmp, pattern); + goto Done; + } + } + + res = 0; +Done: + printf(" %s\n", res == 0 ? "OK" : ""); + + return res; +} + +static void test_map_8M (unsigned long paddr, unsigned long vaddr, int cached) +{ + mtspr (MD_EPN, (vaddr & 0xFFFFFC00) | MI_EVALID); + mtspr (MD_TWC, MI_PS8MEG | MI_SVALID); + mtspr (MD_RPN, (paddr & 0xFFFFF000) | MI_BOOTINIT | (cached ? 0 : 2)); + mtspr (MD_AP, MI_Kp); +} + +static int test_mmu_is_on(void) +{ + unsigned long msr; + + asm volatile("mfmsr %0" : "=r" (msr) :); + + return msr & MSR_DR; +} + +static void test_desc(unsigned long size) +{ + printf( + "The following tests will be conducted:\n" + "1) Map %ld-byte region of physical RAM at 0x%08x\n" + " into two virtual regions:\n" + " one cached at 0x%08x and\n" + " the the other uncached at 0x%08x.\n", + size, TEST_PADDR, TEST_VADDR_NC, TEST_VADDR_C); + + puts( + "2) Fill the cached region with a pattern, and flush the cache\n" + "2a) Check the uncached region to match the pattern\n" + "2b) Check the cached region to match the pattern\n" + "3) Fill the uncached region with a pattern\n" + "3a) Check the cached region to match the pattern\n" + "3b) Check the uncached region to match the pattern\n" + "2b) Change the patterns and go to step 2\n" + "\n" + ); +} + +static void test_error( + char * step, volatile void * addr, unsigned long val, unsigned long pattern) +{ + volatile unsigned long * p = (void *)TEST_FLASH_ADDR; + + signal_error(); + + p[0] = (unsigned long)addr; + p[1] = val; + p[2] = pattern; + + printf ("\nError at step %s, addr %08lx: read %08lx, pattern %08lx", + step, (unsigned long)addr, val, pattern); +} + +static void signal_init(void) +{ +#if defined(GPIO1_INIT) + GPIO1_INIT; +#endif +#if defined(GPIO2_INIT) + GPIO2_INIT; +#endif +} + +static void signal_start(void) +{ +#if defined(GPIO1_INIT) + if (GPIO1_DAT & GPIO1_BIT) { + GPIO1_DAT &= ~GPIO1_BIT; + } else { + GPIO1_DAT |= GPIO1_BIT; + } +#endif +} + +static void signal_error(void) +{ +#if defined(GPIO2_INIT) + if (GPIO2_DAT & GPIO2_BIT) { + GPIO2_DAT &= ~GPIO2_BIT; + } else { + GPIO2_DAT |= GPIO2_BIT; + } +#endif +} + +static void test_usage(void) +{ + printf("Usage: go 0x40004 [size] [count]\n"); +} diff --git a/examples/standalone/test_burst.h b/examples/standalone/test_burst.h new file mode 100644 index 0000000..f85928c --- /dev/null +++ b/examples/standalone/test_burst.h @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2005 + * 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 + */ + +#ifndef _TEST_BURST_H +#define _TEST_BURST_H + +/* Cache line size */ +#define CACHE_LINE_SIZE 16 +/* Binary logarithm of the cache line size */ +#define LG_CACHE_LINE_SIZE 4 + +#ifndef __ASSEMBLY__ +extern void mmu_init(void); +extern void caches_init(void); +extern void flush_dcache_range(unsigned long start, unsigned long stop); +#endif + +#endif /* _TEST_BURST_H */ diff --git a/examples/standalone/test_burst_lib.S b/examples/standalone/test_burst_lib.S new file mode 100644 index 0000000..aef4e32 --- /dev/null +++ b/examples/standalone/test_burst_lib.S @@ -0,0 +1,170 @@ +/* + * (C) Copyright 2005 + * 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 <config.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> +#include <asm/cache.h> +#include <asm/mmu.h> +#include "test_burst.h" + + .text +/* + * void mmu_init(void); + * + * This function turns the MMU on + * + * Three 8 MByte regions are mapped 1:1, uncached + * - SDRAM lower 8 MByte + * - SDRAM higher 8 MByte + * - IMMR + */ + .global mmu_init +mmu_init: + tlbia /* Invalidate all TLB entries */ + li r8, 0 + mtspr MI_CTR, r8 /* Set instruction control to zero */ + lis r8, MD_RESETVAL@h + mtspr MD_CTR, r8 /* Set data TLB control */ + + /* Now map the lower 8 Meg into the TLBs. For this quick hack, + * we can load the instruction and data TLB registers with the + * same values. + */ + li r8, MI_EVALID /* Create EPN for address 0 */ + mtspr MI_EPN, r8 + mtspr MD_EPN, r8 + li r8, MI_PS8MEG /* Set 8M byte page */ + ori r8, r8, MI_SVALID /* Make it valid */ + mtspr MI_TWC, r8 + mtspr MD_TWC, r8 + li r8, MI_BOOTINIT|0x2 /* Create RPN for address 0 */ + mtspr MI_RPN, r8 /* Store TLB entry */ + mtspr MD_RPN, r8 + lis r8, MI_Kp@h /* Set the protection mode */ + mtspr MI_AP, r8 + mtspr MD_AP, r8 + + /* Now map the higher 8 Meg into the TLBs. For this quick hack, + * we can load the instruction and data TLB registers with the + * same values. + */ + lwz r9,20(r2) /* gd->ram_size */ + addis r9,r9,-0x80 + + mr r8, r9 /* Higher 8 Meg in SDRAM */ + ori r8, r8, MI_EVALID /* Mark page valid */ + mtspr MI_EPN, r8 + mtspr MD_EPN, r8 + li r8, MI_PS8MEG /* Set 8M byte page */ + ori r8, r8, MI_SVALID /* Make it valid */ + mtspr MI_TWC, r8 + mtspr MD_TWC, r8 + mr r8, r9 + ori r8, r8, MI_BOOTINIT|0x2 + mtspr MI_RPN, r8 /* Store TLB entry */ + mtspr MD_RPN, r8 + lis r8, MI_Kp@h /* Set the protection mode */ + mtspr MI_AP, r8 + mtspr MD_AP, r8 + + /* Map another 8 MByte at the IMMR to get the processor + * internal registers (among other things). + */ + mfspr r9, 638 /* Get current IMMR */ + andis. r9, r9, 0xff80 /* Get 8Mbyte boundary */ + + mr r8, r9 /* Create vaddr for TLB */ + ori r8, r8, MD_EVALID /* Mark it valid */ + mtspr MD_EPN, r8 + li r8, MD_PS8MEG /* Set 8M byte page */ + ori r8, r8, MD_SVALID /* Make it valid */ + mtspr MD_TWC, r8 + mr r8, r9 /* Create paddr for TLB */ + ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ + mtspr MD_RPN, r8 + + /* We now have the lower and higher 8 Meg mapped into TLB entries, + * and the caches ready to work. + */ + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SRR1,r0 + mflr r0 + mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ + +/* + * void caches_init(void); + */ + .globl caches_init +caches_init: + sync + + mfspr r3, IC_CST /* Clear error bits */ + mfspr r3, DC_CST + + lis r3, IDC_UNALL@h /* Unlock all */ + mtspr IC_CST, r3 + mtspr DC_CST, r3 + + lis r3, IDC_INVALL@h /* Invalidate all */ + mtspr IC_CST, r3 + mtspr DC_CST, r3 + + lis r3, IDC_ENABLE@h /* Enable all */ + mtspr IC_CST, r3 + mtspr DC_CST, r3 + + blr + +/* + * void flush_dcache_range(unsigned long start, unsigned long stop); + */ + .global flush_dcache_range +flush_dcache_range: + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbf 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbf's to get to ram */ + blr + +/* + * void disable_interrupts(void); + */ + .global disable_interrupts +disable_interrupts: + mfmsr r0 + rlwinm r0,r0,0,17,15 + mtmsr r0 + blr diff --git a/examples/standalone/timer.c b/examples/standalone/timer.c new file mode 100644 index 0000000..6628b21 --- /dev/null +++ b/examples/standalone/timer.c @@ -0,0 +1,349 @@ +/* + * (C) Copyright 2000 + * 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 <common.h> +#include <commproc.h> +#include <mpc8xx_irq.h> +#include <exports.h> + +DECLARE_GLOBAL_DATA_PTR; + +#undef DEBUG + +#define TIMER_PERIOD 1000000 /* 1 second clock */ + +static void timer_handler (void *arg); + + +/* Access functions for the Machine State Register */ +static __inline__ unsigned long get_msr(void) +{ + unsigned long msr; + + asm volatile("mfmsr %0" : "=r" (msr) :); + return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ + asm volatile("mtmsr %0" : : "r" (msr)); +} + +/* + * Definitions to access the CPM Timer registers + * See 8xx_immap.h for Internal Memory Map layout, + * and commproc.h for CPM Interrupt vectors (aka "IRQ"s) + */ + +typedef struct tid_8xx_cpmtimer_s { + int cpm_vec; /* CPM Interrupt Vector for this timer */ + ushort *tgcrp; /* Pointer to Timer Global Config Reg. */ + ushort *tmrp; /* Pointer to Timer Mode Register */ + ushort *trrp; /* Pointer to Timer Reference Register */ + ushort *tcrp; /* Pointer to Timer Capture Register */ + ushort *tcnp; /* Pointer to Timer Counter Register */ + ushort *terp; /* Pointer to Timer Event Register */ +} tid_8xx_cpmtimer_t; + +#ifndef CLOCKRATE +# define CLOCKRATE 64 +#endif + +#define CPMT_CLOCK_DIV 16 +#define CPMT_MAX_PRESCALER 256 +#define CPMT_MAX_REFERENCE 65535 /* max. unsigned short */ + +#define CPMT_MAX_TICKS (CPMT_MAX_REFERENCE * CPMT_MAX_PRESCALER) +#define CPMT_MAX_TICKS_WITH_DIV (CPMT_MAX_REFERENCE * CPMT_MAX_PRESCALER * CPMT_CLOCK_DIV) +#define CPMT_MAX_INTERVAL (CPMT_MAX_TICKS_WITH_DIV / CLOCKRATE) + +/* For now: always use max. prescaler value */ +#define CPMT_PRESCALER (CPMT_MAX_PRESCALER) + +/* CPM Timer Event Register Bits */ +#define CPMT_EVENT_CAP 0x0001 /* Capture Event */ +#define CPMT_EVENT_REF 0x0002 /* Reference Counter Event */ + +/* CPM Timer Global Config Register */ +#define CPMT_GCR_RST 0x0001 /* Reset Timer */ +#define CPMT_GCR_STP 0x0002 /* Stop Timer */ +#define CPMT_GCR_FRZ 0x0004 /* Freeze Timer */ +#define CPMT_GCR_GM_CAS 0x0008 /* Gate Mode / Cascade Timers */ +#define CPMT_GCR_MASK (CPMT_GCR_RST|CPMT_GCR_STP|CPMT_GCR_FRZ|CPMT_GCR_GM_CAS) + +/* CPM Timer Mode register */ +#define CPMT_MR_GE 0x0001 /* Gate Enable */ +#define CPMT_MR_ICLK_CASC 0x0000 /* Clock internally cascaded */ +#define CPMT_MR_ICLK_CLK 0x0002 /* Clock = system clock */ +#define CPMT_MR_ICLK_CLKDIV 0x0004 /* Clock = system clock / 16 */ +#define CPMT_MR_ICLK_TIN 0x0006 /* Clock = TINx signal */ +#define CPMT_MR_FRR 0x0008 /* Free Run / Restart */ +#define CPMT_MR_ORI 0x0010 /* Out. Reference Interrupt En. */ +#define CPMT_MR_OM 0x0020 /* Output Mode */ +#define CPMT_MR_CE_DIS 0x0000 /* Capture/Interrupt disabled */ +#define CPMT_MR_CE_RISE 0x0040 /* Capt./Interr. on rising TIN */ +#define CPMT_MR_CE_FALL 0x0080 /* Capt./Interr. on falling TIN */ +#define CPMT_MR_CE_ANY 0x00C0 /* Capt./Interr. on any TIN edge*/ + + +/* + * which CPM timer to use - index starts at 0 (= timer 1) + */ +#define TID_TIMER_ID 0 /* use CPM timer 1 */ + +void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval); + +static char *usage = "\n[q, b, e, ?] "; + +int timer (int argc, char *argv[]) +{ + cpmtimer8xx_t *cpmtimerp; /* Pointer to the CPM Timer structure */ + tid_8xx_cpmtimer_t hw; + tid_8xx_cpmtimer_t *hwp = &hw; + int c; + int running; + + app_startup(argv); + + /* Pointer to CPM Timer structure */ + cpmtimerp = &((immap_t *) gd->bd->bi_immr_base)->im_cpmtimer; + + printf ("TIMERS=0x%x\n", (unsigned) cpmtimerp); + + /* Initialize pointers depending on which timer we use */ + switch (TID_TIMER_ID) { + case 0: + hwp->tmrp = &(cpmtimerp->cpmt_tmr1); + hwp->trrp = &(cpmtimerp->cpmt_trr1); + hwp->tcrp = &(cpmtimerp->cpmt_tcr1); + hwp->tcnp = &(cpmtimerp->cpmt_tcn1); + hwp->terp = &(cpmtimerp->cpmt_ter1); + hwp->cpm_vec = CPMVEC_TIMER1; + break; + case 1: + hwp->tmrp = &(cpmtimerp->cpmt_tmr2); + hwp->trrp = &(cpmtimerp->cpmt_trr2); + hwp->tcrp = &(cpmtimerp->cpmt_tcr2); + hwp->tcnp = &(cpmtimerp->cpmt_tcn2); + hwp->terp = &(cpmtimerp->cpmt_ter2); + hwp->cpm_vec = CPMVEC_TIMER2; + break; + case 2: + hwp->tmrp = &(cpmtimerp->cpmt_tmr3); + hwp->trrp = &(cpmtimerp->cpmt_trr3); + hwp->tcrp = &(cpmtimerp->cpmt_tcr3); + hwp->tcnp = &(cpmtimerp->cpmt_tcn3); + hwp->terp = &(cpmtimerp->cpmt_ter3); + hwp->cpm_vec = CPMVEC_TIMER3; + break; + case 3: + hwp->tmrp = &(cpmtimerp->cpmt_tmr4); + hwp->trrp = &(cpmtimerp->cpmt_trr4); + hwp->tcrp = &(cpmtimerp->cpmt_tcr4); + hwp->tcnp = &(cpmtimerp->cpmt_tcn4); + hwp->terp = &(cpmtimerp->cpmt_ter4); + hwp->cpm_vec = CPMVEC_TIMER4; + break; + } + + hwp->tgcrp = &cpmtimerp->cpmt_tgcr; + + printf ("Using timer %d\n" + "tgcr @ 0x%x, tmr @ 0x%x, trr @ 0x%x," + " tcr @ 0x%x, tcn @ 0x%x, ter @ 0x%x\n", + TID_TIMER_ID + 1, + (unsigned) hwp->tgcrp, + (unsigned) hwp->tmrp, + (unsigned) hwp->trrp, + (unsigned) hwp->tcrp, + (unsigned) hwp->tcnp, + (unsigned) hwp->terp + ); + + /* reset timer */ + *hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); + + /* clear all events */ + *hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF); + + printf (usage); + running = 0; + while ((c = getc()) != 'q') { + if (c == 'b') { + + setPeriod (hwp, TIMER_PERIOD); /* Set period and start ticking */ + + /* Install interrupt handler (enable timer in CIMR) */ + install_hdlr (hwp->cpm_vec, timer_handler, hwp); + + printf ("Enabling timer\n"); + + /* enable timer */ + *hwp->tgcrp |= (CPMT_GCR_RST << TID_TIMER_ID); + running = 1; + +#ifdef DEBUG + printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," + " tcr=0x%x, tcn=0x%x, ter=0x%x\n", + *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, + *hwp->tcrp, *hwp->tcnp, *hwp->terp + ); +#endif + } else if (c == 'e') { + + printf ("Stopping timer\n"); + + *hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); + running = 0; + +#ifdef DEBUG + printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," + " tcr=0x%x, tcn=0x%x, ter=0x%x\n", + *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, + *hwp->tcrp, *hwp->tcnp, *hwp->terp + ); +#endif + /* Uninstall interrupt handler */ + free_hdlr (hwp->cpm_vec); + + } else if (c == '?') { +#ifdef DEBUG + cpic8xx_t *cpm_icp = &((immap_t *) gd->bd->bi_immr_base)->im_cpic; + sysconf8xx_t *siup = &((immap_t *) gd->bd->bi_immr_base)->im_siu_conf; +#endif + + printf ("\ntgcr=0x%x, tmr=0x%x, trr=0x%x," + " tcr=0x%x, tcn=0x%x, ter=0x%x\n", + *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, + *hwp->tcrp, *hwp->tcnp, *hwp->terp + ); +#ifdef DEBUG + printf ("SIUMCR=0x%08lx, SYPCR=0x%08lx," + " SIMASK=0x%08lx, SIPEND=0x%08lx\n", + siup->sc_siumcr, + siup->sc_sypcr, + siup->sc_simask, + siup->sc_sipend + ); + + printf ("CIMR=0x%08lx, CICR=0x%08lx, CIPR=0x%08lx\n", + cpm_icp->cpic_cimr, + cpm_icp->cpic_cicr, + cpm_icp->cpic_cipr + ); +#endif + } else { + printf ("\nEnter: q - quit, b - start timer, e - stop timer, ? - get status\n"); + } + printf (usage); + } + if (running) { + printf ("Stopping timer\n"); + *hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); + free_hdlr (hwp->cpm_vec); + } + + return (0); +} + + +/* Set period in microseconds and start. + * Truncate to maximum period if more than this is requested - but warn about it. + */ + +void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval) +{ + unsigned short prescaler; + unsigned long ticks; + + printf ("Set interval %ld us\n", interval); + + /* Warn if requesting longer period than possible */ + if (interval > CPMT_MAX_INTERVAL) { + printf ("Truncate interval %ld to maximum (%d)\n", + interval, CPMT_MAX_INTERVAL); + interval = CPMT_MAX_INTERVAL; + } + /* + * Check if we want to use clock divider: + * Since the reference counter can be incremented only in integer steps, + * we try to keep it as big as possible to allow the resulting period to be + * as precise as possible. + */ + /* prescaler, enable interrupt, restart after ref count is reached */ + prescaler = (ushort) ((CPMT_PRESCALER - 1) << 8) | + CPMT_MR_ORI | + CPMT_MR_FRR; + + ticks = ((ulong) CLOCKRATE * interval); + + if (ticks > CPMT_MAX_TICKS) { + ticks /= CPMT_CLOCK_DIV; + prescaler |= CPMT_MR_ICLK_CLKDIV; /* use system clock divided by 16 */ + } else { + prescaler |= CPMT_MR_ICLK_CLK; /* use system clock without divider */ + } + +#ifdef DEBUG + printf ("clock/%d, prescale factor %d, reference %ld, ticks %ld\n", + (ticks > CPMT_MAX_TICKS) ? CPMT_CLOCK_DIV : 1, + CPMT_PRESCALER, + (ticks / CPMT_PRESCALER), + ticks + ); +#endif + + /* set prescaler register */ + *hwp->tmrp = prescaler; + + /* clear timer counter */ + *hwp->tcnp = 0; + + /* set reference register */ + *hwp->trrp = (unsigned short) (ticks / CPMT_PRESCALER); + +#ifdef DEBUG + printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," + " tcr=0x%x, tcn=0x%x, ter=0x%x\n", + *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, + *hwp->tcrp, *hwp->tcnp, *hwp->terp + ); +#endif +} + +/* + * Handler for CPMVEC_TIMER1 interrupt + */ +static +void timer_handler (void *arg) +{ + tid_8xx_cpmtimer_t *hwp = (tid_8xx_cpmtimer_t *)arg; + + /* printf ("** TER1=%04x ** ", *hwp->terp); */ + + /* just for demonstration */ + printf ("."); + + /* clear all possible events: Ref. and Cap. */ + *hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF); +} diff --git a/examples/standalone/x86-testapp.c b/examples/standalone/x86-testapp.c new file mode 100644 index 0000000..e8603d9 --- /dev/null +++ b/examples/standalone/x86-testapp.c @@ -0,0 +1,87 @@ +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +void *func[8], **pfunc; + +typedef struct xxx xxx_t; +struct xxx { + int dummy; + void **pfunc; +} q; + +#define XF_strcpy 3 +#define XF_printf 4 + +#define LABEL(x) \ +asm volatile ( \ + +#if defined(__i386__) +#define EXPORT_FUNC(x) \ +asm volatile ( \ +" .globl mon_" #x "\n" \ +"mon_" #x ":\n" \ +" movl %0, %%eax\n" \ +" movl pfunc, %%ecx\n" \ +" jmp *(%%ecx,%%eax)\n" \ + : : "i"(XF_ ## x * sizeof(void *)) : "eax", "ecx"); +#elif defined(__powerpc__) +#define EXPORT_FUNC(x) \ +asm volatile ( \ +" .globl mon_" #x "\n" \ +"mon_" #x ":\n" \ +" lwz %%r11, %0(%%r2)\n" \ +" lwz %%r11, %1(%%r11)\n" \ +" mtctr %%r11\n" \ +" bctr\n" \ + : : "i"(offsetof(xxx_t, pfunc)), "i"(XF_ ## x * sizeof(void *)) : "r11", "r2"); +#elif defined(__arm__) +#define EXPORT_FUNC(x) \ +asm volatile ( \ +" .globl mon_" #x "\n" \ +"mon_" #x ":\n" \ +" ldr ip, [r8, %0]\n" \ +" ldr pc, [ip, %1]\n" \ + : : "i"(offsetof(xxx_t, pfunc)), "i"(XF_ ## x * sizeof(void *)) : "ip"); +#elif defined(__mips__) +#define EXPORT_FUNC(x) \ +asm volatile ( \ +" .globl mon_" #x "\n" \ +"mon_" #x ":\n" \ +" lw $25, %0($26)\n" \ +" lw $25, %1($25)\n" \ +" jr $25\n" \ + : : "i"(offsetof(xxx_t, pfunc)), "i"(XF_ ## x * sizeof(void *)) : "t9"); +#else +#error [No stub code for this arch] +#endif + +void dummy(void) +{ +EXPORT_FUNC(printf) +EXPORT_FUNC(strcpy) +} + +int main(void) +{ +#if defined(__i386__) + xxx_t *pq; +#elif defined(__powerpc__) + register volatile xxx_t *pq asm("r2"); +#elif defined(__arm__) + register volatile xxx_t *pq asm("r8"); +#elif defined(__mips__) + register volatile xxx_t *pq asm("k0"); +#endif + char buf[32]; + + func[XF_strcpy] = strcpy; + func[XF_printf] = printf; + pq = &q; + pq->pfunc = pfunc = func; + + mon_strcpy(buf, "test"); + mon_printf("hi %s %d z\n", buf, 444); + + return 0; +} |