Commit f6953f1345daeea02986fcb78d19f4727bf9992c
1 parent
57e49b40
ETRAX: Add support for the ethernet receivers dest addr filters.
* Support the station address filters MA0 and MA1. * Model the group address bloom filter. * Indentation. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4487 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
162 additions
and
45 deletions
hw/etraxfs_eth.c
| @@ -53,13 +53,13 @@ static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) | @@ -53,13 +53,13 @@ static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) | ||
| 53 | 53 | ||
| 54 | switch (regnum) { | 54 | switch (regnum) { |
| 55 | case 1: | 55 | case 1: |
| 56 | - /* MR1. */ | 56 | + /* MR1. */ |
| 57 | /* Speeds and modes. */ | 57 | /* Speeds and modes. */ |
| 58 | r |= (1 << 13) | (1 << 14); | 58 | r |= (1 << 13) | (1 << 14); |
| 59 | r |= (1 << 11) | (1 << 12); | 59 | r |= (1 << 11) | (1 << 12); |
| 60 | r |= (1 << 5); /* Autoneg complete. */ | 60 | r |= (1 << 5); /* Autoneg complete. */ |
| 61 | - r |= (1 << 3); /* Autoneg able. */ | ||
| 62 | - r |= (1 << 2); /* Link. */ | 61 | + r |= (1 << 3); /* Autoneg able. */ |
| 62 | + r |= (1 << 2); /* Link. */ | ||
| 63 | break; | 63 | break; |
| 64 | case 5: | 64 | case 5: |
| 65 | /* Link partner ability. | 65 | /* Link partner ability. |
| @@ -123,7 +123,7 @@ tdk_init(struct qemu_phy *phy) | @@ -123,7 +123,7 @@ tdk_init(struct qemu_phy *phy) | ||
| 123 | 123 | ||
| 124 | struct qemu_mdio | 124 | struct qemu_mdio |
| 125 | { | 125 | { |
| 126 | - /* bus. */ | 126 | + /* bus. */ |
| 127 | int mdc; | 127 | int mdc; |
| 128 | int mdio; | 128 | int mdio; |
| 129 | 129 | ||
| @@ -285,19 +285,30 @@ static void mdio_cycle(struct qemu_mdio *bus) | @@ -285,19 +285,30 @@ static void mdio_cycle(struct qemu_mdio *bus) | ||
| 285 | 285 | ||
| 286 | /* ETRAX-FS Ethernet MAC block starts here. */ | 286 | /* ETRAX-FS Ethernet MAC block starts here. */ |
| 287 | 287 | ||
| 288 | -#define R_STAT 0x2c | ||
| 289 | -#define RW_MGM_CTRL 0x28 | ||
| 290 | -#define FS_ETH_MAX_REGS 0x5c | 288 | +#define RW_MA0_LO 0x00 |
| 289 | +#define RW_MA0_HI 0x04 | ||
| 290 | +#define RW_MA1_LO 0x08 | ||
| 291 | +#define RW_MA1_HI 0x0c | ||
| 292 | +#define RW_GA_LO 0x10 | ||
| 293 | +#define RW_GA_HI 0x14 | ||
| 294 | +#define RW_GEN_CTRL 0x18 | ||
| 295 | +#define RW_REC_CTRL 0x1c | ||
| 296 | +#define RW_TR_CTRL 0x20 | ||
| 297 | +#define RW_CLR_ERR 0x24 | ||
| 298 | +#define RW_MGM_CTRL 0x28 | ||
| 299 | +#define R_STAT 0x2c | ||
| 300 | +#define FS_ETH_MAX_REGS 0x5c | ||
| 291 | 301 | ||
| 292 | struct fs_eth | 302 | struct fs_eth |
| 293 | { | 303 | { |
| 294 | - CPUState *env; | 304 | + CPUState *env; |
| 295 | qemu_irq *irq; | 305 | qemu_irq *irq; |
| 296 | - target_phys_addr_t base; | 306 | + target_phys_addr_t base; |
| 297 | VLANClientState *vc; | 307 | VLANClientState *vc; |
| 298 | - uint8_t macaddr[6]; | ||
| 299 | int ethregs; | 308 | int ethregs; |
| 300 | 309 | ||
| 310 | + /* Two addrs in the filter. */ | ||
| 311 | + uint8_t macaddr[2][6]; | ||
| 301 | uint32_t regs[FS_ETH_MAX_REGS]; | 312 | uint32_t regs[FS_ETH_MAX_REGS]; |
| 302 | 313 | ||
| 303 | unsigned char rx_fifo[1536]; | 314 | unsigned char rx_fifo[1536]; |
| @@ -309,59 +320,100 @@ struct fs_eth | @@ -309,59 +320,100 @@ struct fs_eth | ||
| 309 | 320 | ||
| 310 | /* MDIO bus. */ | 321 | /* MDIO bus. */ |
| 311 | struct qemu_mdio mdio_bus; | 322 | struct qemu_mdio mdio_bus; |
| 312 | - /* PHY. */ | 323 | + /* PHY. */ |
| 313 | struct qemu_phy phy; | 324 | struct qemu_phy phy; |
| 314 | }; | 325 | }; |
| 315 | 326 | ||
| 316 | static uint32_t eth_rinvalid (void *opaque, target_phys_addr_t addr) | 327 | static uint32_t eth_rinvalid (void *opaque, target_phys_addr_t addr) |
| 317 | { | 328 | { |
| 318 | - struct fs_eth *eth = opaque; | ||
| 319 | - CPUState *env = eth->env; | ||
| 320 | - cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n", | ||
| 321 | - addr, env->pc); | ||
| 322 | - return 0; | 329 | + struct fs_eth *eth = opaque; |
| 330 | + CPUState *env = eth->env; | ||
| 331 | + cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n", | ||
| 332 | + addr, env->pc); | ||
| 333 | + return 0; | ||
| 323 | } | 334 | } |
| 324 | 335 | ||
| 325 | static uint32_t eth_readl (void *opaque, target_phys_addr_t addr) | 336 | static uint32_t eth_readl (void *opaque, target_phys_addr_t addr) |
| 326 | { | 337 | { |
| 327 | - struct fs_eth *eth = opaque; | ||
| 328 | - D(CPUState *env = eth->env); | ||
| 329 | - uint32_t r = 0; | 338 | + struct fs_eth *eth = opaque; |
| 339 | + D(CPUState *env = eth->env); | ||
| 340 | + uint32_t r = 0; | ||
| 330 | 341 | ||
| 331 | - /* Make addr relative to this instances base. */ | ||
| 332 | - addr -= eth->base; | ||
| 333 | - switch (addr) { | 342 | + /* Make addr relative to this instances base. */ |
| 343 | + addr -= eth->base; | ||
| 344 | + switch (addr) { | ||
| 334 | case R_STAT: | 345 | case R_STAT: |
| 335 | /* Attach an MDIO/PHY abstraction. */ | 346 | /* Attach an MDIO/PHY abstraction. */ |
| 336 | r = eth->mdio_bus.mdio & 1; | 347 | r = eth->mdio_bus.mdio & 1; |
| 337 | break; | 348 | break; |
| 338 | - default: | 349 | + default: |
| 339 | r = eth->regs[addr]; | 350 | r = eth->regs[addr]; |
| 340 | - D(printf ("%s %x p=%x\n", __func__, addr, env->pc)); | ||
| 341 | - break; | ||
| 342 | - } | ||
| 343 | - return r; | 351 | + D(printf ("%s %x p=%x\n", __func__, addr, env->pc)); |
| 352 | + break; | ||
| 353 | + } | ||
| 354 | + return r; | ||
| 344 | } | 355 | } |
| 345 | 356 | ||
| 346 | static void | 357 | static void |
| 347 | eth_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value) | 358 | eth_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value) |
| 348 | { | 359 | { |
| 349 | - struct fs_eth *eth = opaque; | ||
| 350 | - CPUState *env = eth->env; | ||
| 351 | - cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n", | ||
| 352 | - addr, env->pc); | 360 | + struct fs_eth *eth = opaque; |
| 361 | + CPUState *env = eth->env; | ||
| 362 | + cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n", | ||
| 363 | + addr, env->pc); | ||
| 364 | +} | ||
| 365 | + | ||
| 366 | +static void eth_update_ma(struct fs_eth *eth, int ma) | ||
| 367 | +{ | ||
| 368 | + int reg; | ||
| 369 | + int i = 0; | ||
| 370 | + | ||
| 371 | + ma &= 1; | ||
| 372 | + | ||
| 373 | + reg = RW_MA0_LO; | ||
| 374 | + if (ma) | ||
| 375 | + reg = RW_MA1_LO; | ||
| 376 | + | ||
| 377 | + eth->macaddr[ma][i++] = eth->regs[reg]; | ||
| 378 | + eth->macaddr[ma][i++] = eth->regs[reg] >> 8; | ||
| 379 | + eth->macaddr[ma][i++] = eth->regs[reg] >> 16; | ||
| 380 | + eth->macaddr[ma][i++] = eth->regs[reg] >> 24; | ||
| 381 | + eth->macaddr[ma][i++] = eth->regs[reg + 4]; | ||
| 382 | + eth->macaddr[ma][i++] = eth->regs[reg + 4] >> 8; | ||
| 383 | + | ||
| 384 | + D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, | ||
| 385 | + eth->macaddr[ma][0], eth->macaddr[ma][1], | ||
| 386 | + eth->macaddr[ma][2], eth->macaddr[ma][3], | ||
| 387 | + eth->macaddr[ma][4], eth->macaddr[ma][5])); | ||
| 353 | } | 388 | } |
| 354 | 389 | ||
| 355 | static void | 390 | static void |
| 356 | eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | 391 | eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
| 357 | { | 392 | { |
| 358 | - struct fs_eth *eth = opaque; | ||
| 359 | - CPUState *env = eth->env; | 393 | + struct fs_eth *eth = opaque; |
| 394 | + CPUState *env = eth->env; | ||
| 395 | + | ||
| 396 | + /* Make addr relative to this instances base. */ | ||
| 397 | + addr -= eth->base; | ||
| 398 | + switch (addr) | ||
| 399 | + { | ||
| 400 | + case RW_MA0_LO: | ||
| 401 | + eth->regs[addr] = value; | ||
| 402 | + eth_update_ma(eth, 0); | ||
| 403 | + break; | ||
| 404 | + case RW_MA0_HI: | ||
| 405 | + eth->regs[addr] = value; | ||
| 406 | + eth_update_ma(eth, 0); | ||
| 407 | + break; | ||
| 408 | + case RW_MA1_LO: | ||
| 409 | + eth->regs[addr] = value; | ||
| 410 | + eth_update_ma(eth, 1); | ||
| 411 | + break; | ||
| 412 | + case RW_MA1_HI: | ||
| 413 | + eth->regs[addr] = value; | ||
| 414 | + eth_update_ma(eth, 1); | ||
| 415 | + break; | ||
| 360 | 416 | ||
| 361 | - /* Make addr relative to this instances base. */ | ||
| 362 | - addr -= eth->base; | ||
| 363 | - switch (addr) | ||
| 364 | - { | ||
| 365 | case RW_MGM_CTRL: | 417 | case RW_MGM_CTRL: |
| 366 | /* Attach an MDIO/PHY abstraction. */ | 418 | /* Attach an MDIO/PHY abstraction. */ |
| 367 | if (value & 2) | 419 | if (value & 2) |
| @@ -371,11 +423,56 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | @@ -371,11 +423,56 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | ||
| 371 | eth->mdio_bus.mdc = !!(value & 4); | 423 | eth->mdio_bus.mdc = !!(value & 4); |
| 372 | break; | 424 | break; |
| 373 | 425 | ||
| 374 | - default: | ||
| 375 | - printf ("%s %x %x pc=%x\n", | ||
| 376 | - __func__, addr, value, env->pc); | ||
| 377 | - break; | ||
| 378 | - } | 426 | + default: |
| 427 | + eth->regs[addr] = value; | ||
| 428 | + printf ("%s %x %x pc=%x\n", | ||
| 429 | + __func__, addr, value, env->pc); | ||
| 430 | + break; | ||
| 431 | + } | ||
| 432 | +} | ||
| 433 | + | ||
| 434 | +/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom | ||
| 435 | + filter dropping group addresses we have not joined. The filter has 64 | ||
| 436 | + bits (m). The has function is a simple nible xor of the group addr. */ | ||
| 437 | +static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa) | ||
| 438 | +{ | ||
| 439 | + unsigned int hsh; | ||
| 440 | + int m_individual = eth->regs[RW_REC_CTRL] & 4; | ||
| 441 | + int match; | ||
| 442 | + | ||
| 443 | + /* First bit on the wire of a MAC address signals multicast or | ||
| 444 | + physical address. */ | ||
| 445 | + if (!m_individual && !sa[0] & 1) | ||
| 446 | + return 0; | ||
| 447 | + | ||
| 448 | + /* Calculate the hash index for the GA registers. */ | ||
| 449 | + hsh = 0; | ||
| 450 | + hsh ^= (*sa) & 0x3f; | ||
| 451 | + hsh ^= ((*sa) >> 6) & 0x03; | ||
| 452 | + ++sa; | ||
| 453 | + hsh ^= ((*sa) << 2) & 0x03c; | ||
| 454 | + hsh ^= ((*sa) >> 4) & 0xf; | ||
| 455 | + ++sa; | ||
| 456 | + hsh ^= ((*sa) << 4) & 0x30; | ||
| 457 | + hsh ^= ((*sa) >> 2) & 0x3f; | ||
| 458 | + ++sa; | ||
| 459 | + hsh ^= (*sa) & 0x3f; | ||
| 460 | + hsh ^= ((*sa) >> 6) & 0x03; | ||
| 461 | + ++sa; | ||
| 462 | + hsh ^= ((*sa) << 2) & 0x03c; | ||
| 463 | + hsh ^= ((*sa) >> 4) & 0xf; | ||
| 464 | + ++sa; | ||
| 465 | + hsh ^= ((*sa) << 4) & 0x30; | ||
| 466 | + hsh ^= ((*sa) >> 2) & 0x3f; | ||
| 467 | + | ||
| 468 | + hsh &= 63; | ||
| 469 | + if (hsh > 31) | ||
| 470 | + match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); | ||
| 471 | + else | ||
| 472 | + match = eth->regs[RW_GA_LO] & (1 << hsh); | ||
| 473 | + D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, | ||
| 474 | + eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); | ||
| 475 | + return match; | ||
| 379 | } | 476 | } |
| 380 | 477 | ||
| 381 | static int eth_can_receive(void *opaque) | 478 | static int eth_can_receive(void *opaque) |
| @@ -393,12 +490,33 @@ static int eth_can_receive(void *opaque) | @@ -393,12 +490,33 @@ static int eth_can_receive(void *opaque) | ||
| 393 | 490 | ||
| 394 | static void eth_receive(void *opaque, const uint8_t *buf, int size) | 491 | static void eth_receive(void *opaque, const uint8_t *buf, int size) |
| 395 | { | 492 | { |
| 493 | + unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
| 396 | struct fs_eth *eth = opaque; | 494 | struct fs_eth *eth = opaque; |
| 495 | + int use_ma0 = eth->regs[RW_REC_CTRL] & 1; | ||
| 496 | + int use_ma1 = eth->regs[RW_REC_CTRL] & 2; | ||
| 497 | + int r_bcast = eth->regs[RW_REC_CTRL] & 8; | ||
| 498 | + | ||
| 499 | + if (size < 12) | ||
| 500 | + return; | ||
| 501 | + | ||
| 502 | + D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", | ||
| 503 | + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], | ||
| 504 | + use_ma0, use_ma1, r_bcast)); | ||
| 505 | + | ||
| 506 | + /* Does the frame get through the address filters? */ | ||
| 507 | + if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) | ||
| 508 | + && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) | ||
| 509 | + && (!r_bcast || memcmp(buf, sa_bcast, 6)) | ||
| 510 | + && !eth_match_groupaddr(eth, buf)) | ||
| 511 | + return; | ||
| 512 | + | ||
| 397 | if (size > sizeof(eth->rx_fifo)) { | 513 | if (size > sizeof(eth->rx_fifo)) { |
| 398 | - /* TODO: signal error. */ | 514 | + /* TODO: signal error. */ |
| 515 | + } else if (eth->rx_fifo_len) { | ||
| 516 | + /* FIFO overrun. */ | ||
| 399 | } else { | 517 | } else { |
| 400 | memcpy(eth->rx_fifo, buf, size); | 518 | memcpy(eth->rx_fifo, buf, size); |
| 401 | - /* +4, HW passes the CRC to sw. */ | 519 | + /* +4, HW passes the CRC to sw. */ |
| 402 | eth->rx_fifo_len = size + 4; | 520 | eth->rx_fifo_len = size + 4; |
| 403 | eth->rx_fifo_pos = 0; | 521 | eth->rx_fifo_pos = 0; |
| 404 | } | 522 | } |
| @@ -471,7 +589,6 @@ void *etraxfs_eth_init(NICInfo *nd, CPUState *env, | @@ -471,7 +589,6 @@ void *etraxfs_eth_init(NICInfo *nd, CPUState *env, | ||
| 471 | eth->irq = irq; | 589 | eth->irq = irq; |
| 472 | eth->dma_out = dma; | 590 | eth->dma_out = dma; |
| 473 | eth->dma_in = dma + 1; | 591 | eth->dma_in = dma + 1; |
| 474 | - memcpy(eth->macaddr, nd->macaddr, 6); | ||
| 475 | 592 | ||
| 476 | /* Connect the phy. */ | 593 | /* Connect the phy. */ |
| 477 | tdk_init(ð->phy); | 594 | tdk_init(ð->phy); |