| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Luminary Micro Stellaris Ethernet Controller | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2007 CodeSourcery. | 
					
						
							|  |  |  |  * Written by Paul Brook | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-06-26 12:21:35 +10:00
										 |  |  |  * This code is licensed under the GPL. | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | #include "sysbus.h"
 | 
					
						
							| 
									
										
										
										
											2012-10-24 08:43:34 +02:00
										 |  |  | #include "net/net.h"
 | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | #include <zlib.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#define DEBUG_STELLARIS_ENET 1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef DEBUG_STELLARIS_ENET
 | 
					
						
							| 
									
										
										
										
											2009-05-13 17:53:17 +00:00
										 |  |  | #define DPRINTF(fmt, ...) \
 | 
					
						
							|  |  |  | do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0) | 
					
						
							|  |  |  | #define BADF(fmt, ...) \
 | 
					
						
							|  |  |  | do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-05-13 17:53:17 +00:00
										 |  |  | #define DPRINTF(fmt, ...) do {} while(0)
 | 
					
						
							|  |  |  | #define BADF(fmt, ...) \
 | 
					
						
							|  |  |  | do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SE_INT_RX       0x01
 | 
					
						
							|  |  |  | #define SE_INT_TXER     0x02
 | 
					
						
							|  |  |  | #define SE_INT_TXEMP    0x04
 | 
					
						
							|  |  |  | #define SE_INT_FOV      0x08
 | 
					
						
							|  |  |  | #define SE_INT_RXER     0x10
 | 
					
						
							|  |  |  | #define SE_INT_MD       0x20
 | 
					
						
							|  |  |  | #define SE_INT_PHY      0x40
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SE_RCTL_RXEN    0x01
 | 
					
						
							|  |  |  | #define SE_RCTL_AMUL    0x02
 | 
					
						
							|  |  |  | #define SE_RCTL_PRMS    0x04
 | 
					
						
							|  |  |  | #define SE_RCTL_BADCRC  0x08
 | 
					
						
							|  |  |  | #define SE_RCTL_RSTFIFO 0x10
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SE_TCTL_TXEN    0x01
 | 
					
						
							|  |  |  | #define SE_TCTL_PADEN   0x02
 | 
					
						
							|  |  |  | #define SE_TCTL_CRC     0x04
 | 
					
						
							|  |  |  | #define SE_TCTL_DUPLEX  0x08
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  |     SysBusDevice busdev; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     uint32_t ris; | 
					
						
							|  |  |  |     uint32_t im; | 
					
						
							|  |  |  |     uint32_t rctl; | 
					
						
							|  |  |  |     uint32_t tctl; | 
					
						
							|  |  |  |     uint32_t thr; | 
					
						
							|  |  |  |     uint32_t mctl; | 
					
						
							|  |  |  |     uint32_t mdv; | 
					
						
							|  |  |  |     uint32_t mtxd; | 
					
						
							|  |  |  |     uint32_t mrxd; | 
					
						
							|  |  |  |     uint32_t np; | 
					
						
							|  |  |  |     int tx_frame_len; | 
					
						
							|  |  |  |     int tx_fifo_len; | 
					
						
							|  |  |  |     uint8_t tx_fifo[2048]; | 
					
						
							|  |  |  |     /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
 | 
					
						
							|  |  |  |        We implement a full 31 packet fifo.  */ | 
					
						
							|  |  |  |     struct { | 
					
						
							|  |  |  |         uint8_t data[2048]; | 
					
						
							|  |  |  |         int len; | 
					
						
							|  |  |  |     } rx[31]; | 
					
						
							|  |  |  |     uint8_t *rx_fifo; | 
					
						
							|  |  |  |     int rx_fifo_len; | 
					
						
							|  |  |  |     int next_packet; | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:24 +00:00
										 |  |  |     NICState *nic; | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  |     NICConf conf; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     qemu_irq irq; | 
					
						
							| 
									
										
										
										
											2011-07-18 11:58:50 +03:00
										 |  |  |     MemoryRegion mmio; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | } stellaris_enet_state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stellaris_enet_update(stellaris_enet_state *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_set_irq(s->irq, (s->ris & s->im) != 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* TODO: Implement MAC address filtering.  */ | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:23 +08:00
										 |  |  |     stellaris_enet_state *s = qemu_get_nic_opaque(nc); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     int n; | 
					
						
							|  |  |  |     uint8_t *p; | 
					
						
							|  |  |  |     uint32_t crc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((s->rctl & SE_RCTL_RXEN) == 0) | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     if (s->np >= 31) { | 
					
						
							|  |  |  |         DPRINTF("Packet dropped\n"); | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     DPRINTF("Received packet len=%d\n", size); | 
					
						
							|  |  |  |     n = s->next_packet + s->np; | 
					
						
							|  |  |  |     if (n >= 31) | 
					
						
							|  |  |  |         n -= 31; | 
					
						
							|  |  |  |     s->np++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->rx[n].len = size + 6; | 
					
						
							|  |  |  |     p = s->rx[n].data; | 
					
						
							|  |  |  |     *(p++) = (size + 6); | 
					
						
							|  |  |  |     *(p++) = (size + 6) >> 8; | 
					
						
							|  |  |  |     memcpy (p, buf, size); | 
					
						
							|  |  |  |     p += size; | 
					
						
							|  |  |  |     crc = crc32(~0, buf, size); | 
					
						
							|  |  |  |     *(p++) = crc; | 
					
						
							|  |  |  |     *(p++) = crc >> 8; | 
					
						
							|  |  |  |     *(p++) = crc >> 16; | 
					
						
							|  |  |  |     *(p++) = crc >> 24; | 
					
						
							|  |  |  |     /* Clear the remaining bytes in the last word.  */ | 
					
						
							|  |  |  |     if ((size & 3) != 2) { | 
					
						
							|  |  |  |         memset(p, 0, (6 - size) & 3); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->ris |= SE_INT_RX; | 
					
						
							|  |  |  |     stellaris_enet_update(s); | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return size; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static int stellaris_enet_can_receive(NetClientState *nc) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:23 +08:00
										 |  |  |     stellaris_enet_state *s = qemu_get_nic_opaque(nc); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((s->rctl & SE_RCTL_RXEN) == 0) | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return (s->np < 31); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, | 
					
						
							| 
									
										
										
										
											2011-07-18 11:58:50 +03:00
										 |  |  |                                     unsigned size) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-06-11 13:22:27 +01:00
										 |  |  |     stellaris_enet_state *s = (stellaris_enet_state *)opaque; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     uint32_t val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (offset) { | 
					
						
							|  |  |  |     case 0x00: /* RIS */ | 
					
						
							|  |  |  |         DPRINTF("IRQ status %02x\n", s->ris); | 
					
						
							|  |  |  |         return s->ris; | 
					
						
							|  |  |  |     case 0x04: /* IM */ | 
					
						
							|  |  |  |         return s->im; | 
					
						
							|  |  |  |     case 0x08: /* RCTL */ | 
					
						
							|  |  |  |         return s->rctl; | 
					
						
							|  |  |  |     case 0x0c: /* TCTL */ | 
					
						
							|  |  |  |         return s->tctl; | 
					
						
							|  |  |  |     case 0x10: /* DATA */ | 
					
						
							|  |  |  |         if (s->rx_fifo_len == 0) { | 
					
						
							|  |  |  |             if (s->np == 0) { | 
					
						
							|  |  |  |                 BADF("RX underflow\n"); | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             s->rx_fifo_len = s->rx[s->next_packet].len; | 
					
						
							|  |  |  |             s->rx_fifo = s->rx[s->next_packet].data; | 
					
						
							|  |  |  |             DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16) | 
					
						
							|  |  |  |               | (s->rx_fifo[3] << 24); | 
					
						
							|  |  |  |         s->rx_fifo += 4; | 
					
						
							|  |  |  |         s->rx_fifo_len -= 4; | 
					
						
							|  |  |  |         if (s->rx_fifo_len <= 0) { | 
					
						
							|  |  |  |             s->rx_fifo_len = 0; | 
					
						
							|  |  |  |             s->next_packet++; | 
					
						
							|  |  |  |             if (s->next_packet >= 31) | 
					
						
							|  |  |  |                 s->next_packet = 0; | 
					
						
							|  |  |  |             s->np--; | 
					
						
							|  |  |  |             DPRINTF("RX done np=%d\n", s->np); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return val; | 
					
						
							|  |  |  |     case 0x14: /* IA0 */ | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  |         return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8) | 
					
						
							|  |  |  |                | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     case 0x18: /* IA1 */ | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  |         return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     case 0x1c: /* THR */ | 
					
						
							|  |  |  |         return s->thr; | 
					
						
							|  |  |  |     case 0x20: /* MCTL */ | 
					
						
							|  |  |  |         return s->mctl; | 
					
						
							|  |  |  |     case 0x24: /* MDV */ | 
					
						
							|  |  |  |         return s->mdv; | 
					
						
							|  |  |  |     case 0x28: /* MADD */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     case 0x2c: /* MTXD */ | 
					
						
							|  |  |  |         return s->mtxd; | 
					
						
							|  |  |  |     case 0x30: /* MRXD */ | 
					
						
							|  |  |  |         return s->mrxd; | 
					
						
							|  |  |  |     case 0x34: /* NP */ | 
					
						
							|  |  |  |         return s->np; | 
					
						
							|  |  |  |     case 0x38: /* TR */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     case 0x3c: /* Undocuented: Timestamp? */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static void stellaris_enet_write(void *opaque, hwaddr offset, | 
					
						
							| 
									
										
										
										
											2011-07-18 11:58:50 +03:00
										 |  |  |                                  uint64_t value, unsigned size) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     stellaris_enet_state *s = (stellaris_enet_state *)opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (offset) { | 
					
						
							|  |  |  |     case 0x00: /* IACK */ | 
					
						
							|  |  |  |         s->ris &= ~value; | 
					
						
							|  |  |  |         DPRINTF("IRQ ack %02x/%02x\n", value, s->ris); | 
					
						
							|  |  |  |         stellaris_enet_update(s); | 
					
						
							|  |  |  |         /* Clearing TXER also resets the TX fifo.  */ | 
					
						
							|  |  |  |         if (value & SE_INT_TXER) | 
					
						
							|  |  |  |             s->tx_frame_len = -1; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x04: /* IM */ | 
					
						
							|  |  |  |         DPRINTF("IRQ mask %02x/%02x\n", value, s->ris); | 
					
						
							|  |  |  |         s->im = value; | 
					
						
							|  |  |  |         stellaris_enet_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x08: /* RCTL */ | 
					
						
							|  |  |  |         s->rctl = value; | 
					
						
							|  |  |  |         if (value & SE_RCTL_RSTFIFO) { | 
					
						
							|  |  |  |             s->rx_fifo_len = 0; | 
					
						
							|  |  |  |             s->np = 0; | 
					
						
							|  |  |  |             stellaris_enet_update(s); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x0c: /* TCTL */ | 
					
						
							|  |  |  |         s->tctl = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x10: /* DATA */ | 
					
						
							|  |  |  |         if (s->tx_frame_len == -1) { | 
					
						
							|  |  |  |             s->tx_frame_len = value & 0xffff; | 
					
						
							|  |  |  |             if (s->tx_frame_len > 2032) { | 
					
						
							|  |  |  |                 DPRINTF("TX frame too long (%d)\n", s->tx_frame_len); | 
					
						
							|  |  |  |                 s->tx_frame_len = 0; | 
					
						
							|  |  |  |                 s->ris |= SE_INT_TXER; | 
					
						
							|  |  |  |                 stellaris_enet_update(s); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 DPRINTF("Start TX frame len=%d\n", s->tx_frame_len); | 
					
						
							|  |  |  |                 /* The value written does not include the ethernet header.  */ | 
					
						
							|  |  |  |                 s->tx_frame_len += 14; | 
					
						
							|  |  |  |                 if ((s->tctl & SE_TCTL_CRC) == 0) | 
					
						
							|  |  |  |                     s->tx_frame_len += 4; | 
					
						
							|  |  |  |                 s->tx_fifo_len = 0; | 
					
						
							|  |  |  |                 s->tx_fifo[s->tx_fifo_len++] = value >> 16; | 
					
						
							|  |  |  |                 s->tx_fifo[s->tx_fifo_len++] = value >> 24; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             s->tx_fifo[s->tx_fifo_len++] = value; | 
					
						
							|  |  |  |             s->tx_fifo[s->tx_fifo_len++] = value >> 8; | 
					
						
							|  |  |  |             s->tx_fifo[s->tx_fifo_len++] = value >> 16; | 
					
						
							|  |  |  |             s->tx_fifo[s->tx_fifo_len++] = value >> 24; | 
					
						
							|  |  |  |             if (s->tx_fifo_len >= s->tx_frame_len) { | 
					
						
							|  |  |  |                 /* We don't implement explicit CRC, so just chop it off.  */ | 
					
						
							|  |  |  |                 if ((s->tctl & SE_TCTL_CRC) == 0) | 
					
						
							|  |  |  |                     s->tx_frame_len -= 4; | 
					
						
							|  |  |  |                 if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) { | 
					
						
							|  |  |  |                     memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); | 
					
						
							|  |  |  |                     s->tx_fifo_len = 60; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:22 +08:00
										 |  |  |                 qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo, | 
					
						
							|  |  |  |                                  s->tx_frame_len); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |                 s->tx_frame_len = -1; | 
					
						
							|  |  |  |                 s->ris |= SE_INT_TXEMP; | 
					
						
							|  |  |  |                 stellaris_enet_update(s); | 
					
						
							|  |  |  |                 DPRINTF("Done TX\n"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x14: /* IA0 */ | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  |         s->conf.macaddr.a[0] = value; | 
					
						
							|  |  |  |         s->conf.macaddr.a[1] = value >> 8; | 
					
						
							|  |  |  |         s->conf.macaddr.a[2] = value >> 16; | 
					
						
							|  |  |  |         s->conf.macaddr.a[3] = value >> 24; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 0x18: /* IA1 */ | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  |         s->conf.macaddr.a[4] = value; | 
					
						
							|  |  |  |         s->conf.macaddr.a[5] = value >> 8; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 0x1c: /* THR */ | 
					
						
							|  |  |  |         s->thr = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x20: /* MCTL */ | 
					
						
							|  |  |  |         s->mctl = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x24: /* MDV */ | 
					
						
							|  |  |  |         s->mdv = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x28: /* MADD */ | 
					
						
							|  |  |  |         /* ignored.  */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x2c: /* MTXD */ | 
					
						
							|  |  |  |         s->mtxd = value & 0xff; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x30: /* MRXD */ | 
					
						
							|  |  |  |     case 0x34: /* NP */ | 
					
						
							|  |  |  |     case 0x38: /* TR */ | 
					
						
							|  |  |  |         /* Ignored.  */ | 
					
						
							|  |  |  |     case 0x3c: /* Undocuented: Timestamp? */ | 
					
						
							|  |  |  |         /* Ignored.  */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-18 11:58:50 +03:00
										 |  |  | static const MemoryRegionOps stellaris_enet_ops = { | 
					
						
							|  |  |  |     .read = stellaris_enet_read, | 
					
						
							|  |  |  |     .write = stellaris_enet_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stellaris_enet_reset(stellaris_enet_state *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     s->mdv = 0x80; | 
					
						
							|  |  |  |     s->rctl = SE_RCTL_BADCRC; | 
					
						
							|  |  |  |     s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP | 
					
						
							|  |  |  |             | SE_INT_TXER | SE_INT_RX; | 
					
						
							|  |  |  |     s->thr = 0x3f; | 
					
						
							|  |  |  |     s->tx_frame_len = -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-02 16:48:32 +00:00
										 |  |  | static void stellaris_enet_save(QEMUFile *f, void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     stellaris_enet_state *s = (stellaris_enet_state *)opaque; | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_put_be32(f, s->ris); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->im); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->rctl); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->tctl); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->thr); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->mctl); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->mdv); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->mtxd); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->mrxd); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->np); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->tx_frame_len); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->tx_fifo_len); | 
					
						
							|  |  |  |     qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); | 
					
						
							|  |  |  |     for (i = 0; i < 31; i++) { | 
					
						
							|  |  |  |         qemu_put_be32(f, s->rx[i].len); | 
					
						
							|  |  |  |         qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qemu_put_be32(f, s->next_packet); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data); | 
					
						
							|  |  |  |     qemu_put_be32(f, s->rx_fifo_len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     stellaris_enet_state *s = (stellaris_enet_state *)opaque; | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (version_id != 1) | 
					
						
							|  |  |  |         return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->ris = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->im = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->rctl = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->tctl = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->thr = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->mctl = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->mdv = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->mtxd = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->mrxd = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->np = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->tx_frame_len = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->tx_fifo_len = qemu_get_be32(f); | 
					
						
							|  |  |  |     qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo)); | 
					
						
							|  |  |  |     for (i = 0; i < 31; i++) { | 
					
						
							|  |  |  |         s->rx[i].len = qemu_get_be32(f); | 
					
						
							|  |  |  |         qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     s->next_packet = qemu_get_be32(f); | 
					
						
							|  |  |  |     s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f); | 
					
						
							|  |  |  |     s->rx_fifo_len = qemu_get_be32(f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static void stellaris_enet_cleanup(NetClientState *nc) | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:23 +08:00
										 |  |  |     stellaris_enet_state *s = qemu_get_nic_opaque(nc); | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 11:09:07 -06:00
										 |  |  |     unregister_savevm(&s->busdev.qdev, "stellaris_enet", s); | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-18 11:58:50 +03:00
										 |  |  |     memory_region_destroy(&s->mmio); | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     g_free(s); | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:24 +00:00
										 |  |  | static NetClientInfo net_stellaris_enet_info = { | 
					
						
							| 
									
										
										
										
											2012-07-17 16:17:12 +02:00
										 |  |  |     .type = NET_CLIENT_OPTIONS_KIND_NIC, | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:24 +00:00
										 |  |  |     .size = sizeof(NICState), | 
					
						
							|  |  |  |     .can_receive = stellaris_enet_can_receive, | 
					
						
							|  |  |  |     .receive = stellaris_enet_receive, | 
					
						
							|  |  |  |     .cleanup = stellaris_enet_cleanup, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-14 10:36:05 +02:00
										 |  |  | static int stellaris_enet_init(SysBusDevice *dev) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  |     stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-18 11:58:50 +03:00
										 |  |  |     memory_region_init_io(&s->mmio, &stellaris_enet_ops, s, "stellaris_enet", | 
					
						
							|  |  |  |                           0x1000); | 
					
						
							| 
									
										
										
										
											2011-11-27 11:38:10 +02:00
										 |  |  |     sysbus_init_mmio(dev, &s->mmio); | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  |     sysbus_init_irq(dev, &s->irq); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  |     qemu_macaddr_default_if_unset(&s->conf.macaddr); | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:24 +00:00
										 |  |  |     s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, | 
					
						
							| 
									
										
										
										
											2011-12-04 11:17:51 -06:00
										 |  |  |                           object_get_typename(OBJECT(dev)), dev->qdev.id, s); | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:22 +08:00
										 |  |  |     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     stellaris_enet_reset(s); | 
					
						
							| 
									
										
										
										
											2010-06-25 11:09:07 -06:00
										 |  |  |     register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1, | 
					
						
							| 
									
										
										
										
											2008-07-02 16:48:32 +00:00
										 |  |  |                     stellaris_enet_save, stellaris_enet_load, s); | 
					
						
							| 
									
										
										
										
											2009-08-14 10:36:05 +02:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2007-11-24 03:13:04 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | static Property stellaris_enet_properties[] = { | 
					
						
							|  |  |  |     DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stellaris_enet_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  |     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     k->init = stellaris_enet_init; | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     dc->props = stellaris_enet_properties; | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-10 16:19:07 +01:00
										 |  |  | static const TypeInfo stellaris_enet_info = { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     .name          = "stellaris_enet", | 
					
						
							|  |  |  |     .parent        = TYPE_SYS_BUS_DEVICE, | 
					
						
							|  |  |  |     .instance_size = sizeof(stellaris_enet_state), | 
					
						
							|  |  |  |     .class_init    = stellaris_enet_class_init, | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:39 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | static void stellaris_enet_register_types(void) | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     type_register_static(&stellaris_enet_info); | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | type_init(stellaris_enet_register_types) |