File:  [Qemu by Fabrice Bellard] / qemu / hw / milkymist-minimac2.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 19:28:27 2018 UTC (2 years, 7 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu1001, HEAD
qemu 1.0.1

    1: /*
    2:  *  QEMU model of the Milkymist minimac2 block.
    3:  *
    4:  *  Copyright (c) 2011 Michael Walle <michael@walle.cc>
    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:     MemoryRegion buffers;
  101:     MemoryRegion regs_region;
  102: 
  103:     qemu_irq rx_irq;
  104:     qemu_irq tx_irq;
  105: 
  106:     uint32_t regs[R_MAX];
  107: 
  108:     MilkymistMinimac2MdioState mdio;
  109: 
  110:     uint16_t phy_regs[R_PHY_MAX];
  111: 
  112:     uint8_t *rx0_buf;
  113:     uint8_t *rx1_buf;
  114:     uint8_t *tx_buf;
  115: };
  116: typedef struct MilkymistMinimac2State MilkymistMinimac2State;
  117: 
  118: static const uint8_t preamble_sfd[] = {
  119:         0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
  120: };
  121: 
  122: static void minimac2_mdio_write_reg(MilkymistMinimac2State *s,
  123:         uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
  124: {
  125:     trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value);
  126: 
  127:     /* nop */
  128: }
  129: 
  130: static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s,
  131:         uint8_t phy_addr, uint8_t reg_addr)
  132: {
  133:     uint16_t r = s->phy_regs[reg_addr];
  134: 
  135:     trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r);
  136: 
  137:     return r;
  138: }
  139: 
  140: static void minimac2_update_mdio(MilkymistMinimac2State *s)
  141: {
  142:     MilkymistMinimac2MdioState *m = &s->mdio;
  143: 
  144:     /* detect rising clk edge */
  145:     if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
  146:         /* shift data in */
  147:         int bit = ((s->regs[R_MDIO] & MDIO_DO)
  148:                    && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
  149:         m->data = (m->data << 1) | bit;
  150: 
  151:         /* check for sync */
  152:         if (m->data == 0xffffffff) {
  153:             m->count = 32;
  154:         }
  155: 
  156:         if (m->count == 16) {
  157:             uint8_t start = (m->data >> 14) & 0x3;
  158:             uint8_t op = (m->data >> 12) & 0x3;
  159:             uint8_t ta = (m->data) & 0x3;
  160: 
  161:             if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
  162:                 m->state = MDIO_STATE_WRITING;
  163:             } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
  164:                 m->state = MDIO_STATE_READING;
  165:             } else {
  166:                 m->state = MDIO_STATE_IDLE;
  167:             }
  168: 
  169:             if (m->state != MDIO_STATE_IDLE) {
  170:                 m->phy_addr = (m->data >> 7) & 0x1f;
  171:                 m->reg_addr = (m->data >> 2) & 0x1f;
  172:             }
  173: 
  174:             if (m->state == MDIO_STATE_READING) {
  175:                 m->data_out = minimac2_mdio_read_reg(s, m->phy_addr,
  176:                         m->reg_addr);
  177:             }
  178:         }
  179: 
  180:         if (m->count < 16 && m->state == MDIO_STATE_READING) {
  181:             int bit = (m->data_out & 0x8000) ? 1 : 0;
  182:             m->data_out <<= 1;
  183: 
  184:             if (bit) {
  185:                 s->regs[R_MDIO] |= MDIO_DI;
  186:             } else {
  187:                 s->regs[R_MDIO] &= ~MDIO_DI;
  188:             }
  189:         }
  190: 
  191:         if (m->count == 0 && m->state) {
  192:             if (m->state == MDIO_STATE_WRITING) {
  193:                 uint16_t data = m->data & 0xffff;
  194:                 minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
  195:             }
  196:             m->state = MDIO_STATE_IDLE;
  197:         }
  198:         m->count--;
  199:     }
  200: 
  201:     m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
  202: }
  203: 
  204: static size_t assemble_frame(uint8_t *buf, size_t size,
  205:         const uint8_t *payload, size_t payload_size)
  206: {
  207:     uint32_t crc;
  208: 
  209:     if (size < payload_size + 12) {
  210:         error_report("milkymist_minimac2: received too big ethernet frame");
  211:         return 0;
  212:     }
  213: 
  214:     /* prepend preamble and sfd */
  215:     memcpy(buf, preamble_sfd, 8);
  216: 
  217:     /* now copy the payload */
  218:     memcpy(buf + 8, payload, payload_size);
  219: 
  220:     /* pad frame if needed */
  221:     if (payload_size < 60) {
  222:         memset(buf + payload_size + 8, 0, 60 - payload_size);
  223:         payload_size = 60;
  224:     }
  225: 
  226:     /* append fcs */
  227:     crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
  228:     memcpy(buf + payload_size + 8, &crc, 4);
  229: 
  230:     return payload_size + 12;
  231: }
  232: 
  233: static void minimac2_tx(MilkymistMinimac2State *s)
  234: {
  235:     uint32_t txcount = s->regs[R_TXCOUNT];
  236:     uint8_t *buf = s->tx_buf;
  237: 
  238:     if (txcount < 64) {
  239:         error_report("milkymist_minimac2: ethernet frame too small (%u < %u)",
  240:                 txcount, 64);
  241:         goto err;
  242:     }
  243: 
  244:     if (txcount > MINIMAC2_MTU) {
  245:         error_report("milkymist_minimac2: MTU exceeded (%u > %u)",
  246:                 txcount, MINIMAC2_MTU);
  247:         goto err;
  248:     }
  249: 
  250:     if (memcmp(buf, preamble_sfd, 8) != 0) {
  251:         error_report("milkymist_minimac2: frame doesn't contain the preamble "
  252:                 "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)",
  253:                 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
  254:         goto err;
  255:     }
  256: 
  257:     trace_milkymist_minimac2_tx_frame(txcount - 12);
  258: 
  259:     /* send packet, skipping preamble and sfd */
  260:     qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12);
  261: 
  262:     s->regs[R_TXCOUNT] = 0;
  263: 
  264: err:
  265:     trace_milkymist_minimac2_pulse_irq_tx();
  266:     qemu_irq_pulse(s->tx_irq);
  267: }
  268: 
  269: static void update_rx_interrupt(MilkymistMinimac2State *s)
  270: {
  271:     if (s->regs[R_STATE0] == STATE_PENDING
  272:             || s->regs[R_STATE1] == STATE_PENDING) {
  273:         trace_milkymist_minimac2_raise_irq_rx();
  274:         qemu_irq_raise(s->rx_irq);
  275:     } else {
  276:         trace_milkymist_minimac2_lower_irq_rx();
  277:         qemu_irq_lower(s->rx_irq);
  278:     }
  279: }
  280: 
  281: static ssize_t minimac2_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
  282: {
  283:     MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
  284: 
  285:     uint32_t r_count;
  286:     uint32_t r_state;
  287:     uint8_t *rx_buf;
  288: 
  289:     size_t frame_size;
  290: 
  291:     trace_milkymist_minimac2_rx_frame(buf, size);
  292: 
  293:     /* choose appropriate slot */
  294:     if (s->regs[R_STATE0] == STATE_LOADED) {
  295:         r_count = R_COUNT0;
  296:         r_state = R_STATE0;
  297:         rx_buf = s->rx0_buf;
  298:     } else if (s->regs[R_STATE1] == STATE_LOADED) {
  299:         r_count = R_COUNT1;
  300:         r_state = R_STATE1;
  301:         rx_buf = s->rx1_buf;
  302:     } else {
  303:         trace_milkymist_minimac2_drop_rx_frame(buf);
  304:         return size;
  305:     }
  306: 
  307:     /* assemble frame */
  308:     frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size);
  309: 
  310:     if (frame_size == 0) {
  311:         return size;
  312:     }
  313: 
  314:     trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size);
  315: 
  316:     /* update slot */
  317:     s->regs[r_count] = frame_size;
  318:     s->regs[r_state] = STATE_PENDING;
  319: 
  320:     update_rx_interrupt(s);
  321: 
  322:     return size;
  323: }
  324: 
  325: static uint64_t
  326: minimac2_read(void *opaque, target_phys_addr_t addr, unsigned size)
  327: {
  328:     MilkymistMinimac2State *s = opaque;
  329:     uint32_t r = 0;
  330: 
  331:     addr >>= 2;
  332:     switch (addr) {
  333:     case R_SETUP:
  334:     case R_MDIO:
  335:     case R_STATE0:
  336:     case R_COUNT0:
  337:     case R_STATE1:
  338:     case R_COUNT1:
  339:     case R_TXCOUNT:
  340:         r = s->regs[addr];
  341:         break;
  342: 
  343:     default:
  344:         error_report("milkymist_minimac2: read access to unknown register 0x"
  345:                 TARGET_FMT_plx, addr << 2);
  346:         break;
  347:     }
  348: 
  349:     trace_milkymist_minimac2_memory_read(addr << 2, r);
  350: 
  351:     return r;
  352: }
  353: 
  354: static void
  355: minimac2_write(void *opaque, target_phys_addr_t addr, uint64_t value,
  356:                unsigned size)
  357: {
  358:     MilkymistMinimac2State *s = opaque;
  359: 
  360:     trace_milkymist_minimac2_memory_read(addr, value);
  361: 
  362:     addr >>= 2;
  363:     switch (addr) {
  364:     case R_MDIO:
  365:     {
  366:         /* MDIO_DI is read only */
  367:         int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
  368:         s->regs[R_MDIO] = value;
  369:         if (mdio_di) {
  370:             s->regs[R_MDIO] |= mdio_di;
  371:         } else {
  372:             s->regs[R_MDIO] &= ~mdio_di;
  373:         }
  374: 
  375:         minimac2_update_mdio(s);
  376:     } break;
  377:     case R_TXCOUNT:
  378:         s->regs[addr] = value;
  379:         if (value > 0) {
  380:             minimac2_tx(s);
  381:         }
  382:         break;
  383:     case R_STATE0:
  384:     case R_STATE1:
  385:         s->regs[addr] = value;
  386:         update_rx_interrupt(s);
  387:         break;
  388:     case R_SETUP:
  389:     case R_COUNT0:
  390:     case R_COUNT1:
  391:         s->regs[addr] = value;
  392:         break;
  393: 
  394:     default:
  395:         error_report("milkymist_minimac2: write access to unknown register 0x"
  396:                 TARGET_FMT_plx, addr << 2);
  397:         break;
  398:     }
  399: }
  400: 
  401: static const MemoryRegionOps minimac2_ops = {
  402:     .read = minimac2_read,
  403:     .write = minimac2_write,
  404:     .valid = {
  405:         .min_access_size = 4,
  406:         .max_access_size = 4,
  407:     },
  408:     .endianness = DEVICE_NATIVE_ENDIAN,
  409: };
  410: 
  411: static int minimac2_can_rx(VLANClientState *nc)
  412: {
  413:     MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
  414: 
  415:     if (s->regs[R_STATE0] == STATE_LOADED) {
  416:         return 1;
  417:     }
  418:     if (s->regs[R_STATE1] == STATE_LOADED) {
  419:         return 1;
  420:     }
  421: 
  422:     return 0;
  423: }
  424: 
  425: static void minimac2_cleanup(VLANClientState *nc)
  426: {
  427:     MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
  428: 
  429:     s->nic = NULL;
  430: }
  431: 
  432: static void milkymist_minimac2_reset(DeviceState *d)
  433: {
  434:     MilkymistMinimac2State *s =
  435:             container_of(d, MilkymistMinimac2State, busdev.qdev);
  436:     int i;
  437: 
  438:     for (i = 0; i < R_MAX; i++) {
  439:         s->regs[i] = 0;
  440:     }
  441:     for (i = 0; i < R_PHY_MAX; i++) {
  442:         s->phy_regs[i] = 0;
  443:     }
  444: 
  445:     /* defaults */
  446:     s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
  447:     s->phy_regs[R_PHY_ID2] = 0x161a;
  448: }
  449: 
  450: static NetClientInfo net_milkymist_minimac2_info = {
  451:     .type = NET_CLIENT_TYPE_NIC,
  452:     .size = sizeof(NICState),
  453:     .can_receive = minimac2_can_rx,
  454:     .receive = minimac2_rx,
  455:     .cleanup = minimac2_cleanup,
  456: };
  457: 
  458: static int milkymist_minimac2_init(SysBusDevice *dev)
  459: {
  460:     MilkymistMinimac2State *s = FROM_SYSBUS(typeof(*s), dev);
  461:     size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE);
  462: 
  463:     sysbus_init_irq(dev, &s->rx_irq);
  464:     sysbus_init_irq(dev, &s->tx_irq);
  465: 
  466:     memory_region_init_io(&s->regs_region, &minimac2_ops, s,
  467:                           "milkymist-minimac2", R_MAX * 4);
  468:     sysbus_init_mmio_region(dev, &s->regs_region);
  469: 
  470:     /* register buffers memory */
  471:     memory_region_init_ram(&s->buffers, NULL, "milkymist-minimac2.buffers",
  472:                            buffers_size);
  473:     s->rx0_buf = memory_region_get_ram_ptr(&s->buffers);
  474:     s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
  475:     s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
  476: 
  477:     sysbus_add_memory(dev, s->buffers_base, &s->buffers);
  478: 
  479:     qemu_macaddr_default_if_unset(&s->conf.macaddr);
  480:     s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
  481:                           dev->qdev.info->name, dev->qdev.id, s);
  482:     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
  483: 
  484:     return 0;
  485: }
  486: 
  487: static const VMStateDescription vmstate_milkymist_minimac2_mdio = {
  488:     .name = "milkymist-minimac2-mdio",
  489:     .version_id = 1,
  490:     .minimum_version_id = 1,
  491:     .minimum_version_id_old = 1,
  492:     .fields      = (VMStateField[]) {
  493:         VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState),
  494:         VMSTATE_INT32(count, MilkymistMinimac2MdioState),
  495:         VMSTATE_UINT32(data, MilkymistMinimac2MdioState),
  496:         VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState),
  497:         VMSTATE_INT32(state, MilkymistMinimac2MdioState),
  498:         VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState),
  499:         VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState),
  500:         VMSTATE_END_OF_LIST()
  501:     }
  502: };
  503: 
  504: static const VMStateDescription vmstate_milkymist_minimac2 = {
  505:     .name = "milkymist-minimac2",
  506:     .version_id = 1,
  507:     .minimum_version_id = 1,
  508:     .minimum_version_id_old = 1,
  509:     .fields      = (VMStateField[]) {
  510:         VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX),
  511:         VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX),
  512:         VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0,
  513:                 vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState),
  514:         VMSTATE_END_OF_LIST()
  515:     }
  516: };
  517: 
  518: static SysBusDeviceInfo milkymist_minimac2_info = {
  519:     .init = milkymist_minimac2_init,
  520:     .qdev.name  = "milkymist-minimac2",
  521:     .qdev.size  = sizeof(MilkymistMinimac2State),
  522:     .qdev.vmsd  = &vmstate_milkymist_minimac2,
  523:     .qdev.reset = milkymist_minimac2_reset,
  524:     .qdev.props = (Property[]) {
  525:         DEFINE_PROP_TADDR("buffers_base", MilkymistMinimac2State,
  526:                 buffers_base, 0),
  527:         DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf),
  528:         DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model),
  529:         DEFINE_PROP_END_OF_LIST(),
  530:     }
  531: };
  532: 
  533: static void milkymist_minimac2_register(void)
  534: {
  535:     sysbus_register_withprop(&milkymist_minimac2_info);
  536: }
  537: 
  538: device_init(milkymist_minimac2_register)

unix.superglobalmegacorp.com