|
|
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)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.