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