[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 1/3] net/bitbang_mdio: Generalize etraxfs MDIO b
From: |
Edgar E. Iglesias |
Subject: |
Re: [Qemu-devel] [PATCH 1/3] net/bitbang_mdio: Generalize etraxfs MDIO bitbanging emulation |
Date: |
Mon, 21 Jan 2013 13:51:02 +0100 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
On Sat, Jan 19, 2013 at 06:28:10PM -0400, Grant Likely wrote:
> The etraxfs Ethernet model implements quite a nice bitbanging core. The
> change splits it out into a separate .c file. There are no functional
> changes here, just movement of code and reformatting to match qemu
> coding standards.
Hi Grant!
It makes sense to move it out.
A few comments:
* Maybe we could separate the phy models from the bus model/infrastructure.
I.e, the qemu_mdio parts into one file and the qemu_phy into another one.
That way we can add other phy models and keep things separated.
* Not sure bitbang_ makes sense in the naming, the abstraction supports
both the bit twiddeling model with MDIO cycles but also a higher level
transactional version (mdio_write_req etc) that bypass the cycle accuracy.
The latter is useful for modeling NICs that have MDIO support in hw, e.g
like the hw/xilinx_axienet.c while beeing able to reuse the PHY models.
I realize I should have done this split when doing the axienet model, sorry..
Best regards,
Edgar
>
> Cc: Peter Maydell <address@hidden>
> Cc: Paul Brook <address@hidden>
> Cc: Edgar E. Iglesias <address@hidden>
> Cc: Anthony Liguori <address@hidden>
> Signed-off-by: Grant Likely <address@hidden>
> ---
> hw/bitbang_mdio.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++
> hw/bitbang_mdio.h | 72 +++++++++++++
> hw/cris/Makefile.objs | 1 +
> hw/etraxfs_eth.c | 273
> +------------------------------------------------
> 4 files changed, 337 insertions(+), 272 deletions(-)
> create mode 100644 hw/bitbang_mdio.c
> create mode 100644 hw/bitbang_mdio.h
>
> diff --git a/hw/bitbang_mdio.c b/hw/bitbang_mdio.c
> new file mode 100644
> index 0000000..f0ee6af
> --- /dev/null
> +++ b/hw/bitbang_mdio.c
> @@ -0,0 +1,263 @@
> +/*
> + * 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 <stdio.h>
> +#include "qemu-common.h"
> +#include "qemu/log.h"
> +#include "bitbang_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. */
> + 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;
> + }
> +}
> +
> +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
> +
> +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;
> + }
> + D(qemu_log("%s addr=%d reg=%d data=%x\n", __func__,
> + bus->addr, bus->req, bus->data));
> +}
> +
> +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);
> + }
> +}
> +
> +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;
> + 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;
> + }
> +}
> diff --git a/hw/bitbang_mdio.h b/hw/bitbang_mdio.h
> new file mode 100644
> index 0000000..3484aba
> --- /dev/null
> +++ b/hw/bitbang_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 <stdint.h>
> +
> +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);
> +void mdio_read_req(struct qemu_mdio *bus);
> +void mdio_write_req(struct qemu_mdio *bus);
> +void mdio_cycle(struct qemu_mdio *bus);
> +
> +#endif
> diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs
> index aa9298a..86759a3 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 += bitbang_mdio.o
>
> obj-y := $(addprefix ../,$(obj-y))
> diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
> index ec23fa6..67b4797 100644
> --- a/hw/etraxfs_eth.c
> +++ b/hw/etraxfs_eth.c
> @@ -26,281 +26,10 @@
> #include "sysbus.h"
> #include "net/net.h"
> #include "etraxfs.h"
> +#include "bitbang_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
> --
> 1.7.10.4
>