|
|
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.