qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] PowerPC 4xx EMAC emulation


From: Hollis Blanchard
Subject: Re: [Qemu-devel] PowerPC 4xx EMAC emulation
Date: Fri, 21 Nov 2008 11:09:55 -0600

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 address@hidden
> + *
> + * 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:
> + *
> + * address@hidden 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("address@hidden,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("address@hidden,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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]