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 <peter.mayd...@linaro.org> > Cc: Paul Brook <p...@codesourcery.com> > Cc: Edgar E. Iglesias <edgar.igles...@gmail.com> > Cc: Anthony Liguori <aligu...@us.ibm.com> > Signed-off-by: Grant Likely <grant.lik...@secretlab.ca> > --- > 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 >