|
|
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;
1.1.1.2 ! root 100: MemoryRegion buffers;
! 101: MemoryRegion regs_region;
1.1 root 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:
1.1.1.2 ! root 325: static uint64_t
! 326: minimac2_read(void *opaque, target_phys_addr_t addr, unsigned size)
1.1 root 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
1.1.1.2 ! root 355: minimac2_write(void *opaque, target_phys_addr_t addr, uint64_t value,
! 356: unsigned size)
1.1 root 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:
1.1.1.2 ! root 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,
1.1 root 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:
1.1.1.2 ! root 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);
1.1 root 469:
470: /* register buffers memory */
1.1.1.2 ! root 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);
1.1 root 474: s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
475: s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
476:
1.1.1.2 ! root 477: sysbus_add_memory(dev, s->buffers_base, &s->buffers);
1.1 root 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)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.