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