1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279 |
- /*
- * i.MX Fast Ethernet Controller emulation.
- *
- * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
- *
- * Based on Coldfire Fast Ethernet Controller emulation.
- *
- * Copyright (c) 2007 CodeSourcery.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
- #include "qemu/osdep.h"
- #include "hw/irq.h"
- #include "hw/net/imx_fec.h"
- #include "hw/qdev-properties.h"
- #include "migration/vmstate.h"
- #include "system/dma.h"
- #include "qemu/log.h"
- #include "qemu/module.h"
- #include "net/checksum.h"
- #include "net/eth.h"
- #include "trace.h"
- #include <zlib.h> /* for crc32 */
- #define IMX_MAX_DESC 1024
- static const char *imx_default_reg_name(IMXFECState *s, uint32_t index)
- {
- static char tmp[20];
- snprintf(tmp, sizeof(tmp), "index %d", index);
- return tmp;
- }
- static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
- {
- switch (index) {
- case ENET_FRBR:
- return "FRBR";
- case ENET_FRSR:
- return "FRSR";
- case ENET_MIIGSK_CFGR:
- return "MIIGSK_CFGR";
- case ENET_MIIGSK_ENR:
- return "MIIGSK_ENR";
- default:
- return imx_default_reg_name(s, index);
- }
- }
- static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index)
- {
- switch (index) {
- case ENET_RSFL:
- return "RSFL";
- case ENET_RSEM:
- return "RSEM";
- case ENET_RAEM:
- return "RAEM";
- case ENET_RAFL:
- return "RAFL";
- case ENET_TSEM:
- return "TSEM";
- case ENET_TAEM:
- return "TAEM";
- case ENET_TAFL:
- return "TAFL";
- case ENET_TIPG:
- return "TIPG";
- case ENET_FTRL:
- return "FTRL";
- case ENET_TACC:
- return "TACC";
- case ENET_RACC:
- return "RACC";
- case ENET_ATCR:
- return "ATCR";
- case ENET_ATVR:
- return "ATVR";
- case ENET_ATOFF:
- return "ATOFF";
- case ENET_ATPER:
- return "ATPER";
- case ENET_ATCOR:
- return "ATCOR";
- case ENET_ATINC:
- return "ATINC";
- case ENET_ATSTMP:
- return "ATSTMP";
- case ENET_TGSR:
- return "TGSR";
- case ENET_TCSR0:
- return "TCSR0";
- case ENET_TCCR0:
- return "TCCR0";
- case ENET_TCSR1:
- return "TCSR1";
- case ENET_TCCR1:
- return "TCCR1";
- case ENET_TCSR2:
- return "TCSR2";
- case ENET_TCCR2:
- return "TCCR2";
- case ENET_TCSR3:
- return "TCSR3";
- case ENET_TCCR3:
- return "TCCR3";
- default:
- return imx_default_reg_name(s, index);
- }
- }
- static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index)
- {
- switch (index) {
- case ENET_EIR:
- return "EIR";
- case ENET_EIMR:
- return "EIMR";
- case ENET_RDAR:
- return "RDAR";
- case ENET_TDAR:
- return "TDAR";
- case ENET_ECR:
- return "ECR";
- case ENET_MMFR:
- return "MMFR";
- case ENET_MSCR:
- return "MSCR";
- case ENET_MIBC:
- return "MIBC";
- case ENET_RCR:
- return "RCR";
- case ENET_TCR:
- return "TCR";
- case ENET_PALR:
- return "PALR";
- case ENET_PAUR:
- return "PAUR";
- case ENET_OPD:
- return "OPD";
- case ENET_IAUR:
- return "IAUR";
- case ENET_IALR:
- return "IALR";
- case ENET_GAUR:
- return "GAUR";
- case ENET_GALR:
- return "GALR";
- case ENET_TFWR:
- return "TFWR";
- case ENET_RDSR:
- return "RDSR";
- case ENET_TDSR:
- return "TDSR";
- case ENET_MRBR:
- return "MRBR";
- default:
- if (s->is_fec) {
- return imx_fec_reg_name(s, index);
- } else {
- return imx_enet_reg_name(s, index);
- }
- }
- }
- /*
- * Versions of this device with more than one TX descriptor save the
- * 2nd and 3rd descriptors in a subsection, to maintain migration
- * compatibility with previous versions of the device that only
- * supported a single descriptor.
- */
- static bool imx_eth_is_multi_tx_ring(void *opaque)
- {
- IMXFECState *s = IMX_FEC(opaque);
- return s->tx_ring_num > 1;
- }
- static const VMStateDescription vmstate_imx_eth_txdescs = {
- .name = "imx.fec/txdescs",
- .version_id = 1,
- .minimum_version_id = 1,
- .needed = imx_eth_is_multi_tx_ring,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32(tx_descriptor[1], IMXFECState),
- VMSTATE_UINT32(tx_descriptor[2], IMXFECState),
- VMSTATE_END_OF_LIST()
- }
- };
- static const VMStateDescription vmstate_imx_eth = {
- .name = TYPE_IMX_FEC,
- .version_id = 3,
- .minimum_version_id = 3,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX),
- VMSTATE_UINT32(rx_descriptor, IMXFECState),
- VMSTATE_UINT32(tx_descriptor[0], IMXFECState),
- VMSTATE_END_OF_LIST()
- },
- .subsections = (const VMStateDescription * const []) {
- &vmstate_imx_eth_txdescs,
- NULL
- },
- };
- static void imx_eth_update(IMXFECState *s);
- /*
- * The MII phy could raise a GPIO to the processor which in turn
- * could be handled as an interrpt by the OS.
- * For now we don't handle any GPIO/interrupt line, so the OS will
- * have to poll for the PHY status.
- */
- static void imx_phy_update_irq(void *opaque, int n, int level)
- {
- imx_eth_update(opaque);
- }
- static void imx_eth_set_link(NetClientState *nc)
- {
- lan9118_phy_update_link(&IMX_FEC(qemu_get_nic_opaque(nc))->mii,
- nc->link_down);
- }
- static uint32_t imx_phy_read(IMXFECState *s, int reg)
- {
- uint32_t phy = reg / 32;
- if (!s->phy_connected) {
- return 0xffff;
- }
- if (phy != s->phy_num) {
- if (s->phy_consumer && phy == s->phy_consumer->phy_num) {
- s = s->phy_consumer;
- } else {
- trace_imx_phy_read_num(phy, s->phy_num);
- return 0xffff;
- }
- }
- reg %= 32;
- return lan9118_phy_read(&s->mii, reg);
- }
- static void imx_phy_write(IMXFECState *s, int reg, uint32_t val)
- {
- uint32_t phy = reg / 32;
- if (!s->phy_connected) {
- return;
- }
- if (phy != s->phy_num) {
- if (s->phy_consumer && phy == s->phy_consumer->phy_num) {
- s = s->phy_consumer;
- } else {
- trace_imx_phy_write_num(phy, s->phy_num);
- return;
- }
- }
- reg %= 32;
- lan9118_phy_write(&s->mii, reg, val);
- }
- static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr)
- {
- dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd),
- MEMTXATTRS_UNSPECIFIED);
- trace_imx_fec_read_bd(addr, bd->flags, bd->length, bd->data);
- }
- static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
- {
- dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd),
- MEMTXATTRS_UNSPECIFIED);
- }
- static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr)
- {
- dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd),
- MEMTXATTRS_UNSPECIFIED);
- trace_imx_enet_read_bd(addr, bd->flags, bd->length, bd->data,
- bd->option, bd->status);
- }
- static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr)
- {
- dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd),
- MEMTXATTRS_UNSPECIFIED);
- }
- static void imx_eth_update(IMXFECState *s)
- {
- /*
- * Previous versions of qemu had the ENET_INT_MAC and ENET_INT_TS_TIMER
- * interrupts swapped. This worked with older versions of Linux (4.14
- * and older) since Linux associated both interrupt lines with Ethernet
- * MAC interrupts. Specifically,
- * - Linux 4.15 and later have separate interrupt handlers for the MAC and
- * timer interrupts. Those versions of Linux fail with versions of QEMU
- * with swapped interrupt assignments.
- * - In linux 4.14, both interrupt lines were registered with the Ethernet
- * MAC interrupt handler. As a result, all versions of qemu happen to
- * work, though that is accidental.
- * - In Linux 4.9 and older, the timer interrupt was registered directly
- * with the Ethernet MAC interrupt handler. The MAC interrupt was
- * redirected to a GPIO interrupt to work around erratum ERR006687.
- * This was implemented using the SOC's IOMUX block. In qemu, this GPIO
- * interrupt never fired since IOMUX is currently not supported in qemu.
- * Linux instead received MAC interrupts on the timer interrupt.
- * As a result, qemu versions with the swapped interrupt assignment work,
- * albeit accidentally, but qemu versions with the correct interrupt
- * assignment fail.
- *
- * To ensure that all versions of Linux work, generate ENET_INT_MAC
- * interrupts on both interrupt lines. This should be changed if and when
- * qemu supports IOMUX.
- */
- if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] &
- (ENET_INT_MAC | ENET_INT_TS_TIMER)) {
- qemu_set_irq(s->irq[1], 1);
- } else {
- qemu_set_irq(s->irq[1], 0);
- }
- if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_MAC) {
- qemu_set_irq(s->irq[0], 1);
- } else {
- qemu_set_irq(s->irq[0], 0);
- }
- }
- static void imx_fec_do_tx(IMXFECState *s)
- {
- int frame_size = 0, descnt = 0;
- uint8_t *ptr = s->frame;
- uint32_t addr = s->tx_descriptor[0];
- while (descnt++ < IMX_MAX_DESC) {
- IMXFECBufDesc bd;
- int len;
- imx_fec_read_bd(&bd, addr);
- if ((bd.flags & ENET_BD_R) == 0) {
- /* Run out of descriptors to transmit. */
- trace_imx_eth_tx_bd_busy();
- break;
- }
- len = bd.length;
- if (frame_size + len > ENET_MAX_FRAME_SIZE) {
- len = ENET_MAX_FRAME_SIZE - frame_size;
- s->regs[ENET_EIR] |= ENET_INT_BABT;
- }
- dma_memory_read(&address_space_memory, bd.data, ptr, len,
- MEMTXATTRS_UNSPECIFIED);
- ptr += len;
- frame_size += len;
- if (bd.flags & ENET_BD_L) {
- /* Last buffer in frame. */
- qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size);
- ptr = s->frame;
- frame_size = 0;
- s->regs[ENET_EIR] |= ENET_INT_TXF;
- }
- s->regs[ENET_EIR] |= ENET_INT_TXB;
- bd.flags &= ~ENET_BD_R;
- /* Write back the modified descriptor. */
- imx_fec_write_bd(&bd, addr);
- /* Advance to the next descriptor. */
- if ((bd.flags & ENET_BD_W) != 0) {
- addr = s->regs[ENET_TDSR];
- } else {
- addr += sizeof(bd);
- }
- }
- s->tx_descriptor[0] = addr;
- imx_eth_update(s);
- }
- static void imx_enet_do_tx(IMXFECState *s, uint32_t index)
- {
- int frame_size = 0, descnt = 0;
- uint8_t *ptr = s->frame;
- uint32_t addr, int_txb, int_txf, tdsr;
- size_t ring;
- switch (index) {
- case ENET_TDAR:
- ring = 0;
- int_txb = ENET_INT_TXB;
- int_txf = ENET_INT_TXF;
- tdsr = ENET_TDSR;
- break;
- case ENET_TDAR1:
- ring = 1;
- int_txb = ENET_INT_TXB1;
- int_txf = ENET_INT_TXF1;
- tdsr = ENET_TDSR1;
- break;
- case ENET_TDAR2:
- ring = 2;
- int_txb = ENET_INT_TXB2;
- int_txf = ENET_INT_TXF2;
- tdsr = ENET_TDSR2;
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: bogus value for index %x\n",
- __func__, index);
- abort();
- break;
- }
- addr = s->tx_descriptor[ring];
- while (descnt++ < IMX_MAX_DESC) {
- IMXENETBufDesc bd;
- int len;
- imx_enet_read_bd(&bd, addr);
- if ((bd.flags & ENET_BD_R) == 0) {
- /* Run out of descriptors to transmit. */
- trace_imx_eth_tx_bd_busy();
- break;
- }
- len = bd.length;
- if (frame_size + len > ENET_MAX_FRAME_SIZE) {
- len = ENET_MAX_FRAME_SIZE - frame_size;
- s->regs[ENET_EIR] |= ENET_INT_BABT;
- }
- dma_memory_read(&address_space_memory, bd.data, ptr, len,
- MEMTXATTRS_UNSPECIFIED);
- ptr += len;
- frame_size += len;
- if (bd.flags & ENET_BD_L) {
- int csum = 0;
- if (bd.option & ENET_BD_PINS) {
- csum |= (CSUM_TCP | CSUM_UDP);
- }
- if (bd.option & ENET_BD_IINS) {
- csum |= CSUM_IP;
- }
- if (csum) {
- net_checksum_calculate(s->frame, frame_size, csum);
- }
- /* Last buffer in frame. */
- qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size);
- ptr = s->frame;
- frame_size = 0;
- if (bd.option & ENET_BD_TX_INT) {
- s->regs[ENET_EIR] |= int_txf;
- }
- /* Indicate that we've updated the last buffer descriptor. */
- bd.last_buffer = ENET_BD_BDU;
- }
- if (bd.option & ENET_BD_TX_INT) {
- s->regs[ENET_EIR] |= int_txb;
- }
- bd.flags &= ~ENET_BD_R;
- /* Write back the modified descriptor. */
- imx_enet_write_bd(&bd, addr);
- /* Advance to the next descriptor. */
- if ((bd.flags & ENET_BD_W) != 0) {
- addr = s->regs[tdsr];
- } else {
- addr += sizeof(bd);
- }
- }
- s->tx_descriptor[ring] = addr;
- imx_eth_update(s);
- }
- static void imx_eth_do_tx(IMXFECState *s, uint32_t index)
- {
- if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
- imx_enet_do_tx(s, index);
- } else {
- imx_fec_do_tx(s);
- }
- }
- static void imx_eth_enable_rx(IMXFECState *s, bool flush)
- {
- IMXFECBufDesc bd;
- imx_fec_read_bd(&bd, s->rx_descriptor);
- s->regs[ENET_RDAR] = (bd.flags & ENET_BD_E) ? ENET_RDAR_RDAR : 0;
- if (!s->regs[ENET_RDAR]) {
- trace_imx_eth_rx_bd_full();
- } else if (flush) {
- qemu_flush_queued_packets(qemu_get_queue(s->nic));
- }
- }
- static void imx_eth_reset(DeviceState *d)
- {
- IMXFECState *s = IMX_FEC(d);
- /* Reset the Device */
- memset(s->regs, 0, sizeof(s->regs));
- s->regs[ENET_ECR] = 0xf0000000;
- s->regs[ENET_MIBC] = 0xc0000000;
- s->regs[ENET_RCR] = 0x05ee0001;
- s->regs[ENET_OPD] = 0x00010000;
- s->regs[ENET_PALR] = (s->conf.macaddr.a[0] << 24)
- | (s->conf.macaddr.a[1] << 16)
- | (s->conf.macaddr.a[2] << 8)
- | s->conf.macaddr.a[3];
- s->regs[ENET_PAUR] = (s->conf.macaddr.a[4] << 24)
- | (s->conf.macaddr.a[5] << 16)
- | 0x8808;
- if (s->is_fec) {
- s->regs[ENET_FRBR] = 0x00000600;
- s->regs[ENET_FRSR] = 0x00000500;
- s->regs[ENET_MIIGSK_ENR] = 0x00000006;
- } else {
- s->regs[ENET_RAEM] = 0x00000004;
- s->regs[ENET_RAFL] = 0x00000004;
- s->regs[ENET_TAEM] = 0x00000004;
- s->regs[ENET_TAFL] = 0x00000008;
- s->regs[ENET_TIPG] = 0x0000000c;
- s->regs[ENET_FTRL] = 0x000007ff;
- s->regs[ENET_ATPER] = 0x3b9aca00;
- }
- s->rx_descriptor = 0;
- memset(s->tx_descriptor, 0, sizeof(s->tx_descriptor));
- }
- static uint32_t imx_default_read(IMXFECState *s, uint32_t index)
- {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
- PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
- return 0;
- }
- static uint32_t imx_fec_read(IMXFECState *s, uint32_t index)
- {
- switch (index) {
- case ENET_FRBR:
- case ENET_FRSR:
- case ENET_MIIGSK_CFGR:
- case ENET_MIIGSK_ENR:
- return s->regs[index];
- default:
- return imx_default_read(s, index);
- }
- }
- static uint32_t imx_enet_read(IMXFECState *s, uint32_t index)
- {
- switch (index) {
- case ENET_RSFL:
- case ENET_RSEM:
- case ENET_RAEM:
- case ENET_RAFL:
- case ENET_TSEM:
- case ENET_TAEM:
- case ENET_TAFL:
- case ENET_TIPG:
- case ENET_FTRL:
- case ENET_TACC:
- case ENET_RACC:
- case ENET_ATCR:
- case ENET_ATVR:
- case ENET_ATOFF:
- case ENET_ATPER:
- case ENET_ATCOR:
- case ENET_ATINC:
- case ENET_ATSTMP:
- case ENET_TGSR:
- case ENET_TCSR0:
- case ENET_TCCR0:
- case ENET_TCSR1:
- case ENET_TCCR1:
- case ENET_TCSR2:
- case ENET_TCCR2:
- case ENET_TCSR3:
- case ENET_TCCR3:
- return s->regs[index];
- default:
- return imx_default_read(s, index);
- }
- }
- static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size)
- {
- uint32_t value = 0;
- IMXFECState *s = IMX_FEC(opaque);
- uint32_t index = offset >> 2;
- switch (index) {
- case ENET_EIR:
- case ENET_EIMR:
- case ENET_RDAR:
- case ENET_TDAR:
- case ENET_ECR:
- case ENET_MMFR:
- case ENET_MSCR:
- case ENET_MIBC:
- case ENET_RCR:
- case ENET_TCR:
- case ENET_PALR:
- case ENET_PAUR:
- case ENET_OPD:
- case ENET_IAUR:
- case ENET_IALR:
- case ENET_GAUR:
- case ENET_GALR:
- case ENET_TFWR:
- case ENET_RDSR:
- case ENET_TDSR:
- case ENET_MRBR:
- value = s->regs[index];
- break;
- default:
- if (s->is_fec) {
- value = imx_fec_read(s, index);
- } else {
- value = imx_enet_read(s, index);
- }
- break;
- }
- trace_imx_eth_read(index, imx_eth_reg_name(s, index), value);
- return value;
- }
- static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value)
- {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
- PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
- return;
- }
- static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value)
- {
- switch (index) {
- case ENET_FRBR:
- /* FRBR is read only */
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n",
- TYPE_IMX_FEC, __func__);
- break;
- case ENET_FRSR:
- s->regs[index] = (value & 0x000003fc) | 0x00000400;
- break;
- case ENET_MIIGSK_CFGR:
- s->regs[index] = value & 0x00000053;
- break;
- case ENET_MIIGSK_ENR:
- s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
- break;
- default:
- imx_default_write(s, index, value);
- break;
- }
- }
- static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value)
- {
- switch (index) {
- case ENET_RSFL:
- case ENET_RSEM:
- case ENET_RAEM:
- case ENET_RAFL:
- case ENET_TSEM:
- case ENET_TAEM:
- case ENET_TAFL:
- s->regs[index] = value & 0x000001ff;
- break;
- case ENET_TIPG:
- s->regs[index] = value & 0x0000001f;
- break;
- case ENET_FTRL:
- s->regs[index] = value & 0x00003fff;
- break;
- case ENET_TACC:
- s->regs[index] = value & 0x00000019;
- break;
- case ENET_RACC:
- s->regs[index] = value & 0x000000C7;
- break;
- case ENET_ATCR:
- s->regs[index] = value & 0x00002a9d;
- break;
- case ENET_ATVR:
- case ENET_ATOFF:
- case ENET_ATPER:
- s->regs[index] = value;
- break;
- case ENET_ATSTMP:
- /* ATSTMP is read only */
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n",
- TYPE_IMX_FEC, __func__);
- break;
- case ENET_ATCOR:
- s->regs[index] = value & 0x7fffffff;
- break;
- case ENET_ATINC:
- s->regs[index] = value & 0x00007f7f;
- break;
- case ENET_TGSR:
- /* implement clear timer flag */
- s->regs[index] &= ~(value & 0x0000000f); /* all bits W1C */
- break;
- case ENET_TCSR0:
- case ENET_TCSR1:
- case ENET_TCSR2:
- case ENET_TCSR3:
- s->regs[index] &= ~(value & 0x00000080); /* W1C bits */
- s->regs[index] &= ~0x0000007d; /* writable fields */
- s->regs[index] |= (value & 0x0000007d);
- break;
- case ENET_TCCR0:
- case ENET_TCCR1:
- case ENET_TCCR2:
- case ENET_TCCR3:
- s->regs[index] = value;
- break;
- default:
- imx_default_write(s, index, value);
- break;
- }
- }
- static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value,
- unsigned size)
- {
- IMXFECState *s = IMX_FEC(opaque);
- const bool single_tx_ring = !imx_eth_is_multi_tx_ring(s);
- uint32_t index = offset >> 2;
- trace_imx_eth_write(index, imx_eth_reg_name(s, index), value);
- switch (index) {
- case ENET_EIR:
- s->regs[index] &= ~value;
- break;
- case ENET_EIMR:
- s->regs[index] = value;
- break;
- case ENET_RDAR:
- if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
- if (!s->regs[index]) {
- imx_eth_enable_rx(s, true);
- }
- } else {
- s->regs[index] = 0;
- }
- break;
- case ENET_TDAR1:
- case ENET_TDAR2:
- if (unlikely(single_tx_ring)) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "[%s]%s: trying to access TDAR2 or TDAR1\n",
- TYPE_IMX_FEC, __func__);
- return;
- }
- /* fall through */
- case ENET_TDAR:
- if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
- s->regs[index] = ENET_TDAR_TDAR;
- imx_eth_do_tx(s, index);
- }
- s->regs[index] = 0;
- break;
- case ENET_ECR:
- if (value & ENET_ECR_RESET) {
- return imx_eth_reset(DEVICE(s));
- }
- s->regs[index] = value;
- if ((s->regs[index] & ENET_ECR_ETHEREN) == 0) {
- s->regs[ENET_RDAR] = 0;
- s->rx_descriptor = s->regs[ENET_RDSR];
- s->regs[ENET_TDAR] = 0;
- s->regs[ENET_TDAR1] = 0;
- s->regs[ENET_TDAR2] = 0;
- s->tx_descriptor[0] = s->regs[ENET_TDSR];
- s->tx_descriptor[1] = s->regs[ENET_TDSR1];
- s->tx_descriptor[2] = s->regs[ENET_TDSR2];
- }
- break;
- case ENET_MMFR:
- s->regs[index] = value;
- if (extract32(value, 29, 1)) {
- /* This is a read operation */
- s->regs[ENET_MMFR] = deposit32(s->regs[ENET_MMFR], 0, 16,
- imx_phy_read(s,
- extract32(value,
- 18, 10)));
- } else {
- /* This is a write operation */
- imx_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16));
- }
- /* raise the interrupt as the PHY operation is done */
- s->regs[ENET_EIR] |= ENET_INT_MII;
- break;
- case ENET_MSCR:
- s->regs[index] = value & 0xfe;
- break;
- case ENET_MIBC:
- /* TODO: Implement MIB. */
- s->regs[index] = (value & 0x80000000) ? 0xc0000000 : 0;
- break;
- case ENET_RCR:
- s->regs[index] = value & 0x07ff003f;
- /* TODO: Implement LOOP mode. */
- break;
- case ENET_TCR:
- /* We transmit immediately, so raise GRA immediately. */
- s->regs[index] = value;
- if (value & 1) {
- s->regs[ENET_EIR] |= ENET_INT_GRA;
- }
- break;
- case ENET_PALR:
- s->regs[index] = value;
- s->conf.macaddr.a[0] = value >> 24;
- s->conf.macaddr.a[1] = value >> 16;
- s->conf.macaddr.a[2] = value >> 8;
- s->conf.macaddr.a[3] = value;
- break;
- case ENET_PAUR:
- s->regs[index] = (value | 0x0000ffff) & 0xffff8808;
- s->conf.macaddr.a[4] = value >> 24;
- s->conf.macaddr.a[5] = value >> 16;
- break;
- case ENET_OPD:
- s->regs[index] = (value & 0x0000ffff) | 0x00010000;
- break;
- case ENET_IAUR:
- case ENET_IALR:
- case ENET_GAUR:
- case ENET_GALR:
- /* TODO: implement MAC hash filtering. */
- break;
- case ENET_TFWR:
- if (s->is_fec) {
- s->regs[index] = value & 0x3;
- } else {
- s->regs[index] = value & 0x13f;
- }
- break;
- case ENET_RDSR:
- if (s->is_fec) {
- s->regs[index] = value & ~3;
- } else {
- s->regs[index] = value & ~7;
- }
- s->rx_descriptor = s->regs[index];
- break;
- case ENET_TDSR:
- if (s->is_fec) {
- s->regs[index] = value & ~3;
- } else {
- s->regs[index] = value & ~7;
- }
- s->tx_descriptor[0] = s->regs[index];
- break;
- case ENET_TDSR1:
- if (unlikely(single_tx_ring)) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "[%s]%s: trying to access TDSR1\n",
- TYPE_IMX_FEC, __func__);
- return;
- }
- s->regs[index] = value & ~7;
- s->tx_descriptor[1] = s->regs[index];
- break;
- case ENET_TDSR2:
- if (unlikely(single_tx_ring)) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "[%s]%s: trying to access TDSR2\n",
- TYPE_IMX_FEC, __func__);
- return;
- }
- s->regs[index] = value & ~7;
- s->tx_descriptor[2] = s->regs[index];
- break;
- case ENET_MRBR:
- s->regs[index] = value & 0x00003ff0;
- break;
- default:
- if (s->is_fec) {
- imx_fec_write(s, index, value);
- } else {
- imx_enet_write(s, index, value);
- }
- return;
- }
- imx_eth_update(s);
- }
- static bool imx_eth_can_receive(NetClientState *nc)
- {
- IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
- return !!s->regs[ENET_RDAR];
- }
- static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
- size_t len)
- {
- IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
- IMXFECBufDesc bd;
- uint32_t flags = 0;
- uint32_t addr;
- uint32_t crc;
- uint32_t buf_addr;
- uint8_t *crc_ptr;
- unsigned int buf_len;
- size_t size = len;
- trace_imx_fec_receive(size);
- if (!s->regs[ENET_RDAR]) {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
- TYPE_IMX_FEC, __func__);
- return 0;
- }
- crc = cpu_to_be32(crc32(~0, buf, size));
- /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */
- size += 4;
- crc_ptr = (uint8_t *) &crc;
- /* Huge frames are truncated. */
- if (size > ENET_MAX_FRAME_SIZE) {
- size = ENET_MAX_FRAME_SIZE;
- flags |= ENET_BD_TR | ENET_BD_LG;
- }
- /* Frames larger than the user limit just set error flags. */
- if (size > (s->regs[ENET_RCR] >> 16)) {
- flags |= ENET_BD_LG;
- }
- addr = s->rx_descriptor;
- while (size > 0) {
- imx_fec_read_bd(&bd, addr);
- if ((bd.flags & ENET_BD_E) == 0) {
- /* No descriptors available. Bail out. */
- /*
- * FIXME: This is wrong. We should probably either
- * save the remainder for when more RX buffers are
- * available, or flag an error.
- */
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
- TYPE_IMX_FEC, __func__);
- break;
- }
- buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR];
- bd.length = buf_len;
- size -= buf_len;
- trace_imx_fec_receive_len(addr, bd.length);
- /* The last 4 bytes are the CRC. */
- if (size < 4) {
- buf_len += size - 4;
- }
- buf_addr = bd.data;
- dma_memory_write(&address_space_memory, buf_addr, buf, buf_len,
- MEMTXATTRS_UNSPECIFIED);
- buf += buf_len;
- if (size < 4) {
- dma_memory_write(&address_space_memory, buf_addr + buf_len,
- crc_ptr, 4 - size, MEMTXATTRS_UNSPECIFIED);
- crc_ptr += 4 - size;
- }
- bd.flags &= ~ENET_BD_E;
- if (size == 0) {
- /* Last buffer in frame. */
- bd.flags |= flags | ENET_BD_L;
- trace_imx_fec_receive_last(bd.flags);
- s->regs[ENET_EIR] |= ENET_INT_RXF;
- } else {
- s->regs[ENET_EIR] |= ENET_INT_RXB;
- }
- imx_fec_write_bd(&bd, addr);
- /* Advance to the next descriptor. */
- if ((bd.flags & ENET_BD_W) != 0) {
- addr = s->regs[ENET_RDSR];
- } else {
- addr += sizeof(bd);
- }
- }
- s->rx_descriptor = addr;
- imx_eth_enable_rx(s, false);
- imx_eth_update(s);
- return len;
- }
- static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf,
- size_t len)
- {
- IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
- IMXENETBufDesc bd;
- uint32_t flags = 0;
- uint32_t addr;
- uint32_t crc;
- uint32_t buf_addr;
- uint8_t *crc_ptr;
- unsigned int buf_len;
- size_t size = len;
- bool shift16 = s->regs[ENET_RACC] & ENET_RACC_SHIFT16;
- trace_imx_enet_receive(size);
- if (!s->regs[ENET_RDAR]) {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
- TYPE_IMX_FEC, __func__);
- return 0;
- }
- crc = cpu_to_be32(crc32(~0, buf, size));
- /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */
- size += 4;
- crc_ptr = (uint8_t *) &crc;
- if (shift16) {
- size += 2;
- }
- /* Huge frames are truncated. */
- if (size > s->regs[ENET_FTRL]) {
- size = s->regs[ENET_FTRL];
- flags |= ENET_BD_TR | ENET_BD_LG;
- }
- /* Frames larger than the user limit just set error flags. */
- if (size > (s->regs[ENET_RCR] >> 16)) {
- flags |= ENET_BD_LG;
- }
- addr = s->rx_descriptor;
- while (size > 0) {
- imx_enet_read_bd(&bd, addr);
- if ((bd.flags & ENET_BD_E) == 0) {
- /* No descriptors available. Bail out. */
- /*
- * FIXME: This is wrong. We should probably either
- * save the remainder for when more RX buffers are
- * available, or flag an error.
- */
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
- TYPE_IMX_FEC, __func__);
- break;
- }
- buf_len = MIN(size, s->regs[ENET_MRBR]);
- bd.length = buf_len;
- size -= buf_len;
- trace_imx_enet_receive_len(addr, bd.length);
- /* The last 4 bytes are the CRC. */
- if (size < 4) {
- buf_len += size - 4;
- }
- buf_addr = bd.data;
- if (shift16) {
- /*
- * If SHIFT16 bit of ENETx_RACC register is set we need to
- * align the payload to 4-byte boundary.
- */
- const uint8_t zeros[2] = { 0 };
- dma_memory_write(&address_space_memory, buf_addr, zeros,
- sizeof(zeros), MEMTXATTRS_UNSPECIFIED);
- buf_addr += sizeof(zeros);
- buf_len -= sizeof(zeros);
- /* We only do this once per Ethernet frame */
- shift16 = false;
- }
- dma_memory_write(&address_space_memory, buf_addr, buf, buf_len,
- MEMTXATTRS_UNSPECIFIED);
- buf += buf_len;
- if (size < 4) {
- dma_memory_write(&address_space_memory, buf_addr + buf_len,
- crc_ptr, 4 - size, MEMTXATTRS_UNSPECIFIED);
- crc_ptr += 4 - size;
- }
- bd.flags &= ~ENET_BD_E;
- if (size == 0) {
- /* Last buffer in frame. */
- bd.flags |= flags | ENET_BD_L;
- trace_imx_enet_receive_last(bd.flags);
- /* Indicate that we've updated the last buffer descriptor. */
- bd.last_buffer = ENET_BD_BDU;
- if (bd.option & ENET_BD_RX_INT) {
- s->regs[ENET_EIR] |= ENET_INT_RXF;
- }
- } else {
- if (bd.option & ENET_BD_RX_INT) {
- s->regs[ENET_EIR] |= ENET_INT_RXB;
- }
- }
- imx_enet_write_bd(&bd, addr);
- /* Advance to the next descriptor. */
- if ((bd.flags & ENET_BD_W) != 0) {
- addr = s->regs[ENET_RDSR];
- } else {
- addr += sizeof(bd);
- }
- }
- s->rx_descriptor = addr;
- imx_eth_enable_rx(s, false);
- imx_eth_update(s);
- return len;
- }
- static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf,
- size_t len)
- {
- IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
- if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
- return imx_enet_receive(nc, buf, len);
- } else {
- return imx_fec_receive(nc, buf, len);
- }
- }
- static const MemoryRegionOps imx_eth_ops = {
- .read = imx_eth_read,
- .write = imx_eth_write,
- .valid.min_access_size = 4,
- .valid.max_access_size = 4,
- .endianness = DEVICE_NATIVE_ENDIAN,
- };
- static void imx_eth_cleanup(NetClientState *nc)
- {
- IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
- s->nic = NULL;
- }
- static NetClientInfo imx_eth_net_info = {
- .type = NET_CLIENT_DRIVER_NIC,
- .size = sizeof(NICState),
- .can_receive = imx_eth_can_receive,
- .receive = imx_eth_receive,
- .cleanup = imx_eth_cleanup,
- .link_status_changed = imx_eth_set_link,
- };
- static void imx_eth_realize(DeviceState *dev, Error **errp)
- {
- IMXFECState *s = IMX_FEC(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s,
- TYPE_IMX_FEC, FSL_IMX25_FEC_SIZE);
- sysbus_init_mmio(sbd, &s->iomem);
- sysbus_init_irq(sbd, &s->irq[0]);
- sysbus_init_irq(sbd, &s->irq[1]);
- qemu_init_irq(&s->mii_irq, imx_phy_update_irq, s, 0);
- object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY);
- if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) {
- return;
- }
- qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq);
- qemu_macaddr_default_if_unset(&s->conf.macaddr);
- s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf,
- object_get_typename(OBJECT(dev)),
- dev->id, &dev->mem_reentrancy_guard, s);
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- }
- static const Property imx_eth_properties[] = {
- DEFINE_NIC_PROPERTIES(IMXFECState, conf),
- DEFINE_PROP_UINT32("tx-ring-num", IMXFECState, tx_ring_num, 1),
- DEFINE_PROP_UINT32("phy-num", IMXFECState, phy_num, 0),
- DEFINE_PROP_BOOL("phy-connected", IMXFECState, phy_connected, true),
- DEFINE_PROP_LINK("phy-consumer", IMXFECState, phy_consumer, TYPE_IMX_FEC,
- IMXFECState *),
- };
- static void imx_eth_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- dc->vmsd = &vmstate_imx_eth;
- device_class_set_legacy_reset(dc, imx_eth_reset);
- device_class_set_props(dc, imx_eth_properties);
- dc->realize = imx_eth_realize;
- dc->desc = "i.MX FEC/ENET Ethernet Controller";
- }
- static void imx_fec_init(Object *obj)
- {
- IMXFECState *s = IMX_FEC(obj);
- s->is_fec = true;
- }
- static void imx_enet_init(Object *obj)
- {
- IMXFECState *s = IMX_FEC(obj);
- s->is_fec = false;
- }
- static const TypeInfo imx_fec_info = {
- .name = TYPE_IMX_FEC,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(IMXFECState),
- .instance_init = imx_fec_init,
- .class_init = imx_eth_class_init,
- };
- static const TypeInfo imx_enet_info = {
- .name = TYPE_IMX_ENET,
- .parent = TYPE_IMX_FEC,
- .instance_init = imx_enet_init,
- };
- static void imx_eth_register_types(void)
- {
- type_register_static(&imx_fec_info);
- type_register_static(&imx_enet_info);
- }
- type_init(imx_eth_register_types)
|