1fcbd8018SJean-Christophe Dubois /* 2fcbd8018SJean-Christophe Dubois * i.MX Fast Ethernet Controller emulation. 3fcbd8018SJean-Christophe Dubois * 4fcbd8018SJean-Christophe Dubois * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> 5fcbd8018SJean-Christophe Dubois * 6fcbd8018SJean-Christophe Dubois * Based on Coldfire Fast Ethernet Controller emulation. 7fcbd8018SJean-Christophe Dubois * 8fcbd8018SJean-Christophe Dubois * Copyright (c) 2007 CodeSourcery. 9fcbd8018SJean-Christophe Dubois * 10fcbd8018SJean-Christophe Dubois * This program is free software; you can redistribute it and/or modify it 11fcbd8018SJean-Christophe Dubois * under the terms of the GNU General Public License as published by the 12fcbd8018SJean-Christophe Dubois * Free Software Foundation; either version 2 of the License, or 13fcbd8018SJean-Christophe Dubois * (at your option) any later version. 14fcbd8018SJean-Christophe Dubois * 15fcbd8018SJean-Christophe Dubois * This program is distributed in the hope that it will be useful, but WITHOUT 16fcbd8018SJean-Christophe Dubois * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17fcbd8018SJean-Christophe Dubois * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18fcbd8018SJean-Christophe Dubois * for more details. 19fcbd8018SJean-Christophe Dubois * 20fcbd8018SJean-Christophe Dubois * You should have received a copy of the GNU General Public License along 21fcbd8018SJean-Christophe Dubois * with this program; if not, see <http://www.gnu.org/licenses/>. 22fcbd8018SJean-Christophe Dubois */ 23fcbd8018SJean-Christophe Dubois 248ef94f0bSPeter Maydell #include "qemu/osdep.h" 25fcbd8018SJean-Christophe Dubois #include "hw/net/imx_fec.h" 26fcbd8018SJean-Christophe Dubois #include "sysemu/dma.h" 2703dd024fSPaolo Bonzini #include "qemu/log.h" 28*a699b410SJean-Christophe Dubois #include "net/checksum.h" 29*a699b410SJean-Christophe Dubois #include "net/eth.h" 30fcbd8018SJean-Christophe Dubois 31fcbd8018SJean-Christophe Dubois /* For crc32 */ 32fcbd8018SJean-Christophe Dubois #include <zlib.h> 33fcbd8018SJean-Christophe Dubois 34b72d8d25SJean-Christophe Dubois #ifndef DEBUG_IMX_FEC 35b72d8d25SJean-Christophe Dubois #define DEBUG_IMX_FEC 0 36fcbd8018SJean-Christophe Dubois #endif 37fcbd8018SJean-Christophe Dubois 38b72d8d25SJean-Christophe Dubois #define FEC_PRINTF(fmt, args...) \ 39b72d8d25SJean-Christophe Dubois do { \ 40b72d8d25SJean-Christophe Dubois if (DEBUG_IMX_FEC) { \ 41b72d8d25SJean-Christophe Dubois fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \ 42b72d8d25SJean-Christophe Dubois __func__, ##args); \ 43b72d8d25SJean-Christophe Dubois } \ 44fcbd8018SJean-Christophe Dubois } while (0) 45b72d8d25SJean-Christophe Dubois 46b72d8d25SJean-Christophe Dubois #ifndef DEBUG_IMX_PHY 47b72d8d25SJean-Christophe Dubois #define DEBUG_IMX_PHY 0 48fcbd8018SJean-Christophe Dubois #endif 49fcbd8018SJean-Christophe Dubois 50b72d8d25SJean-Christophe Dubois #define PHY_PRINTF(fmt, args...) \ 51b72d8d25SJean-Christophe Dubois do { \ 52b72d8d25SJean-Christophe Dubois if (DEBUG_IMX_PHY) { \ 53b72d8d25SJean-Christophe Dubois fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \ 54b72d8d25SJean-Christophe Dubois __func__, ##args); \ 55b72d8d25SJean-Christophe Dubois } \ 56fcbd8018SJean-Christophe Dubois } while (0) 57fcbd8018SJean-Christophe Dubois 58*a699b410SJean-Christophe Dubois static const char *imx_default_reg_name(IMXFECState *s, uint32_t index) 59db0de352SJean-Christophe Dubois { 60db0de352SJean-Christophe Dubois static char tmp[20]; 61*a699b410SJean-Christophe Dubois sprintf(tmp, "index %d", index); 62*a699b410SJean-Christophe Dubois return tmp; 63*a699b410SJean-Christophe Dubois } 64db0de352SJean-Christophe Dubois 65*a699b410SJean-Christophe Dubois static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index) 66*a699b410SJean-Christophe Dubois { 67*a699b410SJean-Christophe Dubois switch (index) { 68*a699b410SJean-Christophe Dubois case ENET_FRBR: 69*a699b410SJean-Christophe Dubois return "FRBR"; 70*a699b410SJean-Christophe Dubois case ENET_FRSR: 71*a699b410SJean-Christophe Dubois return "FRSR"; 72*a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR: 73*a699b410SJean-Christophe Dubois return "MIIGSK_CFGR"; 74*a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR: 75*a699b410SJean-Christophe Dubois return "MIIGSK_ENR"; 76*a699b410SJean-Christophe Dubois default: 77*a699b410SJean-Christophe Dubois return imx_default_reg_name(s, index); 78*a699b410SJean-Christophe Dubois } 79*a699b410SJean-Christophe Dubois } 80*a699b410SJean-Christophe Dubois 81*a699b410SJean-Christophe Dubois static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index) 82*a699b410SJean-Christophe Dubois { 83*a699b410SJean-Christophe Dubois switch (index) { 84*a699b410SJean-Christophe Dubois case ENET_RSFL: 85*a699b410SJean-Christophe Dubois return "RSFL"; 86*a699b410SJean-Christophe Dubois case ENET_RSEM: 87*a699b410SJean-Christophe Dubois return "RSEM"; 88*a699b410SJean-Christophe Dubois case ENET_RAEM: 89*a699b410SJean-Christophe Dubois return "RAEM"; 90*a699b410SJean-Christophe Dubois case ENET_RAFL: 91*a699b410SJean-Christophe Dubois return "RAFL"; 92*a699b410SJean-Christophe Dubois case ENET_TSEM: 93*a699b410SJean-Christophe Dubois return "TSEM"; 94*a699b410SJean-Christophe Dubois case ENET_TAEM: 95*a699b410SJean-Christophe Dubois return "TAEM"; 96*a699b410SJean-Christophe Dubois case ENET_TAFL: 97*a699b410SJean-Christophe Dubois return "TAFL"; 98*a699b410SJean-Christophe Dubois case ENET_TIPG: 99*a699b410SJean-Christophe Dubois return "TIPG"; 100*a699b410SJean-Christophe Dubois case ENET_FTRL: 101*a699b410SJean-Christophe Dubois return "FTRL"; 102*a699b410SJean-Christophe Dubois case ENET_TACC: 103*a699b410SJean-Christophe Dubois return "TACC"; 104*a699b410SJean-Christophe Dubois case ENET_RACC: 105*a699b410SJean-Christophe Dubois return "RACC"; 106*a699b410SJean-Christophe Dubois case ENET_ATCR: 107*a699b410SJean-Christophe Dubois return "ATCR"; 108*a699b410SJean-Christophe Dubois case ENET_ATVR: 109*a699b410SJean-Christophe Dubois return "ATVR"; 110*a699b410SJean-Christophe Dubois case ENET_ATOFF: 111*a699b410SJean-Christophe Dubois return "ATOFF"; 112*a699b410SJean-Christophe Dubois case ENET_ATPER: 113*a699b410SJean-Christophe Dubois return "ATPER"; 114*a699b410SJean-Christophe Dubois case ENET_ATCOR: 115*a699b410SJean-Christophe Dubois return "ATCOR"; 116*a699b410SJean-Christophe Dubois case ENET_ATINC: 117*a699b410SJean-Christophe Dubois return "ATINC"; 118*a699b410SJean-Christophe Dubois case ENET_ATSTMP: 119*a699b410SJean-Christophe Dubois return "ATSTMP"; 120*a699b410SJean-Christophe Dubois case ENET_TGSR: 121*a699b410SJean-Christophe Dubois return "TGSR"; 122*a699b410SJean-Christophe Dubois case ENET_TCSR0: 123*a699b410SJean-Christophe Dubois return "TCSR0"; 124*a699b410SJean-Christophe Dubois case ENET_TCCR0: 125*a699b410SJean-Christophe Dubois return "TCCR0"; 126*a699b410SJean-Christophe Dubois case ENET_TCSR1: 127*a699b410SJean-Christophe Dubois return "TCSR1"; 128*a699b410SJean-Christophe Dubois case ENET_TCCR1: 129*a699b410SJean-Christophe Dubois return "TCCR1"; 130*a699b410SJean-Christophe Dubois case ENET_TCSR2: 131*a699b410SJean-Christophe Dubois return "TCSR2"; 132*a699b410SJean-Christophe Dubois case ENET_TCCR2: 133*a699b410SJean-Christophe Dubois return "TCCR2"; 134*a699b410SJean-Christophe Dubois case ENET_TCSR3: 135*a699b410SJean-Christophe Dubois return "TCSR3"; 136*a699b410SJean-Christophe Dubois case ENET_TCCR3: 137*a699b410SJean-Christophe Dubois return "TCCR3"; 138*a699b410SJean-Christophe Dubois default: 139*a699b410SJean-Christophe Dubois return imx_default_reg_name(s, index); 140*a699b410SJean-Christophe Dubois } 141*a699b410SJean-Christophe Dubois } 142*a699b410SJean-Christophe Dubois 143*a699b410SJean-Christophe Dubois static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index) 144*a699b410SJean-Christophe Dubois { 145db0de352SJean-Christophe Dubois switch (index) { 146db0de352SJean-Christophe Dubois case ENET_EIR: 147db0de352SJean-Christophe Dubois return "EIR"; 148db0de352SJean-Christophe Dubois case ENET_EIMR: 149db0de352SJean-Christophe Dubois return "EIMR"; 150db0de352SJean-Christophe Dubois case ENET_RDAR: 151db0de352SJean-Christophe Dubois return "RDAR"; 152db0de352SJean-Christophe Dubois case ENET_TDAR: 153db0de352SJean-Christophe Dubois return "TDAR"; 154db0de352SJean-Christophe Dubois case ENET_ECR: 155db0de352SJean-Christophe Dubois return "ECR"; 156db0de352SJean-Christophe Dubois case ENET_MMFR: 157db0de352SJean-Christophe Dubois return "MMFR"; 158db0de352SJean-Christophe Dubois case ENET_MSCR: 159db0de352SJean-Christophe Dubois return "MSCR"; 160db0de352SJean-Christophe Dubois case ENET_MIBC: 161db0de352SJean-Christophe Dubois return "MIBC"; 162db0de352SJean-Christophe Dubois case ENET_RCR: 163db0de352SJean-Christophe Dubois return "RCR"; 164db0de352SJean-Christophe Dubois case ENET_TCR: 165db0de352SJean-Christophe Dubois return "TCR"; 166db0de352SJean-Christophe Dubois case ENET_PALR: 167db0de352SJean-Christophe Dubois return "PALR"; 168db0de352SJean-Christophe Dubois case ENET_PAUR: 169db0de352SJean-Christophe Dubois return "PAUR"; 170db0de352SJean-Christophe Dubois case ENET_OPD: 171db0de352SJean-Christophe Dubois return "OPD"; 172db0de352SJean-Christophe Dubois case ENET_IAUR: 173db0de352SJean-Christophe Dubois return "IAUR"; 174db0de352SJean-Christophe Dubois case ENET_IALR: 175db0de352SJean-Christophe Dubois return "IALR"; 176db0de352SJean-Christophe Dubois case ENET_GAUR: 177db0de352SJean-Christophe Dubois return "GAUR"; 178db0de352SJean-Christophe Dubois case ENET_GALR: 179db0de352SJean-Christophe Dubois return "GALR"; 180db0de352SJean-Christophe Dubois case ENET_TFWR: 181db0de352SJean-Christophe Dubois return "TFWR"; 182db0de352SJean-Christophe Dubois case ENET_RDSR: 183db0de352SJean-Christophe Dubois return "RDSR"; 184db0de352SJean-Christophe Dubois case ENET_TDSR: 185db0de352SJean-Christophe Dubois return "TDSR"; 186db0de352SJean-Christophe Dubois case ENET_MRBR: 187db0de352SJean-Christophe Dubois return "MRBR"; 188db0de352SJean-Christophe Dubois default: 189*a699b410SJean-Christophe Dubois if (s->is_fec) { 190*a699b410SJean-Christophe Dubois return imx_fec_reg_name(s, index); 191*a699b410SJean-Christophe Dubois } else { 192*a699b410SJean-Christophe Dubois return imx_enet_reg_name(s, index); 193*a699b410SJean-Christophe Dubois } 194db0de352SJean-Christophe Dubois } 195db0de352SJean-Christophe Dubois } 196db0de352SJean-Christophe Dubois 197*a699b410SJean-Christophe Dubois static const VMStateDescription vmstate_imx_eth = { 198fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC, 199db0de352SJean-Christophe Dubois .version_id = 2, 200db0de352SJean-Christophe Dubois .minimum_version_id = 2, 201fcbd8018SJean-Christophe Dubois .fields = (VMStateField[]) { 202db0de352SJean-Christophe Dubois VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), 203fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(rx_descriptor, IMXFECState), 204fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(tx_descriptor, IMXFECState), 205fcbd8018SJean-Christophe Dubois 206fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_status, IMXFECState), 207fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_control, IMXFECState), 208fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_advertise, IMXFECState), 209fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_int, IMXFECState), 210fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_int_mask, IMXFECState), 211fcbd8018SJean-Christophe Dubois VMSTATE_END_OF_LIST() 212fcbd8018SJean-Christophe Dubois } 213fcbd8018SJean-Christophe Dubois }; 214fcbd8018SJean-Christophe Dubois 215fcbd8018SJean-Christophe Dubois #define PHY_INT_ENERGYON (1 << 7) 216fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_COMPLETE (1 << 6) 217fcbd8018SJean-Christophe Dubois #define PHY_INT_FAULT (1 << 5) 218fcbd8018SJean-Christophe Dubois #define PHY_INT_DOWN (1 << 4) 219fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_LP (1 << 3) 220fcbd8018SJean-Christophe Dubois #define PHY_INT_PARFAULT (1 << 2) 221fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_PAGE (1 << 1) 222fcbd8018SJean-Christophe Dubois 223*a699b410SJean-Christophe Dubois static void imx_eth_update(IMXFECState *s); 224fcbd8018SJean-Christophe Dubois 225fcbd8018SJean-Christophe Dubois /* 226fcbd8018SJean-Christophe Dubois * The MII phy could raise a GPIO to the processor which in turn 227fcbd8018SJean-Christophe Dubois * could be handled as an interrpt by the OS. 228fcbd8018SJean-Christophe Dubois * For now we don't handle any GPIO/interrupt line, so the OS will 229fcbd8018SJean-Christophe Dubois * have to poll for the PHY status. 230fcbd8018SJean-Christophe Dubois */ 231fcbd8018SJean-Christophe Dubois static void phy_update_irq(IMXFECState *s) 232fcbd8018SJean-Christophe Dubois { 233*a699b410SJean-Christophe Dubois imx_eth_update(s); 234fcbd8018SJean-Christophe Dubois } 235fcbd8018SJean-Christophe Dubois 236fcbd8018SJean-Christophe Dubois static void phy_update_link(IMXFECState *s) 237fcbd8018SJean-Christophe Dubois { 238fcbd8018SJean-Christophe Dubois /* Autonegotiation status mirrors link status. */ 239fcbd8018SJean-Christophe Dubois if (qemu_get_queue(s->nic)->link_down) { 240fcbd8018SJean-Christophe Dubois PHY_PRINTF("link is down\n"); 241fcbd8018SJean-Christophe Dubois s->phy_status &= ~0x0024; 242fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_DOWN; 243fcbd8018SJean-Christophe Dubois } else { 244fcbd8018SJean-Christophe Dubois PHY_PRINTF("link is up\n"); 245fcbd8018SJean-Christophe Dubois s->phy_status |= 0x0024; 246fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_ENERGYON; 247fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_AUTONEG_COMPLETE; 248fcbd8018SJean-Christophe Dubois } 249fcbd8018SJean-Christophe Dubois phy_update_irq(s); 250fcbd8018SJean-Christophe Dubois } 251fcbd8018SJean-Christophe Dubois 252*a699b410SJean-Christophe Dubois static void imx_eth_set_link(NetClientState *nc) 253fcbd8018SJean-Christophe Dubois { 254fcbd8018SJean-Christophe Dubois phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); 255fcbd8018SJean-Christophe Dubois } 256fcbd8018SJean-Christophe Dubois 257fcbd8018SJean-Christophe Dubois static void phy_reset(IMXFECState *s) 258fcbd8018SJean-Christophe Dubois { 259fcbd8018SJean-Christophe Dubois s->phy_status = 0x7809; 260fcbd8018SJean-Christophe Dubois s->phy_control = 0x3000; 261fcbd8018SJean-Christophe Dubois s->phy_advertise = 0x01e1; 262fcbd8018SJean-Christophe Dubois s->phy_int_mask = 0; 263fcbd8018SJean-Christophe Dubois s->phy_int = 0; 264fcbd8018SJean-Christophe Dubois phy_update_link(s); 265fcbd8018SJean-Christophe Dubois } 266fcbd8018SJean-Christophe Dubois 267fcbd8018SJean-Christophe Dubois static uint32_t do_phy_read(IMXFECState *s, int reg) 268fcbd8018SJean-Christophe Dubois { 269fcbd8018SJean-Christophe Dubois uint32_t val; 270fcbd8018SJean-Christophe Dubois 271fcbd8018SJean-Christophe Dubois if (reg > 31) { 272fcbd8018SJean-Christophe Dubois /* we only advertise one phy */ 273fcbd8018SJean-Christophe Dubois return 0; 274fcbd8018SJean-Christophe Dubois } 275fcbd8018SJean-Christophe Dubois 276fcbd8018SJean-Christophe Dubois switch (reg) { 277fcbd8018SJean-Christophe Dubois case 0: /* Basic Control */ 278fcbd8018SJean-Christophe Dubois val = s->phy_control; 279fcbd8018SJean-Christophe Dubois break; 280fcbd8018SJean-Christophe Dubois case 1: /* Basic Status */ 281fcbd8018SJean-Christophe Dubois val = s->phy_status; 282fcbd8018SJean-Christophe Dubois break; 283fcbd8018SJean-Christophe Dubois case 2: /* ID1 */ 284fcbd8018SJean-Christophe Dubois val = 0x0007; 285fcbd8018SJean-Christophe Dubois break; 286fcbd8018SJean-Christophe Dubois case 3: /* ID2 */ 287fcbd8018SJean-Christophe Dubois val = 0xc0d1; 288fcbd8018SJean-Christophe Dubois break; 289fcbd8018SJean-Christophe Dubois case 4: /* Auto-neg advertisement */ 290fcbd8018SJean-Christophe Dubois val = s->phy_advertise; 291fcbd8018SJean-Christophe Dubois break; 292fcbd8018SJean-Christophe Dubois case 5: /* Auto-neg Link Partner Ability */ 293fcbd8018SJean-Christophe Dubois val = 0x0f71; 294fcbd8018SJean-Christophe Dubois break; 295fcbd8018SJean-Christophe Dubois case 6: /* Auto-neg Expansion */ 296fcbd8018SJean-Christophe Dubois val = 1; 297fcbd8018SJean-Christophe Dubois break; 298fcbd8018SJean-Christophe Dubois case 29: /* Interrupt source. */ 299fcbd8018SJean-Christophe Dubois val = s->phy_int; 300fcbd8018SJean-Christophe Dubois s->phy_int = 0; 301fcbd8018SJean-Christophe Dubois phy_update_irq(s); 302fcbd8018SJean-Christophe Dubois break; 303fcbd8018SJean-Christophe Dubois case 30: /* Interrupt mask */ 304fcbd8018SJean-Christophe Dubois val = s->phy_int_mask; 305fcbd8018SJean-Christophe Dubois break; 306fcbd8018SJean-Christophe Dubois case 17: 307fcbd8018SJean-Christophe Dubois case 18: 308fcbd8018SJean-Christophe Dubois case 27: 309fcbd8018SJean-Christophe Dubois case 31: 310b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", 311fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 312fcbd8018SJean-Christophe Dubois val = 0; 313fcbd8018SJean-Christophe Dubois break; 314fcbd8018SJean-Christophe Dubois default: 315b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", 316fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 317fcbd8018SJean-Christophe Dubois val = 0; 318fcbd8018SJean-Christophe Dubois break; 319fcbd8018SJean-Christophe Dubois } 320fcbd8018SJean-Christophe Dubois 321fcbd8018SJean-Christophe Dubois PHY_PRINTF("read 0x%04x @ %d\n", val, reg); 322fcbd8018SJean-Christophe Dubois 323fcbd8018SJean-Christophe Dubois return val; 324fcbd8018SJean-Christophe Dubois } 325fcbd8018SJean-Christophe Dubois 326fcbd8018SJean-Christophe Dubois static void do_phy_write(IMXFECState *s, int reg, uint32_t val) 327fcbd8018SJean-Christophe Dubois { 328fcbd8018SJean-Christophe Dubois PHY_PRINTF("write 0x%04x @ %d\n", val, reg); 329fcbd8018SJean-Christophe Dubois 330fcbd8018SJean-Christophe Dubois if (reg > 31) { 331fcbd8018SJean-Christophe Dubois /* we only advertise one phy */ 332fcbd8018SJean-Christophe Dubois return; 333fcbd8018SJean-Christophe Dubois } 334fcbd8018SJean-Christophe Dubois 335fcbd8018SJean-Christophe Dubois switch (reg) { 336fcbd8018SJean-Christophe Dubois case 0: /* Basic Control */ 337fcbd8018SJean-Christophe Dubois if (val & 0x8000) { 338fcbd8018SJean-Christophe Dubois phy_reset(s); 339fcbd8018SJean-Christophe Dubois } else { 340fcbd8018SJean-Christophe Dubois s->phy_control = val & 0x7980; 341fcbd8018SJean-Christophe Dubois /* Complete autonegotiation immediately. */ 342fcbd8018SJean-Christophe Dubois if (val & 0x1000) { 343fcbd8018SJean-Christophe Dubois s->phy_status |= 0x0020; 344fcbd8018SJean-Christophe Dubois } 345fcbd8018SJean-Christophe Dubois } 346fcbd8018SJean-Christophe Dubois break; 347fcbd8018SJean-Christophe Dubois case 4: /* Auto-neg advertisement */ 348fcbd8018SJean-Christophe Dubois s->phy_advertise = (val & 0x2d7f) | 0x80; 349fcbd8018SJean-Christophe Dubois break; 350fcbd8018SJean-Christophe Dubois case 30: /* Interrupt mask */ 351fcbd8018SJean-Christophe Dubois s->phy_int_mask = val & 0xff; 352fcbd8018SJean-Christophe Dubois phy_update_irq(s); 353fcbd8018SJean-Christophe Dubois break; 354fcbd8018SJean-Christophe Dubois case 17: 355fcbd8018SJean-Christophe Dubois case 18: 356fcbd8018SJean-Christophe Dubois case 27: 357fcbd8018SJean-Christophe Dubois case 31: 358b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", 359fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 360fcbd8018SJean-Christophe Dubois break; 361fcbd8018SJean-Christophe Dubois default: 362b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", 363fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 364fcbd8018SJean-Christophe Dubois break; 365fcbd8018SJean-Christophe Dubois } 366fcbd8018SJean-Christophe Dubois } 367fcbd8018SJean-Christophe Dubois 368fcbd8018SJean-Christophe Dubois static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) 369fcbd8018SJean-Christophe Dubois { 370fcbd8018SJean-Christophe Dubois dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); 371fcbd8018SJean-Christophe Dubois } 372fcbd8018SJean-Christophe Dubois 373fcbd8018SJean-Christophe Dubois static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) 374fcbd8018SJean-Christophe Dubois { 375fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); 376fcbd8018SJean-Christophe Dubois } 377fcbd8018SJean-Christophe Dubois 378*a699b410SJean-Christophe Dubois static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr) 379fcbd8018SJean-Christophe Dubois { 380*a699b410SJean-Christophe Dubois dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); 381*a699b410SJean-Christophe Dubois } 382*a699b410SJean-Christophe Dubois 383*a699b410SJean-Christophe Dubois static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr) 384*a699b410SJean-Christophe Dubois { 385*a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); 386*a699b410SJean-Christophe Dubois } 387*a699b410SJean-Christophe Dubois 388*a699b410SJean-Christophe Dubois static void imx_eth_update(IMXFECState *s) 389*a699b410SJean-Christophe Dubois { 390*a699b410SJean-Christophe Dubois if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_TS_TIMER) { 391*a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[1], 1); 392db0de352SJean-Christophe Dubois } else { 393*a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[1], 0); 394*a699b410SJean-Christophe Dubois } 395*a699b410SJean-Christophe Dubois 396*a699b410SJean-Christophe Dubois if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_MAC) { 397*a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[0], 1); 398*a699b410SJean-Christophe Dubois } else { 399*a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[0], 0); 400fcbd8018SJean-Christophe Dubois } 401fcbd8018SJean-Christophe Dubois } 402fcbd8018SJean-Christophe Dubois 403fcbd8018SJean-Christophe Dubois static void imx_fec_do_tx(IMXFECState *s) 404fcbd8018SJean-Christophe Dubois { 405fcbd8018SJean-Christophe Dubois int frame_size = 0; 4061bb3c371SJean-Christophe Dubois uint8_t frame[ENET_MAX_FRAME_SIZE]; 407fcbd8018SJean-Christophe Dubois uint8_t *ptr = frame; 408fcbd8018SJean-Christophe Dubois uint32_t addr = s->tx_descriptor; 409fcbd8018SJean-Christophe Dubois 410fcbd8018SJean-Christophe Dubois while (1) { 411fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 412fcbd8018SJean-Christophe Dubois int len; 413fcbd8018SJean-Christophe Dubois 414fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr); 415fcbd8018SJean-Christophe Dubois FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n", 416fcbd8018SJean-Christophe Dubois addr, bd.flags, bd.length, bd.data); 4171bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_R) == 0) { 418fcbd8018SJean-Christophe Dubois /* Run out of descriptors to transmit. */ 419*a699b410SJean-Christophe Dubois FEC_PRINTF("tx_bd ran out of descriptors to transmit\n"); 420fcbd8018SJean-Christophe Dubois break; 421fcbd8018SJean-Christophe Dubois } 422fcbd8018SJean-Christophe Dubois len = bd.length; 4231bb3c371SJean-Christophe Dubois if (frame_size + len > ENET_MAX_FRAME_SIZE) { 4241bb3c371SJean-Christophe Dubois len = ENET_MAX_FRAME_SIZE - frame_size; 425db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_BABT; 426fcbd8018SJean-Christophe Dubois } 427fcbd8018SJean-Christophe Dubois dma_memory_read(&address_space_memory, bd.data, ptr, len); 428fcbd8018SJean-Christophe Dubois ptr += len; 429fcbd8018SJean-Christophe Dubois frame_size += len; 4301bb3c371SJean-Christophe Dubois if (bd.flags & ENET_BD_L) { 431fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */ 432fcbd8018SJean-Christophe Dubois qemu_send_packet(qemu_get_queue(s->nic), frame, len); 433fcbd8018SJean-Christophe Dubois ptr = frame; 434fcbd8018SJean-Christophe Dubois frame_size = 0; 435db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXF; 436fcbd8018SJean-Christophe Dubois } 437db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXB; 4381bb3c371SJean-Christophe Dubois bd.flags &= ~ENET_BD_R; 439fcbd8018SJean-Christophe Dubois /* Write back the modified descriptor. */ 440fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr); 441fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */ 4421bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 443db0de352SJean-Christophe Dubois addr = s->regs[ENET_TDSR]; 444fcbd8018SJean-Christophe Dubois } else { 445db0de352SJean-Christophe Dubois addr += sizeof(bd); 446fcbd8018SJean-Christophe Dubois } 447fcbd8018SJean-Christophe Dubois } 448fcbd8018SJean-Christophe Dubois 449fcbd8018SJean-Christophe Dubois s->tx_descriptor = addr; 450fcbd8018SJean-Christophe Dubois 451*a699b410SJean-Christophe Dubois imx_eth_update(s); 452fcbd8018SJean-Christophe Dubois } 453fcbd8018SJean-Christophe Dubois 454*a699b410SJean-Christophe Dubois static void imx_enet_do_tx(IMXFECState *s) 455*a699b410SJean-Christophe Dubois { 456*a699b410SJean-Christophe Dubois int frame_size = 0; 457*a699b410SJean-Christophe Dubois uint8_t frame[ENET_MAX_FRAME_SIZE]; 458*a699b410SJean-Christophe Dubois uint8_t *ptr = frame; 459*a699b410SJean-Christophe Dubois uint32_t addr = s->tx_descriptor; 460*a699b410SJean-Christophe Dubois 461*a699b410SJean-Christophe Dubois while (1) { 462*a699b410SJean-Christophe Dubois IMXENETBufDesc bd; 463*a699b410SJean-Christophe Dubois int len; 464*a699b410SJean-Christophe Dubois 465*a699b410SJean-Christophe Dubois imx_enet_read_bd(&bd, addr); 466*a699b410SJean-Christophe Dubois FEC_PRINTF("tx_bd %x flags %04x len %d data %08x option %04x " 467*a699b410SJean-Christophe Dubois "status %04x\n", addr, bd.flags, bd.length, bd.data, 468*a699b410SJean-Christophe Dubois bd.option, bd.status); 469*a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_R) == 0) { 470*a699b410SJean-Christophe Dubois /* Run out of descriptors to transmit. */ 471*a699b410SJean-Christophe Dubois break; 472*a699b410SJean-Christophe Dubois } 473*a699b410SJean-Christophe Dubois len = bd.length; 474*a699b410SJean-Christophe Dubois if (frame_size + len > ENET_MAX_FRAME_SIZE) { 475*a699b410SJean-Christophe Dubois len = ENET_MAX_FRAME_SIZE - frame_size; 476*a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_BABT; 477*a699b410SJean-Christophe Dubois } 478*a699b410SJean-Christophe Dubois dma_memory_read(&address_space_memory, bd.data, ptr, len); 479*a699b410SJean-Christophe Dubois ptr += len; 480*a699b410SJean-Christophe Dubois frame_size += len; 481*a699b410SJean-Christophe Dubois if (bd.flags & ENET_BD_L) { 482*a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_PINS) { 483*a699b410SJean-Christophe Dubois struct ip_header *ip_hd = PKT_GET_IP_HDR(frame); 484*a699b410SJean-Christophe Dubois if (IP_HEADER_VERSION(ip_hd) == 4) { 485*a699b410SJean-Christophe Dubois net_checksum_calculate(frame, frame_size); 486*a699b410SJean-Christophe Dubois } 487*a699b410SJean-Christophe Dubois } 488*a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_IINS) { 489*a699b410SJean-Christophe Dubois struct ip_header *ip_hd = PKT_GET_IP_HDR(frame); 490*a699b410SJean-Christophe Dubois /* We compute checksum only for IPv4 frames */ 491*a699b410SJean-Christophe Dubois if (IP_HEADER_VERSION(ip_hd) == 4) { 492*a699b410SJean-Christophe Dubois uint16_t csum; 493*a699b410SJean-Christophe Dubois ip_hd->ip_sum = 0; 494*a699b410SJean-Christophe Dubois csum = net_raw_checksum((uint8_t *)ip_hd, sizeof(*ip_hd)); 495*a699b410SJean-Christophe Dubois ip_hd->ip_sum = cpu_to_be16(csum); 496*a699b410SJean-Christophe Dubois } 497*a699b410SJean-Christophe Dubois } 498*a699b410SJean-Christophe Dubois /* Last buffer in frame. */ 499*a699b410SJean-Christophe Dubois qemu_send_packet(qemu_get_queue(s->nic), frame, len); 500*a699b410SJean-Christophe Dubois ptr = frame; 501*a699b410SJean-Christophe Dubois frame_size = 0; 502*a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_TX_INT) { 503*a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXF; 504*a699b410SJean-Christophe Dubois } 505*a699b410SJean-Christophe Dubois } 506*a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_TX_INT) { 507*a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXB; 508*a699b410SJean-Christophe Dubois } 509*a699b410SJean-Christophe Dubois bd.flags &= ~ENET_BD_R; 510*a699b410SJean-Christophe Dubois /* Write back the modified descriptor. */ 511*a699b410SJean-Christophe Dubois imx_enet_write_bd(&bd, addr); 512*a699b410SJean-Christophe Dubois /* Advance to the next descriptor. */ 513*a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 514*a699b410SJean-Christophe Dubois addr = s->regs[ENET_TDSR]; 515*a699b410SJean-Christophe Dubois } else { 516*a699b410SJean-Christophe Dubois addr += sizeof(bd); 517*a699b410SJean-Christophe Dubois } 518*a699b410SJean-Christophe Dubois } 519*a699b410SJean-Christophe Dubois 520*a699b410SJean-Christophe Dubois s->tx_descriptor = addr; 521*a699b410SJean-Christophe Dubois 522*a699b410SJean-Christophe Dubois imx_eth_update(s); 523*a699b410SJean-Christophe Dubois } 524*a699b410SJean-Christophe Dubois 525*a699b410SJean-Christophe Dubois static void imx_eth_do_tx(IMXFECState *s) 526*a699b410SJean-Christophe Dubois { 527*a699b410SJean-Christophe Dubois if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { 528*a699b410SJean-Christophe Dubois imx_enet_do_tx(s); 529*a699b410SJean-Christophe Dubois } else { 530*a699b410SJean-Christophe Dubois imx_fec_do_tx(s); 531*a699b410SJean-Christophe Dubois } 532*a699b410SJean-Christophe Dubois } 533*a699b410SJean-Christophe Dubois 534*a699b410SJean-Christophe Dubois static void imx_eth_enable_rx(IMXFECState *s) 535fcbd8018SJean-Christophe Dubois { 536fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 537db0de352SJean-Christophe Dubois bool tmp; 538fcbd8018SJean-Christophe Dubois 539fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, s->rx_descriptor); 540fcbd8018SJean-Christophe Dubois 5411bb3c371SJean-Christophe Dubois tmp = ((bd.flags & ENET_BD_E) != 0); 542fcbd8018SJean-Christophe Dubois 543fcbd8018SJean-Christophe Dubois if (!tmp) { 544fcbd8018SJean-Christophe Dubois FEC_PRINTF("RX buffer full\n"); 545db0de352SJean-Christophe Dubois } else if (!s->regs[ENET_RDAR]) { 546fcbd8018SJean-Christophe Dubois qemu_flush_queued_packets(qemu_get_queue(s->nic)); 547fcbd8018SJean-Christophe Dubois } 548fcbd8018SJean-Christophe Dubois 549db0de352SJean-Christophe Dubois s->regs[ENET_RDAR] = tmp ? ENET_RDAR_RDAR : 0; 550fcbd8018SJean-Christophe Dubois } 551fcbd8018SJean-Christophe Dubois 552*a699b410SJean-Christophe Dubois static void imx_eth_reset(DeviceState *d) 553fcbd8018SJean-Christophe Dubois { 554fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(d); 555fcbd8018SJean-Christophe Dubois 556*a699b410SJean-Christophe Dubois /* Reset the Device */ 557db0de352SJean-Christophe Dubois memset(s->regs, 0, sizeof(s->regs)); 558db0de352SJean-Christophe Dubois s->regs[ENET_ECR] = 0xf0000000; 559db0de352SJean-Christophe Dubois s->regs[ENET_MIBC] = 0xc0000000; 560db0de352SJean-Christophe Dubois s->regs[ENET_RCR] = 0x05ee0001; 561db0de352SJean-Christophe Dubois s->regs[ENET_OPD] = 0x00010000; 562db0de352SJean-Christophe Dubois 563db0de352SJean-Christophe Dubois s->regs[ENET_PALR] = (s->conf.macaddr.a[0] << 24) 564db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[1] << 16) 565db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[2] << 8) 566db0de352SJean-Christophe Dubois | s->conf.macaddr.a[3]; 567db0de352SJean-Christophe Dubois s->regs[ENET_PAUR] = (s->conf.macaddr.a[4] << 24) 568db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[5] << 16) 569db0de352SJean-Christophe Dubois | 0x8808; 570db0de352SJean-Christophe Dubois 571*a699b410SJean-Christophe Dubois if (s->is_fec) { 572db0de352SJean-Christophe Dubois s->regs[ENET_FRBR] = 0x00000600; 573db0de352SJean-Christophe Dubois s->regs[ENET_FRSR] = 0x00000500; 574db0de352SJean-Christophe Dubois s->regs[ENET_MIIGSK_ENR] = 0x00000006; 575*a699b410SJean-Christophe Dubois } else { 576*a699b410SJean-Christophe Dubois s->regs[ENET_RAEM] = 0x00000004; 577*a699b410SJean-Christophe Dubois s->regs[ENET_RAFL] = 0x00000004; 578*a699b410SJean-Christophe Dubois s->regs[ENET_TAEM] = 0x00000004; 579*a699b410SJean-Christophe Dubois s->regs[ENET_TAFL] = 0x00000008; 580*a699b410SJean-Christophe Dubois s->regs[ENET_TIPG] = 0x0000000c; 581*a699b410SJean-Christophe Dubois s->regs[ENET_FTRL] = 0x000007ff; 582*a699b410SJean-Christophe Dubois s->regs[ENET_ATPER] = 0x3b9aca00; 583*a699b410SJean-Christophe Dubois } 584db0de352SJean-Christophe Dubois 585db0de352SJean-Christophe Dubois s->rx_descriptor = 0; 586db0de352SJean-Christophe Dubois s->tx_descriptor = 0; 587fcbd8018SJean-Christophe Dubois 588fcbd8018SJean-Christophe Dubois /* We also reset the PHY */ 589fcbd8018SJean-Christophe Dubois phy_reset(s); 590fcbd8018SJean-Christophe Dubois } 591fcbd8018SJean-Christophe Dubois 592*a699b410SJean-Christophe Dubois static uint32_t imx_default_read(IMXFECState *s, uint32_t index) 593*a699b410SJean-Christophe Dubois { 594*a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 595*a699b410SJean-Christophe Dubois PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); 596*a699b410SJean-Christophe Dubois return 0; 597*a699b410SJean-Christophe Dubois } 598*a699b410SJean-Christophe Dubois 599*a699b410SJean-Christophe Dubois static uint32_t imx_fec_read(IMXFECState *s, uint32_t index) 600*a699b410SJean-Christophe Dubois { 601*a699b410SJean-Christophe Dubois switch (index) { 602*a699b410SJean-Christophe Dubois case ENET_FRBR: 603*a699b410SJean-Christophe Dubois case ENET_FRSR: 604*a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR: 605*a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR: 606*a699b410SJean-Christophe Dubois return s->regs[index]; 607*a699b410SJean-Christophe Dubois default: 608*a699b410SJean-Christophe Dubois return imx_default_read(s, index); 609*a699b410SJean-Christophe Dubois } 610*a699b410SJean-Christophe Dubois } 611*a699b410SJean-Christophe Dubois 612*a699b410SJean-Christophe Dubois static uint32_t imx_enet_read(IMXFECState *s, uint32_t index) 613*a699b410SJean-Christophe Dubois { 614*a699b410SJean-Christophe Dubois switch (index) { 615*a699b410SJean-Christophe Dubois case ENET_RSFL: 616*a699b410SJean-Christophe Dubois case ENET_RSEM: 617*a699b410SJean-Christophe Dubois case ENET_RAEM: 618*a699b410SJean-Christophe Dubois case ENET_RAFL: 619*a699b410SJean-Christophe Dubois case ENET_TSEM: 620*a699b410SJean-Christophe Dubois case ENET_TAEM: 621*a699b410SJean-Christophe Dubois case ENET_TAFL: 622*a699b410SJean-Christophe Dubois case ENET_TIPG: 623*a699b410SJean-Christophe Dubois case ENET_FTRL: 624*a699b410SJean-Christophe Dubois case ENET_TACC: 625*a699b410SJean-Christophe Dubois case ENET_RACC: 626*a699b410SJean-Christophe Dubois case ENET_ATCR: 627*a699b410SJean-Christophe Dubois case ENET_ATVR: 628*a699b410SJean-Christophe Dubois case ENET_ATOFF: 629*a699b410SJean-Christophe Dubois case ENET_ATPER: 630*a699b410SJean-Christophe Dubois case ENET_ATCOR: 631*a699b410SJean-Christophe Dubois case ENET_ATINC: 632*a699b410SJean-Christophe Dubois case ENET_ATSTMP: 633*a699b410SJean-Christophe Dubois case ENET_TGSR: 634*a699b410SJean-Christophe Dubois case ENET_TCSR0: 635*a699b410SJean-Christophe Dubois case ENET_TCCR0: 636*a699b410SJean-Christophe Dubois case ENET_TCSR1: 637*a699b410SJean-Christophe Dubois case ENET_TCCR1: 638*a699b410SJean-Christophe Dubois case ENET_TCSR2: 639*a699b410SJean-Christophe Dubois case ENET_TCCR2: 640*a699b410SJean-Christophe Dubois case ENET_TCSR3: 641*a699b410SJean-Christophe Dubois case ENET_TCCR3: 642*a699b410SJean-Christophe Dubois return s->regs[index]; 643*a699b410SJean-Christophe Dubois default: 644*a699b410SJean-Christophe Dubois return imx_default_read(s, index); 645*a699b410SJean-Christophe Dubois } 646*a699b410SJean-Christophe Dubois } 647*a699b410SJean-Christophe Dubois 648*a699b410SJean-Christophe Dubois static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size) 649fcbd8018SJean-Christophe Dubois { 650db0de352SJean-Christophe Dubois uint32_t value = 0; 651fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque); 652*a699b410SJean-Christophe Dubois uint32_t index = offset >> 2; 653fcbd8018SJean-Christophe Dubois 654db0de352SJean-Christophe Dubois switch (index) { 655db0de352SJean-Christophe Dubois case ENET_EIR: 656db0de352SJean-Christophe Dubois case ENET_EIMR: 657db0de352SJean-Christophe Dubois case ENET_RDAR: 658db0de352SJean-Christophe Dubois case ENET_TDAR: 659db0de352SJean-Christophe Dubois case ENET_ECR: 660db0de352SJean-Christophe Dubois case ENET_MMFR: 661db0de352SJean-Christophe Dubois case ENET_MSCR: 662db0de352SJean-Christophe Dubois case ENET_MIBC: 663db0de352SJean-Christophe Dubois case ENET_RCR: 664db0de352SJean-Christophe Dubois case ENET_TCR: 665db0de352SJean-Christophe Dubois case ENET_PALR: 666db0de352SJean-Christophe Dubois case ENET_PAUR: 667db0de352SJean-Christophe Dubois case ENET_OPD: 668db0de352SJean-Christophe Dubois case ENET_IAUR: 669db0de352SJean-Christophe Dubois case ENET_IALR: 670db0de352SJean-Christophe Dubois case ENET_GAUR: 671db0de352SJean-Christophe Dubois case ENET_GALR: 672db0de352SJean-Christophe Dubois case ENET_TFWR: 673db0de352SJean-Christophe Dubois case ENET_RDSR: 674db0de352SJean-Christophe Dubois case ENET_TDSR: 675db0de352SJean-Christophe Dubois case ENET_MRBR: 676db0de352SJean-Christophe Dubois value = s->regs[index]; 677fcbd8018SJean-Christophe Dubois break; 678fcbd8018SJean-Christophe Dubois default: 679*a699b410SJean-Christophe Dubois if (s->is_fec) { 680*a699b410SJean-Christophe Dubois value = imx_fec_read(s, index); 681*a699b410SJean-Christophe Dubois } else { 682*a699b410SJean-Christophe Dubois value = imx_enet_read(s, index); 683*a699b410SJean-Christophe Dubois } 684db0de352SJean-Christophe Dubois break; 685fcbd8018SJean-Christophe Dubois } 686db0de352SJean-Christophe Dubois 687*a699b410SJean-Christophe Dubois FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_eth_reg_name(s, index), 688db0de352SJean-Christophe Dubois value); 689db0de352SJean-Christophe Dubois 690db0de352SJean-Christophe Dubois return value; 691fcbd8018SJean-Christophe Dubois } 692fcbd8018SJean-Christophe Dubois 693*a699b410SJean-Christophe Dubois static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value) 694*a699b410SJean-Christophe Dubois { 695*a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" 696*a699b410SJean-Christophe Dubois PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); 697*a699b410SJean-Christophe Dubois return; 698*a699b410SJean-Christophe Dubois } 699*a699b410SJean-Christophe Dubois 700*a699b410SJean-Christophe Dubois static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value) 701*a699b410SJean-Christophe Dubois { 702*a699b410SJean-Christophe Dubois switch (index) { 703*a699b410SJean-Christophe Dubois case ENET_FRBR: 704*a699b410SJean-Christophe Dubois /* FRBR is read only */ 705*a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n", 706*a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 707*a699b410SJean-Christophe Dubois break; 708*a699b410SJean-Christophe Dubois case ENET_FRSR: 709*a699b410SJean-Christophe Dubois s->regs[index] = (value & 0x000003fc) | 0x00000400; 710*a699b410SJean-Christophe Dubois break; 711*a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR: 712*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00000053; 713*a699b410SJean-Christophe Dubois break; 714*a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR: 715*a699b410SJean-Christophe Dubois s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0; 716*a699b410SJean-Christophe Dubois break; 717*a699b410SJean-Christophe Dubois default: 718*a699b410SJean-Christophe Dubois imx_default_write(s, index, value); 719*a699b410SJean-Christophe Dubois break; 720*a699b410SJean-Christophe Dubois } 721*a699b410SJean-Christophe Dubois } 722*a699b410SJean-Christophe Dubois 723*a699b410SJean-Christophe Dubois static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) 724*a699b410SJean-Christophe Dubois { 725*a699b410SJean-Christophe Dubois switch (index) { 726*a699b410SJean-Christophe Dubois case ENET_RSFL: 727*a699b410SJean-Christophe Dubois case ENET_RSEM: 728*a699b410SJean-Christophe Dubois case ENET_RAEM: 729*a699b410SJean-Christophe Dubois case ENET_RAFL: 730*a699b410SJean-Christophe Dubois case ENET_TSEM: 731*a699b410SJean-Christophe Dubois case ENET_TAEM: 732*a699b410SJean-Christophe Dubois case ENET_TAFL: 733*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x000001ff; 734*a699b410SJean-Christophe Dubois break; 735*a699b410SJean-Christophe Dubois case ENET_TIPG: 736*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x0000001f; 737*a699b410SJean-Christophe Dubois break; 738*a699b410SJean-Christophe Dubois case ENET_FTRL: 739*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00003fff; 740*a699b410SJean-Christophe Dubois break; 741*a699b410SJean-Christophe Dubois case ENET_TACC: 742*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00000019; 743*a699b410SJean-Christophe Dubois break; 744*a699b410SJean-Christophe Dubois case ENET_RACC: 745*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x000000C7; 746*a699b410SJean-Christophe Dubois break; 747*a699b410SJean-Christophe Dubois case ENET_ATCR: 748*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00002a9d; 749*a699b410SJean-Christophe Dubois break; 750*a699b410SJean-Christophe Dubois case ENET_ATVR: 751*a699b410SJean-Christophe Dubois case ENET_ATOFF: 752*a699b410SJean-Christophe Dubois case ENET_ATPER: 753*a699b410SJean-Christophe Dubois s->regs[index] = value; 754*a699b410SJean-Christophe Dubois break; 755*a699b410SJean-Christophe Dubois case ENET_ATSTMP: 756*a699b410SJean-Christophe Dubois /* ATSTMP is read only */ 757*a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n", 758*a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 759*a699b410SJean-Christophe Dubois break; 760*a699b410SJean-Christophe Dubois case ENET_ATCOR: 761*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x7fffffff; 762*a699b410SJean-Christophe Dubois break; 763*a699b410SJean-Christophe Dubois case ENET_ATINC: 764*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00007f7f; 765*a699b410SJean-Christophe Dubois break; 766*a699b410SJean-Christophe Dubois case ENET_TGSR: 767*a699b410SJean-Christophe Dubois /* implement clear timer flag */ 768*a699b410SJean-Christophe Dubois value = value & 0x0000000f; 769*a699b410SJean-Christophe Dubois break; 770*a699b410SJean-Christophe Dubois case ENET_TCSR0: 771*a699b410SJean-Christophe Dubois case ENET_TCSR1: 772*a699b410SJean-Christophe Dubois case ENET_TCSR2: 773*a699b410SJean-Christophe Dubois case ENET_TCSR3: 774*a699b410SJean-Christophe Dubois value = value & 0x000000fd; 775*a699b410SJean-Christophe Dubois break; 776*a699b410SJean-Christophe Dubois case ENET_TCCR0: 777*a699b410SJean-Christophe Dubois case ENET_TCCR1: 778*a699b410SJean-Christophe Dubois case ENET_TCCR2: 779*a699b410SJean-Christophe Dubois case ENET_TCCR3: 780*a699b410SJean-Christophe Dubois s->regs[index] = value; 781*a699b410SJean-Christophe Dubois break; 782*a699b410SJean-Christophe Dubois default: 783*a699b410SJean-Christophe Dubois imx_default_write(s, index, value); 784*a699b410SJean-Christophe Dubois break; 785*a699b410SJean-Christophe Dubois } 786*a699b410SJean-Christophe Dubois } 787*a699b410SJean-Christophe Dubois 788*a699b410SJean-Christophe Dubois static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, 789*a699b410SJean-Christophe Dubois unsigned size) 790fcbd8018SJean-Christophe Dubois { 791fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque); 792*a699b410SJean-Christophe Dubois uint32_t index = offset >> 2; 793fcbd8018SJean-Christophe Dubois 794*a699b410SJean-Christophe Dubois FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_eth_reg_name(s, index), 795db0de352SJean-Christophe Dubois (uint32_t)value); 796fcbd8018SJean-Christophe Dubois 797db0de352SJean-Christophe Dubois switch (index) { 798db0de352SJean-Christophe Dubois case ENET_EIR: 799db0de352SJean-Christophe Dubois s->regs[index] &= ~value; 800fcbd8018SJean-Christophe Dubois break; 801db0de352SJean-Christophe Dubois case ENET_EIMR: 802db0de352SJean-Christophe Dubois s->regs[index] = value; 803fcbd8018SJean-Christophe Dubois break; 804db0de352SJean-Christophe Dubois case ENET_RDAR: 805db0de352SJean-Christophe Dubois if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { 806db0de352SJean-Christophe Dubois if (!s->regs[index]) { 807db0de352SJean-Christophe Dubois s->regs[index] = ENET_RDAR_RDAR; 808*a699b410SJean-Christophe Dubois imx_eth_enable_rx(s); 809fcbd8018SJean-Christophe Dubois } 810db0de352SJean-Christophe Dubois } else { 811db0de352SJean-Christophe Dubois s->regs[index] = 0; 812db0de352SJean-Christophe Dubois } 813fcbd8018SJean-Christophe Dubois break; 814db0de352SJean-Christophe Dubois case ENET_TDAR: 815db0de352SJean-Christophe Dubois if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { 816db0de352SJean-Christophe Dubois s->regs[index] = ENET_TDAR_TDAR; 817*a699b410SJean-Christophe Dubois imx_eth_do_tx(s); 818fcbd8018SJean-Christophe Dubois } 819db0de352SJean-Christophe Dubois s->regs[index] = 0; 820fcbd8018SJean-Christophe Dubois break; 821db0de352SJean-Christophe Dubois case ENET_ECR: 8221bb3c371SJean-Christophe Dubois if (value & ENET_ECR_RESET) { 823*a699b410SJean-Christophe Dubois return imx_eth_reset(DEVICE(s)); 824fcbd8018SJean-Christophe Dubois } 825db0de352SJean-Christophe Dubois s->regs[index] = value; 826db0de352SJean-Christophe Dubois if ((s->regs[index] & ENET_ECR_ETHEREN) == 0) { 827db0de352SJean-Christophe Dubois s->regs[ENET_RDAR] = 0; 828db0de352SJean-Christophe Dubois s->rx_descriptor = s->regs[ENET_RDSR]; 829db0de352SJean-Christophe Dubois s->regs[ENET_TDAR] = 0; 830db0de352SJean-Christophe Dubois s->tx_descriptor = s->regs[ENET_TDSR]; 831fcbd8018SJean-Christophe Dubois } 832fcbd8018SJean-Christophe Dubois break; 833db0de352SJean-Christophe Dubois case ENET_MMFR: 834db0de352SJean-Christophe Dubois s->regs[index] = value; 8354816dc16SJean-Christophe Dubois if (extract32(value, 29, 1)) { 836db0de352SJean-Christophe Dubois /* This is a read operation */ 837db0de352SJean-Christophe Dubois s->regs[ENET_MMFR] = deposit32(s->regs[ENET_MMFR], 0, 16, 838db0de352SJean-Christophe Dubois do_phy_read(s, 839db0de352SJean-Christophe Dubois extract32(value, 840db0de352SJean-Christophe Dubois 18, 10))); 8414816dc16SJean-Christophe Dubois } else { 842db0de352SJean-Christophe Dubois /* This a write operation */ 843b413643aSJean-Christophe Dubois do_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16)); 844fcbd8018SJean-Christophe Dubois } 845fcbd8018SJean-Christophe Dubois /* raise the interrupt as the PHY operation is done */ 846db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_MII; 847fcbd8018SJean-Christophe Dubois break; 848db0de352SJean-Christophe Dubois case ENET_MSCR: 849db0de352SJean-Christophe Dubois s->regs[index] = value & 0xfe; 850fcbd8018SJean-Christophe Dubois break; 851db0de352SJean-Christophe Dubois case ENET_MIBC: 852fcbd8018SJean-Christophe Dubois /* TODO: Implement MIB. */ 853db0de352SJean-Christophe Dubois s->regs[index] = (value & 0x80000000) ? 0xc0000000 : 0; 854fcbd8018SJean-Christophe Dubois break; 855db0de352SJean-Christophe Dubois case ENET_RCR: 856db0de352SJean-Christophe Dubois s->regs[index] = value & 0x07ff003f; 857fcbd8018SJean-Christophe Dubois /* TODO: Implement LOOP mode. */ 858fcbd8018SJean-Christophe Dubois break; 859db0de352SJean-Christophe Dubois case ENET_TCR: 860fcbd8018SJean-Christophe Dubois /* We transmit immediately, so raise GRA immediately. */ 861db0de352SJean-Christophe Dubois s->regs[index] = value; 862fcbd8018SJean-Christophe Dubois if (value & 1) { 863db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_GRA; 864fcbd8018SJean-Christophe Dubois } 865fcbd8018SJean-Christophe Dubois break; 866db0de352SJean-Christophe Dubois case ENET_PALR: 867db0de352SJean-Christophe Dubois s->regs[index] = value; 868fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[0] = value >> 24; 869fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[1] = value >> 16; 870fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[2] = value >> 8; 871fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[3] = value; 872fcbd8018SJean-Christophe Dubois break; 873db0de352SJean-Christophe Dubois case ENET_PAUR: 874db0de352SJean-Christophe Dubois s->regs[index] = (value | 0x0000ffff) & 0xffff8808; 875fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[4] = value >> 24; 876fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[5] = value >> 16; 877fcbd8018SJean-Christophe Dubois break; 878db0de352SJean-Christophe Dubois case ENET_OPD: 879db0de352SJean-Christophe Dubois s->regs[index] = (value & 0x0000ffff) | 0x00010000; 880fcbd8018SJean-Christophe Dubois break; 881db0de352SJean-Christophe Dubois case ENET_IAUR: 882db0de352SJean-Christophe Dubois case ENET_IALR: 883db0de352SJean-Christophe Dubois case ENET_GAUR: 884db0de352SJean-Christophe Dubois case ENET_GALR: 885fcbd8018SJean-Christophe Dubois /* TODO: implement MAC hash filtering. */ 886fcbd8018SJean-Christophe Dubois break; 887db0de352SJean-Christophe Dubois case ENET_TFWR: 888*a699b410SJean-Christophe Dubois if (s->is_fec) { 889*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x3; 890*a699b410SJean-Christophe Dubois } else { 891*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x13f; 892*a699b410SJean-Christophe Dubois } 893fcbd8018SJean-Christophe Dubois break; 894db0de352SJean-Christophe Dubois case ENET_RDSR: 895*a699b410SJean-Christophe Dubois if (s->is_fec) { 896db0de352SJean-Christophe Dubois s->regs[index] = value & ~3; 897*a699b410SJean-Christophe Dubois } else { 898*a699b410SJean-Christophe Dubois s->regs[index] = value & ~7; 899*a699b410SJean-Christophe Dubois } 900db0de352SJean-Christophe Dubois s->rx_descriptor = s->regs[index]; 901fcbd8018SJean-Christophe Dubois break; 902db0de352SJean-Christophe Dubois case ENET_TDSR: 903*a699b410SJean-Christophe Dubois if (s->is_fec) { 904db0de352SJean-Christophe Dubois s->regs[index] = value & ~3; 905*a699b410SJean-Christophe Dubois } else { 906*a699b410SJean-Christophe Dubois s->regs[index] = value & ~7; 907*a699b410SJean-Christophe Dubois } 908db0de352SJean-Christophe Dubois s->tx_descriptor = s->regs[index]; 909fcbd8018SJean-Christophe Dubois break; 910db0de352SJean-Christophe Dubois case ENET_MRBR: 911*a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00003ff0; 912fcbd8018SJean-Christophe Dubois break; 913fcbd8018SJean-Christophe Dubois default: 914*a699b410SJean-Christophe Dubois if (s->is_fec) { 915*a699b410SJean-Christophe Dubois imx_fec_write(s, index, value); 916*a699b410SJean-Christophe Dubois } else { 917*a699b410SJean-Christophe Dubois imx_enet_write(s, index, value); 918*a699b410SJean-Christophe Dubois } 919*a699b410SJean-Christophe Dubois return; 920fcbd8018SJean-Christophe Dubois } 921fcbd8018SJean-Christophe Dubois 922*a699b410SJean-Christophe Dubois imx_eth_update(s); 923fcbd8018SJean-Christophe Dubois } 924fcbd8018SJean-Christophe Dubois 925*a699b410SJean-Christophe Dubois static int imx_eth_can_receive(NetClientState *nc) 926fcbd8018SJean-Christophe Dubois { 927fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 928fcbd8018SJean-Christophe Dubois 929*a699b410SJean-Christophe Dubois FEC_PRINTF("\n"); 930*a699b410SJean-Christophe Dubois 931db0de352SJean-Christophe Dubois return s->regs[ENET_RDAR] ? 1 : 0; 932fcbd8018SJean-Christophe Dubois } 933fcbd8018SJean-Christophe Dubois 934fcbd8018SJean-Christophe Dubois static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, 935fcbd8018SJean-Christophe Dubois size_t len) 936fcbd8018SJean-Christophe Dubois { 937fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 938fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 939fcbd8018SJean-Christophe Dubois uint32_t flags = 0; 940fcbd8018SJean-Christophe Dubois uint32_t addr; 941fcbd8018SJean-Christophe Dubois uint32_t crc; 942fcbd8018SJean-Christophe Dubois uint32_t buf_addr; 943fcbd8018SJean-Christophe Dubois uint8_t *crc_ptr; 944fcbd8018SJean-Christophe Dubois unsigned int buf_len; 945fcbd8018SJean-Christophe Dubois size_t size = len; 946fcbd8018SJean-Christophe Dubois 947fcbd8018SJean-Christophe Dubois FEC_PRINTF("len %d\n", (int)size); 948fcbd8018SJean-Christophe Dubois 949db0de352SJean-Christophe Dubois if (!s->regs[ENET_RDAR]) { 950b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", 951fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 952fcbd8018SJean-Christophe Dubois return 0; 953fcbd8018SJean-Christophe Dubois } 954fcbd8018SJean-Christophe Dubois 955fcbd8018SJean-Christophe Dubois /* 4 bytes for the CRC. */ 956fcbd8018SJean-Christophe Dubois size += 4; 957fcbd8018SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size)); 958fcbd8018SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc; 959fcbd8018SJean-Christophe Dubois 960*a699b410SJean-Christophe Dubois /* Huge frames are truncated. */ 9611bb3c371SJean-Christophe Dubois if (size > ENET_MAX_FRAME_SIZE) { 9621bb3c371SJean-Christophe Dubois size = ENET_MAX_FRAME_SIZE; 9631bb3c371SJean-Christophe Dubois flags |= ENET_BD_TR | ENET_BD_LG; 964fcbd8018SJean-Christophe Dubois } 965fcbd8018SJean-Christophe Dubois 966fcbd8018SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */ 967db0de352SJean-Christophe Dubois if (size > (s->regs[ENET_RCR] >> 16)) { 9681bb3c371SJean-Christophe Dubois flags |= ENET_BD_LG; 969fcbd8018SJean-Christophe Dubois } 970fcbd8018SJean-Christophe Dubois 971fcbd8018SJean-Christophe Dubois addr = s->rx_descriptor; 972fcbd8018SJean-Christophe Dubois while (size > 0) { 973fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr); 9741bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_E) == 0) { 975fcbd8018SJean-Christophe Dubois /* No descriptors available. Bail out. */ 976fcbd8018SJean-Christophe Dubois /* 977fcbd8018SJean-Christophe Dubois * FIXME: This is wrong. We should probably either 978fcbd8018SJean-Christophe Dubois * save the remainder for when more RX buffers are 979fcbd8018SJean-Christophe Dubois * available, or flag an error. 980fcbd8018SJean-Christophe Dubois */ 981b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", 982fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 983fcbd8018SJean-Christophe Dubois break; 984fcbd8018SJean-Christophe Dubois } 985db0de352SJean-Christophe Dubois buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR]; 986fcbd8018SJean-Christophe Dubois bd.length = buf_len; 987fcbd8018SJean-Christophe Dubois size -= buf_len; 988b72d8d25SJean-Christophe Dubois 989b72d8d25SJean-Christophe Dubois FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); 990b72d8d25SJean-Christophe Dubois 991fcbd8018SJean-Christophe Dubois /* The last 4 bytes are the CRC. */ 992fcbd8018SJean-Christophe Dubois if (size < 4) { 993fcbd8018SJean-Christophe Dubois buf_len += size - 4; 994fcbd8018SJean-Christophe Dubois } 995fcbd8018SJean-Christophe Dubois buf_addr = bd.data; 996fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); 997fcbd8018SJean-Christophe Dubois buf += buf_len; 998fcbd8018SJean-Christophe Dubois if (size < 4) { 999fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len, 1000fcbd8018SJean-Christophe Dubois crc_ptr, 4 - size); 1001fcbd8018SJean-Christophe Dubois crc_ptr += 4 - size; 1002fcbd8018SJean-Christophe Dubois } 10031bb3c371SJean-Christophe Dubois bd.flags &= ~ENET_BD_E; 1004fcbd8018SJean-Christophe Dubois if (size == 0) { 1005fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */ 10061bb3c371SJean-Christophe Dubois bd.flags |= flags | ENET_BD_L; 1007fcbd8018SJean-Christophe Dubois FEC_PRINTF("rx frame flags %04x\n", bd.flags); 1008db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXF; 1009fcbd8018SJean-Christophe Dubois } else { 1010db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXB; 1011fcbd8018SJean-Christophe Dubois } 1012fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr); 1013fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */ 10141bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 1015db0de352SJean-Christophe Dubois addr = s->regs[ENET_RDSR]; 1016fcbd8018SJean-Christophe Dubois } else { 1017db0de352SJean-Christophe Dubois addr += sizeof(bd); 1018fcbd8018SJean-Christophe Dubois } 1019fcbd8018SJean-Christophe Dubois } 1020fcbd8018SJean-Christophe Dubois s->rx_descriptor = addr; 1021*a699b410SJean-Christophe Dubois imx_eth_enable_rx(s); 1022*a699b410SJean-Christophe Dubois imx_eth_update(s); 1023fcbd8018SJean-Christophe Dubois return len; 1024fcbd8018SJean-Christophe Dubois } 1025fcbd8018SJean-Christophe Dubois 1026*a699b410SJean-Christophe Dubois static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf, 1027*a699b410SJean-Christophe Dubois size_t len) 1028*a699b410SJean-Christophe Dubois { 1029*a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1030*a699b410SJean-Christophe Dubois IMXENETBufDesc bd; 1031*a699b410SJean-Christophe Dubois uint32_t flags = 0; 1032*a699b410SJean-Christophe Dubois uint32_t addr; 1033*a699b410SJean-Christophe Dubois uint32_t crc; 1034*a699b410SJean-Christophe Dubois uint32_t buf_addr; 1035*a699b410SJean-Christophe Dubois uint8_t *crc_ptr; 1036*a699b410SJean-Christophe Dubois unsigned int buf_len; 1037*a699b410SJean-Christophe Dubois size_t size = len; 1038*a699b410SJean-Christophe Dubois 1039*a699b410SJean-Christophe Dubois FEC_PRINTF("len %d\n", (int)size); 1040*a699b410SJean-Christophe Dubois 1041*a699b410SJean-Christophe Dubois if (!s->regs[ENET_RDAR]) { 1042*a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", 1043*a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 1044*a699b410SJean-Christophe Dubois return 0; 1045*a699b410SJean-Christophe Dubois } 1046*a699b410SJean-Christophe Dubois 1047*a699b410SJean-Christophe Dubois /* 4 bytes for the CRC. */ 1048*a699b410SJean-Christophe Dubois size += 4; 1049*a699b410SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size)); 1050*a699b410SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc; 1051*a699b410SJean-Christophe Dubois 1052*a699b410SJean-Christophe Dubois /* Huge frames are truncted. */ 1053*a699b410SJean-Christophe Dubois if (size > ENET_MAX_FRAME_SIZE) { 1054*a699b410SJean-Christophe Dubois size = ENET_MAX_FRAME_SIZE; 1055*a699b410SJean-Christophe Dubois flags |= ENET_BD_TR | ENET_BD_LG; 1056*a699b410SJean-Christophe Dubois } 1057*a699b410SJean-Christophe Dubois 1058*a699b410SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */ 1059*a699b410SJean-Christophe Dubois if (size > (s->regs[ENET_RCR] >> 16)) { 1060*a699b410SJean-Christophe Dubois flags |= ENET_BD_LG; 1061*a699b410SJean-Christophe Dubois } 1062*a699b410SJean-Christophe Dubois 1063*a699b410SJean-Christophe Dubois addr = s->rx_descriptor; 1064*a699b410SJean-Christophe Dubois while (size > 0) { 1065*a699b410SJean-Christophe Dubois imx_enet_read_bd(&bd, addr); 1066*a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_E) == 0) { 1067*a699b410SJean-Christophe Dubois /* No descriptors available. Bail out. */ 1068*a699b410SJean-Christophe Dubois /* 1069*a699b410SJean-Christophe Dubois * FIXME: This is wrong. We should probably either 1070*a699b410SJean-Christophe Dubois * save the remainder for when more RX buffers are 1071*a699b410SJean-Christophe Dubois * available, or flag an error. 1072*a699b410SJean-Christophe Dubois */ 1073*a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", 1074*a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 1075*a699b410SJean-Christophe Dubois break; 1076*a699b410SJean-Christophe Dubois } 1077*a699b410SJean-Christophe Dubois buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR]; 1078*a699b410SJean-Christophe Dubois bd.length = buf_len; 1079*a699b410SJean-Christophe Dubois size -= buf_len; 1080*a699b410SJean-Christophe Dubois 1081*a699b410SJean-Christophe Dubois FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); 1082*a699b410SJean-Christophe Dubois 1083*a699b410SJean-Christophe Dubois /* The last 4 bytes are the CRC. */ 1084*a699b410SJean-Christophe Dubois if (size < 4) { 1085*a699b410SJean-Christophe Dubois buf_len += size - 4; 1086*a699b410SJean-Christophe Dubois } 1087*a699b410SJean-Christophe Dubois buf_addr = bd.data; 1088*a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); 1089*a699b410SJean-Christophe Dubois buf += buf_len; 1090*a699b410SJean-Christophe Dubois if (size < 4) { 1091*a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len, 1092*a699b410SJean-Christophe Dubois crc_ptr, 4 - size); 1093*a699b410SJean-Christophe Dubois crc_ptr += 4 - size; 1094*a699b410SJean-Christophe Dubois } 1095*a699b410SJean-Christophe Dubois bd.flags &= ~ENET_BD_E; 1096*a699b410SJean-Christophe Dubois if (size == 0) { 1097*a699b410SJean-Christophe Dubois /* Last buffer in frame. */ 1098*a699b410SJean-Christophe Dubois bd.flags |= flags | ENET_BD_L; 1099*a699b410SJean-Christophe Dubois FEC_PRINTF("rx frame flags %04x\n", bd.flags); 1100*a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_RX_INT) { 1101*a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXF; 1102*a699b410SJean-Christophe Dubois } 1103*a699b410SJean-Christophe Dubois } else { 1104*a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_RX_INT) { 1105*a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXB; 1106*a699b410SJean-Christophe Dubois } 1107*a699b410SJean-Christophe Dubois } 1108*a699b410SJean-Christophe Dubois imx_enet_write_bd(&bd, addr); 1109*a699b410SJean-Christophe Dubois /* Advance to the next descriptor. */ 1110*a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 1111*a699b410SJean-Christophe Dubois addr = s->regs[ENET_RDSR]; 1112*a699b410SJean-Christophe Dubois } else { 1113*a699b410SJean-Christophe Dubois addr += sizeof(bd); 1114*a699b410SJean-Christophe Dubois } 1115*a699b410SJean-Christophe Dubois } 1116*a699b410SJean-Christophe Dubois s->rx_descriptor = addr; 1117*a699b410SJean-Christophe Dubois imx_eth_enable_rx(s); 1118*a699b410SJean-Christophe Dubois imx_eth_update(s); 1119*a699b410SJean-Christophe Dubois return len; 1120*a699b410SJean-Christophe Dubois } 1121*a699b410SJean-Christophe Dubois 1122*a699b410SJean-Christophe Dubois static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf, 1123*a699b410SJean-Christophe Dubois size_t len) 1124*a699b410SJean-Christophe Dubois { 1125*a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1126*a699b410SJean-Christophe Dubois 1127*a699b410SJean-Christophe Dubois if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { 1128*a699b410SJean-Christophe Dubois return imx_enet_receive(nc, buf, len); 1129*a699b410SJean-Christophe Dubois } else { 1130*a699b410SJean-Christophe Dubois return imx_fec_receive(nc, buf, len); 1131*a699b410SJean-Christophe Dubois } 1132*a699b410SJean-Christophe Dubois } 1133*a699b410SJean-Christophe Dubois 1134*a699b410SJean-Christophe Dubois static const MemoryRegionOps imx_eth_ops = { 1135*a699b410SJean-Christophe Dubois .read = imx_eth_read, 1136*a699b410SJean-Christophe Dubois .write = imx_eth_write, 1137fcbd8018SJean-Christophe Dubois .valid.min_access_size = 4, 1138fcbd8018SJean-Christophe Dubois .valid.max_access_size = 4, 1139fcbd8018SJean-Christophe Dubois .endianness = DEVICE_NATIVE_ENDIAN, 1140fcbd8018SJean-Christophe Dubois }; 1141fcbd8018SJean-Christophe Dubois 1142*a699b410SJean-Christophe Dubois static void imx_eth_cleanup(NetClientState *nc) 1143fcbd8018SJean-Christophe Dubois { 1144fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1145fcbd8018SJean-Christophe Dubois 1146fcbd8018SJean-Christophe Dubois s->nic = NULL; 1147fcbd8018SJean-Christophe Dubois } 1148fcbd8018SJean-Christophe Dubois 1149*a699b410SJean-Christophe Dubois static NetClientInfo imx_eth_net_info = { 1150fcbd8018SJean-Christophe Dubois .type = NET_CLIENT_OPTIONS_KIND_NIC, 1151fcbd8018SJean-Christophe Dubois .size = sizeof(NICState), 1152*a699b410SJean-Christophe Dubois .can_receive = imx_eth_can_receive, 1153*a699b410SJean-Christophe Dubois .receive = imx_eth_receive, 1154*a699b410SJean-Christophe Dubois .cleanup = imx_eth_cleanup, 1155*a699b410SJean-Christophe Dubois .link_status_changed = imx_eth_set_link, 1156fcbd8018SJean-Christophe Dubois }; 1157fcbd8018SJean-Christophe Dubois 1158fcbd8018SJean-Christophe Dubois 1159*a699b410SJean-Christophe Dubois static void imx_eth_realize(DeviceState *dev, Error **errp) 1160fcbd8018SJean-Christophe Dubois { 1161fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(dev); 1162fcbd8018SJean-Christophe Dubois SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 1163fcbd8018SJean-Christophe Dubois 1164*a699b410SJean-Christophe Dubois memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s, 1165fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, 0x400); 1166fcbd8018SJean-Christophe Dubois sysbus_init_mmio(sbd, &s->iomem); 1167*a699b410SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq[0]); 1168*a699b410SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq[1]); 1169*a699b410SJean-Christophe Dubois 1170fcbd8018SJean-Christophe Dubois qemu_macaddr_default_if_unset(&s->conf.macaddr); 1171fcbd8018SJean-Christophe Dubois 1172fcbd8018SJean-Christophe Dubois s->conf.peers.ncs[0] = nd_table[0].netdev; 1173fcbd8018SJean-Christophe Dubois 1174*a699b410SJean-Christophe Dubois s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, 1175*a699b410SJean-Christophe Dubois object_get_typename(OBJECT(dev)), 1176*a699b410SJean-Christophe Dubois DEVICE(dev)->id, s); 1177*a699b410SJean-Christophe Dubois 1178fcbd8018SJean-Christophe Dubois qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 1179fcbd8018SJean-Christophe Dubois } 1180fcbd8018SJean-Christophe Dubois 1181*a699b410SJean-Christophe Dubois static Property imx_eth_properties[] = { 1182fcbd8018SJean-Christophe Dubois DEFINE_NIC_PROPERTIES(IMXFECState, conf), 1183fcbd8018SJean-Christophe Dubois DEFINE_PROP_END_OF_LIST(), 1184fcbd8018SJean-Christophe Dubois }; 1185fcbd8018SJean-Christophe Dubois 1186*a699b410SJean-Christophe Dubois static void imx_eth_class_init(ObjectClass *klass, void *data) 1187fcbd8018SJean-Christophe Dubois { 1188fcbd8018SJean-Christophe Dubois DeviceClass *dc = DEVICE_CLASS(klass); 1189fcbd8018SJean-Christophe Dubois 1190*a699b410SJean-Christophe Dubois dc->vmsd = &vmstate_imx_eth; 1191*a699b410SJean-Christophe Dubois dc->reset = imx_eth_reset; 1192*a699b410SJean-Christophe Dubois dc->props = imx_eth_properties; 1193*a699b410SJean-Christophe Dubois dc->realize = imx_eth_realize; 1194*a699b410SJean-Christophe Dubois dc->desc = "i.MX FEC/ENET Ethernet Controller"; 1195*a699b410SJean-Christophe Dubois } 1196*a699b410SJean-Christophe Dubois 1197*a699b410SJean-Christophe Dubois static void imx_fec_init(Object *obj) 1198*a699b410SJean-Christophe Dubois { 1199*a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(obj); 1200*a699b410SJean-Christophe Dubois 1201*a699b410SJean-Christophe Dubois s->is_fec = true; 1202*a699b410SJean-Christophe Dubois } 1203*a699b410SJean-Christophe Dubois 1204*a699b410SJean-Christophe Dubois static void imx_enet_init(Object *obj) 1205*a699b410SJean-Christophe Dubois { 1206*a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(obj); 1207*a699b410SJean-Christophe Dubois 1208*a699b410SJean-Christophe Dubois s->is_fec = false; 1209fcbd8018SJean-Christophe Dubois } 1210fcbd8018SJean-Christophe Dubois 1211fcbd8018SJean-Christophe Dubois static const TypeInfo imx_fec_info = { 1212fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC, 1213fcbd8018SJean-Christophe Dubois .parent = TYPE_SYS_BUS_DEVICE, 1214fcbd8018SJean-Christophe Dubois .instance_size = sizeof(IMXFECState), 1215*a699b410SJean-Christophe Dubois .instance_init = imx_fec_init, 1216*a699b410SJean-Christophe Dubois .class_init = imx_eth_class_init, 1217fcbd8018SJean-Christophe Dubois }; 1218fcbd8018SJean-Christophe Dubois 1219*a699b410SJean-Christophe Dubois static const TypeInfo imx_enet_info = { 1220*a699b410SJean-Christophe Dubois .name = TYPE_IMX_ENET, 1221*a699b410SJean-Christophe Dubois .parent = TYPE_IMX_FEC, 1222*a699b410SJean-Christophe Dubois .instance_init = imx_enet_init, 1223*a699b410SJean-Christophe Dubois }; 1224*a699b410SJean-Christophe Dubois 1225*a699b410SJean-Christophe Dubois static void imx_eth_register_types(void) 1226fcbd8018SJean-Christophe Dubois { 1227fcbd8018SJean-Christophe Dubois type_register_static(&imx_fec_info); 1228*a699b410SJean-Christophe Dubois type_register_static(&imx_enet_info); 1229fcbd8018SJean-Christophe Dubois } 1230fcbd8018SJean-Christophe Dubois 1231*a699b410SJean-Christophe Dubois type_init(imx_eth_register_types) 1232