From patchwork Wed Jan 23 16:15:26 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Grant Likely X-Patchwork-Id: 215080 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 17F1F2C0089 for ; Thu, 24 Jan 2013 09:49:14 +1100 (EST) Received: from localhost ([::1]:39462 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ty8hY-0004Lb-Lg for incoming@patchwork.ozlabs.org; Wed, 23 Jan 2013 17:21:48 -0500 Received: from eggs.gnu.org ([208.118.235.92]:50717) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ty8h4-0003zQ-MH for qemu-devel@nongnu.org; Wed, 23 Jan 2013 17:21:21 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Ty8gw-0007qc-JZ for qemu-devel@nongnu.org; Wed, 23 Jan 2013 17:21:18 -0500 Received: from mail-wi0-x22a.google.com ([2a00:1450:400c:c05::22a]:65405) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Ty8gw-0007qQ-5n for qemu-devel@nongnu.org; Wed, 23 Jan 2013 17:21:10 -0500 Received: by mail-wi0-f170.google.com with SMTP id hq7so4877952wib.3 for ; Wed, 23 Jan 2013 14:21:09 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:sender:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:mime-version:content-type :content-transfer-encoding:x-gm-message-state; bh=qf/U8c4bLGVy+fGZd2C7NGAw9+rotMQGx2EPOHgbw6c=; b=DKCYGLoG2dQ4WYjzpKy90eGSew+Xkq2xnfi7AfacN9ya/G679dqmMTgvyOuHNHnt/R KGguInGMejd9Hss37Lzw841PGjI1JjsZdyJEchTZKB//HgdZnxmIRaQVp3x13N9gPoDt 8fDYr7+n9Ta//VZYtBAf2ujwUcQV3U2QIphYENlNvrkUAjuDAhTe1QN6frJzfJusRcb2 WXaRWjFqtK4CxHY5bcKTNh/eHC0omuf52sVbbvTUSR9DXfEzsKjSicKoWbbphxXifB0g y6X64Z1aJRecJlkf50I8TqHdKnIUfXATI9IOAMR9xFRIv/+nseXU5mJMa/cWY3XdV44g PCKA== X-Received: by 10.194.177.199 with SMTP id cs7mr5285305wjc.41.1358979669489; Wed, 23 Jan 2013 14:21:09 -0800 (PST) Received: from localhost (host31-53-18-123.range31-53.btcentralplus.com. [31.53.18.123]) by mx.google.com with ESMTPS id s16sm33417518wii.0.2013.01.23.14.21.06 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 23 Jan 2013 14:21:08 -0800 (PST) Received: by localhost (Postfix, from userid 1000) id DEBEB3E0984; Wed, 23 Jan 2013 11:15:35 -0500 (EST) From: Grant Likely To: qemu-devel@nongnu.org Date: Wed, 23 Jan 2013 16:15:26 +0000 Message-Id: <1358957730-17897-3-git-send-email-grant.likely@secretlab.ca> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1358957730-17897-1-git-send-email-grant.likely@secretlab.ca> References: <1358957730-17897-1-git-send-email-grant.likely@secretlab.ca> MIME-Version: 1.0 X-Gm-Message-State: ALoCoQnoh/uip+GBTHpw9Bmbix2dFsW9+dcG5nSVTw7SqSswfeEhIxRgVqnhenL5yVSrUBoHvwbF X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c05::22a Cc: Peter Maydell , Anthony Liguori , Grant Likely , Paul Brook , "Edgar E. Iglesias" , =?UTF-8?q?Andreas=20F=C3=A4rber?= Subject: [Qemu-devel] [PATCH V2 2/6] hw/mdio: Generalize etraxfs MDIO bitbanging emulation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The etraxfs and Xilinx axienet Ethernet models implement quite a nice MDIO core that supports both bitbanging and direct register access. This change factors the common code out into a separate file. There are no functional changes here, just movement of code. The etraxfs and axienet are slightly different. The etraxfs version includes the bitbang state processing, but the axienet version has a minor enhancement for read/write of phy registers without using bitbang state variables. This patch generalizes the etraxfs version, with the axienet change backported in. Cc: Peter Maydell Cc: Paul Brook Cc: Edgar E. Iglesias Cc: Anthony Liguori Cc: Andreas Färber Signed-off-by: Grant Likely --- hw/Makefile.objs | 2 +- hw/cris/Makefile.objs | 1 + hw/etraxfs_eth.c | 278 +------------------------------------------------ hw/mdio.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++ hw/mdio.h | 72 +++++++++++++ hw/xilinx_axienet.c | 184 +------------------------------- 6 files changed, 344 insertions(+), 459 deletions(-) create mode 100644 hw/mdio.c create mode 100644 hw/mdio.h diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 74b07a7..af93dfc 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -74,7 +74,7 @@ common-obj-$(CONFIG_XILINX) += xilinx_timer.o common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o -common-obj-$(CONFIG_XILINX_AXI) += stream.o +common-obj-$(CONFIG_XILINX_AXI) += stream.o mdio.o # PKUnity SoC devices common-obj-$(CONFIG_PUV3) += puv3_intc.o diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs index aa9298a..b3da1e6 100644 --- a/hw/cris/Makefile.objs +++ b/hw/cris/Makefile.objs @@ -9,5 +9,6 @@ obj-y += etraxfs_pic.o obj-y += etraxfs_eth.o obj-y += etraxfs_timer.o obj-y += etraxfs_ser.o +obj-y += mdio.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index 0b474c0..f26e338 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -26,286 +26,10 @@ #include "sysbus.h" #include "net/net.h" #include "etraxfs.h" +#include "mdio.h" #define D(x) -/* Advertisement control register. */ -#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ -#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ -#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ - -/* - * The MDIO extensions in the TDK PHY model were reversed engineered from the - * linux driver (PHYID and Diagnostics reg). - * TODO: Add friendly names for the register nums. - */ -struct qemu_phy -{ - uint32_t regs[32]; - - int link; - - unsigned int (*read)(struct qemu_phy *phy, unsigned int req); - void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); -}; - -static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) -{ - int regnum; - unsigned r = 0; - - regnum = req & 0x1f; - - switch (regnum) { - case 1: - if (!phy->link) { - break; - } - /* MR1. */ - /* Speeds and modes. */ - r |= (1 << 13) | (1 << 14); - r |= (1 << 11) | (1 << 12); - r |= (1 << 5); /* Autoneg complete. */ - r |= (1 << 3); /* Autoneg able. */ - r |= (1 << 2); /* link. */ - break; - case 5: - /* Link partner ability. - We are kind; always agree with whatever best mode - the guest advertises. */ - r = 1 << 14; /* Success. */ - /* Copy advertised modes. */ - r |= phy->regs[4] & (15 << 5); - /* Autoneg support. */ - r |= 1; - break; - case 18: - { - /* Diagnostics reg. */ - int duplex = 0; - int speed_100 = 0; - - if (!phy->link) { - break; - } - - /* Are we advertising 100 half or 100 duplex ? */ - speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); - speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); - - /* Are we advertising 10 duplex or 100 duplex ? */ - duplex = !!(phy->regs[4] & ADVERTISE_100FULL); - duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); - r = (speed_100 << 10) | (duplex << 11); - } - break; - - default: - r = phy->regs[regnum]; - break; - } - D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); - return r; -} - -static void -tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) -{ - int regnum; - - regnum = req & 0x1f; - D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); - switch (regnum) { - default: - phy->regs[regnum] = data; - break; - } -} - -static void -tdk_init(struct qemu_phy *phy) -{ - phy->regs[0] = 0x3100; - /* PHY Id. */ - phy->regs[2] = 0x0300; - phy->regs[3] = 0xe400; - /* Autonegotiation advertisement reg. */ - phy->regs[4] = 0x01E1; - phy->link = 1; - - phy->read = tdk_read; - phy->write = tdk_write; -} - -struct qemu_mdio -{ - /* bus. */ - int mdc; - int mdio; - - /* decoder. */ - enum { - PREAMBLE, - SOF, - OPC, - ADDR, - REQ, - TURNAROUND, - DATA - } state; - unsigned int drive; - - unsigned int cnt; - unsigned int addr; - unsigned int opc; - unsigned int req; - unsigned int data; - - struct qemu_phy *devs[32]; -}; - -static void -mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = phy; -} - -#ifdef USE_THIS_DEAD_CODE -static void -mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = NULL; -} -#endif - -static void mdio_read_req(struct qemu_mdio *bus) -{ - struct qemu_phy *phy; - - phy = bus->devs[bus->addr]; - if (phy && phy->read) { - bus->data = phy->read(phy, bus->req); - } else { - bus->data = 0xffff; - } -} - -static void mdio_write_req(struct qemu_mdio *bus) -{ - struct qemu_phy *phy; - - phy = bus->devs[bus->addr]; - if (phy && phy->write) { - phy->write(phy, bus->req, bus->data); - } -} - -static void mdio_cycle(struct qemu_mdio *bus) -{ - bus->cnt++; - - D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", - bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); -#if 0 - if (bus->mdc) { - printf("%d", bus->mdio); - } -#endif - switch (bus->state) { - case PREAMBLE: - if (bus->mdc) { - if (bus->cnt >= (32 * 2) && !bus->mdio) { - bus->cnt = 0; - bus->state = SOF; - bus->data = 0; - } - } - break; - case SOF: - if (bus->mdc) { - if (bus->mdio != 1) { - printf("WARNING: no SOF\n"); - } - if (bus->cnt == 1*2) { - bus->cnt = 0; - bus->opc = 0; - bus->state = OPC; - } - } - break; - case OPC: - if (bus->mdc) { - bus->opc <<= 1; - bus->opc |= bus->mdio & 1; - if (bus->cnt == 2*2) { - bus->cnt = 0; - bus->addr = 0; - bus->state = ADDR; - } - } - break; - case ADDR: - if (bus->mdc) { - bus->addr <<= 1; - bus->addr |= bus->mdio & 1; - - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->req = 0; - bus->state = REQ; - } - } - break; - case REQ: - if (bus->mdc) { - bus->req <<= 1; - bus->req |= bus->mdio & 1; - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->state = TURNAROUND; - } - } - break; - case TURNAROUND: - if (bus->mdc && bus->cnt == 2*2) { - bus->mdio = 0; - bus->cnt = 0; - - if (bus->opc == 2) { - bus->drive = 1; - mdio_read_req(bus); - bus->mdio = bus->data & 1; - } - bus->state = DATA; - } - break; - case DATA: - if (!bus->mdc) { - if (bus->drive) { - bus->mdio = !!(bus->data & (1 << 15)); - bus->data <<= 1; - } - } else { - if (!bus->drive) { - bus->data <<= 1; - bus->data |= bus->mdio; - } - if (bus->cnt == 16 * 2) { - bus->cnt = 0; - bus->state = PREAMBLE; - if (!bus->drive) { - mdio_write_req(bus); - } - bus->drive = 0; - } - } - break; - default: - break; - } -} - /* ETRAX-FS Ethernet MAC block starts here. */ #define RW_MA0_LO 0x00 diff --git a/hw/mdio.c b/hw/mdio.c new file mode 100644 index 0000000..20d7603 --- /dev/null +++ b/hw/mdio.c @@ -0,0 +1,266 @@ +/* + * QEMU Bitbang Ethernet MDIO bus & PHY controllers. + * + * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "qemu-common.h" +#include "qemu/log.h" +#include "mdio.h" + +#define D(x) + +/* Advertisement control register. */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ + +/* + * The MDIO extensions in the TDK PHY model were reversed engineered from the + * linux driver (PHYID and Diagnostics reg). + * TODO: Add friendly names for the register nums. + */ +static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) +{ + int regnum; + unsigned r = 0; + + regnum = req & 0x1f; + + switch (regnum) { + case 1: + if (!phy->link) { + break; + } + /* MR1. */ + /* Speeds and modes. */ + r |= (1 << 13) | (1 << 14); + r |= (1 << 11) | (1 << 12); + r |= (1 << 5); /* Autoneg complete. */ + r |= (1 << 3); /* Autoneg able. */ + r |= (1 << 2); /* link. */ + r |= (1 << 1); /* link. */ + break; + case 5: + /* Link partner ability. + We are kind; always agree with whatever best mode + the guest advertises. */ + r = 1 << 14; /* Success. */ + /* Copy advertised modes. */ + r |= phy->regs[4] & (15 << 5); + /* Autoneg support. */ + r |= 1; + break; + case 17: + /* Marvel PHY on many xilinx boards. */ + r = 0x8000; /* 1000Mb */ + break; + case 18: + { + /* Diagnostics reg. */ + int duplex = 0; + int speed_100 = 0; + + if (!phy->link) { + break; + } + + /* Are we advertising 100 half or 100 duplex ? */ + speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); + speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); + + /* Are we advertising 10 duplex or 100 duplex ? */ + duplex = !!(phy->regs[4] & ADVERTISE_100FULL); + duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); + r = (speed_100 << 10) | (duplex << 11); + } + break; + + default: + r = phy->regs[regnum]; + break; + } + D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); + return r; +} + +static void tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) +{ + int regnum; + + regnum = req & 0x1f; + D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); + switch (regnum) { + default: + phy->regs[regnum] = data; + break; + } +} + +void tdk_init(struct qemu_phy *phy) +{ + phy->regs[0] = 0x3100; + /* PHY Id. */ + phy->regs[2] = 0x0300; + phy->regs[3] = 0xe400; + /* Autonegotiation advertisement reg. */ + phy->regs[4] = 0x01e1; + phy->link = 1; + + phy->read = tdk_read; + phy->write = tdk_write; +} + +void mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) +{ + bus->devs[addr & 0x1f] = phy; +} + +#ifdef USE_THIS_DEAD_CODE +void mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) +{ + bus->devs[addr & 0x1f] = NULL; +} +#endif + +uint16_t mdio_read_req(struct qemu_mdio *bus, uint8_t addr, uint8_t req) +{ + struct qemu_phy *phy; + + phy = bus->devs[bus->addr]; + if (phy && phy->read) { + return phy->read(phy, req); + } + return 0xffff; +} + +void mdio_write_req(struct qemu_mdio *bus, uint8_t addr, uint8_t req, + uint16_t data) +{ + struct qemu_phy *phy; + + phy = bus->devs[bus->addr]; + if (phy && phy->write) { + phy->write(phy, req, data); + } +} + +void mdio_cycle(struct qemu_mdio *bus) +{ + bus->cnt++; + + D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", + bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); + switch (bus->state) { + case PREAMBLE: + if (bus->mdc) { + if (bus->cnt >= (32 * 2) && !bus->mdio) { + bus->cnt = 0; + bus->state = SOF; + bus->data = 0; + } + } + break; + case SOF: + if (bus->mdc) { + if (bus->mdio != 1) { + printf("WARNING: no SOF\n"); + } + if (bus->cnt == 1*2) { + bus->cnt = 0; + bus->opc = 0; + bus->state = OPC; + } + } + break; + case OPC: + if (bus->mdc) { + bus->opc <<= 1; + bus->opc |= bus->mdio & 1; + if (bus->cnt == 2*2) { + bus->cnt = 0; + bus->addr = 0; + bus->state = ADDR; + } + } + break; + case ADDR: + if (bus->mdc) { + bus->addr <<= 1; + bus->addr |= bus->mdio & 1; + + if (bus->cnt == 5*2) { + bus->cnt = 0; + bus->req = 0; + bus->state = REQ; + } + } + break; + case REQ: + if (bus->mdc) { + bus->req <<= 1; + bus->req |= bus->mdio & 1; + if (bus->cnt == 5*2) { + bus->cnt = 0; + bus->state = TURNAROUND; + } + } + break; + case TURNAROUND: + if (bus->mdc && bus->cnt == 2*2) { + bus->mdio = 0; + bus->cnt = 0; + + if (bus->opc == 2) { + bus->drive = 1; + bus->data = mdio_read_req(bus, bus->addr, bus->req); + bus->mdio = bus->data & 1; + } + bus->state = DATA; + } + break; + case DATA: + if (!bus->mdc) { + if (bus->drive) { + bus->mdio = !!(bus->data & (1 << 15)); + bus->data <<= 1; + } + } else { + if (!bus->drive) { + bus->data <<= 1; + bus->data |= bus->mdio; + } + if (bus->cnt == 16 * 2) { + bus->cnt = 0; + bus->state = PREAMBLE; + if (!bus->drive) { + mdio_write_req(bus, bus->addr, bus->req, bus->data); + } + bus->drive = 0; + } + } + break; + default: + break; + } +} diff --git a/hw/mdio.h b/hw/mdio.h new file mode 100644 index 0000000..c0c2533 --- /dev/null +++ b/hw/mdio.h @@ -0,0 +1,72 @@ +#ifndef BITBANG_MDIO_H +#define BITBANG_MDIO_H + +/* + * QEMU Bitbang Ethernet MDIO bus & PHY controllers. + * + * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +struct qemu_phy { + uint32_t regs[32]; + + int link; + + unsigned int (*read)(struct qemu_phy *phy, unsigned int req); + void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); +}; + +struct qemu_mdio { + /* bus. */ + int mdc; + int mdio; + + /* decoder. */ + enum { + PREAMBLE, + SOF, + OPC, + ADDR, + REQ, + TURNAROUND, + DATA + } state; + unsigned int drive; + + unsigned int cnt; + unsigned int addr; + unsigned int opc; + unsigned int req; + unsigned int data; + + struct qemu_phy *devs[32]; +}; + +void tdk_init(struct qemu_phy *phy); +void mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, + unsigned int addr); +uint16_t mdio_read_req(struct qemu_mdio *bus, uint8_t addr, uint8_t req); +void mdio_write_req(struct qemu_mdio *bus, uint8_t addr, uint8_t req, uint16_t data); +void mdio_cycle(struct qemu_mdio *bus); + +#endif diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index 51c2896..ff09510 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -24,190 +24,12 @@ #include "sysbus.h" #include "qemu/log.h" +#include "mdio.h" #include "net/net.h" #include "net/checksum.h" #include "stream.h" -#define DPHY(x) - -/* Advertisement control register. */ -#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ -#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ -#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ - -struct PHY { - uint32_t regs[32]; - - int link; - - unsigned int (*read)(struct PHY *phy, unsigned int req); - void (*write)(struct PHY *phy, unsigned int req, - unsigned int data); -}; - -static unsigned int tdk_read(struct PHY *phy, unsigned int req) -{ - int regnum; - unsigned r = 0; - - regnum = req & 0x1f; - - switch (regnum) { - case 1: - if (!phy->link) { - break; - } - /* MR1. */ - /* Speeds and modes. */ - r |= (1 << 13) | (1 << 14); - r |= (1 << 11) | (1 << 12); - r |= (1 << 5); /* Autoneg complete. */ - r |= (1 << 3); /* Autoneg able. */ - r |= (1 << 2); /* link. */ - r |= (1 << 1); /* link. */ - break; - case 5: - /* Link partner ability. - We are kind; always agree with whatever best mode - the guest advertises. */ - r = 1 << 14; /* Success. */ - /* Copy advertised modes. */ - r |= phy->regs[4] & (15 << 5); - /* Autoneg support. */ - r |= 1; - break; - case 17: - /* Marvel PHY on many xilinx boards. */ - r = 0x8000; /* 1000Mb */ - break; - case 18: - { - /* Diagnostics reg. */ - int duplex = 0; - int speed_100 = 0; - - if (!phy->link) { - break; - } - - /* Are we advertising 100 half or 100 duplex ? */ - speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); - speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); - - /* Are we advertising 10 duplex or 100 duplex ? */ - duplex = !!(phy->regs[4] & ADVERTISE_100FULL); - duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); - r = (speed_100 << 10) | (duplex << 11); - } - break; - - default: - r = phy->regs[regnum]; - break; - } - DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum)); - return r; -} - -static void -tdk_write(struct PHY *phy, unsigned int req, unsigned int data) -{ - int regnum; - - regnum = req & 0x1f; - DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data)); - switch (regnum) { - default: - phy->regs[regnum] = data; - break; - } -} - -static void -tdk_init(struct PHY *phy) -{ - phy->regs[0] = 0x3100; - /* PHY Id. */ - phy->regs[2] = 0x0300; - phy->regs[3] = 0xe400; - /* Autonegotiation advertisement reg. */ - phy->regs[4] = 0x01E1; - phy->link = 1; - - phy->read = tdk_read; - phy->write = tdk_write; -} - -struct MDIOBus { - /* bus. */ - int mdc; - int mdio; - - /* decoder. */ - enum { - PREAMBLE, - SOF, - OPC, - ADDR, - REQ, - TURNAROUND, - DATA - } state; - unsigned int drive; - - unsigned int cnt; - unsigned int addr; - unsigned int opc; - unsigned int req; - unsigned int data; - - struct PHY *devs[32]; -}; - -static void -mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = phy; -} - -#ifdef USE_THIS_DEAD_CODE -static void -mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = NULL; -} -#endif - -static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr, - unsigned int reg) -{ - struct PHY *phy; - uint16_t data; - - phy = bus->devs[addr]; - if (phy && phy->read) { - data = phy->read(phy, reg); - } else { - data = 0xffff; - } - DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data)); - return data; -} - -static void mdio_write_req(struct MDIOBus *bus, unsigned int addr, - unsigned int reg, uint16_t data) -{ - struct PHY *phy; - - DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data)); - phy = bus->devs[addr]; - if (phy && phy->write) { - phy->write(phy, reg, data); - } -} - #define DENET(x) #define R_RAF (0x000 / 4) @@ -299,8 +121,8 @@ enum { /* Indirect registers. */ struct TEMAC { - struct MDIOBus mdio_bus; - struct PHY phy; + struct qemu_mdio mdio_bus; + struct qemu_phy phy; void *parent; };