All of lore.kernel.org
 help / color / mirror / Atom feed
From: Hollis Blanchard <hollisb@us.ibm.com>
To: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] PowerPC 4xx EMAC emulation
Date: Fri, 21 Nov 2008 11:09:55 -0600	[thread overview]
Message-ID: <1227287395.26513.51.camel@localhost.localdomain> (raw)
In-Reply-To: <169263.53603.qm@web27203.mail.ukl.yahoo.com>

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

      parent reply	other threads:[~2008-11-21 17:10 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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   ` Hollis Blanchard [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1227287395.26513.51.camel@localhost.localdomain \
    --to=hollisb@us.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.