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