qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* Re: [Qemu-devel] PowerPC 4xx EMAC emulation
@ 2008-11-20 23:42 Hollis Blanchard
  2008-11-21  1:53 ` [Qemu-devel] PowerPC 4xx enhacement Salvatore Lionetti
  0 siblings, 1 reply; 5+ messages in thread
From: Hollis Blanchard @ 2008-11-20 23:42 UTC (permalink / raw)
  To: salvatorelionetti; +Cc: qemu-devel

Hi Salvatore, how is this going? Have you updated your EMAC patch for
the latest qemu source? If so, I may be able to test it with Linux and
my Bamboo board emulation patches.

-Hollis

On Mon, Nov 3, 2008 at 5:02 AM, Salvatore Lionetti
<salvatorelionetti@yahoo.it> wrote:
> Hi,
>
> thanks for trustworth toward me!
>
> As i said in previous email, i decide to test u-boot emac driver.
> Actually:
> - uboot cross compile and make process generates an image for walnut board
> - qemu load such image but stop executing during board init
>
> Next step i'm doing is to build a new board in u-boot (possibly linking the configuration of qemu machine with u-boot, but with low priority) and verify emac/mal layer.
>
> The link you give is about 'Sycamore' board, also support by u-boot.
> Thanks for this info, i'll try the most 'simple' board to give feedback ASAP.
>
> Have a good day
>
> --- Ven 31/10/08, Hollis Blanchard <hollis@penguinppc.org> ha scritto:
>
>> Da: Hollis Blanchard <hollis@penguinppc.org>
>> Oggetto: Re: [Qemu-devel] TCG on PowerPC arch
>> A: salvatorelionetti@yahoo.it
>> Cc: qemu-devel@nongnu.org
>> Data: Venerdì 31 ottobre 2008, 15:44
>> To summarize, you're saying that the only way you're
>> able to test the
>> EMAC patch is with a custom firmware you can't make
>> public? Obviously
>> that's not ideal, but I think some EMAC emulation is
>> better than none
>> at all, so IMHO that shouldn't necessarily block your
>> patch.
>>
>> The problem I had when trying to test qemu's 405
>> emulation was that
>> Linux needed firmware to load it, and no 405 firmware is
>> distributed
>> with qemu. At the time, Jocelyn claimed we could download a
>> binary
>> uboot image from AMCC, but that didn't actually work.
>> (I think we
>> ended up using
>> http://www.amcc.com/Embedded/Downloads/download.html?item=129&start=y).
>> This was probably a year ago, so details are fuzzy now.
>>
>> However, if you have access to a 440 board, you could use
>> KVM
>> (http://kvm.qumranet.com/kvmwiki/PowerPc) to boot a guest
>> kernel with
>> the EMAC driver and test that way. Qemu's instruction
>> emulation
>> doesn't support 440, but KVM implements all the
>> instruction emulation
>> itself and does run on 440.
>>
>> Anyways, I definitely think it's worthwhile to update
>> your patch to current SVN.
>>
>> -Hollis
>>
>> On Thu, Oct 30, 2008 at 3:53 AM, Salvatore Lionetti
>> <salvatorelionetti@yahoo.it> wrote:
>> > Hi Hollis,
>> >
>> > please excuse me, but i can't release my firmware
>> (based on OSE)
>> > If you agree, i'd like to
>> > - catch svn status to test emac for my 405 based
>> board, custom fw
>> > - compile a linux kernel for ppc405 cpu, with emac
>> driver
>> > (i've trying eCos but no emac driver :(0)
>> >
>> > For first step actually have some pb with proxy
>> script.
>> > The second one is not strictly depend on me, but
>> 'm very glad if anybody do it. Moreover emac driver in
>> our custom firmware only used reversed mii, and is not well
>> written code!  so however i'd like to use another os.
>> > Moreover the emac device is tested only with such
>> driver so i expect some pb during porting, so excuse in
>> advance if some pb could arise.
>> >
>> > Have a good day
>> > Salvatore
>> >
>> > --- Mer 29/10/08, Hollis Blanchard
>> <hollis@penguinppc.org> ha scritto:
>> >
>> >> Da: Hollis Blanchard <hollis@penguinppc.org>
>> >> Oggetto: Re: [Qemu-devel] TCG on PowerPC arch
>> >> A: qemu-devel@nongnu.org
>> >> Cc: salvatorelionetti@yahoo.it
>> >> Data: Mercoledì 29 ottobre 2008, 16:15
>> >> On Tue, Oct 28, 2008 at 3:41 PM, Andreas Färber
>> >> <andreas.faerber@web.de> wrote:
>> >> >
>> >> > How did you actually use and test your eMac
>> emulation?
>> >> I assume you did not
>> >> > use a PReP Linux kernel for that? If you
>> could provide
>> >> some details, that
>> >> > might help with testing the TCG conversion
>> too.
>> >>
>> >> (Just in case there's any confusion, EMAC is
>> the name
>> >> for the ethernet
>> >> controller on IBM's 405- and 440-based SoCs,
>> not to be
>> >> confused with
>> >> Apple's eMac product.)
>> >>
>> >> Salvatore, did you use a 405 Linux kernel with the
>> EMAC
>> >> driver to test
>> >> this patch? If so, how exactly did you boot? What
>> did you
>> >> do for
>> >> firmware?
>> >>
>> >> -Hollis
>> >
>> >
>> >      Unisciti alla community di Io fotografo e video,
>> il nuovo corso di fotografia di Gazzetta dello sport:
>> > http://www.flickr.com/groups/iofotografoevideo
>> >
>
>
>      Unisciti alla community di Io fotografo e video, il nuovo corso di fotografia di Gazzetta dello sport:
> http://www.flickr.com/groups/iofotografoevideo
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] PowerPC 4xx enhacement
  2008-11-20 23:42 [Qemu-devel] PowerPC 4xx EMAC emulation Hollis Blanchard
@ 2008-11-21  1:53 ` Salvatore Lionetti
  2008-11-21 11:40   ` Jean-Christophe PLAGNIOL-VILLARD
                     ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Salvatore Lionetti @ 2008-11-21  1:53 UTC (permalink / raw)
  To: Hollis Blanchard; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 79205 bytes --]

Source modified
===============
- code factoring for ppc405GPr, ppc405EP, ppc405GPe
- reorganization in i2c code, with possibility of board customization callback
- new board added for 'DHT Walnut board', fw in pc-bios/walnut_uboot.bin
- cfi flash now support 'buffer mode', write N {byte, halfword, word} consecutively.
- emac/mal now work with vlan mode

Board Walnut:
=============
http://elinux.org/DHT-Walnut-Flameman

CPU PowerPC 405 GPe running at 266Mhz
LAN On-chip 405GP ethernet, namely emac/mal
ROM 512k of boot flash, AMD 29LV040B
PID eeprom via i2c

Full Tested (eth, flash, pid)
=============================
- u-boot 1.1.6 on walnut board (with few patch, see ppc405_board.c)
- OSE on proprietary NSN 4G radio module
- Can anyone test ref405ep and taihu?

P.S:
I've tryed to have patch for bin file but no luck
Since i can not version file, does diff support binary file?does attachment work?



Index: Makefile.target
===================================================================
--- Makefile.target	(revision 5720)
+++ Makefile.target	(working copy)
@@ -679,7 +679,7 @@
 # NewWorld PowerMac
 OBJS+= unin_pci.o ppc_chrp.o
 # PowerPC 4xx boards
-OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
+OBJS+= pflash_cfi02.o emac.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
 endif
 ifeq ($(TARGET_BASE_ARCH), mips)
 OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
Index: osdep.c
===================================================================
--- osdep.c	(revision 5720)
+++ osdep.c	(working copy)
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 #include <sys/statvfs.h>
 #endif
+#include <assert.h>
 
 #include "qemu-common.h"
 #include "sysemu.h"
@@ -310,3 +311,49 @@
     fcntl(fd, F_SETFL, f | O_NONBLOCK);
 }
 #endif
+
+void *qemu_mmap(void * start, size_t length, int prot, int flags, int fd, off_t  offset) {
+	void *ret;
+#ifndef _WIN32
+	ret = mmap(start, length, prot, flags, fd, offset);
+#else
+	{ /* TO TEST!!! */
+		static char name[15];
+		static char ind=0;
+		HANDLE fd_map;
+		LPVOID fd_buf;
+		char*  buf;
+
+		assert(fd!=INVALID_HANDLE_VALUE);
+
+		snprintf(name, 15, "qemu_mmap%d", ind++);
+		fd_map = CreateFileMapping(fd,
+					   NULL, /* Security attr */
+					   PAGE_READWRITE,
+					   0, TOTAL_SIZE,
+					   name);
+
+		fd_buf = MapViewOfFile(fd_map,
+				       FILE_MAP_ALL_ACCESS,
+				       0, 0, /* Offset */
+				       TOTAL_SIZE);
+		assert(fd_buf);
+		ret = fd_buf;
+
+	}
+#endif
+	return ret;
+}
+int qemu_munmap(void *start, size_t length) {
+	int ret;
+#ifndef _WIN32
+	ret = munmap(start, length);
+#else
+	{ /* TO TEST!!! */
+		UnmapViewOfFile(start);
+		/*CloseHandle(fd_map);
+		CloseHandle(fd);*/
+	}
+#endif
+	return ret;
+}
Index: target-ppc/machine.c
===================================================================
--- target-ppc/machine.c	(revision 5720)
+++ target-ppc/machine.c	(working copy)
@@ -8,6 +8,7 @@
     qemu_register_machine(&prep_machine);
     qemu_register_machine(&ref405ep_machine);
     qemu_register_machine(&taihu_machine);
+    qemu_register_machine(&walnut_machine);
 }
 
 void cpu_save(QEMUFile *f, void *opaque)
Index: qemu-common.h
===================================================================
--- qemu-common.h	(revision 5720)
+++ qemu-common.h	(working copy)
@@ -152,4 +152,11 @@
 /* Force QEMU to stop what it's doing and service IO */
 void qemu_service_io(void);
 
+/* KR function prototype. */
+typedef size_t KRHandler(int fd, char* buf, size_t size);
+
+/* mmap like function
+ * (cygwin seem !have aio yet so no linux env under Winzozz) */
+void *qemu_mmap(void * start, size_t length, int prot, int flags, int fd, off_t  offset);
+int qemu_munmap(void *start, size_t length);
 #endif
Index: hw/ppc405_boards.c
===================================================================
--- hw/ppc405_boards.c	(revision 5720)
+++ hw/ppc405_boards.c	(working copy)
@@ -206,7 +206,7 @@
 #ifdef DEBUG_BOARD_INIT
     printf("%s: register cpu\n", __func__);
 #endif
-    env = ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic, &sram_offset,
+    env = ppc405xp_init("405ep", ram_bases, ram_sizes, 33333333, &pic, &sram_offset,
                         kernel_filename == NULL ? 0 : 1);
     /* allocate SRAM */
 #ifdef DEBUG_BOARD_INIT
@@ -353,7 +353,6 @@
     printf("bdloc %016lx %s\n",
            (unsigned long)bdloc, (char *)(phys_ram_base + bdloc));
 }
-
 QEMUMachine ref405ep_machine = {
     .name = "ref405ep",
     .desc = "ref405ep",
@@ -361,6 +360,349 @@
     .ram_require = (128 * 1024 * 1024 + 4096 + 512 * 1024 + BIOS_SIZE) | RAMSIZE_FIXED,
 };
 
+/* 
+ * Walnut board
+ *
+ * Starting point from official uboot 1.1.6 with following patch:
+ * - compilation error: cmd_bootm.c:470 manually expand #if
+ * - mac address not in nvram: include/configs/walnut.h add "ethaddr=52:54:00:12:34:56\0" in CONFIG_EXTRA_ENV_SETTING
+ * - remove trailer from recv packet(fcs?): cpu/ppc4xx/4xx_enet.c:1478 call NetReceive with 'length' as 2nd parameter.
+ */
+/* Addedum related to 405GP: is it only for such device? */
+static target_ulong walnut_dcr_read (void *opaque, int dcrn)
+{
+	target_ulong ret=0;
+	switch (dcrn) {
+		case 0xAA: /* CPCO_ECR */
+			break;
+		case 0xB0: /* CPC0_PLLMR */
+			/*                                    |--> {ForwardA, Backward} Divisor
+			 *                              ----  |--> Opb
+			 *                        ---> | x2 |-|--> ExtBus
+			 *            ----        |     ----
+			 * CPU  ---> | /4 | ---> PLB ------------> PCI
+			 * Clock      ----
+			 */
+			ret  = -2 << 29;/* Forward divisor is 2 	*/
+			ret |= 2 << 25; /* Backward divisor is 2 	*/
+			ret |= 3 << 17; /* Cpu/Plb is 4 		*/
+			ret |= 0 << 13; /* Pci/Plb is 1 		*/
+			ret |= 0 << 11; /* ExtBus/Plb is 2 		*/
+			ret |= 1 << 15; /* Opb/Plb is 2			*/
+			break;
+		case 0xB1: /* CPC0_CR0 */
+			break;
+		case 0xB2: /* CPC0_CR1 */
+			break;
+		case 0xB4: /* CPC0_PSR: For same uP model, could be different! */
+			break;
+		default:
+			break;
+	}
+/*	printf("walnut_dcr [%x] => %x\n", dcrn, ret);*/
+	return ret;
+}
+static void walnut_dcr_write (void *opaque, int dcrn, target_ulong val)
+{
+/*	printf("walnut_dcr [%x] <= %x\n", dcrn, val);*/
+}
+
+/* 
+ * I2C Customization
+ *
+ * A file.
+ */
+static char PID_content[128]; /* If no file present */
+static uint32_t PID_cursor;
+size_t PID_read(int addr, char *buf, size_t len) {
+	int ret;
+	ret = len;
+	if (len+PID_cursor>128)
+		len = 128 - PID_cursor;
+	memcpy(buf, &PID_content[PID_cursor], len);
+
+#if 0
+	{
+	int i;
+	printf("PID_Read dev(%x) len %d offs %d =>(", addr, ret, PID_cursor);
+	for (i=0; i<ret; i++) {
+		printf(" %x,", buf[i]);
+	}
+	printf(") ret %d\n", ret);
+	}
+#endif
+	return ret;
+}
+size_t PID_write(int addr, char *buf, size_t len) {
+	int ret;
+	ret = len;
+	/* buf = {offset}                  if write to set position (a read'll follow)
+	 *     = {offset,data0, data1 ...} if write data different from position */
+	if (len>0) {
+		PID_cursor = buf[0];
+		len--;
+		if (len>0) {
+			if (len+PID_cursor>128)
+				len = 128 - PID_cursor;
+			memcpy(&PID_content[PID_cursor], buf+1, len);
+		}
+	}
+#if 0
+	{
+	int i;
+	printf("PID_Write dev(0x%x) len %d, off %d <=(", addr, ret, PID_cursor);
+	for (i=0; i<ret; i++) {
+		printf(" %x,", buf[i]);
+	}
+	printf(")\n");
+	}
+#endif
+
+	return ret;
+}
+static void walnut_init (ram_addr_t ram_size, int vga_ram_size,
+                         const char *boot_device, DisplayState *ds,
+                         const char *kernel_filename,
+                         const char *kernel_cmdline,
+                         const char *initrd_filename,
+                         const char *cpu_model)
+{
+    ppc4xx_bd_info_t bd;
+    CPUPPCState *env;
+    qemu_irq *pic;
+    ram_addr_t sram_offset, bios_offset, bdloc;
+    target_phys_addr_t ram_bases[2], ram_sizes[2];
+    target_ulong sram_size, bios_size;
+    //int phy_addr = 0;
+    //static int phy_addr = 1;
+    target_ulong kernel_base, kernel_size, initrd_base, initrd_size;
+    int linux_boot;
+    int fl_idx, fl_sectors, len;
+    int ppc_boot_device = boot_device[0];
+    int index;
+
+    /* XXX: fix this */
+    ram_bases[0] = 0x00000000;
+    ram_sizes[0] = 0x08000000;
+    ram_bases[1] = 0x00000000;
+    ram_sizes[1] = 0x00000000;
+    ram_size = 128 * 1024 * 1024;
+#ifdef DEBUG_BOARD_INIT
+    printf("%s: register cpu\n", __func__);
+#endif
+    env = ppc405xp_init("405GPe", ram_bases, ram_sizes, 33333333, &pic, &sram_offset,
+                        kernel_filename == NULL ? 0 : 1);
+    /* Customize some dcr */
+    ppc_dcr_register(env, 0xAA/* CPC0_ECR   */, NULL, &walnut_dcr_read, &walnut_dcr_write);
+    ppc_dcr_register(env, 0xB0/* CPC0_PLLMR */, NULL, &walnut_dcr_read, NULL);
+    ppc_dcr_register(env, 0xB1/* CPC0_CR0   */, NULL, &walnut_dcr_read, &walnut_dcr_write);
+    ppc_dcr_register(env, 0xB2/* CPC0_CR1   */, NULL, &walnut_dcr_read, &walnut_dcr_write);
+    ppc_dcr_register(env, 0xB4/* CPC0_PSR   */, NULL, &walnut_dcr_read, NULL);
+
+    /* Customize I2C access */
+    {
+	extern KRHandler* readF;
+	extern KRHandler* writeF;
+
+	/* PID */
+	//load_init_image("pid.bin", NULL, 256, 0xff);
+	int PID_fd;
+	memset(PID_content, 0, 128);
+	if ((PID_fd=open("pid.bin", O_RDWR | O_BINARY, 0666))>=0) { /*"r+b");*/
+		int n = read(PID_fd, PID_content, 128);
+		if (n<0) {
+			printf("Error reading file pid.bin (ret %d)\n", n); 
+		} else if (n<128) {
+			printf("Warning reading file pid.bin: only %d/128 bytes loaded\n", n);
+		}
+		close(PID_fd);
+	}
+	if (PID_fd<0) {
+		/* Built in eeprom */
+		printf("Warning using builtin pid content!\n");
+		PID_content[2]	= 4; /* Magic word, use sdram */
+		PID_content[3]	= 12; /* Row word, use sdram */
+		PID_content[4]	= 10; /* Col word, use sdram */
+		PID_content[5]	= 1; /* 1 bank */
+		PID_content[6]	= 32; /* Module width */
+		PID_content[31]	= 0x10; /* How many 4MB is long */
+		PID_content[127]= 0 | 2 /* Cas latency */;
+	}
+
+	readF = &PID_read;
+	writeF = &PID_write;
+    }
+    /* allocate SRAM */
+#ifdef DEBUG_BOARD_INIT
+    printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset);
+#endif
+    /* Last 48 (max 128) bytes) used for bd info */
+    sram_size = 8 * 1024;
+    cpu_register_physical_memory(0x40000000, sram_size,
+                                 sram_offset | IO_MEM_RAM);
+    /* allocate and load BIOS */
+#ifdef DEBUG_BOARD_INIT
+    printf("%s: register BIOS\n", __func__);
+#endif
+    bios_offset = sram_offset + sram_size;
+    fl_idx = 0;
+#ifdef USE_FLASH_BIOS
+    index = drive_get_index(IF_PFLASH, 0, fl_idx);
+    if (index != -1) {
+        bios_size = bdrv_getlength(drives_table[index].bdrv);
+        fl_sectors = (bios_size + 65535) >> 16;
+#ifdef DEBUG_BOARD_INIT
+        printf("Register parallel flash %d size " ADDRX " at offset %08lx "
+               " addr " ADDRX " '%s' %d\n",
+               fl_idx, bios_size, bios_offset, -bios_size,
+               bdrv_get_device_name(drives_table[index].bdrv), fl_sectors);
+#endif
+        pflash_cfi02_register((uint32_t)(-bios_size), bios_offset,
+                              drives_table[index].bdrv, 65536, fl_sectors, 1,
+                              2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA);
+        fl_idx++;
+    } else
+#endif
+    {
+        char buf[1024];
+        ram_addr_t flash_offset;
+	target_ulong flash_size;
+	
+	flash_offset = bios_offset;
+	flash_size = 512*1024;
+#ifdef DEBUG_BOARD_INIT
+        printf("Load BIOS from file\n");
+#endif
+        if (bios_name == NULL)
+            bios_name = BIOS_FILENAME;
+        snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name);
+        bios_size = get_image_size(buf);
+        if (bios_size < 0 || bios_size > BIOS_SIZE) {
+            fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf);
+            exit(1);
+        }
+        bios_size = (bios_size + 0xfff) & ~0xfff;
+#ifdef DEBUG_BOARD_INIT
+        printf("Loading bios@" ADDRX " %d bytes\n", (uint32_t)(-bios_size), bios_size);
+#endif
+#if 1
+        pflash_cfi02_register((uint32_t)(-flash_size), flash_offset,
+                              NULL, 65536 /*sector len*/, flash_size/65536 /* nblocks */, 1,
+                              1 /* width */, 0x0001/* AMD */, 0x4F/* Am29LV040B */, 0x0000, 0x0000, 0x555/* unlock addrs */, 0x2AA);
+#else
+        cpu_register_physical_memory((uint32_t)(-flash_size),
+                                     flash_size, flash_offset | IO_MEM_ROM);
+#endif
+
+        bios_size = load_image_targphys(buf, (-bios_size), BIOS_SIZE/* max sz */);
+        if (bios_size < 0 || bios_size > BIOS_SIZE) {
+            fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf);
+            exit(1);
+        }
+	bios_size = flash_size;
+    }
+    bios_offset += bios_size;
+    /* Register FPGA */
+#ifdef DEBUG_BOARD_INIT
+    printf("%s: register FPGA\n", __func__);
+#endif
+    ref405ep_fpga_init(0xF0300000);
+    /* Register NVRAM */
+#ifdef DEBUG_BOARD_INIT
+    printf("%s: register NVRAM\n", __func__);
+#endif
+    m48t59_init(NULL, 0xF0000000, 0, 8192, 8);
+    /* Load kernel */
+    linux_boot = (kernel_filename != NULL);
+    if (0) {
+#ifdef DEBUG_BOARD_INIT
+        printf("%s: load kernel\n", __func__);
+#endif
+        memset(&bd, 0, sizeof(bd));
+        bd.bi_memstart = 0x00000000;
+        bd.bi_memsize = ram_size;
+        bd.bi_flashstart = -bios_size;
+        bd.bi_flashsize = -bios_size;
+        bd.bi_flashoffset = 0;
+        bd.bi_sramstart = 0xFFF00000;
+        bd.bi_sramsize = sram_size;
+        bd.bi_bootflags = 0;
+        bd.bi_intfreq = 133333333;
+        bd.bi_busfreq = 33333333;
+        bd.bi_baudrate = 115200;
+        bd.bi_s_version[0] = 'Q';
+        bd.bi_s_version[1] = 'M';
+        bd.bi_s_version[2] = 'U';
+        bd.bi_s_version[3] = '\0';
+        bd.bi_r_version[0] = 'Q';
+        bd.bi_r_version[1] = 'E';
+        bd.bi_r_version[2] = 'M';
+        bd.bi_r_version[3] = 'U';
+        bd.bi_r_version[4] = '\0';
+        bd.bi_procfreq = 133333333;
+        bd.bi_plb_busfreq = 33333333;
+        bd.bi_pci_busfreq = 33333333;
+        bd.bi_opbfreq = 33333333;
+        bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001);
+        env->gpr[3] = bdloc;
+        kernel_base = KERNEL_LOAD_ADDR;
+        /* now we can load the kernel */
+        kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
+        if (kernel_size < 0) {
+            fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                    kernel_filename);
+            exit(1);
+        }
+        printf("Load kernel size " TARGET_FMT_ld " at " TARGET_FMT_lx
+               " %02x %02x %02x %02x\n", kernel_size, kernel_base,
+               *(char *)(phys_ram_base + kernel_base),
+               *(char *)(phys_ram_base + kernel_base + 1),
+               *(char *)(phys_ram_base + kernel_base + 2),
+               *(char *)(phys_ram_base + kernel_base + 3));
+        /* load initrd */
+        if (initrd_filename) {
+            initrd_base = INITRD_LOAD_ADDR;
+            initrd_size = load_image(initrd_filename,
+                                     phys_ram_base + initrd_base);
+            if (initrd_size < 0) {
+                fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+                        initrd_filename);
+                exit(1);
+            }
+        } else {
+            initrd_base = 0;
+            initrd_size = 0;
+        }
+        env->gpr[4] = initrd_base;
+        env->gpr[5] = initrd_size;
+        ppc_boot_device = 'm';
+        if (kernel_cmdline != NULL) {
+            len = strlen(kernel_cmdline);
+            bdloc -= ((len + 255) & ~255);
+            memcpy(phys_ram_base + bdloc, kernel_cmdline, len + 1);
+            env->gpr[6] = bdloc;
+            env->gpr[7] = bdloc + len;
+        } else {
+            env->gpr[6] = 0;
+            env->gpr[7] = 0;
+        }
+        env->nip = KERNEL_LOAD_ADDR;
+    } else {
+        env->nip = 0xFFFFFFFC;
+    }
+#ifdef DEBUG_BOARD_INIT
+    printf("%s: Done\n", __func__);
+#endif
+    /*printf("bdloc %016lx %s\n",
+           (unsigned long)bdloc, (char *)(phys_ram_base + bdloc));*/
+}
+QEMUMachine walnut_machine = {
+    .name = "walnut",
+    .desc = "DHT Walnut board, based on ppc405GPe",
+    .init = walnut_init,
+    .ram_require = (128 * 1024 * 1024 + 4096 + 512 * 1024 + BIOS_SIZE) | RAMSIZE_FIXED,
+};
+
 /*****************************************************************************/
 /* AMCC Taihu evaluation board */
 /* - PowerPC 405EP processor
@@ -530,7 +872,7 @@
 #ifdef DEBUG_BOARD_INIT
     printf("%s: register cpu\n", __func__);
 #endif
-    env = ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic, &bios_offset,
+    env = ppc405xp_init("405ep", ram_bases, ram_sizes, 33333333, &pic, &bios_offset,
                         kernel_filename == NULL ? 0 : 1);
     /* allocate and load BIOS */
 #ifdef DEBUG_BOARD_INIT
Index: hw/emac.c
===================================================================
--- hw/emac.c	(revision 0)
+++ hw/emac.c	(revision 0)
@@ -0,0 +1,797 @@
+/*
+ * QEMU EMAC emulation
+ *
+ * Copyright (c) 2008, Lionetti Salvatore salvatorelionetti@yahoo.it
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This module provide an emulation for ethernet onchip controller on ppc4xx cpu.
+ * Tested on
+ * - emulator of a NSN proprietary board, for 4G
+ * - walnut board/u-boot-1.1.6 (whith some patch)
+ *
+ * TODO:
+ * - >1 {tx, rx} channel 
+ * - can_receive() and buffer_full() too simple.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "ppc405.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "net.h"
+
+/*#define DEBUG_EMAC*/
+#ifdef DEBUG_EMAC
+#define DPRINT(fmt, args...)                           \
+    do { printf("%" PRIu64 " %s: " fmt , qemu_get_clock(rt_clock), __func__, ##args); } while (0)
+#else
+#define DPRINT(fmt, args...)
+#endif 
+
+/* 
+ * Redefined because if !exist, linker gives a warning but produce qemu,
+ * but on execution this call cause problem
+ * (could be cygwin & mingw cohesistence?)
+ */
+#if (defined(WORDS_BIGENDIAN) && defined(TARGET_WORDS_BIGENDIAN)) || ((!defined(WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN)))
+#if 0
+static unsigned long int htonl(unsigned long int hostlong);
+static unsigned short int htons(unsigned short int hostshort);
+#endif
+static unsigned long int ntohl(unsigned long int ing) {
+	return ing;
+}
+static unsigned short int ntohs(unsigned short int ing) {
+	return ing;
+}
+#else
+static unsigned long int ntohl(unsigned long int ing) {
+	unsigned char tmp;
+	unsigned char* tmpp = (unsigned char*)&ing;
+	tmp=tmpp[0]; tmpp[0]=tmpp[3]; tmpp[3]=tmp;
+	tmp=tmpp[1]; tmpp[1]=tmpp[2]; tmpp[2]=tmp;
+	return *(unsigned long int*)tmpp;
+}
+static unsigned short int ntohs(unsigned short int ing) {
+	return ((ing&0xFF)<<8) | ((ing&0xFF00)>>8);
+}
+#endif
+
+/* qemu initialization:
+ *
+ * parse command line:	call net_client_init() for each -net, either for {if, collision domain} options.
+ * 			==========					================
+ * 			IF MAC=...	<==== Vlan{Id,Ptr} ====>	COLLISION DOMAIN (mean N-1 other IF MAC=...)
+ * 			========== 					================
+ * 				-net nic[,...]:	fill field onto nd_table[] {MAC(cl|auto), model, vlanPtr=f(vlanId)}
+ * 						vlanPtr=f(vlanId)->guest++
+ * 				-net tap[,...]: vlanPtr=f(vlanId)->host ++
+ * 						tap_win32_init(vlanPtr, "ifname=tap..."):
+ * 							qemu_new_vlan_client(vlanPtr,tap_receive, tap* opaque)		RECV
+ * 							qemu_add_wait_object(tap_sem, tap_w32_send, tap* opaque)	SEND
+ * 			
+ * 			For each vlan created,
+ * 				exit if no GUEST & some HOST
+ * 				warn if some GUEST & no HOST
+ * 											  
+ * start machine:	Es ppc (also pc use n2000) call machine->init():
+ * 			isa_ne2000_init(base, irqno, &nd_table[i])
+ * 				register_ioport_write(base + ..., ne2000_asic_ioport_write, opaque NE2000State*);	SEND
+ * 				qemu_new_vlan_client(nd->vlan, ne2000_receive, ne2000_can_receive, opaque NE2000State*) RECV
+ *
+ * Done!!!
+ *
+ * qemu runtime scenario:
+ *
+ * packet send from HOST:	a signal (after exec() cycle) is sent to tap_sem => tap_w32_send() => tap_win32_read()
+ * 				if some bytes returned => qemu_send_packet() => send to all 'client' (better peer) of
+ * 				such vlan, different from itself => ne2000_receive()
+ *
+ * packet send from GUEST:	in machine code we call qemu_send_packet() => (prec) => tap_receive() => tap_w32_write()
+ *
+ *
+ * So onto VLAN actors always call qemu_send_packet() that dispatch packet to all peer attached to same vlan.
+ *
+ */
+/* PPC405 layer 2, reversed MII mode:
+ *
+ * Register@0xEF600000 already mapped.
+ * Configuration RW:
+ *
+ * Read(ind, data):
+ * 	[EMAC0_STACR] <= (STACR_STAC_R | ind)
+ *	[EMAC0_STACR] & STACR_OC must goes to 0
+ *	[EMAC0_STACR] => >>16 => data
+ *
+ * Write(ind, data)
+ * 	[EMAC0_STACR] <= (STACR_STAC_W | ind | data<<16)
+ * 	[EMAC0_STACR] & STACR_OC must goes to 0
+ *
+ * Physical access:
+ * Read(ind_phy, ind_reg, data)
+ * 	[EMAC0_STACR] <= (STACR_STAC_W | TANTOS_REG_MIIAC | (ind_reg&0x1F | OP_READ | (ind_phy&0x1F)<<5))>>16
+ * 	[EMAC0_STACR] & STACR_OC must goes to 0
+ *	
+ *	Verify:
+ *	[EMAC0_STACR] <= (STACR_STAC_R | TANTOS_REG_MIIRD)
+ * 	[EMAC0_STACR] & STACR_OC must goes to 0
+ * 	[EMAC0_STACR] => data
+ * 	data & STACR_PHYE should be 0
+ * 	data=data>>16
+ *
+ * Tantos Stub Layer: (needed?)
+ *
+ * From mal we need
+ * - MAL0_TXCTPxR
+ * - MAL0_RXCTPxR
+ * - MAL0_RCBS0
+ */
+/* TODO: need interaction between device!
+ * very difficult since all struct is in .c file */
+extern uint32_t txctpr[4];
+extern uint32_t rxctpr[2];
+extern uint32_t rcbs[2];
+extern uint32_t *rxeobisr; /* With namespace, use & to automatize correlation.*/
+extern uint32_t *txeobisr;
+// For PPC405
+#define EMAC0_BASE        0xEF600800
+#define EMAC0_STACR_DISP  0x35
+#define EMAC0_STACR      (EMAC0_BASE + EMAC0_STACR_DISP) /* 4 byte addressing */
+#define STACR_OC          0x00008000 /* Occupied flag */
+#define STACR_PHYE        0x00004000
+#define STACR_STAC_W      0x00002000
+#define STACR_STAC_R      0x00001000
+
+#define TANTOS_REG_MIIAC    0x120      /// TANTOS3G MII indirect acess registers
+#define TANTOS_REG_MIIWD    0x121
+#define TANTOS_REG_MIIRD    0x122
+#define OP_WRITE	0x0400
+#define OP_READ		0x0800
+#define MBUSY 		0x8000
+
+/* WORDS_BIGENDIAN = FALSE su HOST i386, GUEST ppc
+ * per cui e' l'architetura HOST
+ */
+struct EmacMalRxDes {
+#ifndef WORDS_BIGENDIAN
+    union {
+	    struct { /* Mal */
+		    unsigned int emac1_nu	: 2;
+		    unsigned int bit5_intr	: 1;
+		    unsigned int bit4_first	: 1;
+		    unsigned int bit3_last	: 1;
+		    unsigned int bit2_contm	: 1;
+		    unsigned int bit1_wrap	: 1;
+		    unsigned int bit0_empty	: 1;
+		    unsigned int emac2_nu	: 8; 
+	    } __attribute__ ((packed)) mal;
+	    struct { /* Status: Read access, error cause */
+		    unsigned int bit7_pausepacket	: 1;
+		    unsigned int bit6_overrun		: 1;
+		    unsigned int mal_nu			: 6;
+		    unsigned int bit15_inrange		: 1;
+		    unsigned int bit14_outrange		: 1;
+		    unsigned int bit13_longpacket	: 1;
+		    unsigned int bit12_fcs		: 1;
+		    unsigned int bit11_alignment	: 1;
+		    unsigned int bit10_shortevent	: 1;
+		    unsigned int bit9_runtpacket	: 1;
+		    unsigned int bit8_badpacket		: 1;
+	    } __attribute__ ((packed)) emac_errstatus;
+    } __attribute__ ((packed));
+#else
+#error "Host endianism BUG: To Test!!!"
+#endif
+    unsigned short len;
+    unsigned char* buf;
+} __attribute__ ((packed)); /* Host endianism, to be converted */
+
+struct EmacMalTxDes {
+#ifndef WORDS_BIGENDIAN
+    /* IN A HALF-WORD (16bits)
+     * position		position
+     * in bitfield	in value
+     * from high	from MSb
+     * =========================
+     * 0		8
+     * 1		9
+     * 2		10
+     * 3		11
+     * 4		12
+     * 5		13
+     * 6		14
+     * 7		15
+     * 8		0
+     * 9		1
+     * 10		2
+     * 11		3
+     * 12		4
+     * 13		5
+     * 14		6
+     * 15		7
+     *
+     * ===HOST LE, TARGET BE===
+     * IN A BYTE (8bits)
+     *
+     * IN A HALF-WORD (16bits)
+     * 0-7		8-15
+     * 8-15		0-7
+     */
+    union {
+	    struct { /* Mal */
+		    unsigned int emac1_nu	: 2;
+		    unsigned int bit5_intr	: 1;
+		    unsigned int bit4_resv	: 1;
+		    unsigned int bit3_last	: 1;
+		    unsigned int bit2_contm	: 1;
+		    unsigned int bit1_wrap	: 1;
+		    unsigned int bit0_ready	: 1;
+		    unsigned int emac2_nu	: 8; 
+	    } __attribute__ ((packed)) mal;
+	    struct { /* Status: Read access, error cause */
+		    unsigned int bit7_badpacket		: 1;
+		    unsigned int bit6_badfsc		: 1;
+		    unsigned int mal_nu			: 6;
+		    unsigned int bit15_sqe		: 1;
+		    unsigned int bit14_underrun		: 1;
+		    unsigned int bit13_singlecoll	: 1;
+		    unsigned int bit12_multiplecoll	: 1;
+		    unsigned int bit11_latecoll		: 1;
+		    unsigned int bit10_excessivecoll	: 1;
+		    unsigned int bit9_excessivedeferral : 1;
+		    unsigned int bit8_lossofcarrier	: 1;
+	    } __attribute__ ((packed)) emac_errorstatus;
+	    struct { /* Control: Write access */
+		    unsigned int bit7_generatepad	: 1;
+		    unsigned int bit6_generatefcs	: 1;
+		    unsigned int mal_nu			: 6;
+		    unsigned int emac_nu		: 4;
+		    unsigned int bit11_vlantag_replace	: 1;
+		    unsigned int bit10_vlantag_insert	: 1;
+		    unsigned int bit9_sourceaddr_insert	: 1;
+		    unsigned int bit8_sourceaddr_replace: 1;
+	    } __attribute__ ((packed)) emac_control;
+    };
+#else
+#error "Host endianism BUG: To Test!!!"
+#endif
+    unsigned short len;
+    unsigned char* buf;
+}__attribute__ ((packed)); /* Host endianism, to be converted */
+
+/* Controlo Register definition */
+struct EmacRegs {
+#if 0
+#ifndef WORDS_BIGENDIAN
+	struct Emac0_mr0 {
+		unsigned int ;
+	} __attribute__((packed)); 
+#else
+#error "Host endianism BUG: To Test!!!"
+#endif
+#else
+	/* 0 */
+	uint32_t mr0;
+	uint32_t mr1;
+	uint32_t tmr0;
+	uint32_t tmr1;
+	uint32_t rmr;
+	uint32_t isr;
+	uint32_t isre;
+	uint32_t iahr;
+	uint32_t ialr;
+	uint32_t vtpid;
+	/* 10 */
+	uint32_t vtci;
+	uint32_t ptr;
+	uint32_t iaht1;
+	uint32_t iaht2;
+	uint32_t iaht3;
+	uint32_t iaht4;
+	uint32_t gaht1;
+	uint32_t gaht2;
+	uint32_t gaht3;
+	uint32_t gaht4;
+	/* 20 */
+	uint32_t lsah;
+	uint32_t lsal;
+	uint32_t ipgvr;
+	uint32_t stacr;
+	uint32_t trtr;
+	uint32_t rwmr;
+	uint32_t octx;
+	uint32_t ocrx;
+	/* 28 reg */
+
+#define RSTA  mr0 & 0x20000000 
+#define TXEN  mr0 & 0x10000000 
+#define RXEN  mr0 & 0x08000000 
+#endif	
+} __attribute__ ((packed));
+
+typedef struct ppc4xx_emac_t ppc4xx_emac_t;
+struct ppc4xx_emac_t {
+    /* Guest related */
+    target_phys_addr_t base;
+    NICInfo nic;
+    /* To remove, already defined in mal */
+    qemu_irq mal_irqs_txeob;
+    qemu_irq mal_irqs_rxeob;
+    qemu_irq mal_irqs_txerr;
+    qemu_irq mal_irqs_rxerr;
+    qemu_irq mal_irqs_syserr;
+
+    struct EmacRegs reg, _reg;
+
+    struct EmacMalTxDes* tx;
+    struct EmacMalRxDes* rx;
+
+    int txPos, rxPos;
+    int txNum, rxNum;
+
+    int rxLostNum, rxLostLimit;
+
+    /* Host related */
+    VLANClientState *vc;
+    QEMUTimer* tx_timer;
+};
+
+#if 0
+#define EMACMAL_EMPTY 0x8000
+#define EMACMAL_WRAP  0x8000
+#define EMACMAL_EMPTY 0x8000
+#define EMACMAL_EMPTY 0x8000
+void EmacMalDes_readFromTarget(struct EmacMalDes* host, struct EmacMalDes* guest) {
+
+    host->status = ntohs(guest->status);
+    host->len = ntohs(guest->len);
+    host->buf = htol(guest->buf);
+}
+void EmacMalDes_writeToTarget(struct EmacMalDes* host, struct EmacMalDes* guest) {
+}
+#endif
+void EmacMalRxDes_dump(struct EmacMalRxDes* des) {
+#ifdef DEBUG_EMAC
+	unsigned char* tdes = (unsigned char*) ((unsigned char*)des - phys_ram_base);
+	printf("RXdes@%p={len%d,buf%p,empty%d wrap%d contm %d last%d first%d intr%d\n",
+			tdes, ntohs(des->len), (unsigned char*) ntohl((unsigned long int)des->buf),
+			des->mal.bit0_empty, des->mal.bit1_wrap, des->mal.bit2_contm, des->mal.bit3_last, des->mal.bit4_first, des->mal.bit5_intr);
+#endif
+}
+void EmacMalTxDes_dump(struct EmacMalTxDes* des) {
+#ifdef DEBUG_EMAC
+	unsigned char* tdes = (unsigned char*)((unsigned char*)des - phys_ram_base);
+	printf("TXdes@%p={len%d,buf%p,ready%d wrap%d contm %d last%d first%d intr%d\n",
+			tdes, ntohs(des->len), (unsigned char*)ntohl((unsigned long int)des->buf),
+			des->mal.bit0_ready, des->mal.bit1_wrap, des->mal.bit2_contm, des->mal.bit3_last, des->mal.bit4_resv, des->mal.bit5_intr);
+#endif
+}
+#if 0
+static int emac_buffer_full(ppc4xx_emac_t *s)
+{
+#if 0
+    int avail, index, boundary;
+
+    index = s->curpag << 8;
+    boundary = s->boundary << 8;
+    if (index < boundary)
+        avail = boundary - index;
+    else
+        avail = (s->stop - s->start) - (index - boundary);
+    if (avail < (MAX_ETH_FRAME_SIZE + 4))
+        return 1;
+#endif
+    return 0;
+}
+
+static int emac_can_receive(void *opaque)
+{
+#if 0
+    NE2000State *s = opaque;
+
+    if (s->cmd & E8390_STOP)
+        return 1;
+    return !ne2000_buffer_full(s);
+#endif
+    return 0;
+}
+#endif
+
+
+static void emac_receive(void *opaque, const uint8_t *buf, int size)
+{
+    ppc4xx_emac_t* emac = (ppc4xx_emac_t*) opaque;
+
+    DPRINT("%d bytes\n", size);
+    if (emac->reg.RXEN) {
+    if (emac->rx->buf) {
+    if (size<1520) {
+	    if (emac->rx[emac->rxPos].mal.bit0_empty) {
+	    /* data is allocated with the maximum possible length, on eth = 1520Bytes
+	     * len field represent packet size.*/
+		    /* We !use Continuos mode, first & last field ignored */
+		    unsigned char* dstG = (unsigned char*) ntohl((unsigned long int)emac->rx[emac->rxPos].buf);
+		    unsigned char* dstH= dstG+(unsigned int)phys_ram_base;
+		    unsigned short size16 = size;
+		    DPRINT(" Delivery message HOST %p-> GUEST %p,pos%d\n", dstH, dstG, emac->rxPos);
+		    EmacMalRxDes_dump(&emac->rx[emac->rxPos]);
+		    emac->reg.ocrx += size16;
+		    memcpy(dstH, buf, size);	
+		    emac->rx[emac->rxPos].len = ntohs(size16);
+		    emac->rx[emac->rxPos].mal.bit0_empty = 0;
+		    if (emac->rx[emac->rxPos].mal.bit5_intr) {
+			    /* TODO: use appropriate channel */
+			    *rxeobisr = 0xC0000000;
+			    qemu_irq_raise(emac->mal_irqs_rxeob);
+		    }
+
+		    emac->rxPos += (emac->rx[emac->rxPos].mal.bit1_wrap)? -(emac->rxPos) : 1;
+	    } else {
+		    if (++emac->rxLostNum == emac->rxLostLimit) {
+			    printf(" %d Message lost, board seem hung up!\n", emac->rxLostNum);
+			    emac->rxLostLimit*=10;
+		    }
+	    }
+    } else {
+	    printf(" Message size > 1520, discarding packet\n");
+    }
+    } else {
+    }
+    } else {
+	    static int sndOrMore=0;
+	    if (!sndOrMore) {
+		    printf("emac/mal: Driver !command start yet\n");
+		    sndOrMore=1;
+	    }
+    }
+#if 0
+#define MIN_BUF_SIZE 60
+    NE2000State *s = opaque;
+    uint8_t *p;
+    unsigned int total_len, next, avail, len, index, mcast_idx;
+    uint8_t buf1[60];
+    static const uint8_t broadcast_macaddr[6] =
+        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#if defined(DEBUG_NE2000)
+    printf("NE2000: received len=%d\n", size);
+#endif
+
+    if (s->cmd & E8390_STOP || ne2000_bufstruct mal_emac_bdfer_full(s))
+        return;
+
+    /* XXX: check this */
+    if (s->rxcr & 0x10) {
+        /* promiscuous: receive all */
+    } else {
+        if (!memcmp(buf,  broadcast_macaddr, 6)) {
+            /* broadcast address */
+            if (!(s->rxcr & 0x04))
+                return;
+        } else if (buf[0] & 0x01) {
+            /* multicast */
+            if (!(s->rxcr & 0x08))
+                return;
+            mcast_idx = compute_mcast_idx(buf);
+            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+                return;
+        } else if (s->mem[0] == buf[0] &&
+                   s->mem[2] == buf[1] &&
+                   s->mem[4] == buf[2] &&
+                   s->mem[6] == buf[3] &&
+                   s->mem[8] == buf[4] &&
+                   s->mem[10] == buf[5]) {
+            /* match */
+        } else {
+            return;
+        }
+    }
+
+
+    /* if too small buffer, then expand it */
+    if (size < MIN_BUF_SIZE) {
+        memcpy(buf1, buf, size);
+        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+        buf = buf1;
+        size = MIN_BUF_SIZE;
+    }
+
+    index = s->curpag << 8;
+    /* 4 bytes for header */
+    total_len = size + 4;
+    /* address for next packet (4 bytes for CRC) */
+    next = index + ((total_len + 4 + 255) & ~0xff);
+    if (next >= s->stop)
+        next -= (s->stop - s->start);
+    /* prepare packet header */
+    p = s->mem + index;
+    s->rsr = ENRSR_RXOK; /* receive status */
+    /* XXX: check this */
+    if (buf[0] & 0x01)
+        s->rsr |= ENRSR_PHY;
+    p[0] = s->rsr;
+    p[1] = next >> 8;
+    p[2] = total_len;
+    p[3] = total_len >> 8;
+    index += 4;
+
+    /* write packet data */
+    while (size > 0) {
+        if (index <= s->stop)
+            avail = s->stop - index;
+        else
+            avail = 0;
+        len = size;
+        if (len > avail)
+            len = avail;
+        memcpy(s->mem + index, buf, len);
+        buf += len;
+        index += len;
+        if (index == s->stop)
+            index = s->start;
+        size -= len;
+    }
+    s->curpag = next >> 8;
+
+    /* now we can signal we have received something */
+    s->isr |= ENISR_RX;
+    ne2000_update_irq(s);
+#endif
+}
+
+void emac_ppc405_init(ppc4xx_emac_t* emac, NICInfo *nd)
+{
+    memcpy(&emac->nic, nd, sizeof(NICInfo));
+    /* We are only be able to receive. Sending packet mean receiver action get up */
+    emac->vc = qemu_new_vlan_client(nd->vlan, emac_receive,
+                                 NULL/*emac_can_receive*/, emac);
+
+    snprintf(emac->vc->info_str, sizeof(emac->vc->info_str),
+             "emac macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+             emac->nic.macaddr[0],
+             emac->nic.macaddr[1],
+             emac->nic.macaddr[2],
+             emac->nic.macaddr[3],
+             emac->nic.macaddr[4],
+             emac->nic.macaddr[5]);
+    printf("%s\n", emac->vc->info_str);
+}
+
+static void ppc4xx_emac_reset (void *opaque)
+{
+    ppc4xx_emac_t *emac;
+
+    emac = opaque;
+    emac->rx = NULL;
+    emac->tx = NULL;
+    emac->txPos = emac->rxPos = 0;
+    emac->txNum = emac->rxNum = 0;
+    emac->rxLostNum=0;
+    emac->rxLostLimit=1;
+
+    memset(&emac->reg, 0, sizeof(emac->reg));
+    emac->reg.mr0   = 0xC0000000;
+    emac->reg.tmr1  = 0x380F0000;
+    emac->reg.vtpid = 0x00008808;
+    emac->reg.ptr   = 0x0000FFFF;
+    emac->reg.ipgvr = 0x00000004;
+    emac->reg.stacr = 0x00008000;
+    emac->reg.rwmr  = 0x04001000;
+
+    /* Now ready to issue mii control transfer */
+    emac->reg.stacr = 0;
+}
+/* It looks like a lot of Linux programs assume page size
+ * is 4kB long. This is evil, but we have to deal with it...
+ *
+#define TARGET_PAGE_BITS 12
+ * Here we have many register.
+ * Do action only for reset or transmit command.
+ * Other will be simple configuration latched during device emulation
+ * es stop rx, allow pause packet.
+ *
+ * Another tip:
+ * When possible only implemente the read of value, since using var read/write is target indep
+ */
+static uint32_t emac_readl (void *opaque, target_phys_addr_t addr)
+{
+	uint32_t value;
+	ppc4xx_emac_t* emac = (ppc4xx_emac_t*)opaque;
+	uint32_t ind = (addr-emac->base)>>2;
+	/* Keep in a separate way, as stackable code */
+	if (ind>=28) {
+		printf("emac: Out of range register %d!\n", ind);
+		return 0;
+	}
+	value = ((uint32_t*)&emac->reg)[ind];
+	DPRINT("emac: [%d] => %x\n", ind, value);
+	return value/*STACR_OC*/;
+}
+
+static void emac_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+	ppc4xx_emac_t* emac = (ppc4xx_emac_t*)opaque;
+	uint32_t ind = (addr-emac->base)>>2;
+
+/*	printf("%s: " PADDRX " val %x\n",__func__, addr, value);*/
+	/* Exist some common code for this? */
+	if (ind>=28) {
+		printf("emac: Out of range register %d!\n", ind);
+		return;
+	}
+	switch (ind) {
+		case 20:
+		case 21:
+		case 26:
+		case 27:
+			printf("emac: Attemping to write to read only reg!");
+			return;
+	}
+	DPRINT("emac: [%d] <= %x\n", ind, value);
+	/* Keep in a separate way, as stackable code */
+	((uint32_t*)&emac->reg)[ind] = value;
+	if (ind==0) {
+		emac->_reg.mr0 = value;
+		/* soft reset */
+		if (emac->_reg.RSTA) {
+			printf("emac: soft reset commanded!\n");
+			ppc4xx_emac_reset(emac);/* Should be protected with a semaphore, also in emac_receive()? */
+			value &= ~(emac->_reg.RSTA);	/* Sw look for this bit go down */
+		}
+
+		DPRINT("emac: St%s tx channel!\n", emac->_reg.TXEN?"arting":"opping");
+		DPRINT("emac: St%s rx channel!\n", emac->_reg.RXEN?"arting":"opping");
+
+		/* Suppose the mal reg was updated, seem a good approximation */
+		if (emac->_reg.TXEN && emac->tx==NULL) {
+			int l=0;
+			emac->tx = (struct EmacMalTxDes*) (txctpr[0] + phys_ram_base);
+			do { EmacMalTxDes_dump(&emac->tx[l]); } while (!emac->tx[l].mal.bit1_wrap && l++<50);
+		}
+		if (emac->_reg.RXEN && emac->rx==NULL) {
+			int l=0;
+			emac->rx = (struct EmacMalRxDes*) (rxctpr[0] + phys_ram_base);
+			do { EmacMalRxDes_dump(&emac->rx[l]); } while (!emac->rx[l].mal.bit1_wrap && l++<50);
+		}
+		/* A timer that periodically check packet to send 
+		emac->tx_timer = qemu_new_timer();*/
+	}
+	if (ind==2) {
+		if (value & 0xC0000000) { /* both channel */
+			if (emac->reg.TXEN) {
+			if (emac->tx[emac->txPos].mal.bit0_ready) {
+				unsigned char* dstG = (unsigned char*) ntohl((unsigned long int)emac->tx[emac->txPos].buf);
+				unsigned char* dstH= dstG+(unsigned int)phys_ram_base;
+				unsigned short size16 = ntohs(emac->tx[emac->txPos].len);
+
+				DPRINT("Sending packet on pos %d\n", emac->txPos);
+				qemu_send_packet(emac->vc, dstH, size16);
+				emac->reg.octx += size16;
+				if (emac->tx[emac->txPos].mal.bit5_intr) {
+					/* TODO: use appropriate channel */
+					*txeobisr = 0xC0000000;
+					qemu_irq_raise(emac->mal_irqs_txeob);
+				}
+
+				/* Clear status */
+				emac->tx[emac->txPos].mal.bit0_ready = 0;
+				/* 0x808 is stucked at STACR_OC */
+				emac->txPos += (emac->tx[emac->txPos].mal.bit1_wrap)? -(emac->txPos) : 1;
+
+				/* 
+				 * u-boot driver (no os=>no task) poll this bit until became 0,
+				 * but in PPC405GPr User manual nothing said about this
+				 */
+				value &= ~0xC0000000;
+			} else {printf("Processor goes crazy(ready bit was 0)!!! Stopping tx\n");}
+			} else {DPRINT("Tx is disabled\n");}
+		}
+	}
+	if (ind==23) {
+		/* PPC405GPr_UM2004 say, on page 568:
+		 * 'EMAC sets EMAC0_STACR[OC] = 0 when the EMAC0_STACR is written to.'
+		 * 'EMAC then sets EMAC0_STACR[OC] = 1 to indicate that the data has been written to the PHY, or the data'
+		 * 'read from the PHY is valid. The device driver should poll for EMAC0_STACR[OC] = 1 before issuing a new'
+		 * 'command, or before using data read from the PHY'
+		 * 
+		 * our approximation:
+		 * - initial condition			0
+		 * - write(sw start some MII command)	1 (we end in the same cycle)
+		 * - read(poll status)			1
+		 * - write(sw start new cmd | ...)	0
+		 *
+		 * reg write clear (bit index is opposite then those reported by official manual)
+		 * PHYD	bit 31:16	Data (RW)
+		 * OC	bit 15		0 when this reg is addressed, 1 data written to PHY/data readed correctly from PHY
+		 * PHYE	bit 14		1 PhyError during read
+		 * STAC bit 13:12	00 Reserved/01 Read/10 Write/11 Reserved
+		 * OPBC	bit 11:10	OPB Bus clock freq. signal EMCMDCIk
+		 * PCDA	bit 9:5		PHY Command destination address
+		 * PRA	bit 4:0		PHY Register address
+		 *
+		value &= ~(1<<15);*/
+		value ^=  (1<<15);
+		value &= ~(1<<14);
+	}
+	/* Keep in a separate way, as stackable code */
+	((uint32_t*)&emac->reg)[ind] = value;
+}
+static uint32_t emac_readw (void *opaque, target_phys_addr_t addr)
+{
+	printf("Lettura a granularita' word su EMAC " PADDRX "\n",addr);
+	return 0;
+}
+
+static void emac_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+	printf("Scrittura a granularita' word su EMAC " PADDRX "\n",addr);
+}
+
+static uint32_t emac_readb (void *opaque, target_phys_addr_t addr)
+{
+	printf("Lettura a granularita' byte su EMAC " PADDRX "\n",addr);
+	return 0;
+}
+
+static void emac_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+	printf("Scrittura a granularita' byte su EMAC " PADDRX "\n",addr);
+}
+
+
+static CPUReadMemoryFunc *emac_read[] = {
+    &emac_readb,
+    &emac_readw,
+    &emac_readl,
+};
+
+static CPUWriteMemoryFunc *emac_write[] = {
+    &emac_writeb,
+    &emac_writew,
+    &emac_writel,
+};
+void ppc4xx_emac_init (CPUState *env, ppc4xx_mmio_t *mmio, target_phys_addr_t offset, NICInfo *nic, qemu_irq mal_irqs[4])
+{
+    ppc4xx_emac_t *emac;
+
+    emac = qemu_mallocz(sizeof(ppc4xx_emac_t));
+    if (emac != NULL) {
+        emac->base = offset;
+#ifdef DEBUG_OPBA
+        printf("%s: offset " PADDRX "\n", __func__, offset);
+#endif
+        ppc4xx_mmio_register(env, mmio, offset, 0x70,
+                             emac_read, emac_write, emac);
+        qemu_register_reset(ppc4xx_emac_reset, emac);
+
+	/* One time configuration, HOST related */
+	emac_ppc405_init(emac, nic);
+
+	emac->mal_irqs_txeob = mal_irqs[0];
+	emac->mal_irqs_rxeob = mal_irqs[1];
+	emac->mal_irqs_txerr = mal_irqs[2];
+	emac->mal_irqs_rxerr = mal_irqs[3];
+
+	/* N Time configuration, GUEST related */
+        ppc4xx_emac_reset(emac);
+    }
+}
Index: hw/pflash_cfi02.c
===================================================================
--- hw/pflash_cfi02.c	(revision 5720)
+++ hw/pflash_cfi02.c	(working copy)
@@ -2,6 +2,7 @@
  *  CFI parallel flash with AMD command set emulation
  *
  *  Copyright (c) 2005 Jocelyn Mayer
+ *  Copyright (c) 2008 Lionetti Salvatore
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,7 +23,7 @@
  * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
  * Supported commands/modes are:
  * - flash read
- * - flash write
+ * - flash write (also buffer mode)
  * - flash ID read
  * - sector erase
  * - chip erase
@@ -34,14 +35,20 @@
  * It does not implement software data protection as found in many real chips
  * It does not implement erase suspend/resume commands
  * It does not implement multiple sectors erase
+ *
+ * Revisione done to optimize, (the next'll be mmap()), expecially for WriteBufferLoad command.
+ * Use different name for structure, so multiple device could be handed@same time
  */
 
 #include "hw.h"
 #include "flash.h"
 #include "qemu-timer.h"
 #include "block.h"
+#include "cpu-all.h" /* To have FLASH_LOG_CFI */
+#include "exec-all.h" /* To have loglevel */
 
-//#define PFLASH_DEBUG
+#define USE_SIMPLE_TABLE
+/*#define PFLASH_DEBUG*/
 #ifdef PFLASH_DEBUG
 #define DPRINTF(fmt, args...)                      \
 do {                                               \
@@ -51,7 +58,8 @@
 #define DPRINTF(fmt, args...) do { } while (0)
 #endif
 
-struct pflash_t {
+#define pflash_cfi02_t pflash_t
+struct pflash_cfi02_t {
     BlockDriverState *bs;
     target_phys_addr_t base;
     uint32_t sector_len;
@@ -60,18 +68,30 @@
     int width;
     int wcycle; /* if 0, the flash is read normally */
     int bypass;
+    uint32_t bytes2write; /* In buffer load mode, must remember the number of bytes to elaborate */
+    uint8_t buffer_loading; /* For buffer load mode 1 mean read nword-1 & apply write & apply end command */
+    uint32_t buffer_loading_start;
+    uint32_t buffer_loading_end;
     int ro;
     uint8_t cmd;
     uint8_t status;
     uint16_t ident[4];
     uint16_t unlock_addr[2];
     uint8_t cfi_len;
-    uint8_t cfi_table[0x52];
+    uint8_t cfi_table[0x80];
     QEMUTimer *timer;
     ram_addr_t off;
     int fl_mem;
     int rom_mode;
     void *storage;
+#if 0
+    /* Layer needed to optimize (driver common size) Vs (device common size)
+     * Used by timer() & update(), update() use SIGNED!!!
+     * A simple way to represent a list of number. */
+    int pendingW;
+    int fstSector;
+    int curSector;
+#endif
 };
 
 static void pflash_register_memory(pflash_t *pfl, int rom_mode)
@@ -88,9 +108,41 @@
                                      pfl->chip_len, phys_offset);
 }
 
+static void pflash_cfi02_core_reset (void *opaque) {
+    struct pflash_cfi02_t *pfl = (struct pflash_cfi02_t*)opaque;
+
+    pfl->bypass = 0;
+    pfl->wcycle = 0;
+    pfl->cmd = 0;
+    pfl->status = 0;
+
+    pfl->bytes2write = 0;
+    pfl->buffer_loading = 0;
+    pfl->buffer_loading_start = 0;
+    pfl->buffer_loading_end = 0;
+
+/*    pfl->pendingW = pfl->fstSector = curSector = 0; Lost current write? */
+    
+    pflash_register_memory(pfl, 1);
+}
+
+static void pflash_cfi02_reset (void *opaque) {
+    struct pflash_cfi02_t *pfl = (struct pflash_cfi02_t*)opaque;
+
+    qemu_del_timer(pfl->timer);
+#if 0 /* XXX: there should be a bit to set up read-only,
+       *      the same way the hardware does (with WP pin).
+       */
+    pfl->ro = 1;
+#else
+    pfl->ro = 0;
+#endif
+    pflash_cfi02_core_reset(pfl);
+    printf("Resetting cfi chip!\n");
+}
 static void pflash_timer (void *opaque)
 {
-    pflash_t *pfl = opaque;
+    struct pflash_cfi02_t *pfl = opaque;
 
     DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
     /* Reset flash */
@@ -104,13 +156,12 @@
     pfl->cmd = 0;
 }
 
-static uint32_t pflash_read (pflash_t *pfl, uint32_t offset, int width)
+static uint32_t pflash_read (struct pflash_cfi02_t *pfl, uint32_t offset, int width)
 {
-    uint32_t boff;
+    uint32_t boff, _boff;
     uint32_t ret;
     uint8_t *p;
 
-    DPRINTF("%s: offset " TARGET_FMT_lx "\n", __func__, offset);
     ret = -1;
     offset -= pfl->base;
     if (pfl->rom_mode) {
@@ -119,7 +170,7 @@
             pflash_register_memory(pfl, 1);
     }
     offset &= pfl->chip_len - 1;
-    boff = offset & 0xFF;
+    boff = _boff = offset & 0xFF;
     if (pfl->width == 2)
         boff = boff >> 1;
     else if (pfl->width == 4)
@@ -136,32 +187,34 @@
     flash_read:
         /* Flash area read */
         p = pfl->storage;
+        p += offset;
+common_read:
         switch (width) {
         case 1:
-            ret = p[offset];
+            ret = p[0];
 //            DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
             break;
         case 2:
 #if defined(TARGET_WORDS_BIGENDIAN)
-            ret = p[offset] << 8;
-            ret |= p[offset + 1];
+            ret = p[0] << 8;
+            ret |= p[1];
 #else
-            ret = p[offset];
-            ret |= p[offset + 1] << 8;
+            ret = p[0];
+            ret |= p[1] << 8;
 #endif
 //            DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
             break;
         case 4:
 #if defined(TARGET_WORDS_BIGENDIAN)
-            ret = p[offset] << 24;
-            ret |= p[offset + 1] << 16;
-            ret |= p[offset + 2] << 8;
-            ret |= p[offset + 3];
+            ret = p[0] << 24;
+            ret |= p[1] << 16;
+            ret |= p[2] << 8;
+            ret |= p[3];
 #else
-            ret = p[offset];
-            ret |= p[offset + 1] << 8;
-            ret |= p[offset + 2] << 16;
-            ret |= p[offset + 3] << 24;
+            ret = p[0];
+            ret |= p[1] << 8;
+            ret |= p[2] << 16;
+            ret |= p[3] << 24;
 #endif
 //            DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
             break;
@@ -199,16 +252,81 @@
         break;
     case 0x98:
         /* CFI query mode */
-        if (boff > pfl->cfi_len)
+        if (_boff > pfl->cfi_len)
             ret = 0;
-        else
-            ret = pfl->cfi_table[boff];
+        else {
+#ifndef	USE_SIMPLE_TABLE
+            ret = pfl->cfi_table[_boff];
+#else
+	    p = &pfl->cfi_table[_boff];
+	    goto common_read;
+#endif
+	}
         break;
     }
 
+    DPRINTF("%s: offset " TARGET_FMT_lx " value 0x%x width %d func %x\n", __func__, _boff, ret, width, pfl->cmd);
     return ret;
 }
 
+#if 0
+/* 
+ * update flash content on disk 
+ * Act as intermediate between
+ * - 32B from Driver 
+ * - 512B of device
+ * Optimize only when length==32 & Write only when sector is crossed
+ * Need a timer that commit if no more write? No if we write every 512B
+ * RAM is always updated 
+ */
+#define PFLASH_512BLOCK 8	/* 0 -> sector of 512B, 1 -> sector of 1024B, ... */
+#define PFLASH_XBLOCK	(9 + PFLASH_512BLOCK)
+#define PFLASH_XBOUND	((1 << PFLASH_XBLOCK)-1)
+static inline void pflash_update(struct pflash_cfi02_t *pfl, int offset,
+                          int size)
+{
+	int offset_end;
+	int grouped = 0; /* Current version is linked with internal cache */
+	int forcefl = 0; /* Force flush after all op */
+
+	if (pfl->bs) {
+		int _offset = offset;
+		offset = offset >> PFLASH_XBLOCK;
+
+		/* Try to group sequential write
+		 * Stop also if found a sector bound! (so avoid timer for now) */
+		if (size == 32) {
+			forcefl = (((_offset+size)&PFLASH_XBOUND/*511*/)==0);
+			if (pfl->pendingW) {
+				/* Try to group with previous write() */
+				grouped = (offset == pfl->curSector);
+			} else {
+				/* First write in this group */
+				pfl->fstSector = offset;
+				pfl->curSector = offset;
+				pfl->pendingW = 1;
+				grouped = 1;
+			}
+		}
+
+		/* Commit any pending write if the case. */
+		if ((pfl->pendingW && !grouped) || forcefl) {
+			DPRINTF("%s: flushing sectors " TARGET_FMT_lx " ns %d\n", __func__, pfl->fstSector, pfl->curSector - pfl->fstSector + 1);
+			bdrv_write(pfl->bs, pfl->fstSector<<PFLASH_512BLOCK, pfl->storage + (pfl->fstSector << (PFLASH_XBLOCK)), (pfl->curSector - pfl->fstSector + 1)<<(PFLASH_512BLOCK));
+			pfl->pendingW = 0;
+		}
+
+		/* Current write (!in case we just do it with previous commit) */
+		if (!grouped) {
+			offset_end = _offset + size;
+			/* round to sectors */
+			offset_end = (offset_end + 511) >> 9;
+			DPRINTF("%s: flushing sectors " TARGET_FMT_lx " ns %d\n", __func__, offset, offset_end - offset);
+			bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9), offset_end - offset);
+		}
+	}
+}
+#endif
 /* update flash content on disk */
 static void pflash_update(pflash_t *pfl, int offset,
                           int size)
@@ -224,7 +342,7 @@
     }
 }
 
-static void pflash_write (pflash_t *pfl, uint32_t offset, uint32_t value,
+static void pflash_write (struct pflash_cfi02_t *pfl, uint32_t offset, uint32_t value,
                           int width)
 {
     uint32_t boff;
@@ -232,31 +350,32 @@
     uint8_t cmd;
 
     cmd = value;
-    if (pfl->cmd != 0xA0 && cmd == 0xF0) {
-#if 0
-        DPRINTF("%s: flash reset asked (%02x %02x)\n",
-                __func__, pfl->cmd, cmd);
+    if (pfl->cmd != 0xA0 && pfl->cmd != 0x25 && cmd == 0xF0) {
+#if 1
+        DPRINTF("%s: flash reset asked, cmd(%02x) " TARGET_FMT_lx " %x %d\n",
+                __func__, pfl->cmd, (char*)offset-(char*)pfl->storage, value, width);
 #endif
         goto reset_flash;
     }
-    DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d %d\n", __func__,
-            offset, value, width, pfl->wcycle);
+
     offset -= pfl->base;
     offset &= pfl->chip_len - 1;
 
-    DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d\n", __func__,
-            offset, value, width);
     boff = offset & (pfl->sector_len - 1);
     if (pfl->width == 2)
         boff = boff >> 1;
     else if (pfl->width == 4)
         boff = boff >> 2;
+
+    DPRINTF("%s: offset " TARGET_FMT_lx " val %x wid %d wcycle %d\n", __func__,
+	    boff, value, width, pfl->wcycle);
+
     switch (pfl->wcycle) {
     case 0:
         /* Set the device in I/O access mode if required */
         if (pfl->rom_mode)
             pflash_register_memory(pfl, 0);
-        /* We're in read mode */
+    /* We're in read mode */
     check_unlock0:
         if (boff == 0x55 && cmd == 0x98) {
         enter_CFI_mode:
@@ -265,26 +384,40 @@
             pfl->cmd = 0x98;
             return;
         }
-        if (boff != pfl->unlock_addr[0] || cmd != 0xAA) {
+        if (!(boff == pfl->unlock_addr[0] || boff == 0x5555) || cmd != 0xAA) {
             DPRINTF("%s: unlock0 failed " TARGET_FMT_lx " %02x %04x\n",
                     __func__, boff, cmd, pfl->unlock_addr[0]);
             goto reset_flash;
         }
         DPRINTF("%s: unlock sequence started\n", __func__);
+	pfl->wcycle++;
         break;
     case 1:
         /* We started an unlock sequence */
     check_unlock1:
-        if (boff != pfl->unlock_addr[1] || cmd != 0x55) {
+        if (!(boff == pfl->unlock_addr[1] || boff == 0x2AAA) || cmd != 0x55) {
             DPRINTF("%s: unlock1 failed " TARGET_FMT_lx " %02x\n", __func__,
                     boff, cmd);
             goto reset_flash;
         }
         DPRINTF("%s: unlock sequence done\n", __func__);
+	pfl->wcycle++;
         break;
     case 2:
+	/* Command write buffer load. */
+	if (boff==0 && cmd==0x25) {
+		/* Wait #word - 1 on same addr */
+		DPRINTF("%s: start buffer load, expect len4next write\n", __func__);
+		pfl->buffer_loading=1;
+		pfl->wcycle--;
+	} else if (pfl->buffer_loading) {
+		pfl->bytes2write=(value+1)<<(width>>1);
+		DPRINTF("%s: got %d bytes2write\n", __func__, pfl->bytes2write);
+		pfl->wcycle++;
+		break;
+	} else 
         /* We finished an unlock sequence */
-        if (!pfl->bypass && boff != pfl->unlock_addr[0]) {
+        if (!pfl->bypass && (boff != pfl->unlock_addr[0] && boff != 0x5555)) {
             DPRINTF("%s: command failed " TARGET_FMT_lx " %02x\n", __func__,
                     boff, cmd);
             goto reset_flash;
@@ -296,19 +429,29 @@
         case 0x80:
         case 0x90:
         case 0xA0:
-            pfl->cmd = cmd;
-            DPRINTF("%s: starting command %02x\n", __func__, cmd);
-            break;
+        case 0x25:
+	    pfl->cmd = cmd;
+	    DPRINTF("%s: starting command %02x\n", __func__, cmd);
+	    break;
         default:
             DPRINTF("%s: unknown command %02x\n", __func__, cmd);
             goto reset_flash;
         }
-        break;
+	pfl->wcycle++;
+	break;
     case 3:
         switch (pfl->cmd) {
         case 0x80:
             /* We need another unlock sequence */
             goto check_unlock0;
+        case 0x25:
+	    if (pfl->bytes2write==0) {
+		    printf("%s: buffer mode have 0 bytes2write!\n", __func__);
+		    goto reset_flash;
+	    }
+	    if (!pfl->buffer_loading_start)
+		    pfl->buffer_loading_start=offset;
+	    pfl->buffer_loading_end=offset+width;
         case 0xA0:
             DPRINTF("%s: write data offset " TARGET_FMT_lx " %08x %d\n",
                     __func__, offset, value, width);
@@ -343,11 +486,24 @@
                 pflash_update(pfl, offset, 4);
                 break;
             }
-            pfl->status = 0x00 | ~(value & 0x80);
-            /* Let's pretend write is immediate */
-            if (pfl->bypass)
-                goto do_bypass;
-            goto reset_flash;
+	    /* In buffer load mode, status is available only after requested bytes2write */
+	    if (pfl->cmd!=0x25) {
+		    pfl->status = 0x00 | ~(value & 0x80);
+		    /* Let's pretend write is immediate */
+		    if (pfl->bypass)
+			goto do_bypass;
+		    goto reset_flash;
+	    } else {
+		    if (pfl->bytes2write>=width)
+			    pfl->bytes2write-=width;
+		    else {
+			    printf("%s: buffer mode discarded %d bytes at end of transfer\n",__func__,width-pfl->bytes2write);
+			    pfl->bytes2write=0;
+		    }
+		    if (pfl->bytes2write)
+			    pfl->wcycle--; /* Dangerous if use timer */
+	    }
+	    break;
         case 0x90:
             if (pfl->bypass && cmd == 0x00) {
                 /* Unlock bypass reset */
@@ -362,6 +518,8 @@
                     __func__, pfl->cmd);
             goto reset_flash;
         }
+	pfl->wcycle++;
+	break;
     case 4:
         switch (pfl->cmd) {
         case 0xA0:
@@ -370,6 +528,13 @@
             return;
         case 0x80:
             goto check_unlock1;
+        case 0x25:
+	    DPRINTF("%s: Buffer load terminated %s (%d bytes remaining)\n", __func__, !pfl->bytes2write?"Ok" : "NOK", pfl->bytes2write);
+	    /* Update written sequence 
+	    pflash_update(pfl, pfl->buffer_loading_start, pfl->buffer_loading_end - pfl->buffer_loading_start); */
+	    pfl->buffer_loading = 0;
+	    pfl->buffer_loading_start = 0;
+	    goto reset_flash;
         default:
             /* Should never happen */
             DPRINTF("%s: invalid command state %02x (wc 4)\n",
@@ -436,18 +601,15 @@
         DPRINTF("%s: invalid write state (wc 7)\n",  __func__);
         goto reset_flash;
     }
-    pfl->wcycle++;
-
     return;
 
     /* Reset flash */
  reset_flash:
-    pfl->bypass = 0;
-    pfl->wcycle = 0;
-    pfl->cmd = 0;
+    pflash_cfi02_core_reset(pfl);
     return;
 
  do_bypass:
+    DPRINTF("Requesting bypass\n");
     pfl->wcycle = 2;
     pfl->cmd = 0;
     return;
@@ -461,14 +623,14 @@
 
 static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr)
 {
-    pflash_t *pfl = opaque;
+    struct pflash_cfi02_t *pfl = opaque;
 
     return pflash_read(pfl, addr, 2);
 }
 
 static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr)
 {
-    pflash_t *pfl = opaque;
+    struct pflash_cfi02_t *pfl = opaque;
 
     return pflash_read(pfl, addr, 4);
 }
@@ -482,7 +644,7 @@
 static void pflash_writew (void *opaque, target_phys_addr_t addr,
                            uint32_t value)
 {
-    pflash_t *pfl = opaque;
+    struct pflash_cfi02_t *pfl = opaque;
 
     pflash_write(pfl, addr, value, 2);
 }
@@ -490,7 +652,7 @@
 static void pflash_writel (void *opaque, target_phys_addr_t addr,
                            uint32_t value)
 {
-    pflash_t *pfl = opaque;
+    struct pflash_cfi02_t *pfl = opaque;
 
     pflash_write(pfl, addr, value, 4);
 }
@@ -507,6 +669,7 @@
     &pflash_readl,
 };
 
+#ifndef USE_SIMPLE_TABLE
 /* Count trailing zeroes of a 32 bits quantity */
 static int ctz32 (uint32_t n)
 {
@@ -540,15 +703,15 @@
 
     return ret;
 }
-
+#endif
 pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off,
                                 BlockDriverState *bs, uint32_t sector_len,
                                 int nb_blocs, int nb_mappings, int width,
                                 uint16_t id0, uint16_t id1,
                                 uint16_t id2, uint16_t id3,
-                                uint16_t unlock_addr0, uint16_t unlock_addr1)
+				uint16_t unlock_addr0, uint16_t unlock_addr1)
 {
-    pflash_t *pfl;
+    struct pflash_cfi02_t *pfl;
     int32_t chip_len;
 
     chip_len = sector_len * nb_blocs;
@@ -558,9 +721,11 @@
         total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
         return NULL;
 #endif
-    pfl = qemu_mallocz(sizeof(pflash_t));
+    pfl = qemu_mallocz(sizeof(struct pflash_cfi02_t));
     if (pfl == NULL)
         return NULL;
+
+    /* One time initialization, HOST related. */
     pfl->storage = phys_ram_base + off;
     pfl->fl_mem = cpu_register_io_memory(0, pflash_read_ops, pflash_write_ops,
                                          pfl);
@@ -574,7 +739,7 @@
         /* read the initial flash content */
         bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
     }
-#if 0 /* XXX: there should be a bit to set up read-only,
+    #if 0 /* XXX: there should be a bit to set up read-only,
        *      the same way the hardware does (with WP pin).
        */
     pfl->ro = 1;
@@ -593,6 +758,7 @@
     pfl->ident[3] = id3;
     pfl->unlock_addr[0] = unlock_addr0;
     pfl->unlock_addr[1] = unlock_addr1;
+#ifndef USE_SIMPLE_TABLE 
     /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
     pfl->cfi_len = 0x52;
     /* Standard "QRY" string */
@@ -621,22 +787,22 @@
     pfl->cfi_table[0x1E] = 0x00;
     /* Reserved */
     pfl->cfi_table[0x1F] = 0x07;
-    /* Timeout for min size buffer write (NA) */
-    pfl->cfi_table[0x20] = 0x00;
+    /* Timeout for min size buffer write (16 s) */
+    pfl->cfi_table[0x20] = 0x04;
     /* Typical timeout for block erase (512 ms) */
     pfl->cfi_table[0x21] = 0x09;
     /* Typical timeout for full chip erase (4096 ms) */
     pfl->cfi_table[0x22] = 0x0C;
     /* Reserved */
     pfl->cfi_table[0x23] = 0x01;
-    /* Max timeout for buffer write (NA) */
-    pfl->cfi_table[0x24] = 0x00;
+    /* Max timeout for buffer write */
+    pfl->cfi_table[0x24] = 0x04;
     /* Max timeout for block erase */
     pfl->cfi_table[0x25] = 0x0A;
     /* Max timeout for chip erase */
     pfl->cfi_table[0x26] = 0x0D;
     /* Device size */
-    pfl->cfi_table[0x27] = ctz32(chip_len);
+    pfl->cfi_table[0x27] = ctz32(chip_len) + 1;
     /* Flash device interface (8 & 16 bits) */
     pfl->cfi_table[0x28] = 0x02;
     pfl->cfi_table[0x29] = 0x00;
@@ -652,7 +818,7 @@
     pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
     pfl->cfi_table[0x2F] = sector_len >> 8;
     pfl->cfi_table[0x30] = sector_len >> 16;
-
+    
     /* Extended */
     pfl->cfi_table[0x31] = 'P';
     pfl->cfi_table[0x32] = 'R';
@@ -660,7 +826,6 @@
 
     pfl->cfi_table[0x34] = '1';
     pfl->cfi_table[0x35] = '0';
-
     pfl->cfi_table[0x36] = 0x00;
     pfl->cfi_table[0x37] = 0x00;
     pfl->cfi_table[0x38] = 0x00;
@@ -670,6 +835,31 @@
 
     pfl->cfi_table[0x3b] = 0x00;
     pfl->cfi_table[0x3c] = 0x00;
+#else
+    /* Don't know why but driver expect this 
+     * Now this table have !to be 'interpolated' basing on width.
+     * Moreover we were called with width == 4. */
+    pfl->cfi_len = 0x80;
+    pfl->cfi_table[0x21] = 'Q';
+    pfl->cfi_table[0x23] = 'R';
+    pfl->cfi_table[0x25] = 'Y';
+    pfl->cfi_table[0x27] = 2;		/* oam command set */
+    pfl->cfi_table[0x4F] = 25; 		/* size = 2^x Bytes*/
+    pfl->cfi_table[0x59] = 1;		/* num of blocks   */
+    pfl->cfi_table[0x5b] = 255;		/*        sectors  */
+    pfl->cfi_table[0x61] = 2;		/* sector size	   */
+#endif
 
-    return pfl;
+#if 0
+    /* Per reset initialization, GUEST related.
+     *
+     * Reset action is needed, since memory attribute is changed over the writing process
+     * @reset we must@least read from flash.
+     * Actually we stop all started action (reset chip)
+     */
+    pflash_cfi02_reset(pfl);
+    qemu_register_reset(&pflash_cfi02_reset, pfl);
+#endif
+    
+    return (pflash_t*)pfl;
 }
Index: hw/ppc405_uc.c
===================================================================
--- hw/ppc405_uc.c	(revision 5720)
+++ hw/ppc405_uc.c	(working copy)
@@ -21,6 +21,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+#include <assert.h>
 #include "hw.h"
 #include "ppc.h"
 #include "ppc405.h"
@@ -28,13 +29,17 @@
 #include "qemu-timer.h"
 #include "sysemu.h"
 #include "qemu-log.h"
+#include "net.h" /* To have nd_table.h */
 
+extern int loglevel;
+extern FILE *logfile;
+
 #define DEBUG_OPBA
 #define DEBUG_SDRAM
-#define DEBUG_GPIO
+/*#define DEBUG_GPIO*/
 #define DEBUG_SERIAL
 #define DEBUG_OCM
-//#define DEBUG_I2C
+/*#define DEBUG_I2C*/
 #define DEBUG_GPT
 #define DEBUG_MAL
 #define DEBUG_CLOCKS
@@ -835,6 +840,8 @@
             ret = 0x00000000;
             break;
         }
+	printf("Read ebc[%d]=>%x\n", ebc->addr, ret);
+	break;
     default:
         ret = 0x00000000;
         break;
@@ -853,6 +860,7 @@
         ebc->addr = val;
         break;
     case EBC0_CFGDATA:
+	printf("Write ebc[%d]=>%x\n", ebc->addr, val);
         switch (ebc->addr) {
         case 0x00: /* B0CR */
             break;
@@ -1395,7 +1403,37 @@
 }
 
 /*****************************************************************************/
-/* I2C controller */
+/* 
+ * I2C controller (SEE ALSO IN i2c.h before choice!!!)
+ *
+ * Control many device through uP resource.
+ * User could implement their device linked with this bus,
+ * specializing simple read(offset,value)/writec(offset,value)
+ * Only 7 bit address mode is supported.
+ */
+#define I2C_VER2
+/* Generic function like:
+ * hw/hw.h
+	* These should really be in isa.h, but are here to make pc.h happy.  *
+	typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data);
+	typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address);
+ *
+ * cpu-all.h
+	typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
+	typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
+ *
+ * qemu-common.h
+	typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
+	typedef int IOCanRWHandler(void *opaque);
+	typedef void IOHandler(void *opaque);
+
+	typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size);
+
+   THERE !EXIST SIMPLE int read(int fd, const char* pos, int len), CREATE IT
+ */
+KRHandler* readF=NULL;
+KRHandler* writeF=NULL;
+
 typedef struct ppc4xx_i2c_t ppc4xx_i2c_t;
 struct ppc4xx_i2c_t {
     target_phys_addr_t base;
@@ -1415,21 +1453,82 @@
     uint8_t xfrcnt;
     uint8_t xtcntlss;
     uint8_t directcntl;
+
+    /* This is treated like a FIFO with same OPB beat (!implemented) */
+    uint8_t dataLen;
+    uint8_t data[4]; /* This is on PPC405GPr, uP dependent */
+    void* opaque;
 };
 
+/* 
+ * This method simulate the insertion of a byte on the bus,
+ * and acquisition in uP registers.
+ *
+ * Return number of readed bytes
+ * FIX: Should be linked to OPB clock
+ */
+static uint32_t ppc4xx_i2c_pushb (ppc4xx_i2c_t *i2c, uint8_t val)
+{
+	uint32_t ret = 0;
+
+	assert(i2c->dataLen<=sizeof(i2c->data));
+	/* Check if space */
+	if (i2c->dataLen<sizeof(i2c->data)) {
+		i2c->data[i2c->dataLen] = val;
+		i2c->dataLen++;
+		/* Signal to uP that data exist */
+		i2c->sts |= 0x20;
+		ret=1;
+	} else {
+		/* Signal to uP that no more space exist */
+		i2c->sts |= 0x10;
+	}
+	/* Always read a byte, also need different read/write cursor? */
+	return ret;
+}
+
+static uint8_t ppc4xx_i2c_popb (ppc4xx_i2c_t *i2c)
+{
+	uint8_t ret = 0;
+
+	assert(i2c->dataLen<=sizeof(i2c->data));
+	if (i2c->dataLen>0) {
+		uint8_t i;
+		ret = i2c->data[0];
+		i2c->dataLen--;
+		for (i=0; i<(sizeof(i2c->data)-1); i++)
+			i2c->data[i] = i2c->data[i+1];
+	}
+
+	if (i2c->dataLen<=0) {
+		/* Signal to uP that no more data exist */
+		i2c->sts &= ~0x30;
+	}
+	return ret;
+}
+
 static uint32_t ppc4xx_i2c_readb (void *opaque, target_phys_addr_t addr)
 {
     ppc4xx_i2c_t *i2c;
     uint32_t ret;
 
+#if 0
 #ifdef DEBUG_I2C
     printf("%s: addr " PADDRX "\n", __func__, addr);
 #endif
+#endif
     i2c = opaque;
     switch (addr - i2c->base) {
     case 0x00:
+#ifndef I2C_VER2  
         //        i2c_readbyte(&i2c->mdata);
         ret = i2c->mdata;
+#else
+#ifdef DEBUG_I2C
+	printf("I2C read %x (hnd %p)\n", i2c->mdata, readF);
+#endif
+	ret = ppc4xx_i2c_popb(i2c);
+#endif
         break;
     case 0x02:
         ret = i2c->sdata;
@@ -1477,9 +1576,11 @@
         ret = 0x00;
         break;
     }
+#if 0
 #ifdef DEBUG_I2C
     printf("%s: addr " PADDRX " %02" PRIx32 "\n", __func__, addr, ret);
 #endif
+#endif
 
     return ret;
 }
@@ -1489,14 +1590,20 @@
 {
     ppc4xx_i2c_t *i2c;
 
+#if  0
 #ifdef DEBUG_I2C
     printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value);
 #endif
+#endif
     i2c = opaque;
     switch (addr - i2c->base) {
     case 0x00:
+#ifndef I2C_VER2  
         i2c->mdata = value;
         //        i2c_sendbyte(&i2c->mdata);
+#else
+	ppc4xx_i2c_pushb(i2c, (char)value);
+#endif
         break;
     case 0x02:
         i2c->sdata = value;
@@ -1509,6 +1616,62 @@
         break;
     case 0x06:
         i2c->cntl = value;
+#ifdef I2C_VER2
+	/* IIC0_CNTL (8bits)
+	 * -----------------
+	 * HMT 	0x80	Halt master Transfer
+	 * AMD 	0x40	Address MoDe
+	 * TCT 	0x30	Transfer CounT
+	 * RPST	0x08	RePeated STart
+	 * CHT 	0x04	CHain Tranfer
+	 * RW  	0x02	ReadWrite
+	 * PT	0x01	PendingTransfer
+	 *
+	 * State/Action
+	 * HMT RPSP 	CHT 	PT
+	 * 0	-	-	0	No action
+	 * 0	0	1	1	Start, Transfer, ACK on last byte, Pause
+	 * 0	0	0	1			NACK 		 , Stop
+	 * 0	1	x	1					 , Wait
+	 * 1	x	x	x	NACK on current byte, Stop
+	 */
+	if (value & 0x81) {
+		unsigned char len, lettu, aspe;
+		int addr;
+		size_t ret;
+		len = ((value >> 4) & 3) + 1;
+		lettu = (value & 2);
+		aspe = (value & 1);
+		addr = i2c->lmadr;
+		ret = 0;
+#ifdef DEBUG_I2C
+		printf("(I2C command len %d, %s, start %s pending transfer)\n", len, lettu?"READING":"WRITING", aspe?"only if not":"also if");
+#endif
+		assert(len<=sizeof(i2c->data));
+		if (lettu && readF) {
+			ret = readF(addr, i2c->data, len);
+		}
+		if (!lettu && writeF) {
+			ret = writeF(addr, i2c->data, len);
+		}
+#ifdef DEBUG_I2C
+		printf("User command return %d\n", ret);
+#endif
+		/* 
+		 * on read from device 	=> Push bytes in uP buffer (simulate controller acquiring)
+		 * on write ...		=> Pop bytes from uP buffer (simulate device reading)
+		 */
+		{
+			unsigned int l;
+			for (l=0; l<ret; l++) {
+				lettu ?
+					ppc4xx_i2c_pushb(i2c, i2c->data[l]) : 	/* Produce data for uP */
+					ppc4xx_i2c_popb(i2c) ;			/* Consume data for uP */
+			}
+
+		}
+	}
+#endif
         break;
     case 0x07:
         i2c->mdcntl = value & 0xDF;
@@ -1967,6 +2130,15 @@
     uint32_t rcbs[2];
 };
 
+/* TODO: need interaction between device!
+ * very difficult since all struct is in .c file */
+uint32_t txctpr[4];
+uint32_t rxctpr[2];
+uint32_t rcbs[2];
+uint32_t *rxeobisr;
+uint32_t *txeobisr;
+uint32_t *rxcasr;
+
 static void ppc40x_mal_reset (void *opaque);
 
 static target_ulong dcr_read_mal (void *opaque, int dcrn)
@@ -2067,21 +2239,29 @@
         mal->txcarr = val & 0xF0000000;
         break;
     case MAL0_TXEOBISR:
-        /* Read/clear */
-        mal->txeobisr &= ~val;
+        /* Read/clear TODO SEPARATE CHANNEL */
+	if (mal->txeobisr & val & 0xC0000000) {
+		mal->txeobisr &= ~val;
+		qemu_irq_lower(mal->irqs[0]);
+	}
+	mal->txeobisr &= ~val;
         break;
     case MAL0_TXDEIR:
         /* Read/clear */
         mal->txdeir &= ~val;
         break;
     case MAL0_RXCASR:
-        mal->rxcasr = val & 0xC0000000;
+        mal->rxcasr = val & ~0xC0000000; /* Active Set immediately rx channel */
         break;
     case MAL0_RXCARR:
         mal->rxcarr = val & 0xC0000000;
         break;
     case MAL0_RXEOBISR:
         /* Read/clear */
+	if (mal->rxeobisr & val & 0x80000000) {
+		mal->rxeobisr &= ~val;
+		qemu_irq_lower(mal->irqs[1]);
+	}
         mal->rxeobisr &= ~val;
         break;
     case MAL0_RXDEIR:
@@ -2101,6 +2281,7 @@
         idx = 3;
     update_tx_ptr:
         mal->txctpr[idx] = val;
+	txctpr[idx] = val;
         break;
     case MAL0_RXCTP0R:
         idx = 0;
@@ -2109,6 +2290,7 @@
         idx = 1;
     update_rx_ptr:
         mal->rxctpr[idx] = val;
+        rxctpr[idx] = val;
         break;
     case MAL0_RCBS0:
         idx = 0;
@@ -2117,6 +2299,7 @@
         idx = 1;
     update_rx_size:
         mal->rcbs[idx] = val & 0x000000FF;
+        rcbs[idx] = val & 0x000000FF;
         break;
     }
 }
@@ -2135,6 +2318,10 @@
     mal->txcasr = 0x00000000;
     mal->txdeir = 0x00000000;
     mal->txeobisr = 0x00000000;
+
+    rxeobisr = &(mal->rxeobisr);
+    txeobisr = &(mal->txeobisr);
+    rxcasr = &(mal->rxcasr);
 }
 
 void ppc405_mal_init (CPUState *env, qemu_irq irqs[4])
@@ -2234,6 +2421,7 @@
     qemu_system_reset_request();
 }
 
+/* ->halted=1 seem that loose memory. */
 void store_40x_dbcr0 (CPUState *env, uint32_t val)
 {
     switch ((val >> 28) & 0x3) {
@@ -2891,7 +3079,8 @@
     }
 }
 
-CPUState *ppc405ep_init (target_phys_addr_t ram_bases[2],
+CPUState *ppc405xp_init (const char* cpu_model,
+                         target_phys_addr_t ram_bases[2],
                          target_phys_addr_t ram_sizes[2],
                          uint32_t sysclk, qemu_irq **picp,
                          ram_addr_t *offsetp, int do_init)
@@ -2906,7 +3095,9 @@
 
     memset(clk_setup, 0, sizeof(clk_setup));
     /* init CPUs */
-    env = ppc4xx_init("405ep", &clk_setup[PPC405EP_CPU_CLK],
+    if (!cpu_model)
+	    cpu_model = "405ep";
+    env = ppc4xx_init(cpu_model, &clk_setup[PPC405EP_CPU_CLK],
                       &tlb_clk_setup, sysclk);
     clk_setup[PPC405EP_CPU_CLK].cb = tlb_clk_setup.cb;
     clk_setup[PPC405EP_CPU_CLK].opaque = tlb_clk_setup.opaque;
@@ -2970,7 +3161,11 @@
     mal_irqs[2] = pic[13];
     mal_irqs[3] = pic[14];
     ppc405_mal_init(env, mal_irqs);
-    /* Ethernet */
+    /* EMAC (Ethernet Media Access Controller) */
+    {
+    	void ppc4xx_emac_init(CPUState *env, ppc4xx_mmio_t *mmio, target_phys_addr_t offset, NICInfo* nic, qemu_irq irq[4]);
+    	ppc4xx_emac_init(env, mmio, 0x800, &nd_table[0], mal_irqs);
+    }
     /* Uses pic[9], pic[15], pic[17] */
     /* CPU control */
     ppc405ep_cpc_init(env, clk_setup, sysclk);
Index: hw/ppc4xx_devs.c
===================================================================
--- hw/ppc4xx_devs.c	(revision 5720)
+++ hw/ppc4xx_devs.c	(working copy)
@@ -26,6 +26,7 @@
 #include "ppc4xx.h"
 #include "sysemu.h"
 #include "qemu-log.h"
+#include "qemu-timer.h"
 
 //#define DEBUG_MMIO
 //#define DEBUG_UNASSIGNED
@@ -296,10 +297,10 @@
     cr = uic->uicsr & uic->uicer & uic->uiccr;
 #ifdef DEBUG_UIC
     if (loglevel & CPU_LOG_INT) {
-        fprintf(logfile, "%s: uicsr %08" PRIx32 " uicer %08" PRIx32
+        fprintf(logfile, "%s %" PRIu64 ": uicsr %08" PRIx32 " uicer %08" PRIx32
                 " uiccr %08" PRIx32 "\n"
                 "   %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
-                __func__, uic->uicsr, uic->uicer, uic->uiccr,
+                __func__, qemu_get_clock(rt_clock), uic->uicsr, uic->uicer, uic->uiccr,
                 uic->uicsr & uic->uicer, ir, cr);
     }
 #endif
@@ -366,9 +367,9 @@
     mask = 1 << (31-irq_num);
 #ifdef DEBUG_UIC
     if (loglevel & CPU_LOG_INT) {
-        fprintf(logfile, "%s: irq %d level %d uicsr %08" PRIx32
+        fprintf(logfile, "%s %" PRIu64 ": irq %d level %d uicsr %08" PRIx32
                 " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
-                __func__, irq_num, level,
+                __func__, qemu_get_clock(rt_clock), irq_num, level,
                 uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
     }
 #endif
Index: hw/ppc405.h
===================================================================
--- hw/ppc405.h	(revision 5720)
+++ hw/ppc405.h	(working copy)
@@ -97,7 +97,8 @@
                          target_phys_addr_t ram_sizes[4],
                          uint32_t sysclk, qemu_irq **picp,
                          ram_addr_t *offsetp, int do_init);
-CPUState *ppc405ep_init (target_phys_addr_t ram_bases[2],
+CPUState *ppc405xp_init (const char* cpu_model,
+			 target_phys_addr_t ram_bases[2],
                          target_phys_addr_t ram_sizes[2],
                          uint32_t sysclk, qemu_irq **picp,
                          ram_addr_t *offsetp, int do_init);
Index: hw/boards.h
===================================================================
--- hw/boards.h	(revision 5720)
+++ hw/boards.h	(working copy)
@@ -38,6 +38,7 @@
 extern QEMUMachine heathrow_machine;
 extern QEMUMachine ref405ep_machine;
 extern QEMUMachine taihu_machine;
+extern QEMUMachine walnut_machine;
 
 /* mips_r4k.c */
 extern QEMUMachine mips_machine;
Index: pc-bios/u-boot.bin
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: pc-bios/u-boot.bin
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream




      Unisciti alla community di Io fotografo e video, il nuovo corso di fotografia di Gazzetta dello sport:
http://www.flickr.com/groups/iofotografoevideo

[-- Attachment #2: u-boot.bin.gz --]
[-- Type: application/x-gzip, Size: 90030 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] PowerPC 4xx enhacement
  2008-11-21  1:53 ` [Qemu-devel] PowerPC 4xx enhacement Salvatore Lionetti
@ 2008-11-21 11:40   ` Jean-Christophe PLAGNIOL-VILLARD
  2008-11-21 16:40   ` [Qemu-devel] DHT Walnut board support Hollis Blanchard
  2008-11-21 17:09   ` [Qemu-devel] PowerPC 4xx EMAC emulation Hollis Blanchard
  2 siblings, 0 replies; 5+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2008-11-21 11:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hollis Blanchard

On 01:53 Fri 21 Nov     , Salvatore Lionetti wrote:
> Source modified
> ===============
> - code factoring for ppc405GPr, ppc405EP, ppc405GPe
> - reorganization in i2c code, with possibility of board customization callback
> - new board added for 'DHT Walnut board', fw in pc-bios/walnut_uboot.bin
> - cfi flash now support 'buffer mode', write N {byte, halfword, word} consecutively.
> - emac/mal now work with vlan mode
> 
> Board Walnut:
> =============
> http://elinux.org/DHT-Walnut-Flameman
> 
> CPU PowerPC 405 GPe running at 266Mhz
> LAN On-chip 405GP ethernet, namely emac/mal
> ROM 512k of boot flash, AMD 29LV040B
> PID eeprom via i2c
> 
> Full Tested (eth, flash, pid)
> =============================
> - u-boot 1.1.6 on walnut board (with few patch, see ppc405_board.c)
I'll provide the DHT-Walnut U-Boot 2008.10 instead of the old version
> - OSE on proprietary NSN 4G radio module
> - Can anyone test ref405ep and taihu?
> 
> P.S:
> I've tryed to have patch for bin file but no luck
> Since i can not version file, does diff support binary file?does attachment work?
> 
git allow you to do it
> 
> 
> Index: Makefile.target
> ===================================================================
> --- Makefile.target	(revision 5720)
> +++ Makefile.target	(working copy)
> @@ -679,7 +679,7 @@
>  # NewWorld PowerMac
>  OBJS+= unin_pci.o ppc_chrp.o
>  # PowerPC 4xx boards
> -OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
> +OBJS+= pflash_cfi02.o emac.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
>  endif
>  ifeq ($(TARGET_BASE_ARCH), mips)
>  OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
> Index: target-ppc/machine.c
> ===================================================================
> --- target-ppc/machine.c	(revision 5720)
> +++ target-ppc/machine.c	(working copy)
> @@ -8,6 +8,7 @@
>      qemu_register_machine(&prep_machine);
>      qemu_register_machine(&ref405ep_machine);
>      qemu_register_machine(&taihu_machine);
> +    qemu_register_machine(&walnut_machine);
please rename it as dht_walnut which is not the orginal AMCC/IBM Walnut

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] DHT Walnut board support
  2008-11-21  1:53 ` [Qemu-devel] PowerPC 4xx enhacement Salvatore Lionetti
  2008-11-21 11:40   ` Jean-Christophe PLAGNIOL-VILLARD
@ 2008-11-21 16:40   ` Hollis Blanchard
  2008-11-21 17:09   ` [Qemu-devel] PowerPC 4xx EMAC emulation Hollis Blanchard
  2 siblings, 0 replies; 5+ messages in thread
From: Hollis Blanchard @ 2008-11-21 16:40 UTC (permalink / raw)
  To: qemu-devel

On Fri, 2008-11-21 at 01:53 +0000, Salvatore Lionetti wrote:
> Source modified
> ===============
> - code factoring for ppc405GPr, ppc405EP, ppc405GPe
> - reorganization in i2c code, with possibility of board customization callback
> - new board added for 'DHT Walnut board', fw in pc-bios/walnut_uboot.bin
> - cfi flash now support 'buffer mode', write N {byte, halfword, word} consecutively.
> - emac/mal now work with vlan mode

First, you *really* need to split these into separate patches. That will
make it possible to actually review all this code. Each bullet you list
above should be at least one separate patch (and in some cases maybe
more).

> Board Walnut:
> =============
> http://elinux.org/DHT-Walnut-Flameman
> 
> CPU PowerPC 405 GPe running at 266Mhz
> LAN On-chip 405GP ethernet, namely emac/mal
> ROM 512k of boot flash, AMD 29LV040B
> PID eeprom via i2c
> 
> Full Tested (eth, flash, pid)
> =============================
> - u-boot 1.1.6 on walnut board (with few patch, see ppc405_board.c)
> - OSE on proprietary NSN 4G radio module
> - Can anyone test ref405ep and taihu?
> 
> P.S:
> I've tryed to have patch for bin file but no luck
> Since i can not version file, does diff support binary file?does attachment work?
> 
> 
> 
> Index: Makefile.target
> ===================================================================
> --- Makefile.target	(revision 5720)
> +++ Makefile.target	(working copy)
> @@ -679,7 +679,7 @@
>  # NewWorld PowerMac
>  OBJS+= unin_pci.o ppc_chrp.o
>  # PowerPC 4xx boards
> -OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
> +OBJS+= pflash_cfi02.o emac.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
>  endif
>  ifeq ($(TARGET_BASE_ARCH), mips)
>  OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
> Index: osdep.c
> ===================================================================
> --- osdep.c	(revision 5720)
> +++ osdep.c	(working copy)
> @@ -32,6 +32,7 @@
>  #include <sys/types.h>
>  #include <sys/statvfs.h>
>  #endif
> +#include <assert.h>
>  
>  #include "qemu-common.h"
>  #include "sysemu.h"
> @@ -310,3 +311,49 @@
>      fcntl(fd, F_SETFL, f | O_NONBLOCK);
>  }
>  #endif
> +
> +void *qemu_mmap(void * start, size_t length, int prot, int flags, int fd, off_t  offset) {
> +	void *ret;
> +#ifndef _WIN32
> +	ret = mmap(start, length, prot, flags, fd, offset);
> +#else
> +	{ /* TO TEST!!! */
> +		static char name[15];
> +		static char ind=0;
> +		HANDLE fd_map;
> +		LPVOID fd_buf;
> +		char*  buf;
> +
> +		assert(fd!=INVALID_HANDLE_VALUE);
> +
> +		snprintf(name, 15, "qemu_mmap%d", ind++);
> +		fd_map = CreateFileMapping(fd,
> +					   NULL, /* Security attr */
> +					   PAGE_READWRITE,
> +					   0, TOTAL_SIZE,
> +					   name);
> +
> +		fd_buf = MapViewOfFile(fd_map,
> +				       FILE_MAP_ALL_ACCESS,
> +				       0, 0, /* Offset */
> +				       TOTAL_SIZE);
> +		assert(fd_buf);
> +		ret = fd_buf;
> +
> +	}
> +#endif
> +	return ret;
> +}
> +int qemu_munmap(void *start, size_t length) {
> +	int ret;
> +#ifndef _WIN32
> +	ret = munmap(start, length);
> +#else
> +	{ /* TO TEST!!! */
> +		UnmapViewOfFile(start);
> +		/*CloseHandle(fd_map);
> +		CloseHandle(fd);*/
> +	}
> +#endif
> +	return ret;
> +}

Why is this here? You seem to have created a new function that nobody
calls. You should remove it completely.

> Index: target-ppc/machine.c
> ===================================================================
> --- target-ppc/machine.c	(revision 5720)
> +++ target-ppc/machine.c	(working copy)
> @@ -8,6 +8,7 @@
>      qemu_register_machine(&prep_machine);
>      qemu_register_machine(&ref405ep_machine);
>      qemu_register_machine(&taihu_machine);
> +    qemu_register_machine(&walnut_machine);
>  }
>  
>  void cpu_save(QEMUFile *f, void *opaque)
> Index: qemu-common.h
> ===================================================================
> --- qemu-common.h	(revision 5720)
> +++ qemu-common.h	(working copy)
> @@ -152,4 +152,11 @@
>  /* Force QEMU to stop what it's doing and service IO */
>  void qemu_service_io(void);
>  
> +/* KR function prototype. */
> +typedef size_t KRHandler(int fd, char* buf, size_t size);

Aside from the fact that I have no idea what "KR" means, this does not
belong in qemu-common.h.

> +/* mmap like function
> + * (cygwin seem !have aio yet so no linux env under Winzozz) */
> +void *qemu_mmap(void * start, size_t length, int prot, int flags, int fd, off_t  offset);
> +int qemu_munmap(void *start, size_t length);
>  #endif
> Index: hw/ppc405_boards.c
> ===================================================================
> --- hw/ppc405_boards.c	(revision 5720)
> +++ hw/ppc405_boards.c	(working copy)
> @@ -206,7 +206,7 @@
>  #ifdef DEBUG_BOARD_INIT
>      printf("%s: register cpu\n", __func__);
>  #endif
> -    env = ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic, &sram_offset,
> +    env = ppc405xp_init("405ep", ram_bases, ram_sizes, 33333333, &pic, &sram_offset,
>                          kernel_filename == NULL ? 0 : 1);
>      /* allocate SRAM */
>  #ifdef DEBUG_BOARD_INIT

There is no such thing as a 405XP.

> @@ -353,7 +353,6 @@
>      printf("bdloc %016lx %s\n",
>             (unsigned long)bdloc, (char *)(phys_ram_base + bdloc));
>  }
> -
>  QEMUMachine ref405ep_machine = {
>      .name = "ref405ep",
>      .desc = "ref405ep",
> @@ -361,6 +360,349 @@
>      .ram_require = (128 * 1024 * 1024 + 4096 + 512 * 1024 + BIOS_SIZE) | RAMSIZE_FIXED,
>  };
>  
> +/* 
> + * Walnut board
> + *
> + * Starting point from official uboot 1.1.6 with following patch:
> + * - compilation error: cmd_bootm.c:470 manually expand #if
> + * - mac address not in nvram: include/configs/walnut.h add "ethaddr=52:54:00:12:34:56\0" in CONFIG_EXTRA_ENV_SETTING
> + * - remove trailer from recv packet(fcs?): cpu/ppc4xx/4xx_enet.c:1478 call NetReceive with 'length' as 2nd parameter.
> + */
> +/* Addedum related to 405GP: is it only for such device? */
> +static target_ulong walnut_dcr_read (void *opaque, int dcrn)
> +{
> +	target_ulong ret=0;
> +	switch (dcrn) {
> +		case 0xAA: /* CPCO_ECR */
> +			break;
> +		case 0xB0: /* CPC0_PLLMR */
> +			/*                                    |--> {ForwardA, Backward} Divisor
> +			 *                              ----  |--> Opb
> +			 *                        ---> | x2 |-|--> ExtBus
> +			 *            ----        |     ----
> +			 * CPU  ---> | /4 | ---> PLB ------------> PCI
> +			 * Clock      ----
> +			 */
> +			ret  = -2 << 29;/* Forward divisor is 2 	*/
> +			ret |= 2 << 25; /* Backward divisor is 2 	*/
> +			ret |= 3 << 17; /* Cpu/Plb is 4 		*/
> +			ret |= 0 << 13; /* Pci/Plb is 1 		*/
> +			ret |= 0 << 11; /* ExtBus/Plb is 2 		*/
> +			ret |= 1 << 15; /* Opb/Plb is 2			*/
> +			break;
> +		case 0xB1: /* CPC0_CR0 */
> +			break;
> +		case 0xB2: /* CPC0_CR1 */
> +			break;
> +		case 0xB4: /* CPC0_PSR: For same uP model, could be different! */
> +			break;
> +		default:
> +			break;
> +	}
> +/*	printf("walnut_dcr [%x] => %x\n", dcrn, ret);*/
> +	return ret;
> +}
> +static void walnut_dcr_write (void *opaque, int dcrn, target_ulong val)
> +{
> +/*	printf("walnut_dcr [%x] <= %x\n", dcrn, val);*/
> +}

The CPC is an SoC device, not a board device. Accordingly, these
functions shouldn't have "walnut" in the name.

Also, some of these registers (CPC0_CR0, CR1, and PLLMR) are common with
405GP, and some (ECR, PSR) are specific to 405GPr. Some better code
sharing is needed.

> +/* 
> + * I2C Customization
> + *
> + * A file.
> + */
> +static char PID_content[128]; /* If no file present */
> +static uint32_t PID_cursor;

At the very least, these should be part of an I2C structure, so that
multiple I2C controllers can be created.

> +size_t PID_read(int addr, char *buf, size_t len) {
> +	int ret;
> +	ret = len;
> +	if (len+PID_cursor>128)
> +		len = 128 - PID_cursor;
> +	memcpy(buf, &PID_content[PID_cursor], len);

Use a #define for "128".

> +#if 0
> +	{
> +	int i;
> +	printf("PID_Read dev(%x) len %d offs %d =>(", addr, ret, PID_cursor);
> +	for (i=0; i<ret; i++) {
> +		printf(" %x,", buf[i]);
> +	}
> +	printf(") ret %d\n", ret);
> +	}
> +#endif
> +	return ret;
> +}
> +size_t PID_write(int addr, char *buf, size_t len) {
> +	int ret;
> +	ret = len;
> +	/* buf = {offset}                  if write to set position (a read'll follow)
> +	 *     = {offset,data0, data1 ...} if write data different from position */
> +	if (len>0) {
> +		PID_cursor = buf[0];
> +		len--;
> +		if (len>0) {
> +			if (len+PID_cursor>128)
> +				len = 128 - PID_cursor;
> +			memcpy(&PID_content[PID_cursor], buf+1, len);
> +		}
> +	}
> +#if 0
> +	{
> +	int i;
> +	printf("PID_Write dev(0x%x) len %d, off %d <=(", addr, ret, PID_cursor);
> +	for (i=0; i<ret; i++) {
> +		printf(" %x,", buf[i]);
> +	}
> +	printf(")\n");
> +	}
> +#endif
> +
> +	return ret;
> +}
> +static void walnut_init (ram_addr_t ram_size, int vga_ram_size,
> +                         const char *boot_device, DisplayState *ds,
> +                         const char *kernel_filename,
> +                         const char *kernel_cmdline,
> +                         const char *initrd_filename,
> +                         const char *cpu_model)
> +{
> +    ppc4xx_bd_info_t bd;
> +    CPUPPCState *env;
> +    qemu_irq *pic;
> +    ram_addr_t sram_offset, bios_offset, bdloc;
> +    target_phys_addr_t ram_bases[2], ram_sizes[2];
> +    target_ulong sram_size, bios_size;
> +    //int phy_addr = 0;
> +    //static int phy_addr = 1;
> +    target_ulong kernel_base, kernel_size, initrd_base, initrd_size;
> +    int linux_boot;
> +    int fl_idx, fl_sectors, len;
> +    int ppc_boot_device = boot_device[0];
> +    int index;
> +
> +    /* XXX: fix this */
> +    ram_bases[0] = 0x00000000;
> +    ram_sizes[0] = 0x08000000;
> +    ram_bases[1] = 0x00000000;
> +    ram_sizes[1] = 0x00000000;
> +    ram_size = 128 * 1024 * 1024;
> +#ifdef DEBUG_BOARD_INIT
> +    printf("%s: register cpu\n", __func__);
> +#endif
> +    env = ppc405xp_init("405GPe", ram_bases, ram_sizes, 33333333, &pic, &sram_offset,
> +                        kernel_filename == NULL ? 0 : 1);
> +    /* Customize some dcr */
> +    ppc_dcr_register(env, 0xAA/* CPC0_ECR   */, NULL, &walnut_dcr_read, &walnut_dcr_write);
> +    ppc_dcr_register(env, 0xB0/* CPC0_PLLMR */, NULL, &walnut_dcr_read, NULL);
> +    ppc_dcr_register(env, 0xB1/* CPC0_CR0   */, NULL, &walnut_dcr_read, &walnut_dcr_write);
> +    ppc_dcr_register(env, 0xB2/* CPC0_CR1   */, NULL, &walnut_dcr_read, &walnut_dcr_write);
> +    ppc_dcr_register(env, 0xB4/* CPC0_PSR   */, NULL, &walnut_dcr_read, NULL);
> +
> +    /* Customize I2C access */
> +    {
> +	extern KRHandler* readF;
> +	extern KRHandler* writeF;

Get rid of these nested braces.

"readF" and "writeF" are terrible names, especially considering that
they're global! I'm not sure why you even have them; could you explain?
It looks like you're trying to extend the I2C emulation so that some
chips (405GPr) support functionality that other chips (405GP?) don't?

> +	/* PID */
> +	//load_init_image("pid.bin", NULL, 256, 0xff);
> +	int PID_fd;
> +	memset(PID_content, 0, 128);
> +	if ((PID_fd=open("pid.bin", O_RDWR | O_BINARY, 0666))>=0) { /*"r+b");*/
> +		int n = read(PID_fd, PID_content, 128);
> +		if (n<0) {
> +			printf("Error reading file pid.bin (ret %d)\n", n); 
> +		} else if (n<128) {
> +			printf("Warning reading file pid.bin: only %d/128 bytes loaded\n", n);
> +		}
> +		close(PID_fd);
> +	}

You should use load_image() here. Also, it looks like pid.bin is
something that should live inside 'bios_dir', just like your "bios"
loading below.

What *is* pid.bin, anyways? How would I obtain/create my own, if I
wanted to run your code?

> +	if (PID_fd<0) {
> +		/* Built in eeprom */
> +		printf("Warning using builtin pid content!\n");
> +		PID_content[2]	= 4; /* Magic word, use sdram */
> +		PID_content[3]	= 12; /* Row word, use sdram */
> +		PID_content[4]	= 10; /* Col word, use sdram */
> +		PID_content[5]	= 1; /* 1 bank */
> +		PID_content[6]	= 32; /* Module width */
> +		PID_content[31]	= 0x10; /* How many 4MB is long */
> +		PID_content[127]= 0 | 2 /* Cas latency */;
> +	}
> +
> +	readF = &PID_read;
> +	writeF = &PID_write;
> +    }
> +    /* allocate SRAM */
> +#ifdef DEBUG_BOARD_INIT
> +    printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset);
> +#endif
> +    /* Last 48 (max 128) bytes) used for bd info */

This comment doesn't seem related to this code.

> +    sram_size = 8 * 1024;
> +    cpu_register_physical_memory(0x40000000, sram_size,
> +                                 sram_offset | IO_MEM_RAM);
> +    /* allocate and load BIOS */
> +#ifdef DEBUG_BOARD_INIT
> +    printf("%s: register BIOS\n", __func__);
> +#endif
> +    bios_offset = sram_offset + sram_size;
> +    fl_idx = 0;
> +#ifdef USE_FLASH_BIOS
> +    index = drive_get_index(IF_PFLASH, 0, fl_idx);
> +    if (index != -1) {
> +        bios_size = bdrv_getlength(drives_table[index].bdrv);
> +        fl_sectors = (bios_size + 65535) >> 16;
> +#ifdef DEBUG_BOARD_INIT
> +        printf("Register parallel flash %d size " ADDRX " at offset %08lx "
> +               " addr " ADDRX " '%s' %d\n",
> +               fl_idx, bios_size, bios_offset, -bios_size,
> +               bdrv_get_device_name(drives_table[index].bdrv), fl_sectors);
> +#endif
> +        pflash_cfi02_register((uint32_t)(-bios_size), bios_offset,
> +                              drives_table[index].bdrv, 65536, fl_sectors, 1,
> +                              2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA);
> +        fl_idx++;
> +    } else
> +#endif
> +    {
> +        char buf[1024];
> +        ram_addr_t flash_offset;
> +	target_ulong flash_size;
> +	
> +	flash_offset = bios_offset;
> +	flash_size = 512*1024;
> +#ifdef DEBUG_BOARD_INIT
> +        printf("Load BIOS from file\n");
> +#endif
> +        if (bios_name == NULL)
> +            bios_name = BIOS_FILENAME;
> +        snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name);
> +        bios_size = get_image_size(buf);
> +        if (bios_size < 0 || bios_size > BIOS_SIZE) {
> +            fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf);
> +            exit(1);
> +        }
> +        bios_size = (bios_size + 0xfff) & ~0xfff;
> +#ifdef DEBUG_BOARD_INIT
> +        printf("Loading bios@" ADDRX " %d bytes\n", (uint32_t)(-bios_size), bios_size);
> +#endif
> +#if 1
> +        pflash_cfi02_register((uint32_t)(-flash_size), flash_offset,
> +                              NULL, 65536 /*sector len*/, flash_size/65536 /* nblocks */, 1,
> +                              1 /* width */, 0x0001/* AMD */, 0x4F/* Am29LV040B */, 0x0000, 0x0000, 0x555/* unlock addrs */, 0x2AA);
> +#else
> +        cpu_register_physical_memory((uint32_t)(-flash_size),
> +                                     flash_size, flash_offset | IO_MEM_ROM);
> +#endif
> +
> +        bios_size = load_image_targphys(buf, (-bios_size), BIOS_SIZE/* max sz */);
> +        if (bios_size < 0 || bios_size > BIOS_SIZE) {
> +            fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf);
> +            exit(1);
> +        }
> +	bios_size = flash_size;
> +    }
> +    bios_offset += bios_size;
> +    /* Register FPGA */
> +#ifdef DEBUG_BOARD_INIT
> +    printf("%s: register FPGA\n", __func__);
> +#endif
> +    ref405ep_fpga_init(0xF0300000);
> +    /* Register NVRAM */
> +#ifdef DEBUG_BOARD_INIT
> +    printf("%s: register NVRAM\n", __func__);
> +#endif
> +    m48t59_init(NULL, 0xF0000000, 0, 8192, 8);
> +    /* Load kernel */
> +    linux_boot = (kernel_filename != NULL);
> +    if (0) {
> +#ifdef DEBUG_BOARD_INIT
> +        printf("%s: load kernel\n", __func__);
> +#endif
> +        memset(&bd, 0, sizeof(bd));
> +        bd.bi_memstart = 0x00000000;
> +        bd.bi_memsize = ram_size;
> +        bd.bi_flashstart = -bios_size;
> +        bd.bi_flashsize = -bios_size;
> +        bd.bi_flashoffset = 0;
> +        bd.bi_sramstart = 0xFFF00000;
> +        bd.bi_sramsize = sram_size;
> +        bd.bi_bootflags = 0;
> +        bd.bi_intfreq = 133333333;
> +        bd.bi_busfreq = 33333333;
> +        bd.bi_baudrate = 115200;
> +        bd.bi_s_version[0] = 'Q';
> +        bd.bi_s_version[1] = 'M';
> +        bd.bi_s_version[2] = 'U';
> +        bd.bi_s_version[3] = '\0';
> +        bd.bi_r_version[0] = 'Q';
> +        bd.bi_r_version[1] = 'E';
> +        bd.bi_r_version[2] = 'M';
> +        bd.bi_r_version[3] = 'U';
> +        bd.bi_r_version[4] = '\0';
> +        bd.bi_procfreq = 133333333;
> +        bd.bi_plb_busfreq = 33333333;
> +        bd.bi_pci_busfreq = 33333333;
> +        bd.bi_opbfreq = 33333333;
> +        bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001);
> +        env->gpr[3] = bdloc;
> +        kernel_base = KERNEL_LOAD_ADDR;
> +        /* now we can load the kernel */
> +        kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
> +        if (kernel_size < 0) {
> +            fprintf(stderr, "qemu: could not load kernel '%s'\n",
> +                    kernel_filename);
> +            exit(1);
> +        }
> +        printf("Load kernel size " TARGET_FMT_ld " at " TARGET_FMT_lx
> +               " %02x %02x %02x %02x\n", kernel_size, kernel_base,
> +               *(char *)(phys_ram_base + kernel_base),
> +               *(char *)(phys_ram_base + kernel_base + 1),
> +               *(char *)(phys_ram_base + kernel_base + 2),
> +               *(char *)(phys_ram_base + kernel_base + 3));
> +        /* load initrd */
> +        if (initrd_filename) {
> +            initrd_base = INITRD_LOAD_ADDR;
> +            initrd_size = load_image(initrd_filename,
> +                                     phys_ram_base + initrd_base);
> +            if (initrd_size < 0) {
> +                fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
> +                        initrd_filename);
> +                exit(1);
> +            }
> +        } else {
> +            initrd_base = 0;
> +            initrd_size = 0;
> +        }
> +        env->gpr[4] = initrd_base;
> +        env->gpr[5] = initrd_size;
> +        ppc_boot_device = 'm';
> +        if (kernel_cmdline != NULL) {
> +            len = strlen(kernel_cmdline);
> +            bdloc -= ((len + 255) & ~255);
> +            memcpy(phys_ram_base + bdloc, kernel_cmdline, len + 1);
> +            env->gpr[6] = bdloc;
> +            env->gpr[7] = bdloc + len;
> +        } else {
> +            env->gpr[6] = 0;
> +            env->gpr[7] = 0;
> +        }
> +        env->nip = KERNEL_LOAD_ADDR;
> +    } else {
> +        env->nip = 0xFFFFFFFC;
> +    }
> +#ifdef DEBUG_BOARD_INIT
> +    printf("%s: Done\n", __func__);
> +#endif
> +    /*printf("bdloc %016lx %s\n",
> +           (unsigned long)bdloc, (char *)(phys_ram_base + bdloc));*/
> +}

A *ton* of this code seems to be nearly identical to ref405ep_init().
How can you better share it?

> +QEMUMachine walnut_machine = {
> +    .name = "walnut",
> +    .desc = "DHT Walnut board, based on ppc405GPe",
> +    .init = walnut_init,
> +    .ram_require = (128 * 1024 * 1024 + 4096 + 512 * 1024 + BIOS_SIZE) | RAMSIZE_FIXED,
> +};

Is that really the *minimum* memory possible? Why couldn't I use less
RAM?

I'll make comments on your EMAC code in another email.

-- 
Hollis Blanchard
IBM Linux Technology Center

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] PowerPC 4xx EMAC emulation
  2008-11-21  1:53 ` [Qemu-devel] PowerPC 4xx enhacement Salvatore Lionetti
  2008-11-21 11:40   ` Jean-Christophe PLAGNIOL-VILLARD
  2008-11-21 16:40   ` [Qemu-devel] DHT Walnut board support Hollis Blanchard
@ 2008-11-21 17:09   ` Hollis Blanchard
  2 siblings, 0 replies; 5+ messages in thread
From: Hollis Blanchard @ 2008-11-21 17:09 UTC (permalink / raw)
  To: qemu-devel

On Fri, 2008-11-21 at 01:53 +0000, Salvatore Lionetti wrote:

> Index: hw/emac.c
> ===================================================================
> --- hw/emac.c	(revision 0)
> +++ hw/emac.c	(revision 0)

You should name this ppc4xx_emac.c to avoid confusion.

> @@ -0,0 +1,797 @@
> +/*
> + * QEMU EMAC emulation
> + *
> + * Copyright (c) 2008, Lionetti Salvatore salvatorelionetti@yahoo.it
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +/* This module provide an emulation for ethernet onchip controller on ppc4xx cpu.
> + * Tested on
> + * - emulator of a NSN proprietary board, for 4G
> + * - walnut board/u-boot-1.1.6 (whith some patch)
> + *
> + * TODO:
> + * - >1 {tx, rx} channel 
> + * - can_receive() and buffer_full() too simple.
> + */
> +#include "hw.h"
> +#include "ppc.h"
> +#include "ppc405.h"
> +#include "pc.h"
> +#include "qemu-timer.h"
> +#include "sysemu.h"
> +#include "net.h"
> +
> +/*#define DEBUG_EMAC*/
> +#ifdef DEBUG_EMAC
> +#define DPRINT(fmt, args...)                           \
> +    do { printf("%" PRIu64 " %s: " fmt , qemu_get_clock(rt_clock), __func__, ##args); } while (0)
> +#else
> +#define DPRINT(fmt, args...)
> +#endif 
> +
> +/* 
> + * Redefined because if !exist, linker gives a warning but produce qemu,
> + * but on execution this call cause problem
> + * (could be cygwin & mingw cohesistence?)
> + */
> +#if (defined(WORDS_BIGENDIAN) && defined(TARGET_WORDS_BIGENDIAN)) || ((!defined(WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN)))
> +#if 0
> +static unsigned long int htonl(unsigned long int hostlong);
> +static unsigned short int htons(unsigned short int hostshort);
> +#endif
> +static unsigned long int ntohl(unsigned long int ing) {
> +	return ing;
> +}
> +static unsigned short int ntohs(unsigned short int ing) {
> +	return ing;
> +}
> +#else
> +static unsigned long int ntohl(unsigned long int ing) {
> +	unsigned char tmp;
> +	unsigned char* tmpp = (unsigned char*)&ing;
> +	tmp=tmpp[0]; tmpp[0]=tmpp[3]; tmpp[3]=tmp;
> +	tmp=tmpp[1]; tmpp[1]=tmpp[2]; tmpp[2]=tmp;
> +	return *(unsigned long int*)tmpp;
> +}
> +static unsigned short int ntohs(unsigned short int ing) {
> +	return ((ing&0xFF)<<8) | ((ing&0xFF00)>>8);
> +}
> +#endif

That comment makes me very nervous. At any rate, you don't need to
invent your own byteswapping macros here; use the standard qemu ones.

> +/* qemu initialization:
> + *
> + * parse command line:	call net_client_init() for each -net, either for {if, collision domain} options.
> + * 			==========					================
> + * 			IF MAC=...	<==== Vlan{Id,Ptr} ====>	COLLISION DOMAIN (mean N-1 other IF MAC=...)
> + * 			========== 					================
> + * 				-net nic[,...]:	fill field onto nd_table[] {MAC(cl|auto), model, vlanPtr=f(vlanId)}
> + * 						vlanPtr=f(vlanId)->guest++
> + * 				-net tap[,...]: vlanPtr=f(vlanId)->host ++
> + * 						tap_win32_init(vlanPtr, "ifname=tap..."):
> + * 							qemu_new_vlan_client(vlanPtr,tap_receive, tap* opaque)		RECV
> + * 							qemu_add_wait_object(tap_sem, tap_w32_send, tap* opaque)	SEND
> + * 			
> + * 			For each vlan created,
> + * 				exit if no GUEST & some HOST
> + * 				warn if some GUEST & no HOST
> + * 											  
> + * start machine:	Es ppc (also pc use n2000) call machine->init():
> + * 			isa_ne2000_init(base, irqno, &nd_table[i])
> + * 				register_ioport_write(base + ..., ne2000_asic_ioport_write, opaque NE2000State*);	SEND
> + * 				qemu_new_vlan_client(nd->vlan, ne2000_receive, ne2000_can_receive, opaque NE2000State*) RECV
> + *
> + * Done!!!

This comment is way over 80 chars wide, and should be removed anyways.
This is not the appropriate place to document qemu's initialization
sequence. I also don't understand why you're talking about NE2000 in
emac.c.

> + * qemu runtime scenario:
> + *
> + * packet send from HOST:	a signal (after exec() cycle) is sent to tap_sem => tap_w32_send() => tap_win32_read()
> + * 				if some bytes returned => qemu_send_packet() => send to all 'client' (better peer) of
> + * 				such vlan, different from itself => ne2000_receive()
> + *
> + * packet send from GUEST:	in machine code we call qemu_send_packet() => (prec) => tap_receive() => tap_w32_write()
> + *
> + *
> + * So onto VLAN actors always call qemu_send_packet() that dispatch packet to all peer attached to same vlan.
> + *
> + */
> +/* PPC405 layer 2, reversed MII mode:
> + *
> + * Register@0xEF600000 already mapped.
> + * Configuration RW:
> + *
> + * Read(ind, data):
> + * 	[EMAC0_STACR] <= (STACR_STAC_R | ind)
> + *	[EMAC0_STACR] & STACR_OC must goes to 0
> + *	[EMAC0_STACR] => >>16 => data
> + *
> + * Write(ind, data)
> + * 	[EMAC0_STACR] <= (STACR_STAC_W | ind | data<<16)
> + * 	[EMAC0_STACR] & STACR_OC must goes to 0
> + *
> + * Physical access:
> + * Read(ind_phy, ind_reg, data)
> + * 	[EMAC0_STACR] <= (STACR_STAC_W | TANTOS_REG_MIIAC | (ind_reg&0x1F | OP_READ | (ind_phy&0x1F)<<5))>>16
> + * 	[EMAC0_STACR] & STACR_OC must goes to 0
> + *	
> + *	Verify:
> + *	[EMAC0_STACR] <= (STACR_STAC_R | TANTOS_REG_MIIRD)
> + * 	[EMAC0_STACR] & STACR_OC must goes to 0
> + * 	[EMAC0_STACR] => data
> + * 	data & STACR_PHYE should be 0
> + * 	data=data>>16
> + *
> + * Tantos Stub Layer: (needed?)
> + *
> + * From mal we need
> + * - MAL0_TXCTPxR
> + * - MAL0_RXCTPxR
> + * - MAL0_RCBS0
> + */
> +/* TODO: need interaction between device!
> + * very difficult since all struct is in .c file */

Refactor it then.

> +extern uint32_t txctpr[4];
> +extern uint32_t rxctpr[2];
> +extern uint32_t rcbs[2];
> +extern uint32_t *rxeobisr; /* With namespace, use & to automatize correlation.*/
> +extern uint32_t *txeobisr;

These should not be not global. Pass a pointer to ppc40x_mal_t into the
EMAC init code and access them that way.

> +// For PPC405
> +#define EMAC0_BASE        0xEF600800
> +#define EMAC0_STACR_DISP  0x35
> +#define EMAC0_STACR      (EMAC0_BASE + EMAC0_STACR_DISP) /* 4 byte addressing */
> +#define STACR_OC          0x00008000 /* Occupied flag */
> +#define STACR_PHYE        0x00004000
> +#define STACR_STAC_W      0x00002000
> +#define STACR_STAC_R      0x00001000
> +
> +#define TANTOS_REG_MIIAC    0x120      /// TANTOS3G MII indirect acess registers
> +#define TANTOS_REG_MIIWD    0x121
> +#define TANTOS_REG_MIIRD    0x122
> +#define OP_WRITE	0x0400
> +#define OP_READ		0x0800
> +#define MBUSY 		0x8000

Probably all these Tantos references should be removed; they're not used
in the code.

> +/* WORDS_BIGENDIAN = FALSE su HOST i386, GUEST ppc
> + * per cui e' l'architetura HOST
> + */

Some Italian was left here.

> +struct EmacMalRxDes {
> +#ifndef WORDS_BIGENDIAN
> +    union {
> +	    struct { /* Mal */
> +		    unsigned int emac1_nu	: 2;
> +		    unsigned int bit5_intr	: 1;
> +		    unsigned int bit4_first	: 1;
> +		    unsigned int bit3_last	: 1;
> +		    unsigned int bit2_contm	: 1;
> +		    unsigned int bit1_wrap	: 1;
> +		    unsigned int bit0_empty	: 1;
> +		    unsigned int emac2_nu	: 8; 
> +	    } __attribute__ ((packed)) mal;
> +	    struct { /* Status: Read access, error cause */
> +		    unsigned int bit7_pausepacket	: 1;
> +		    unsigned int bit6_overrun		: 1;
> +		    unsigned int mal_nu			: 6;
> +		    unsigned int bit15_inrange		: 1;
> +		    unsigned int bit14_outrange		: 1;
> +		    unsigned int bit13_longpacket	: 1;
> +		    unsigned int bit12_fcs		: 1;
> +		    unsigned int bit11_alignment	: 1;
> +		    unsigned int bit10_shortevent	: 1;
> +		    unsigned int bit9_runtpacket	: 1;
> +		    unsigned int bit8_badpacket		: 1;
> +	    } __attribute__ ((packed)) emac_errstatus;
> +    } __attribute__ ((packed));
> +#else
> +#error "Host endianism BUG: To Test!!!"
> +#endif
> +    unsigned short len;
> +    unsigned char* buf;
> +} __attribute__ ((packed)); /* Host endianism, to be converted */

I worry a lot about this endianness handling. You should probably
byteswap whole words *before* storing into this structure, so that you
don't need to define two structures with different layouts.

> +struct EmacMalTxDes {
> +#ifndef WORDS_BIGENDIAN
> +    /* IN A HALF-WORD (16bits)
> +     * position		position
> +     * in bitfield	in value
> +     * from high	from MSb
> +     * =========================
> +     * 0		8
> +     * 1		9
> +     * 2		10
> +     * 3		11
> +     * 4		12
> +     * 5		13
> +     * 6		14
> +     * 7		15
> +     * 8		0
> +     * 9		1
> +     * 10		2
> +     * 11		3
> +     * 12		4
> +     * 13		5
> +     * 14		6
> +     * 15		7
> +     *
> +     * ===HOST LE, TARGET BE===
> +     * IN A BYTE (8bits)
> +     *
> +     * IN A HALF-WORD (16bits)
> +     * 0-7		8-15
> +     * 8-15		0-7
> +     */
> +    union {
> +	    struct { /* Mal */
> +		    unsigned int emac1_nu	: 2;
> +		    unsigned int bit5_intr	: 1;
> +		    unsigned int bit4_resv	: 1;
> +		    unsigned int bit3_last	: 1;
> +		    unsigned int bit2_contm	: 1;
> +		    unsigned int bit1_wrap	: 1;
> +		    unsigned int bit0_ready	: 1;
> +		    unsigned int emac2_nu	: 8; 
> +	    } __attribute__ ((packed)) mal;
> +	    struct { /* Status: Read access, error cause */
> +		    unsigned int bit7_badpacket		: 1;
> +		    unsigned int bit6_badfsc		: 1;
> +		    unsigned int mal_nu			: 6;
> +		    unsigned int bit15_sqe		: 1;
> +		    unsigned int bit14_underrun		: 1;
> +		    unsigned int bit13_singlecoll	: 1;
> +		    unsigned int bit12_multiplecoll	: 1;
> +		    unsigned int bit11_latecoll		: 1;
> +		    unsigned int bit10_excessivecoll	: 1;
> +		    unsigned int bit9_excessivedeferral : 1;
> +		    unsigned int bit8_lossofcarrier	: 1;
> +	    } __attribute__ ((packed)) emac_errorstatus;
> +	    struct { /* Control: Write access */
> +		    unsigned int bit7_generatepad	: 1;
> +		    unsigned int bit6_generatefcs	: 1;
> +		    unsigned int mal_nu			: 6;
> +		    unsigned int emac_nu		: 4;
> +		    unsigned int bit11_vlantag_replace	: 1;
> +		    unsigned int bit10_vlantag_insert	: 1;
> +		    unsigned int bit9_sourceaddr_insert	: 1;
> +		    unsigned int bit8_sourceaddr_replace: 1;
> +	    } __attribute__ ((packed)) emac_control;
> +    };
> +#else
> +#error "Host endianism BUG: To Test!!!"
> +#endif
> +    unsigned short len;
> +    unsigned char* buf;
> +}__attribute__ ((packed)); /* Host endianism, to be converted */
> +
> +/* Controlo Register definition */
> +struct EmacRegs {
> +#if 0
> +#ifndef WORDS_BIGENDIAN
> +	struct Emac0_mr0 {
> +		unsigned int ;
> +	} __attribute__((packed)); 
> +#else
> +#error "Host endianism BUG: To Test!!!"
> +#endif
> +#else
> +	/* 0 */
> +	uint32_t mr0;
> +	uint32_t mr1;
> +	uint32_t tmr0;
> +	uint32_t tmr1;
> +	uint32_t rmr;
> +	uint32_t isr;
> +	uint32_t isre;
> +	uint32_t iahr;
> +	uint32_t ialr;
> +	uint32_t vtpid;
> +	/* 10 */
> +	uint32_t vtci;
> +	uint32_t ptr;
> +	uint32_t iaht1;
> +	uint32_t iaht2;
> +	uint32_t iaht3;
> +	uint32_t iaht4;
> +	uint32_t gaht1;
> +	uint32_t gaht2;
> +	uint32_t gaht3;
> +	uint32_t gaht4;
> +	/* 20 */
> +	uint32_t lsah;
> +	uint32_t lsal;
> +	uint32_t ipgvr;
> +	uint32_t stacr;
> +	uint32_t trtr;
> +	uint32_t rwmr;
> +	uint32_t octx;
> +	uint32_t ocrx;
> +	/* 28 reg */
> +
> +#define RSTA  mr0 & 0x20000000 
> +#define TXEN  mr0 & 0x10000000 
> +#define RXEN  mr0 & 0x08000000 
> +#endif	
> +} __attribute__ ((packed));
> +
> +typedef struct ppc4xx_emac_t ppc4xx_emac_t;
> +struct ppc4xx_emac_t {
> +    /* Guest related */
> +    target_phys_addr_t base;
> +    NICInfo nic;
> +    /* To remove, already defined in mal */
> +    qemu_irq mal_irqs_txeob;
> +    qemu_irq mal_irqs_rxeob;
> +    qemu_irq mal_irqs_txerr;
> +    qemu_irq mal_irqs_rxerr;
> +    qemu_irq mal_irqs_syserr;
> +
> +    struct EmacRegs reg, _reg;
> +
> +    struct EmacMalTxDes* tx;
> +    struct EmacMalRxDes* rx;
> +
> +    int txPos, rxPos;
> +    int txNum, rxNum;
> +
> +    int rxLostNum, rxLostLimit;
> +
> +    /* Host related */
> +    VLANClientState *vc;
> +    QEMUTimer* tx_timer;
> +};
> +
> +#if 0
> +#define EMACMAL_EMPTY 0x8000
> +#define EMACMAL_WRAP  0x8000
> +#define EMACMAL_EMPTY 0x8000
> +#define EMACMAL_EMPTY 0x8000
> +void EmacMalDes_readFromTarget(struct EmacMalDes* host, struct EmacMalDes* guest) {
> +
> +    host->status = ntohs(guest->status);
> +    host->len = ntohs(guest->len);
> +    host->buf = htol(guest->buf);
> +}
> +void EmacMalDes_writeToTarget(struct EmacMalDes* host, struct EmacMalDes* guest) {
> +}
> +#endif

Remove #if 0 code.

> +void EmacMalRxDes_dump(struct EmacMalRxDes* des) {
> +#ifdef DEBUG_EMAC
> +	unsigned char* tdes = (unsigned char*) ((unsigned char*)des - phys_ram_base);
> +	printf("RXdes@%p={len%d,buf%p,empty%d wrap%d contm %d last%d first%d intr%d\n",
> +			tdes, ntohs(des->len), (unsigned char*) ntohl((unsigned long int)des->buf),
> +			des->mal.bit0_empty, des->mal.bit1_wrap, des->mal.bit2_contm, des->mal.bit3_last, des->mal.bit4_first, des->mal.bit5_intr);
> +#endif
> +}
> +void EmacMalTxDes_dump(struct EmacMalTxDes* des) {
> +#ifdef DEBUG_EMAC
> +	unsigned char* tdes = (unsigned char*)((unsigned char*)des - phys_ram_base);
> +	printf("TXdes@%p={len%d,buf%p,ready%d wrap%d contm %d last%d first%d intr%d\n",
> +			tdes, ntohs(des->len), (unsigned char*)ntohl((unsigned long int)des->buf),
> +			des->mal.bit0_ready, des->mal.bit1_wrap, des->mal.bit2_contm, des->mal.bit3_last, des->mal.bit4_resv, des->mal.bit5_intr);
> +#endif
> +}
> +#if 0
> +static int emac_buffer_full(ppc4xx_emac_t *s)
> +{
> +#if 0
> +    int avail, index, boundary;
> +
> +    index = s->curpag << 8;
> +    boundary = s->boundary << 8;
> +    if (index < boundary)
> +        avail = boundary - index;
> +    else
> +        avail = (s->stop - s->start) - (index - boundary);
> +    if (avail < (MAX_ETH_FRAME_SIZE + 4))
> +        return 1;
> +#endif
> +    return 0;
> +}
> +
> +static int emac_can_receive(void *opaque)
> +{
> +#if 0
> +    NE2000State *s = opaque;
> +
> +    if (s->cmd & E8390_STOP)
> +        return 1;
> +    return !ne2000_buffer_full(s);
> +#endif
> +    return 0;
> +}
> +#endif

Remove lots more #if 0 code.

> +static void emac_receive(void *opaque, const uint8_t *buf, int size)
> +{
> +    ppc4xx_emac_t* emac = (ppc4xx_emac_t*) opaque;
> +
> +    DPRINT("%d bytes\n", size);
> +    if (emac->reg.RXEN) {
> +    if (emac->rx->buf) {
> +    if (size<1520) {
> +	    if (emac->rx[emac->rxPos].mal.bit0_empty) {
> +	    /* data is allocated with the maximum possible length, on eth = 1520Bytes
> +	     * len field represent packet size.*/
> +		    /* We !use Continuos mode, first & last field ignored */
> +		    unsigned char* dstG = (unsigned char*) ntohl((unsigned long int)emac->rx[emac->rxPos].buf);
> +		    unsigned char* dstH= dstG+(unsigned int)phys_ram_base;
> +		    unsigned short size16 = size;
> +		    DPRINT(" Delivery message HOST %p-> GUEST %p,pos%d\n", dstH, dstG, emac->rxPos);
> +		    EmacMalRxDes_dump(&emac->rx[emac->rxPos]);
> +		    emac->reg.ocrx += size16;
> +		    memcpy(dstH, buf, size);	
> +		    emac->rx[emac->rxPos].len = ntohs(size16);
> +		    emac->rx[emac->rxPos].mal.bit0_empty = 0;
> +		    if (emac->rx[emac->rxPos].mal.bit5_intr) {
> +			    /* TODO: use appropriate channel */
> +			    *rxeobisr = 0xC0000000;
> +			    qemu_irq_raise(emac->mal_irqs_rxeob);
> +		    }
> +
> +		    emac->rxPos += (emac->rx[emac->rxPos].mal.bit1_wrap)? -(emac->rxPos) : 1;
> +	    } else {
> +		    if (++emac->rxLostNum == emac->rxLostLimit) {
> +			    printf(" %d Message lost, board seem hung up!\n", emac->rxLostNum);
> +			    emac->rxLostLimit*=10;
> +		    }
> +	    }
> +    } else {
> +	    printf(" Message size > 1520, discarding packet\n");
> +    }

The indentation here looks pretty messed up. Also, what is the 1520 byte
limitation? From a quick skim, I don't see mention of that in the user
manual.

> +    } else {
> +    }
> +    } else {
> +	    static int sndOrMore=0;
> +	    if (!sndOrMore) {
> +		    printf("emac/mal: Driver !command start yet\n");
> +		    sndOrMore=1;
> +	    }
> +    }
> +#if 0
> +#define MIN_BUF_SIZE 60
> +    NE2000State *s = opaque;
> +    uint8_t *p;
> +    unsigned int total_len, next, avail, len, index, mcast_idx;
> +    uint8_t buf1[60];
> +    static const uint8_t broadcast_macaddr[6] =
> +        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
> +
> +#if defined(DEBUG_NE2000)
> +    printf("NE2000: received len=%d\n", size);
> +#endif
> +
> +    if (s->cmd & E8390_STOP || ne2000_bufstruct mal_emac_bdfer_full(s))
> +        return;
> +
> +    /* XXX: check this */
> +    if (s->rxcr & 0x10) {
> +        /* promiscuous: receive all */
> +    } else {
> +        if (!memcmp(buf,  broadcast_macaddr, 6)) {
> +            /* broadcast address */
> +            if (!(s->rxcr & 0x04))
> +                return;
> +        } else if (buf[0] & 0x01) {
> +            /* multicast */
> +            if (!(s->rxcr & 0x08))
> +                return;
> +            mcast_idx = compute_mcast_idx(buf);
> +            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
> +                return;
> +        } else if (s->mem[0] == buf[0] &&
> +                   s->mem[2] == buf[1] &&
> +                   s->mem[4] == buf[2] &&
> +                   s->mem[6] == buf[3] &&
> +                   s->mem[8] == buf[4] &&
> +                   s->mem[10] == buf[5]) {
> +            /* match */
> +        } else {
> +            return;
> +        }
> +    }
> +
> +
> +    /* if too small buffer, then expand it */
> +    if (size < MIN_BUF_SIZE) {
> +        memcpy(buf1, buf, size);
> +        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
> +        buf = buf1;
> +        size = MIN_BUF_SIZE;
> +    }
> +
> +    index = s->curpag << 8;
> +    /* 4 bytes for header */
> +    total_len = size + 4;
> +    /* address for next packet (4 bytes for CRC) */
> +    next = index + ((total_len + 4 + 255) & ~0xff);
> +    if (next >= s->stop)
> +        next -= (s->stop - s->start);
> +    /* prepare packet header */
> +    p = s->mem + index;
> +    s->rsr = ENRSR_RXOK; /* receive status */
> +    /* XXX: check this */
> +    if (buf[0] & 0x01)
> +        s->rsr |= ENRSR_PHY;
> +    p[0] = s->rsr;
> +    p[1] = next >> 8;
> +    p[2] = total_len;
> +    p[3] = total_len >> 8;
> +    index += 4;
> +
> +    /* write packet data */
> +    while (size > 0) {
> +        if (index <= s->stop)
> +            avail = s->stop - index;
> +        else
> +            avail = 0;
> +        len = size;
> +        if (len > avail)
> +            len = avail;
> +        memcpy(s->mem + index, buf, len);
> +        buf += len;
> +        index += len;
> +        if (index == s->stop)
> +            index = s->start;
> +        size -= len;
> +    }
> +    s->curpag = next >> 8;
> +
> +    /* now we can signal we have received something */
> +    s->isr |= ENISR_RX;
> +    ne2000_update_irq(s);
> +#endif

All that #if 0 code looks like it was copied directly from ne2000
emulation. Get rid of it.

> +}
> +
> +void emac_ppc405_init(ppc4xx_emac_t* emac, NICInfo *nd)
> +{
> +    memcpy(&emac->nic, nd, sizeof(NICInfo));
> +    /* We are only be able to receive. Sending packet mean receiver action get up */

I don't understand this comment at all.

Is this function really specific to ppc405, or could it also apply to
440 SoCs with EMAC?

> +    emac->vc = qemu_new_vlan_client(nd->vlan, emac_receive,
> +                                 NULL/*emac_can_receive*/, emac);
> +
> +    snprintf(emac->vc->info_str, sizeof(emac->vc->info_str),
> +             "emac macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
> +             emac->nic.macaddr[0],
> +             emac->nic.macaddr[1],
> +             emac->nic.macaddr[2],
> +             emac->nic.macaddr[3],
> +             emac->nic.macaddr[4],
> +             emac->nic.macaddr[5]);
> +    printf("%s\n", emac->vc->info_str);
> +}
> +
> +static void ppc4xx_emac_reset (void *opaque)
> +{
> +    ppc4xx_emac_t *emac;
> +
> +    emac = opaque;
> +    emac->rx = NULL;
> +    emac->tx = NULL;
> +    emac->txPos = emac->rxPos = 0;
> +    emac->txNum = emac->rxNum = 0;
> +    emac->rxLostNum=0;
> +    emac->rxLostLimit=1;
> +
> +    memset(&emac->reg, 0, sizeof(emac->reg));
> +    emac->reg.mr0   = 0xC0000000;
> +    emac->reg.tmr1  = 0x380F0000;
> +    emac->reg.vtpid = 0x00008808;
> +    emac->reg.ptr   = 0x0000FFFF;
> +    emac->reg.ipgvr = 0x00000004;
> +    emac->reg.stacr = 0x00008000;
> +    emac->reg.rwmr  = 0x04001000;
> +
> +    /* Now ready to issue mii control transfer */
> +    emac->reg.stacr = 0;
> +}
> +/* It looks like a lot of Linux programs assume page size
> + * is 4kB long. This is evil, but we have to deal with it...
> + *
> +#define TARGET_PAGE_BITS 12

TARGET_PAGE_BITS has no place here at all, and you never even use it
anyways.

> + * Here we have many register.
> + * Do action only for reset or transmit command.
> + * Other will be simple configuration latched during device emulation
> + * es stop rx, allow pause packet.
> + *
> + * Another tip:
> + * When possible only implemente the read of value, since using var read/write is target indep
> + */
> +static uint32_t emac_readl (void *opaque, target_phys_addr_t addr)
> +{
> +	uint32_t value;
> +	ppc4xx_emac_t* emac = (ppc4xx_emac_t*)opaque;
> +	uint32_t ind = (addr-emac->base)>>2;
> +	/* Keep in a separate way, as stackable code */
> +	if (ind>=28) {
> +		printf("emac: Out of range register %d!\n", ind);
> +		return 0;
> +	}
> +	value = ((uint32_t*)&emac->reg)[ind];
> +	DPRINT("emac: [%d] => %x\n", ind, value);
> +	return value/*STACR_OC*/;
> +}
> +
> +static void emac_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +	ppc4xx_emac_t* emac = (ppc4xx_emac_t*)opaque;
> +	uint32_t ind = (addr-emac->base)>>2;
> +
> +/*	printf("%s: " PADDRX " val %x\n",__func__, addr, value);*/
> +	/* Exist some common code for this? */
> +	if (ind>=28) {
> +		printf("emac: Out of range register %d!\n", ind);
> +		return;
> +	}
> +	switch (ind) {
> +		case 20:
> +		case 21:
> +		case 26:
> +		case 27:
> +			printf("emac: Attemping to write to read only reg!");
> +			return;
> +	}
> +	DPRINT("emac: [%d] <= %x\n", ind, value);
> +	/* Keep in a separate way, as stackable code */
> +	((uint32_t*)&emac->reg)[ind] = value;
> +	if (ind==0) {
> +		emac->_reg.mr0 = value;
> +		/* soft reset */
> +		if (emac->_reg.RSTA) {
> +			printf("emac: soft reset commanded!\n");
> +			ppc4xx_emac_reset(emac);/* Should be protected with a semaphore, also in emac_receive()? */
> +			value &= ~(emac->_reg.RSTA);	/* Sw look for this bit go down */
> +		}
> +
> +		DPRINT("emac: St%s tx channel!\n", emac->_reg.TXEN?"arting":"opping");
> +		DPRINT("emac: St%s rx channel!\n", emac->_reg.RXEN?"arting":"opping");
> +
> +		/* Suppose the mal reg was updated, seem a good approximation */
> +		if (emac->_reg.TXEN && emac->tx==NULL) {
> +			int l=0;
> +			emac->tx = (struct EmacMalTxDes*) (txctpr[0] + phys_ram_base);
> +			do { EmacMalTxDes_dump(&emac->tx[l]); } while (!emac->tx[l].mal.bit1_wrap && l++<50);
> +		}
> +		if (emac->_reg.RXEN && emac->rx==NULL) {
> +			int l=0;
> +			emac->rx = (struct EmacMalRxDes*) (rxctpr[0] + phys_ram_base);
> +			do { EmacMalRxDes_dump(&emac->rx[l]); } while (!emac->rx[l].mal.bit1_wrap && l++<50);
> +		}
> +		/* A timer that periodically check packet to send 
> +		emac->tx_timer = qemu_new_timer();*/
> +	}
> +	if (ind==2) {
> +		if (value & 0xC0000000) { /* both channel */
> +			if (emac->reg.TXEN) {
> +			if (emac->tx[emac->txPos].mal.bit0_ready) {
> +				unsigned char* dstG = (unsigned char*) ntohl((unsigned long int)emac->tx[emac->txPos].buf);
> +				unsigned char* dstH= dstG+(unsigned int)phys_ram_base;
> +				unsigned short size16 = ntohs(emac->tx[emac->txPos].len);
> +
> +				DPRINT("Sending packet on pos %d\n", emac->txPos);
> +				qemu_send_packet(emac->vc, dstH, size16);
> +				emac->reg.octx += size16;
> +				if (emac->tx[emac->txPos].mal.bit5_intr) {
> +					/* TODO: use appropriate channel */
> +					*txeobisr = 0xC0000000;
> +					qemu_irq_raise(emac->mal_irqs_txeob);
> +				}
> +
> +				/* Clear status */
> +				emac->tx[emac->txPos].mal.bit0_ready = 0;
> +				/* 0x808 is stucked at STACR_OC */
> +				emac->txPos += (emac->tx[emac->txPos].mal.bit1_wrap)? -(emac->txPos) : 1;
> +
> +				/* 
> +				 * u-boot driver (no os=>no task) poll this bit until became 0,
> +				 * but in PPC405GPr User manual nothing said about this
> +				 */
> +				value &= ~0xC0000000;
> +			} else {printf("Processor goes crazy(ready bit was 0)!!! Stopping tx\n");}
> +			} else {DPRINT("Tx is disabled\n");}
> +		}
> +	}
> +	if (ind==23) {
> +		/* PPC405GPr_UM2004 say, on page 568:
> +		 * 'EMAC sets EMAC0_STACR[OC] = 0 when the EMAC0_STACR is written to.'
> +		 * 'EMAC then sets EMAC0_STACR[OC] = 1 to indicate that the data has been written to the PHY, or the data'
> +		 * 'read from the PHY is valid. The device driver should poll for EMAC0_STACR[OC] = 1 before issuing a new'
> +		 * 'command, or before using data read from the PHY'
> +		 * 
> +		 * our approximation:
> +		 * - initial condition			0
> +		 * - write(sw start some MII command)	1 (we end in the same cycle)
> +		 * - read(poll status)			1
> +		 * - write(sw start new cmd | ...)	0
> +		 *
> +		 * reg write clear (bit index is opposite then those reported by official manual)
> +		 * PHYD	bit 31:16	Data (RW)
> +		 * OC	bit 15		0 when this reg is addressed, 1 data written to PHY/data readed correctly from PHY
> +		 * PHYE	bit 14		1 PhyError during read
> +		 * STAC bit 13:12	00 Reserved/01 Read/10 Write/11 Reserved
> +		 * OPBC	bit 11:10	OPB Bus clock freq. signal EMCMDCIk
> +		 * PCDA	bit 9:5		PHY Command destination address
> +		 * PRA	bit 4:0		PHY Register address
> +		 *
> +		value &= ~(1<<15);*/
> +		value ^=  (1<<15);
> +		value &= ~(1<<14);
> +	}
> +	/* Keep in a separate way, as stackable code */
> +	((uint32_t*)&emac->reg)[ind] = value;
> +}
> +static uint32_t emac_readw (void *opaque, target_phys_addr_t addr)
> +{
> +	printf("Lettura a granularita' word su EMAC " PADDRX "\n",addr);
> +	return 0;
> +}
> +
> +static void emac_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +	printf("Scrittura a granularita' word su EMAC " PADDRX "\n",addr);
> +}
> +
> +static uint32_t emac_readb (void *opaque, target_phys_addr_t addr)
> +{
> +	printf("Lettura a granularita' byte su EMAC " PADDRX "\n",addr);
> +	return 0;
> +}
> +
> +static void emac_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
> +{
> +	printf("Scrittura a granularita' byte su EMAC " PADDRX "\n",addr);
> +}

A lot of Italian in here. :)

> +
> +static CPUReadMemoryFunc *emac_read[] = {
> +    &emac_readb,
> +    &emac_readw,
> +    &emac_readl,
> +};
> +
> +static CPUWriteMemoryFunc *emac_write[] = {
> +    &emac_writeb,
> +    &emac_writew,
> +    &emac_writel,
> +};
> +void ppc4xx_emac_init (CPUState *env, ppc4xx_mmio_t *mmio, target_phys_addr_t offset, NICInfo *nic, qemu_irq mal_irqs[4])
> +{
> +    ppc4xx_emac_t *emac;
> +
> +    emac = qemu_mallocz(sizeof(ppc4xx_emac_t));
> +    if (emac != NULL) {
> +        emac->base = offset;
> +#ifdef DEBUG_OPBA
> +        printf("%s: offset " PADDRX "\n", __func__, offset);
> +#endif
> +        ppc4xx_mmio_register(env, mmio, offset, 0x70,
> +                             emac_read, emac_write, emac);
> +        qemu_register_reset(ppc4xx_emac_reset, emac);
> +
> +	/* One time configuration, HOST related */
> +	emac_ppc405_init(emac, nic);
> +
> +	emac->mal_irqs_txeob = mal_irqs[0];
> +	emac->mal_irqs_rxeob = mal_irqs[1];
> +	emac->mal_irqs_txerr = mal_irqs[2];
> +	emac->mal_irqs_rxerr = mal_irqs[3];
> +
> +	/* N Time configuration, GUEST related */
> +        ppc4xx_emac_reset(emac);
> +    }
> +}

I'm more interested in your EMAC work, so if you split that into a
separate patch as you revise it, we may be able to get it cleaned up and
usable sooner than later.

-- 
Hollis Blanchard
IBM Linux Technology Center

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-11-21 17:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-11-20 23:42 [Qemu-devel] PowerPC 4xx EMAC emulation Hollis Blanchard
2008-11-21  1:53 ` [Qemu-devel] PowerPC 4xx enhacement Salvatore Lionetti
2008-11-21 11:40   ` Jean-Christophe PLAGNIOL-VILLARD
2008-11-21 16:40   ` [Qemu-devel] DHT Walnut board support Hollis Blanchard
2008-11-21 17:09   ` [Qemu-devel] PowerPC 4xx EMAC emulation Hollis Blanchard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).