Annotation of qemu/hw/stellaris_enet.c, revision 1.1.1.2

1.1       root        1: /*
                      2:  * Luminary Micro Stellaris Ethernet Controller
                      3:  *
                      4:  * Copyright (c) 2007 CodeSourcery.
                      5:  * Written by Paul Brook
                      6:  *
                      7:  * This code is licenced under the GPL.
                      8:  */
                      9: #include "hw.h"
                     10: #include "arm-misc.h"
                     11: #include "net.h"
                     12: #include <zlib.h>
                     13: 
                     14: //#define DEBUG_STELLARIS_ENET 1
                     15: 
                     16: #ifdef DEBUG_STELLARIS_ENET
                     17: #define DPRINTF(fmt, args...) \
                     18: do { printf("stellaris_enet: " fmt , ##args); } while (0)
                     19: #define BADF(fmt, args...) \
                     20: do { fprintf(stderr, "stellaris_enet: error: " fmt , ##args); exit(1);} while (0)
                     21: #else
                     22: #define DPRINTF(fmt, args...) do {} while(0)
                     23: #define BADF(fmt, args...) \
                     24: do { fprintf(stderr, "stellaris_enet: error: " fmt , ##args);} while (0)
                     25: #endif
                     26: 
                     27: #define SE_INT_RX       0x01
                     28: #define SE_INT_TXER     0x02
                     29: #define SE_INT_TXEMP    0x04
                     30: #define SE_INT_FOV      0x08
                     31: #define SE_INT_RXER     0x10
                     32: #define SE_INT_MD       0x20
                     33: #define SE_INT_PHY      0x40
                     34: 
                     35: #define SE_RCTL_RXEN    0x01
                     36: #define SE_RCTL_AMUL    0x02
                     37: #define SE_RCTL_PRMS    0x04
                     38: #define SE_RCTL_BADCRC  0x08
                     39: #define SE_RCTL_RSTFIFO 0x10
                     40: 
                     41: #define SE_TCTL_TXEN    0x01
                     42: #define SE_TCTL_PADEN   0x02
                     43: #define SE_TCTL_CRC     0x04
                     44: #define SE_TCTL_DUPLEX  0x08
                     45: 
                     46: typedef struct {
                     47:     uint32_t ris;
                     48:     uint32_t im;
                     49:     uint32_t rctl;
                     50:     uint32_t tctl;
                     51:     uint32_t thr;
                     52:     uint32_t mctl;
                     53:     uint32_t mdv;
                     54:     uint32_t mtxd;
                     55:     uint32_t mrxd;
                     56:     uint32_t np;
                     57:     int tx_frame_len;
                     58:     int tx_fifo_len;
                     59:     uint8_t tx_fifo[2048];
                     60:     /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
                     61:        We implement a full 31 packet fifo.  */
                     62:     struct {
                     63:         uint8_t data[2048];
                     64:         int len;
                     65:     } rx[31];
                     66:     uint8_t *rx_fifo;
                     67:     int rx_fifo_len;
                     68:     int next_packet;
                     69:     VLANClientState *vc;
                     70:     qemu_irq irq;
                     71:     uint8_t macaddr[6];
                     72: } stellaris_enet_state;
                     73: 
                     74: static void stellaris_enet_update(stellaris_enet_state *s)
                     75: {
                     76:     qemu_set_irq(s->irq, (s->ris & s->im) != 0);
                     77: }
                     78: 
                     79: /* TODO: Implement MAC address filtering.  */
                     80: static void stellaris_enet_receive(void *opaque, const uint8_t *buf, int size)
                     81: {
                     82:     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
                     83:     int n;
                     84:     uint8_t *p;
                     85:     uint32_t crc;
                     86: 
                     87:     if ((s->rctl & SE_RCTL_RXEN) == 0)
                     88:         return;
                     89:     if (s->np >= 31) {
                     90:         DPRINTF("Packet dropped\n");
                     91:         return;
                     92:     }
                     93: 
                     94:     DPRINTF("Received packet len=%d\n", size);
                     95:     n = s->next_packet + s->np;
                     96:     if (n >= 31)
                     97:         n -= 31;
                     98:     s->np++;
                     99: 
                    100:     s->rx[n].len = size + 6;
                    101:     p = s->rx[n].data;
                    102:     *(p++) = (size + 6);
                    103:     *(p++) = (size + 6) >> 8;
                    104:     memcpy (p, buf, size);
                    105:     p += size;
                    106:     crc = crc32(~0, buf, size);
                    107:     *(p++) = crc;
                    108:     *(p++) = crc >> 8;
                    109:     *(p++) = crc >> 16;
                    110:     *(p++) = crc >> 24;
                    111:     /* Clear the remaining bytes in the last word.  */
                    112:     if ((size & 3) != 2) {
                    113:         memset(p, 0, (6 - size) & 3);
                    114:     }
                    115: 
                    116:     s->ris |= SE_INT_RX;
                    117:     stellaris_enet_update(s);
                    118: }
                    119: 
                    120: static int stellaris_enet_can_receive(void *opaque)
                    121: {
                    122:     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
                    123: 
                    124:     if ((s->rctl & SE_RCTL_RXEN) == 0)
                    125:         return 1;
                    126: 
                    127:     return (s->np < 31);
                    128: }
                    129: 
                    130: static uint32_t stellaris_enet_read(void *opaque, target_phys_addr_t offset)
                    131: {
                    132:     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
                    133:     uint32_t val;
                    134: 
                    135:     switch (offset) {
                    136:     case 0x00: /* RIS */
                    137:         DPRINTF("IRQ status %02x\n", s->ris);
                    138:         return s->ris;
                    139:     case 0x04: /* IM */
                    140:         return s->im;
                    141:     case 0x08: /* RCTL */
                    142:         return s->rctl;
                    143:     case 0x0c: /* TCTL */
                    144:         return s->tctl;
                    145:     case 0x10: /* DATA */
                    146:         if (s->rx_fifo_len == 0) {
                    147:             if (s->np == 0) {
                    148:                 BADF("RX underflow\n");
                    149:                 return 0;
                    150:             }
                    151:             s->rx_fifo_len = s->rx[s->next_packet].len;
                    152:             s->rx_fifo = s->rx[s->next_packet].data;
                    153:             DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len);
                    154:         }
                    155:         val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16)
                    156:               | (s->rx_fifo[3] << 24);
                    157:         s->rx_fifo += 4;
                    158:         s->rx_fifo_len -= 4;
                    159:         if (s->rx_fifo_len <= 0) {
                    160:             s->rx_fifo_len = 0;
                    161:             s->next_packet++;
                    162:             if (s->next_packet >= 31)
                    163:                 s->next_packet = 0;
                    164:             s->np--;
                    165:             DPRINTF("RX done np=%d\n", s->np);
                    166:         }
                    167:         return val;
                    168:     case 0x14: /* IA0 */
                    169:         return s->macaddr[0] | (s->macaddr[1] << 8)
                    170:                | (s->macaddr[2] << 16) | (s->macaddr[3] << 24);
                    171:     case 0x18: /* IA1 */
                    172:         return s->macaddr[4] | (s->macaddr[5] << 8);
                    173:     case 0x1c: /* THR */
                    174:         return s->thr;
                    175:     case 0x20: /* MCTL */
                    176:         return s->mctl;
                    177:     case 0x24: /* MDV */
                    178:         return s->mdv;
                    179:     case 0x28: /* MADD */
                    180:         return 0;
                    181:     case 0x2c: /* MTXD */
                    182:         return s->mtxd;
                    183:     case 0x30: /* MRXD */
                    184:         return s->mrxd;
                    185:     case 0x34: /* NP */
                    186:         return s->np;
                    187:     case 0x38: /* TR */
                    188:         return 0;
                    189:     case 0x3c: /* Undocuented: Timestamp? */
                    190:         return 0;
                    191:     default:
                    192:         cpu_abort (cpu_single_env, "stellaris_enet_read: Bad offset %x\n",
                    193:                    (int)offset);
                    194:         return 0;
                    195:     }
                    196: }
                    197: 
                    198: static void stellaris_enet_write(void *opaque, target_phys_addr_t offset,
                    199:                         uint32_t value)
                    200: {
                    201:     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
                    202: 
                    203:     switch (offset) {
                    204:     case 0x00: /* IACK */
                    205:         s->ris &= ~value;
                    206:         DPRINTF("IRQ ack %02x/%02x\n", value, s->ris);
                    207:         stellaris_enet_update(s);
                    208:         /* Clearing TXER also resets the TX fifo.  */
                    209:         if (value & SE_INT_TXER)
                    210:             s->tx_frame_len = -1;
                    211:         break;
                    212:     case 0x04: /* IM */
                    213:         DPRINTF("IRQ mask %02x/%02x\n", value, s->ris);
                    214:         s->im = value;
                    215:         stellaris_enet_update(s);
                    216:         break;
                    217:     case 0x08: /* RCTL */
                    218:         s->rctl = value;
                    219:         if (value & SE_RCTL_RSTFIFO) {
                    220:             s->rx_fifo_len = 0;
                    221:             s->np = 0;
                    222:             stellaris_enet_update(s);
                    223:         }
                    224:         break;
                    225:     case 0x0c: /* TCTL */
                    226:         s->tctl = value;
                    227:         break;
                    228:     case 0x10: /* DATA */
                    229:         if (s->tx_frame_len == -1) {
                    230:             s->tx_frame_len = value & 0xffff;
                    231:             if (s->tx_frame_len > 2032) {
                    232:                 DPRINTF("TX frame too long (%d)\n", s->tx_frame_len);
                    233:                 s->tx_frame_len = 0;
                    234:                 s->ris |= SE_INT_TXER;
                    235:                 stellaris_enet_update(s);
                    236:             } else {
                    237:                 DPRINTF("Start TX frame len=%d\n", s->tx_frame_len);
                    238:                 /* The value written does not include the ethernet header.  */
                    239:                 s->tx_frame_len += 14;
                    240:                 if ((s->tctl & SE_TCTL_CRC) == 0)
                    241:                     s->tx_frame_len += 4;
                    242:                 s->tx_fifo_len = 0;
                    243:                 s->tx_fifo[s->tx_fifo_len++] = value >> 16;
                    244:                 s->tx_fifo[s->tx_fifo_len++] = value >> 24;
                    245:             }
                    246:         } else {
                    247:             s->tx_fifo[s->tx_fifo_len++] = value;
                    248:             s->tx_fifo[s->tx_fifo_len++] = value >> 8;
                    249:             s->tx_fifo[s->tx_fifo_len++] = value >> 16;
                    250:             s->tx_fifo[s->tx_fifo_len++] = value >> 24;
                    251:             if (s->tx_fifo_len >= s->tx_frame_len) {
                    252:                 /* We don't implement explicit CRC, so just chop it off.  */
                    253:                 if ((s->tctl & SE_TCTL_CRC) == 0)
                    254:                     s->tx_frame_len -= 4;
                    255:                 if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) {
                    256:                     memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len);
                    257:                     s->tx_fifo_len = 60;
                    258:                 }
                    259:                 qemu_send_packet(s->vc, s->tx_fifo, s->tx_frame_len);
                    260:                 s->tx_frame_len = -1;
                    261:                 s->ris |= SE_INT_TXEMP;
                    262:                 stellaris_enet_update(s);
                    263:                 DPRINTF("Done TX\n");
                    264:             }
                    265:         }
                    266:         break;
                    267:     case 0x14: /* IA0 */
                    268:         s->macaddr[0] = value;
                    269:         s->macaddr[1] = value >> 8;
                    270:         s->macaddr[2] = value >> 16;
                    271:         s->macaddr[3] = value >> 24;
                    272:         break;
                    273:     case 0x18: /* IA1 */
                    274:         s->macaddr[4] = value;
                    275:         s->macaddr[5] = value >> 8;
                    276:         break;
                    277:     case 0x1c: /* THR */
                    278:         s->thr = value;
                    279:         break;
                    280:     case 0x20: /* MCTL */
                    281:         s->mctl = value;
                    282:         break;
                    283:     case 0x24: /* MDV */
                    284:         s->mdv = value;
                    285:         break;
                    286:     case 0x28: /* MADD */
                    287:         /* ignored.  */
                    288:         break;
                    289:     case 0x2c: /* MTXD */
                    290:         s->mtxd = value & 0xff;
                    291:         break;
                    292:     case 0x30: /* MRXD */
                    293:     case 0x34: /* NP */
                    294:     case 0x38: /* TR */
                    295:         /* Ignored.  */
                    296:     case 0x3c: /* Undocuented: Timestamp? */
                    297:         /* Ignored.  */
                    298:         break;
                    299:     default:
                    300:         cpu_abort (cpu_single_env, "stellaris_enet_write: Bad offset %x\n",
                    301:                    (int)offset);
                    302:     }
                    303: }
                    304: 
                    305: static CPUReadMemoryFunc *stellaris_enet_readfn[] = {
                    306:    stellaris_enet_read,
                    307:    stellaris_enet_read,
                    308:    stellaris_enet_read
                    309: };
                    310: 
                    311: static CPUWriteMemoryFunc *stellaris_enet_writefn[] = {
                    312:    stellaris_enet_write,
                    313:    stellaris_enet_write,
                    314:    stellaris_enet_write
                    315: };
                    316: static void stellaris_enet_reset(stellaris_enet_state *s)
                    317: {
                    318:     s->mdv = 0x80;
                    319:     s->rctl = SE_RCTL_BADCRC;
                    320:     s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP
                    321:             | SE_INT_TXER | SE_INT_RX;
                    322:     s->thr = 0x3f;
                    323:     s->tx_frame_len = -1;
                    324: }
                    325: 
1.1.1.2 ! root      326: static void stellaris_enet_save(QEMUFile *f, void *opaque)
        !           327: {
        !           328:     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
        !           329:     int i;
        !           330: 
        !           331:     qemu_put_be32(f, s->ris);
        !           332:     qemu_put_be32(f, s->im);
        !           333:     qemu_put_be32(f, s->rctl);
        !           334:     qemu_put_be32(f, s->tctl);
        !           335:     qemu_put_be32(f, s->thr);
        !           336:     qemu_put_be32(f, s->mctl);
        !           337:     qemu_put_be32(f, s->mdv);
        !           338:     qemu_put_be32(f, s->mtxd);
        !           339:     qemu_put_be32(f, s->mrxd);
        !           340:     qemu_put_be32(f, s->np);
        !           341:     qemu_put_be32(f, s->tx_frame_len);
        !           342:     qemu_put_be32(f, s->tx_fifo_len);
        !           343:     qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
        !           344:     for (i = 0; i < 31; i++) {
        !           345:         qemu_put_be32(f, s->rx[i].len);
        !           346:         qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
        !           347: 
        !           348:     }
        !           349:     qemu_put_be32(f, s->next_packet);
        !           350:     qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data);
        !           351:     qemu_put_be32(f, s->rx_fifo_len);
        !           352: }
        !           353: 
        !           354: static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
        !           355: {
        !           356:     stellaris_enet_state *s = (stellaris_enet_state *)opaque;
        !           357:     int i;
        !           358: 
        !           359:     if (version_id != 1)
        !           360:         return -EINVAL;
        !           361: 
        !           362:     s->ris = qemu_get_be32(f);
        !           363:     s->im = qemu_get_be32(f);
        !           364:     s->rctl = qemu_get_be32(f);
        !           365:     s->tctl = qemu_get_be32(f);
        !           366:     s->thr = qemu_get_be32(f);
        !           367:     s->mctl = qemu_get_be32(f);
        !           368:     s->mdv = qemu_get_be32(f);
        !           369:     s->mtxd = qemu_get_be32(f);
        !           370:     s->mrxd = qemu_get_be32(f);
        !           371:     s->np = qemu_get_be32(f);
        !           372:     s->tx_frame_len = qemu_get_be32(f);
        !           373:     s->tx_fifo_len = qemu_get_be32(f);
        !           374:     qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
        !           375:     for (i = 0; i < 31; i++) {
        !           376:         s->rx[i].len = qemu_get_be32(f);
        !           377:         qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
        !           378: 
        !           379:     }
        !           380:     s->next_packet = qemu_get_be32(f);
        !           381:     s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f);
        !           382:     s->rx_fifo_len = qemu_get_be32(f);
        !           383: 
        !           384:     return 0;
        !           385: }
        !           386: 
1.1       root      387: void stellaris_enet_init(NICInfo *nd, uint32_t base, qemu_irq irq)
                    388: {
                    389:     stellaris_enet_state *s;
                    390:     int iomemtype;
                    391: 
1.1.1.2 ! root      392:     qemu_check_nic_model(nd, "stellaris");
        !           393: 
1.1       root      394:     s = (stellaris_enet_state *)qemu_mallocz(sizeof(stellaris_enet_state));
                    395:     iomemtype = cpu_register_io_memory(0, stellaris_enet_readfn,
                    396:                                        stellaris_enet_writefn, s);
                    397:     cpu_register_physical_memory(base, 0x00001000, iomemtype);
                    398:     s->irq = irq;
                    399:     memcpy(s->macaddr, nd->macaddr, 6);
                    400: 
1.1.1.2 ! root      401:     if (nd->vlan) {
        !           402:         s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
        !           403:                                      stellaris_enet_receive, stellaris_enet_can_receive, s);
        !           404:         qemu_format_nic_info_str(s->vc, s->macaddr);
        !           405:     }
1.1       root      406: 
                    407:     stellaris_enet_reset(s);
1.1.1.2 ! root      408:     register_savevm("stellaris_enet", -1, 1,
        !           409:                     stellaris_enet_save, stellaris_enet_load, s);
1.1       root      410: }

unix.superglobalmegacorp.com