| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU ETRAX Ethernet Controller. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2013-02-04 15:40:22 +01:00
										 |  |  | #include "hw/sysbus.h"
 | 
					
						
							| 
									
										
										
										
											2012-10-24 08:43:34 +02:00
										 |  |  | #include "net/net.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-05 17:06:20 +01:00
										 |  |  | #include "hw/cris/etraxfs.h"
 | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define D(x)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-22 20:34:18 +00:00
										 |  |  | /* Advertisement control register. */ | 
					
						
							|  |  |  | #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
 | 
					
						
							|  |  |  | #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
 | 
					
						
							|  |  |  | #define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
 | 
					
						
							|  |  |  | #define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * The MDIO extensions in the TDK PHY model were reversed engineered from the | 
					
						
							| 
									
										
										
										
											2008-05-13 23:51:49 +00:00
										 |  |  |  * linux driver (PHYID and Diagnostics reg). | 
					
						
							|  |  |  |  * TODO: Add friendly names for the register nums. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | struct qemu_phy | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     uint32_t regs[32]; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     int link; | 
					
						
							| 
									
										
										
										
											2009-01-09 00:04:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     unsigned int (*read)(struct qemu_phy *phy, unsigned int req); | 
					
						
							|  |  |  |     void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     int regnum; | 
					
						
							|  |  |  |     unsigned r = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     regnum = req & 0x1f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (regnum) { | 
					
						
							|  |  |  |     case 1: | 
					
						
							|  |  |  |         if (!phy->link) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* MR1.     */ | 
					
						
							|  |  |  |         /* Speeds and modes.  */ | 
					
						
							|  |  |  |         r |= (1 << 13) | (1 << 14); | 
					
						
							|  |  |  |         r |= (1 << 11) | (1 << 12); | 
					
						
							|  |  |  |         r |= (1 << 5); /* Autoneg complete.  */ | 
					
						
							|  |  |  |         r |= (1 << 3); /* Autoneg able.     */ | 
					
						
							|  |  |  |         r |= (1 << 2); /* link.     */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 5: | 
					
						
							|  |  |  |         /* Link partner ability.
 | 
					
						
							|  |  |  |            We are kind; always agree with whatever best mode | 
					
						
							|  |  |  |            the guest advertises.  */ | 
					
						
							|  |  |  |         r = 1 << 14; /* Success.  */ | 
					
						
							|  |  |  |         /* Copy advertised modes.  */ | 
					
						
							|  |  |  |         r |= phy->regs[4] & (15 << 5); | 
					
						
							|  |  |  |         /* Autoneg support.  */ | 
					
						
							|  |  |  |         r |= 1; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 18: | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         /* Diagnostics reg.  */ | 
					
						
							|  |  |  |         int duplex = 0; | 
					
						
							|  |  |  |         int speed_100 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!phy->link) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Are we advertising 100 half or 100 duplex ? */ | 
					
						
							|  |  |  |         speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); | 
					
						
							|  |  |  |         speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Are we advertising 10 duplex or 100 duplex ? */ | 
					
						
							|  |  |  |         duplex = !!(phy->regs[4] & ADVERTISE_100FULL); | 
					
						
							|  |  |  |         duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); | 
					
						
							|  |  |  |         r = (speed_100 << 10) | (duplex << 11); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         r = phy->regs[regnum]; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); | 
					
						
							|  |  |  |     return r; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     int regnum; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     regnum = req & 0x1f; | 
					
						
							|  |  |  |     D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); | 
					
						
							|  |  |  |     switch (regnum) { | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         phy->regs[regnum] = data; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | tdk_init(struct qemu_phy *phy) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     phy->regs[0] = 0x3100; | 
					
						
							|  |  |  |     /* PHY Id.  */ | 
					
						
							|  |  |  |     phy->regs[2] = 0x0300; | 
					
						
							|  |  |  |     phy->regs[3] = 0xe400; | 
					
						
							|  |  |  |     /* Autonegotiation advertisement reg.  */ | 
					
						
							|  |  |  |     phy->regs[4] = 0x01E1; | 
					
						
							|  |  |  |     phy->link = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     phy->read = tdk_read; | 
					
						
							|  |  |  |     phy->write = tdk_write; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct qemu_mdio | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     /* bus.     */ | 
					
						
							|  |  |  |     int mdc; | 
					
						
							|  |  |  |     int mdio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* decoder.  */ | 
					
						
							|  |  |  |     enum { | 
					
						
							|  |  |  |         PREAMBLE, | 
					
						
							|  |  |  |         SOF, | 
					
						
							|  |  |  |         OPC, | 
					
						
							|  |  |  |         ADDR, | 
					
						
							|  |  |  |         REQ, | 
					
						
							|  |  |  |         TURNAROUND, | 
					
						
							|  |  |  |         DATA | 
					
						
							|  |  |  |     } state; | 
					
						
							|  |  |  |     unsigned int drive; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     unsigned int cnt; | 
					
						
							|  |  |  |     unsigned int addr; | 
					
						
							|  |  |  |     unsigned int opc; | 
					
						
							|  |  |  |     unsigned int req; | 
					
						
							|  |  |  |     unsigned int data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     struct qemu_phy *devs[32]; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     bus->devs[addr & 0x1f] = phy; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-30 08:59:49 +00:00
										 |  |  | #ifdef USE_THIS_DEAD_CODE
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     bus->devs[addr & 0x1f] = NULL; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-06-30 08:59:49 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void mdio_read_req(struct qemu_mdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     struct qemu_phy *phy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     phy = bus->devs[bus->addr]; | 
					
						
							|  |  |  |     if (phy && phy->read) { | 
					
						
							|  |  |  |         bus->data = phy->read(phy, bus->req); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         bus->data = 0xffff; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void mdio_write_req(struct qemu_mdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     struct qemu_phy *phy; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     phy = bus->devs[bus->addr]; | 
					
						
							|  |  |  |     if (phy && phy->write) { | 
					
						
							|  |  |  |         phy->write(phy, bus->req, bus->data); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void mdio_cycle(struct qemu_mdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     bus->cnt++; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", | 
					
						
							|  |  |  |         bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | #if 0
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     if (bus->mdc) { | 
					
						
							|  |  |  |         printf("%d", bus->mdio); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     switch (bus->state) { | 
					
						
							|  |  |  |     case PREAMBLE: | 
					
						
							|  |  |  |         if (bus->mdc) { | 
					
						
							|  |  |  |             if (bus->cnt >= (32 * 2) && !bus->mdio) { | 
					
						
							|  |  |  |                 bus->cnt = 0; | 
					
						
							|  |  |  |                 bus->state = SOF; | 
					
						
							|  |  |  |                 bus->data = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case SOF: | 
					
						
							|  |  |  |         if (bus->mdc) { | 
					
						
							|  |  |  |             if (bus->mdio != 1) { | 
					
						
							|  |  |  |                 printf("WARNING: no SOF\n"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (bus->cnt == 1*2) { | 
					
						
							|  |  |  |                 bus->cnt = 0; | 
					
						
							|  |  |  |                 bus->opc = 0; | 
					
						
							|  |  |  |                 bus->state = OPC; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case OPC: | 
					
						
							|  |  |  |         if (bus->mdc) { | 
					
						
							|  |  |  |             bus->opc <<= 1; | 
					
						
							|  |  |  |             bus->opc |= bus->mdio & 1; | 
					
						
							|  |  |  |             if (bus->cnt == 2*2) { | 
					
						
							|  |  |  |                 bus->cnt = 0; | 
					
						
							|  |  |  |                 bus->addr = 0; | 
					
						
							|  |  |  |                 bus->state = ADDR; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case ADDR: | 
					
						
							|  |  |  |         if (bus->mdc) { | 
					
						
							|  |  |  |             bus->addr <<= 1; | 
					
						
							|  |  |  |             bus->addr |= bus->mdio & 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (bus->cnt == 5*2) { | 
					
						
							|  |  |  |                 bus->cnt = 0; | 
					
						
							|  |  |  |                 bus->req = 0; | 
					
						
							|  |  |  |                 bus->state = REQ; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case REQ: | 
					
						
							|  |  |  |         if (bus->mdc) { | 
					
						
							|  |  |  |             bus->req <<= 1; | 
					
						
							|  |  |  |             bus->req |= bus->mdio & 1; | 
					
						
							|  |  |  |             if (bus->cnt == 5*2) { | 
					
						
							|  |  |  |                 bus->cnt = 0; | 
					
						
							|  |  |  |                 bus->state = TURNAROUND; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case TURNAROUND: | 
					
						
							|  |  |  |         if (bus->mdc && bus->cnt == 2*2) { | 
					
						
							|  |  |  |             bus->mdio = 0; | 
					
						
							|  |  |  |             bus->cnt = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (bus->opc == 2) { | 
					
						
							|  |  |  |                 bus->drive = 1; | 
					
						
							|  |  |  |                 mdio_read_req(bus); | 
					
						
							|  |  |  |                 bus->mdio = bus->data & 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             bus->state = DATA; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case DATA: | 
					
						
							|  |  |  |         if (!bus->mdc) { | 
					
						
							|  |  |  |             if (bus->drive) { | 
					
						
							|  |  |  |                 bus->mdio = !!(bus->data & (1 << 15)); | 
					
						
							|  |  |  |                 bus->data <<= 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             if (!bus->drive) { | 
					
						
							|  |  |  |                 bus->data <<= 1; | 
					
						
							|  |  |  |                 bus->data |= bus->mdio; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (bus->cnt == 16 * 2) { | 
					
						
							|  |  |  |                 bus->cnt = 0; | 
					
						
							|  |  |  |                 bus->state = PREAMBLE; | 
					
						
							|  |  |  |                 if (!bus->drive) { | 
					
						
							|  |  |  |                     mdio_write_req(bus); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 bus->drive = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 23:51:49 +00:00
										 |  |  | /* ETRAX-FS Ethernet MAC block starts here.  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  | #define RW_MA0_LO      0x00
 | 
					
						
							|  |  |  | #define RW_MA0_HI      0x01
 | 
					
						
							|  |  |  | #define RW_MA1_LO      0x02
 | 
					
						
							|  |  |  | #define RW_MA1_HI      0x03
 | 
					
						
							|  |  |  | #define RW_GA_LO      0x04
 | 
					
						
							|  |  |  | #define RW_GA_HI      0x05
 | 
					
						
							|  |  |  | #define RW_GEN_CTRL      0x06
 | 
					
						
							|  |  |  | #define RW_REC_CTRL      0x07
 | 
					
						
							|  |  |  | #define RW_TR_CTRL      0x08
 | 
					
						
							|  |  |  | #define RW_CLR_ERR      0x09
 | 
					
						
							|  |  |  | #define RW_MGM_CTRL      0x0a
 | 
					
						
							|  |  |  | #define R_STAT          0x0b
 | 
					
						
							|  |  |  | #define FS_ETH_MAX_REGS      0x17
 | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  | #define TYPE_ETRAX_FS_ETH "etraxfs-eth"
 | 
					
						
							|  |  |  | #define ETRAX_FS_ETH(obj) \
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     OBJECT_CHECK(ETRAXFSEthState, (obj), TYPE_ETRAX_FS_ETH) | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  | typedef struct ETRAXFSEthState | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  |     SysBusDevice parent_obj; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     MemoryRegion mmio; | 
					
						
							|  |  |  |     NICState *nic; | 
					
						
							|  |  |  |     NICConf conf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Two addrs in the filter.  */ | 
					
						
							|  |  |  |     uint8_t macaddr[2][6]; | 
					
						
							|  |  |  |     uint32_t regs[FS_ETH_MAX_REGS]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     union { | 
					
						
							|  |  |  |         void *vdma_out; | 
					
						
							|  |  |  |         struct etraxfs_dma_client *dma_out; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     union { | 
					
						
							|  |  |  |         void *vdma_in; | 
					
						
							|  |  |  |         struct etraxfs_dma_client *dma_in; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* MDIO bus.  */ | 
					
						
							|  |  |  |     struct qemu_mdio mdio_bus; | 
					
						
							|  |  |  |     unsigned int phyaddr; | 
					
						
							|  |  |  |     int duplex_mismatch; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* PHY.     */ | 
					
						
							|  |  |  |     struct qemu_phy phy; | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  | } ETRAXFSEthState; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  | static void eth_validate_duplex(ETRAXFSEthState *eth) | 
					
						
							| 
									
										
										
										
											2008-09-22 20:34:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     struct qemu_phy *phy; | 
					
						
							|  |  |  |     unsigned int phy_duplex; | 
					
						
							|  |  |  |     unsigned int mac_duplex; | 
					
						
							|  |  |  |     int new_mm = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     phy = eth->mdio_bus.devs[eth->phyaddr]; | 
					
						
							|  |  |  |     phy_duplex = !!(phy->read(phy, 18) & (1 << 11)); | 
					
						
							|  |  |  |     mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mac_duplex != phy_duplex) { | 
					
						
							|  |  |  |         new_mm = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (eth->regs[RW_GEN_CTRL] & 1) { | 
					
						
							|  |  |  |         if (new_mm != eth->duplex_mismatch) { | 
					
						
							|  |  |  |             if (new_mm) { | 
					
						
							|  |  |  |                 printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n", | 
					
						
							|  |  |  |                        mac_duplex, phy_duplex); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 printf("HW: ETH duplex ok.\n"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         eth->duplex_mismatch = new_mm; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-09-22 20:34:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-11 13:47:48 +02:00
										 |  |  | static uint64_t | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | eth_read(void *opaque, hwaddr addr, unsigned int size) | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *eth = opaque; | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     uint32_t r = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addr >>= 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case R_STAT: | 
					
						
							|  |  |  |         r = eth->mdio_bus.mdio & 1; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         r = eth->regs[addr]; | 
					
						
							|  |  |  |         D(printf("%s %x\n", __func__, addr * 4)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return r; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  | static void eth_update_ma(ETRAXFSEthState *eth, int ma) | 
					
						
							| 
									
										
										
										
											2008-05-18 08:50:32 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     int reg; | 
					
						
							|  |  |  |     int i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ma &= 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     reg = RW_MA0_LO; | 
					
						
							|  |  |  |     if (ma) { | 
					
						
							|  |  |  |         reg = RW_MA1_LO; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     eth->macaddr[ma][i++] = eth->regs[reg]; | 
					
						
							|  |  |  |     eth->macaddr[ma][i++] = eth->regs[reg] >> 8; | 
					
						
							|  |  |  |     eth->macaddr[ma][i++] = eth->regs[reg] >> 16; | 
					
						
							|  |  |  |     eth->macaddr[ma][i++] = eth->regs[reg] >> 24; | 
					
						
							|  |  |  |     eth->macaddr[ma][i++] = eth->regs[reg + 1]; | 
					
						
							|  |  |  |     eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, | 
					
						
							|  |  |  |              eth->macaddr[ma][0], eth->macaddr[ma][1], | 
					
						
							|  |  |  |              eth->macaddr[ma][2], eth->macaddr[ma][3], | 
					
						
							|  |  |  |              eth->macaddr[ma][4], eth->macaddr[ma][5])); | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | eth_write(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-11 13:47:48 +02:00
										 |  |  |           uint64_t val64, unsigned int size) | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *eth = opaque; | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     uint32_t value = val64; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     addr >>= 2; | 
					
						
							|  |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case RW_MA0_LO: | 
					
						
							|  |  |  |     case RW_MA0_HI: | 
					
						
							|  |  |  |         eth->regs[addr] = value; | 
					
						
							|  |  |  |         eth_update_ma(eth, 0); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case RW_MA1_LO: | 
					
						
							|  |  |  |     case RW_MA1_HI: | 
					
						
							|  |  |  |         eth->regs[addr] = value; | 
					
						
							|  |  |  |         eth_update_ma(eth, 1); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RW_MGM_CTRL: | 
					
						
							|  |  |  |         /* Attach an MDIO/PHY abstraction.  */ | 
					
						
							|  |  |  |         if (value & 2) { | 
					
						
							|  |  |  |             eth->mdio_bus.mdio = value & 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (eth->mdio_bus.mdc != (value & 4)) { | 
					
						
							|  |  |  |             mdio_cycle(ð->mdio_bus); | 
					
						
							|  |  |  |             eth_validate_duplex(eth); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         eth->mdio_bus.mdc = !!(value & 4); | 
					
						
							|  |  |  |         eth->regs[addr] = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RW_REC_CTRL: | 
					
						
							|  |  |  |         eth->regs[addr] = value; | 
					
						
							|  |  |  |         eth_validate_duplex(eth); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         eth->regs[addr] = value; | 
					
						
							|  |  |  |         D(printf("%s %x %x\n", __func__, addr, value)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-18 08:50:32 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |    filter dropping group addresses we have not joined.    The filter has 64 | 
					
						
							|  |  |  |    bits (m). The has function is a simple nible xor of the group addr.    */ | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  | static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa) | 
					
						
							| 
									
										
										
										
											2008-05-18 08:50:32 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     unsigned int hsh; | 
					
						
							|  |  |  |     int m_individual = eth->regs[RW_REC_CTRL] & 4; | 
					
						
							|  |  |  |     int match; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* First bit on the wire of a MAC address signals multicast or
 | 
					
						
							|  |  |  |        physical address.  */ | 
					
						
							|  |  |  |     if (!m_individual && !(sa[0] & 1)) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Calculate the hash index for the GA registers. */ | 
					
						
							|  |  |  |     hsh = 0; | 
					
						
							|  |  |  |     hsh ^= (*sa) & 0x3f; | 
					
						
							|  |  |  |     hsh ^= ((*sa) >> 6) & 0x03; | 
					
						
							|  |  |  |     ++sa; | 
					
						
							|  |  |  |     hsh ^= ((*sa) << 2) & 0x03c; | 
					
						
							|  |  |  |     hsh ^= ((*sa) >> 4) & 0xf; | 
					
						
							|  |  |  |     ++sa; | 
					
						
							|  |  |  |     hsh ^= ((*sa) << 4) & 0x30; | 
					
						
							|  |  |  |     hsh ^= ((*sa) >> 2) & 0x3f; | 
					
						
							|  |  |  |     ++sa; | 
					
						
							|  |  |  |     hsh ^= (*sa) & 0x3f; | 
					
						
							|  |  |  |     hsh ^= ((*sa) >> 6) & 0x03; | 
					
						
							|  |  |  |     ++sa; | 
					
						
							|  |  |  |     hsh ^= ((*sa) << 2) & 0x03c; | 
					
						
							|  |  |  |     hsh ^= ((*sa) >> 4) & 0xf; | 
					
						
							|  |  |  |     ++sa; | 
					
						
							|  |  |  |     hsh ^= ((*sa) << 4) & 0x30; | 
					
						
							|  |  |  |     hsh ^= ((*sa) >> 2) & 0x3f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     hsh &= 63; | 
					
						
							|  |  |  |     if (hsh > 31) { | 
					
						
							|  |  |  |         match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         match = eth->regs[RW_GA_LO] & (1 << hsh); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, | 
					
						
							|  |  |  |              eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); | 
					
						
							|  |  |  |     return match; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static int eth_can_receive(NetClientState *nc) | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     return 1; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     int use_ma0 = eth->regs[RW_REC_CTRL] & 1; | 
					
						
							|  |  |  |     int use_ma1 = eth->regs[RW_REC_CTRL] & 2; | 
					
						
							|  |  |  |     int r_bcast = eth->regs[RW_REC_CTRL] & 8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (size < 12) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", | 
					
						
							|  |  |  |          buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], | 
					
						
							|  |  |  |          use_ma0, use_ma1, r_bcast)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Does the frame get through the address filters?  */ | 
					
						
							|  |  |  |     if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) | 
					
						
							|  |  |  |         && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) | 
					
						
							|  |  |  |         && (!r_bcast || memcmp(buf, sa_bcast, 6)) | 
					
						
							|  |  |  |         && !eth_match_groupaddr(eth, buf)) { | 
					
						
							|  |  |  |         return size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* FIXME: Find another way to pass on the fake csum.  */ | 
					
						
							|  |  |  |     etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1); | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     return size; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-21 15:11:35 +01:00
										 |  |  | static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop) | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *eth = opaque; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     D(printf("%s buf=%p len=%d\n", __func__, buf, len)); | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:22 +08:00
										 |  |  |     qemu_send_packet(qemu_get_queue(eth->nic), buf, len); | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     return len; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static void eth_set_link(NetClientState *nc) | 
					
						
							| 
									
										
										
										
											2009-01-09 00:04:35 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     D(printf("%s %d\n", __func__, nc->link_down)); | 
					
						
							|  |  |  |     eth->phy.link = !nc->link_down; | 
					
						
							| 
									
										
										
										
											2009-01-09 00:04:35 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-11 13:47:48 +02:00
										 |  |  | static const MemoryRegionOps eth_ops = { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     .read = eth_read, | 
					
						
							|  |  |  |     .write = eth_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_LITTLE_ENDIAN, | 
					
						
							|  |  |  |     .valid = { | 
					
						
							|  |  |  |         .min_access_size = 4, | 
					
						
							|  |  |  |         .max_access_size = 4 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-24 16:35:13 +01:00
										 |  |  | static void eth_cleanup(NetClientState *nc) | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     /* Disconnect the client.  */ | 
					
						
							|  |  |  |     eth->dma_out->client.push = NULL; | 
					
						
							|  |  |  |     eth->dma_out->client.opaque = NULL; | 
					
						
							|  |  |  |     eth->dma_in->client.opaque = NULL; | 
					
						
							|  |  |  |     eth->dma_in->client.pull = NULL; | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |         g_free(eth); | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:18 +00:00
										 |  |  | static NetClientInfo net_etraxfs_info = { | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     .type = NET_CLIENT_OPTIONS_KIND_NIC, | 
					
						
							|  |  |  |     .size = sizeof(NICState), | 
					
						
							|  |  |  |     .can_receive = eth_can_receive, | 
					
						
							|  |  |  |     .receive = eth_receive, | 
					
						
							|  |  |  |     .cleanup = eth_cleanup, | 
					
						
							|  |  |  |     .link_status_changed = eth_set_link, | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:18 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  | static int fs_eth_init(SysBusDevice *sbd) | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  |     DeviceState *dev = DEVICE(sbd); | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     ETRAXFSEthState *s = ETRAX_FS_ETH(dev); | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     if (!s->dma_out || !s->dma_in) { | 
					
						
							|  |  |  |         hw_error("Unconnected ETRAX-FS Ethernet MAC.\n"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     s->dma_out->client.push = eth_tx_push; | 
					
						
							|  |  |  |     s->dma_out->client.opaque = s; | 
					
						
							|  |  |  |     s->dma_in->client.opaque = s; | 
					
						
							|  |  |  |     s->dma_in->client.pull = NULL; | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-06 21:25:08 -04:00
										 |  |  |     memory_region_init_io(&s->mmio, OBJECT(dev), ð_ops, s, | 
					
						
							|  |  |  |                           "etraxfs-eth", 0x5c); | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  |     sysbus_init_mmio(sbd, &s->mmio); | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     qemu_macaddr_default_if_unset(&s->conf.macaddr); | 
					
						
							|  |  |  |     s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  |                           object_get_typename(OBJECT(s)), dev->id, s); | 
					
						
							| 
									
										
										
										
											2013-01-30 19:12:22 +08:00
										 |  |  |     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 16:15:25 +00:00
										 |  |  |     tdk_init(&s->phy); | 
					
						
							|  |  |  |     mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); | 
					
						
							|  |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2011-08-09 13:24:04 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | static Property etraxfs_eth_properties[] = { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1), | 
					
						
							|  |  |  |     DEFINE_PROP_PTR("dma_out", ETRAXFSEthState, vdma_out), | 
					
						
							|  |  |  |     DEFINE_PROP_PTR("dma_in", ETRAXFSEthState, vdma_in), | 
					
						
							|  |  |  |     DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf), | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void etraxfs_eth_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 = fs_eth_init; | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     dc->props = etraxfs_eth_properties; | 
					
						
							| 
									
										
										
										
											2013-11-29 10:43:44 +01:00
										 |  |  |     /* Reason: pointer properties "dma_out", "dma_in" */ | 
					
						
							|  |  |  |     dc->cannot_instantiate_with_device_add_yet = true; | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-10 16:19:07 +01:00
										 |  |  | static const TypeInfo etraxfs_eth_info = { | 
					
						
							| 
									
										
										
										
											2013-07-27 11:44:01 +02:00
										 |  |  |     .name          = TYPE_ETRAX_FS_ETH, | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     .parent        = TYPE_SYS_BUS_DEVICE, | 
					
						
							| 
									
										
										
										
											2013-07-27 11:48:42 +02:00
										 |  |  |     .instance_size = sizeof(ETRAXFSEthState), | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     .class_init    = etraxfs_eth_class_init, | 
					
						
							| 
									
										
										
										
											2011-08-09 13:24:04 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | static void etraxfs_eth_register_types(void) | 
					
						
							| 
									
										
										
										
											2011-08-09 13:24:04 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     type_register_static(&etraxfs_eth_info); | 
					
						
							| 
									
										
										
										
											2008-05-11 15:04:22 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-08-09 13:24:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | type_init(etraxfs_eth_register_types) |