Annotation of qemu/hw/milkymist-minimac2.c, revision 1.1

1.1     ! root        1: /*
        !             2:  *  QEMU model of the Milkymist minimac2 block.
        !             3:  *
        !             4:  *  Copyright (c) 2011 Michael Walle <[email protected]>
        !             5:  *
        !             6:  * This library is free software; you can redistribute it and/or
        !             7:  * modify it under the terms of the GNU Lesser General Public
        !             8:  * License as published by the Free Software Foundation; either
        !             9:  * version 2 of the License, or (at your option) any later version.
        !            10:  *
        !            11:  * This library is distributed in the hope that it will be useful,
        !            12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        !            14:  * Lesser General Public License for more details.
        !            15:  *
        !            16:  * You should have received a copy of the GNU Lesser General Public
        !            17:  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
        !            18:  *
        !            19:  *
        !            20:  * Specification available at:
        !            21:  *   not available yet
        !            22:  *
        !            23:  */
        !            24: 
        !            25: #include "hw.h"
        !            26: #include "sysbus.h"
        !            27: #include "trace.h"
        !            28: #include "net.h"
        !            29: #include "qemu-error.h"
        !            30: #include "qdev-addr.h"
        !            31: 
        !            32: #include <zlib.h>
        !            33: 
        !            34: enum {
        !            35:     R_SETUP = 0,
        !            36:     R_MDIO,
        !            37:     R_STATE0,
        !            38:     R_COUNT0,
        !            39:     R_STATE1,
        !            40:     R_COUNT1,
        !            41:     R_TXCOUNT,
        !            42:     R_MAX
        !            43: };
        !            44: 
        !            45: enum {
        !            46:     SETUP_PHY_RST = (1<<0),
        !            47: };
        !            48: 
        !            49: enum {
        !            50:     MDIO_DO  = (1<<0),
        !            51:     MDIO_DI  = (1<<1),
        !            52:     MDIO_OE  = (1<<2),
        !            53:     MDIO_CLK = (1<<3),
        !            54: };
        !            55: 
        !            56: enum {
        !            57:     STATE_EMPTY   = 0,
        !            58:     STATE_LOADED  = 1,
        !            59:     STATE_PENDING = 2,
        !            60: };
        !            61: 
        !            62: enum {
        !            63:     MDIO_OP_WRITE = 1,
        !            64:     MDIO_OP_READ  = 2,
        !            65: };
        !            66: 
        !            67: enum mdio_state {
        !            68:     MDIO_STATE_IDLE,
        !            69:     MDIO_STATE_READING,
        !            70:     MDIO_STATE_WRITING,
        !            71: };
        !            72: 
        !            73: enum {
        !            74:     R_PHY_ID1  = 2,
        !            75:     R_PHY_ID2  = 3,
        !            76:     R_PHY_MAX  = 32
        !            77: };
        !            78: 
        !            79: #define MINIMAC2_MTU 1530
        !            80: #define MINIMAC2_BUFFER_SIZE 2048
        !            81: 
        !            82: struct MilkymistMinimac2MdioState {
        !            83:     int last_clk;
        !            84:     int count;
        !            85:     uint32_t data;
        !            86:     uint16_t data_out;
        !            87:     int state;
        !            88: 
        !            89:     uint8_t phy_addr;
        !            90:     uint8_t reg_addr;
        !            91: };
        !            92: typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState;
        !            93: 
        !            94: struct MilkymistMinimac2State {
        !            95:     SysBusDevice busdev;
        !            96:     NICState *nic;
        !            97:     NICConf conf;
        !            98:     char *phy_model;
        !            99:     target_phys_addr_t buffers_base;
        !           100: 
        !           101:     qemu_irq rx_irq;
        !           102:     qemu_irq tx_irq;
        !           103: 
        !           104:     uint32_t regs[R_MAX];
        !           105: 
        !           106:     MilkymistMinimac2MdioState mdio;
        !           107: 
        !           108:     uint16_t phy_regs[R_PHY_MAX];
        !           109: 
        !           110:     uint8_t *rx0_buf;
        !           111:     uint8_t *rx1_buf;
        !           112:     uint8_t *tx_buf;
        !           113: };
        !           114: typedef struct MilkymistMinimac2State MilkymistMinimac2State;
        !           115: 
        !           116: static const uint8_t preamble_sfd[] = {
        !           117:         0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
        !           118: };
        !           119: 
        !           120: static void minimac2_mdio_write_reg(MilkymistMinimac2State *s,
        !           121:         uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
        !           122: {
        !           123:     trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value);
        !           124: 
        !           125:     /* nop */
        !           126: }
        !           127: 
        !           128: static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s,
        !           129:         uint8_t phy_addr, uint8_t reg_addr)
        !           130: {
        !           131:     uint16_t r = s->phy_regs[reg_addr];
        !           132: 
        !           133:     trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r);
        !           134: 
        !           135:     return r;
        !           136: }
        !           137: 
        !           138: static void minimac2_update_mdio(MilkymistMinimac2State *s)
        !           139: {
        !           140:     MilkymistMinimac2MdioState *m = &s->mdio;
        !           141: 
        !           142:     /* detect rising clk edge */
        !           143:     if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
        !           144:         /* shift data in */
        !           145:         int bit = ((s->regs[R_MDIO] & MDIO_DO)
        !           146:                    && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
        !           147:         m->data = (m->data << 1) | bit;
        !           148: 
        !           149:         /* check for sync */
        !           150:         if (m->data == 0xffffffff) {
        !           151:             m->count = 32;
        !           152:         }
        !           153: 
        !           154:         if (m->count == 16) {
        !           155:             uint8_t start = (m->data >> 14) & 0x3;
        !           156:             uint8_t op = (m->data >> 12) & 0x3;
        !           157:             uint8_t ta = (m->data) & 0x3;
        !           158: 
        !           159:             if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
        !           160:                 m->state = MDIO_STATE_WRITING;
        !           161:             } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
        !           162:                 m->state = MDIO_STATE_READING;
        !           163:             } else {
        !           164:                 m->state = MDIO_STATE_IDLE;
        !           165:             }
        !           166: 
        !           167:             if (m->state != MDIO_STATE_IDLE) {
        !           168:                 m->phy_addr = (m->data >> 7) & 0x1f;
        !           169:                 m->reg_addr = (m->data >> 2) & 0x1f;
        !           170:             }
        !           171: 
        !           172:             if (m->state == MDIO_STATE_READING) {
        !           173:                 m->data_out = minimac2_mdio_read_reg(s, m->phy_addr,
        !           174:                         m->reg_addr);
        !           175:             }
        !           176:         }
        !           177: 
        !           178:         if (m->count < 16 && m->state == MDIO_STATE_READING) {
        !           179:             int bit = (m->data_out & 0x8000) ? 1 : 0;
        !           180:             m->data_out <<= 1;
        !           181: 
        !           182:             if (bit) {
        !           183:                 s->regs[R_MDIO] |= MDIO_DI;
        !           184:             } else {
        !           185:                 s->regs[R_MDIO] &= ~MDIO_DI;
        !           186:             }
        !           187:         }
        !           188: 
        !           189:         if (m->count == 0 && m->state) {
        !           190:             if (m->state == MDIO_STATE_WRITING) {
        !           191:                 uint16_t data = m->data & 0xffff;
        !           192:                 minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
        !           193:             }
        !           194:             m->state = MDIO_STATE_IDLE;
        !           195:         }
        !           196:         m->count--;
        !           197:     }
        !           198: 
        !           199:     m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
        !           200: }
        !           201: 
        !           202: static size_t assemble_frame(uint8_t *buf, size_t size,
        !           203:         const uint8_t *payload, size_t payload_size)
        !           204: {
        !           205:     uint32_t crc;
        !           206: 
        !           207:     if (size < payload_size + 12) {
        !           208:         error_report("milkymist_minimac2: received too big ethernet frame");
        !           209:         return 0;
        !           210:     }
        !           211: 
        !           212:     /* prepend preamble and sfd */
        !           213:     memcpy(buf, preamble_sfd, 8);
        !           214: 
        !           215:     /* now copy the payload */
        !           216:     memcpy(buf + 8, payload, payload_size);
        !           217: 
        !           218:     /* pad frame if needed */
        !           219:     if (payload_size < 60) {
        !           220:         memset(buf + payload_size + 8, 0, 60 - payload_size);
        !           221:         payload_size = 60;
        !           222:     }
        !           223: 
        !           224:     /* append fcs */
        !           225:     crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
        !           226:     memcpy(buf + payload_size + 8, &crc, 4);
        !           227: 
        !           228:     return payload_size + 12;
        !           229: }
        !           230: 
        !           231: static void minimac2_tx(MilkymistMinimac2State *s)
        !           232: {
        !           233:     uint32_t txcount = s->regs[R_TXCOUNT];
        !           234:     uint8_t *buf = s->tx_buf;
        !           235: 
        !           236:     if (txcount < 64) {
        !           237:         error_report("milkymist_minimac2: ethernet frame too small (%u < %u)",
        !           238:                 txcount, 64);
        !           239:         goto err;
        !           240:     }
        !           241: 
        !           242:     if (txcount > MINIMAC2_MTU) {
        !           243:         error_report("milkymist_minimac2: MTU exceeded (%u > %u)",
        !           244:                 txcount, MINIMAC2_MTU);
        !           245:         goto err;
        !           246:     }
        !           247: 
        !           248:     if (memcmp(buf, preamble_sfd, 8) != 0) {
        !           249:         error_report("milkymist_minimac2: frame doesn't contain the preamble "
        !           250:                 "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)",
        !           251:                 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
        !           252:         goto err;
        !           253:     }
        !           254: 
        !           255:     trace_milkymist_minimac2_tx_frame(txcount - 12);
        !           256: 
        !           257:     /* send packet, skipping preamble and sfd */
        !           258:     qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12);
        !           259: 
        !           260:     s->regs[R_TXCOUNT] = 0;
        !           261: 
        !           262: err:
        !           263:     trace_milkymist_minimac2_pulse_irq_tx();
        !           264:     qemu_irq_pulse(s->tx_irq);
        !           265: }
        !           266: 
        !           267: static void update_rx_interrupt(MilkymistMinimac2State *s)
        !           268: {
        !           269:     if (s->regs[R_STATE0] == STATE_PENDING
        !           270:             || s->regs[R_STATE1] == STATE_PENDING) {
        !           271:         trace_milkymist_minimac2_raise_irq_rx();
        !           272:         qemu_irq_raise(s->rx_irq);
        !           273:     } else {
        !           274:         trace_milkymist_minimac2_lower_irq_rx();
        !           275:         qemu_irq_lower(s->rx_irq);
        !           276:     }
        !           277: }
        !           278: 
        !           279: static ssize_t minimac2_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
        !           280: {
        !           281:     MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
        !           282: 
        !           283:     uint32_t r_count;
        !           284:     uint32_t r_state;
        !           285:     uint8_t *rx_buf;
        !           286: 
        !           287:     size_t frame_size;
        !           288: 
        !           289:     trace_milkymist_minimac2_rx_frame(buf, size);
        !           290: 
        !           291:     /* choose appropriate slot */
        !           292:     if (s->regs[R_STATE0] == STATE_LOADED) {
        !           293:         r_count = R_COUNT0;
        !           294:         r_state = R_STATE0;
        !           295:         rx_buf = s->rx0_buf;
        !           296:     } else if (s->regs[R_STATE1] == STATE_LOADED) {
        !           297:         r_count = R_COUNT1;
        !           298:         r_state = R_STATE1;
        !           299:         rx_buf = s->rx1_buf;
        !           300:     } else {
        !           301:         trace_milkymist_minimac2_drop_rx_frame(buf);
        !           302:         return size;
        !           303:     }
        !           304: 
        !           305:     /* assemble frame */
        !           306:     frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size);
        !           307: 
        !           308:     if (frame_size == 0) {
        !           309:         return size;
        !           310:     }
        !           311: 
        !           312:     trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size);
        !           313: 
        !           314:     /* update slot */
        !           315:     s->regs[r_count] = frame_size;
        !           316:     s->regs[r_state] = STATE_PENDING;
        !           317: 
        !           318:     update_rx_interrupt(s);
        !           319: 
        !           320:     return size;
        !           321: }
        !           322: 
        !           323: static uint32_t
        !           324: minimac2_read(void *opaque, target_phys_addr_t addr)
        !           325: {
        !           326:     MilkymistMinimac2State *s = opaque;
        !           327:     uint32_t r = 0;
        !           328: 
        !           329:     addr >>= 2;
        !           330:     switch (addr) {
        !           331:     case R_SETUP:
        !           332:     case R_MDIO:
        !           333:     case R_STATE0:
        !           334:     case R_COUNT0:
        !           335:     case R_STATE1:
        !           336:     case R_COUNT1:
        !           337:     case R_TXCOUNT:
        !           338:         r = s->regs[addr];
        !           339:         break;
        !           340: 
        !           341:     default:
        !           342:         error_report("milkymist_minimac2: read access to unknown register 0x"
        !           343:                 TARGET_FMT_plx, addr << 2);
        !           344:         break;
        !           345:     }
        !           346: 
        !           347:     trace_milkymist_minimac2_memory_read(addr << 2, r);
        !           348: 
        !           349:     return r;
        !           350: }
        !           351: 
        !           352: static void
        !           353: minimac2_write(void *opaque, target_phys_addr_t addr, uint32_t value)
        !           354: {
        !           355:     MilkymistMinimac2State *s = opaque;
        !           356: 
        !           357:     trace_milkymist_minimac2_memory_read(addr, value);
        !           358: 
        !           359:     addr >>= 2;
        !           360:     switch (addr) {
        !           361:     case R_MDIO:
        !           362:     {
        !           363:         /* MDIO_DI is read only */
        !           364:         int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
        !           365:         s->regs[R_MDIO] = value;
        !           366:         if (mdio_di) {
        !           367:             s->regs[R_MDIO] |= mdio_di;
        !           368:         } else {
        !           369:             s->regs[R_MDIO] &= ~mdio_di;
        !           370:         }
        !           371: 
        !           372:         minimac2_update_mdio(s);
        !           373:     } break;
        !           374:     case R_TXCOUNT:
        !           375:         s->regs[addr] = value;
        !           376:         if (value > 0) {
        !           377:             minimac2_tx(s);
        !           378:         }
        !           379:         break;
        !           380:     case R_STATE0:
        !           381:     case R_STATE1:
        !           382:         s->regs[addr] = value;
        !           383:         update_rx_interrupt(s);
        !           384:         break;
        !           385:     case R_SETUP:
        !           386:     case R_COUNT0:
        !           387:     case R_COUNT1:
        !           388:         s->regs[addr] = value;
        !           389:         break;
        !           390: 
        !           391:     default:
        !           392:         error_report("milkymist_minimac2: write access to unknown register 0x"
        !           393:                 TARGET_FMT_plx, addr << 2);
        !           394:         break;
        !           395:     }
        !           396: }
        !           397: 
        !           398: static CPUReadMemoryFunc * const minimac2_read_fn[] = {
        !           399:     NULL,
        !           400:     NULL,
        !           401:     &minimac2_read,
        !           402: };
        !           403: 
        !           404: static CPUWriteMemoryFunc * const minimac2_write_fn[] = {
        !           405:     NULL,
        !           406:     NULL,
        !           407:     &minimac2_write,
        !           408: };
        !           409: 
        !           410: static int minimac2_can_rx(VLANClientState *nc)
        !           411: {
        !           412:     MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
        !           413: 
        !           414:     if (s->regs[R_STATE0] == STATE_LOADED) {
        !           415:         return 1;
        !           416:     }
        !           417:     if (s->regs[R_STATE1] == STATE_LOADED) {
        !           418:         return 1;
        !           419:     }
        !           420: 
        !           421:     return 0;
        !           422: }
        !           423: 
        !           424: static void minimac2_cleanup(VLANClientState *nc)
        !           425: {
        !           426:     MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
        !           427: 
        !           428:     s->nic = NULL;
        !           429: }
        !           430: 
        !           431: static void milkymist_minimac2_reset(DeviceState *d)
        !           432: {
        !           433:     MilkymistMinimac2State *s =
        !           434:             container_of(d, MilkymistMinimac2State, busdev.qdev);
        !           435:     int i;
        !           436: 
        !           437:     for (i = 0; i < R_MAX; i++) {
        !           438:         s->regs[i] = 0;
        !           439:     }
        !           440:     for (i = 0; i < R_PHY_MAX; i++) {
        !           441:         s->phy_regs[i] = 0;
        !           442:     }
        !           443: 
        !           444:     /* defaults */
        !           445:     s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
        !           446:     s->phy_regs[R_PHY_ID2] = 0x161a;
        !           447: }
        !           448: 
        !           449: static NetClientInfo net_milkymist_minimac2_info = {
        !           450:     .type = NET_CLIENT_TYPE_NIC,
        !           451:     .size = sizeof(NICState),
        !           452:     .can_receive = minimac2_can_rx,
        !           453:     .receive = minimac2_rx,
        !           454:     .cleanup = minimac2_cleanup,
        !           455: };
        !           456: 
        !           457: static int milkymist_minimac2_init(SysBusDevice *dev)
        !           458: {
        !           459:     MilkymistMinimac2State *s = FROM_SYSBUS(typeof(*s), dev);
        !           460:     int regs;
        !           461:     ram_addr_t buffers;
        !           462:     size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE);
        !           463: 
        !           464:     sysbus_init_irq(dev, &s->rx_irq);
        !           465:     sysbus_init_irq(dev, &s->tx_irq);
        !           466: 
        !           467:     regs = cpu_register_io_memory(minimac2_read_fn, minimac2_write_fn, s,
        !           468:             DEVICE_NATIVE_ENDIAN);
        !           469:     sysbus_init_mmio(dev, R_MAX * 4, regs);
        !           470: 
        !           471:     /* register buffers memory */
        !           472:     buffers = qemu_ram_alloc(NULL, "milkymist_minimac2.buffers", buffers_size);
        !           473:     s->rx0_buf = qemu_get_ram_ptr(buffers);
        !           474:     s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
        !           475:     s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
        !           476: 
        !           477:     cpu_register_physical_memory(s->buffers_base, buffers_size,
        !           478:             buffers | IO_MEM_RAM);
        !           479: 
        !           480:     qemu_macaddr_default_if_unset(&s->conf.macaddr);
        !           481:     s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
        !           482:                           dev->qdev.info->name, dev->qdev.id, s);
        !           483:     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
        !           484: 
        !           485:     return 0;
        !           486: }
        !           487: 
        !           488: static const VMStateDescription vmstate_milkymist_minimac2_mdio = {
        !           489:     .name = "milkymist-minimac2-mdio",
        !           490:     .version_id = 1,
        !           491:     .minimum_version_id = 1,
        !           492:     .minimum_version_id_old = 1,
        !           493:     .fields      = (VMStateField[]) {
        !           494:         VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState),
        !           495:         VMSTATE_INT32(count, MilkymistMinimac2MdioState),
        !           496:         VMSTATE_UINT32(data, MilkymistMinimac2MdioState),
        !           497:         VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState),
        !           498:         VMSTATE_INT32(state, MilkymistMinimac2MdioState),
        !           499:         VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState),
        !           500:         VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState),
        !           501:         VMSTATE_END_OF_LIST()
        !           502:     }
        !           503: };
        !           504: 
        !           505: static const VMStateDescription vmstate_milkymist_minimac2 = {
        !           506:     .name = "milkymist-minimac2",
        !           507:     .version_id = 1,
        !           508:     .minimum_version_id = 1,
        !           509:     .minimum_version_id_old = 1,
        !           510:     .fields      = (VMStateField[]) {
        !           511:         VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX),
        !           512:         VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX),
        !           513:         VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0,
        !           514:                 vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState),
        !           515:         VMSTATE_END_OF_LIST()
        !           516:     }
        !           517: };
        !           518: 
        !           519: static SysBusDeviceInfo milkymist_minimac2_info = {
        !           520:     .init = milkymist_minimac2_init,
        !           521:     .qdev.name  = "milkymist-minimac2",
        !           522:     .qdev.size  = sizeof(MilkymistMinimac2State),
        !           523:     .qdev.vmsd  = &vmstate_milkymist_minimac2,
        !           524:     .qdev.reset = milkymist_minimac2_reset,
        !           525:     .qdev.props = (Property[]) {
        !           526:         DEFINE_PROP_TADDR("buffers_base", MilkymistMinimac2State,
        !           527:                 buffers_base, 0),
        !           528:         DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf),
        !           529:         DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model),
        !           530:         DEFINE_PROP_END_OF_LIST(),
        !           531:     }
        !           532: };
        !           533: 
        !           534: static void milkymist_minimac2_register(void)
        !           535: {
        !           536:     sysbus_register_withprop(&milkymist_minimac2_info);
        !           537: }
        !           538: 
        !           539: device_init(milkymist_minimac2_register)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.