Subject: Boot ROMs
To: None <port-bebox@netbsd.org>
From: Scott Lindsey <wombat@gobe.com>
List: port-bebox
Date: 02/03/2000 01:39:43
--============_-1262551711==_============
Content-Type: text/plain; charset="us-ascii"

At the behest of someone on the be-linux mailing list, I got in touch with
Dominic Giampaolo at Be regarding the boot ROM on the BeBox.  He had this
to say:

>Actually this stuff is kind of lost in the mists of time (i.e. Bob
>Herold is the one who really understood this stuff and he's long
>gone).
>
>Here's what I remember... there are two parts to the boot-rom.  One
>part, the "nub", is not writable and about all it knows how to do is
>to read data off the floppy and write it to the writable part of the
>flash.  The writable part is basically a copy of the bootmain.image
>that is loaded into memory by the nub and executed.
>
>A lot of this stuff is in our build process but I don't really have
>time to extricate it all.  It's a pretty complicated build process
>and I really don't remember all the details.  The only other tidbit
>I can find quickly is that the nub tries to load a pef image out of
>the boot image and put it at 0x00300000, then it will jump to the
>entry point for that pef image (which you can set with the
>metrowerks linker).  That code is running on a very raw machine
>(no mmu, in supervisor mode, etc).  The problem will of course
>be generating a pef image but you can probably do a two-stage sort of
>thing where you have a simple pef image that then loads an elf linux
>kernel or something.... the memory map of the bebox is all public so
>it shouldn't be too hard to figure out how to read the flash device.
>Actually, what the heck.  I've attached the flash driver so you can
>use it if you need.


--============_-1262551711==_============
Content-Type: text/plain; name="fl_amd.h"; charset="us-ascii"
Content-Disposition: attachment; filename="fl_amd.h"

/* ++++++++++
	FILE:	fl_amd.h
	REVS:	$Revision: 1.2 $

	NAME:	herold
	DATE:	Feb 3, 1992
	Copyright (c) 1992-96 by Be
Incorporated.  All Rights Reserved.

	Definitions for programming the
Advanced Micor Devices 29F010 
	and 29F040 flash roms.

	For more
info, see the AMD 'Flash Memory Products' databook.
+++++ */

#ifndef
_FL_AMD_H
#define _FL_AMD_H

/* identification */

#define
FL_MANUFACTURER_amd				0x01
#define
FL_DEVICE_amd29f010				0x20
#define
FL_DEVICE_amd29f040				0xa4

/* sizes */

#define
FL_BLOCK_SIZE_amd29f010			0x4000
#define
FL_BLOCK_SIZE_amd29f040			0x10000

/* all commands start with
2 write operations to particular addresses */

#define
FLCMD_PREFIX1_ADDR_amd	0x5555
#define FLCMD_PREFIX1_DATA_amd
	0xAA
#define FLCMD_PREFIX2_ADDR_amd	0x2AAA
#define
FLCMD_PREFIX2_DATA_amd	0x55

/* command codes for operating on a single
part */

#define FLCMD_READ_amd			0xF0
#define
FLCMD_AUTOSELECT_amd	0x90
#define FLCMD_ERASESETUP_amd
	0x80
#define FLCMD_ERASECONFIRM_amd	0x30
#define
FLCMD_PROGRAMSETUP_amd	0xA0

/* status bits (single part) */

#define
FLST_DATA_amd			0x80		/* data polling */
#define
FLST_TOGGLE_amd			0x40		/* toggles while busy
*/
#define FLST_TIMEOUT_amd		0x20		/* pgm/erase
timeout */
#define FLST_SEQ_amd			0x10		/* if
timeout, 1=erase 0=pgm */
#define FLST_SECTOR_amd
	0x08		/* 0=not yet, 1=erasing */

#endif


--============_-1262551711==_============
Content-Type: text/plain; name="flash.c"; charset="us-ascii"
Content-Disposition: attachment; filename="flash.c"

/* ++++++++++
	FILE:  joe/flash.c
	REVS:  $Revision: 1.26 $

	NAME:  herold
	DATE:  Tue Apr 18 18:02:17 PDT 1995

	Copyright
(c) 1995-97 by Be Incorporated.  All Rights Reserved.
+++++ */

#include
<OS.h>
#include <KernelExport.h>
#include <PCI.h>
#include
<Drivers.h>
#include <drivers_priv.h>
#include <flash_driver.h>
#include
<cpu.h>
#include <fl_amd.h>
#include <vm.h>
#include <eagle.h>
#undef
ANALYZE
#include <analyser.h>

#define ddprintf 


/* -----
	private
definitions
----- */

typedef struct {
	ulong	block_size;
	ulong
	num_blocks;
	uchar	manufacturer_code;
	uchar
	device_code;
} flash_type;

typedef struct {
	area_id	aid;
	/* id of flash area */
	int		config;		/* eagle
config reg contents */
} enable_state;

#define FL_ADDR(offset) ((vuchar
*)(flash + (offset))))


/* ----------
	private globals
----- */

static
sem_id		mapping_sem = -1;	/* mutex for mapping */
static
vchar		*flash;				/* -> mapped flash
*/
static area_id		flash_area = -1;	/* area that maps
the flash */
static pci_info		eagle;				/*
pci info for MPC105 */
static flash_type types[2] = {
	{
FL_BLOCK_SIZE_amd29f010, 8, FL_MANUFACTURER_amd, FL_DEVICE_amd29f010 },

	{ FL_BLOCK_SIZE_amd29f040, 8, FL_MANUFACTURER_amd,
FL_DEVICE_amd29f040 }
};


/* ----------
	inp - read a byte from
flash
----- */
static vuchar
inp (int offset)
{
	uchar	val;

	val
= * (vuchar *) (flash + offset);
	__eieio();
	
	return
val;
}



/* ----------
	outp - write a byte to flash
-----
*/
static void
outp (int offset, uchar val)
{
	* (vuchar *) (flash +
offset) = val;
	__eieio();
}



/* ----------
	find_eagle - looks for the
Eagle chip
----- */
static bool
find_eagle (void)
{
	int	i;

	for
(i = 0; ; i++) {
		if (get_nth_pci_info (i, &eagle) !=
B_NO_ERROR)
			break;

		if
(eagle.class_base == PCI_bridge &&
		  eagle.class_sub ==
PCI_host &&
		  eagle.vendor_id == 0x1057 &&	/* motorola */

	  eagle.device_id == 0x0001)	/* MPC105 */

	return TRUE;
	}
	return FALSE;
}


/* ----------

	init_hardware - check for Eagle chip, fail if not there
-----
*/
status_t
init_hardware (void)
{
	return find_eagle() ? B_NO_ERROR :
B_ERROR;
}


/* ----------
	init_driver - check for Eagle chip, fail if
not there
----- */
status_t
init_driver (void)
{
	if (!find_eagle())

		return B_ERROR;
	
	mapping_sem = create_sem
(1, "flash driver open");
	return (mapping_sem < 0) ? B_ERROR :
B_NO_ERROR;
}


/* ----------
	uninit_driver - clean up resources used by
driver
----- */
void
uninit_driver (void)
{
	#if !BOOT
	if
(flash_area >= 0)
		delete_area (flash_area);

	mapping_sem
= -1;
	#endif
}
	

/* ----------
	map_flash - make flash rom
accessible by mapping it into virtual space
----- */
static bool
map_flash
(bool writing)
{
	#if BOOT
	flash = (vchar *) HOST_FLASH;

	return TRUE;

	#else
	acquire_sem (mapping_sem);

	if
(flash_area < 0)
		flash_area = map_physical_memory (

		"flash",
			(void *) HOST_FLASH,

		512*1024,

	B_ANY_KERNEL_BLOCK_ADDRESS,
			writing ?
B_WRITE_AREA + B_READ_AREA : B_READ_AREA,

	(void**)&flash
		);

	release_sem (mapping_sem);


	return (flash_area < 0) ? FALSE : TRUE;
	#endif
}


/*
----------
	enable_flash_writes
----- */
static int
enable_flash_writes
(enable_state *s)
{
	/* make the flash writeable */

	#ifdef
FLASH_UPDATER
	disable_dcache();
	#endif

	/* set up Eagle to
enable flash updates */

	s->config = read_pci_config (eagle.bus,
eagle.device, eagle.function, EGL_config_1, 4);
	write_pci_config
(eagle.bus, eagle.device, eagle.function, EGL_config_1, 4, s->config |
0x1000);

	return B_NO_ERROR;
	
}


/* ----------

	disable_flash_writes
----- */
static void
disable_flash_writes
(enable_state *s)
{
	/* restore the flash to read only */


	write_pci_config (eagle.bus, eagle.device, eagle.function,
EGL_config_1, 4, s->config);
	#ifdef FLASH_UPDATER

	inval_lock_enable_dcache();
	#endif
}


/* ----------

	write_cmd writes the passed command, along with the common

	command prefix.
----- */
static void
write_cmd (char cmd)
{

	outp (FLCMD_PREFIX1_ADDR_amd, FLCMD_PREFIX1_DATA_amd);
	outp
(FLCMD_PREFIX2_ADDR_amd, FLCMD_PREFIX2_DATA_amd);
	outp
(FLCMD_PREFIX1_ADDR_amd, cmd);
}


/* ----------
	reset_amd returns
the roms to read mode.
----- */

static void
reset_amd (void)
{

	write_cmd (FLCMD_READ_amd);
}


/* ----------
	is_sector_protected
returns true if sector is write-protected.
----- */
static
status_t
is_sector_protected (flash_type *f, long sector)
{

	enable_state	s;
	int				i;

	uchar 			prot;
	int
	err;

	if (sector < 0 || sector > 8)
		return B_ERROR;


	err = enable_flash_writes (&s);
	if (err != B_NO_ERROR)

	return B_ERROR;

	write_cmd (FLCMD_AUTOSELECT_amd);


	prot = inp ((sector * f->block_size) + 2);	/* addr 2:
protection */

	reset_amd();
	disable_flash_writes (&s);

	return prot
& 0x01;
}


/* ----------
	get_flash_type figures out what kind of
flash is installed.
----- */
static flash_type *
get_flash_type (void)
{

	enable_state	s;
	int				i;

	uchar 			mfg, dev;
	int
	err;

	/* ---
		rwh 4/1/97
		There is some sort
of cache coherency problem here.  If the 
		dprintf lines w/
"fix#1" are removed, this routines fails on a
		133 603e.  This
should be investigated ASAP.  In the meantime,
		the lines stay.

	--- */

	err = enable_flash_writes (&s);
	dprintf
("get_flash_type: fix #1\n");
	if (err != B_NO_ERROR)
		return
NULL;

	write_cmd (FLCMD_AUTOSELECT_amd);

	mfg = inp (0);	/* addr 0:
manufacturer code */
	dev = inp (1);	/* addr 1: device code */


	reset_amd();
	dprintf ("get_flash_type: fix #1\n");

	disable_flash_writes (&s);

	/* search supported devices to get
device type */

	for (i = 0; i < sizeof (types) / sizeof (types[0]);
i++)
		if ((types[i].manufacturer_code) == mfg &&
(types[i].device_code == dev))
			return types + i;


	return NULL;
}


/* -----
   	poll_status polls the device until
the operation is complete.

	The 'data polling' and 'operation timeout'
bits are checked
	until the data bits equals the passed value
(operation complete)
	or the operation times out.
----- */

static
int
poll_status (ulong addr, uchar value)
{
	uchar	status;

	for
(;;) {
		status = inp (addr);
		if ((status &
FLST_DATA_amd) == (value & FLST_DATA_amd))
			break;

	if (status & FLST_TIMEOUT_amd)
			return B_ERROR;

	}
	return B_NO_ERROR;
}


/* ----------
	erase_amd erases
the flash blocks intersecting with the passed
	offset/size.
	
	It
is assumed that the offset is flash-block aligned, and that the

	size is a multiple of the flash block size
----- */

static
int
erase_amd (ulong addr, ulong size, ulong block_size)
{
	int
		err;
	cpu_status	ps;
	ulong		save_addr;

	ulong		save_size;
	int			retry;

	const int	max_retry = 50;
	
	ddprintf
("erase_amd: addr=%.8x size=%.8x block_size=%.8x\n", addr, size,
block_size);

	save_addr = addr;
	save_size = size;

	for (retry
= 0; retry < max_retry; retry++) {

		addr = save_addr;

	size = save_size;

		ps = disable_interrupts ();


	write_cmd (FLCMD_ERASESETUP_amd);
		outp
(FLCMD_PREFIX1_ADDR_amd, FLCMD_PREFIX1_DATA_amd);
		outp
(FLCMD_PREFIX2_ADDR_amd, FLCMD_PREFIX2_DATA_amd);

		/* write
erase cmd for each sector.  These must all happen within

100 uSec of each other, so we disable interrupts while doing it */


	while (size > 0) {
			outp (addr,
FLCMD_ERASECONFIRM_amd);
			addr += block_size;

		size -= block_size;
		}


	restore_interrupts (ps);

		/* wait till erase complete
*/

		if ((err = poll_status (save_addr, 0x80)) == B_NO_ERROR)

			break;
	}

	if (err != B_NO_ERROR)

	dprintf("erase_amd: erase failed\n");

	reset_amd ();
	/* back to read mode */
	return err;
}


/* ----------

	write_amd writes stuff to the flash rom.
----- */

static
int
write_amd (uchar *data, ulong addr, ulong size)
{
	const int
	max_retry = 50;
	int			retry;
	int
		err;

	ddprintf ("write_amd: data=%.8x addr=%.8x
size=%.8x\n", data, addr, size);

	while (size) {

		for
(retry = 0; retry < max_retry; retry++) {
			write_cmd
(FLCMD_PROGRAMSETUP_amd);			/* setup... */

	outp (addr, *data);
	/* program! */
			err = poll_status (addr, *data);
			/* check */
			if (err ==
B_NO_ERROR)
				break;
		}

		if
(err != B_NO_ERROR) {
			dprintf ("fl_amd, write_amd: error
writing flash at offset %.8x\n", addr);
			break;

	}
		data++;
		addr++;

	size--;
	}
	reset_amd ();			/* back to read mode */
	return err;
}


/*
----------
	flash_open
----- */

static status_t
flash_open (const char
*name, uint32 flags, void **cookie)
{
	bool writing;

	writing = (flags &
(O_RDWR | O_WRONLY)) ? TRUE : FALSE;

#if 0
	/* O_EXCLOPEN is a no-op.
*/
	/* if writing, better be exclusive */
	if (writing && !(flags &
O_EXCLOPEN))
		return B_PERMISSION_DENIED;
#endif

	if
(!find_eagle())
		return B_ERROR;

	if
(!map_flash(writing))
		return B_ERROR;

	if ((*cookie =
get_flash_type()) == NULL) {
		dprintf("flash_open: could not
determine device type!\n");
		return B_ERROR;
	}


	return B_NO_ERROR;
}


/* ----------
	flash_close
-----
*/

static status_t
flash_close (void *cookie)
{
	return
B_NO_ERROR;
}


/* ----------
	flash_free
----- */

static
status_t
flash_free (void *cookie)
{
	#if !BOOT
	if (flash_area)

		delete_area (flash_area);
	#endif

	return
B_NO_ERROR;
}

/* ----------
	flash_control
----- */

static
status_t
flash_control(void *_f, uint32 msg, void *buf, size_t len)
{

	flash_type *f = (flash_type *) _f;

	switch (msg) {
	case
B_GET_DEVICE_SIZE:
		*(size_t *)buf = f->block_size *
f->num_blocks;
		return B_NO_ERROR;
	case
B_IS_FLASH_SECTOR_PROTECTED:
		*(long *)buf = is_sector_protected
(f, * (long *) buf);
		return B_NO_ERROR;
	case
B_GET_FLASH_SECTOR_SIZE:
		*(long *)buf = f->block_size;

	return B_NO_ERROR;
	}

	return B_ERROR;
}


/* ----------

	flash_read
----- */

static status_t
flash_read (void *_f, off_t
pos, void *buf, size_t *len)
{
	flash_type *f = (flash_type *) _f;

	size_t	count;

	count = *len;
	if ((count + pos) >
(f->block_size * f->num_blocks)) {
		dprintf ("flash_write:
attempt to write beyond end\n");
		return B_ERROR;
	}


	memcpy (buf, flash + pos, count);
	return 0;
}


/*
----------
	flash_write
----- */

static status_t
flash_write (void
*_f, off_t pos, const void *buf, size_t *len)
{
	flash_type *f =
(flash_type *) _f;

	size_t			count;
	enable_state	s;

	int				err;
	ulong
	blksize;
	uchar			*mbuf = NULL;
	uchar
		*pr_buf;		/* -> data to be programmed this
time */
	ulong			src_count;		/* # source
bytes being programmed */
	ulong			pr_dev_count;	/*
# device bytes being programmed */
	ulong			pr_pos;
		/* device positon to start erase/pgm */

	count =
*len;
	ddprintf ("flash_write: buf=%.8x count=%.8x pos=%.8x\n", buf,
count, pos);

	if ((count + pos) > (f->block_size * f->num_blocks)) {

	dprintf ("flash_write: attempt to write beyond end\n");

	return B_ERROR;
	}

	/* get a buffer for
non-block-aligned writes */
	
	blksize = f->block_size;
	if
((pos % blksize) || (count % blksize)) 
		if (!(mbuf = (uchar
*) malloc (blksize)))
			return B_ERROR;
		

	err = enable_flash_writes (&s);
	if (err != B_NO_ERROR)

	goto exit;

	while (count > 0) {
		/* if not starting
and ending on block boundary, we must merge
		   with what is
already there */

		if ((pos % blksize) || (count < blksize))
{
			pr_pos = (pos/blksize) * blksize;

	memcpy (mbuf, flash + pr_pos, blksize);

	pr_dev_count = blksize;
			src_count = blksize
- (pos % blksize);
			if (count < src_count)

		src_count = count;
			memcpy (mbuf + (pos
% blksize), buf, src_count);
			pr_buf = mbuf;
		}
else {
			pr_pos = pos;
			pr_dev_count =
src_count = (count / blksize) * blksize;
			pr_buf =
(uchar *) buf;
		}

		if ((err = erase_amd(pr_pos,
pr_dev_count, blksize)) == B_NO_ERROR)
			err = write_amd
(pr_buf, pr_pos, pr_dev_count);
		
		if (err !=
B_NO_ERROR)
			break;
		pos += src_count;

	buf = (char *) buf + src_count;
		count -=
src_count;
	}
	
	disable_flash_writes (&s);

exit:
	if
(mbuf)
		free (mbuf);
	return err;
}


/* ----------

	driver-related structures
----- */

static device_hooks
flash_device = {
	flash_open,		/* -> open entry point */

	flash_close,	/* -> close entry point */
	flash_free,
	/* -> free cookie entry point */
	flash_control,	/* ->
control entry point */
	flash_read,		/* -> read entry point */

	flash_write		/* -> write entry point */
};

static const
char	*flash_name[] = { "flash", NULL };

const char
**
publish_devices()
{
	return flash_name;
}

device_hooks
*
find_device(const char *name)
{
	return &flash_device;
}

fixed_driver_info	flash_driver_info = {
	"flash",
	B_CUR_DRIVER
_API_VERSION,
	NULL,
	&publish_devices,
	&find_device,
	NULL,

	NULL
};

--============_-1262551711==_============
Content-Type: text/plain; charset="us-ascii"

--
Scott Lindsey <wombat@gobe.com>

--============_-1262551711==_============--