Add Enhanced Three-Speed Ethernet Controller (eTSEC)
This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. Signed-off-by: Fabien Chouteau <chouteau@adacore.com> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
		
							parent
							
								
									b36f100e17
								
							
						
					
					
						commit
						eb1e7c3e51
					
				| @ -47,4 +47,5 @@ CONFIG_E500=y | ||||
| CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) | ||||
| # For PReP
 | ||||
| CONFIG_MC146818RTC=y | ||||
| CONFIG_ETSEC=y | ||||
| CONFIG_ISA_TESTDEV=y | ||||
|  | ||||
| @ -32,3 +32,6 @@ obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o | ||||
| 
 | ||||
| obj-$(CONFIG_VIRTIO) += virtio-net.o | ||||
| obj-y += vhost_net.o | ||||
| 
 | ||||
| obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
 | ||||
| 			fsl_etsec/rings.o fsl_etsec/miim.o | ||||
|  | ||||
							
								
								
									
										465
									
								
								hw/net/fsl_etsec/etsec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								hw/net/fsl_etsec/etsec.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,465 @@ | ||||
| /*
 | ||||
|  * QEMU Freescale eTSEC Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011-2013 AdaCore | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. | ||||
|  */ | ||||
| 
 | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "hw/sysbus.h" | ||||
| #include "trace.h" | ||||
| #include "hw/ptimer.h" | ||||
| #include "etsec.h" | ||||
| #include "registers.h" | ||||
| 
 | ||||
| /* #define HEX_DUMP */ | ||||
| /* #define DEBUG_REGISTER */ | ||||
| 
 | ||||
| #ifdef DEBUG_REGISTER | ||||
| static const int debug_etsec = 1; | ||||
| #else | ||||
| static const int debug_etsec; | ||||
| #endif | ||||
| 
 | ||||
| #define DPRINTF(fmt, ...) do {                 \ | ||||
|     if (debug_etsec) {                         \ | ||||
|         qemu_log(fmt , ## __VA_ARGS__);        \ | ||||
|     }                                          \ | ||||
|     } while (0) | ||||
| 
 | ||||
| static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) | ||||
| { | ||||
|     eTSEC          *etsec     = opaque; | ||||
|     uint32_t        reg_index = addr / 4; | ||||
|     eTSEC_Register *reg       = NULL; | ||||
|     uint32_t        ret       = 0x0; | ||||
| 
 | ||||
|     assert(reg_index < ETSEC_REG_NUMBER); | ||||
| 
 | ||||
|     reg = &etsec->regs[reg_index]; | ||||
| 
 | ||||
| 
 | ||||
|     switch (reg->access) { | ||||
|     case ACC_WO: | ||||
|         ret = 0x00000000; | ||||
|         break; | ||||
| 
 | ||||
|     case ACC_RW: | ||||
|     case ACC_W1C: | ||||
|     case ACC_RO: | ||||
|     default: | ||||
|         ret = reg->value; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     DPRINTF("Read  0x%08x @ 0x" TARGET_FMT_plx | ||||
|             "                            : %s (%s)\n", | ||||
|             ret, addr, reg->name, reg->desc); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void write_tstat(eTSEC          *etsec, | ||||
|                         eTSEC_Register *reg, | ||||
|                         uint32_t        reg_index, | ||||
|                         uint32_t        value) | ||||
| { | ||||
|     int i = 0; | ||||
| 
 | ||||
|     for (i = 0; i < 8; i++) { | ||||
|         /* Check THLTi flag in TSTAT */ | ||||
|         if (value & (1 << (31 - i))) { | ||||
|             etsec_walk_tx_ring(etsec, i); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Write 1 to clear */ | ||||
|     reg->value &= ~value; | ||||
| } | ||||
| 
 | ||||
| static void write_rstat(eTSEC          *etsec, | ||||
|                         eTSEC_Register *reg, | ||||
|                         uint32_t        reg_index, | ||||
|                         uint32_t        value) | ||||
| { | ||||
|     int i = 0; | ||||
| 
 | ||||
|     for (i = 0; i < 8; i++) { | ||||
|         /* Check QHLTi flag in RSTAT */ | ||||
|         if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) { | ||||
|             etsec_walk_rx_ring(etsec, i); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Write 1 to clear */ | ||||
|     reg->value &= ~value; | ||||
| } | ||||
| 
 | ||||
| static void write_tbasex(eTSEC          *etsec, | ||||
|                          eTSEC_Register *reg, | ||||
|                          uint32_t        reg_index, | ||||
|                          uint32_t        value) | ||||
| { | ||||
|     reg->value = value & ~0x7; | ||||
| 
 | ||||
|     /* Copy this value in the ring's TxBD pointer */ | ||||
|     etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7; | ||||
| } | ||||
| 
 | ||||
| static void write_rbasex(eTSEC          *etsec, | ||||
|                          eTSEC_Register *reg, | ||||
|                          uint32_t        reg_index, | ||||
|                          uint32_t        value) | ||||
| { | ||||
|     reg->value = value & ~0x7; | ||||
| 
 | ||||
|     /* Copy this value in the ring's RxBD pointer */ | ||||
|     etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; | ||||
| } | ||||
| 
 | ||||
| static void write_ievent(eTSEC          *etsec, | ||||
|                          eTSEC_Register *reg, | ||||
|                          uint32_t        reg_index, | ||||
|                          uint32_t        value) | ||||
| { | ||||
|     /* Write 1 to clear */ | ||||
|     reg->value &= ~value; | ||||
| 
 | ||||
|     if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { | ||||
|         qemu_irq_lower(etsec->tx_irq); | ||||
|     } | ||||
|     if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { | ||||
|         qemu_irq_lower(etsec->rx_irq); | ||||
|     } | ||||
| 
 | ||||
|     if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | | ||||
|                         IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | | ||||
|                         IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | | ||||
|                         IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | | ||||
|                         IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | | ||||
|                         IEVENT_MMRW))) { | ||||
|         qemu_irq_lower(etsec->err_irq); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void write_dmactrl(eTSEC          *etsec, | ||||
|                           eTSEC_Register *reg, | ||||
|                           uint32_t        reg_index, | ||||
|                           uint32_t        value) | ||||
| { | ||||
|     reg->value = value; | ||||
| 
 | ||||
|     if (value & DMACTRL_GRS) { | ||||
| 
 | ||||
|         if (etsec->rx_buffer_len != 0) { | ||||
|             /* Graceful receive stop delayed until end of frame */ | ||||
|         } else { | ||||
|             /* Graceful receive stop now */ | ||||
|             etsec->regs[IEVENT].value |= IEVENT_GRSC; | ||||
|             if (etsec->regs[IMASK].value & IMASK_GRSCEN) { | ||||
|                 qemu_irq_raise(etsec->err_irq); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (value & DMACTRL_GTS) { | ||||
| 
 | ||||
|         if (etsec->tx_buffer_len != 0) { | ||||
|             /* Graceful transmit stop delayed until end of frame */ | ||||
|         } else { | ||||
|             /* Graceful transmit stop now */ | ||||
|             etsec->regs[IEVENT].value |= IEVENT_GTSC; | ||||
|             if (etsec->regs[IMASK].value & IMASK_GTSCEN) { | ||||
|                 qemu_irq_raise(etsec->err_irq); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!(value & DMACTRL_WOP)) { | ||||
|         /* Start polling */ | ||||
|         ptimer_stop(etsec->ptimer); | ||||
|         ptimer_set_count(etsec->ptimer, 1); | ||||
|         ptimer_run(etsec->ptimer, 1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void etsec_write(void     *opaque, | ||||
|                         hwaddr    addr, | ||||
|                         uint64_t  value, | ||||
|                         unsigned  size) | ||||
| { | ||||
|     eTSEC          *etsec     = opaque; | ||||
|     uint32_t        reg_index = addr / 4; | ||||
|     eTSEC_Register *reg       = NULL; | ||||
|     uint32_t        before    = 0x0; | ||||
| 
 | ||||
|     assert(reg_index < ETSEC_REG_NUMBER); | ||||
| 
 | ||||
|     reg = &etsec->regs[reg_index]; | ||||
|     before = reg->value; | ||||
| 
 | ||||
|     switch (reg_index) { | ||||
|     case IEVENT: | ||||
|         write_ievent(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     case DMACTRL: | ||||
|         write_dmactrl(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     case TSTAT: | ||||
|         write_tstat(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     case RSTAT: | ||||
|         write_rstat(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     case TBASE0 ... TBASE7: | ||||
|         write_tbasex(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     case RBASE0 ... RBASE7: | ||||
|         write_rbasex(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     case MIIMCFG ... MIIMIND: | ||||
|         etsec_write_miim(etsec, reg, reg_index, value); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         /* Default handling */ | ||||
|         switch (reg->access) { | ||||
| 
 | ||||
|         case ACC_RW: | ||||
|         case ACC_WO: | ||||
|             reg->value = value; | ||||
|             break; | ||||
| 
 | ||||
|         case ACC_W1C: | ||||
|             reg->value &= ~value; | ||||
|             break; | ||||
| 
 | ||||
|         case ACC_RO: | ||||
|         default: | ||||
|             /* Read Only or Unknown register */ | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx | ||||
|             " val:0x%08x->0x%08x : %s (%s)\n", | ||||
|             (unsigned int)value, addr, before, reg->value, | ||||
|             reg->name, reg->desc); | ||||
| } | ||||
| 
 | ||||
| static const MemoryRegionOps etsec_ops = { | ||||
|     .read = etsec_read, | ||||
|     .write = etsec_write, | ||||
|     .endianness = DEVICE_NATIVE_ENDIAN, | ||||
|     .impl = { | ||||
|         .min_access_size = 4, | ||||
|         .max_access_size = 4, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| static void etsec_timer_hit(void *opaque) | ||||
| { | ||||
|     eTSEC *etsec = opaque; | ||||
| 
 | ||||
|     ptimer_stop(etsec->ptimer); | ||||
| 
 | ||||
|     if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) { | ||||
| 
 | ||||
|         if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) { | ||||
|             etsec_walk_tx_ring(etsec, 0); | ||||
|         } | ||||
|         ptimer_set_count(etsec->ptimer, 1); | ||||
|         ptimer_run(etsec->ptimer, 1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void etsec_reset(DeviceState *d) | ||||
| { | ||||
|     eTSEC *etsec = ETSEC_COMMON(d); | ||||
|     int i = 0; | ||||
|     int reg_index = 0; | ||||
| 
 | ||||
|     /* Default value for all registers */ | ||||
|     for (i = 0; i < ETSEC_REG_NUMBER; i++) { | ||||
|         etsec->regs[i].name   = "Reserved"; | ||||
|         etsec->regs[i].desc   = ""; | ||||
|         etsec->regs[i].access = ACC_UNKNOWN; | ||||
|         etsec->regs[i].value  = 0x00000000; | ||||
|     } | ||||
| 
 | ||||
|     /* Set-up known registers */ | ||||
|     for (i = 0; eTSEC_registers_def[i].name != NULL; i++) { | ||||
| 
 | ||||
|         reg_index = eTSEC_registers_def[i].offset / 4; | ||||
| 
 | ||||
|         etsec->regs[reg_index].name   = eTSEC_registers_def[i].name; | ||||
|         etsec->regs[reg_index].desc   = eTSEC_registers_def[i].desc; | ||||
|         etsec->regs[reg_index].access = eTSEC_registers_def[i].access; | ||||
|         etsec->regs[reg_index].value  = eTSEC_registers_def[i].reset; | ||||
|     } | ||||
| 
 | ||||
|     etsec->tx_buffer     = NULL; | ||||
|     etsec->tx_buffer_len = 0; | ||||
|     etsec->rx_buffer     = NULL; | ||||
|     etsec->rx_buffer_len = 0; | ||||
| 
 | ||||
|     etsec->phy_status = | ||||
|         MII_SR_EXTENDED_CAPS    | MII_SR_LINK_STATUS   | MII_SR_AUTONEG_CAPS  | | ||||
|         MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS | | ||||
|         MII_SR_EXTENDED_STATUS  | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | | ||||
|         MII_SR_10T_HD_CAPS      | MII_SR_10T_FD_CAPS   | MII_SR_100X_HD_CAPS  | | ||||
|         MII_SR_100X_FD_CAPS     | MII_SR_100T4_CAPS; | ||||
| } | ||||
| 
 | ||||
| static void etsec_cleanup(NetClientState *nc) | ||||
| { | ||||
|     /* qemu_log("eTSEC cleanup\n"); */ | ||||
| } | ||||
| 
 | ||||
| static int etsec_can_receive(NetClientState *nc) | ||||
| { | ||||
|     eTSEC *etsec = qemu_get_nic_opaque(nc); | ||||
| 
 | ||||
|     return etsec->rx_buffer_len == 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t etsec_receive(NetClientState *nc, | ||||
|                              const uint8_t  *buf, | ||||
|                              size_t          size) | ||||
| { | ||||
|     eTSEC *etsec = qemu_get_nic_opaque(nc); | ||||
| 
 | ||||
| #if defined(HEX_DUMP) | ||||
|     fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size); | ||||
|     qemu_hexdump(buf, stderr, "", size); | ||||
| #endif | ||||
|     etsec_rx_ring_write(etsec, buf, size); | ||||
|     return size; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void etsec_set_link_status(NetClientState *nc) | ||||
| { | ||||
|     eTSEC *etsec = qemu_get_nic_opaque(nc); | ||||
| 
 | ||||
|     etsec_miim_link_status(etsec, nc); | ||||
| } | ||||
| 
 | ||||
| static NetClientInfo net_etsec_info = { | ||||
|     .type = NET_CLIENT_OPTIONS_KIND_NIC, | ||||
|     .size = sizeof(NICState), | ||||
|     .can_receive = etsec_can_receive, | ||||
|     .receive = etsec_receive, | ||||
|     .cleanup = etsec_cleanup, | ||||
|     .link_status_changed = etsec_set_link_status, | ||||
| }; | ||||
| 
 | ||||
| static void etsec_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     eTSEC        *etsec = ETSEC_COMMON(dev); | ||||
| 
 | ||||
|     etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, | ||||
|                               object_get_typename(OBJECT(dev)), dev->id, etsec); | ||||
|     qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); | ||||
| 
 | ||||
| 
 | ||||
|     etsec->bh     = qemu_bh_new(etsec_timer_hit, etsec); | ||||
|     etsec->ptimer = ptimer_init(etsec->bh); | ||||
|     ptimer_set_freq(etsec->ptimer, 100); | ||||
| } | ||||
| 
 | ||||
| static void etsec_instance_init(Object *obj) | ||||
| { | ||||
|     eTSEC        *etsec = ETSEC_COMMON(obj); | ||||
|     SysBusDevice *sbd   = SYS_BUS_DEVICE(obj); | ||||
| 
 | ||||
|     memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec, | ||||
|                           "eTSEC", 0x1000); | ||||
|     sysbus_init_mmio(sbd, &etsec->io_area); | ||||
| 
 | ||||
|     sysbus_init_irq(sbd, &etsec->tx_irq); | ||||
|     sysbus_init_irq(sbd, &etsec->rx_irq); | ||||
|     sysbus_init_irq(sbd, &etsec->err_irq); | ||||
| } | ||||
| 
 | ||||
| static Property etsec_properties[] = { | ||||
|     DEFINE_NIC_PROPERTIES(eTSEC, conf), | ||||
|     DEFINE_PROP_END_OF_LIST(), | ||||
| }; | ||||
| 
 | ||||
| static void etsec_class_init(ObjectClass *klass, void *data) | ||||
| { | ||||
|     DeviceClass *dc = DEVICE_CLASS(klass); | ||||
| 
 | ||||
|     dc->realize = etsec_realize; | ||||
|     dc->reset = etsec_reset; | ||||
|     dc->props = etsec_properties; | ||||
| } | ||||
| 
 | ||||
| static TypeInfo etsec_info = { | ||||
|     .name                  = "eTSEC", | ||||
|     .parent                = TYPE_SYS_BUS_DEVICE, | ||||
|     .instance_size         = sizeof(eTSEC), | ||||
|     .class_init            = etsec_class_init, | ||||
|     .instance_init         = etsec_instance_init, | ||||
| }; | ||||
| 
 | ||||
| static void etsec_register_types(void) | ||||
| { | ||||
|     type_register_static(&etsec_info); | ||||
| } | ||||
| 
 | ||||
| type_init(etsec_register_types) | ||||
| 
 | ||||
| DeviceState *etsec_create(hwaddr         base, | ||||
|                           MemoryRegion * mr, | ||||
|                           NICInfo      * nd, | ||||
|                           qemu_irq       tx_irq, | ||||
|                           qemu_irq       rx_irq, | ||||
|                           qemu_irq       err_irq) | ||||
| { | ||||
|     DeviceState *dev; | ||||
| 
 | ||||
|     dev = qdev_create(NULL, "eTSEC"); | ||||
|     qdev_set_nic_properties(dev, nd); | ||||
| 
 | ||||
|     if (qdev_init(dev)) { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); | ||||
|     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); | ||||
|     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq); | ||||
| 
 | ||||
|     memory_region_add_subregion(mr, base, | ||||
|                                 SYS_BUS_DEVICE(dev)->mmio[0].memory); | ||||
| 
 | ||||
|     return dev; | ||||
| } | ||||
							
								
								
									
										174
									
								
								hw/net/fsl_etsec/etsec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								hw/net/fsl_etsec/etsec.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,174 @@ | ||||
| /*
 | ||||
|  * QEMU Freescale eTSEC Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011-2013 AdaCore | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #ifndef _ETSEC_H_ | ||||
| #define _ETSEC_H_ | ||||
| 
 | ||||
| #include "hw/qdev.h" | ||||
| #include "hw/sysbus.h" | ||||
| #include "net/net.h" | ||||
| #include "hw/ptimer.h" | ||||
| 
 | ||||
| /* Buffer Descriptors */ | ||||
| 
 | ||||
| typedef struct eTSEC_rxtx_bd { | ||||
|     uint16_t flags; | ||||
|     uint16_t length; | ||||
|     uint32_t bufptr; | ||||
| } eTSEC_rxtx_bd; | ||||
| 
 | ||||
| #define BD_WRAP       (1 << 13) | ||||
| #define BD_INTERRUPT  (1 << 12) | ||||
| #define BD_LAST       (1 << 11) | ||||
| 
 | ||||
| #define BD_TX_READY     (1 << 15) | ||||
| #define BD_TX_PADCRC    (1 << 14) | ||||
| #define BD_TX_TC        (1 << 10) | ||||
| #define BD_TX_PREDEF    (1 << 9) | ||||
| #define BD_TX_HFELC     (1 << 7) | ||||
| #define BD_TX_CFRL      (1 << 6) | ||||
| #define BD_TX_RC_MASK   0xF | ||||
| #define BD_TX_RC_OFFSET 0x2 | ||||
| #define BD_TX_TOEUN     (1 << 1) | ||||
| #define BD_TX_TR        (1 << 0) | ||||
| 
 | ||||
| #define BD_RX_EMPTY     (1 << 15) | ||||
| #define BD_RX_RO1       (1 << 14) | ||||
| #define BD_RX_FIRST     (1 << 10) | ||||
| #define BD_RX_MISS      (1 << 8) | ||||
| #define BD_RX_BROADCAST (1 << 7) | ||||
| #define BD_RX_MULTICAST (1 << 6) | ||||
| #define BD_RX_LG        (1 << 5) | ||||
| #define BD_RX_NO        (1 << 4) | ||||
| #define BD_RX_SH        (1 << 3) | ||||
| #define BD_RX_CR        (1 << 2) | ||||
| #define BD_RX_OV        (1 << 1) | ||||
| #define BD_RX_TR        (1 << 0) | ||||
| 
 | ||||
| /* Tx FCB flags */ | ||||
| #define FCB_TX_VLN     (1 << 7) | ||||
| #define FCB_TX_IP      (1 << 6) | ||||
| #define FCB_TX_IP6     (1 << 5) | ||||
| #define FCB_TX_TUP     (1 << 4) | ||||
| #define FCB_TX_UDP     (1 << 3) | ||||
| #define FCB_TX_CIP     (1 << 2) | ||||
| #define FCB_TX_CTU     (1 << 1) | ||||
| #define FCB_TX_NPH     (1 << 0) | ||||
| 
 | ||||
| /* PHY Status Register */ | ||||
| #define MII_SR_EXTENDED_CAPS     0x0001    /* Extended register capabilities */ | ||||
| #define MII_SR_JABBER_DETECT     0x0002    /* Jabber Detected */ | ||||
| #define MII_SR_LINK_STATUS       0x0004    /* Link Status 1 = link */ | ||||
| #define MII_SR_AUTONEG_CAPS      0x0008    /* Auto Neg Capable */ | ||||
| #define MII_SR_REMOTE_FAULT      0x0010    /* Remote Fault Detect */ | ||||
| #define MII_SR_AUTONEG_COMPLETE  0x0020    /* Auto Neg Complete */ | ||||
| #define MII_SR_PREAMBLE_SUPPRESS 0x0040    /* Preamble may be suppressed */ | ||||
| #define MII_SR_EXTENDED_STATUS   0x0100    /* Ext. status info in Reg 0x0F */ | ||||
| #define MII_SR_100T2_HD_CAPS     0x0200    /* 100T2 Half Duplex Capable */ | ||||
| #define MII_SR_100T2_FD_CAPS     0x0400    /* 100T2 Full Duplex Capable */ | ||||
| #define MII_SR_10T_HD_CAPS       0x0800    /* 10T   Half Duplex Capable */ | ||||
| #define MII_SR_10T_FD_CAPS       0x1000    /* 10T   Full Duplex Capable */ | ||||
| #define MII_SR_100X_HD_CAPS      0x2000    /* 100X  Half Duplex Capable */ | ||||
| #define MII_SR_100X_FD_CAPS      0x4000    /* 100X  Full Duplex Capable */ | ||||
| #define MII_SR_100T4_CAPS        0x8000    /* 100T4 Capable */ | ||||
| 
 | ||||
| /* eTSEC */ | ||||
| 
 | ||||
| /* Number of register in the device */ | ||||
| #define ETSEC_REG_NUMBER 1024 | ||||
| 
 | ||||
| typedef struct eTSEC_Register { | ||||
|     const char *name; | ||||
|     const char *desc; | ||||
|     uint32_t    access; | ||||
|     uint32_t    value; | ||||
| } eTSEC_Register; | ||||
| 
 | ||||
| typedef struct eTSEC { | ||||
|     SysBusDevice  busdev; | ||||
| 
 | ||||
|     MemoryRegion  io_area; | ||||
| 
 | ||||
|     eTSEC_Register regs[ETSEC_REG_NUMBER]; | ||||
| 
 | ||||
|     NICState *nic; | ||||
|     NICConf   conf; | ||||
| 
 | ||||
|     /* Tx */ | ||||
| 
 | ||||
|     uint8_t       *tx_buffer; | ||||
|     uint32_t       tx_buffer_len; | ||||
|     eTSEC_rxtx_bd  first_bd; | ||||
| 
 | ||||
|     /* Rx */ | ||||
| 
 | ||||
|     uint8_t       *rx_buffer; | ||||
|     uint32_t       rx_buffer_len; | ||||
|     uint32_t       rx_remaining_data; | ||||
|     uint8_t        rx_first_in_frame; | ||||
|     uint8_t        rx_fcb_size; | ||||
|     eTSEC_rxtx_bd  rx_first_bd; | ||||
|     uint8_t        rx_fcb[10]; | ||||
|     uint32_t       rx_padding; | ||||
| 
 | ||||
|     /* IRQs */ | ||||
|     qemu_irq     tx_irq; | ||||
|     qemu_irq     rx_irq; | ||||
|     qemu_irq     err_irq; | ||||
| 
 | ||||
| 
 | ||||
|     uint16_t phy_status; | ||||
|     uint16_t phy_control; | ||||
| 
 | ||||
|     /* Polling */ | ||||
|     QEMUBH *bh; | ||||
|     struct ptimer_state *ptimer; | ||||
| 
 | ||||
| } eTSEC; | ||||
| 
 | ||||
| #define TYPE_ETSEC_COMMON "eTSEC" | ||||
| #define ETSEC_COMMON(obj) \ | ||||
|      OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON) | ||||
| 
 | ||||
| #define eTSEC_TRANSMIT 1 | ||||
| #define eTSEC_RECEIVE  2 | ||||
| 
 | ||||
| DeviceState *etsec_create(hwaddr        base, | ||||
|                           MemoryRegion *mr, | ||||
|                           NICInfo      *nd, | ||||
|                           qemu_irq      tx_irq, | ||||
|                           qemu_irq      rx_irq, | ||||
|                           qemu_irq      err_irq); | ||||
| 
 | ||||
| void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); | ||||
| void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); | ||||
| void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); | ||||
| 
 | ||||
| void etsec_write_miim(eTSEC          *etsec, | ||||
|                       eTSEC_Register *reg, | ||||
|                       uint32_t        reg_index, | ||||
|                       uint32_t        value); | ||||
| 
 | ||||
| void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc); | ||||
| 
 | ||||
| #endif /* ! _ETSEC_H_ */ | ||||
							
								
								
									
										146
									
								
								hw/net/fsl_etsec/miim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								hw/net/fsl_etsec/miim.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | ||||
| /*
 | ||||
|  * QEMU Freescale eTSEC Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011-2013 AdaCore | ||||
|  * | ||||
|  * 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 "etsec.h" | ||||
| #include "registers.h" | ||||
| 
 | ||||
| /* #define DEBUG_MIIM */ | ||||
| 
 | ||||
| #define MIIM_CONTROL    0 | ||||
| #define MIIM_STATUS     1 | ||||
| #define MIIM_PHY_ID_1   2 | ||||
| #define MIIM_PHY_ID_2   3 | ||||
| #define MIIM_T2_STATUS  10 | ||||
| #define MIIM_EXT_STATUS 15 | ||||
| 
 | ||||
| static void miim_read_cycle(eTSEC *etsec) | ||||
| { | ||||
|     uint8_t  phy; | ||||
|     uint8_t  addr; | ||||
|     uint16_t value; | ||||
| 
 | ||||
|     phy  = (etsec->regs[MIIMADD].value >> 8) & 0x1F; | ||||
|     (void)phy; /* Unreferenced */ | ||||
|     addr = etsec->regs[MIIMADD].value & 0x1F; | ||||
| 
 | ||||
|     switch (addr) { | ||||
|     case MIIM_CONTROL: | ||||
|         value = etsec->phy_control; | ||||
|         break; | ||||
|     case MIIM_STATUS: | ||||
|         value = etsec->phy_status; | ||||
|         break; | ||||
|     case MIIM_T2_STATUS: | ||||
|         value = 0x1800;           /* Local and remote receivers OK */ | ||||
|         break; | ||||
|     default: | ||||
|         value = 0x0; | ||||
|         break; | ||||
|     }; | ||||
| 
 | ||||
| #ifdef DEBUG_MIIM | ||||
|     qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); | ||||
| #endif | ||||
| 
 | ||||
|     etsec->regs[MIIMSTAT].value = value; | ||||
| } | ||||
| 
 | ||||
| static void miim_write_cycle(eTSEC *etsec) | ||||
| { | ||||
|     uint8_t  phy; | ||||
|     uint8_t  addr; | ||||
|     uint16_t value; | ||||
| 
 | ||||
|     phy   = (etsec->regs[MIIMADD].value >> 8) & 0x1F; | ||||
|     (void)phy; /* Unreferenced */ | ||||
|     addr  = etsec->regs[MIIMADD].value & 0x1F; | ||||
|     value = etsec->regs[MIIMCON].value & 0xffff; | ||||
| 
 | ||||
| #ifdef DEBUG_MIIM | ||||
|     qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); | ||||
| #endif | ||||
| 
 | ||||
|     switch (addr) { | ||||
|     case MIIM_CONTROL: | ||||
|         etsec->phy_control = value & ~(0x8100); | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| void etsec_write_miim(eTSEC          *etsec, | ||||
|                       eTSEC_Register *reg, | ||||
|                       uint32_t        reg_index, | ||||
|                       uint32_t        value) | ||||
| { | ||||
| 
 | ||||
|     switch (reg_index) { | ||||
| 
 | ||||
|     case MIIMCOM: | ||||
|         /* Read and scan cycle */ | ||||
| 
 | ||||
|         if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) { | ||||
|             /* Read */ | ||||
|             miim_read_cycle(etsec); | ||||
|         } | ||||
|         reg->value = value; | ||||
|         break; | ||||
| 
 | ||||
|     case MIIMCON: | ||||
|         reg->value = value & 0xffff; | ||||
|         miim_write_cycle(etsec); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         /* Default handling */ | ||||
|         switch (reg->access) { | ||||
| 
 | ||||
|         case ACC_RW: | ||||
|         case ACC_WO: | ||||
|             reg->value = value; | ||||
|             break; | ||||
| 
 | ||||
|         case ACC_W1C: | ||||
|             reg->value &= ~value; | ||||
|             break; | ||||
| 
 | ||||
|         case ACC_RO: | ||||
|         default: | ||||
|             /* Read Only or Unknown register */ | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc) | ||||
| { | ||||
|     /* Set link status */ | ||||
|     if (nc->link_down) { | ||||
|         etsec->phy_status &= ~MII_SR_LINK_STATUS; | ||||
|     } else { | ||||
|         etsec->phy_status |= MII_SR_LINK_STATUS; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										295
									
								
								hw/net/fsl_etsec/registers.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								hw/net/fsl_etsec/registers.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,295 @@ | ||||
| /*
 | ||||
|  * QEMU Freescale eTSEC Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011-2013 AdaCore | ||||
|  * | ||||
|  * 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 "registers.h" | ||||
| 
 | ||||
| const eTSEC_Register_Definition eTSEC_registers_def[] = { | ||||
| {0x000, "TSEC_ID",  "Controller ID register",    ACC_RO,  0x01240000}, | ||||
| {0x004, "TSEC_ID2", "Controller ID register 2",  ACC_RO,  0x003000F0}, | ||||
| {0x010, "IEVENT",   "Interrupt event register",  ACC_W1C, 0x00000000}, | ||||
| {0x014, "IMASK",    "Interrupt mask register",   ACC_RW,  0x00000000}, | ||||
| {0x018, "EDIS",     "Error disabled register",   ACC_RW,  0x00000000}, | ||||
| {0x020, "ECNTRL",   "Ethernet control register", ACC_RW,  0x00000040}, | ||||
| {0x028, "PTV",      "Pause time value register", ACC_RW,  0x00000000}, | ||||
| {0x02C, "DMACTRL",  "DMA control register",      ACC_RW,  0x00000000}, | ||||
| {0x030, "TBIPA",    "TBI PHY address register",  ACC_RW,  0x00000000}, | ||||
| 
 | ||||
| /* eTSEC FIFO Control and Status Registers */ | ||||
| 
 | ||||
| {0x058, "FIFO_RX_ALARM",          "FIFO receive alarm start threshold register",    ACC_RW, 0x00000040}, | ||||
| {0x05C, "FIFO_RX_ALARM_SHUTOFF",  "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080}, | ||||
| {0x08C, "FIFO_TX_THR",            "FIFO transmit threshold register",               ACC_RW, 0x00000080}, | ||||
| {0x098, "FIFO_TX_STARVE",         "FIFO transmit starve register",                  ACC_RW, 0x00000040}, | ||||
| {0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register",         ACC_RW, 0x00000080}, | ||||
| 
 | ||||
| /* eTSEC Transmit Control and Status Registers */ | ||||
| 
 | ||||
| {0x100, "TCTRL",        "Transmit control register",                ACC_RW,  0x00000000}, | ||||
| {0x104, "TSTAT",        "Transmit status register",                 ACC_W1C, 0x00000000}, | ||||
| {0x108, "DFVLAN",       "Default VLAN control word",                ACC_RW,  0x81000000}, | ||||
| {0x110, "TXIC",         "Transmit interrupt coalescing register",   ACC_RW,  0x00000000}, | ||||
| {0x114, "TQUEUE",       "Transmit queue control register",          ACC_RW,  0x00008000}, | ||||
| {0x140, "TR03WT",       "TxBD Rings 0-3 round-robin weightings",    ACC_RW,  0x00000000}, | ||||
| {0x144, "TR47WT",       "TxBD Rings 4-7 round-robin weightings",    ACC_RW,  0x00000000}, | ||||
| {0x180, "TBDBPH",       "Tx data buffer pointer high bits",         ACC_RW,  0x00000000}, | ||||
| {0x184, "TBPTR0",       "TxBD pointer for ring 0",                  ACC_RW,  0x00000000}, | ||||
| {0x18C, "TBPTR1",       "TxBD pointer for ring 1",                  ACC_RW,  0x00000000}, | ||||
| {0x194, "TBPTR2",       "TxBD pointer for ring 2",                  ACC_RW,  0x00000000}, | ||||
| {0x19C, "TBPTR3",       "TxBD pointer for ring 3",                  ACC_RW,  0x00000000}, | ||||
| {0x1A4, "TBPTR4",       "TxBD pointer for ring 4",                  ACC_RW,  0x00000000}, | ||||
| {0x1AC, "TBPTR5",       "TxBD pointer for ring 5",                  ACC_RW,  0x00000000}, | ||||
| {0x1B4, "TBPTR6",       "TxBD pointer for ring 6",                  ACC_RW,  0x00000000}, | ||||
| {0x1BC, "TBPTR7",       "TxBD pointer for ring 7",                  ACC_RW,  0x00000000}, | ||||
| {0x200, "TBASEH",       "TxBD base address high bits",              ACC_RW,  0x00000000}, | ||||
| {0x204, "TBASE0",       "TxBD base address of ring 0",              ACC_RW,  0x00000000}, | ||||
| {0x20C, "TBASE1",       "TxBD base address of ring 1",              ACC_RW,  0x00000000}, | ||||
| {0x214, "TBASE2",       "TxBD base address of ring 2",              ACC_RW,  0x00000000}, | ||||
| {0x21C, "TBASE3",       "TxBD base address of ring 3",              ACC_RW,  0x00000000}, | ||||
| {0x224, "TBASE4",       "TxBD base address of ring 4",              ACC_RW,  0x00000000}, | ||||
| {0x22C, "TBASE5",       "TxBD base address of ring 5",              ACC_RW,  0x00000000}, | ||||
| {0x234, "TBASE6",       "TxBD base address of ring 6",              ACC_RW,  0x00000000}, | ||||
| {0x23C, "TBASE7",       "TxBD base address of ring 7",              ACC_RW,  0x00000000}, | ||||
| {0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO,  0x00000000}, | ||||
| {0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO,  0x00000000}, | ||||
| {0x2C0, "TMR_TXTS1_H",  "Tx time stamp high (set 1)",               ACC_RO,  0x00000000}, | ||||
| {0x2C4, "TMR_TXTS1_L",  "Tx time stamp high (set 1)",               ACC_RO,  0x00000000}, | ||||
| {0x2C8, "TMR_TXTS2_H",  "Tx time stamp high (set 2)",               ACC_RO,  0x00000000}, | ||||
| {0x2CC, "TMR_TXTS2_L",  "Tx time stamp high (set 2)",               ACC_RO,  0x00000000}, | ||||
| 
 | ||||
| /* eTSEC Receive Control and Status Registers */ | ||||
| 
 | ||||
| {0x300, "RCTRL",      "Receive control register",                     ACC_RW,  0x00000000}, | ||||
| {0x304, "RSTAT",      "Receive status register",                      ACC_W1C, 0x00000000}, | ||||
| {0x310, "RXIC",       "Receive interrupt coalescing register",        ACC_RW,  0x00000000}, | ||||
| {0x314, "RQUEUE",     "Receive queue control register.",              ACC_RW,  0x00800080}, | ||||
| {0x330, "RBIFX",      "Receive bit field extract control register",   ACC_RW,  0x00000000}, | ||||
| {0x334, "RQFAR",      "Receive queue filing table address register",  ACC_RW,  0x00000000}, | ||||
| {0x338, "RQFCR",      "Receive queue filing table control register",  ACC_RW,  0x00000000}, | ||||
| {0x33C, "RQFPR",      "Receive queue filing table property register", ACC_RW,  0x00000000}, | ||||
| {0x340, "MRBLR",      "Maximum receive buffer length register",       ACC_RW,  0x00000000}, | ||||
| {0x380, "RBDBPH",     "Rx data buffer pointer high bits",             ACC_RW,  0x00000000}, | ||||
| {0x384, "RBPTR0",     "RxBD pointer for ring 0",                      ACC_RW,  0x00000000}, | ||||
| {0x38C, "RBPTR1",     "RxBD pointer for ring 1",                      ACC_RW,  0x00000000}, | ||||
| {0x394, "RBPTR2",     "RxBD pointer for ring 2",                      ACC_RW,  0x00000000}, | ||||
| {0x39C, "RBPTR3",     "RxBD pointer for ring 3",                      ACC_RW,  0x00000000}, | ||||
| {0x3A4, "RBPTR4",     "RxBD pointer for ring 4",                      ACC_RW,  0x00000000}, | ||||
| {0x3AC, "RBPTR5",     "RxBD pointer for ring 5",                      ACC_RW,  0x00000000}, | ||||
| {0x3B4, "RBPTR6",     "RxBD pointer for ring 6",                      ACC_RW,  0x00000000}, | ||||
| {0x3BC, "RBPTR7",     "RxBD pointer for ring 7",                      ACC_RW,  0x00000000}, | ||||
| {0x400, "RBASEH",     "RxBD base address high bits",                  ACC_RW,  0x00000000}, | ||||
| {0x404, "RBASE0",     "RxBD base address of ring 0",                  ACC_RW,  0x00000000}, | ||||
| {0x40C, "RBASE1",     "RxBD base address of ring 1",                  ACC_RW,  0x00000000}, | ||||
| {0x414, "RBASE2",     "RxBD base address of ring 2",                  ACC_RW,  0x00000000}, | ||||
| {0x41C, "RBASE3",     "RxBD base address of ring 3",                  ACC_RW,  0x00000000}, | ||||
| {0x424, "RBASE4",     "RxBD base address of ring 4",                  ACC_RW,  0x00000000}, | ||||
| {0x42C, "RBASE5",     "RxBD base address of ring 5",                  ACC_RW,  0x00000000}, | ||||
| {0x434, "RBASE6",     "RxBD base address of ring 6",                  ACC_RW,  0x00000000}, | ||||
| {0x43C, "RBASE7",     "RxBD base address of ring 7",                  ACC_RW,  0x00000000}, | ||||
| {0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high",            ACC_RW,  0x00000000}, | ||||
| {0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low",             ACC_RW,  0x00000000}, | ||||
| 
 | ||||
| /* eTSEC MAC Registers */ | ||||
| 
 | ||||
| {0x500, "MACCFG1",     "MAC configuration register 1",          ACC_RW, 0x00000000}, | ||||
| {0x504, "MACCFG2",     "MAC configuration register 2",          ACC_RW, 0x00007000}, | ||||
| {0x508, "IPGIFG",      "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060}, | ||||
| {0x50C, "HAFDUP",      "Half-duplex control",                   ACC_RW, 0x00A1F037}, | ||||
| {0x510, "MAXFRM",      "Maximum frame length",                  ACC_RW, 0x00000600}, | ||||
| {0x520, "MIIMCFG",     "MII management configuration",          ACC_RW, 0x00000007}, | ||||
| {0x524, "MIIMCOM",     "MII management command",                ACC_RW, 0x00000000}, | ||||
| {0x528, "MIIMADD",     "MII management address",                ACC_RW, 0x00000000}, | ||||
| {0x52C, "MIIMCON",     "MII management control",                ACC_WO, 0x00000000}, | ||||
| {0x530, "MIIMSTAT",    "MII management status",                 ACC_RO, 0x00000000}, | ||||
| {0x534, "MIIMIND",     "MII management indicator",              ACC_RO, 0x00000000}, | ||||
| {0x53C, "IFSTAT",      "Interface status",                      ACC_RO, 0x00000000}, | ||||
| {0x540, "MACSTNADDR1", "MAC station address register 1",        ACC_RW, 0x00000000}, | ||||
| {0x544, "MACSTNADDR2", "MAC station address register 2",        ACC_RW, 0x00000000}, | ||||
| {0x548, "MAC01ADDR1",  "MAC exact match address 1, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x54C, "MAC01ADDR2",  "MAC exact match address 1, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x550, "MAC02ADDR1",  "MAC exact match address 2, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x554, "MAC02ADDR2",  "MAC exact match address 2, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x558, "MAC03ADDR1",  "MAC exact match address 3, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x55C, "MAC03ADDR2",  "MAC exact match address 3, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x560, "MAC04ADDR1",  "MAC exact match address 4, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x564, "MAC04ADDR2",  "MAC exact match address 4, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x568, "MAC05ADDR1",  "MAC exact match address 5, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x56C, "MAC05ADDR2",  "MAC exact match address 5, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x570, "MAC06ADDR1",  "MAC exact match address 6, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x574, "MAC06ADDR2",  "MAC exact match address 6, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x578, "MAC07ADDR1",  "MAC exact match address 7, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x57C, "MAC07ADDR2",  "MAC exact match address 7, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x580, "MAC08ADDR1",  "MAC exact match address 8, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x584, "MAC08ADDR2",  "MAC exact match address 8, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x588, "MAC09ADDR1",  "MAC exact match address 9, part 1",     ACC_RW, 0x00000000}, | ||||
| {0x58C, "MAC09ADDR2",  "MAC exact match address 9, part 2",     ACC_RW, 0x00000000}, | ||||
| {0x590, "MAC10ADDR1",  "MAC exact match address 10, part 1",    ACC_RW, 0x00000000}, | ||||
| {0x594, "MAC10ADDR2",  "MAC exact match address 10, part 2",    ACC_RW, 0x00000000}, | ||||
| {0x598, "MAC11ADDR1",  "MAC exact match address 11, part 1",    ACC_RW, 0x00000000}, | ||||
| {0x59C, "MAC11ADDR2",  "MAC exact match address 11, part 2",    ACC_RW, 0x00000000}, | ||||
| {0x5A0, "MAC12ADDR1",  "MAC exact match address 12, part 1",    ACC_RW, 0x00000000}, | ||||
| {0x5A4, "MAC12ADDR2",  "MAC exact match address 12, part 2",    ACC_RW, 0x00000000}, | ||||
| {0x5A8, "MAC13ADDR1",  "MAC exact match address 13, part 1",    ACC_RW, 0x00000000}, | ||||
| {0x5AC, "MAC13ADDR2",  "MAC exact match address 13, part 2",    ACC_RW, 0x00000000}, | ||||
| {0x5B0, "MAC14ADDR1",  "MAC exact match address 14, part 1",    ACC_RW, 0x00000000}, | ||||
| {0x5B4, "MAC14ADDR2",  "MAC exact match address 14, part 2",    ACC_RW, 0x00000000}, | ||||
| {0x5B8, "MAC15ADDR1",  "MAC exact match address 15, part 1",    ACC_RW, 0x00000000}, | ||||
| {0x5BC, "MAC15ADDR2",  "MAC exact match address 15, part 2",    ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| /* eTSEC, "Transmit", "and", Receive, Counters */ | ||||
| 
 | ||||
| {0x680, "TR64",  "Transmit and receive 64-byte frame counter ",                   ACC_RW, 0x00000000}, | ||||
| {0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter",            ACC_RW, 0x00000000}, | ||||
| {0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter",           ACC_RW, 0x00000000}, | ||||
| {0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter",           ACC_RW, 0x00000000}, | ||||
| {0x690, "TR1K",  "Transmit and receive 512- to 1023-byte frame counter",          ACC_RW, 0x00000000}, | ||||
| {0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter",         ACC_RW, 0x00000000}, | ||||
| {0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| /* eTSEC Receive Counters */ | ||||
| 
 | ||||
| {0x69C, "RBYT", "Receive byte counter",                  ACC_RW, 0x00000000}, | ||||
| {0x6A0, "RPKT", "Receive packet counter",                ACC_RW, 0x00000000}, | ||||
| {0x6A4, "RFCS", "Receive FCS error counter",             ACC_RW, 0x00000000}, | ||||
| {0x6A8, "RMCA", "Receive multicast packet counter",      ACC_RW, 0x00000000}, | ||||
| {0x6AC, "RBCA", "Receive broadcast packet counter",      ACC_RW, 0x00000000}, | ||||
| {0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000}, | ||||
| {0x6B4, "RXPF", "Receive PAUSE frame packet counter",    ACC_RW, 0x00000000}, | ||||
| {0x6B8, "RXUO", "Receive unknown OP code counter ",      ACC_RW, 0x00000000}, | ||||
| {0x6BC, "RALN", "Receive alignment error counter ",      ACC_RW, 0x00000000}, | ||||
| {0x6C0, "RFLR", "Receive frame length error counter ",   ACC_RW, 0x00000000}, | ||||
| {0x6C4, "RCDE", "Receive code error counter ",           ACC_RW, 0x00000000}, | ||||
| {0x6C8, "RCSE", "Receive carrier sense error counter",   ACC_RW, 0x00000000}, | ||||
| {0x6CC, "RUND", "Receive undersize packet counter",      ACC_RW, 0x00000000}, | ||||
| {0x6D0, "ROVR", "Receive oversize packet counter ",      ACC_RW, 0x00000000}, | ||||
| {0x6D4, "RFRG", "Receive fragments counter",             ACC_RW, 0x00000000}, | ||||
| {0x6D8, "RJBR", "Receive jabber counter ",               ACC_RW, 0x00000000}, | ||||
| {0x6DC, "RDRP", "Receive drop counter",                  ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| /* eTSEC Transmit Counters */ | ||||
| 
 | ||||
| {0x6E0, "TBYT", "Transmit byte counter",                       ACC_RW, 0x00000000}, | ||||
| {0x6E4, "TPKT", "Transmit packet counter",                     ACC_RW, 0x00000000}, | ||||
| {0x6E8, "TMCA", "Transmit multicast packet counter ",          ACC_RW, 0x00000000}, | ||||
| {0x6EC, "TBCA", "Transmit broadcast packet counter ",          ACC_RW, 0x00000000}, | ||||
| {0x6F0, "TXPF", "Transmit PAUSE control frame counter ",       ACC_RW, 0x00000000}, | ||||
| {0x6F4, "TDFR", "Transmit deferral packet counter ",           ACC_RW, 0x00000000}, | ||||
| {0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000}, | ||||
| {0x6FC, "TSCL", "Transmit single collision packet counter",    ACC_RW, 0x00000000}, | ||||
| {0x700, "TMCL", "Transmit multiple collision packet counter",  ACC_RW, 0x00000000}, | ||||
| {0x704, "TLCL", "Transmit late collision packet counter",      ACC_RW, 0x00000000}, | ||||
| {0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000}, | ||||
| {0x70C, "TNCL", "Transmit total collision counter ",           ACC_RW, 0x00000000}, | ||||
| {0x714, "TDRP", "Transmit drop frame counter",                 ACC_RW, 0x00000000}, | ||||
| {0x718, "TJBR", "Transmit jabber frame counter ",              ACC_RW, 0x00000000}, | ||||
| {0x71C, "TFCS", "Transmit FCS error counter",                  ACC_RW, 0x00000000}, | ||||
| {0x720, "TXCF", "Transmit control frame counter ",             ACC_RW, 0x00000000}, | ||||
| {0x724, "TOVR", "Transmit oversize frame counter",             ACC_RW, 0x00000000}, | ||||
| {0x728, "TUND", "Transmit undersize frame counter ",           ACC_RW, 0x00000000}, | ||||
| {0x72C, "TFRG", "Transmit fragments frame counter ",           ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| /* eTSEC Counter Control and TOE Statistics Registers */ | ||||
| 
 | ||||
| {0x730, "CAR1", "Carry register one register",           ACC_W1C, 0x00000000}, | ||||
| {0x734, "CAR2", "Carry register two register ",          ACC_W1C, 0x00000000}, | ||||
| {0x738, "CAM1", "Carry register one mask register ",     ACC_RW,  0xFE03FFFF}, | ||||
| {0x73C, "CAM2", "Carry register two mask register ",     ACC_RW,  0x000FFFFD}, | ||||
| {0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW,  0x00000000}, | ||||
| 
 | ||||
| /* Hash Function Registers */ | ||||
| 
 | ||||
| {0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000}, | ||||
| {0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000}, | ||||
| {0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000}, | ||||
| {0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000}, | ||||
| {0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000}, | ||||
| {0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000}, | ||||
| {0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000}, | ||||
| {0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000}, | ||||
| {0x880, "GADDR0",  "Group address register 0",            ACC_RW, 0x00000000}, | ||||
| {0x884, "GADDR1",  "Group address register 1",            ACC_RW, 0x00000000}, | ||||
| {0x888, "GADDR2",  "Group address register 2",            ACC_RW, 0x00000000}, | ||||
| {0x88C, "GADDR3",  "Group address register 3",            ACC_RW, 0x00000000}, | ||||
| {0x890, "GADDR4",  "Group address register 4",            ACC_RW, 0x00000000}, | ||||
| {0x894, "GADDR5",  "Group address register 5",            ACC_RW, 0x00000000}, | ||||
| {0x898, "GADDR6",  "Group address register 6",            ACC_RW, 0x00000000}, | ||||
| {0x89C, "GADDR7",  "Group address register 7",            ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| /* eTSEC DMA Attribute Registers */ | ||||
| 
 | ||||
| {0xBF8, "ATTR",    "Attribute register",                                  ACC_RW, 0x00000000}, | ||||
| {0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| 
 | ||||
| /* eTSEC Lossless Flow Control Registers */ | ||||
| 
 | ||||
| {0xC00, "RQPRM0",  "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000}, | ||||
| {0xC04, "RQPRM1",  "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000}, | ||||
| {0xC08, "RQPRM2",  "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000}, | ||||
| {0xC0C, "RQPRM3",  "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000}, | ||||
| {0xC10, "RQPRM4",  "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000}, | ||||
| {0xC14, "RQPRM5",  "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000}, | ||||
| {0xC18, "RQPRM6",  "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000}, | ||||
| {0xC1C, "RQPRM7",  "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000}, | ||||
| {0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0",    ACC_RW, 0x00000000}, | ||||
| {0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1",    ACC_RW, 0x00000000}, | ||||
| {0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2",    ACC_RW, 0x00000000}, | ||||
| {0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3",    ACC_RW, 0x00000000}, | ||||
| {0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4",    ACC_RW, 0x00000000}, | ||||
| {0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5",    ACC_RW, 0x00000000}, | ||||
| {0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6",    ACC_RW, 0x00000000}, | ||||
| {0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7",    ACC_RW, 0x00000000}, | ||||
| 
 | ||||
| /* eTSEC Future Expansion Space */ | ||||
| 
 | ||||
| /* Reserved*/ | ||||
| 
 | ||||
| /* eTSEC IEEE 1588 Registers */ | ||||
| 
 | ||||
| {0xE00, "TMR_CTRL",     "Timer control register",                          ACC_RW,  0x00010001}, | ||||
| {0xE04, "TMR_TEVENT",   "time stamp event register",                       ACC_W1C, 0x00000000}, | ||||
| {0xE08, "TMR_TEMASK",   "Timer event mask register",                       ACC_RW,  0x00000000}, | ||||
| {0xE0C, "TMR_PEVENT",   "time stamp event register",                       ACC_RW,  0x00000000}, | ||||
| {0xE10, "TMR_PEMASK",   "Timer event mask register",                       ACC_RW,  0x00000000}, | ||||
| {0xE14, "TMR_STAT",     "time stamp status register",                      ACC_RW,  0x00000000}, | ||||
| {0xE18, "TMR_CNT_H",    "timer counter high register",                     ACC_RW,  0x00000000}, | ||||
| {0xE1C, "TMR_CNT_L",    "timer counter low register",                      ACC_RW,  0x00000000}, | ||||
| {0xE20, "TMR_ADD",      "Timer drift compensation addend register",        ACC_RW,  0x00000000}, | ||||
| {0xE24, "TMR_ACC",      "Timer accumulator register",                      ACC_RW,  0x00000000}, | ||||
| {0xE28, "TMR_PRSC",     "Timer prescale",                                  ACC_RW,  0x00000002}, | ||||
| {0xE30, "TMROFF_H",     "Timer offset high",                               ACC_RW,  0x00000000}, | ||||
| {0xE34, "TMROFF_L",     "Timer offset low",                                ACC_RW,  0x00000000}, | ||||
| {0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xE80, "TMR_FIPER1",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xE84, "TMR_FIPER2",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xE88, "TMR_FIPER3",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF}, | ||||
| {0xEA0, "TMR_ETTS1_H",  "Time stamp of general purpose external trigger ", ACC_RW,  0x00000000}, | ||||
| {0xEA4, "TMR_ETTS1_L",  "Time stamp of general purpose external trigger",  ACC_RW,  0x00000000}, | ||||
| {0xEA8, "TMR_ETTS2_H",  "Time stamp of general purpose external trigger ", ACC_RW,  0x00000000}, | ||||
| {0xEAC, "TMR_ETTS2_L",  "Time stamp of general purpose external trigger",  ACC_RW,  0x00000000}, | ||||
| 
 | ||||
| /* End Of Table */ | ||||
| {0x0, 0x0, 0x0, 0x0, 0x0} | ||||
| }; | ||||
							
								
								
									
										320
									
								
								hw/net/fsl_etsec/registers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								hw/net/fsl_etsec/registers.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,320 @@ | ||||
| /*
 | ||||
|  * QEMU Freescale eTSEC Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011-2013 AdaCore | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| #ifndef _ETSEC_REGISTERS_H_ | ||||
| #define _ETSEC_REGISTERS_H_ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| enum eTSEC_Register_Access_Type { | ||||
|     ACC_RW      = 1,            /* Read/Write */ | ||||
|     ACC_RO      = 2,            /* Read Only */ | ||||
|     ACC_WO      = 3,            /* Write Only */ | ||||
|     ACC_W1C     = 4,            /* Write 1 to clear */ | ||||
|     ACC_UNKNOWN = 5             /* Unknown register*/ | ||||
| }; | ||||
| 
 | ||||
| typedef struct eTSEC_Register_Definition { | ||||
|     uint32_t                         offset; | ||||
|     const char                      *name; | ||||
|     const char                      *desc; | ||||
|     enum eTSEC_Register_Access_Type  access; | ||||
|     uint32_t                         reset; | ||||
| } eTSEC_Register_Definition; | ||||
| 
 | ||||
| extern const eTSEC_Register_Definition eTSEC_registers_def[]; | ||||
| 
 | ||||
| #define DMACTRL_LE  (1 << 15) | ||||
| #define DMACTRL_GRS (1 <<  4) | ||||
| #define DMACTRL_GTS (1 <<  3) | ||||
| #define DMACTRL_WOP (1 <<  0) | ||||
| 
 | ||||
| #define IEVENT_PERR  (1 <<  0) | ||||
| #define IEVENT_DPE   (1 <<  1) | ||||
| #define IEVENT_FIQ   (1 <<  2) | ||||
| #define IEVENT_FIR   (1 <<  3) | ||||
| #define IEVENT_FGPI  (1 <<  4) | ||||
| #define IEVENT_RXF   (1 <<  7) | ||||
| #define IEVENT_GRSC  (1 <<  8) | ||||
| #define IEVENT_MMRW  (1 <<  9) | ||||
| #define IEVENT_MMRD  (1 << 10) | ||||
| #define IEVENT_MAG   (1 << 11) | ||||
| #define IEVENT_RXB   (1 << 15) | ||||
| #define IEVENT_XFUN  (1 << 16) | ||||
| #define IEVENT_CRL   (1 << 17) | ||||
| #define IEVENT_LC    (1 << 18) | ||||
| #define IEVENT_TXF   (1 << 20) | ||||
| #define IEVENT_TXB   (1 << 21) | ||||
| #define IEVENT_TXE   (1 << 22) | ||||
| #define IEVENT_TXC   (1 << 23) | ||||
| #define IEVENT_BABT  (1 << 24) | ||||
| #define IEVENT_GTSC  (1 << 25) | ||||
| #define IEVENT_MSRO  (1 << 26) | ||||
| #define IEVENT_EBERR (1 << 28) | ||||
| #define IEVENT_BSY   (1 << 29) | ||||
| #define IEVENT_RXC   (1 << 30) | ||||
| #define IEVENT_BABR  (1 << 31) | ||||
| 
 | ||||
| #define IMASK_RXFEN  (1 <<  7) | ||||
| #define IMASK_GRSCEN (1 <<  8) | ||||
| #define IMASK_RXBEN  (1 << 15) | ||||
| #define IMASK_TXFEN  (1 << 20) | ||||
| #define IMASK_TXBEN  (1 << 21) | ||||
| #define IMASK_GTSCEN (1 << 25) | ||||
| 
 | ||||
| #define MACCFG1_TX_EN  (1 << 0) | ||||
| #define MACCFG1_RX_EN  (1 << 2) | ||||
| 
 | ||||
| #define MACCFG2_CRC_EN  (1 << 1) | ||||
| #define MACCFG2_PADCRC  (1 << 2) | ||||
| 
 | ||||
| #define MIIMCOM_READ (1 << 0) | ||||
| #define MIIMCOM_SCAN (1 << 1) | ||||
| 
 | ||||
| #define RCTRL_PRSDEP_MASK   (0x3) | ||||
| #define RCTRL_PRSDEP_OFFSET (6) | ||||
| #define RCTRL_RSF           (1 << 2) | ||||
| 
 | ||||
| /* Index of each register */ | ||||
| 
 | ||||
| #define TSEC_ID      (0x000 / 4) | ||||
| #define TSEC_ID2     (0x004 / 4) | ||||
| #define IEVENT       (0x010 / 4) | ||||
| #define IMASK        (0x014 / 4) | ||||
| #define EDIS         (0x018 / 4) | ||||
| #define ECNTRL       (0x020 / 4) | ||||
| #define PTV          (0x028 / 4) | ||||
| #define DMACTRL      (0x02C / 4) | ||||
| #define TBIPA        (0x030 / 4) | ||||
| #define TCTRL        (0x100 / 4) | ||||
| #define TSTAT        (0x104 / 4) | ||||
| #define DFVLAN       (0x108 / 4) | ||||
| #define TXIC         (0x110 / 4) | ||||
| #define TQUEUE       (0x114 / 4) | ||||
| #define TR03WT       (0x140 / 4) | ||||
| #define TR47WT       (0x144 / 4) | ||||
| #define TBDBPH       (0x180 / 4) | ||||
| #define TBPTR0       (0x184 / 4) | ||||
| #define TBPTR1       (0x18C / 4) | ||||
| #define TBPTR2       (0x194 / 4) | ||||
| #define TBPTR3       (0x19C / 4) | ||||
| #define TBPTR4       (0x1A4 / 4) | ||||
| #define TBPTR5       (0x1AC / 4) | ||||
| #define TBPTR6       (0x1B4 / 4) | ||||
| #define TBPTR7       (0x1BC / 4) | ||||
| #define TBASEH       (0x200 / 4) | ||||
| #define TBASE0       (0x204 / 4) | ||||
| #define TBASE1       (0x20C / 4) | ||||
| #define TBASE2       (0x214 / 4) | ||||
| #define TBASE3       (0x21C / 4) | ||||
| #define TBASE4       (0x224 / 4) | ||||
| #define TBASE5       (0x22C / 4) | ||||
| #define TBASE6       (0x234 / 4) | ||||
| #define TBASE7       (0x23C / 4) | ||||
| #define TMR_TXTS1_ID (0x280 / 4) | ||||
| #define TMR_TXTS2_ID (0x284 / 4) | ||||
| #define TMR_TXTS1_H  (0x2C0 / 4) | ||||
| #define TMR_TXTS1_L  (0x2C4 / 4) | ||||
| #define TMR_TXTS2_H  (0x2C8 / 4) | ||||
| #define TMR_TXTS2_L  (0x2CC / 4) | ||||
| #define RCTRL        (0x300 / 4) | ||||
| #define RSTAT        (0x304 / 4) | ||||
| #define RXIC         (0x310 / 4) | ||||
| #define RQUEUE       (0x314 / 4) | ||||
| #define RBIFX        (0x330 / 4) | ||||
| #define RQFAR        (0x334 / 4) | ||||
| #define RQFCR        (0x338 / 4) | ||||
| #define RQFPR        (0x33C / 4) | ||||
| #define MRBLR        (0x340 / 4) | ||||
| #define RBDBPH       (0x380 / 4) | ||||
| #define RBPTR0       (0x384 / 4) | ||||
| #define RBPTR1       (0x38C / 4) | ||||
| #define RBPTR2       (0x394 / 4) | ||||
| #define RBPTR3       (0x39C / 4) | ||||
| #define RBPTR4       (0x3A4 / 4) | ||||
| #define RBPTR5       (0x3AC / 4) | ||||
| #define RBPTR6       (0x3B4 / 4) | ||||
| #define RBPTR7       (0x3BC / 4) | ||||
| #define RBASEH       (0x400 / 4) | ||||
| #define RBASE0       (0x404 / 4) | ||||
| #define RBASE1       (0x40C / 4) | ||||
| #define RBASE2       (0x414 / 4) | ||||
| #define RBASE3       (0x41C / 4) | ||||
| #define RBASE4       (0x424 / 4) | ||||
| #define RBASE5       (0x42C / 4) | ||||
| #define RBASE6       (0x434 / 4) | ||||
| #define RBASE7       (0x43C / 4) | ||||
| #define TMR_RXTS_H   (0x4C0 / 4) | ||||
| #define TMR_RXTS_L   (0x4C4 / 4) | ||||
| #define MACCFG1      (0x500 / 4) | ||||
| #define MACCFG2      (0x504 / 4) | ||||
| #define IPGIFG       (0x508 / 4) | ||||
| #define HAFDUP       (0x50C / 4) | ||||
| #define MAXFRM       (0x510 / 4) | ||||
| #define MIIMCFG      (0x520 / 4) | ||||
| #define MIIMCOM      (0x524 / 4) | ||||
| #define MIIMADD      (0x528 / 4) | ||||
| #define MIIMCON      (0x52C / 4) | ||||
| #define MIIMSTAT     (0x530 / 4) | ||||
| #define MIIMIND      (0x534 / 4) | ||||
| #define IFSTAT       (0x53C / 4) | ||||
| #define MACSTNADDR1  (0x540 / 4) | ||||
| #define MACSTNADDR2  (0x544 / 4) | ||||
| #define MAC01ADDR1   (0x548 / 4) | ||||
| #define MAC01ADDR2   (0x54C / 4) | ||||
| #define MAC02ADDR1   (0x550 / 4) | ||||
| #define MAC02ADDR2   (0x554 / 4) | ||||
| #define MAC03ADDR1   (0x558 / 4) | ||||
| #define MAC03ADDR2   (0x55C / 4) | ||||
| #define MAC04ADDR1   (0x560 / 4) | ||||
| #define MAC04ADDR2   (0x564 / 4) | ||||
| #define MAC05ADDR1   (0x568 / 4) | ||||
| #define MAC05ADDR2   (0x56C / 4) | ||||
| #define MAC06ADDR1   (0x570 / 4) | ||||
| #define MAC06ADDR2   (0x574 / 4) | ||||
| #define MAC07ADDR1   (0x578 / 4) | ||||
| #define MAC07ADDR2   (0x57C / 4) | ||||
| #define MAC08ADDR1   (0x580 / 4) | ||||
| #define MAC08ADDR2   (0x584 / 4) | ||||
| #define MAC09ADDR1   (0x588 / 4) | ||||
| #define MAC09ADDR2   (0x58C / 4) | ||||
| #define MAC10ADDR1   (0x590 / 4) | ||||
| #define MAC10ADDR2   (0x594 / 4) | ||||
| #define MAC11ADDR1   (0x598 / 4) | ||||
| #define MAC11ADDR2   (0x59C / 4) | ||||
| #define MAC12ADDR1   (0x5A0 / 4) | ||||
| #define MAC12ADDR2   (0x5A4 / 4) | ||||
| #define MAC13ADDR1   (0x5A8 / 4) | ||||
| #define MAC13ADDR2   (0x5AC / 4) | ||||
| #define MAC14ADDR1   (0x5B0 / 4) | ||||
| #define MAC14ADDR2   (0x5B4 / 4) | ||||
| #define MAC15ADDR1   (0x5B8 / 4) | ||||
| #define MAC15ADDR2   (0x5BC / 4) | ||||
| #define TR64         (0x680 / 4) | ||||
| #define TR127        (0x684 / 4) | ||||
| #define TR255        (0x688 / 4) | ||||
| #define TR511        (0x68C / 4) | ||||
| #define TR1K         (0x690 / 4) | ||||
| #define TRMAX        (0x694 / 4) | ||||
| #define TRMGV        (0x698 / 4) | ||||
| #define RBYT         (0x69C / 4) | ||||
| #define RPKT         (0x6A0 / 4) | ||||
| #define RFCS         (0x6A4 / 4) | ||||
| #define RMCA         (0x6A8 / 4) | ||||
| #define RBCA         (0x6AC / 4) | ||||
| #define RXCF         (0x6B0 / 4) | ||||
| #define RXPF         (0x6B4 / 4) | ||||
| #define RXUO         (0x6B8 / 4) | ||||
| #define RALN         (0x6BC / 4) | ||||
| #define RFLR         (0x6C0 / 4) | ||||
| #define RCDE         (0x6C4 / 4) | ||||
| #define RCSE         (0x6C8 / 4) | ||||
| #define RUND         (0x6CC / 4) | ||||
| #define ROVR         (0x6D0 / 4) | ||||
| #define RFRG         (0x6D4 / 4) | ||||
| #define RJBR         (0x6D8 / 4) | ||||
| #define RDRP         (0x6DC / 4) | ||||
| #define TBYT         (0x6E0 / 4) | ||||
| #define TPKT         (0x6E4 / 4) | ||||
| #define TMCA         (0x6E8 / 4) | ||||
| #define TBCA         (0x6EC / 4) | ||||
| #define TXPF         (0x6F0 / 4) | ||||
| #define TDFR         (0x6F4 / 4) | ||||
| #define TEDF         (0x6F8 / 4) | ||||
| #define TSCL         (0x6FC / 4) | ||||
| #define TMCL         (0x700 / 4) | ||||
| #define TLCL         (0x704 / 4) | ||||
| #define TXCL         (0x708 / 4) | ||||
| #define TNCL         (0x70C / 4) | ||||
| #define TDRP         (0x714 / 4) | ||||
| #define TJBR         (0x718 / 4) | ||||
| #define TFCS         (0x71C / 4) | ||||
| #define TXCF         (0x720 / 4) | ||||
| #define TOVR         (0x724 / 4) | ||||
| #define TUND         (0x728 / 4) | ||||
| #define TFRG         (0x72C / 4) | ||||
| #define CAR1         (0x730 / 4) | ||||
| #define CAR2         (0x734 / 4) | ||||
| #define CAM1         (0x738 / 4) | ||||
| #define CAM2         (0x73C / 4) | ||||
| #define RREJ         (0x740 / 4) | ||||
| #define IGADDR0      (0x800 / 4) | ||||
| #define IGADDR1      (0x804 / 4) | ||||
| #define IGADDR2      (0x808 / 4) | ||||
| #define IGADDR3      (0x80C / 4) | ||||
| #define IGADDR4      (0x810 / 4) | ||||
| #define IGADDR5      (0x814 / 4) | ||||
| #define IGADDR6      (0x818 / 4) | ||||
| #define IGADDR7      (0x81C / 4) | ||||
| #define GADDR0       (0x880 / 4) | ||||
| #define GADDR1       (0x884 / 4) | ||||
| #define GADDR2       (0x888 / 4) | ||||
| #define GADDR3       (0x88C / 4) | ||||
| #define GADDR4       (0x890 / 4) | ||||
| #define GADDR5       (0x894 / 4) | ||||
| #define GADDR6       (0x898 / 4) | ||||
| #define GADDR7       (0x89C / 4) | ||||
| #define ATTR         (0xBF8 / 4) | ||||
| #define ATTRELI      (0xBFC / 4) | ||||
| #define RQPRM0       (0xC00 / 4) | ||||
| #define RQPRM1       (0xC04 / 4) | ||||
| #define RQPRM2       (0xC08 / 4) | ||||
| #define RQPRM3       (0xC0C / 4) | ||||
| #define RQPRM4       (0xC10 / 4) | ||||
| #define RQPRM5       (0xC14 / 4) | ||||
| #define RQPRM6       (0xC18 / 4) | ||||
| #define RQPRM7       (0xC1C / 4) | ||||
| #define RFBPTR0      (0xC44 / 4) | ||||
| #define RFBPTR1      (0xC4C / 4) | ||||
| #define RFBPTR2      (0xC54 / 4) | ||||
| #define RFBPTR3      (0xC5C / 4) | ||||
| #define RFBPTR4      (0xC64 / 4) | ||||
| #define RFBPTR5      (0xC6C / 4) | ||||
| #define RFBPTR6      (0xC74 / 4) | ||||
| #define RFBPTR7      (0xC7C / 4) | ||||
| #define TMR_CTRL     (0xE00 / 4) | ||||
| #define TMR_TEVENT   (0xE04 / 4) | ||||
| #define TMR_TEMASK   (0xE08 / 4) | ||||
| #define TMR_PEVENT   (0xE0C / 4) | ||||
| #define TMR_PEMASK   (0xE10 / 4) | ||||
| #define TMR_STAT     (0xE14 / 4) | ||||
| #define TMR_CNT_H    (0xE18 / 4) | ||||
| #define TMR_CNT_L    (0xE1C / 4) | ||||
| #define TMR_ADD      (0xE20 / 4) | ||||
| #define TMR_ACC      (0xE24 / 4) | ||||
| #define TMR_PRSC     (0xE28 / 4) | ||||
| #define TMROFF_H     (0xE30 / 4) | ||||
| #define TMROFF_L     (0xE34 / 4) | ||||
| #define TMR_ALARM1_H (0xE40 / 4) | ||||
| #define TMR_ALARM1_L (0xE44 / 4) | ||||
| #define TMR_ALARM2_H (0xE48 / 4) | ||||
| #define TMR_ALARM2_L (0xE4C / 4) | ||||
| #define TMR_FIPER1   (0xE80 / 4) | ||||
| #define TMR_FIPER2   (0xE84 / 4) | ||||
| #define TMR_FIPER3   (0xE88 / 4) | ||||
| #define TMR_ETTS1_H  (0xEA0 / 4) | ||||
| #define TMR_ETTS1_L  (0xEA4 / 4) | ||||
| #define TMR_ETTS2_H  (0xEA8 / 4) | ||||
| #define TMR_ETTS2_L  (0xEAC / 4) | ||||
| 
 | ||||
| #endif /* ! _ETSEC_REGISTERS_H_ */ | ||||
							
								
								
									
										650
									
								
								hw/net/fsl_etsec/rings.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										650
									
								
								hw/net/fsl_etsec/rings.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,650 @@ | ||||
| /*
 | ||||
|  * QEMU Freescale eTSEC Emulator | ||||
|  * | ||||
|  * Copyright (c) 2011-2013 AdaCore | ||||
|  * | ||||
|  * 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 "net/checksum.h" | ||||
| 
 | ||||
| #include "etsec.h" | ||||
| #include "registers.h" | ||||
| 
 | ||||
| /* #define ETSEC_RING_DEBUG */ | ||||
| /* #define HEX_DUMP */ | ||||
| /* #define DEBUG_BD */ | ||||
| 
 | ||||
| #ifdef ETSEC_RING_DEBUG | ||||
| static const int debug_etsec = 1; | ||||
| #else | ||||
| static const int debug_etsec; | ||||
| #endif | ||||
| 
 | ||||
| #define RING_DEBUG(fmt, ...) do {              \ | ||||
|  if (debug_etsec) {                            \ | ||||
|         qemu_log(fmt , ## __VA_ARGS__);        \ | ||||
|     }                                          \ | ||||
|     } while (0) | ||||
| 
 | ||||
| #ifdef DEBUG_BD | ||||
| 
 | ||||
| static void print_tx_bd_flags(uint16_t flags) | ||||
| { | ||||
|     qemu_log("      Ready: %d\n", !!(flags & BD_TX_READY)); | ||||
|     qemu_log("      PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC)); | ||||
|     qemu_log("      Wrap: %d\n", !!(flags & BD_WRAP)); | ||||
|     qemu_log("      Interrupt: %d\n", !!(flags & BD_INTERRUPT)); | ||||
|     qemu_log("      Last in frame: %d\n", !!(flags & BD_LAST)); | ||||
|     qemu_log("      Tx CRC: %d\n", !!(flags & BD_TX_TC)); | ||||
|     qemu_log("      User-defined preamble / defer: %d\n", | ||||
|            !!(flags & BD_TX_PREDEF)); | ||||
|     qemu_log("      Huge frame enable / Late collision: %d\n", | ||||
|            !!(flags & BD_TX_HFELC)); | ||||
|     qemu_log("      Control frame / Retransmission Limit: %d\n", | ||||
|            !!(flags & BD_TX_CFRL)); | ||||
|     qemu_log("      Retry count: %d\n", | ||||
|            (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK); | ||||
|     qemu_log("      Underrun / TCP/IP off-load enable: %d\n", | ||||
|            !!(flags & BD_TX_TOEUN)); | ||||
|     qemu_log("      Truncation: %d\n", !!(flags & BD_TX_TR)); | ||||
| } | ||||
| 
 | ||||
| static void print_rx_bd_flags(uint16_t flags) | ||||
| { | ||||
|     qemu_log("      Empty: %d\n", !!(flags & BD_RX_EMPTY)); | ||||
|     qemu_log("      Receive software ownership: %d\n", !!(flags & BD_RX_RO1)); | ||||
|     qemu_log("      Wrap: %d\n", !!(flags & BD_WRAP)); | ||||
|     qemu_log("      Interrupt: %d\n", !!(flags & BD_INTERRUPT)); | ||||
|     qemu_log("      Last in frame: %d\n", !!(flags & BD_LAST)); | ||||
|     qemu_log("      First in frame: %d\n", !!(flags & BD_RX_FIRST)); | ||||
|     qemu_log("      Miss: %d\n", !!(flags & BD_RX_MISS)); | ||||
|     qemu_log("      Broadcast: %d\n", !!(flags & BD_RX_BROADCAST)); | ||||
|     qemu_log("      Multicast: %d\n", !!(flags & BD_RX_MULTICAST)); | ||||
|     qemu_log("      Rx frame length violation: %d\n", !!(flags & BD_RX_LG)); | ||||
|     qemu_log("      Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO)); | ||||
|     qemu_log("      Short frame: %d\n", !!(flags & BD_RX_SH)); | ||||
|     qemu_log("      Rx CRC Error: %d\n", !!(flags & BD_RX_CR)); | ||||
|     qemu_log("      Overrun: %d\n", !!(flags & BD_RX_OV)); | ||||
|     qemu_log("      Truncation: %d\n", !!(flags & BD_RX_TR)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index) | ||||
| { | ||||
|     qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n", | ||||
|            mode == eTSEC_TRANSMIT ? "Transmit" : "Receive", | ||||
|            index); | ||||
|     qemu_log("   Flags   : 0x%04x\n", bd.flags); | ||||
|     if (mode == eTSEC_TRANSMIT) { | ||||
|         print_tx_bd_flags(bd.flags); | ||||
|     } else { | ||||
|         print_rx_bd_flags(bd.flags); | ||||
|     } | ||||
|     qemu_log("   Length  : 0x%04x\n", bd.length); | ||||
|     qemu_log("   Pointer : 0x%08x\n", bd.bufptr); | ||||
| } | ||||
| 
 | ||||
| #endif  /* DEBUG_BD */ | ||||
| 
 | ||||
| static void read_buffer_descriptor(eTSEC         *etsec, | ||||
|                                    hwaddr         addr, | ||||
|                                    eTSEC_rxtx_bd *bd) | ||||
| { | ||||
|     assert(bd != NULL); | ||||
| 
 | ||||
|     RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); | ||||
|     cpu_physical_memory_read(addr, | ||||
|                              bd, | ||||
|                              sizeof(eTSEC_rxtx_bd)); | ||||
| 
 | ||||
|     if (etsec->regs[DMACTRL].value & DMACTRL_LE) { | ||||
|         bd->flags  = lduw_le_p(&bd->flags); | ||||
|         bd->length = lduw_le_p(&bd->length); | ||||
|         bd->bufptr = ldl_le_p(&bd->bufptr); | ||||
|     } else { | ||||
|         bd->flags  = lduw_be_p(&bd->flags); | ||||
|         bd->length = lduw_be_p(&bd->length); | ||||
|         bd->bufptr = ldl_be_p(&bd->bufptr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void write_buffer_descriptor(eTSEC         *etsec, | ||||
|                                     hwaddr         addr, | ||||
|                                     eTSEC_rxtx_bd *bd) | ||||
| { | ||||
|     assert(bd != NULL); | ||||
| 
 | ||||
|     if (etsec->regs[DMACTRL].value & DMACTRL_LE) { | ||||
|         stw_le_p(&bd->flags, bd->flags); | ||||
|         stw_le_p(&bd->length, bd->length); | ||||
|         stl_le_p(&bd->bufptr, bd->bufptr); | ||||
|     } else { | ||||
|         stw_be_p(&bd->flags, bd->flags); | ||||
|         stw_be_p(&bd->length, bd->length); | ||||
|         stl_be_p(&bd->bufptr, bd->bufptr); | ||||
|     } | ||||
| 
 | ||||
|     RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); | ||||
|     cpu_physical_memory_write(addr, | ||||
|                               bd, | ||||
|                               sizeof(eTSEC_rxtx_bd)); | ||||
| } | ||||
| 
 | ||||
| static void ievent_set(eTSEC    *etsec, | ||||
|                        uint32_t  flags) | ||||
| { | ||||
|     etsec->regs[IEVENT].value |= flags; | ||||
| 
 | ||||
|     if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) | ||||
|         || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { | ||||
|         qemu_irq_raise(etsec->tx_irq); | ||||
|         RING_DEBUG("%s Raise Tx IRQ\n", __func__); | ||||
|     } | ||||
| 
 | ||||
|     if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) | ||||
|         || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { | ||||
|         qemu_irq_pulse(etsec->rx_irq); | ||||
|         RING_DEBUG("%s Raise Rx IRQ\n", __func__); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) | ||||
| { | ||||
|     int add = min_frame_len - etsec->tx_buffer_len; | ||||
| 
 | ||||
|     /* Padding */ | ||||
|     if (add > 0) { | ||||
|         RING_DEBUG("pad:%u\n", add); | ||||
|         etsec->tx_buffer = g_realloc(etsec->tx_buffer, | ||||
|                                         etsec->tx_buffer_len + add); | ||||
| 
 | ||||
|         memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add); | ||||
|         etsec->tx_buffer_len += add; | ||||
|     } | ||||
| 
 | ||||
|     /* Never add CRC in QEMU */ | ||||
| } | ||||
| 
 | ||||
| static void process_tx_fcb(eTSEC *etsec) | ||||
| { | ||||
|     uint8_t flags = (uint8_t)(*etsec->tx_buffer); | ||||
|     /* L3 header offset from start of frame */ | ||||
|     uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); | ||||
|     /* L4 header offset from start of L3 header */ | ||||
|     uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); | ||||
|     /* L3 header */ | ||||
|     uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; | ||||
|     /* L4 header */ | ||||
|     uint8_t *l4_header = l3_header + l4_header_offset; | ||||
| 
 | ||||
|     /* if packet is IP4 and IP checksum is requested */ | ||||
|     if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { | ||||
|         /* do IP4 checksum (TODO This funtion does TCP/UDP checksum but not sure
 | ||||
|          * if it also does IP4 checksum. */ | ||||
|         net_checksum_calculate(etsec->tx_buffer + 8, | ||||
|                 etsec->tx_buffer_len - 8); | ||||
|     } | ||||
|     /* TODO Check the correct usage of the PHCS field of the FCB in case the NPH
 | ||||
|      * flag is on */ | ||||
| 
 | ||||
|     /* if packet is IP4 and TCP or UDP */ | ||||
|     if (flags & FCB_TX_IP && flags & FCB_TX_TUP) { | ||||
|         /* if UDP */ | ||||
|         if (flags & FCB_TX_UDP) { | ||||
|             /* if checksum is requested */ | ||||
|             if (flags & FCB_TX_CTU) { | ||||
|                 /* do UDP checksum */ | ||||
| 
 | ||||
|                 net_checksum_calculate(etsec->tx_buffer + 8, | ||||
|                         etsec->tx_buffer_len - 8); | ||||
|             } else { | ||||
|                 /* set checksum field to 0 */ | ||||
|                 l4_header[6] = 0; | ||||
|                 l4_header[7] = 0; | ||||
|             } | ||||
|         } else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */ | ||||
|             /* do TCP checksum */ | ||||
|             net_checksum_calculate(etsec->tx_buffer + 8, | ||||
|                                    etsec->tx_buffer_len - 8); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void process_tx_bd(eTSEC         *etsec, | ||||
|                           eTSEC_rxtx_bd *bd) | ||||
| { | ||||
|     uint8_t *tmp_buff = NULL; | ||||
|     hwaddr tbdbth     = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32; | ||||
| 
 | ||||
|     if (bd->length == 0) { | ||||
|         /* ERROR */ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (etsec->tx_buffer_len == 0) { | ||||
|         /* It's the first BD */ | ||||
|         etsec->first_bd = *bd; | ||||
|     } | ||||
| 
 | ||||
|     /* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/ | ||||
| 
 | ||||
|     /* Load this Data Buffer */ | ||||
|     etsec->tx_buffer = g_realloc(etsec->tx_buffer, | ||||
|                                     etsec->tx_buffer_len + bd->length); | ||||
|     tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len; | ||||
|     cpu_physical_memory_read(bd->bufptr + tbdbth, tmp_buff, bd->length); | ||||
| 
 | ||||
|     /* Update buffer length */ | ||||
|     etsec->tx_buffer_len += bd->length; | ||||
| 
 | ||||
| 
 | ||||
|     if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) { | ||||
|         if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) { | ||||
|             /* MAC Transmit enabled */ | ||||
| 
 | ||||
|             /* Process offload Tx FCB */ | ||||
|             if (etsec->first_bd.flags & BD_TX_TOEUN) { | ||||
|                 process_tx_fcb(etsec); | ||||
|             } | ||||
| 
 | ||||
|             if (etsec->first_bd.flags & BD_TX_PADCRC | ||||
|                 || etsec->regs[MACCFG2].value & MACCFG2_PADCRC) { | ||||
| 
 | ||||
|                 /* Padding and CRC (Padding implies CRC) */ | ||||
|                 tx_padding_and_crc(etsec, 64); | ||||
| 
 | ||||
|             } else if (etsec->first_bd.flags & BD_TX_TC | ||||
|                        || etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) { | ||||
| 
 | ||||
|                 /* Only CRC */ | ||||
|                 /* Never add CRC in QEMU */ | ||||
|             } | ||||
| 
 | ||||
| #if defined(HEX_DUMP) | ||||
|             qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len); | ||||
|             qemu_hexdump(etsec->tx_buffer, stderr, "", etsec->tx_buffer_len); | ||||
| #endif  /* ETSEC_RING_DEBUG */ | ||||
| 
 | ||||
|             if (etsec->first_bd.flags & BD_TX_TOEUN) { | ||||
|                 qemu_send_packet(qemu_get_queue(etsec->nic), | ||||
|                         etsec->tx_buffer + 8, | ||||
|                         etsec->tx_buffer_len - 8); | ||||
|             } else { | ||||
|                 qemu_send_packet(qemu_get_queue(etsec->nic), | ||||
|                         etsec->tx_buffer, | ||||
|                         etsec->tx_buffer_len); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         etsec->tx_buffer_len = 0; | ||||
| 
 | ||||
|         if (bd->flags & BD_INTERRUPT) { | ||||
|             ievent_set(etsec, IEVENT_TXF); | ||||
|         } | ||||
|     } else { | ||||
|         if (bd->flags & BD_INTERRUPT) { | ||||
|             ievent_set(etsec, IEVENT_TXB); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Update DB flags */ | ||||
| 
 | ||||
|     /* Clear Ready */ | ||||
|     bd->flags &= ~BD_TX_READY; | ||||
| 
 | ||||
|     /* Clear Defer */ | ||||
|     bd->flags &= ~BD_TX_PREDEF; | ||||
| 
 | ||||
|     /* Clear Late Collision */ | ||||
|     bd->flags &= ~BD_TX_HFELC; | ||||
| 
 | ||||
|     /* Clear Retransmission Limit */ | ||||
|     bd->flags &= ~BD_TX_CFRL; | ||||
| 
 | ||||
|     /* Clear Retry Count */ | ||||
|     bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET); | ||||
| 
 | ||||
|     /* Clear Underrun */ | ||||
|     bd->flags &= ~BD_TX_TOEUN; | ||||
| 
 | ||||
|     /* Clear Truncation */ | ||||
|     bd->flags &= ~BD_TX_TR; | ||||
| } | ||||
| 
 | ||||
| void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) | ||||
| { | ||||
|     hwaddr        ring_base = 0; | ||||
|     hwaddr        bd_addr   = 0; | ||||
|     eTSEC_rxtx_bd bd; | ||||
|     uint16_t      bd_flags; | ||||
| 
 | ||||
|     if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) { | ||||
|         RING_DEBUG("%s: MAC Transmit not enabled\n", __func__); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     ring_base = (hwaddr)(etsec->regs[TBASEH].value & 0xF) << 32; | ||||
|     ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7; | ||||
|     bd_addr    = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7; | ||||
| 
 | ||||
|     do { | ||||
|         read_buffer_descriptor(etsec, bd_addr, &bd); | ||||
| 
 | ||||
| #ifdef DEBUG_BD | ||||
|         print_bd(bd, | ||||
|                  eTSEC_TRANSMIT, | ||||
|                  (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); | ||||
| 
 | ||||
| #endif  /* DEBUG_BD */ | ||||
| 
 | ||||
|         /* Save flags before BD update */ | ||||
|         bd_flags = bd.flags; | ||||
| 
 | ||||
|         if (bd_flags & BD_TX_READY) { | ||||
|             process_tx_bd(etsec, &bd); | ||||
| 
 | ||||
|             /* Write back BD after update */ | ||||
|             write_buffer_descriptor(etsec, bd_addr, &bd); | ||||
|         } | ||||
| 
 | ||||
|         /* Wrap or next BD */ | ||||
|         if (bd_flags & BD_WRAP) { | ||||
|             bd_addr = ring_base; | ||||
|         } else { | ||||
|             bd_addr += sizeof(eTSEC_rxtx_bd); | ||||
|         } | ||||
| 
 | ||||
|     } while (bd_addr != ring_base); | ||||
| 
 | ||||
|     bd_addr = ring_base; | ||||
| 
 | ||||
|     /* Save the Buffer Descriptor Pointers to current bd */ | ||||
|     etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; | ||||
| 
 | ||||
|     /* Set transmit halt THLTx */ | ||||
|     etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr); | ||||
| } | ||||
| 
 | ||||
| static void fill_rx_bd(eTSEC          *etsec, | ||||
|                        eTSEC_rxtx_bd  *bd, | ||||
|                        const uint8_t **buf, | ||||
|                        size_t         *size) | ||||
| { | ||||
|     uint16_t to_write; | ||||
|     hwaddr   bufptr = bd->bufptr + | ||||
|         ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32); | ||||
|     uint8_t  padd[etsec->rx_padding]; | ||||
|     uint8_t  rem; | ||||
| 
 | ||||
|     RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx | ||||
|                " size:%zu(padding + crc:%u) + fcb:%u\n", | ||||
|                bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size); | ||||
| 
 | ||||
|     bd->length = 0; | ||||
| 
 | ||||
|     /* This operation will only write FCB */ | ||||
|     if (etsec->rx_fcb_size != 0) { | ||||
| 
 | ||||
|         cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size); | ||||
| 
 | ||||
|         bufptr             += etsec->rx_fcb_size; | ||||
|         bd->length         += etsec->rx_fcb_size; | ||||
|         etsec->rx_fcb_size  = 0; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /* We remove padding from the computation of to_write because it is not
 | ||||
|      * allocated in the buffer. | ||||
|      */ | ||||
|     to_write = MIN(*size - etsec->rx_padding, | ||||
|                    etsec->regs[MRBLR].value - etsec->rx_fcb_size); | ||||
| 
 | ||||
|     /* This operation can only write packet data and no padding */ | ||||
|     if (to_write > 0) { | ||||
|         cpu_physical_memory_write(bufptr, *buf, to_write); | ||||
| 
 | ||||
|         *buf   += to_write; | ||||
|         bufptr += to_write; | ||||
|         *size  -= to_write; | ||||
| 
 | ||||
|         bd->flags  &= ~BD_RX_EMPTY; | ||||
|         bd->length += to_write; | ||||
|     } | ||||
| 
 | ||||
|     if (*size == etsec->rx_padding) { | ||||
|         /* The remaining bytes are only for padding which is not actually
 | ||||
|          * allocated in the data buffer. | ||||
|          */ | ||||
| 
 | ||||
|         rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding); | ||||
| 
 | ||||
|         if (rem > 0) { | ||||
|             memset(padd, 0x0, sizeof(padd)); | ||||
|             etsec->rx_padding -= rem; | ||||
|             *size             -= rem; | ||||
|             bd->length        += rem; | ||||
|             cpu_physical_memory_write(bufptr, padd, rem); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) | ||||
| { | ||||
|     uint32_t fcb_size = 0; | ||||
|     uint8_t  prsdep   = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET) | ||||
|         & RCTRL_PRSDEP_MASK; | ||||
| 
 | ||||
|     if (prsdep != 0) { | ||||
|         /* Prepend FCB (FCB size + RCTRL[PAL]) */ | ||||
|         fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F); | ||||
| 
 | ||||
|         etsec->rx_fcb_size = fcb_size; | ||||
| 
 | ||||
|         /* TODO: fill_FCB(etsec); */ | ||||
|         memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb)); | ||||
| 
 | ||||
|     } else { | ||||
|         etsec->rx_fcb_size = 0; | ||||
|     } | ||||
| 
 | ||||
|     if (etsec->rx_buffer != NULL) { | ||||
|         g_free(etsec->rx_buffer); | ||||
|     } | ||||
| 
 | ||||
|     /* Do not copy the frame for now */ | ||||
|     etsec->rx_buffer     = (uint8_t *)buf; | ||||
|     etsec->rx_buffer_len = size; | ||||
| 
 | ||||
|     /* CRC padding (We don't have to compute the CRC) */ | ||||
|     etsec->rx_padding = 4; | ||||
| 
 | ||||
|     etsec->rx_first_in_frame = 1; | ||||
|     etsec->rx_remaining_data = etsec->rx_buffer_len; | ||||
|     RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__, | ||||
|                etsec->rx_buffer_len, etsec->rx_padding); | ||||
| } | ||||
| 
 | ||||
| void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size) | ||||
| { | ||||
|     int ring_nbr = 0;           /* Always use ring0 (no filer) */ | ||||
| 
 | ||||
|     if (etsec->rx_buffer_len != 0) { | ||||
|         RING_DEBUG("%s: We can't receive now," | ||||
|                    " a buffer is already in the pipe\n", __func__); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) { | ||||
|         RING_DEBUG("%s: The ring is halted\n", __func__); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (etsec->regs[DMACTRL].value & DMACTRL_GRS) { | ||||
|         RING_DEBUG("%s: Graceful receive stop\n", __func__); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) { | ||||
|         RING_DEBUG("%s: MAC Receive not enabled\n", __func__); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) { | ||||
|         /* CRC is not in the packet yet, so short frame is below 60 bytes */ | ||||
|         RING_DEBUG("%s: Drop short frame\n", __func__); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     rx_init_frame(etsec, buf, size); | ||||
| 
 | ||||
|     etsec_walk_rx_ring(etsec, ring_nbr); | ||||
| } | ||||
| 
 | ||||
| void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr) | ||||
| { | ||||
|     hwaddr         ring_base     = 0; | ||||
|     hwaddr         bd_addr       = 0; | ||||
|     hwaddr         start_bd_addr = 0; | ||||
|     eTSEC_rxtx_bd  bd; | ||||
|     uint16_t       bd_flags; | ||||
|     size_t         remaining_data; | ||||
|     const uint8_t *buf; | ||||
|     uint8_t       *tmp_buf; | ||||
|     size_t         size; | ||||
| 
 | ||||
|     if (etsec->rx_buffer_len == 0) { | ||||
|         /* No frame to send */ | ||||
|         RING_DEBUG("No frame to send\n"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     remaining_data = etsec->rx_remaining_data + etsec->rx_padding; | ||||
|     buf            = etsec->rx_buffer | ||||
|         + (etsec->rx_buffer_len - etsec->rx_remaining_data); | ||||
|     size           = etsec->rx_buffer_len + etsec->rx_padding; | ||||
| 
 | ||||
|     ring_base = (hwaddr)(etsec->regs[RBASEH].value & 0xF) << 32; | ||||
|     ring_base += etsec->regs[RBASE0 + ring_nbr].value & ~0x7; | ||||
|     start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7; | ||||
| 
 | ||||
|     do { | ||||
|         read_buffer_descriptor(etsec, bd_addr, &bd); | ||||
| 
 | ||||
| #ifdef DEBUG_BD | ||||
|         print_bd(bd, | ||||
|                  eTSEC_RECEIVE, | ||||
|                  (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); | ||||
| 
 | ||||
| #endif  /* DEBUG_BD */ | ||||
| 
 | ||||
|         /* Save flags before BD update */ | ||||
|         bd_flags = bd.flags; | ||||
| 
 | ||||
|         if (bd_flags & BD_RX_EMPTY) { | ||||
|             fill_rx_bd(etsec, &bd, &buf, &remaining_data); | ||||
| 
 | ||||
|             if (etsec->rx_first_in_frame) { | ||||
|                 bd.flags |= BD_RX_FIRST; | ||||
|                 etsec->rx_first_in_frame = 0; | ||||
|                 etsec->rx_first_bd = bd; | ||||
|             } | ||||
| 
 | ||||
|             /* Last in frame */ | ||||
|             if (remaining_data == 0) { | ||||
| 
 | ||||
|                 /* Clear flags */ | ||||
| 
 | ||||
|                 bd.flags &= ~0x7ff; | ||||
| 
 | ||||
|                 bd.flags |= BD_LAST; | ||||
| 
 | ||||
|                 /* NOTE: non-octet aligned frame is impossible in qemu */ | ||||
| 
 | ||||
|                 if (size >= etsec->regs[MAXFRM].value) { | ||||
|                     /* frame length violation */ | ||||
|                     qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n", | ||||
|                            __func__, size, etsec->regs[MAXFRM].value); | ||||
| 
 | ||||
|                     bd.flags |= BD_RX_LG; | ||||
|                 } | ||||
| 
 | ||||
|                 if (size  < 64) { | ||||
|                     /* Short frame */ | ||||
|                     bd.flags |= BD_RX_SH; | ||||
|                 } | ||||
| 
 | ||||
|                 /* TODO: Broadcast and Multicast */ | ||||
| 
 | ||||
|                 if (bd.flags | BD_INTERRUPT) { | ||||
|                     /* Set RXFx */ | ||||
|                     etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr); | ||||
| 
 | ||||
|                     /* Set IEVENT */ | ||||
|                     ievent_set(etsec, IEVENT_RXF); | ||||
|                 } | ||||
| 
 | ||||
|             } else { | ||||
|                 if (bd.flags | BD_INTERRUPT) { | ||||
|                     /* Set IEVENT */ | ||||
|                     ievent_set(etsec, IEVENT_RXB); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             /* Write back BD after update */ | ||||
|             write_buffer_descriptor(etsec, bd_addr, &bd); | ||||
|         } | ||||
| 
 | ||||
|         /* Wrap or next BD */ | ||||
|         if (bd_flags & BD_WRAP) { | ||||
|             bd_addr = ring_base; | ||||
|         } else { | ||||
|             bd_addr += sizeof(eTSEC_rxtx_bd); | ||||
|         } | ||||
|     } while (remaining_data != 0 | ||||
|              && (bd_flags & BD_RX_EMPTY) | ||||
|              && bd_addr != start_bd_addr); | ||||
| 
 | ||||
|     /* Reset ring ptr */ | ||||
|     etsec->regs[RBPTR0 + ring_nbr].value = bd_addr; | ||||
| 
 | ||||
|     /* The frame is too large to fit in the Rx ring */ | ||||
|     if (remaining_data > 0) { | ||||
| 
 | ||||
|         /* Set RSTAT[QHLTx] */ | ||||
|         etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr); | ||||
| 
 | ||||
|         /* Save remaining data to send the end of the frame when the ring will
 | ||||
|          * be restarted | ||||
|          */ | ||||
|         etsec->rx_remaining_data = remaining_data; | ||||
| 
 | ||||
|         /* Copy the frame */ | ||||
|         tmp_buf = g_malloc(size); | ||||
|         memcpy(tmp_buf, etsec->rx_buffer, size); | ||||
|         etsec->rx_buffer = tmp_buf; | ||||
| 
 | ||||
|         RING_DEBUG("no empty RxBD available any more\n"); | ||||
|     } else { | ||||
|         etsec->rx_buffer_len = 0; | ||||
|         etsec->rx_buffer     = NULL; | ||||
|     } | ||||
| 
 | ||||
|     RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data); | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Fabien Chouteau
						Fabien Chouteau