12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376 |
- /*
- * 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 "sysemu/dma.h"
- #include "qemu/log.h"
- #include "qemu/module.h"
- #include "net/checksum.h"
- #include "net/eth.h"
- #include "trace.h"
- /* For crc32 */
- #include <zlib.h>
- #define IMX_MAX_DESC 1024
- static const char *imx_default_reg_name(IMXFECState *s, uint32_t index)
- {
- static char tmp[20];
- sprintf(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 = (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 = 2,
- .minimum_version_id = 2,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX),
- VMSTATE_UINT32(rx_descriptor, IMXFECState),
- VMSTATE_UINT32(tx_descriptor[0], IMXFECState),
- VMSTATE_UINT32(phy_status, IMXFECState),
- VMSTATE_UINT32(phy_control, IMXFECState),
- VMSTATE_UINT32(phy_advertise, IMXFECState),
- VMSTATE_UINT32(phy_int, IMXFECState),
- VMSTATE_UINT32(phy_int_mask, IMXFECState),
- VMSTATE_END_OF_LIST()
- },
- .subsections = (const VMStateDescription * []) {
- &vmstate_imx_eth_txdescs,
- NULL
- },
- };
- #define PHY_INT_ENERGYON (1 << 7)
- #define PHY_INT_AUTONEG_COMPLETE (1 << 6)
- #define PHY_INT_FAULT (1 << 5)
- #define PHY_INT_DOWN (1 << 4)
- #define PHY_INT_AUTONEG_LP (1 << 3)
- #define PHY_INT_PARFAULT (1 << 2)
- #define PHY_INT_AUTONEG_PAGE (1 << 1)
- 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(IMXFECState *s)
- {
- imx_eth_update(s);
- }
- static void imx_phy_update_link(IMXFECState *s)
- {
- /* Autonegotiation status mirrors link status. */
- if (qemu_get_queue(s->nic)->link_down) {
- trace_imx_phy_update_link("down");
- s->phy_status &= ~0x0024;
- s->phy_int |= PHY_INT_DOWN;
- } else {
- trace_imx_phy_update_link("up");
- s->phy_status |= 0x0024;
- s->phy_int |= PHY_INT_ENERGYON;
- s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
- }
- imx_phy_update_irq(s);
- }
- static void imx_eth_set_link(NetClientState *nc)
- {
- imx_phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc)));
- }
- static void imx_phy_reset(IMXFECState *s)
- {
- trace_imx_phy_reset();
- s->phy_status = 0x7809;
- s->phy_control = 0x3000;
- s->phy_advertise = 0x01e1;
- s->phy_int_mask = 0;
- s->phy_int = 0;
- imx_phy_update_link(s);
- }
- static uint32_t imx_phy_read(IMXFECState *s, int reg)
- {
- uint32_t val;
- uint32_t phy = reg / 32;
- if (phy != s->phy_num) {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad phy num %u\n",
- TYPE_IMX_FEC, __func__, phy);
- return 0;
- }
- reg %= 32;
- switch (reg) {
- case 0: /* Basic Control */
- val = s->phy_control;
- break;
- case 1: /* Basic Status */
- val = s->phy_status;
- break;
- case 2: /* ID1 */
- val = 0x0007;
- break;
- case 3: /* ID2 */
- val = 0xc0d1;
- break;
- case 4: /* Auto-neg advertisement */
- val = s->phy_advertise;
- break;
- case 5: /* Auto-neg Link Partner Ability */
- val = 0x0f71;
- break;
- case 6: /* Auto-neg Expansion */
- val = 1;
- break;
- case 29: /* Interrupt source. */
- val = s->phy_int;
- s->phy_int = 0;
- imx_phy_update_irq(s);
- break;
- case 30: /* Interrupt mask */
- val = s->phy_int_mask;
- break;
- case 17:
- case 18:
- case 27:
- case 31:
- qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n",
- TYPE_IMX_FEC, __func__, reg);
- val = 0;
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n",
- TYPE_IMX_FEC, __func__, reg);
- val = 0;
- break;
- }
- trace_imx_phy_read(val, phy, reg);
- return val;
- }
- static void imx_phy_write(IMXFECState *s, int reg, uint32_t val)
- {
- uint32_t phy = reg / 32;
- if (phy != s->phy_num) {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad phy num %u\n",
- TYPE_IMX_FEC, __func__, phy);
- return;
- }
- reg %= 32;
- trace_imx_phy_write(val, phy, reg);
- switch (reg) {
- case 0: /* Basic Control */
- if (val & 0x8000) {
- imx_phy_reset(s);
- } else {
- s->phy_control = val & 0x7980;
- /* Complete autonegotiation immediately. */
- if (val & 0x1000) {
- s->phy_status |= 0x0020;
- }
- }
- break;
- case 4: /* Auto-neg advertisement */
- s->phy_advertise = (val & 0x2d7f) | 0x80;
- break;
- case 30: /* Interrupt mask */
- s->phy_int_mask = val & 0xff;
- imx_phy_update_irq(s);
- break;
- case 17:
- case 18:
- case 27:
- case 31:
- qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n",
- TYPE_IMX_FEC, __func__, reg);
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n",
- TYPE_IMX_FEC, __func__, reg);
- break;
- }
- }
- static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr)
- {
- dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
- 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));
- }
- static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr)
- {
- dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
- 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));
- }
- 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
- * interrrupts 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);
- 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);
- ptr += len;
- frame_size += len;
- if (bd.flags & ENET_BD_L) {
- if (bd.option & ENET_BD_PINS) {
- struct ip_header *ip_hd = PKT_GET_IP_HDR(s->frame);
- if (IP_HEADER_VERSION(ip_hd) == 4) {
- net_checksum_calculate(s->frame, frame_size);
- }
- }
- if (bd.option & ENET_BD_IINS) {
- struct ip_header *ip_hd = PKT_GET_IP_HDR(s->frame);
- /* We compute checksum only for IPv4 frames */
- if (IP_HEADER_VERSION(ip_hd) == 4) {
- uint16_t csum;
- ip_hd->ip_sum = 0;
- csum = net_raw_checksum((uint8_t *)ip_hd, sizeof(*ip_hd));
- ip_hd->ip_sum = cpu_to_be16(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));
- /* We also reset the PHY */
- imx_phy_reset(s);
- }
- 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;
- }
- /* 4 bytes for the CRC. */
- size += 4;
- crc = cpu_to_be32(crc32(~0, buf, size));
- 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);
- buf += buf_len;
- if (size < 4) {
- dma_memory_write(&address_space_memory, buf_addr + buf_len,
- crc_ptr, 4 - size);
- 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;
- }
- /* 4 bytes for the CRC. */
- size += 4;
- crc = cpu_to_be32(crc32(~0, buf, size));
- 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));
- 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);
- buf += buf_len;
- if (size < 4) {
- dma_memory_write(&address_space_memory, buf_addr + buf_len,
- crc_ptr, 4 - size);
- 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_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, s);
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- }
- static 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_END_OF_LIST(),
- };
- static void imx_eth_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- dc->vmsd = &vmstate_imx_eth;
- dc->reset = 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)
|