1fcbd8018SJean-Christophe Dubois /* 2fcbd8018SJean-Christophe Dubois * i.MX Fast Ethernet Controller emulation. 3fcbd8018SJean-Christophe Dubois * 4fcbd8018SJean-Christophe Dubois * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> 5fcbd8018SJean-Christophe Dubois * 6fcbd8018SJean-Christophe Dubois * Based on Coldfire Fast Ethernet Controller emulation. 7fcbd8018SJean-Christophe Dubois * 8fcbd8018SJean-Christophe Dubois * Copyright (c) 2007 CodeSourcery. 9fcbd8018SJean-Christophe Dubois * 10fcbd8018SJean-Christophe Dubois * This program is free software; you can redistribute it and/or modify it 11fcbd8018SJean-Christophe Dubois * under the terms of the GNU General Public License as published by the 12fcbd8018SJean-Christophe Dubois * Free Software Foundation; either version 2 of the License, or 13fcbd8018SJean-Christophe Dubois * (at your option) any later version. 14fcbd8018SJean-Christophe Dubois * 15fcbd8018SJean-Christophe Dubois * This program is distributed in the hope that it will be useful, but WITHOUT 16fcbd8018SJean-Christophe Dubois * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17fcbd8018SJean-Christophe Dubois * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18fcbd8018SJean-Christophe Dubois * for more details. 19fcbd8018SJean-Christophe Dubois * 20fcbd8018SJean-Christophe Dubois * You should have received a copy of the GNU General Public License along 21fcbd8018SJean-Christophe Dubois * with this program; if not, see <http://www.gnu.org/licenses/>. 22fcbd8018SJean-Christophe Dubois */ 23fcbd8018SJean-Christophe Dubois 248ef94f0bSPeter Maydell #include "qemu/osdep.h" 25fcbd8018SJean-Christophe Dubois #include "hw/net/imx_fec.h" 26fcbd8018SJean-Christophe Dubois #include "sysemu/dma.h" 27*03dd024fSPaolo Bonzini #include "qemu/log.h" 28fcbd8018SJean-Christophe Dubois 29fcbd8018SJean-Christophe Dubois /* For crc32 */ 30fcbd8018SJean-Christophe Dubois #include <zlib.h> 31fcbd8018SJean-Christophe Dubois 32b72d8d25SJean-Christophe Dubois #ifndef DEBUG_IMX_FEC 33b72d8d25SJean-Christophe Dubois #define DEBUG_IMX_FEC 0 34fcbd8018SJean-Christophe Dubois #endif 35fcbd8018SJean-Christophe Dubois 36b72d8d25SJean-Christophe Dubois #define FEC_PRINTF(fmt, args...) \ 37b72d8d25SJean-Christophe Dubois do { \ 38b72d8d25SJean-Christophe Dubois if (DEBUG_IMX_FEC) { \ 39b72d8d25SJean-Christophe Dubois fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \ 40b72d8d25SJean-Christophe Dubois __func__, ##args); \ 41b72d8d25SJean-Christophe Dubois } \ 42fcbd8018SJean-Christophe Dubois } while (0) 43b72d8d25SJean-Christophe Dubois 44b72d8d25SJean-Christophe Dubois #ifndef DEBUG_IMX_PHY 45b72d8d25SJean-Christophe Dubois #define DEBUG_IMX_PHY 0 46fcbd8018SJean-Christophe Dubois #endif 47fcbd8018SJean-Christophe Dubois 48b72d8d25SJean-Christophe Dubois #define PHY_PRINTF(fmt, args...) \ 49b72d8d25SJean-Christophe Dubois do { \ 50b72d8d25SJean-Christophe Dubois if (DEBUG_IMX_PHY) { \ 51b72d8d25SJean-Christophe Dubois fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \ 52b72d8d25SJean-Christophe Dubois __func__, ##args); \ 53b72d8d25SJean-Christophe Dubois } \ 54fcbd8018SJean-Christophe Dubois } while (0) 55fcbd8018SJean-Christophe Dubois 56fcbd8018SJean-Christophe Dubois static const VMStateDescription vmstate_imx_fec = { 57fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC, 58fcbd8018SJean-Christophe Dubois .version_id = 1, 59fcbd8018SJean-Christophe Dubois .minimum_version_id = 1, 60fcbd8018SJean-Christophe Dubois .fields = (VMStateField[]) { 61fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(irq_state, IMXFECState), 62fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(eir, IMXFECState), 63fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(eimr, IMXFECState), 64fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(rx_enabled, IMXFECState), 65fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(rx_descriptor, IMXFECState), 66fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(tx_descriptor, IMXFECState), 67fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(ecr, IMXFECState), 68fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(mmfr, IMXFECState), 69fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(mscr, IMXFECState), 70fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(mibc, IMXFECState), 71fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(rcr, IMXFECState), 72fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(tcr, IMXFECState), 73fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(tfwr, IMXFECState), 74fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(frsr, IMXFECState), 75fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(erdsr, IMXFECState), 76fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(etdsr, IMXFECState), 77fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(emrbr, IMXFECState), 78fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(miigsk_cfgr, IMXFECState), 79fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(miigsk_enr, IMXFECState), 80fcbd8018SJean-Christophe Dubois 81fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_status, IMXFECState), 82fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_control, IMXFECState), 83fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_advertise, IMXFECState), 84fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_int, IMXFECState), 85fcbd8018SJean-Christophe Dubois VMSTATE_UINT32(phy_int_mask, IMXFECState), 86fcbd8018SJean-Christophe Dubois VMSTATE_END_OF_LIST() 87fcbd8018SJean-Christophe Dubois } 88fcbd8018SJean-Christophe Dubois }; 89fcbd8018SJean-Christophe Dubois 90fcbd8018SJean-Christophe Dubois #define PHY_INT_ENERGYON (1 << 7) 91fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_COMPLETE (1 << 6) 92fcbd8018SJean-Christophe Dubois #define PHY_INT_FAULT (1 << 5) 93fcbd8018SJean-Christophe Dubois #define PHY_INT_DOWN (1 << 4) 94fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_LP (1 << 3) 95fcbd8018SJean-Christophe Dubois #define PHY_INT_PARFAULT (1 << 2) 96fcbd8018SJean-Christophe Dubois #define PHY_INT_AUTONEG_PAGE (1 << 1) 97fcbd8018SJean-Christophe Dubois 98fcbd8018SJean-Christophe Dubois static void imx_fec_update(IMXFECState *s); 99fcbd8018SJean-Christophe Dubois 100fcbd8018SJean-Christophe Dubois /* 101fcbd8018SJean-Christophe Dubois * The MII phy could raise a GPIO to the processor which in turn 102fcbd8018SJean-Christophe Dubois * could be handled as an interrpt by the OS. 103fcbd8018SJean-Christophe Dubois * For now we don't handle any GPIO/interrupt line, so the OS will 104fcbd8018SJean-Christophe Dubois * have to poll for the PHY status. 105fcbd8018SJean-Christophe Dubois */ 106fcbd8018SJean-Christophe Dubois static void phy_update_irq(IMXFECState *s) 107fcbd8018SJean-Christophe Dubois { 108fcbd8018SJean-Christophe Dubois imx_fec_update(s); 109fcbd8018SJean-Christophe Dubois } 110fcbd8018SJean-Christophe Dubois 111fcbd8018SJean-Christophe Dubois static void phy_update_link(IMXFECState *s) 112fcbd8018SJean-Christophe Dubois { 113fcbd8018SJean-Christophe Dubois /* Autonegotiation status mirrors link status. */ 114fcbd8018SJean-Christophe Dubois if (qemu_get_queue(s->nic)->link_down) { 115fcbd8018SJean-Christophe Dubois PHY_PRINTF("link is down\n"); 116fcbd8018SJean-Christophe Dubois s->phy_status &= ~0x0024; 117fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_DOWN; 118fcbd8018SJean-Christophe Dubois } else { 119fcbd8018SJean-Christophe Dubois PHY_PRINTF("link is up\n"); 120fcbd8018SJean-Christophe Dubois s->phy_status |= 0x0024; 121fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_ENERGYON; 122fcbd8018SJean-Christophe Dubois s->phy_int |= PHY_INT_AUTONEG_COMPLETE; 123fcbd8018SJean-Christophe Dubois } 124fcbd8018SJean-Christophe Dubois phy_update_irq(s); 125fcbd8018SJean-Christophe Dubois } 126fcbd8018SJean-Christophe Dubois 127fcbd8018SJean-Christophe Dubois static void imx_fec_set_link(NetClientState *nc) 128fcbd8018SJean-Christophe Dubois { 129fcbd8018SJean-Christophe Dubois phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); 130fcbd8018SJean-Christophe Dubois } 131fcbd8018SJean-Christophe Dubois 132fcbd8018SJean-Christophe Dubois static void phy_reset(IMXFECState *s) 133fcbd8018SJean-Christophe Dubois { 134fcbd8018SJean-Christophe Dubois s->phy_status = 0x7809; 135fcbd8018SJean-Christophe Dubois s->phy_control = 0x3000; 136fcbd8018SJean-Christophe Dubois s->phy_advertise = 0x01e1; 137fcbd8018SJean-Christophe Dubois s->phy_int_mask = 0; 138fcbd8018SJean-Christophe Dubois s->phy_int = 0; 139fcbd8018SJean-Christophe Dubois phy_update_link(s); 140fcbd8018SJean-Christophe Dubois } 141fcbd8018SJean-Christophe Dubois 142fcbd8018SJean-Christophe Dubois static uint32_t do_phy_read(IMXFECState *s, int reg) 143fcbd8018SJean-Christophe Dubois { 144fcbd8018SJean-Christophe Dubois uint32_t val; 145fcbd8018SJean-Christophe Dubois 146fcbd8018SJean-Christophe Dubois if (reg > 31) { 147fcbd8018SJean-Christophe Dubois /* we only advertise one phy */ 148fcbd8018SJean-Christophe Dubois return 0; 149fcbd8018SJean-Christophe Dubois } 150fcbd8018SJean-Christophe Dubois 151fcbd8018SJean-Christophe Dubois switch (reg) { 152fcbd8018SJean-Christophe Dubois case 0: /* Basic Control */ 153fcbd8018SJean-Christophe Dubois val = s->phy_control; 154fcbd8018SJean-Christophe Dubois break; 155fcbd8018SJean-Christophe Dubois case 1: /* Basic Status */ 156fcbd8018SJean-Christophe Dubois val = s->phy_status; 157fcbd8018SJean-Christophe Dubois break; 158fcbd8018SJean-Christophe Dubois case 2: /* ID1 */ 159fcbd8018SJean-Christophe Dubois val = 0x0007; 160fcbd8018SJean-Christophe Dubois break; 161fcbd8018SJean-Christophe Dubois case 3: /* ID2 */ 162fcbd8018SJean-Christophe Dubois val = 0xc0d1; 163fcbd8018SJean-Christophe Dubois break; 164fcbd8018SJean-Christophe Dubois case 4: /* Auto-neg advertisement */ 165fcbd8018SJean-Christophe Dubois val = s->phy_advertise; 166fcbd8018SJean-Christophe Dubois break; 167fcbd8018SJean-Christophe Dubois case 5: /* Auto-neg Link Partner Ability */ 168fcbd8018SJean-Christophe Dubois val = 0x0f71; 169fcbd8018SJean-Christophe Dubois break; 170fcbd8018SJean-Christophe Dubois case 6: /* Auto-neg Expansion */ 171fcbd8018SJean-Christophe Dubois val = 1; 172fcbd8018SJean-Christophe Dubois break; 173fcbd8018SJean-Christophe Dubois case 29: /* Interrupt source. */ 174fcbd8018SJean-Christophe Dubois val = s->phy_int; 175fcbd8018SJean-Christophe Dubois s->phy_int = 0; 176fcbd8018SJean-Christophe Dubois phy_update_irq(s); 177fcbd8018SJean-Christophe Dubois break; 178fcbd8018SJean-Christophe Dubois case 30: /* Interrupt mask */ 179fcbd8018SJean-Christophe Dubois val = s->phy_int_mask; 180fcbd8018SJean-Christophe Dubois break; 181fcbd8018SJean-Christophe Dubois case 17: 182fcbd8018SJean-Christophe Dubois case 18: 183fcbd8018SJean-Christophe Dubois case 27: 184fcbd8018SJean-Christophe Dubois case 31: 185b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", 186fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 187fcbd8018SJean-Christophe Dubois val = 0; 188fcbd8018SJean-Christophe Dubois break; 189fcbd8018SJean-Christophe Dubois default: 190b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", 191fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 192fcbd8018SJean-Christophe Dubois val = 0; 193fcbd8018SJean-Christophe Dubois break; 194fcbd8018SJean-Christophe Dubois } 195fcbd8018SJean-Christophe Dubois 196fcbd8018SJean-Christophe Dubois PHY_PRINTF("read 0x%04x @ %d\n", val, reg); 197fcbd8018SJean-Christophe Dubois 198fcbd8018SJean-Christophe Dubois return val; 199fcbd8018SJean-Christophe Dubois } 200fcbd8018SJean-Christophe Dubois 201fcbd8018SJean-Christophe Dubois static void do_phy_write(IMXFECState *s, int reg, uint32_t val) 202fcbd8018SJean-Christophe Dubois { 203fcbd8018SJean-Christophe Dubois PHY_PRINTF("write 0x%04x @ %d\n", val, reg); 204fcbd8018SJean-Christophe Dubois 205fcbd8018SJean-Christophe Dubois if (reg > 31) { 206fcbd8018SJean-Christophe Dubois /* we only advertise one phy */ 207fcbd8018SJean-Christophe Dubois return; 208fcbd8018SJean-Christophe Dubois } 209fcbd8018SJean-Christophe Dubois 210fcbd8018SJean-Christophe Dubois switch (reg) { 211fcbd8018SJean-Christophe Dubois case 0: /* Basic Control */ 212fcbd8018SJean-Christophe Dubois if (val & 0x8000) { 213fcbd8018SJean-Christophe Dubois phy_reset(s); 214fcbd8018SJean-Christophe Dubois } else { 215fcbd8018SJean-Christophe Dubois s->phy_control = val & 0x7980; 216fcbd8018SJean-Christophe Dubois /* Complete autonegotiation immediately. */ 217fcbd8018SJean-Christophe Dubois if (val & 0x1000) { 218fcbd8018SJean-Christophe Dubois s->phy_status |= 0x0020; 219fcbd8018SJean-Christophe Dubois } 220fcbd8018SJean-Christophe Dubois } 221fcbd8018SJean-Christophe Dubois break; 222fcbd8018SJean-Christophe Dubois case 4: /* Auto-neg advertisement */ 223fcbd8018SJean-Christophe Dubois s->phy_advertise = (val & 0x2d7f) | 0x80; 224fcbd8018SJean-Christophe Dubois break; 225fcbd8018SJean-Christophe Dubois case 30: /* Interrupt mask */ 226fcbd8018SJean-Christophe Dubois s->phy_int_mask = val & 0xff; 227fcbd8018SJean-Christophe Dubois phy_update_irq(s); 228fcbd8018SJean-Christophe Dubois break; 229fcbd8018SJean-Christophe Dubois case 17: 230fcbd8018SJean-Christophe Dubois case 18: 231fcbd8018SJean-Christophe Dubois case 27: 232fcbd8018SJean-Christophe Dubois case 31: 233b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", 234fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 235fcbd8018SJean-Christophe Dubois break; 236fcbd8018SJean-Christophe Dubois default: 237b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", 238fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__, reg); 239fcbd8018SJean-Christophe Dubois break; 240fcbd8018SJean-Christophe Dubois } 241fcbd8018SJean-Christophe Dubois } 242fcbd8018SJean-Christophe Dubois 243fcbd8018SJean-Christophe Dubois static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) 244fcbd8018SJean-Christophe Dubois { 245fcbd8018SJean-Christophe Dubois dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); 246fcbd8018SJean-Christophe Dubois } 247fcbd8018SJean-Christophe Dubois 248fcbd8018SJean-Christophe Dubois static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) 249fcbd8018SJean-Christophe Dubois { 250fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); 251fcbd8018SJean-Christophe Dubois } 252fcbd8018SJean-Christophe Dubois 253fcbd8018SJean-Christophe Dubois static void imx_fec_update(IMXFECState *s) 254fcbd8018SJean-Christophe Dubois { 255fcbd8018SJean-Christophe Dubois uint32_t active; 256fcbd8018SJean-Christophe Dubois uint32_t changed; 257fcbd8018SJean-Christophe Dubois 258fcbd8018SJean-Christophe Dubois active = s->eir & s->eimr; 259fcbd8018SJean-Christophe Dubois changed = active ^ s->irq_state; 260fcbd8018SJean-Christophe Dubois if (changed) { 261fcbd8018SJean-Christophe Dubois qemu_set_irq(s->irq, active); 262fcbd8018SJean-Christophe Dubois } 263fcbd8018SJean-Christophe Dubois s->irq_state = active; 264fcbd8018SJean-Christophe Dubois } 265fcbd8018SJean-Christophe Dubois 266fcbd8018SJean-Christophe Dubois static void imx_fec_do_tx(IMXFECState *s) 267fcbd8018SJean-Christophe Dubois { 268fcbd8018SJean-Christophe Dubois int frame_size = 0; 269fcbd8018SJean-Christophe Dubois uint8_t frame[FEC_MAX_FRAME_SIZE]; 270fcbd8018SJean-Christophe Dubois uint8_t *ptr = frame; 271fcbd8018SJean-Christophe Dubois uint32_t addr = s->tx_descriptor; 272fcbd8018SJean-Christophe Dubois 273fcbd8018SJean-Christophe Dubois while (1) { 274fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 275fcbd8018SJean-Christophe Dubois int len; 276fcbd8018SJean-Christophe Dubois 277fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr); 278fcbd8018SJean-Christophe Dubois FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n", 279fcbd8018SJean-Christophe Dubois addr, bd.flags, bd.length, bd.data); 280fcbd8018SJean-Christophe Dubois if ((bd.flags & FEC_BD_R) == 0) { 281fcbd8018SJean-Christophe Dubois /* Run out of descriptors to transmit. */ 282fcbd8018SJean-Christophe Dubois break; 283fcbd8018SJean-Christophe Dubois } 284fcbd8018SJean-Christophe Dubois len = bd.length; 285fcbd8018SJean-Christophe Dubois if (frame_size + len > FEC_MAX_FRAME_SIZE) { 286fcbd8018SJean-Christophe Dubois len = FEC_MAX_FRAME_SIZE - frame_size; 287fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_BABT; 288fcbd8018SJean-Christophe Dubois } 289fcbd8018SJean-Christophe Dubois dma_memory_read(&address_space_memory, bd.data, ptr, len); 290fcbd8018SJean-Christophe Dubois ptr += len; 291fcbd8018SJean-Christophe Dubois frame_size += len; 292fcbd8018SJean-Christophe Dubois if (bd.flags & FEC_BD_L) { 293fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */ 294fcbd8018SJean-Christophe Dubois qemu_send_packet(qemu_get_queue(s->nic), frame, len); 295fcbd8018SJean-Christophe Dubois ptr = frame; 296fcbd8018SJean-Christophe Dubois frame_size = 0; 297fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_TXF; 298fcbd8018SJean-Christophe Dubois } 299fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_TXB; 300fcbd8018SJean-Christophe Dubois bd.flags &= ~FEC_BD_R; 301fcbd8018SJean-Christophe Dubois /* Write back the modified descriptor. */ 302fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr); 303fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */ 304fcbd8018SJean-Christophe Dubois if ((bd.flags & FEC_BD_W) != 0) { 305fcbd8018SJean-Christophe Dubois addr = s->etdsr; 306fcbd8018SJean-Christophe Dubois } else { 307fcbd8018SJean-Christophe Dubois addr += 8; 308fcbd8018SJean-Christophe Dubois } 309fcbd8018SJean-Christophe Dubois } 310fcbd8018SJean-Christophe Dubois 311fcbd8018SJean-Christophe Dubois s->tx_descriptor = addr; 312fcbd8018SJean-Christophe Dubois 313fcbd8018SJean-Christophe Dubois imx_fec_update(s); 314fcbd8018SJean-Christophe Dubois } 315fcbd8018SJean-Christophe Dubois 316fcbd8018SJean-Christophe Dubois static void imx_fec_enable_rx(IMXFECState *s) 317fcbd8018SJean-Christophe Dubois { 318fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 319fcbd8018SJean-Christophe Dubois uint32_t tmp; 320fcbd8018SJean-Christophe Dubois 321fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, s->rx_descriptor); 322fcbd8018SJean-Christophe Dubois 323fcbd8018SJean-Christophe Dubois tmp = ((bd.flags & FEC_BD_E) != 0); 324fcbd8018SJean-Christophe Dubois 325fcbd8018SJean-Christophe Dubois if (!tmp) { 326fcbd8018SJean-Christophe Dubois FEC_PRINTF("RX buffer full\n"); 327fcbd8018SJean-Christophe Dubois } else if (!s->rx_enabled) { 328fcbd8018SJean-Christophe Dubois qemu_flush_queued_packets(qemu_get_queue(s->nic)); 329fcbd8018SJean-Christophe Dubois } 330fcbd8018SJean-Christophe Dubois 331fcbd8018SJean-Christophe Dubois s->rx_enabled = tmp; 332fcbd8018SJean-Christophe Dubois } 333fcbd8018SJean-Christophe Dubois 334fcbd8018SJean-Christophe Dubois static void imx_fec_reset(DeviceState *d) 335fcbd8018SJean-Christophe Dubois { 336fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(d); 337fcbd8018SJean-Christophe Dubois 338fcbd8018SJean-Christophe Dubois /* Reset the FEC */ 339fcbd8018SJean-Christophe Dubois s->eir = 0; 340fcbd8018SJean-Christophe Dubois s->eimr = 0; 341fcbd8018SJean-Christophe Dubois s->rx_enabled = 0; 342fcbd8018SJean-Christophe Dubois s->ecr = 0; 343fcbd8018SJean-Christophe Dubois s->mscr = 0; 344fcbd8018SJean-Christophe Dubois s->mibc = 0xc0000000; 345fcbd8018SJean-Christophe Dubois s->rcr = 0x05ee0001; 346fcbd8018SJean-Christophe Dubois s->tcr = 0; 347fcbd8018SJean-Christophe Dubois s->tfwr = 0; 348fcbd8018SJean-Christophe Dubois s->frsr = 0x500; 349fcbd8018SJean-Christophe Dubois s->miigsk_cfgr = 0; 350fcbd8018SJean-Christophe Dubois s->miigsk_enr = 0x6; 351fcbd8018SJean-Christophe Dubois 352fcbd8018SJean-Christophe Dubois /* We also reset the PHY */ 353fcbd8018SJean-Christophe Dubois phy_reset(s); 354fcbd8018SJean-Christophe Dubois } 355fcbd8018SJean-Christophe Dubois 356fcbd8018SJean-Christophe Dubois static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) 357fcbd8018SJean-Christophe Dubois { 358fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque); 359fcbd8018SJean-Christophe Dubois 360b72d8d25SJean-Christophe Dubois FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr); 361fcbd8018SJean-Christophe Dubois 362fcbd8018SJean-Christophe Dubois switch (addr & 0x3ff) { 363fcbd8018SJean-Christophe Dubois case 0x004: 364fcbd8018SJean-Christophe Dubois return s->eir; 365fcbd8018SJean-Christophe Dubois case 0x008: 366fcbd8018SJean-Christophe Dubois return s->eimr; 367fcbd8018SJean-Christophe Dubois case 0x010: 368fcbd8018SJean-Christophe Dubois return s->rx_enabled ? (1 << 24) : 0; /* RDAR */ 369fcbd8018SJean-Christophe Dubois case 0x014: 370fcbd8018SJean-Christophe Dubois return 0; /* TDAR */ 371fcbd8018SJean-Christophe Dubois case 0x024: 372fcbd8018SJean-Christophe Dubois return s->ecr; 373fcbd8018SJean-Christophe Dubois case 0x040: 374fcbd8018SJean-Christophe Dubois return s->mmfr; 375fcbd8018SJean-Christophe Dubois case 0x044: 376fcbd8018SJean-Christophe Dubois return s->mscr; 377fcbd8018SJean-Christophe Dubois case 0x064: 378fcbd8018SJean-Christophe Dubois return s->mibc; /* MIBC */ 379fcbd8018SJean-Christophe Dubois case 0x084: 380fcbd8018SJean-Christophe Dubois return s->rcr; 381fcbd8018SJean-Christophe Dubois case 0x0c4: 382fcbd8018SJean-Christophe Dubois return s->tcr; 383fcbd8018SJean-Christophe Dubois case 0x0e4: /* PALR */ 384fcbd8018SJean-Christophe Dubois return (s->conf.macaddr.a[0] << 24) 385fcbd8018SJean-Christophe Dubois | (s->conf.macaddr.a[1] << 16) 386fcbd8018SJean-Christophe Dubois | (s->conf.macaddr.a[2] << 8) 387fcbd8018SJean-Christophe Dubois | s->conf.macaddr.a[3]; 388fcbd8018SJean-Christophe Dubois break; 389fcbd8018SJean-Christophe Dubois case 0x0e8: /* PAUR */ 390fcbd8018SJean-Christophe Dubois return (s->conf.macaddr.a[4] << 24) 391fcbd8018SJean-Christophe Dubois | (s->conf.macaddr.a[5] << 16) 392fcbd8018SJean-Christophe Dubois | 0x8808; 393fcbd8018SJean-Christophe Dubois case 0x0ec: 394fcbd8018SJean-Christophe Dubois return 0x10000; /* OPD */ 395fcbd8018SJean-Christophe Dubois case 0x118: 396fcbd8018SJean-Christophe Dubois return 0; 397fcbd8018SJean-Christophe Dubois case 0x11c: 398fcbd8018SJean-Christophe Dubois return 0; 399fcbd8018SJean-Christophe Dubois case 0x120: 400fcbd8018SJean-Christophe Dubois return 0; 401fcbd8018SJean-Christophe Dubois case 0x124: 402fcbd8018SJean-Christophe Dubois return 0; 403fcbd8018SJean-Christophe Dubois case 0x144: 404fcbd8018SJean-Christophe Dubois return s->tfwr; 405fcbd8018SJean-Christophe Dubois case 0x14c: 406fcbd8018SJean-Christophe Dubois return 0x600; 407fcbd8018SJean-Christophe Dubois case 0x150: 408fcbd8018SJean-Christophe Dubois return s->frsr; 409fcbd8018SJean-Christophe Dubois case 0x180: 410fcbd8018SJean-Christophe Dubois return s->erdsr; 411fcbd8018SJean-Christophe Dubois case 0x184: 412fcbd8018SJean-Christophe Dubois return s->etdsr; 413fcbd8018SJean-Christophe Dubois case 0x188: 414fcbd8018SJean-Christophe Dubois return s->emrbr; 415fcbd8018SJean-Christophe Dubois case 0x300: 416fcbd8018SJean-Christophe Dubois return s->miigsk_cfgr; 417fcbd8018SJean-Christophe Dubois case 0x308: 418fcbd8018SJean-Christophe Dubois return s->miigsk_enr; 419fcbd8018SJean-Christophe Dubois default: 420b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" 421b72d8d25SJean-Christophe Dubois HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); 422fcbd8018SJean-Christophe Dubois return 0; 423fcbd8018SJean-Christophe Dubois } 424fcbd8018SJean-Christophe Dubois } 425fcbd8018SJean-Christophe Dubois 426fcbd8018SJean-Christophe Dubois static void imx_fec_write(void *opaque, hwaddr addr, 427fcbd8018SJean-Christophe Dubois uint64_t value, unsigned size) 428fcbd8018SJean-Christophe Dubois { 429fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(opaque); 430fcbd8018SJean-Christophe Dubois 431b72d8d25SJean-Christophe Dubois FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr); 432fcbd8018SJean-Christophe Dubois 433fcbd8018SJean-Christophe Dubois switch (addr & 0x3ff) { 434fcbd8018SJean-Christophe Dubois case 0x004: /* EIR */ 435fcbd8018SJean-Christophe Dubois s->eir &= ~value; 436fcbd8018SJean-Christophe Dubois break; 437fcbd8018SJean-Christophe Dubois case 0x008: /* EIMR */ 438fcbd8018SJean-Christophe Dubois s->eimr = value; 439fcbd8018SJean-Christophe Dubois break; 440fcbd8018SJean-Christophe Dubois case 0x010: /* RDAR */ 441fcbd8018SJean-Christophe Dubois if ((s->ecr & FEC_EN) && !s->rx_enabled) { 442fcbd8018SJean-Christophe Dubois imx_fec_enable_rx(s); 443fcbd8018SJean-Christophe Dubois } 444fcbd8018SJean-Christophe Dubois break; 445fcbd8018SJean-Christophe Dubois case 0x014: /* TDAR */ 446fcbd8018SJean-Christophe Dubois if (s->ecr & FEC_EN) { 447fcbd8018SJean-Christophe Dubois imx_fec_do_tx(s); 448fcbd8018SJean-Christophe Dubois } 449fcbd8018SJean-Christophe Dubois break; 450fcbd8018SJean-Christophe Dubois case 0x024: /* ECR */ 451fcbd8018SJean-Christophe Dubois s->ecr = value; 452fcbd8018SJean-Christophe Dubois if (value & FEC_RESET) { 453fcbd8018SJean-Christophe Dubois imx_fec_reset(DEVICE(s)); 454fcbd8018SJean-Christophe Dubois } 455fcbd8018SJean-Christophe Dubois if ((s->ecr & FEC_EN) == 0) { 456fcbd8018SJean-Christophe Dubois s->rx_enabled = 0; 457fcbd8018SJean-Christophe Dubois } 458fcbd8018SJean-Christophe Dubois break; 459fcbd8018SJean-Christophe Dubois case 0x040: /* MMFR */ 460fcbd8018SJean-Christophe Dubois /* store the value */ 461fcbd8018SJean-Christophe Dubois s->mmfr = value; 462fcbd8018SJean-Christophe Dubois if (extract32(value, 28, 1)) { 463fcbd8018SJean-Christophe Dubois do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16)); 464fcbd8018SJean-Christophe Dubois } else { 465fcbd8018SJean-Christophe Dubois s->mmfr = do_phy_read(s, extract32(value, 18, 9)); 466fcbd8018SJean-Christophe Dubois } 467fcbd8018SJean-Christophe Dubois /* raise the interrupt as the PHY operation is done */ 468fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_MII; 469fcbd8018SJean-Christophe Dubois break; 470fcbd8018SJean-Christophe Dubois case 0x044: /* MSCR */ 471fcbd8018SJean-Christophe Dubois s->mscr = value & 0xfe; 472fcbd8018SJean-Christophe Dubois break; 473fcbd8018SJean-Christophe Dubois case 0x064: /* MIBC */ 474fcbd8018SJean-Christophe Dubois /* TODO: Implement MIB. */ 475fcbd8018SJean-Christophe Dubois s->mibc = (value & 0x80000000) ? 0xc0000000 : 0; 476fcbd8018SJean-Christophe Dubois break; 477fcbd8018SJean-Christophe Dubois case 0x084: /* RCR */ 478fcbd8018SJean-Christophe Dubois s->rcr = value & 0x07ff003f; 479fcbd8018SJean-Christophe Dubois /* TODO: Implement LOOP mode. */ 480fcbd8018SJean-Christophe Dubois break; 481fcbd8018SJean-Christophe Dubois case 0x0c4: /* TCR */ 482fcbd8018SJean-Christophe Dubois /* We transmit immediately, so raise GRA immediately. */ 483fcbd8018SJean-Christophe Dubois s->tcr = value; 484fcbd8018SJean-Christophe Dubois if (value & 1) { 485fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_GRA; 486fcbd8018SJean-Christophe Dubois } 487fcbd8018SJean-Christophe Dubois break; 488fcbd8018SJean-Christophe Dubois case 0x0e4: /* PALR */ 489fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[0] = value >> 24; 490fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[1] = value >> 16; 491fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[2] = value >> 8; 492fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[3] = value; 493fcbd8018SJean-Christophe Dubois break; 494fcbd8018SJean-Christophe Dubois case 0x0e8: /* PAUR */ 495fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[4] = value >> 24; 496fcbd8018SJean-Christophe Dubois s->conf.macaddr.a[5] = value >> 16; 497fcbd8018SJean-Christophe Dubois break; 498fcbd8018SJean-Christophe Dubois case 0x0ec: /* OPDR */ 499fcbd8018SJean-Christophe Dubois break; 500fcbd8018SJean-Christophe Dubois case 0x118: /* IAUR */ 501fcbd8018SJean-Christophe Dubois case 0x11c: /* IALR */ 502fcbd8018SJean-Christophe Dubois case 0x120: /* GAUR */ 503fcbd8018SJean-Christophe Dubois case 0x124: /* GALR */ 504fcbd8018SJean-Christophe Dubois /* TODO: implement MAC hash filtering. */ 505fcbd8018SJean-Christophe Dubois break; 506fcbd8018SJean-Christophe Dubois case 0x144: /* TFWR */ 507fcbd8018SJean-Christophe Dubois s->tfwr = value & 3; 508fcbd8018SJean-Christophe Dubois break; 509fcbd8018SJean-Christophe Dubois case 0x14c: /* FRBR */ 510fcbd8018SJean-Christophe Dubois /* FRBR writes ignored. */ 511fcbd8018SJean-Christophe Dubois break; 512fcbd8018SJean-Christophe Dubois case 0x150: /* FRSR */ 513fcbd8018SJean-Christophe Dubois s->frsr = (value & 0x3fc) | 0x400; 514fcbd8018SJean-Christophe Dubois break; 515fcbd8018SJean-Christophe Dubois case 0x180: /* ERDSR */ 516fcbd8018SJean-Christophe Dubois s->erdsr = value & ~3; 517fcbd8018SJean-Christophe Dubois s->rx_descriptor = s->erdsr; 518fcbd8018SJean-Christophe Dubois break; 519fcbd8018SJean-Christophe Dubois case 0x184: /* ETDSR */ 520fcbd8018SJean-Christophe Dubois s->etdsr = value & ~3; 521fcbd8018SJean-Christophe Dubois s->tx_descriptor = s->etdsr; 522fcbd8018SJean-Christophe Dubois break; 523fcbd8018SJean-Christophe Dubois case 0x188: /* EMRBR */ 524fcbd8018SJean-Christophe Dubois s->emrbr = value & 0x7f0; 525fcbd8018SJean-Christophe Dubois break; 526fcbd8018SJean-Christophe Dubois case 0x300: /* MIIGSK_CFGR */ 527fcbd8018SJean-Christophe Dubois s->miigsk_cfgr = value & 0x53; 528fcbd8018SJean-Christophe Dubois break; 529fcbd8018SJean-Christophe Dubois case 0x308: /* MIIGSK_ENR */ 530fcbd8018SJean-Christophe Dubois s->miigsk_enr = (value & 0x2) ? 0x6 : 0; 531fcbd8018SJean-Christophe Dubois break; 532fcbd8018SJean-Christophe Dubois default: 533b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" 534b72d8d25SJean-Christophe Dubois HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); 535fcbd8018SJean-Christophe Dubois break; 536fcbd8018SJean-Christophe Dubois } 537fcbd8018SJean-Christophe Dubois 538fcbd8018SJean-Christophe Dubois imx_fec_update(s); 539fcbd8018SJean-Christophe Dubois } 540fcbd8018SJean-Christophe Dubois 541fcbd8018SJean-Christophe Dubois static int imx_fec_can_receive(NetClientState *nc) 542fcbd8018SJean-Christophe Dubois { 543fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 544fcbd8018SJean-Christophe Dubois 545fcbd8018SJean-Christophe Dubois return s->rx_enabled; 546fcbd8018SJean-Christophe Dubois } 547fcbd8018SJean-Christophe Dubois 548fcbd8018SJean-Christophe Dubois static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, 549fcbd8018SJean-Christophe Dubois size_t len) 550fcbd8018SJean-Christophe Dubois { 551fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 552fcbd8018SJean-Christophe Dubois IMXFECBufDesc bd; 553fcbd8018SJean-Christophe Dubois uint32_t flags = 0; 554fcbd8018SJean-Christophe Dubois uint32_t addr; 555fcbd8018SJean-Christophe Dubois uint32_t crc; 556fcbd8018SJean-Christophe Dubois uint32_t buf_addr; 557fcbd8018SJean-Christophe Dubois uint8_t *crc_ptr; 558fcbd8018SJean-Christophe Dubois unsigned int buf_len; 559fcbd8018SJean-Christophe Dubois size_t size = len; 560fcbd8018SJean-Christophe Dubois 561fcbd8018SJean-Christophe Dubois FEC_PRINTF("len %d\n", (int)size); 562fcbd8018SJean-Christophe Dubois 563fcbd8018SJean-Christophe Dubois if (!s->rx_enabled) { 564b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", 565fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 566fcbd8018SJean-Christophe Dubois return 0; 567fcbd8018SJean-Christophe Dubois } 568fcbd8018SJean-Christophe Dubois 569fcbd8018SJean-Christophe Dubois /* 4 bytes for the CRC. */ 570fcbd8018SJean-Christophe Dubois size += 4; 571fcbd8018SJean-Christophe Dubois crc = cpu_to_be32(crc32(~0, buf, size)); 572fcbd8018SJean-Christophe Dubois crc_ptr = (uint8_t *) &crc; 573fcbd8018SJean-Christophe Dubois 574fcbd8018SJean-Christophe Dubois /* Huge frames are truncted. */ 575fcbd8018SJean-Christophe Dubois if (size > FEC_MAX_FRAME_SIZE) { 576fcbd8018SJean-Christophe Dubois size = FEC_MAX_FRAME_SIZE; 577fcbd8018SJean-Christophe Dubois flags |= FEC_BD_TR | FEC_BD_LG; 578fcbd8018SJean-Christophe Dubois } 579fcbd8018SJean-Christophe Dubois 580fcbd8018SJean-Christophe Dubois /* Frames larger than the user limit just set error flags. */ 581fcbd8018SJean-Christophe Dubois if (size > (s->rcr >> 16)) { 582fcbd8018SJean-Christophe Dubois flags |= FEC_BD_LG; 583fcbd8018SJean-Christophe Dubois } 584fcbd8018SJean-Christophe Dubois 585fcbd8018SJean-Christophe Dubois addr = s->rx_descriptor; 586fcbd8018SJean-Christophe Dubois while (size > 0) { 587fcbd8018SJean-Christophe Dubois imx_fec_read_bd(&bd, addr); 588fcbd8018SJean-Christophe Dubois if ((bd.flags & FEC_BD_E) == 0) { 589fcbd8018SJean-Christophe Dubois /* No descriptors available. Bail out. */ 590fcbd8018SJean-Christophe Dubois /* 591fcbd8018SJean-Christophe Dubois * FIXME: This is wrong. We should probably either 592fcbd8018SJean-Christophe Dubois * save the remainder for when more RX buffers are 593fcbd8018SJean-Christophe Dubois * available, or flag an error. 594fcbd8018SJean-Christophe Dubois */ 595b72d8d25SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", 596fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, __func__); 597fcbd8018SJean-Christophe Dubois break; 598fcbd8018SJean-Christophe Dubois } 599fcbd8018SJean-Christophe Dubois buf_len = (size <= s->emrbr) ? size : s->emrbr; 600fcbd8018SJean-Christophe Dubois bd.length = buf_len; 601fcbd8018SJean-Christophe Dubois size -= buf_len; 602b72d8d25SJean-Christophe Dubois 603b72d8d25SJean-Christophe Dubois FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); 604b72d8d25SJean-Christophe Dubois 605fcbd8018SJean-Christophe Dubois /* The last 4 bytes are the CRC. */ 606fcbd8018SJean-Christophe Dubois if (size < 4) { 607fcbd8018SJean-Christophe Dubois buf_len += size - 4; 608fcbd8018SJean-Christophe Dubois } 609fcbd8018SJean-Christophe Dubois buf_addr = bd.data; 610fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); 611fcbd8018SJean-Christophe Dubois buf += buf_len; 612fcbd8018SJean-Christophe Dubois if (size < 4) { 613fcbd8018SJean-Christophe Dubois dma_memory_write(&address_space_memory, buf_addr + buf_len, 614fcbd8018SJean-Christophe Dubois crc_ptr, 4 - size); 615fcbd8018SJean-Christophe Dubois crc_ptr += 4 - size; 616fcbd8018SJean-Christophe Dubois } 617fcbd8018SJean-Christophe Dubois bd.flags &= ~FEC_BD_E; 618fcbd8018SJean-Christophe Dubois if (size == 0) { 619fcbd8018SJean-Christophe Dubois /* Last buffer in frame. */ 620fcbd8018SJean-Christophe Dubois bd.flags |= flags | FEC_BD_L; 621fcbd8018SJean-Christophe Dubois FEC_PRINTF("rx frame flags %04x\n", bd.flags); 622fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_RXF; 623fcbd8018SJean-Christophe Dubois } else { 624fcbd8018SJean-Christophe Dubois s->eir |= FEC_INT_RXB; 625fcbd8018SJean-Christophe Dubois } 626fcbd8018SJean-Christophe Dubois imx_fec_write_bd(&bd, addr); 627fcbd8018SJean-Christophe Dubois /* Advance to the next descriptor. */ 628fcbd8018SJean-Christophe Dubois if ((bd.flags & FEC_BD_W) != 0) { 629fcbd8018SJean-Christophe Dubois addr = s->erdsr; 630fcbd8018SJean-Christophe Dubois } else { 631fcbd8018SJean-Christophe Dubois addr += 8; 632fcbd8018SJean-Christophe Dubois } 633fcbd8018SJean-Christophe Dubois } 634fcbd8018SJean-Christophe Dubois s->rx_descriptor = addr; 635fcbd8018SJean-Christophe Dubois imx_fec_enable_rx(s); 636fcbd8018SJean-Christophe Dubois imx_fec_update(s); 637fcbd8018SJean-Christophe Dubois return len; 638fcbd8018SJean-Christophe Dubois } 639fcbd8018SJean-Christophe Dubois 640fcbd8018SJean-Christophe Dubois static const MemoryRegionOps imx_fec_ops = { 641fcbd8018SJean-Christophe Dubois .read = imx_fec_read, 642fcbd8018SJean-Christophe Dubois .write = imx_fec_write, 643fcbd8018SJean-Christophe Dubois .valid.min_access_size = 4, 644fcbd8018SJean-Christophe Dubois .valid.max_access_size = 4, 645fcbd8018SJean-Christophe Dubois .endianness = DEVICE_NATIVE_ENDIAN, 646fcbd8018SJean-Christophe Dubois }; 647fcbd8018SJean-Christophe Dubois 648fcbd8018SJean-Christophe Dubois static void imx_fec_cleanup(NetClientState *nc) 649fcbd8018SJean-Christophe Dubois { 650fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); 651fcbd8018SJean-Christophe Dubois 652fcbd8018SJean-Christophe Dubois s->nic = NULL; 653fcbd8018SJean-Christophe Dubois } 654fcbd8018SJean-Christophe Dubois 655fcbd8018SJean-Christophe Dubois static NetClientInfo net_imx_fec_info = { 656fcbd8018SJean-Christophe Dubois .type = NET_CLIENT_OPTIONS_KIND_NIC, 657fcbd8018SJean-Christophe Dubois .size = sizeof(NICState), 658fcbd8018SJean-Christophe Dubois .can_receive = imx_fec_can_receive, 659fcbd8018SJean-Christophe Dubois .receive = imx_fec_receive, 660fcbd8018SJean-Christophe Dubois .cleanup = imx_fec_cleanup, 661fcbd8018SJean-Christophe Dubois .link_status_changed = imx_fec_set_link, 662fcbd8018SJean-Christophe Dubois }; 663fcbd8018SJean-Christophe Dubois 664fcbd8018SJean-Christophe Dubois 665fcbd8018SJean-Christophe Dubois static void imx_fec_realize(DeviceState *dev, Error **errp) 666fcbd8018SJean-Christophe Dubois { 667fcbd8018SJean-Christophe Dubois IMXFECState *s = IMX_FEC(dev); 668fcbd8018SJean-Christophe Dubois SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 669fcbd8018SJean-Christophe Dubois 670fcbd8018SJean-Christophe Dubois memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s, 671fcbd8018SJean-Christophe Dubois TYPE_IMX_FEC, 0x400); 672fcbd8018SJean-Christophe Dubois sysbus_init_mmio(sbd, &s->iomem); 673fcbd8018SJean-Christophe Dubois sysbus_init_irq(sbd, &s->irq); 674fcbd8018SJean-Christophe Dubois qemu_macaddr_default_if_unset(&s->conf.macaddr); 675fcbd8018SJean-Christophe Dubois 676fcbd8018SJean-Christophe Dubois s->conf.peers.ncs[0] = nd_table[0].netdev; 677fcbd8018SJean-Christophe Dubois 678fcbd8018SJean-Christophe Dubois s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf, 679fcbd8018SJean-Christophe Dubois object_get_typename(OBJECT(dev)), DEVICE(dev)->id, 680fcbd8018SJean-Christophe Dubois s); 681fcbd8018SJean-Christophe Dubois qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 682fcbd8018SJean-Christophe Dubois } 683fcbd8018SJean-Christophe Dubois 684fcbd8018SJean-Christophe Dubois static Property imx_fec_properties[] = { 685fcbd8018SJean-Christophe Dubois DEFINE_NIC_PROPERTIES(IMXFECState, conf), 686fcbd8018SJean-Christophe Dubois DEFINE_PROP_END_OF_LIST(), 687fcbd8018SJean-Christophe Dubois }; 688fcbd8018SJean-Christophe Dubois 689fcbd8018SJean-Christophe Dubois static void imx_fec_class_init(ObjectClass *klass, void *data) 690fcbd8018SJean-Christophe Dubois { 691fcbd8018SJean-Christophe Dubois DeviceClass *dc = DEVICE_CLASS(klass); 692fcbd8018SJean-Christophe Dubois 693fcbd8018SJean-Christophe Dubois dc->vmsd = &vmstate_imx_fec; 694fcbd8018SJean-Christophe Dubois dc->reset = imx_fec_reset; 695fcbd8018SJean-Christophe Dubois dc->props = imx_fec_properties; 696fcbd8018SJean-Christophe Dubois dc->realize = imx_fec_realize; 697eccfa35eSJean-Christophe Dubois dc->desc = "i.MX FEC Ethernet Controller"; 698fcbd8018SJean-Christophe Dubois } 699fcbd8018SJean-Christophe Dubois 700fcbd8018SJean-Christophe Dubois static const TypeInfo imx_fec_info = { 701fcbd8018SJean-Christophe Dubois .name = TYPE_IMX_FEC, 702fcbd8018SJean-Christophe Dubois .parent = TYPE_SYS_BUS_DEVICE, 703fcbd8018SJean-Christophe Dubois .instance_size = sizeof(IMXFECState), 704fcbd8018SJean-Christophe Dubois .class_init = imx_fec_class_init, 705fcbd8018SJean-Christophe Dubois }; 706fcbd8018SJean-Christophe Dubois 707fcbd8018SJean-Christophe Dubois static void imx_fec_register_types(void) 708fcbd8018SJean-Christophe Dubois { 709fcbd8018SJean-Christophe Dubois type_register_static(&imx_fec_info); 710fcbd8018SJean-Christophe Dubois } 711fcbd8018SJean-Christophe Dubois 712fcbd8018SJean-Christophe Dubois type_init(imx_fec_register_types) 713