Commit a3ea5df58845aac6e49ee36d4bef654e67b78c8a

Authored by edgar_igl
1 parent 1ba13a5d

Add limited support for the etrax ethernet controller.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4429 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 453 additions and 0 deletions
hw/etraxfs_eth.c 0 → 100644
  1 +/*
  2 + * QEMU ETRAX Ethernet Controller.
  3 + *
  4 + * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include <stdio.h>
  26 +#include "hw.h"
  27 +#include "net.h"
  28 +
  29 +#include "etraxfs_dma.h"
  30 +
  31 +#define D(x)
  32 +
  33 +#define R_STAT 0x2c
  34 +#define RW_MGM_CTRL 0x28
  35 +#define FS_ETH_MAX_REGS 0x5c
  36 +
  37 +
  38 +
  39 +struct qemu_phy
  40 +{
  41 + uint32_t regs[32];
  42 +
  43 + unsigned int (*read)(struct qemu_phy *phy, unsigned int req);
  44 + void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data);
  45 +};
  46 +
  47 +static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
  48 +{
  49 + int regnum;
  50 + unsigned r = 0;
  51 +
  52 + regnum = req & 0x1f;
  53 +
  54 + switch (regnum) {
  55 + case 1:
  56 + /* MR1. */
  57 + /* Speeds and modes. */
  58 + r |= (1 << 13) | (1 << 14);
  59 + r |= (1 << 11) | (1 << 12);
  60 + r |= (1 << 5); /* Autoneg complete. */
  61 + r |= (1 << 3); /* Autoneg able. */
  62 + r |= (1 << 2); /* Link. */
  63 + break;
  64 + default:
  65 + r = phy->regs[regnum];
  66 + break;
  67 + }
  68 + D(printf("%s %x = reg[%d]\n", __func__, r, regnum));
  69 + return r;
  70 +}
  71 +
  72 +static void
  73 +tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data)
  74 +{
  75 + int regnum;
  76 +
  77 + regnum = req & 0x1f;
  78 + D(printf("%s reg[%d] = %x\n", __func__, regnum, data));
  79 + switch (regnum) {
  80 + default:
  81 + phy->regs[regnum] = data;
  82 + break;
  83 + }
  84 +}
  85 +
  86 +static void
  87 +tdk_init(struct qemu_phy *phy)
  88 +{
  89 + phy->read = tdk_read;
  90 + phy->write = tdk_write;
  91 +}
  92 +
  93 +struct qemu_mdio
  94 +{
  95 + /* bus. */
  96 + int mdc;
  97 + int mdio;
  98 +
  99 + /* decoder. */
  100 + enum {
  101 + PREAMBLE,
  102 + SOF,
  103 + OPC,
  104 + ADDR,
  105 + REQ,
  106 + TURNAROUND,
  107 + DATA
  108 + } state;
  109 + unsigned int drive;
  110 +
  111 + unsigned int cnt;
  112 + unsigned int addr;
  113 + unsigned int opc;
  114 + unsigned int req;
  115 + unsigned int data;
  116 +
  117 + struct qemu_phy *devs[32];
  118 +};
  119 +
  120 +static void
  121 +mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
  122 +{
  123 + bus->devs[addr & 0x1f] = phy;
  124 +}
  125 +
  126 +static void
  127 +mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
  128 +{
  129 + bus->devs[addr & 0x1f] = NULL;
  130 +}
  131 +
  132 +static void mdio_read_req(struct qemu_mdio *bus)
  133 +{
  134 + struct qemu_phy *phy;
  135 +
  136 + phy = bus->devs[bus->addr];
  137 + if (phy && phy->read)
  138 + bus->data = phy->read(phy, bus->req);
  139 + else
  140 + bus->data = 0xffff;
  141 +}
  142 +
  143 +static void mdio_write_req(struct qemu_mdio *bus)
  144 +{
  145 + struct qemu_phy *phy;
  146 +
  147 + phy = bus->devs[bus->addr];
  148 + if (phy && phy->write)
  149 + phy->write(phy, bus->req, bus->data);
  150 +}
  151 +
  152 +static void mdio_cycle(struct qemu_mdio *bus)
  153 +{
  154 + bus->cnt++;
  155 +
  156 + D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n",
  157 + bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive));
  158 +#if 0
  159 + if (bus->mdc)
  160 + printf("%d", bus->mdio);
  161 +#endif
  162 + switch (bus->state)
  163 + {
  164 + case PREAMBLE:
  165 + if (bus->mdc) {
  166 + if (bus->cnt >= (32 * 2) && !bus->mdio) {
  167 + bus->cnt = 0;
  168 + bus->state = SOF;
  169 + bus->data = 0;
  170 + }
  171 + }
  172 + break;
  173 + case SOF:
  174 + if (bus->mdc) {
  175 + if (bus->mdio != 1)
  176 + printf("WARNING: no SOF\n");
  177 + if (bus->cnt == 1*2) {
  178 + bus->cnt = 0;
  179 + bus->opc = 0;
  180 + bus->state = OPC;
  181 + }
  182 + }
  183 + break;
  184 + case OPC:
  185 + if (bus->mdc) {
  186 + bus->opc <<= 1;
  187 + bus->opc |= bus->mdio & 1;
  188 + if (bus->cnt == 2*2) {
  189 + bus->cnt = 0;
  190 + bus->addr = 0;
  191 + bus->state = ADDR;
  192 + }
  193 + }
  194 + break;
  195 + case ADDR:
  196 + if (bus->mdc) {
  197 + bus->addr <<= 1;
  198 + bus->addr |= bus->mdio & 1;
  199 +
  200 + if (bus->cnt == 5*2) {
  201 + bus->cnt = 0;
  202 + bus->req = 0;
  203 + bus->state = REQ;
  204 + }
  205 + }
  206 + break;
  207 + case REQ:
  208 + if (bus->mdc) {
  209 + bus->req <<= 1;
  210 + bus->req |= bus->mdio & 1;
  211 + if (bus->cnt == 5*2) {
  212 + bus->cnt = 0;
  213 + bus->state = TURNAROUND;
  214 + }
  215 + }
  216 + break;
  217 + case TURNAROUND:
  218 + if (bus->mdc && bus->cnt == 2*2) {
  219 + bus->mdio = 0;
  220 + bus->cnt = 0;
  221 +
  222 + if (bus->opc == 2) {
  223 + bus->drive = 1;
  224 + mdio_read_req(bus);
  225 + bus->mdio = bus->data & 1;
  226 + }
  227 + bus->state = DATA;
  228 + }
  229 + break;
  230 + case DATA:
  231 + if (!bus->mdc) {
  232 + if (bus->drive) {
  233 + bus->mdio = bus->data & 1;
  234 + bus->data >>= 1;
  235 + }
  236 + } else {
  237 + if (!bus->drive) {
  238 + bus->data <<= 1;
  239 + bus->data |= bus->mdio;
  240 + }
  241 + if (bus->cnt == 16 * 2) {
  242 + bus->cnt = 0;
  243 + bus->state = PREAMBLE;
  244 + mdio_write_req(bus);
  245 + }
  246 + }
  247 + break;
  248 + default:
  249 + break;
  250 + }
  251 +}
  252 +
  253 +
  254 +struct fs_eth
  255 +{
  256 + CPUState *env;
  257 + qemu_irq *irq;
  258 + target_phys_addr_t base;
  259 + VLANClientState *vc;
  260 + uint8_t macaddr[6];
  261 + int ethregs;
  262 +
  263 + uint32_t regs[FS_ETH_MAX_REGS];
  264 +
  265 + unsigned char rx_fifo[1536];
  266 + int rx_fifo_len;
  267 + int rx_fifo_pos;
  268 +
  269 + struct etraxfs_dma_client *dma_out;
  270 + struct etraxfs_dma_client *dma_in;
  271 +
  272 + /* MDIO bus. */
  273 + struct qemu_mdio mdio_bus;
  274 + /* PHY. */
  275 + struct qemu_phy phy;
  276 +};
  277 +
  278 +static uint32_t eth_rinvalid (void *opaque, target_phys_addr_t addr)
  279 +{
  280 + struct fs_eth *eth = opaque;
  281 + CPUState *env = eth->env;
  282 + cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n",
  283 + addr, env->pc);
  284 + return 0;
  285 +}
  286 +
  287 +static uint32_t eth_readl (void *opaque, target_phys_addr_t addr)
  288 +{
  289 + struct fs_eth *eth = opaque;
  290 + D(CPUState *env = eth->env);
  291 + uint32_t r = 0;
  292 +
  293 + /* Make addr relative to this instances base. */
  294 + addr -= eth->base;
  295 + switch (addr) {
  296 + case R_STAT:
  297 + /* Attach an MDIO/PHY abstraction. */
  298 + r = eth->mdio_bus.mdio & 1;
  299 + break;
  300 + default:
  301 + r = eth->regs[addr];
  302 + D(printf ("%s %x p=%x\n", __func__, addr, env->pc));
  303 + break;
  304 + }
  305 + return r;
  306 +}
  307 +
  308 +static void
  309 +eth_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
  310 +{
  311 + struct fs_eth *eth = opaque;
  312 + CPUState *env = eth->env;
  313 + cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n",
  314 + addr, env->pc);
  315 +}
  316 +
  317 +static void
  318 +eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
  319 +{
  320 + struct fs_eth *eth = opaque;
  321 + CPUState *env = eth->env;
  322 +
  323 + /* Make addr relative to this instances base. */
  324 + addr -= eth->base;
  325 + switch (addr)
  326 + {
  327 + case RW_MGM_CTRL:
  328 + /* Attach an MDIO/PHY abstraction. */
  329 + if (value & 2)
  330 + eth->mdio_bus.mdio = value & 1;
  331 + if (eth->mdio_bus.mdc != (value & 4))
  332 + mdio_cycle(&eth->mdio_bus);
  333 + eth->mdio_bus.mdc = !!(value & 4);
  334 + break;
  335 +
  336 + default:
  337 + printf ("%s %x %x pc=%x\n",
  338 + __func__, addr, value, env->pc);
  339 + break;
  340 + }
  341 +}
  342 +
  343 +static int eth_can_receive(void *opaque)
  344 +{
  345 + struct fs_eth *eth = opaque;
  346 + int r;
  347 +
  348 + r = eth->rx_fifo_len == 0;
  349 + if (!r) {
  350 + /* TODO: signal fifo overrun. */
  351 + printf("PACKET LOSS!\n");
  352 + }
  353 + return r;
  354 +}
  355 +
  356 +static void eth_receive(void *opaque, const uint8_t *buf, int size)
  357 +{
  358 + struct fs_eth *eth = opaque;
  359 + if (size > sizeof(eth->rx_fifo)) {
  360 + /* TODO: signal error. */
  361 + } else {
  362 + memcpy(eth->rx_fifo, buf, size);
  363 + /* +4, HW passes the CRC to sw. */
  364 + eth->rx_fifo_len = size + 4;
  365 + eth->rx_fifo_pos = 0;
  366 + }
  367 +}
  368 +
  369 +static void eth_rx_pull(void *opaque)
  370 +{
  371 + struct fs_eth *eth = opaque;
  372 + int len;
  373 + if (eth->rx_fifo_len) {
  374 + D(printf("%s %d\n", __func__, eth->rx_fifo_len));
  375 +#if 0
  376 + {
  377 + int i;
  378 + for (i = 0; i < 32; i++)
  379 + printf("%2.2x", eth->rx_fifo[i]);
  380 + printf("\n");
  381 + }
  382 +#endif
  383 + len = etraxfs_dmac_input(eth->dma_in,
  384 + eth->rx_fifo + eth->rx_fifo_pos,
  385 + eth->rx_fifo_len, 1);
  386 + eth->rx_fifo_len -= len;
  387 + eth->rx_fifo_pos += len;
  388 + }
  389 +}
  390 +
  391 +static int eth_tx_push(void *opaque, unsigned char *buf, int len)
  392 +{
  393 + struct fs_eth *eth = opaque;
  394 +
  395 + D(printf("%s buf=%p len=%d\n", __func__, buf, len));
  396 + qemu_send_packet(eth->vc, buf, len);
  397 + return len;
  398 +}
  399 +
  400 +static CPUReadMemoryFunc *eth_read[] = {
  401 + &eth_rinvalid,
  402 + &eth_rinvalid,
  403 + &eth_readl,
  404 +};
  405 +
  406 +static CPUWriteMemoryFunc *eth_write[] = {
  407 + &eth_winvalid,
  408 + &eth_winvalid,
  409 + &eth_writel,
  410 +};
  411 +
  412 +void *etraxfs_eth_init(NICInfo *nd, CPUState *env,
  413 + qemu_irq *irq, target_phys_addr_t base)
  414 +{
  415 + struct etraxfs_dma_client *dma = NULL;
  416 + struct fs_eth *eth = NULL;
  417 +
  418 + dma = qemu_mallocz(sizeof *dma * 2);
  419 + if (!dma)
  420 + return NULL;
  421 +
  422 + eth = qemu_mallocz(sizeof *eth);
  423 + if (!eth)
  424 + goto err;
  425 +
  426 + dma[0].client.push = eth_tx_push;
  427 + dma[0].client.opaque = eth;
  428 + dma[1].client.opaque = eth;
  429 + dma[1].client.pull = eth_rx_pull;
  430 +
  431 + eth->env = env;
  432 + eth->base = base;
  433 + eth->irq = irq;
  434 + eth->dma_out = dma;
  435 + eth->dma_in = dma + 1;
  436 + memcpy(eth->macaddr, nd->macaddr, 6);
  437 +
  438 + /* Connect the phy. */
  439 + tdk_init(&eth->phy);
  440 + mdio_attach(&eth->mdio_bus, &eth->phy, 0x1);
  441 +
  442 + eth->ethregs = cpu_register_io_memory(0, eth_read, eth_write, eth);
  443 + cpu_register_physical_memory (base, 0x5c, eth->ethregs);
  444 +
  445 + eth->vc = qemu_new_vlan_client(nd->vlan,
  446 + eth_receive, eth_can_receive, eth);
  447 +
  448 + return dma;
  449 + err:
  450 + qemu_free(eth);
  451 + qemu_free(dma);
  452 + return NULL;
  453 +}
... ...