Add a boot loader for ixp425 based boards like the Gateworks Avila

and ADI Pronghorn Metro with Redboot on them.
This commit is contained in:
John Hay 2008-10-06 19:38:10 +00:00
parent 2c8995842c
commit 563db8c39c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=183651
7 changed files with 1473 additions and 0 deletions

View file

@ -0,0 +1,42 @@
# $FreeBSD$
.if !target(__ixp425_boot_Makefile.inc__)
.PATH: ${.CURDIR}/../../../../libkern ${.CURDIR}/../../../../libkern/arm
__ixp425_boot_Makefile.inc__:
# Both Avila and Pronghorn Metro are supported by ixp425
BOOT_FLAVOR=ixp425
CFLAGS+=-Os -ffreestanding \
-I${.CURDIR}/../../../.. \
-I${.CURDIR}/../../../../arm \
-DCPU_XSCALE_IXP425 \
-Wall -Waggregate-return \
-Werror \
-Wnested-externs \
-Wpointer-arith -Wshadow -Wwrite-strings \
-Wmissing-prototypes \
-Wmissing-declarations
# -Wstrict-prototypes
CFLAGS+=-DBOOT_${BOOT_FLAVOR:U}
LD ?= ld
OBJCOPY ?= objcopy
.if defined(P)
${P}: ${OBJS}
${LD} ${LDFLAGS} -o ${.TARGET} ${OBJS}
CLEANFILES+= ${P}
.endif
.if defined(WITH_TAG_LIST)
MK_TAG_LIST:=yes
.else
MK_TAG_LIST:=no
.endif
.endif

View file

@ -0,0 +1,75 @@
# $FreeBSD$
# We get a lot of the std lib functions from here.
.PATH: ${.CURDIR}/../../at91/libat91
# Enable to get debug msgs
#DEBUG=yes
# Hack to search through the kernel for ufs:ad0s1a and replace it with
# the correct one for the active slice/partition.
FIXUP_BOOT_DRV=yes
P=boot2
FILES=${P}
SRCS=arm_init.S boot2.c ${BOOT_FLAVOR:L}_board.c
SRCS+=memchr.c memcmp.c memcpy.c memmem.c memset.c printf.c strcmp.c strcpy.c
SRCS+=strlen.c ashldi3.c divsi3.S muldi3.c
NO_MAN=
KERNPHYSADDR=0x180000
KERNVIRTADDR=${KERNPHYSADDR}
BOOT_STACK=0x200000-4
M=${MACHINE_ARCH}
LDFLAGS=-e ${KERNPHYSADDR} -EB -T ldscript.${M}
OBJS+= ${SRCS:N*.h:R:S/$/.o/g}
S=${.CURDIR}/../../../..
CFLAGS+= \
-DBOOT_STACK=${BOOT_STACK} \
-I${.CURDIR}/../../../common \
-I${.CURDIR}
.if defined(FIXUP_BOOT_DRV)
CFLAGS+=-DFIXUP_BOOT_DRV
.endif
.if defined(DEBUG)
CFLAGS+=-DDEBUG
.endif
ldscript.$M: $S/conf/ldscript.$M
cat $S/conf/ldscript.$M|sed s/KERNPHYSADDR/${KERNPHYSADDR}/g| \
sed s/KERNVIRTADDR/${KERNVIRTADDR}/g | \
sed s/" + SIZEOF_HEADERS"// > ldscript.$M
${P}: ldscript.$M
CLEANFILES+=ldscript.$M
memchr.c: $S/../lib/libc/string/memchr.c
sed -e 's/string\.h/lib.h/' < $S/../lib/libc/string/memchr.c > \
${.TARGET}
memmem.c: $S/../lib/libc/string/memmem.c
sed -e 's/string\.h/lib.h/' < $S/../lib/libc/string/memmem.c > \
${.TARGET}
CLEANFILES+=memchr.c memmem.c
ashldi3.o: $S/libkern/ashldi3.c
cc -c ${CFLAGS} -D_KERNEL -o ${.TARGET} ${.IMPSRC}
divsi3.o: $S/libkern/${M}/divsi3.S
cc -c ${CFLAGS} -D_KERNEL -o ${.TARGET} ${.IMPSRC}
muldi3.o: $S/libkern/${M}/muldi3.c
cc -c ${CFLAGS} -D_KERNEL -o ${.TARGET} ${.IMPSRC}
inflate.c: $S/kern/inflate.c
sed -e 's/extern void putstr (char/extern void putstr (const char/' < \
$S/kern/inflate.c > ${.TARGET}
CLEANFILES+=inflate.c
.include <bsd.prog.mk>

View file

@ -0,0 +1,50 @@
/*-
* Copyright (c) 2008 John Hay. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
start:
/* Initialise bss and sp */
nop
adr r1, .Lstart
ldmia r1, {r1, r2, sp} /* Set initial stack and */
sub r2, r2, r1 /* get zero init data */
mov r3, #0
.L1:
str r3, [r1], #0x0004 /* get zero init data */
subs r2, r2, #4
bgt .L1
.extern main
bl main
/* main should not return. If it does, spin forever */
infiniteLoop:
b infiniteLoop
.Lstart:
.word _edata
.word _end
.word BOOT_STACK
/* End */

View file

@ -0,0 +1,483 @@
/*-
* Copyright (c) 2008 John Hay
* Copyright (c) 1998 Robert Nordier
* All rights reserved.
*
* Redistribution and use in source and binary forms are freely
* permitted provided that the above copyright notice and this
* paragraph and the following disclaimer are duplicated in all
* such forms.
*
* This software is provided "AS IS" and without any express or
* implied warranties, including, without limitation, the implied
* warranties of merchantability and fitness for a particular
* purpose.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/disklabel.h>
#include <sys/diskmbr.h>
#include <sys/dirent.h>
#include <sys/reboot.h>
#include <machine/elf.h>
#include <stdarg.h>
#include "lib.h"
#define RBX_ASKNAME 0x0 /* -a */
#define RBX_SINGLE 0x1 /* -s */
/* 0x2 is reserved for log2(RB_NOSYNC). */
/* 0x3 is reserved for log2(RB_HALT). */
/* 0x4 is reserved for log2(RB_INITNAME). */
#define RBX_DFLTROOT 0x5 /* -r */
/* #define RBX_KDB 0x6 -d */
/* 0x7 is reserved for log2(RB_RDONLY). */
/* 0x8 is reserved for log2(RB_DUMP). */
/* 0x9 is reserved for log2(RB_MINIROOT). */
#define RBX_CONFIG 0xa /* -c */
#define RBX_VERBOSE 0xb /* -v */
/* #define RBX_SERIAL 0xc -h */
/* #define RBX_CDROM 0xd -C */
/* 0xe is reserved for log2(RB_POWEROFF). */
#define RBX_GDB 0xf /* -g */
/* #define RBX_MUTE 0x10 -m */
/* 0x11 is reserved for log2(RB_SELFTEST). */
/* 0x12 is reserved for boot programs. */
/* 0x13 is reserved for boot programs. */
/* #define RBX_PAUSE 0x14 -p */
/* #define RBX_QUIET 0x15 -q */
#define RBX_NOINTR 0x1c /* -n */
/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
/* #define RBX_DUAL 0x1d -D */
/* 0x1f is reserved for log2(RB_BOOTINFO). */
/* pass: -a, -s, -r, -v, -g */
#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
OPT_SET(RBX_DFLTROOT) | \
OPT_SET(RBX_VERBOSE) | \
OPT_SET(RBX_GDB))
#define PATH_CONFIG "/boot.config"
#define PATH_KERNEL "/boot/kernel/kernel"
extern uint32_t _end;
#define NOPT 6
#define OPT_SET(opt) (1 << (opt))
#define OPT_CHECK(opt) ((opts) & OPT_SET(opt))
static const char optstr[NOPT] = "agnrsv";
static const unsigned char flags[NOPT] = {
RBX_ASKNAME,
RBX_GDB,
RBX_NOINTR,
RBX_DFLTROOT,
RBX_SINGLE,
RBX_VERBOSE
};
static unsigned dsk_start;
static char cmd[512];
static char kname[1024];
static uint32_t opts;
static int dsk_meta;
static int bootslice;
static int bootpart;
static int disk_layout;
#define DL_UNKNOWN 0
#define DL_RAW 1 /* Dangerously dedicated */
#define DL_SLICE 2 /* Use only slices (DOS partitions) */
#define DL_SLICEPART 3 /* Use slices and partitions */
static void load(void);
static int parse(void);
static int xfsread(ino_t, void *, size_t);
static int dskread(void *, unsigned, unsigned);
static int drvread(void *, unsigned, unsigned);
#ifdef FIXUP_BOOT_DRV
static void fixup_boot_drv(caddr_t, int, int, int);
#endif
#include "ufsread.c"
#ifdef DEBUG
#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DPRINTF(fmt, ...)
#endif
static inline int
xfsread(ino_t inode, void *buf, size_t nbyte)
{
if ((size_t)fsread(inode, buf, nbyte) != nbyte)
return -1;
return 0;
}
static inline void
getstr(int c)
{
char *s;
s = cmd;
if (c == 0)
c = getc(10000);
for (;;) {
switch (c) {
case 0:
break;
case '\177':
case '\b':
if (s > cmd) {
s--;
printf("\b \b");
}
break;
case '\n':
case '\r':
*s = 0;
return;
default:
if (s - cmd < sizeof(cmd) - 1)
*s++ = c;
xputchar(c);
}
c = getc(10000);
}
}
int
main(void)
{
const char *bt;
int autoboot, c = 0;
ino_t ino;
dmadat = (void *)(0x1c0000);
p_memset((char *)dmadat, 0, 32 * 1024);
bt = board_init();
printf("FreeBSD ARM (%s) boot2 v%d.%d\n", bt, 0, 3);
autoboot = 1;
/* Process configuration file */
if ((ino = lookup(PATH_CONFIG)))
fsread(ino, cmd, sizeof(cmd));
if (*cmd) {
if (parse())
autoboot = 0;
printf("%s: %s\n", PATH_CONFIG, cmd);
/* Do not process this command twice */
*cmd = 0;
}
if (*kname == '\0')
strcpy(kname, PATH_KERNEL);
/* Present the user with the boot2 prompt. */
for (;;) {
printf("\nDefault: %s\nboot: ", kname);
if (!autoboot ||
(OPT_CHECK(RBX_NOINTR) == 0 && (c = getc(2)) != 0))
getstr(c);
xputchar('\n');
autoboot = 0;
c = 0;
DPRINTF("cmd is '%s'\n", cmd);
if (parse())
xputchar('\a');
else
load();
}
}
static void
load(void)
{
Elf32_Ehdr eh;
static Elf32_Phdr ep[2];
caddr_t p;
ino_t ino;
uint32_t addr;
int i, j;
#ifdef FIXUP_BOOT_DRV
caddr_t staddr;
int klen;
staddr = (caddr_t)0xffffffff;
klen = 0;
#endif
if (!(ino = lookup(kname))) {
if (!ls)
printf("No %s\n", kname);
return;
}
DPRINTF("Found %s\n", kname);
if (xfsread(ino, &eh, sizeof(eh)))
return;
if (!IS_ELF(eh)) {
printf("Invalid %s\n", "format");
return;
}
fs_off = eh.e_phoff;
for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
if (xfsread(ino, ep + j, sizeof(ep[0])))
return;
if (ep[j].p_type == PT_LOAD)
j++;
}
for (i = 0; i < 2; i++) {
p = (caddr_t)(ep[i].p_paddr & 0x0fffffff);
fs_off = ep[i].p_offset;
#ifdef FIXUP_BOOT_DRV
if (staddr == (caddr_t)0xffffffff)
staddr = p;
klen += ep[i].p_filesz;
#endif
if (xfsread(ino, p, ep[i].p_filesz))
return;
}
addr = eh.e_entry & 0x0fffffff;
DPRINTF("Entry point %x for %s\n", addr, kname);
clr_board();
#ifdef FIXUP_BOOT_DRV
fixup_boot_drv(staddr, klen, bootslice, bootpart);
#endif
((void(*)(int))addr)(RB_BOOTINFO /* XXX | (opts & RBX_MASK) */);
}
static int
parse()
{
char *arg = cmd;
char *ep, *p;
int c, i;
while ((c = *arg++)) {
if (c == ' ' || c == '\t' || c == '\n')
continue;
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
ep = p;
if (*p)
*p++ = 0;
if (c == '-') {
while ((c = *arg++)) {
for (i = 0; c != optstr[i]; i++)
if (i == NOPT - 1)
return -1;
opts ^= OPT_SET(flags[i]);
}
} else {
arg--;
/* look for ad0s1a:... | ad0s1:... */
if (strlen(arg) > 6 && arg[0] == 'a' &&
arg[1] == 'd' && arg[3] == 's' &&
(arg[5] == ':' || arg[6] == ':')) {
/* XXX Should also handle disk. */
bootslice = arg[4] - '0';
if (bootslice < 1 || bootslice > 4)
return (-1);
bootpart = 0;
if (arg[5] != ':')
bootpart = arg[5] - 'a';
if (bootpart < 0 || bootpart > 7)
return (-1);
dsk_meta = 0;
if (arg[5] == ':')
arg += 6;
else
arg += 7;
/* look for ad0a:... */
} else if (strlen(arg) > 4 && arg[0] == 'a' &&
arg[1] == 'd' && arg[2] == '0' && arg[4] == ':') {
bootslice = 0;
bootpart = arg[3] - 'a';
if (bootpart < 0 || bootpart > 7)
return (-1);
dsk_meta = 0;
arg += 5;
}
if ((i = ep - arg)) {
if ((size_t)i >= sizeof(kname))
return -1;
memcpy(kname, arg, i + 1);
}
}
arg = p;
}
return 0;
}
/*
* dskread() will try to handle the disk layouts that are typically
* encountered.
* - raw or "Dangerously Dedicated" mode. No real slice table, just the
* default one that is included with bsdlabel -B. Typically this is
* used with ROOTDEVNAME=\"ufs:ad0a\".
* - slice only. Only a slice table is installed with no bsd label or
* bsd partition table. This is typically used with
* ROOTDEVNAME=\"ufs:ad0s1\".
* - slice + bsd label + partition table. This is typically done with
* with fdisk + bsdlabel and is used with ROOTDEVNAME=\"ufs:ad0s1a\".
*/
static int
dskread(void *buf, unsigned lba, unsigned nblk)
{
struct dos_partition *dp;
struct disklabel *d;
char *sec;
int i;
if (!dsk_meta) {
sec = dmadat->secbuf;
dsk_start = 0;
if (drvread(sec, DOSBBSECTOR, 1))
return -1;
dp = (void *)(sec + DOSPARTOFF);
if (bootslice != 0) {
i = bootslice - 1;
if (dp[i].dp_typ != DOSPTYP_386BSD)
return -1;
} else {
for (i = 0; i < NDOSPART; i++) {
if ((dp[i].dp_typ == DOSPTYP_386BSD) &&
(dp[i].dp_flag == 0x80))
break;
}
}
if (i != NDOSPART) {
bootslice = i + 1;
DPRINTF("Found an active fbsd slice. (%d)\n", i + 1);
/*
* Although dp_start is aligned within the disk
* partition structure, DOSPARTOFF is 446, which
* is only word (2) aligned, not longword (4)
* aligned. Cope by using memcpy to fetch the
* start of this partition.
*/
memcpy(&dsk_start, &dp[i].dp_start, 4);
dsk_start = swap32(dsk_start);
DPRINTF("dsk_start %x\n", dsk_start);
if ((bootslice == 4) && (dsk_start == 0)) {
disk_layout = DL_RAW;
bootslice = 0;
}
}
if (drvread(sec, dsk_start + LABELSECTOR, 1))
return -1;
d = (void *)(sec + LABELOFFSET);
if ((d->d_magic == DISKMAGIC && d->d_magic2 == DISKMAGIC) ||
(swap32(d->d_magic) == DISKMAGIC &&
swap32(d->d_magic2) == DISKMAGIC)) {
DPRINTF("p_size = %x\n",
!d->d_partitions[bootpart].p_size);
if (!d->d_partitions[bootpart].p_size) {
printf("Invalid partition\n");
return -1;
}
DPRINTF("p_offset %x, RAW %x\n",
swap32(d->d_partitions[bootpart].p_offset),
swap32(d->d_partitions[RAW_PART].p_offset));
dsk_start += swap32(d->d_partitions[bootpart].p_offset);
dsk_start -= swap32(d->d_partitions[RAW_PART].p_offset);
if ((disk_layout == DL_UNKNOWN) && (bootslice == 0))
disk_layout = DL_RAW;
else if (disk_layout == DL_UNKNOWN)
disk_layout = DL_SLICEPART;
} else {
disk_layout = DL_SLICE;
DPRINTF("Invalid %s\n", "label");
}
DPRINTF("bootslice %d, bootpart %d, dsk_start %u\n", bootslice,
bootpart, dsk_start);
dsk_meta++;
}
return drvread(buf, dsk_start + lba, nblk);
}
static int
drvread(void *buf, unsigned lba, unsigned nblk)
{
static unsigned c = 0x2d5c7c2f;
printf("%c\b", c = c << 8 | c >> 24);
return (avila_read((char *)buf, lba, nblk));
}
#ifdef FIXUP_BOOT_DRV
/*
* fixup_boot_drv() will try to find the ROOTDEVNAME spec in the kernel
* and change it to what was specified on the comandline or /boot.conf
* file or to what was encountered on the disk. It will try to handle 3
* different disk layouts, raw (dangerously dedicated), slice only and
* slice + partition. It will look for the following strings in the
* kernel, but if it is one of the first three, the string in the kernel
* must use the correct form to match the actual disk layout:
* - ufs:ad0a
* - ufs:ad0s1
* - ufs:ad0s1a
* - ufs:ROOTDEVNAME
* In the case of the first three strings, only the "a" at the end and
* the "1" after the "s" will be modified, if they exist. The string
* length will not be changed. In the case of the last string, the
* whole string will be built up and nul, '\0' terminated.
*/
static void
fixup_boot_drv(caddr_t addr, int klen, int bs, int bp)
{
const u_int8_t op[] = "ufs:ROOTDEVNAME";
const u_int8_t op2[] = "ufs:ad0";
u_int8_t *p, *ps;
DPRINTF("fixup_boot_drv: 0x%x, %d, slice %d, partition %d\n",
(int)addr, klen, bs, bp);
if (bs > 4)
return;
if (bp > 7)
return;
ps = memmem(addr, klen, op, sizeof(op));
if (ps != NULL) {
p = ps + 4; /* past ufs: */
DPRINTF("Found it at 0x%x\n", (int)ps);
p[0] = 'a'; p[1] = 'd'; p[2] = '0'; /* ad0 */
p += 3;
if (bs > 0) {
/* append slice */
*p++ = 's';
*p++ = bs + '0';
}
if (disk_layout != DL_SLICE) {
/* append partition */
*p++ = bp + 'a';
}
*p = '\0';
} else {
ps = memmem(addr, klen, op2, sizeof(op2) - 1);
if (ps != NULL) {
p = ps + sizeof(op2) - 1;
DPRINTF("Found it at 0x%x\n", (int)ps);
if (*p == 's') {
/* fix slice */
p++;
*p++ = bs + '0';
}
if (*p == 'a')
*p = bp + 'a';
}
}
if (ps == NULL) {
printf("Could not locate \"%s\" to fix kernel boot device, "
"check ROOTDEVNAME is set\n", op);
return;
}
DPRINTF("Changed boot device to %s\n", ps);
}
#endif

View file

@ -0,0 +1,62 @@
/*-
* Copyright (c) 2008 John Hay. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef ARM_BOOT_CF_ATA_H
#define ARM_BOOT_CF_ATA_H
#define CF_DATA 0x00
#define CF_ERROR 0x01
#define CF_FEATURE 0x01
#define CF_SECT_CNT 0x02
#define CF_SECT_NUM 0x03
#define CF_CYL_L 0x04
#define CF_CYL_H 0x05
#define CF_DRV_HEAD 0x06
#define CF_D_MASTER 0x00
#define CF_D_LBA 0x40
#define CF_D_IBM 0xa0
#define CF_STATUS 0x07
#define CF_S_ERROR 0x01
#define CF_S_INDEX 0x02
#define CF_S_CORR 0x04
#define CF_S_DRQ 0x08
#define CF_S_DSC 0x10
#define CF_S_DWF 0x20
#define CF_S_READY 0x40
#define CF_S_BUSY 0x80
#define CF_COMMAND 0x07
/* This is according to the appnote, but Sam use 0x1e in avila_ata.c */
#define CF_ALT_STATUS 0x16
#define CF_ALT_DEV_CTR 0x16
#define CF_ALT_DEV_CTR2 0x1e
#define CF_A_IDS 0x02
#define CF_A_RESET 0x04
#define CF_A_4BIT 0x08
#define AVILA_IDE_GPIN 12
#endif /* !ARM_BOOT_CF_ATA_H */

View file

@ -0,0 +1,697 @@
/*-
* Copyright (c) 2008 John Hay. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/ata.h>
#include <stdarg.h>
#include "lib.h"
#include "cf_ata.h"
#include <arm/xscale/ixp425/ixp425reg.h>
#include <dev/ic/ns16550.h>
static u_int8_t *ubase;
#define BOARD_AVILA 0
#define BOARD_PRONGHORN 1
static int board;
static u_int8_t uart_getreg(u_int8_t *, int);
static void uart_setreg(u_int8_t *, int, u_int8_t);
static void cf_init(void);
static void cf_clr(void);
#ifdef DEBUG
#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DPRINTF(fmt, ...)
#endif
const char *
board_init(void)
{
volatile u_int32_t *cs;
const char *bt = NULL;
/*
* Redboot only configure the chip selects that are needed, so
* use that to figure out if it is an Avila or ADI board. The
* Avila boards use CS2 and ADI does not.
*/
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET);
if (*cs != 0) {
board = BOARD_AVILA;
bt = "Avila";
} else {
board = BOARD_PRONGHORN;
bt = "Pronghorn Metro";
}
/* Config the serial port. RedBoot should do the rest. */
if (board == BOARD_AVILA)
ubase = (u_int8_t *)(IXP425_UART0_HWBASE);
else
ubase = (u_int8_t *)(IXP425_UART1_HWBASE);
cf_init();
return bt;
}
/*
* This should be called just before starting the kernel. This is so
* that one can undo incompatable hardware settings.
*/
void
clr_board(void)
{
cf_clr();
}
/*
* General support functions.
*/
/*
* DELAY should delay for the number of microseconds.
* The idea is that the inner loop should take 1us, so val is the
* number of usecs to delay.
*/
void
DELAY(int val)
{
volatile int sub;
volatile int subsub;
sub = val;
while(sub) {
subsub = 3;
while(subsub)
subsub--;
sub--;
}
}
u_int32_t
swap32(u_int32_t a)
{
return (((a & 0xff) << 24) | ((a & 0xff00) << 8) |
((a & 0xff0000) >> 8) | ((a & 0xff000000) >> 24));
}
u_int16_t
swap16(u_int16_t val)
{
return (val << 8) | (val >> 8);
}
/*
* uart related funcs
*/
static u_int8_t
uart_getreg(u_int8_t *bas, int off)
{
return *((volatile u_int32_t *)(bas + (off << 2))) & 0xff;
}
static void
uart_setreg(u_int8_t *bas, int off, u_int8_t val)
{
*((volatile u_int32_t *)(bas + (off << 2))) = (u_int32_t)val;
}
int
getc(int seconds)
{
int c, delay, limit;
c = 0;
delay = 10000;
limit = seconds * 1000000/10000;
while ((uart_getreg(ubase, REG_LSR) & LSR_RXRDY) == 0 && --limit)
DELAY(delay);
if ((uart_getreg(ubase, REG_LSR) & LSR_RXRDY) == LSR_RXRDY)
c = uart_getreg(ubase, REG_DATA);
return c;
}
void
putchar(int ch)
{
int delay, limit;
delay = 500;
limit = 20;
while ((uart_getreg(ubase, REG_LSR) & LSR_THRE) == 0 && --limit)
DELAY(delay);
uart_setreg(ubase, REG_DATA, ch);
limit = 40;
while ((uart_getreg(ubase, REG_LSR) & LSR_TEMT) == 0 && --limit)
DELAY(delay);
}
void
xputchar(int ch)
{
if (ch == '\n')
putchar('\r');
putchar(ch);
}
void
putstr(const char *str)
{
while(*str)
xputchar(*str++);
}
void
puthex8(u_int8_t ch)
{
const char *hex = "0123456789abcdef";
putchar(hex[ch >> 4]);
putchar(hex[ch & 0xf]);
}
void
puthexlist(const u_int8_t *str, int length)
{
while(length) {
puthex8(*str);
putchar(' ');
str++;
length--;
}
}
/*
*
* CF/IDE functions.
*
*/
struct {
u_int64_t dsize;
u_int64_t total_secs;
u_int8_t heads;
u_int8_t sectors;
u_int32_t cylinders;
u_int8_t *cs1;
u_int8_t *cs2;
u_int32_t use_lba;
u_int32_t use_stream8;
u_int32_t debug;
u_int8_t status;
u_int8_t error;
} dskinf;
static void cfenable16(void);
static void cfdisable16(void);
static u_int8_t cfread8(u_int32_t off);
static u_int16_t cfread16(u_int32_t off);
static void cfreadstream8(void *buf, int length);
static void cfreadstream16(void *buf, int length);
static void cfwrite8(u_int32_t off, u_int8_t val);
static u_int8_t cfaltread8(u_int32_t off);
static void cfaltwrite8(u_int32_t off, u_int8_t val);
static int cfwait(u_int8_t mask);
static int cfaltwait(u_int8_t mask);
static int cfcmd(u_int32_t cmd, u_int32_t cylinder, u_int32_t head,
u_int32_t sector, u_int32_t count, u_int32_t feature);
static void cfreset(void);
#ifdef DEBUG
static int cfgetparams(void);
#endif
static void cfprintregs(void);
static void
cf_init(void)
{
u_int8_t status;
#ifdef DEBUG
int rval;
#endif
volatile u_int32_t *cs;
/* Setup the CF select timeing. Maybe already done by RedBoot? */
if (board == BOARD_AVILA) {
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS1_OFFSET);
*cs |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
DPRINTF("t1 %x, ", *cs);
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET);
*cs |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
DPRINTF("t2 %x\n", *cs);
dskinf.cs1 = (u_int8_t *)IXP425_EXP_BUS_CS1_HWBASE;
dskinf.cs2 = (u_int8_t *)IXP425_EXP_BUS_CS2_HWBASE;
} else {
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET);
*cs |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
DPRINTF("t1 %x, ", *cs);
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS4_OFFSET);
*cs |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
DPRINTF("t2 %x\n", *cs);
dskinf.cs1 = (u_int8_t *)IXP425_EXP_BUS_CS3_HWBASE;
dskinf.cs2 = (u_int8_t *)IXP425_EXP_BUS_CS4_HWBASE;
}
DPRINTF("cs1 %x, cs2 %x\n", dskinf.cs1, dskinf.cs2);
dskinf.use_stream8 = 0;
dskinf.use_lba = 0;
dskinf.debug = 1;
/* Detect if there is a disk. */
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
DELAY(1000);
status = cfread8(CF_STATUS);
if (status != 0x50)
printf("cf-ata0 %x\n", (u_int32_t)status);
if (status == 0xff) {
printf("cf_ata0: No disk!\n");
return;
}
cfreset();
if (dskinf.use_stream8) {
DPRINTF("setting %d bit mode.\n", 8);
cfwrite8(CF_FEATURE, 0x01); /* Enable 8 bit transfers */
cfwrite8(CF_COMMAND, ATA_SETFEATURES);
cfaltwait(CF_S_READY);
}
#ifdef DEBUG
rval = cfgetparams();
if (rval)
return;
#endif
dskinf.use_lba = 1;
dskinf.debug = 0;
}
static void
cf_clr(void)
{
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
cfaltwait(CF_S_READY);
cfwrite8(CF_FEATURE, 0x81); /* Enable 8 bit transfers */
cfwrite8(CF_COMMAND, ATA_SETFEATURES);
cfaltwait(CF_S_READY);
}
static void
cfenable16(void)
{
u_int32_t val;
volatile u_int32_t *cs;
if (board == BOARD_AVILA)
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS1_OFFSET);
else
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET);
val = *cs;
*cs = val & (~1);
DPRINTF("cfenable16: cs1 timing reg %x\n", *cs);
}
static void
cfdisable16(void)
{
u_int32_t val;
volatile u_int32_t *cs;
if (board == BOARD_AVILA)
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS1_OFFSET);
else
cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET);
val = *cs;
*cs = val | 1;
}
static u_int8_t
cfread8(u_int32_t off)
{
volatile u_int8_t *vp;
vp = (volatile u_int8_t *)(dskinf.cs1 + off);
return *vp;
}
static void
cfreadstream8(void *buf, int length)
{
u_int8_t *lbuf;
u_int8_t tmp;
lbuf = buf;
while (length) {
tmp = cfread8(CF_DATA);
*lbuf = tmp;
#ifdef DEBUG
if (dskinf.debug && (length > (512 - 32))) {
if ((length % 16) == 0)
xputchar('\n');
puthex8(tmp);
putchar(' ');
}
#endif
lbuf++;
length--;
}
#ifdef DEBUG
if (dskinf.debug)
xputchar('\n');
#endif
}
static u_int16_t
cfread16(u_int32_t off)
{
volatile u_int16_t *vp;
vp = (volatile u_int16_t *)(dskinf.cs1 + off);
return swap16(*vp);
}
static void
cfreadstream16(void *buf, int length)
{
u_int16_t *lbuf;
length = length / 2;
cfenable16();
lbuf = buf;
while (length--) {
*lbuf = cfread16(CF_DATA);
lbuf++;
}
cfdisable16();
}
static void
cfwrite8(u_int32_t off, u_int8_t val)
{
volatile u_int8_t *vp;
vp = (volatile u_int8_t *)(dskinf.cs1 + off);
*vp = val;
}
#if 0
static void
cfwrite16(u_int32_t off, u_int16_t val)
{
volatile u_int16_t *vp;
vp = (volatile u_int16_t *)(dskinf.cs1 + off);
*vp = val;
}
#endif
static u_int8_t
cfaltread8(u_int32_t off)
{
volatile u_int8_t *vp;
off &= 0x0f;
vp = (volatile u_int8_t *)(dskinf.cs2 + off);
return *vp;
}
static void
cfaltwrite8(u_int32_t off, u_int8_t val)
{
volatile u_int8_t *vp;
/*
* This is documented in the Intel appnote 302456.
*/
off &= 0x0f;
vp = (volatile u_int8_t *)(dskinf.cs2 + off);
*vp = val;
}
static int
cfwait(u_int8_t mask)
{
u_int8_t status;
u_int32_t tout;
tout = 0;
while (tout <= 5000000) {
status = cfread8(CF_STATUS);
if (status == 0xff) {
printf("cfwait: master: no status, reselecting\n");
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
DELAY(1);
status = cfread8(CF_STATUS);
}
if (status == 0xff)
return -1;
dskinf.status = status;
if (!(status & CF_S_BUSY)) {
if (status & CF_S_ERROR)
dskinf.error = cfread8(CF_ERROR);
if ((status & mask) == mask) {
DPRINTF("cfwait: tout %u\n", tout);
return (status & CF_S_ERROR);
}
}
if (tout > 1000) {
tout += 1000;
DELAY(1000);
} else {
tout += 10;
DELAY(10);
}
}
return -1;
}
static int
cfaltwait(u_int8_t mask)
{
u_int8_t status;
u_int32_t tout;
tout = 0;
while (tout <= 5000000) {
status = cfaltread8(CF_ALT_STATUS);
if (status == 0xff) {
printf("cfaltwait: master: no status, reselectin\n");
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
DELAY(1);
status = cfread8(CF_STATUS);
}
if (status == 0xff)
return -1;
dskinf.status = status;
if (!(status & CF_S_BUSY)) {
if (status & CF_S_ERROR)
dskinf.error = cfread8(CF_ERROR);
if ((status & mask) == mask) {
DPRINTF("cfaltwait: tout %u\n", tout);
return (status & CF_S_ERROR);
}
}
if (tout > 1000) {
tout += 1000;
DELAY(1000);
} else {
tout += 10;
DELAY(10);
}
}
return -1;
}
static int
cfcmd(u_int32_t cmd, u_int32_t cylinder, u_int32_t head, u_int32_t sector,
u_int32_t count, u_int32_t feature)
{
if (cfwait(0) < 0) {
printf("cfcmd: timeout\n");
return -1;
}
cfwrite8(CF_FEATURE, feature);
cfwrite8(CF_CYL_L, cylinder);
cfwrite8(CF_CYL_H, cylinder >> 8);
if (dskinf.use_lba)
cfwrite8(CF_DRV_HEAD, CF_D_IBM | CF_D_LBA | head);
else
cfwrite8(CF_DRV_HEAD, CF_D_IBM | head);
cfwrite8(CF_SECT_NUM, sector);
cfwrite8(CF_SECT_CNT, count);
cfwrite8(CF_COMMAND, cmd);
return 0;
}
static void
cfreset(void)
{
u_int8_t status;
u_int32_t tout;
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
DELAY(1);
#ifdef DEBUG
cfprintregs();
#endif
cfread8(CF_STATUS);
cfaltwrite8(CF_ALT_DEV_CTR, CF_A_IDS | CF_A_RESET);
DELAY(10000);
cfaltwrite8(CF_ALT_DEV_CTR, CF_A_IDS);
DELAY(10000);
cfread8(CF_ERROR);
DELAY(3000);
for (tout = 0; tout < 310000; tout++) {
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
DELAY(1);
status = cfread8(CF_STATUS);
if (!(status & CF_S_BUSY))
break;
DELAY(100);
}
DELAY(1);
if (status & CF_S_BUSY) {
cfprintregs();
printf("cfreset: Status stayed busy after reset.\n");
}
DPRINTF("cfreset: finished, tout %u\n", tout);
}
#ifdef DEBUG
static int
cfgetparams(void)
{
u_int8_t *buf;
buf = (u_int8_t *)(0x170000);
p_memset((char *)buf, 0, 1024);
/* Select the drive. */
cfwrite8(CF_DRV_HEAD, CF_D_IBM);
DELAY(1);
cfcmd(ATA_ATA_IDENTIFY, 0, 0, 0, 0, 0);
if (cfaltwait(CF_S_READY | CF_S_DSC | CF_S_DRQ)) {
printf("cfgetparams: ATA_IDENTIFY failed.\n");
return -1;
}
if (dskinf.use_stream8)
cfreadstream8(buf, 512);
else
cfreadstream16(buf, 512);
if (dskinf.debug)
cfprintregs();
#if 0
memcpy(&dskinf.ata_params, buf, sizeof(struct ata_params));
dskinf.cylinders = dskinf.ata_params.cylinders;
dskinf.heads = dskinf.ata_params.heads;
dskinf.sectors = dskinf.ata_params.sectors;
printf("dsk0: sec %x, hd %x, cyl %x, stat %x, err %x\n",
(u_int32_t)dskinf.ata_params.sectors,
(u_int32_t)dskinf.ata_params.heads,
(u_int32_t)dskinf.ata_params.cylinders,
(u_int32_t)dskinf.status,
(u_int32_t)dskinf.error);
#endif
dskinf.status = cfread8(CF_STATUS);
if (dskinf.debug)
printf("cfgetparams: ata_params * %x, stat %x\n",
(u_int32_t)buf, (u_int32_t)dskinf.status);
return 0;
}
#endif /* DEBUG */
static void
cfprintregs(void)
{
u_int8_t rv;
putstr("cfprintregs: regs error ");
rv = cfread8(CF_ERROR);
puthex8(rv);
putstr(", count ");
rv = cfread8(CF_SECT_CNT);
puthex8(rv);
putstr(", sect ");
rv = cfread8(CF_SECT_NUM);
puthex8(rv);
putstr(", cyl low ");
rv = cfread8(CF_CYL_L);
puthex8(rv);
putstr(", cyl high ");
rv = cfread8(CF_CYL_H);
puthex8(rv);
putstr(", drv head ");
rv = cfread8(CF_DRV_HEAD);
puthex8(rv);
putstr(", status ");
rv = cfread8(CF_STATUS);
puthex8(rv);
putstr("\n");
}
int
avila_read(char *dest, unsigned source, unsigned length)
{
if (dskinf.use_lba == 0 && source == 0)
source++;
if (dskinf.debug)
printf("avila_read: 0x%x, sect %d num secs %d\n",
(u_int32_t)dest, source, length);
while (length) {
cfwait(CF_S_READY);
/* cmd, cyl, head, sect, count, feature */
cfcmd(ATA_READ, (source >> 8) & 0xffff, source >> 24,
source & 0xff, 1, 0);
cfwait(CF_S_READY | CF_S_DRQ | CF_S_DSC);
if (dskinf.use_stream8)
cfreadstream8(dest, 512);
else
cfreadstream16(dest, 512);
length--;
source++;
dest += 512;
}
return 0;
}

View file

@ -0,0 +1,64 @@
/*-
* Copyright (c) 2008 John Hay. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef ARM_BOOT_LIB_H
#define ARM_BOOT_LIB_H
#include <sys/cdefs.h>
#include <sys/param.h>
void DELAY(int);
int getc(int);
void putchar(int);
void xputchar(int);
void putstr(const char *);
void puthex8(u_int8_t);
void puthexlist(const u_int8_t *, int);
void printf(const char *fmt,...);
void bzero(void *, size_t);
char *strcpy(char *to, const char *from);
int strcmp(const char *to, const char *from);
int p_strlen(const char *);
int p_memcmp(const char *, const char *, unsigned);
void *memchr(const void *, int, size_t);
void memcpy(void *to, const void *from, unsigned size);
void *memmem(const void *, size_t, const void *, size_t);
void p_memset(char *buffer, char value, int size);
#define strlen p_strlen
#define memcmp p_memcmp
#define memset p_memset
u_int16_t swap16(u_int16_t);
u_int32_t swap32(u_int32_t);
const char *board_init(void);
void clr_board(void);
int avila_read(char*, unsigned, unsigned);
#endif /* !ARM_BOOT_LIB_H */