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" 2564552b6bSMarkus Armbruster #include "hw/irq.h" 26fcbd8018SJean-Christophe Dubois #include "hw/net/imx_fec.h" 27a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 28d6454270SMarkus Armbruster #include "migration/vmstate.h" 29fcbd8018SJean-Christophe Dubois #include "sysemu/dma.h" 3003dd024fSPaolo Bonzini #include "qemu/log.h" 310b8fa32fSMarkus Armbruster #include "qemu/module.h" 32a699b410SJean-Christophe Dubois #include "net/checksum.h" 33a699b410SJean-Christophe Dubois #include "net/eth.h" 348095508aSJean-Christophe Dubois #include "trace.h" 35fcbd8018SJean-Christophe Dubois 36fcbd8018SJean-Christophe Dubois /* For crc32 */ 37fcbd8018SJean-Christophe Dubois #include <zlib.h> 38fcbd8018SJean-Christophe Dubois 3981f17e0dSPrasad J Pandit #define IMX_MAX_DESC 1024 4081f17e0dSPrasad J Pandit 41a699b410SJean-Christophe Dubois static const char *imx_default_reg_name(IMXFECState *s, uint32_t index) 42db0de352SJean-Christophe Dubois { 43db0de352SJean-Christophe Dubois static char tmp[20]; 44a699b410SJean-Christophe Dubois sprintf(tmp, "index %d", index); 45a699b410SJean-Christophe Dubois return tmp; 46a699b410SJean-Christophe Dubois } 47db0de352SJean-Christophe Dubois 48a699b410SJean-Christophe Dubois static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index) 49a699b410SJean-Christophe Dubois { 50a699b410SJean-Christophe Dubois switch (index) { 51a699b410SJean-Christophe Dubois case ENET_FRBR: 52a699b410SJean-Christophe Dubois return "FRBR"; 53a699b410SJean-Christophe Dubois case ENET_FRSR: 54a699b410SJean-Christophe Dubois return "FRSR"; 55a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR: 56a699b410SJean-Christophe Dubois return "MIIGSK_CFGR"; 57a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR: 58a699b410SJean-Christophe Dubois return "MIIGSK_ENR"; 59a699b410SJean-Christophe Dubois default: 60a699b410SJean-Christophe Dubois return imx_default_reg_name(s, index); 61a699b410SJean-Christophe Dubois } 62a699b410SJean-Christophe Dubois } 63a699b410SJean-Christophe Dubois 64a699b410SJean-Christophe Dubois static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index) 65a699b410SJean-Christophe Dubois { 66a699b410SJean-Christophe Dubois switch (index) { 67a699b410SJean-Christophe Dubois case ENET_RSFL: 68a699b410SJean-Christophe Dubois return "RSFL"; 69a699b410SJean-Christophe Dubois case ENET_RSEM: 70a699b410SJean-Christophe Dubois return "RSEM"; 71a699b410SJean-Christophe Dubois case ENET_RAEM: 72a699b410SJean-Christophe Dubois return "RAEM"; 73a699b410SJean-Christophe Dubois case ENET_RAFL: 74a699b410SJean-Christophe Dubois return "RAFL"; 75a699b410SJean-Christophe Dubois case ENET_TSEM: 76a699b410SJean-Christophe Dubois return "TSEM"; 77a699b410SJean-Christophe Dubois case ENET_TAEM: 78a699b410SJean-Christophe Dubois return "TAEM"; 79a699b410SJean-Christophe Dubois case ENET_TAFL: 80a699b410SJean-Christophe Dubois return "TAFL"; 81a699b410SJean-Christophe Dubois case ENET_TIPG: 82a699b410SJean-Christophe Dubois return "TIPG"; 83a699b410SJean-Christophe Dubois case ENET_FTRL: 84a699b410SJean-Christophe Dubois return "FTRL"; 85a699b410SJean-Christophe Dubois case ENET_TACC: 86a699b410SJean-Christophe Dubois return "TACC"; 87a699b410SJean-Christophe Dubois case ENET_RACC: 88a699b410SJean-Christophe Dubois return "RACC"; 89a699b410SJean-Christophe Dubois case ENET_ATCR: 90a699b410SJean-Christophe Dubois return "ATCR"; 91a699b410SJean-Christophe Dubois case ENET_ATVR: 92a699b410SJean-Christophe Dubois return "ATVR"; 93a699b410SJean-Christophe Dubois case ENET_ATOFF: 94a699b410SJean-Christophe Dubois return "ATOFF"; 95a699b410SJean-Christophe Dubois case ENET_ATPER: 96a699b410SJean-Christophe Dubois return "ATPER"; 97a699b410SJean-Christophe Dubois case ENET_ATCOR: 98a699b410SJean-Christophe Dubois return "ATCOR"; 99a699b410SJean-Christophe Dubois case ENET_ATINC: 100a699b410SJean-Christophe Dubois return "ATINC"; 101a699b410SJean-Christophe Dubois case ENET_ATSTMP: 102a699b410SJean-Christophe Dubois return "ATSTMP"; 103a699b410SJean-Christophe Dubois case ENET_TGSR: 104a699b410SJean-Christophe Dubois return "TGSR"; 105a699b410SJean-Christophe Dubois case ENET_TCSR0: 106a699b410SJean-Christophe Dubois return "TCSR0"; 107a699b410SJean-Christophe Dubois case ENET_TCCR0: 108a699b410SJean-Christophe Dubois return "TCCR0"; 109a699b410SJean-Christophe Dubois case ENET_TCSR1: 110a699b410SJean-Christophe Dubois return "TCSR1"; 111a699b410SJean-Christophe Dubois case ENET_TCCR1: 112a699b410SJean-Christophe Dubois return "TCCR1"; 113a699b410SJean-Christophe Dubois case ENET_TCSR2: 114a699b410SJean-Christophe Dubois return "TCSR2"; 115a699b410SJean-Christophe Dubois case ENET_TCCR2: 116a699b410SJean-Christophe Dubois return "TCCR2"; 117a699b410SJean-Christophe Dubois case ENET_TCSR3: 118a699b410SJean-Christophe Dubois return "TCSR3"; 119a699b410SJean-Christophe Dubois case ENET_TCCR3: 120a699b410SJean-Christophe Dubois return "TCCR3"; 121a699b410SJean-Christophe Dubois default: 122a699b410SJean-Christophe Dubois return imx_default_reg_name(s, index); 123a699b410SJean-Christophe Dubois } 124a699b410SJean-Christophe Dubois } 125a699b410SJean-Christophe Dubois 126a699b410SJean-Christophe Dubois static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index) 127a699b410SJean-Christophe Dubois { 128db0de352SJean-Christophe Dubois switch (index) { 129db0de352SJean-Christophe Dubois case ENET_EIR: 130db0de352SJean-Christophe Dubois return "EIR"; 131db0de352SJean-Christophe Dubois case ENET_EIMR: 132db0de352SJean-Christophe Dubois return "EIMR"; 133db0de352SJean-Christophe Dubois case ENET_RDAR: 134db0de352SJean-Christophe Dubois return "RDAR"; 135db0de352SJean-Christophe Dubois case ENET_TDAR: 136db0de352SJean-Christophe Dubois return "TDAR"; 137db0de352SJean-Christophe Dubois case ENET_ECR: 138db0de352SJean-Christophe Dubois return "ECR"; 139db0de352SJean-Christophe Dubois case ENET_MMFR: 140db0de352SJean-Christophe Dubois return "MMFR"; 141db0de352SJean-Christophe Dubois case ENET_MSCR: 142db0de352SJean-Christophe Dubois return "MSCR"; 143db0de352SJean-Christophe Dubois case ENET_MIBC: 144db0de352SJean-Christophe Dubois return "MIBC"; 145db0de352SJean-Christophe Dubois case ENET_RCR: 146db0de352SJean-Christophe Dubois return "RCR"; 147db0de352SJean-Christophe Dubois case ENET_TCR: 148db0de352SJean-Christophe Dubois return "TCR"; 149db0de352SJean-Christophe Dubois case ENET_PALR: 150db0de352SJean-Christophe Dubois return "PALR"; 151db0de352SJean-Christophe Dubois case ENET_PAUR: 152db0de352SJean-Christophe Dubois return "PAUR"; 153db0de352SJean-Christophe Dubois case ENET_OPD: 154db0de352SJean-Christophe Dubois return "OPD"; 155db0de352SJean-Christophe Dubois case ENET_IAUR: 156db0de352SJean-Christophe Dubois return "IAUR"; 157db0de352SJean-Christophe Dubois case ENET_IALR: 158db0de352SJean-Christophe Dubois return "IALR"; 159db0de352SJean-Christophe Dubois case ENET_GAUR: 160db0de352SJean-Christophe Dubois return "GAUR"; 161db0de352SJean-Christophe Dubois case ENET_GALR: 162db0de352SJean-Christophe Dubois return "GALR"; 163db0de352SJean-Christophe Dubois case ENET_TFWR: 164db0de352SJean-Christophe Dubois return "TFWR"; 165db0de352SJean-Christophe Dubois case ENET_RDSR: 166db0de352SJean-Christophe Dubois return "RDSR"; 167db0de352SJean-Christophe Dubois case ENET_TDSR: 168db0de352SJean-Christophe Dubois return "TDSR"; 169db0de352SJean-Christophe Dubois case ENET_MRBR: 170db0de352SJean-Christophe Dubois return "MRBR"; 171db0de352SJean-Christophe Dubois default: 172a699b410SJean-Christophe Dubois if (s->is_fec) { 173a699b410SJean-Christophe Dubois return imx_fec_reg_name(s, index); 174a699b410SJean-Christophe Dubois } else { 175a699b410SJean-Christophe Dubois return imx_enet_reg_name(s, index); 176a699b410SJean-Christophe Dubois } 177db0de352SJean-Christophe Dubois } 178db0de352SJean-Christophe Dubois } 179db0de352SJean-Christophe Dubois 180f93f961cSAndrey Smirnov /* 181f93f961cSAndrey Smirnov * Versions of this device with more than one TX descriptor save the 182f93f961cSAndrey Smirnov * 2nd and 3rd descriptors in a subsection, to maintain migration 183f93f961cSAndrey Smirnov * compatibility with previous versions of the device that only 184f93f961cSAndrey Smirnov * supported a single descriptor. 185f93f961cSAndrey Smirnov */ 186f93f961cSAndrey Smirnov static bool imx_eth_is_multi_tx_ring(void *opaque) 187f93f961cSAndrey Smirnov { 188f93f961cSAndrey Smirnov IMXFECState *s = IMX_FEC(opaque); 189f93f961cSAndrey Smirnov 190f93f961cSAndrey Smirnov return s->tx_ring_num > 1; 191f93f961cSAndrey Smirnov } 192f93f961cSAndrey Smirnov 193f93f961cSAndrey Smirnov static const VMStateDescription vmstate_imx_eth_txdescs = { 194f93f961cSAndrey Smirnov .name = "imx.fec/txdescs", 195f93f961cSAndrey Smirnov .version_id = 1, 196f93f961cSAndrey Smirnov .minimum_version_id = 1, 197f93f961cSAndrey Smirnov .needed = imx_eth_is_multi_tx_ring, 198f93f961cSAndrey Smirnov .fields = (VMStateField[]) { 199f93f961cSAndrey Smirnov VMSTATE_UINT32(tx_descriptor[1], IMXFECState), 200f93f961cSAndrey Smirnov VMSTATE_UINT32(tx_descriptor[2], IMXFECState), 201f93f961cSAndrey Smirnov VMSTATE_END_OF_LIST() 202f93f961cSAndrey Smirnov } 203f93f961cSAndrey Smirnov }; 204f93f961cSAndrey Smirnov 205a699b410SJean-Christophe Dubois static const VMStateDescription vmstate_imx_eth = { 206fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC, 207db0de352SJean-Christophe Dubois .version_id = 2, 208db0de352SJean-Christophe Dubois .minimum_version_id = 2, 209fcbd8018SJean-Christophe Dubois .fields = (VMStateField[]) { 210db0de352SJean-Christophe Dubois VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), 211fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(rx_descriptor, IMXFECState), 212f93f961cSAndrey Smirnov VMSTATE_UINT32(tx_descriptor[0], IMXFECState), 213fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_status, IMXFECState), 214fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_control, IMXFECState), 215fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_advertise, IMXFECState), 216fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_int, IMXFECState), 217fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_int_mask, IMXFECState), 218fcbd8018SJean-Christophe Dubois VMSTATE_END_OF_LIST() 219f93f961cSAndrey Smirnov }, 220f93f961cSAndrey Smirnov .subsections = (const VMStateDescription * []) { 221f93f961cSAndrey Smirnov &vmstate_imx_eth_txdescs, 222f93f961cSAndrey Smirnov NULL 223f93f961cSAndrey Smirnov }, 224fcbd8018SJean-Christophe Dubois }; 225fcbd8018SJean-Christophe Dubois 226fcbd8018SJean-Christophe Dubois #define PHY_INT_ENERGYON (1 << 7) 227fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_COMPLETE (1 << 6) 228fcbd8018SJean-Christophe Dubois #define PHY_INT_FAULT (1 << 5) 229fcbd8018SJean-Christophe Dubois #define PHY_INT_DOWN (1 << 4) 230fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_LP (1 << 3) 231fcbd8018SJean-Christophe Dubois #define PHY_INT_PARFAULT (1 << 2) 232fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_PAGE (1 << 1) 233fcbd8018SJean-Christophe Dubois 234a699b410SJean-Christophe Dubois static void imx_eth_update(IMXFECState *s); 235fcbd8018SJean-Christophe Dubois 236fcbd8018SJean-Christophe Dubois /* 237fcbd8018SJean-Christophe Dubois * The MII phy could raise a GPIO to the processor which in turn 238fcbd8018SJean-Christophe Dubois * could be handled as an interrpt by the OS. 239fcbd8018SJean-Christophe Dubois * For now we don't handle any GPIO/interrupt line, so the OS will 240fcbd8018SJean-Christophe Dubois * have to poll for the PHY status. 241fcbd8018SJean-Christophe Dubois */ 2428095508aSJean-Christophe Dubois static void imx_phy_update_irq(IMXFECState *s) 243fcbd8018SJean-Christophe Dubois { 244a699b410SJean-Christophe Dubois imx_eth_update(s); 245fcbd8018SJean-Christophe Dubois } 246fcbd8018SJean-Christophe Dubois 2478095508aSJean-Christophe Dubois static void imx_phy_update_link(IMXFECState *s) 248fcbd8018SJean-Christophe Dubois { 249fcbd8018SJean-Christophe Dubois /* Autonegotiation status mirrors link status. */ 250fcbd8018SJean-Christophe Dubois if (qemu_get_queue(s->nic)->link_down) { 2518095508aSJean-Christophe Dubois trace_imx_phy_update_link("down"); 252fcbd8018SJean-Christophe Dubois s->phy_status &= ~0x0024; 253fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_DOWN; 254fcbd8018SJean-Christophe Dubois } else { 2558095508aSJean-Christophe Dubois trace_imx_phy_update_link("up"); 256fcbd8018SJean-Christophe Dubois s->phy_status |= 0x0024; 257fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_ENERGYON; 258fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_AUTONEG_COMPLETE; 259fcbd8018SJean-Christophe Dubois } 2608095508aSJean-Christophe Dubois imx_phy_update_irq(s); 261fcbd8018SJean-Christophe Dubois } 262fcbd8018SJean-Christophe Dubois 263a699b410SJean-Christophe Dubois static void imx_eth_set_link(NetClientState *nc) 264fcbd8018SJean-Christophe Dubois { 2658095508aSJean-Christophe Dubois imx_phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); 266fcbd8018SJean-Christophe Dubois } 267fcbd8018SJean-Christophe Dubois 2688095508aSJean-Christophe Dubois static void imx_phy_reset(IMXFECState *s) 269fcbd8018SJean-Christophe Dubois { 2708095508aSJean-Christophe Dubois trace_imx_phy_reset(); 2718095508aSJean-Christophe Dubois 272fcbd8018SJean-Christophe Dubois s->phy_status = 0x7809; 273fcbd8018SJean-Christophe Dubois s->phy_control = 0x3000; 274fcbd8018SJean-Christophe Dubois s->phy_advertise = 0x01e1; 275fcbd8018SJean-Christophe Dubois s->phy_int_mask = 0; 276fcbd8018SJean-Christophe Dubois s->phy_int = 0; 2778095508aSJean-Christophe Dubois imx_phy_update_link(s); 278fcbd8018SJean-Christophe Dubois } 279fcbd8018SJean-Christophe Dubois 2808095508aSJean-Christophe Dubois static uint32_t imx_phy_read(IMXFECState *s, int reg) 281fcbd8018SJean-Christophe Dubois { 282fcbd8018SJean-Christophe Dubois uint32_t val; 283461c51adSJean-Christophe Dubois uint32_t phy = reg / 32; 284fcbd8018SJean-Christophe Dubois 285461c51adSJean-Christophe Dubois if (phy != s->phy_num) { 286*f607dce2SGuenter Roeck trace_imx_phy_read_num(phy, s->phy_num); 287*f607dce2SGuenter Roeck return 0xffff; 288fcbd8018SJean-Christophe Dubois } 289fcbd8018SJean-Christophe Dubois 290461c51adSJean-Christophe Dubois reg %= 32; 291461c51adSJean-Christophe Dubois 292fcbd8018SJean-Christophe Dubois switch (reg) { 293fcbd8018SJean-Christophe Dubois case 0: /* Basic Control */ 294fcbd8018SJean-Christophe Dubois val = s->phy_control; 295fcbd8018SJean-Christophe Dubois break; 296fcbd8018SJean-Christophe Dubois case 1: /* Basic Status */ 297fcbd8018SJean-Christophe Dubois val = s->phy_status; 298fcbd8018SJean-Christophe Dubois break; 299fcbd8018SJean-Christophe Dubois case 2: /* ID1 */ 300fcbd8018SJean-Christophe Dubois val = 0x0007; 301fcbd8018SJean-Christophe Dubois break; 302fcbd8018SJean-Christophe Dubois case 3: /* ID2 */ 303fcbd8018SJean-Christophe Dubois val = 0xc0d1; 304fcbd8018SJean-Christophe Dubois break; 305fcbd8018SJean-Christophe Dubois case 4: /* Auto-neg advertisement */ 306fcbd8018SJean-Christophe Dubois val = s->phy_advertise; 307fcbd8018SJean-Christophe Dubois break; 308fcbd8018SJean-Christophe Dubois case 5: /* Auto-neg Link Partner Ability */ 309fcbd8018SJean-Christophe Dubois val = 0x0f71; 310fcbd8018SJean-Christophe Dubois break; 311fcbd8018SJean-Christophe Dubois case 6: /* Auto-neg Expansion */ 312fcbd8018SJean-Christophe Dubois val = 1; 313fcbd8018SJean-Christophe Dubois break; 314fcbd8018SJean-Christophe Dubois case 29: /* Interrupt source. */ 315fcbd8018SJean-Christophe Dubois val = s->phy_int; 316fcbd8018SJean-Christophe Dubois s->phy_int = 0; 3178095508aSJean-Christophe Dubois imx_phy_update_irq(s); 318fcbd8018SJean-Christophe Dubois break; 319fcbd8018SJean-Christophe Dubois case 30: /* Interrupt mask */ 320fcbd8018SJean-Christophe Dubois val = s->phy_int_mask; 321fcbd8018SJean-Christophe Dubois break; 322fcbd8018SJean-Christophe Dubois case 17: 323fcbd8018SJean-Christophe Dubois case 18: 324fcbd8018SJean-Christophe Dubois case 27: 325fcbd8018SJean-Christophe Dubois case 31: 326b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", 327fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 328fcbd8018SJean-Christophe Dubois val = 0; 329fcbd8018SJean-Christophe Dubois break; 330fcbd8018SJean-Christophe Dubois default: 331b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", 332fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 333fcbd8018SJean-Christophe Dubois val = 0; 334fcbd8018SJean-Christophe Dubois break; 335fcbd8018SJean-Christophe Dubois } 336fcbd8018SJean-Christophe Dubois 337461c51adSJean-Christophe Dubois trace_imx_phy_read(val, phy, reg); 338fcbd8018SJean-Christophe Dubois 339fcbd8018SJean-Christophe Dubois return val; 340fcbd8018SJean-Christophe Dubois } 341fcbd8018SJean-Christophe Dubois 3428095508aSJean-Christophe Dubois static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) 343fcbd8018SJean-Christophe Dubois { 344461c51adSJean-Christophe Dubois uint32_t phy = reg / 32; 345fcbd8018SJean-Christophe Dubois 346461c51adSJean-Christophe Dubois if (phy != s->phy_num) { 347*f607dce2SGuenter Roeck trace_imx_phy_write_num(phy, s->phy_num); 348fcbd8018SJean-Christophe Dubois return; 349fcbd8018SJean-Christophe Dubois } 350fcbd8018SJean-Christophe Dubois 351461c51adSJean-Christophe Dubois reg %= 32; 352461c51adSJean-Christophe Dubois 353461c51adSJean-Christophe Dubois trace_imx_phy_write(val, phy, reg); 354461c51adSJean-Christophe Dubois 355fcbd8018SJean-Christophe Dubois switch (reg) { 356fcbd8018SJean-Christophe Dubois case 0: /* Basic Control */ 357fcbd8018SJean-Christophe Dubois if (val & 0x8000) { 3588095508aSJean-Christophe Dubois imx_phy_reset(s); 359fcbd8018SJean-Christophe Dubois } else { 360fcbd8018SJean-Christophe Dubois s->phy_control = val & 0x7980; 361fcbd8018SJean-Christophe Dubois /* Complete autonegotiation immediately. */ 362fcbd8018SJean-Christophe Dubois if (val & 0x1000) { 363fcbd8018SJean-Christophe Dubois s->phy_status |= 0x0020; 364fcbd8018SJean-Christophe Dubois } 365fcbd8018SJean-Christophe Dubois } 366fcbd8018SJean-Christophe Dubois break; 367fcbd8018SJean-Christophe Dubois case 4: /* Auto-neg advertisement */ 368fcbd8018SJean-Christophe Dubois s->phy_advertise = (val & 0x2d7f) | 0x80; 369fcbd8018SJean-Christophe Dubois break; 370fcbd8018SJean-Christophe Dubois case 30: /* Interrupt mask */ 371fcbd8018SJean-Christophe Dubois s->phy_int_mask = val & 0xff; 3728095508aSJean-Christophe Dubois imx_phy_update_irq(s); 373fcbd8018SJean-Christophe Dubois break; 374fcbd8018SJean-Christophe Dubois case 17: 375fcbd8018SJean-Christophe Dubois case 18: 376fcbd8018SJean-Christophe Dubois case 27: 377fcbd8018SJean-Christophe Dubois case 31: 378b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", 379fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 380fcbd8018SJean-Christophe Dubois break; 381fcbd8018SJean-Christophe Dubois default: 382b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", 383fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 384fcbd8018SJean-Christophe Dubois break; 385fcbd8018SJean-Christophe Dubois } 386fcbd8018SJean-Christophe Dubois } 387fcbd8018SJean-Christophe Dubois 388fcbd8018SJean-Christophe Dubois static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) 389fcbd8018SJean-Christophe Dubois { 390fcbd8018SJean-Christophe Dubois dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); 3918095508aSJean-Christophe Dubois 3928095508aSJean-Christophe Dubois trace_imx_fec_read_bd(addr, bd->flags, bd->length, bd->data); 393fcbd8018SJean-Christophe Dubois } 394fcbd8018SJean-Christophe Dubois 395fcbd8018SJean-Christophe Dubois static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) 396fcbd8018SJean-Christophe Dubois { 397fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); 398fcbd8018SJean-Christophe Dubois } 399fcbd8018SJean-Christophe Dubois 400a699b410SJean-Christophe Dubois static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr) 401fcbd8018SJean-Christophe Dubois { 402a699b410SJean-Christophe Dubois dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); 4038095508aSJean-Christophe Dubois 4048095508aSJean-Christophe Dubois trace_imx_enet_read_bd(addr, bd->flags, bd->length, bd->data, 4058095508aSJean-Christophe Dubois bd->option, bd->status); 406a699b410SJean-Christophe Dubois } 407a699b410SJean-Christophe Dubois 408a699b410SJean-Christophe Dubois static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr) 409a699b410SJean-Christophe Dubois { 410a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); 411a699b410SJean-Christophe Dubois } 412a699b410SJean-Christophe Dubois 413a699b410SJean-Christophe Dubois static void imx_eth_update(IMXFECState *s) 414a699b410SJean-Christophe Dubois { 4156461d7e2SGuenter Roeck /* 4166461d7e2SGuenter Roeck * Previous versions of qemu had the ENET_INT_MAC and ENET_INT_TS_TIMER 4176461d7e2SGuenter Roeck * interrupts swapped. This worked with older versions of Linux (4.14 4186461d7e2SGuenter Roeck * and older) since Linux associated both interrupt lines with Ethernet 4196461d7e2SGuenter Roeck * MAC interrupts. Specifically, 4206461d7e2SGuenter Roeck * - Linux 4.15 and later have separate interrupt handlers for the MAC and 4216461d7e2SGuenter Roeck * timer interrupts. Those versions of Linux fail with versions of QEMU 4226461d7e2SGuenter Roeck * with swapped interrupt assignments. 4236461d7e2SGuenter Roeck * - In linux 4.14, both interrupt lines were registered with the Ethernet 4246461d7e2SGuenter Roeck * MAC interrupt handler. As a result, all versions of qemu happen to 4256461d7e2SGuenter Roeck * work, though that is accidental. 4266461d7e2SGuenter Roeck * - In Linux 4.9 and older, the timer interrupt was registered directly 4276461d7e2SGuenter Roeck * with the Ethernet MAC interrupt handler. The MAC interrupt was 4286461d7e2SGuenter Roeck * redirected to a GPIO interrupt to work around erratum ERR006687. 4296461d7e2SGuenter Roeck * This was implemented using the SOC's IOMUX block. In qemu, this GPIO 4306461d7e2SGuenter Roeck * interrupt never fired since IOMUX is currently not supported in qemu. 4316461d7e2SGuenter Roeck * Linux instead received MAC interrupts on the timer interrupt. 4326461d7e2SGuenter Roeck * As a result, qemu versions with the swapped interrupt assignment work, 4336461d7e2SGuenter Roeck * albeit accidentally, but qemu versions with the correct interrupt 4346461d7e2SGuenter Roeck * assignment fail. 4356461d7e2SGuenter Roeck * 4366461d7e2SGuenter Roeck * To ensure that all versions of Linux work, generate ENET_INT_MAC 4376461d7e2SGuenter Roeck * interrrupts on both interrupt lines. This should be changed if and when 4386461d7e2SGuenter Roeck * qemu supports IOMUX. 4396461d7e2SGuenter Roeck */ 4406461d7e2SGuenter Roeck if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & 4416461d7e2SGuenter Roeck (ENET_INT_MAC | ENET_INT_TS_TIMER)) { 442a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[1], 1); 443db0de352SJean-Christophe Dubois } else { 444a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[1], 0); 445a699b410SJean-Christophe Dubois } 446a699b410SJean-Christophe Dubois 447a699b410SJean-Christophe Dubois if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_MAC) { 448a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[0], 1); 449a699b410SJean-Christophe Dubois } else { 450a699b410SJean-Christophe Dubois qemu_set_irq(s->irq[0], 0); 451fcbd8018SJean-Christophe Dubois } 452fcbd8018SJean-Christophe Dubois } 453fcbd8018SJean-Christophe Dubois 454fcbd8018SJean-Christophe Dubois static void imx_fec_do_tx(IMXFECState *s) 455fcbd8018SJean-Christophe Dubois { 45681f17e0dSPrasad J Pandit int frame_size = 0, descnt = 0; 4577bac20dcSAndrey Smirnov uint8_t *ptr = s->frame; 458f93f961cSAndrey Smirnov uint32_t addr = s->tx_descriptor[0]; 459fcbd8018SJean-Christophe Dubois 46081f17e0dSPrasad J Pandit while (descnt++ < IMX_MAX_DESC) { 461fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 462fcbd8018SJean-Christophe Dubois int len; 463fcbd8018SJean-Christophe Dubois 464fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr); 4651bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_R) == 0) { 4668095508aSJean-Christophe Dubois 467fcbd8018SJean-Christophe Dubois /* Run out of descriptors to transmit. */ 4688095508aSJean-Christophe Dubois trace_imx_eth_tx_bd_busy(); 4698095508aSJean-Christophe Dubois 470fcbd8018SJean-Christophe Dubois break; 471fcbd8018SJean-Christophe Dubois } 472fcbd8018SJean-Christophe Dubois len = bd.length; 4731bb3c371SJean-Christophe Dubois if (frame_size + len > ENET_MAX_FRAME_SIZE) { 4741bb3c371SJean-Christophe Dubois len = ENET_MAX_FRAME_SIZE - frame_size; 475db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_BABT; 476fcbd8018SJean-Christophe Dubois } 477fcbd8018SJean-Christophe Dubois dma_memory_read(&address_space_memory, bd.data, ptr, len); 478fcbd8018SJean-Christophe Dubois ptr += len; 479fcbd8018SJean-Christophe Dubois frame_size += len; 4801bb3c371SJean-Christophe Dubois if (bd.flags & ENET_BD_L) { 481fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */ 4827bac20dcSAndrey Smirnov qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size); 4837bac20dcSAndrey Smirnov ptr = s->frame; 484fcbd8018SJean-Christophe Dubois frame_size = 0; 485db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXF; 486fcbd8018SJean-Christophe Dubois } 487db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_TXB; 4881bb3c371SJean-Christophe Dubois bd.flags &= ~ENET_BD_R; 489fcbd8018SJean-Christophe Dubois /* Write back the modified descriptor. */ 490fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr); 491fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */ 4921bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 493db0de352SJean-Christophe Dubois addr = s->regs[ENET_TDSR]; 494fcbd8018SJean-Christophe Dubois } else { 495db0de352SJean-Christophe Dubois addr += sizeof(bd); 496fcbd8018SJean-Christophe Dubois } 497fcbd8018SJean-Christophe Dubois } 498fcbd8018SJean-Christophe Dubois 499f93f961cSAndrey Smirnov s->tx_descriptor[0] = addr; 500fcbd8018SJean-Christophe Dubois 501a699b410SJean-Christophe Dubois imx_eth_update(s); 502fcbd8018SJean-Christophe Dubois } 503fcbd8018SJean-Christophe Dubois 504f93f961cSAndrey Smirnov static void imx_enet_do_tx(IMXFECState *s, uint32_t index) 505a699b410SJean-Christophe Dubois { 50681f17e0dSPrasad J Pandit int frame_size = 0, descnt = 0; 507f93f961cSAndrey Smirnov 5087bac20dcSAndrey Smirnov uint8_t *ptr = s->frame; 509f93f961cSAndrey Smirnov uint32_t addr, int_txb, int_txf, tdsr; 510f93f961cSAndrey Smirnov size_t ring; 511f93f961cSAndrey Smirnov 512f93f961cSAndrey Smirnov switch (index) { 513f93f961cSAndrey Smirnov case ENET_TDAR: 514f93f961cSAndrey Smirnov ring = 0; 515f93f961cSAndrey Smirnov int_txb = ENET_INT_TXB; 516f93f961cSAndrey Smirnov int_txf = ENET_INT_TXF; 517f93f961cSAndrey Smirnov tdsr = ENET_TDSR; 518f93f961cSAndrey Smirnov break; 519f93f961cSAndrey Smirnov case ENET_TDAR1: 520f93f961cSAndrey Smirnov ring = 1; 521f93f961cSAndrey Smirnov int_txb = ENET_INT_TXB1; 522f93f961cSAndrey Smirnov int_txf = ENET_INT_TXF1; 523f93f961cSAndrey Smirnov tdsr = ENET_TDSR1; 524f93f961cSAndrey Smirnov break; 525f93f961cSAndrey Smirnov case ENET_TDAR2: 526f93f961cSAndrey Smirnov ring = 2; 527f93f961cSAndrey Smirnov int_txb = ENET_INT_TXB2; 528f93f961cSAndrey Smirnov int_txf = ENET_INT_TXF2; 529f93f961cSAndrey Smirnov tdsr = ENET_TDSR2; 530f93f961cSAndrey Smirnov break; 531f93f961cSAndrey Smirnov default: 532f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR, 533f93f961cSAndrey Smirnov "%s: bogus value for index %x\n", 534f93f961cSAndrey Smirnov __func__, index); 535f93f961cSAndrey Smirnov abort(); 536f93f961cSAndrey Smirnov break; 537f93f961cSAndrey Smirnov } 538f93f961cSAndrey Smirnov 539f93f961cSAndrey Smirnov addr = s->tx_descriptor[ring]; 540a699b410SJean-Christophe Dubois 54181f17e0dSPrasad J Pandit while (descnt++ < IMX_MAX_DESC) { 542a699b410SJean-Christophe Dubois IMXENETBufDesc bd; 543a699b410SJean-Christophe Dubois int len; 544a699b410SJean-Christophe Dubois 545a699b410SJean-Christophe Dubois imx_enet_read_bd(&bd, addr); 546a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_R) == 0) { 547a699b410SJean-Christophe Dubois /* Run out of descriptors to transmit. */ 5488095508aSJean-Christophe Dubois 5498095508aSJean-Christophe Dubois trace_imx_eth_tx_bd_busy(); 5508095508aSJean-Christophe Dubois 551a699b410SJean-Christophe Dubois break; 552a699b410SJean-Christophe Dubois } 553a699b410SJean-Christophe Dubois len = bd.length; 554a699b410SJean-Christophe Dubois if (frame_size + len > ENET_MAX_FRAME_SIZE) { 555a699b410SJean-Christophe Dubois len = ENET_MAX_FRAME_SIZE - frame_size; 556a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_BABT; 557a699b410SJean-Christophe Dubois } 558a699b410SJean-Christophe Dubois dma_memory_read(&address_space_memory, bd.data, ptr, len); 559a699b410SJean-Christophe Dubois ptr += len; 560a699b410SJean-Christophe Dubois frame_size += len; 561a699b410SJean-Christophe Dubois if (bd.flags & ENET_BD_L) { 562f5746335SBin Meng int csum = 0; 563f5746335SBin Meng 564a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_PINS) { 565f5746335SBin Meng csum |= (CSUM_TCP | CSUM_UDP); 566a699b410SJean-Christophe Dubois } 567a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_IINS) { 568f5746335SBin Meng csum |= CSUM_IP; 569a699b410SJean-Christophe Dubois } 570f5746335SBin Meng if (csum) { 571f5746335SBin Meng net_checksum_calculate(s->frame, frame_size, csum); 572a699b410SJean-Christophe Dubois } 573f5746335SBin Meng 574a699b410SJean-Christophe Dubois /* Last buffer in frame. */ 5757bac20dcSAndrey Smirnov 57652cfd584SAndrey Smirnov qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size); 5777bac20dcSAndrey Smirnov ptr = s->frame; 5787bac20dcSAndrey Smirnov 579a699b410SJean-Christophe Dubois frame_size = 0; 580a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_TX_INT) { 581f93f961cSAndrey Smirnov s->regs[ENET_EIR] |= int_txf; 582a699b410SJean-Christophe Dubois } 58388e1b59eSAaron Hill /* Indicate that we've updated the last buffer descriptor. */ 58488e1b59eSAaron Hill bd.last_buffer = ENET_BD_BDU; 585a699b410SJean-Christophe Dubois } 586a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_TX_INT) { 587f93f961cSAndrey Smirnov s->regs[ENET_EIR] |= int_txb; 588a699b410SJean-Christophe Dubois } 589a699b410SJean-Christophe Dubois bd.flags &= ~ENET_BD_R; 590a699b410SJean-Christophe Dubois /* Write back the modified descriptor. */ 591a699b410SJean-Christophe Dubois imx_enet_write_bd(&bd, addr); 592a699b410SJean-Christophe Dubois /* Advance to the next descriptor. */ 593a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 594f93f961cSAndrey Smirnov addr = s->regs[tdsr]; 595a699b410SJean-Christophe Dubois } else { 596a699b410SJean-Christophe Dubois addr += sizeof(bd); 597a699b410SJean-Christophe Dubois } 598a699b410SJean-Christophe Dubois } 599a699b410SJean-Christophe Dubois 600f93f961cSAndrey Smirnov s->tx_descriptor[ring] = addr; 601a699b410SJean-Christophe Dubois 602a699b410SJean-Christophe Dubois imx_eth_update(s); 603a699b410SJean-Christophe Dubois } 604a699b410SJean-Christophe Dubois 605f93f961cSAndrey Smirnov static void imx_eth_do_tx(IMXFECState *s, uint32_t index) 606a699b410SJean-Christophe Dubois { 607a699b410SJean-Christophe Dubois if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { 608f93f961cSAndrey Smirnov imx_enet_do_tx(s, index); 609a699b410SJean-Christophe Dubois } else { 610a699b410SJean-Christophe Dubois imx_fec_do_tx(s); 611a699b410SJean-Christophe Dubois } 612a699b410SJean-Christophe Dubois } 613a699b410SJean-Christophe Dubois 614b2b012afSAndrey Smirnov static void imx_eth_enable_rx(IMXFECState *s, bool flush) 615fcbd8018SJean-Christophe Dubois { 616fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 617fcbd8018SJean-Christophe Dubois 618fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, s->rx_descriptor); 619fcbd8018SJean-Christophe Dubois 6201b58d58fSJean-Christophe Dubois s->regs[ENET_RDAR] = (bd.flags & ENET_BD_E) ? ENET_RDAR_RDAR : 0; 621fcbd8018SJean-Christophe Dubois 6221b58d58fSJean-Christophe Dubois if (!s->regs[ENET_RDAR]) { 6238095508aSJean-Christophe Dubois trace_imx_eth_rx_bd_full(); 624b2b012afSAndrey Smirnov } else if (flush) { 625fcbd8018SJean-Christophe Dubois qemu_flush_queued_packets(qemu_get_queue(s->nic)); 626fcbd8018SJean-Christophe Dubois } 627fcbd8018SJean-Christophe Dubois } 628fcbd8018SJean-Christophe Dubois 629a699b410SJean-Christophe Dubois static void imx_eth_reset(DeviceState *d) 630fcbd8018SJean-Christophe Dubois { 631fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(d); 632fcbd8018SJean-Christophe Dubois 633a699b410SJean-Christophe Dubois /* Reset the Device */ 634db0de352SJean-Christophe Dubois memset(s->regs, 0, sizeof(s->regs)); 635db0de352SJean-Christophe Dubois s->regs[ENET_ECR] = 0xf0000000; 636db0de352SJean-Christophe Dubois s->regs[ENET_MIBC] = 0xc0000000; 637db0de352SJean-Christophe Dubois s->regs[ENET_RCR] = 0x05ee0001; 638db0de352SJean-Christophe Dubois s->regs[ENET_OPD] = 0x00010000; 639db0de352SJean-Christophe Dubois 640db0de352SJean-Christophe Dubois s->regs[ENET_PALR] = (s->conf.macaddr.a[0] << 24) 641db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[1] << 16) 642db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[2] << 8) 643db0de352SJean-Christophe Dubois | s->conf.macaddr.a[3]; 644db0de352SJean-Christophe Dubois s->regs[ENET_PAUR] = (s->conf.macaddr.a[4] << 24) 645db0de352SJean-Christophe Dubois | (s->conf.macaddr.a[5] << 16) 646db0de352SJean-Christophe Dubois | 0x8808; 647db0de352SJean-Christophe Dubois 648a699b410SJean-Christophe Dubois if (s->is_fec) { 649db0de352SJean-Christophe Dubois s->regs[ENET_FRBR] = 0x00000600; 650db0de352SJean-Christophe Dubois s->regs[ENET_FRSR] = 0x00000500; 651db0de352SJean-Christophe Dubois s->regs[ENET_MIIGSK_ENR] = 0x00000006; 652a699b410SJean-Christophe Dubois } else { 653a699b410SJean-Christophe Dubois s->regs[ENET_RAEM] = 0x00000004; 654a699b410SJean-Christophe Dubois s->regs[ENET_RAFL] = 0x00000004; 655a699b410SJean-Christophe Dubois s->regs[ENET_TAEM] = 0x00000004; 656a699b410SJean-Christophe Dubois s->regs[ENET_TAFL] = 0x00000008; 657a699b410SJean-Christophe Dubois s->regs[ENET_TIPG] = 0x0000000c; 658a699b410SJean-Christophe Dubois s->regs[ENET_FTRL] = 0x000007ff; 659a699b410SJean-Christophe Dubois s->regs[ENET_ATPER] = 0x3b9aca00; 660a699b410SJean-Christophe Dubois } 661db0de352SJean-Christophe Dubois 662db0de352SJean-Christophe Dubois s->rx_descriptor = 0; 663f93f961cSAndrey Smirnov memset(s->tx_descriptor, 0, sizeof(s->tx_descriptor)); 664fcbd8018SJean-Christophe Dubois 665fcbd8018SJean-Christophe Dubois /* We also reset the PHY */ 6668095508aSJean-Christophe Dubois imx_phy_reset(s); 667fcbd8018SJean-Christophe Dubois } 668fcbd8018SJean-Christophe Dubois 669a699b410SJean-Christophe Dubois static uint32_t imx_default_read(IMXFECState *s, uint32_t index) 670a699b410SJean-Christophe Dubois { 671a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 672a699b410SJean-Christophe Dubois PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); 673a699b410SJean-Christophe Dubois return 0; 674a699b410SJean-Christophe Dubois } 675a699b410SJean-Christophe Dubois 676a699b410SJean-Christophe Dubois static uint32_t imx_fec_read(IMXFECState *s, uint32_t index) 677a699b410SJean-Christophe Dubois { 678a699b410SJean-Christophe Dubois switch (index) { 679a699b410SJean-Christophe Dubois case ENET_FRBR: 680a699b410SJean-Christophe Dubois case ENET_FRSR: 681a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR: 682a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR: 683a699b410SJean-Christophe Dubois return s->regs[index]; 684a699b410SJean-Christophe Dubois default: 685a699b410SJean-Christophe Dubois return imx_default_read(s, index); 686a699b410SJean-Christophe Dubois } 687a699b410SJean-Christophe Dubois } 688a699b410SJean-Christophe Dubois 689a699b410SJean-Christophe Dubois static uint32_t imx_enet_read(IMXFECState *s, uint32_t index) 690a699b410SJean-Christophe Dubois { 691a699b410SJean-Christophe Dubois switch (index) { 692a699b410SJean-Christophe Dubois case ENET_RSFL: 693a699b410SJean-Christophe Dubois case ENET_RSEM: 694a699b410SJean-Christophe Dubois case ENET_RAEM: 695a699b410SJean-Christophe Dubois case ENET_RAFL: 696a699b410SJean-Christophe Dubois case ENET_TSEM: 697a699b410SJean-Christophe Dubois case ENET_TAEM: 698a699b410SJean-Christophe Dubois case ENET_TAFL: 699a699b410SJean-Christophe Dubois case ENET_TIPG: 700a699b410SJean-Christophe Dubois case ENET_FTRL: 701a699b410SJean-Christophe Dubois case ENET_TACC: 702a699b410SJean-Christophe Dubois case ENET_RACC: 703a699b410SJean-Christophe Dubois case ENET_ATCR: 704a699b410SJean-Christophe Dubois case ENET_ATVR: 705a699b410SJean-Christophe Dubois case ENET_ATOFF: 706a699b410SJean-Christophe Dubois case ENET_ATPER: 707a699b410SJean-Christophe Dubois case ENET_ATCOR: 708a699b410SJean-Christophe Dubois case ENET_ATINC: 709a699b410SJean-Christophe Dubois case ENET_ATSTMP: 710a699b410SJean-Christophe Dubois case ENET_TGSR: 711a699b410SJean-Christophe Dubois case ENET_TCSR0: 712a699b410SJean-Christophe Dubois case ENET_TCCR0: 713a699b410SJean-Christophe Dubois case ENET_TCSR1: 714a699b410SJean-Christophe Dubois case ENET_TCCR1: 715a699b410SJean-Christophe Dubois case ENET_TCSR2: 716a699b410SJean-Christophe Dubois case ENET_TCCR2: 717a699b410SJean-Christophe Dubois case ENET_TCSR3: 718a699b410SJean-Christophe Dubois case ENET_TCCR3: 719a699b410SJean-Christophe Dubois return s->regs[index]; 720a699b410SJean-Christophe Dubois default: 721a699b410SJean-Christophe Dubois return imx_default_read(s, index); 722a699b410SJean-Christophe Dubois } 723a699b410SJean-Christophe Dubois } 724a699b410SJean-Christophe Dubois 725a699b410SJean-Christophe Dubois static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size) 726fcbd8018SJean-Christophe Dubois { 727db0de352SJean-Christophe Dubois uint32_t value = 0; 728fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque); 729a699b410SJean-Christophe Dubois uint32_t index = offset >> 2; 730fcbd8018SJean-Christophe Dubois 731db0de352SJean-Christophe Dubois switch (index) { 732db0de352SJean-Christophe Dubois case ENET_EIR: 733db0de352SJean-Christophe Dubois case ENET_EIMR: 734db0de352SJean-Christophe Dubois case ENET_RDAR: 735db0de352SJean-Christophe Dubois case ENET_TDAR: 736db0de352SJean-Christophe Dubois case ENET_ECR: 737db0de352SJean-Christophe Dubois case ENET_MMFR: 738db0de352SJean-Christophe Dubois case ENET_MSCR: 739db0de352SJean-Christophe Dubois case ENET_MIBC: 740db0de352SJean-Christophe Dubois case ENET_RCR: 741db0de352SJean-Christophe Dubois case ENET_TCR: 742db0de352SJean-Christophe Dubois case ENET_PALR: 743db0de352SJean-Christophe Dubois case ENET_PAUR: 744db0de352SJean-Christophe Dubois case ENET_OPD: 745db0de352SJean-Christophe Dubois case ENET_IAUR: 746db0de352SJean-Christophe Dubois case ENET_IALR: 747db0de352SJean-Christophe Dubois case ENET_GAUR: 748db0de352SJean-Christophe Dubois case ENET_GALR: 749db0de352SJean-Christophe Dubois case ENET_TFWR: 750db0de352SJean-Christophe Dubois case ENET_RDSR: 751db0de352SJean-Christophe Dubois case ENET_TDSR: 752db0de352SJean-Christophe Dubois case ENET_MRBR: 753db0de352SJean-Christophe Dubois value = s->regs[index]; 754fcbd8018SJean-Christophe Dubois break; 755fcbd8018SJean-Christophe Dubois default: 756a699b410SJean-Christophe Dubois if (s->is_fec) { 757a699b410SJean-Christophe Dubois value = imx_fec_read(s, index); 758a699b410SJean-Christophe Dubois } else { 759a699b410SJean-Christophe Dubois value = imx_enet_read(s, index); 760a699b410SJean-Christophe Dubois } 761db0de352SJean-Christophe Dubois break; 762fcbd8018SJean-Christophe Dubois } 763db0de352SJean-Christophe Dubois 7648095508aSJean-Christophe Dubois trace_imx_eth_read(index, imx_eth_reg_name(s, index), value); 765db0de352SJean-Christophe Dubois 766db0de352SJean-Christophe Dubois return value; 767fcbd8018SJean-Christophe Dubois } 768fcbd8018SJean-Christophe Dubois 769a699b410SJean-Christophe Dubois static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value) 770a699b410SJean-Christophe Dubois { 771a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" 772a699b410SJean-Christophe Dubois PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); 773a699b410SJean-Christophe Dubois return; 774a699b410SJean-Christophe Dubois } 775a699b410SJean-Christophe Dubois 776a699b410SJean-Christophe Dubois static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value) 777a699b410SJean-Christophe Dubois { 778a699b410SJean-Christophe Dubois switch (index) { 779a699b410SJean-Christophe Dubois case ENET_FRBR: 780a699b410SJean-Christophe Dubois /* FRBR is read only */ 781a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n", 782a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 783a699b410SJean-Christophe Dubois break; 784a699b410SJean-Christophe Dubois case ENET_FRSR: 785a699b410SJean-Christophe Dubois s->regs[index] = (value & 0x000003fc) | 0x00000400; 786a699b410SJean-Christophe Dubois break; 787a699b410SJean-Christophe Dubois case ENET_MIIGSK_CFGR: 788a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00000053; 789a699b410SJean-Christophe Dubois break; 790a699b410SJean-Christophe Dubois case ENET_MIIGSK_ENR: 791a699b410SJean-Christophe Dubois s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0; 792a699b410SJean-Christophe Dubois break; 793a699b410SJean-Christophe Dubois default: 794a699b410SJean-Christophe Dubois imx_default_write(s, index, value); 795a699b410SJean-Christophe Dubois break; 796a699b410SJean-Christophe Dubois } 797a699b410SJean-Christophe Dubois } 798a699b410SJean-Christophe Dubois 799a699b410SJean-Christophe Dubois static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) 800a699b410SJean-Christophe Dubois { 801a699b410SJean-Christophe Dubois switch (index) { 802a699b410SJean-Christophe Dubois case ENET_RSFL: 803a699b410SJean-Christophe Dubois case ENET_RSEM: 804a699b410SJean-Christophe Dubois case ENET_RAEM: 805a699b410SJean-Christophe Dubois case ENET_RAFL: 806a699b410SJean-Christophe Dubois case ENET_TSEM: 807a699b410SJean-Christophe Dubois case ENET_TAEM: 808a699b410SJean-Christophe Dubois case ENET_TAFL: 809a699b410SJean-Christophe Dubois s->regs[index] = value & 0x000001ff; 810a699b410SJean-Christophe Dubois break; 811a699b410SJean-Christophe Dubois case ENET_TIPG: 812a699b410SJean-Christophe Dubois s->regs[index] = value & 0x0000001f; 813a699b410SJean-Christophe Dubois break; 814a699b410SJean-Christophe Dubois case ENET_FTRL: 815a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00003fff; 816a699b410SJean-Christophe Dubois break; 817a699b410SJean-Christophe Dubois case ENET_TACC: 818a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00000019; 819a699b410SJean-Christophe Dubois break; 820a699b410SJean-Christophe Dubois case ENET_RACC: 821a699b410SJean-Christophe Dubois s->regs[index] = value & 0x000000C7; 822a699b410SJean-Christophe Dubois break; 823a699b410SJean-Christophe Dubois case ENET_ATCR: 824a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00002a9d; 825a699b410SJean-Christophe Dubois break; 826a699b410SJean-Christophe Dubois case ENET_ATVR: 827a699b410SJean-Christophe Dubois case ENET_ATOFF: 828a699b410SJean-Christophe Dubois case ENET_ATPER: 829a699b410SJean-Christophe Dubois s->regs[index] = value; 830a699b410SJean-Christophe Dubois break; 831a699b410SJean-Christophe Dubois case ENET_ATSTMP: 832a699b410SJean-Christophe Dubois /* ATSTMP is read only */ 833a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n", 834a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 835a699b410SJean-Christophe Dubois break; 836a699b410SJean-Christophe Dubois case ENET_ATCOR: 837a699b410SJean-Christophe Dubois s->regs[index] = value & 0x7fffffff; 838a699b410SJean-Christophe Dubois break; 839a699b410SJean-Christophe Dubois case ENET_ATINC: 840a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00007f7f; 841a699b410SJean-Christophe Dubois break; 842a699b410SJean-Christophe Dubois case ENET_TGSR: 843a699b410SJean-Christophe Dubois /* implement clear timer flag */ 844a510d0c1SChen Qun s->regs[index] &= ~(value & 0x0000000f); /* all bits W1C */ 845a699b410SJean-Christophe Dubois break; 846a699b410SJean-Christophe Dubois case ENET_TCSR0: 847a699b410SJean-Christophe Dubois case ENET_TCSR1: 848a699b410SJean-Christophe Dubois case ENET_TCSR2: 849a699b410SJean-Christophe Dubois case ENET_TCSR3: 850a510d0c1SChen Qun s->regs[index] &= ~(value & 0x00000080); /* W1C bits */ 851a510d0c1SChen Qun s->regs[index] &= ~0x0000007d; /* writable fields */ 852a510d0c1SChen Qun s->regs[index] |= (value & 0x0000007d); 853a699b410SJean-Christophe Dubois break; 854a699b410SJean-Christophe Dubois case ENET_TCCR0: 855a699b410SJean-Christophe Dubois case ENET_TCCR1: 856a699b410SJean-Christophe Dubois case ENET_TCCR2: 857a699b410SJean-Christophe Dubois case ENET_TCCR3: 858a699b410SJean-Christophe Dubois s->regs[index] = value; 859a699b410SJean-Christophe Dubois break; 860a699b410SJean-Christophe Dubois default: 861a699b410SJean-Christophe Dubois imx_default_write(s, index, value); 862a699b410SJean-Christophe Dubois break; 863a699b410SJean-Christophe Dubois } 864a699b410SJean-Christophe Dubois } 865a699b410SJean-Christophe Dubois 866a699b410SJean-Christophe Dubois static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, 867a699b410SJean-Christophe Dubois unsigned size) 868fcbd8018SJean-Christophe Dubois { 869fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque); 870f93f961cSAndrey Smirnov const bool single_tx_ring = !imx_eth_is_multi_tx_ring(s); 871a699b410SJean-Christophe Dubois uint32_t index = offset >> 2; 872fcbd8018SJean-Christophe Dubois 8738095508aSJean-Christophe Dubois trace_imx_eth_write(index, imx_eth_reg_name(s, index), value); 874fcbd8018SJean-Christophe Dubois 875db0de352SJean-Christophe Dubois switch (index) { 876db0de352SJean-Christophe Dubois case ENET_EIR: 877db0de352SJean-Christophe Dubois s->regs[index] &= ~value; 878fcbd8018SJean-Christophe Dubois break; 879db0de352SJean-Christophe Dubois case ENET_EIMR: 880db0de352SJean-Christophe Dubois s->regs[index] = value; 881fcbd8018SJean-Christophe Dubois break; 882db0de352SJean-Christophe Dubois case ENET_RDAR: 883db0de352SJean-Christophe Dubois if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { 884db0de352SJean-Christophe Dubois if (!s->regs[index]) { 885b2b012afSAndrey Smirnov imx_eth_enable_rx(s, true); 886fcbd8018SJean-Christophe Dubois } 887db0de352SJean-Christophe Dubois } else { 888db0de352SJean-Christophe Dubois s->regs[index] = 0; 889db0de352SJean-Christophe Dubois } 890fcbd8018SJean-Christophe Dubois break; 8917c45c1d3SPhilippe Mathieu-Daudé case ENET_TDAR1: 8927c45c1d3SPhilippe Mathieu-Daudé case ENET_TDAR2: 893f93f961cSAndrey Smirnov if (unlikely(single_tx_ring)) { 894f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR, 895f93f961cSAndrey Smirnov "[%s]%s: trying to access TDAR2 or TDAR1\n", 896f93f961cSAndrey Smirnov TYPE_IMX_FEC, __func__); 897f93f961cSAndrey Smirnov return; 898f93f961cSAndrey Smirnov } 899174c556cSPhilippe Mathieu-Daudé /* fall through */ 900174c556cSPhilippe Mathieu-Daudé case ENET_TDAR: 901db0de352SJean-Christophe Dubois if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { 902db0de352SJean-Christophe Dubois s->regs[index] = ENET_TDAR_TDAR; 903f93f961cSAndrey Smirnov imx_eth_do_tx(s, index); 904fcbd8018SJean-Christophe Dubois } 905db0de352SJean-Christophe Dubois s->regs[index] = 0; 906fcbd8018SJean-Christophe Dubois break; 907db0de352SJean-Christophe Dubois case ENET_ECR: 9081bb3c371SJean-Christophe Dubois if (value & ENET_ECR_RESET) { 909a699b410SJean-Christophe Dubois return imx_eth_reset(DEVICE(s)); 910fcbd8018SJean-Christophe Dubois } 911db0de352SJean-Christophe Dubois s->regs[index] = value; 912db0de352SJean-Christophe Dubois if ((s->regs[index] & ENET_ECR_ETHEREN) == 0) { 913db0de352SJean-Christophe Dubois s->regs[ENET_RDAR] = 0; 914db0de352SJean-Christophe Dubois s->rx_descriptor = s->regs[ENET_RDSR]; 915db0de352SJean-Christophe Dubois s->regs[ENET_TDAR] = 0; 916f93f961cSAndrey Smirnov s->regs[ENET_TDAR1] = 0; 917f93f961cSAndrey Smirnov s->regs[ENET_TDAR2] = 0; 918f93f961cSAndrey Smirnov s->tx_descriptor[0] = s->regs[ENET_TDSR]; 919f93f961cSAndrey Smirnov s->tx_descriptor[1] = s->regs[ENET_TDSR1]; 920f93f961cSAndrey Smirnov s->tx_descriptor[2] = s->regs[ENET_TDSR2]; 921fcbd8018SJean-Christophe Dubois } 922fcbd8018SJean-Christophe Dubois break; 923db0de352SJean-Christophe Dubois case ENET_MMFR: 924db0de352SJean-Christophe Dubois s->regs[index] = value; 9254816dc16SJean-Christophe Dubois if (extract32(value, 29, 1)) { 926db0de352SJean-Christophe Dubois /* This is a read operation */ 927db0de352SJean-Christophe Dubois s->regs[ENET_MMFR] = deposit32(s->regs[ENET_MMFR], 0, 16, 9288095508aSJean-Christophe Dubois imx_phy_read(s, 929db0de352SJean-Christophe Dubois extract32(value, 930db0de352SJean-Christophe Dubois 18, 10))); 9314816dc16SJean-Christophe Dubois } else { 932461c51adSJean-Christophe Dubois /* This is a write operation */ 9338095508aSJean-Christophe Dubois imx_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16)); 934fcbd8018SJean-Christophe Dubois } 935fcbd8018SJean-Christophe Dubois /* raise the interrupt as the PHY operation is done */ 936db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_MII; 937fcbd8018SJean-Christophe Dubois break; 938db0de352SJean-Christophe Dubois case ENET_MSCR: 939db0de352SJean-Christophe Dubois s->regs[index] = value & 0xfe; 940fcbd8018SJean-Christophe Dubois break; 941db0de352SJean-Christophe Dubois case ENET_MIBC: 942fcbd8018SJean-Christophe Dubois /* TODO: Implement MIB. */ 943db0de352SJean-Christophe Dubois s->regs[index] = (value & 0x80000000) ? 0xc0000000 : 0; 944fcbd8018SJean-Christophe Dubois break; 945db0de352SJean-Christophe Dubois case ENET_RCR: 946db0de352SJean-Christophe Dubois s->regs[index] = value & 0x07ff003f; 947fcbd8018SJean-Christophe Dubois /* TODO: Implement LOOP mode. */ 948fcbd8018SJean-Christophe Dubois break; 949db0de352SJean-Christophe Dubois case ENET_TCR: 950fcbd8018SJean-Christophe Dubois /* We transmit immediately, so raise GRA immediately. */ 951db0de352SJean-Christophe Dubois s->regs[index] = value; 952fcbd8018SJean-Christophe Dubois if (value & 1) { 953db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_GRA; 954fcbd8018SJean-Christophe Dubois } 955fcbd8018SJean-Christophe Dubois break; 956db0de352SJean-Christophe Dubois case ENET_PALR: 957db0de352SJean-Christophe Dubois s->regs[index] = value; 958fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[0] = value >> 24; 959fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[1] = value >> 16; 960fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[2] = value >> 8; 961fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[3] = value; 962fcbd8018SJean-Christophe Dubois break; 963db0de352SJean-Christophe Dubois case ENET_PAUR: 964db0de352SJean-Christophe Dubois s->regs[index] = (value | 0x0000ffff) & 0xffff8808; 965fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[4] = value >> 24; 966fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[5] = value >> 16; 967fcbd8018SJean-Christophe Dubois break; 968db0de352SJean-Christophe Dubois case ENET_OPD: 969db0de352SJean-Christophe Dubois s->regs[index] = (value & 0x0000ffff) | 0x00010000; 970fcbd8018SJean-Christophe Dubois break; 971db0de352SJean-Christophe Dubois case ENET_IAUR: 972db0de352SJean-Christophe Dubois case ENET_IALR: 973db0de352SJean-Christophe Dubois case ENET_GAUR: 974db0de352SJean-Christophe Dubois case ENET_GALR: 975fcbd8018SJean-Christophe Dubois /* TODO: implement MAC hash filtering. */ 976fcbd8018SJean-Christophe Dubois break; 977db0de352SJean-Christophe Dubois case ENET_TFWR: 978a699b410SJean-Christophe Dubois if (s->is_fec) { 979a699b410SJean-Christophe Dubois s->regs[index] = value & 0x3; 980a699b410SJean-Christophe Dubois } else { 981a699b410SJean-Christophe Dubois s->regs[index] = value & 0x13f; 982a699b410SJean-Christophe Dubois } 983fcbd8018SJean-Christophe Dubois break; 984db0de352SJean-Christophe Dubois case ENET_RDSR: 985a699b410SJean-Christophe Dubois if (s->is_fec) { 986db0de352SJean-Christophe Dubois s->regs[index] = value & ~3; 987a699b410SJean-Christophe Dubois } else { 988a699b410SJean-Christophe Dubois s->regs[index] = value & ~7; 989a699b410SJean-Christophe Dubois } 990db0de352SJean-Christophe Dubois s->rx_descriptor = s->regs[index]; 991fcbd8018SJean-Christophe Dubois break; 992db0de352SJean-Christophe Dubois case ENET_TDSR: 993a699b410SJean-Christophe Dubois if (s->is_fec) { 994db0de352SJean-Christophe Dubois s->regs[index] = value & ~3; 995a699b410SJean-Christophe Dubois } else { 996a699b410SJean-Christophe Dubois s->regs[index] = value & ~7; 997a699b410SJean-Christophe Dubois } 998f93f961cSAndrey Smirnov s->tx_descriptor[0] = s->regs[index]; 999f93f961cSAndrey Smirnov break; 1000f93f961cSAndrey Smirnov case ENET_TDSR1: 1001f93f961cSAndrey Smirnov if (unlikely(single_tx_ring)) { 1002f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR, 1003f93f961cSAndrey Smirnov "[%s]%s: trying to access TDSR1\n", 1004f93f961cSAndrey Smirnov TYPE_IMX_FEC, __func__); 1005f93f961cSAndrey Smirnov return; 1006f93f961cSAndrey Smirnov } 1007f93f961cSAndrey Smirnov 1008f93f961cSAndrey Smirnov s->regs[index] = value & ~7; 1009f93f961cSAndrey Smirnov s->tx_descriptor[1] = s->regs[index]; 1010f93f961cSAndrey Smirnov break; 1011f93f961cSAndrey Smirnov case ENET_TDSR2: 1012f93f961cSAndrey Smirnov if (unlikely(single_tx_ring)) { 1013f93f961cSAndrey Smirnov qemu_log_mask(LOG_GUEST_ERROR, 1014f93f961cSAndrey Smirnov "[%s]%s: trying to access TDSR2\n", 1015f93f961cSAndrey Smirnov TYPE_IMX_FEC, __func__); 1016f93f961cSAndrey Smirnov return; 1017f93f961cSAndrey Smirnov } 1018f93f961cSAndrey Smirnov 1019f93f961cSAndrey Smirnov s->regs[index] = value & ~7; 1020f93f961cSAndrey Smirnov s->tx_descriptor[2] = s->regs[index]; 1021fcbd8018SJean-Christophe Dubois break; 1022db0de352SJean-Christophe Dubois case ENET_MRBR: 1023a699b410SJean-Christophe Dubois s->regs[index] = value & 0x00003ff0; 1024fcbd8018SJean-Christophe Dubois break; 1025fcbd8018SJean-Christophe Dubois default: 1026a699b410SJean-Christophe Dubois if (s->is_fec) { 1027a699b410SJean-Christophe Dubois imx_fec_write(s, index, value); 1028a699b410SJean-Christophe Dubois } else { 1029a699b410SJean-Christophe Dubois imx_enet_write(s, index, value); 1030a699b410SJean-Christophe Dubois } 1031a699b410SJean-Christophe Dubois return; 1032fcbd8018SJean-Christophe Dubois } 1033fcbd8018SJean-Christophe Dubois 1034a699b410SJean-Christophe Dubois imx_eth_update(s); 1035fcbd8018SJean-Christophe Dubois } 1036fcbd8018SJean-Christophe Dubois 1037b8c4b67eSPhilippe Mathieu-Daudé static bool imx_eth_can_receive(NetClientState *nc) 1038fcbd8018SJean-Christophe Dubois { 1039fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1040fcbd8018SJean-Christophe Dubois 1041b2b012afSAndrey Smirnov return !!s->regs[ENET_RDAR]; 1042fcbd8018SJean-Christophe Dubois } 1043fcbd8018SJean-Christophe Dubois 1044fcbd8018SJean-Christophe Dubois static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, 1045fcbd8018SJean-Christophe Dubois size_t len) 1046fcbd8018SJean-Christophe Dubois { 1047fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1048fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 1049fcbd8018SJean-Christophe Dubois uint32_t flags = 0; 1050fcbd8018SJean-Christophe Dubois uint32_t addr; 1051fcbd8018SJean-Christophe Dubois uint32_t crc; 1052fcbd8018SJean-Christophe Dubois uint32_t buf_addr; 1053fcbd8018SJean-Christophe Dubois uint8_t *crc_ptr; 1054fcbd8018SJean-Christophe Dubois unsigned int buf_len; 1055fcbd8018SJean-Christophe Dubois size_t size = len; 1056fcbd8018SJean-Christophe Dubois 10578095508aSJean-Christophe Dubois trace_imx_fec_receive(size); 1058fcbd8018SJean-Christophe Dubois 1059db0de352SJean-Christophe Dubois if (!s->regs[ENET_RDAR]) { 1060b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", 1061fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 1062fcbd8018SJean-Christophe Dubois return 0; 1063fcbd8018SJean-Christophe Dubois } 1064fcbd8018SJean-Christophe Dubois 1065fcbd8018SJean-Christophe Dubois /* 4 bytes for the CRC. */ 1066fcbd8018SJean-Christophe Dubois size += 4; 1067fcbd8018SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size)); 1068fcbd8018SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc; 1069fcbd8018SJean-Christophe Dubois 1070a699b410SJean-Christophe Dubois /* Huge frames are truncated. */ 10711bb3c371SJean-Christophe Dubois if (size > ENET_MAX_FRAME_SIZE) { 10721bb3c371SJean-Christophe Dubois size = ENET_MAX_FRAME_SIZE; 10731bb3c371SJean-Christophe Dubois flags |= ENET_BD_TR | ENET_BD_LG; 1074fcbd8018SJean-Christophe Dubois } 1075fcbd8018SJean-Christophe Dubois 1076fcbd8018SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */ 1077db0de352SJean-Christophe Dubois if (size > (s->regs[ENET_RCR] >> 16)) { 10781bb3c371SJean-Christophe Dubois flags |= ENET_BD_LG; 1079fcbd8018SJean-Christophe Dubois } 1080fcbd8018SJean-Christophe Dubois 1081fcbd8018SJean-Christophe Dubois addr = s->rx_descriptor; 1082fcbd8018SJean-Christophe Dubois while (size > 0) { 1083fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr); 10841bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_E) == 0) { 1085fcbd8018SJean-Christophe Dubois /* No descriptors available. Bail out. */ 1086fcbd8018SJean-Christophe Dubois /* 1087fcbd8018SJean-Christophe Dubois * FIXME: This is wrong. We should probably either 1088fcbd8018SJean-Christophe Dubois * save the remainder for when more RX buffers are 1089fcbd8018SJean-Christophe Dubois * available, or flag an error. 1090fcbd8018SJean-Christophe Dubois */ 1091b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", 1092fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 1093fcbd8018SJean-Christophe Dubois break; 1094fcbd8018SJean-Christophe Dubois } 1095db0de352SJean-Christophe Dubois buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR]; 1096fcbd8018SJean-Christophe Dubois bd.length = buf_len; 1097fcbd8018SJean-Christophe Dubois size -= buf_len; 1098b72d8d25SJean-Christophe Dubois 10998095508aSJean-Christophe Dubois trace_imx_fec_receive_len(addr, bd.length); 1100b72d8d25SJean-Christophe Dubois 1101fcbd8018SJean-Christophe Dubois /* The last 4 bytes are the CRC. */ 1102fcbd8018SJean-Christophe Dubois if (size < 4) { 1103fcbd8018SJean-Christophe Dubois buf_len += size - 4; 1104fcbd8018SJean-Christophe Dubois } 1105fcbd8018SJean-Christophe Dubois buf_addr = bd.data; 1106fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); 1107fcbd8018SJean-Christophe Dubois buf += buf_len; 1108fcbd8018SJean-Christophe Dubois if (size < 4) { 1109fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len, 1110fcbd8018SJean-Christophe Dubois crc_ptr, 4 - size); 1111fcbd8018SJean-Christophe Dubois crc_ptr += 4 - size; 1112fcbd8018SJean-Christophe Dubois } 11131bb3c371SJean-Christophe Dubois bd.flags &= ~ENET_BD_E; 1114fcbd8018SJean-Christophe Dubois if (size == 0) { 1115fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */ 11161bb3c371SJean-Christophe Dubois bd.flags |= flags | ENET_BD_L; 11178095508aSJean-Christophe Dubois 11188095508aSJean-Christophe Dubois trace_imx_fec_receive_last(bd.flags); 11198095508aSJean-Christophe Dubois 1120db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXF; 1121fcbd8018SJean-Christophe Dubois } else { 1122db0de352SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXB; 1123fcbd8018SJean-Christophe Dubois } 1124fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr); 1125fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */ 11261bb3c371SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 1127db0de352SJean-Christophe Dubois addr = s->regs[ENET_RDSR]; 1128fcbd8018SJean-Christophe Dubois } else { 1129db0de352SJean-Christophe Dubois addr += sizeof(bd); 1130fcbd8018SJean-Christophe Dubois } 1131fcbd8018SJean-Christophe Dubois } 1132fcbd8018SJean-Christophe Dubois s->rx_descriptor = addr; 1133b2b012afSAndrey Smirnov imx_eth_enable_rx(s, false); 1134a699b410SJean-Christophe Dubois imx_eth_update(s); 1135fcbd8018SJean-Christophe Dubois return len; 1136fcbd8018SJean-Christophe Dubois } 1137fcbd8018SJean-Christophe Dubois 1138a699b410SJean-Christophe Dubois static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf, 1139a699b410SJean-Christophe Dubois size_t len) 1140a699b410SJean-Christophe Dubois { 1141a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1142a699b410SJean-Christophe Dubois IMXENETBufDesc bd; 1143a699b410SJean-Christophe Dubois uint32_t flags = 0; 1144a699b410SJean-Christophe Dubois uint32_t addr; 1145a699b410SJean-Christophe Dubois uint32_t crc; 1146a699b410SJean-Christophe Dubois uint32_t buf_addr; 1147a699b410SJean-Christophe Dubois uint8_t *crc_ptr; 1148a699b410SJean-Christophe Dubois unsigned int buf_len; 1149a699b410SJean-Christophe Dubois size_t size = len; 1150ebdd8cddSAndrey Smirnov bool shift16 = s->regs[ENET_RACC] & ENET_RACC_SHIFT16; 1151a699b410SJean-Christophe Dubois 11528095508aSJean-Christophe Dubois trace_imx_enet_receive(size); 1153a699b410SJean-Christophe Dubois 1154a699b410SJean-Christophe Dubois if (!s->regs[ENET_RDAR]) { 1155a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", 1156a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 1157a699b410SJean-Christophe Dubois return 0; 1158a699b410SJean-Christophe Dubois } 1159a699b410SJean-Christophe Dubois 1160a699b410SJean-Christophe Dubois /* 4 bytes for the CRC. */ 1161a699b410SJean-Christophe Dubois size += 4; 1162a699b410SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size)); 1163a699b410SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc; 1164a699b410SJean-Christophe Dubois 1165ebdd8cddSAndrey Smirnov if (shift16) { 1166ebdd8cddSAndrey Smirnov size += 2; 1167ebdd8cddSAndrey Smirnov } 1168ebdd8cddSAndrey Smirnov 1169894d74ccSAndrey Smirnov /* Huge frames are truncated. */ 1170ff9a7feeSAndrey Smirnov if (size > s->regs[ENET_FTRL]) { 1171ff9a7feeSAndrey Smirnov size = s->regs[ENET_FTRL]; 1172a699b410SJean-Christophe Dubois flags |= ENET_BD_TR | ENET_BD_LG; 1173a699b410SJean-Christophe Dubois } 1174a699b410SJean-Christophe Dubois 1175a699b410SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */ 1176a699b410SJean-Christophe Dubois if (size > (s->regs[ENET_RCR] >> 16)) { 1177a699b410SJean-Christophe Dubois flags |= ENET_BD_LG; 1178a699b410SJean-Christophe Dubois } 1179a699b410SJean-Christophe Dubois 1180a699b410SJean-Christophe Dubois addr = s->rx_descriptor; 1181a699b410SJean-Christophe Dubois while (size > 0) { 1182a699b410SJean-Christophe Dubois imx_enet_read_bd(&bd, addr); 1183a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_E) == 0) { 1184a699b410SJean-Christophe Dubois /* No descriptors available. Bail out. */ 1185a699b410SJean-Christophe Dubois /* 1186a699b410SJean-Christophe Dubois * FIXME: This is wrong. We should probably either 1187a699b410SJean-Christophe Dubois * save the remainder for when more RX buffers are 1188a699b410SJean-Christophe Dubois * available, or flag an error. 1189a699b410SJean-Christophe Dubois */ 1190a699b410SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", 1191a699b410SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 1192a699b410SJean-Christophe Dubois break; 1193a699b410SJean-Christophe Dubois } 11944c5e7a6cSAndrey Smirnov buf_len = MIN(size, s->regs[ENET_MRBR]); 1195a699b410SJean-Christophe Dubois bd.length = buf_len; 1196a699b410SJean-Christophe Dubois size -= buf_len; 1197a699b410SJean-Christophe Dubois 11988095508aSJean-Christophe Dubois trace_imx_enet_receive_len(addr, bd.length); 1199a699b410SJean-Christophe Dubois 1200a699b410SJean-Christophe Dubois /* The last 4 bytes are the CRC. */ 1201a699b410SJean-Christophe Dubois if (size < 4) { 1202a699b410SJean-Christophe Dubois buf_len += size - 4; 1203a699b410SJean-Christophe Dubois } 1204a699b410SJean-Christophe Dubois buf_addr = bd.data; 1205ebdd8cddSAndrey Smirnov 1206ebdd8cddSAndrey Smirnov if (shift16) { 1207ebdd8cddSAndrey Smirnov /* 1208ebdd8cddSAndrey Smirnov * If SHIFT16 bit of ENETx_RACC register is set we need to 1209ebdd8cddSAndrey Smirnov * align the payload to 4-byte boundary. 1210ebdd8cddSAndrey Smirnov */ 1211ebdd8cddSAndrey Smirnov const uint8_t zeros[2] = { 0 }; 1212ebdd8cddSAndrey Smirnov 1213ebdd8cddSAndrey Smirnov dma_memory_write(&address_space_memory, buf_addr, 1214ebdd8cddSAndrey Smirnov zeros, sizeof(zeros)); 1215ebdd8cddSAndrey Smirnov 1216ebdd8cddSAndrey Smirnov buf_addr += sizeof(zeros); 1217ebdd8cddSAndrey Smirnov buf_len -= sizeof(zeros); 1218ebdd8cddSAndrey Smirnov 1219ebdd8cddSAndrey Smirnov /* We only do this once per Ethernet frame */ 1220ebdd8cddSAndrey Smirnov shift16 = false; 1221ebdd8cddSAndrey Smirnov } 1222ebdd8cddSAndrey Smirnov 1223a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); 1224a699b410SJean-Christophe Dubois buf += buf_len; 1225a699b410SJean-Christophe Dubois if (size < 4) { 1226a699b410SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len, 1227a699b410SJean-Christophe Dubois crc_ptr, 4 - size); 1228a699b410SJean-Christophe Dubois crc_ptr += 4 - size; 1229a699b410SJean-Christophe Dubois } 1230a699b410SJean-Christophe Dubois bd.flags &= ~ENET_BD_E; 1231a699b410SJean-Christophe Dubois if (size == 0) { 1232a699b410SJean-Christophe Dubois /* Last buffer in frame. */ 1233a699b410SJean-Christophe Dubois bd.flags |= flags | ENET_BD_L; 12348095508aSJean-Christophe Dubois 12358095508aSJean-Christophe Dubois trace_imx_enet_receive_last(bd.flags); 12368095508aSJean-Christophe Dubois 123788e1b59eSAaron Hill /* Indicate that we've updated the last buffer descriptor. */ 123888e1b59eSAaron Hill bd.last_buffer = ENET_BD_BDU; 1239a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_RX_INT) { 1240a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXF; 1241a699b410SJean-Christophe Dubois } 1242a699b410SJean-Christophe Dubois } else { 1243a699b410SJean-Christophe Dubois if (bd.option & ENET_BD_RX_INT) { 1244a699b410SJean-Christophe Dubois s->regs[ENET_EIR] |= ENET_INT_RXB; 1245a699b410SJean-Christophe Dubois } 1246a699b410SJean-Christophe Dubois } 1247a699b410SJean-Christophe Dubois imx_enet_write_bd(&bd, addr); 1248a699b410SJean-Christophe Dubois /* Advance to the next descriptor. */ 1249a699b410SJean-Christophe Dubois if ((bd.flags & ENET_BD_W) != 0) { 1250a699b410SJean-Christophe Dubois addr = s->regs[ENET_RDSR]; 1251a699b410SJean-Christophe Dubois } else { 1252a699b410SJean-Christophe Dubois addr += sizeof(bd); 1253a699b410SJean-Christophe Dubois } 1254a699b410SJean-Christophe Dubois } 1255a699b410SJean-Christophe Dubois s->rx_descriptor = addr; 1256b2b012afSAndrey Smirnov imx_eth_enable_rx(s, false); 1257a699b410SJean-Christophe Dubois imx_eth_update(s); 1258a699b410SJean-Christophe Dubois return len; 1259a699b410SJean-Christophe Dubois } 1260a699b410SJean-Christophe Dubois 1261a699b410SJean-Christophe Dubois static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf, 1262a699b410SJean-Christophe Dubois size_t len) 1263a699b410SJean-Christophe Dubois { 1264a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1265a699b410SJean-Christophe Dubois 1266a699b410SJean-Christophe Dubois if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { 1267a699b410SJean-Christophe Dubois return imx_enet_receive(nc, buf, len); 1268a699b410SJean-Christophe Dubois } else { 1269a699b410SJean-Christophe Dubois return imx_fec_receive(nc, buf, len); 1270a699b410SJean-Christophe Dubois } 1271a699b410SJean-Christophe Dubois } 1272a699b410SJean-Christophe Dubois 1273a699b410SJean-Christophe Dubois static const MemoryRegionOps imx_eth_ops = { 1274a699b410SJean-Christophe Dubois .read = imx_eth_read, 1275a699b410SJean-Christophe Dubois .write = imx_eth_write, 1276fcbd8018SJean-Christophe Dubois .valid.min_access_size = 4, 1277fcbd8018SJean-Christophe Dubois .valid.max_access_size = 4, 1278fcbd8018SJean-Christophe Dubois .endianness = DEVICE_NATIVE_ENDIAN, 1279fcbd8018SJean-Christophe Dubois }; 1280fcbd8018SJean-Christophe Dubois 1281a699b410SJean-Christophe Dubois static void imx_eth_cleanup(NetClientState *nc) 1282fcbd8018SJean-Christophe Dubois { 1283fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 1284fcbd8018SJean-Christophe Dubois 1285fcbd8018SJean-Christophe Dubois s->nic = NULL; 1286fcbd8018SJean-Christophe Dubois } 1287fcbd8018SJean-Christophe Dubois 1288a699b410SJean-Christophe Dubois static NetClientInfo imx_eth_net_info = { 1289f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 1290fcbd8018SJean-Christophe Dubois .size = sizeof(NICState), 1291a699b410SJean-Christophe Dubois .can_receive = imx_eth_can_receive, 1292a699b410SJean-Christophe Dubois .receive = imx_eth_receive, 1293a699b410SJean-Christophe Dubois .cleanup = imx_eth_cleanup, 1294a699b410SJean-Christophe Dubois .link_status_changed = imx_eth_set_link, 1295fcbd8018SJean-Christophe Dubois }; 1296fcbd8018SJean-Christophe Dubois 1297fcbd8018SJean-Christophe Dubois 1298a699b410SJean-Christophe Dubois static void imx_eth_realize(DeviceState *dev, Error **errp) 1299fcbd8018SJean-Christophe Dubois { 1300fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(dev); 1301fcbd8018SJean-Christophe Dubois SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 1302fcbd8018SJean-Christophe Dubois 1303a699b410SJean-Christophe Dubois memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s, 1304831858adSAndrey Smirnov TYPE_IMX_FEC, FSL_IMX25_FEC_SIZE); 1305fcbd8018SJean-Christophe Dubois sysbus_init_mmio(sbd, &s->iomem); 1306a699b410SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq[0]); 1307a699b410SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq[1]); 1308a699b410SJean-Christophe Dubois 1309fcbd8018SJean-Christophe Dubois qemu_macaddr_default_if_unset(&s->conf.macaddr); 1310fcbd8018SJean-Christophe Dubois 1311a699b410SJean-Christophe Dubois s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, 1312a699b410SJean-Christophe Dubois object_get_typename(OBJECT(dev)), 13138e5c952bSPhilippe Mathieu-Daudé dev->id, s); 1314a699b410SJean-Christophe Dubois 1315fcbd8018SJean-Christophe Dubois qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 1316fcbd8018SJean-Christophe Dubois } 1317fcbd8018SJean-Christophe Dubois 1318a699b410SJean-Christophe Dubois static Property imx_eth_properties[] = { 1319fcbd8018SJean-Christophe Dubois DEFINE_NIC_PROPERTIES(IMXFECState, conf), 1320f93f961cSAndrey Smirnov DEFINE_PROP_UINT32("tx-ring-num", IMXFECState, tx_ring_num, 1), 1321461c51adSJean-Christophe Dubois DEFINE_PROP_UINT32("phy-num", IMXFECState, phy_num, 0), 1322fcbd8018SJean-Christophe Dubois DEFINE_PROP_END_OF_LIST(), 1323fcbd8018SJean-Christophe Dubois }; 1324fcbd8018SJean-Christophe Dubois 1325a699b410SJean-Christophe Dubois static void imx_eth_class_init(ObjectClass *klass, void *data) 1326fcbd8018SJean-Christophe Dubois { 1327fcbd8018SJean-Christophe Dubois DeviceClass *dc = DEVICE_CLASS(klass); 1328fcbd8018SJean-Christophe Dubois 1329a699b410SJean-Christophe Dubois dc->vmsd = &vmstate_imx_eth; 1330a699b410SJean-Christophe Dubois dc->reset = imx_eth_reset; 13314f67d30bSMarc-André Lureau device_class_set_props(dc, imx_eth_properties); 1332a699b410SJean-Christophe Dubois dc->realize = imx_eth_realize; 1333a699b410SJean-Christophe Dubois dc->desc = "i.MX FEC/ENET Ethernet Controller"; 1334a699b410SJean-Christophe Dubois } 1335a699b410SJean-Christophe Dubois 1336a699b410SJean-Christophe Dubois static void imx_fec_init(Object *obj) 1337a699b410SJean-Christophe Dubois { 1338a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(obj); 1339a699b410SJean-Christophe Dubois 1340a699b410SJean-Christophe Dubois s->is_fec = true; 1341a699b410SJean-Christophe Dubois } 1342a699b410SJean-Christophe Dubois 1343a699b410SJean-Christophe Dubois static void imx_enet_init(Object *obj) 1344a699b410SJean-Christophe Dubois { 1345a699b410SJean-Christophe Dubois IMXFECState *s = IMX_FEC(obj); 1346a699b410SJean-Christophe Dubois 1347a699b410SJean-Christophe Dubois s->is_fec = false; 1348fcbd8018SJean-Christophe Dubois } 1349fcbd8018SJean-Christophe Dubois 1350fcbd8018SJean-Christophe Dubois static const TypeInfo imx_fec_info = { 1351fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC, 1352fcbd8018SJean-Christophe Dubois .parent = TYPE_SYS_BUS_DEVICE, 1353fcbd8018SJean-Christophe Dubois .instance_size = sizeof(IMXFECState), 1354a699b410SJean-Christophe Dubois .instance_init = imx_fec_init, 1355a699b410SJean-Christophe Dubois .class_init = imx_eth_class_init, 1356fcbd8018SJean-Christophe Dubois }; 1357fcbd8018SJean-Christophe Dubois 1358a699b410SJean-Christophe Dubois static const TypeInfo imx_enet_info = { 1359a699b410SJean-Christophe Dubois .name = TYPE_IMX_ENET, 1360a699b410SJean-Christophe Dubois .parent = TYPE_IMX_FEC, 1361a699b410SJean-Christophe Dubois .instance_init = imx_enet_init, 1362a699b410SJean-Christophe Dubois }; 1363a699b410SJean-Christophe Dubois 1364a699b410SJean-Christophe Dubois static void imx_eth_register_types(void) 1365fcbd8018SJean-Christophe Dubois { 1366fcbd8018SJean-Christophe Dubois type_register_static(&imx_fec_info); 1367a699b410SJean-Christophe Dubois type_register_static(&imx_enet_info); 1368fcbd8018SJean-Christophe Dubois } 1369fcbd8018SJean-Christophe Dubois 1370a699b410SJean-Christophe Dubois type_init(imx_eth_register_types) 1371