1a65f56eeSaurel32 /* 2a65f56eeSaurel32 * QEMU NS SONIC DP8393x netcard 3a65f56eeSaurel32 * 4a65f56eeSaurel32 * Copyright (c) 2008-2009 Herve Poussineau 5a65f56eeSaurel32 * 6a65f56eeSaurel32 * This program is free software; you can redistribute it and/or 7a65f56eeSaurel32 * modify it under the terms of the GNU General Public License as 8a65f56eeSaurel32 * published by the Free Software Foundation; either version 2 of 9a65f56eeSaurel32 * the License, or (at your option) any later version. 10a65f56eeSaurel32 * 11a65f56eeSaurel32 * This program is distributed in the hope that it will be useful, 12a65f56eeSaurel32 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13a65f56eeSaurel32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14a65f56eeSaurel32 * GNU General Public License for more details. 15a65f56eeSaurel32 * 16a65f56eeSaurel32 * You should have received a copy of the GNU General Public License along 178167ee88SBlue Swirl * with this program; if not, see <http://www.gnu.org/licenses/>. 18a65f56eeSaurel32 */ 19a65f56eeSaurel32 2083c9f4caSPaolo Bonzini #include "hw/hw.h" 211de7afc9SPaolo Bonzini #include "qemu/timer.h" 221422e32dSPaolo Bonzini #include "net/net.h" 230d09e41aSPaolo Bonzini #include "hw/mips/mips.h" 24a65f56eeSaurel32 25a65f56eeSaurel32 //#define DEBUG_SONIC 26a65f56eeSaurel32 27a65f56eeSaurel32 /* Calculate CRCs properly on Rx packets */ 28a65f56eeSaurel32 #define SONIC_CALCULATE_RXCRC 29a65f56eeSaurel32 30a65f56eeSaurel32 #if defined(SONIC_CALCULATE_RXCRC) 31a65f56eeSaurel32 /* For crc32 */ 32a65f56eeSaurel32 #include <zlib.h> 33a65f56eeSaurel32 #endif 34a65f56eeSaurel32 35a65f56eeSaurel32 #ifdef DEBUG_SONIC 36001faf32SBlue Swirl #define DPRINTF(fmt, ...) \ 37001faf32SBlue Swirl do { printf("sonic: " fmt , ## __VA_ARGS__); } while (0) 38a65f56eeSaurel32 static const char* reg_names[] = { 39a65f56eeSaurel32 "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA", 40a65f56eeSaurel32 "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0", 41a65f56eeSaurel32 "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP", 42a65f56eeSaurel32 "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA", 43a65f56eeSaurel32 "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC", 44a65f56eeSaurel32 "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT", 45a65f56eeSaurel32 "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", 46a65f56eeSaurel32 "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" }; 47a65f56eeSaurel32 #else 48001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0) 49a65f56eeSaurel32 #endif 50a65f56eeSaurel32 51001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \ 52001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) 53a65f56eeSaurel32 54a65f56eeSaurel32 #define SONIC_CR 0x00 55a65f56eeSaurel32 #define SONIC_DCR 0x01 56a65f56eeSaurel32 #define SONIC_RCR 0x02 57a65f56eeSaurel32 #define SONIC_TCR 0x03 58a65f56eeSaurel32 #define SONIC_IMR 0x04 59a65f56eeSaurel32 #define SONIC_ISR 0x05 60a65f56eeSaurel32 #define SONIC_UTDA 0x06 61a65f56eeSaurel32 #define SONIC_CTDA 0x07 62a65f56eeSaurel32 #define SONIC_TPS 0x08 63a65f56eeSaurel32 #define SONIC_TFC 0x09 64a65f56eeSaurel32 #define SONIC_TSA0 0x0a 65a65f56eeSaurel32 #define SONIC_TSA1 0x0b 66a65f56eeSaurel32 #define SONIC_TFS 0x0c 67a65f56eeSaurel32 #define SONIC_URDA 0x0d 68a65f56eeSaurel32 #define SONIC_CRDA 0x0e 69a65f56eeSaurel32 #define SONIC_CRBA0 0x0f 70a65f56eeSaurel32 #define SONIC_CRBA1 0x10 71a65f56eeSaurel32 #define SONIC_RBWC0 0x11 72a65f56eeSaurel32 #define SONIC_RBWC1 0x12 73a65f56eeSaurel32 #define SONIC_EOBC 0x13 74a65f56eeSaurel32 #define SONIC_URRA 0x14 75a65f56eeSaurel32 #define SONIC_RSA 0x15 76a65f56eeSaurel32 #define SONIC_REA 0x16 77a65f56eeSaurel32 #define SONIC_RRP 0x17 78a65f56eeSaurel32 #define SONIC_RWP 0x18 79a65f56eeSaurel32 #define SONIC_TRBA0 0x19 80a65f56eeSaurel32 #define SONIC_TRBA1 0x1a 81a65f56eeSaurel32 #define SONIC_LLFA 0x1f 82a65f56eeSaurel32 #define SONIC_TTDA 0x20 83a65f56eeSaurel32 #define SONIC_CEP 0x21 84a65f56eeSaurel32 #define SONIC_CAP2 0x22 85a65f56eeSaurel32 #define SONIC_CAP1 0x23 86a65f56eeSaurel32 #define SONIC_CAP0 0x24 87a65f56eeSaurel32 #define SONIC_CE 0x25 88a65f56eeSaurel32 #define SONIC_CDP 0x26 89a65f56eeSaurel32 #define SONIC_CDC 0x27 90a65f56eeSaurel32 #define SONIC_SR 0x28 91a65f56eeSaurel32 #define SONIC_WT0 0x29 92a65f56eeSaurel32 #define SONIC_WT1 0x2a 93a65f56eeSaurel32 #define SONIC_RSC 0x2b 94a65f56eeSaurel32 #define SONIC_CRCT 0x2c 95a65f56eeSaurel32 #define SONIC_FAET 0x2d 96a65f56eeSaurel32 #define SONIC_MPT 0x2e 97a65f56eeSaurel32 #define SONIC_MDT 0x2f 98a65f56eeSaurel32 #define SONIC_DCR2 0x3f 99a65f56eeSaurel32 100a65f56eeSaurel32 #define SONIC_CR_HTX 0x0001 101a65f56eeSaurel32 #define SONIC_CR_TXP 0x0002 102a65f56eeSaurel32 #define SONIC_CR_RXDIS 0x0004 103a65f56eeSaurel32 #define SONIC_CR_RXEN 0x0008 104a65f56eeSaurel32 #define SONIC_CR_STP 0x0010 105a65f56eeSaurel32 #define SONIC_CR_ST 0x0020 106a65f56eeSaurel32 #define SONIC_CR_RST 0x0080 107a65f56eeSaurel32 #define SONIC_CR_RRRA 0x0100 108a65f56eeSaurel32 #define SONIC_CR_LCAM 0x0200 109a65f56eeSaurel32 #define SONIC_CR_MASK 0x03bf 110a65f56eeSaurel32 111a65f56eeSaurel32 #define SONIC_DCR_DW 0x0020 112a65f56eeSaurel32 #define SONIC_DCR_LBR 0x2000 113a65f56eeSaurel32 #define SONIC_DCR_EXBUS 0x8000 114a65f56eeSaurel32 115a65f56eeSaurel32 #define SONIC_RCR_PRX 0x0001 116a65f56eeSaurel32 #define SONIC_RCR_LBK 0x0002 117a65f56eeSaurel32 #define SONIC_RCR_FAER 0x0004 118a65f56eeSaurel32 #define SONIC_RCR_CRCR 0x0008 119a65f56eeSaurel32 #define SONIC_RCR_CRS 0x0020 120a65f56eeSaurel32 #define SONIC_RCR_LPKT 0x0040 121a65f56eeSaurel32 #define SONIC_RCR_BC 0x0080 122a65f56eeSaurel32 #define SONIC_RCR_MC 0x0100 123a65f56eeSaurel32 #define SONIC_RCR_LB0 0x0200 124a65f56eeSaurel32 #define SONIC_RCR_LB1 0x0400 125a65f56eeSaurel32 #define SONIC_RCR_AMC 0x0800 126a65f56eeSaurel32 #define SONIC_RCR_PRO 0x1000 127a65f56eeSaurel32 #define SONIC_RCR_BRD 0x2000 128a65f56eeSaurel32 #define SONIC_RCR_RNT 0x4000 129a65f56eeSaurel32 130a65f56eeSaurel32 #define SONIC_TCR_PTX 0x0001 131a65f56eeSaurel32 #define SONIC_TCR_BCM 0x0002 132a65f56eeSaurel32 #define SONIC_TCR_FU 0x0004 133a65f56eeSaurel32 #define SONIC_TCR_EXC 0x0040 134a65f56eeSaurel32 #define SONIC_TCR_CRSL 0x0080 135a65f56eeSaurel32 #define SONIC_TCR_NCRS 0x0100 136a65f56eeSaurel32 #define SONIC_TCR_EXD 0x0400 137a65f56eeSaurel32 #define SONIC_TCR_CRCI 0x2000 138a65f56eeSaurel32 #define SONIC_TCR_PINT 0x8000 139a65f56eeSaurel32 140a65f56eeSaurel32 #define SONIC_ISR_RBE 0x0020 141a65f56eeSaurel32 #define SONIC_ISR_RDE 0x0040 142a65f56eeSaurel32 #define SONIC_ISR_TC 0x0080 143a65f56eeSaurel32 #define SONIC_ISR_TXDN 0x0200 144a65f56eeSaurel32 #define SONIC_ISR_PKTRX 0x0400 145a65f56eeSaurel32 #define SONIC_ISR_PINT 0x0800 146a65f56eeSaurel32 #define SONIC_ISR_LCD 0x1000 147a65f56eeSaurel32 148a65f56eeSaurel32 typedef struct dp8393xState { 149a65f56eeSaurel32 /* Hardware */ 150a65f56eeSaurel32 int it_shift; 151a65f56eeSaurel32 qemu_irq irq; 152a65f56eeSaurel32 #ifdef DEBUG_SONIC 153a65f56eeSaurel32 int irq_level; 154a65f56eeSaurel32 #endif 155a65f56eeSaurel32 QEMUTimer *watchdog; 156a65f56eeSaurel32 int64_t wt_last_update; 15705f41fe3SMark McLoughlin NICConf conf; 15805f41fe3SMark McLoughlin NICState *nic; 159024e5bb6SAvi Kivity MemoryRegion *address_space; 160024e5bb6SAvi Kivity MemoryRegion mmio; 161a65f56eeSaurel32 162a65f56eeSaurel32 /* Registers */ 163a65f56eeSaurel32 uint8_t cam[16][6]; 164a65f56eeSaurel32 uint16_t regs[0x40]; 165a65f56eeSaurel32 166a65f56eeSaurel32 /* Temporaries */ 167a65f56eeSaurel32 uint8_t tx_buffer[0x10000]; 168a65f56eeSaurel32 int loopback_packet; 169a65f56eeSaurel32 170a65f56eeSaurel32 /* Memory access */ 171a8170e5eSAvi Kivity void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write); 172a65f56eeSaurel32 void* mem_opaque; 173a65f56eeSaurel32 } dp8393xState; 174a65f56eeSaurel32 175a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s) 176a65f56eeSaurel32 { 177a65f56eeSaurel32 int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; 178a65f56eeSaurel32 179a65f56eeSaurel32 #ifdef DEBUG_SONIC 180a65f56eeSaurel32 if (level != s->irq_level) { 181a65f56eeSaurel32 s->irq_level = level; 182a65f56eeSaurel32 if (level) { 183a65f56eeSaurel32 DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]); 184a65f56eeSaurel32 } else { 185a65f56eeSaurel32 DPRINTF("lower irq\n"); 186a65f56eeSaurel32 } 187a65f56eeSaurel32 } 188a65f56eeSaurel32 #endif 189a65f56eeSaurel32 190a65f56eeSaurel32 qemu_set_irq(s->irq, level); 191a65f56eeSaurel32 } 192a65f56eeSaurel32 193a65f56eeSaurel32 static void do_load_cam(dp8393xState *s) 194a65f56eeSaurel32 { 195a65f56eeSaurel32 uint16_t data[8]; 196a65f56eeSaurel32 int width, size; 197a65f56eeSaurel32 uint16_t index = 0; 198a65f56eeSaurel32 199a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 200a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 201a65f56eeSaurel32 202a65f56eeSaurel32 while (s->regs[SONIC_CDC] & 0x1f) { 203a65f56eeSaurel32 /* Fill current entry */ 204a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 205a65f56eeSaurel32 (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], 206a65f56eeSaurel32 (uint8_t *)data, size, 0); 207a65f56eeSaurel32 s->cam[index][0] = data[1 * width] & 0xff; 208a65f56eeSaurel32 s->cam[index][1] = data[1 * width] >> 8; 209a65f56eeSaurel32 s->cam[index][2] = data[2 * width] & 0xff; 210a65f56eeSaurel32 s->cam[index][3] = data[2 * width] >> 8; 211a65f56eeSaurel32 s->cam[index][4] = data[3 * width] & 0xff; 212a65f56eeSaurel32 s->cam[index][5] = data[3 * width] >> 8; 213a65f56eeSaurel32 DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index, 214a65f56eeSaurel32 s->cam[index][0], s->cam[index][1], s->cam[index][2], 215a65f56eeSaurel32 s->cam[index][3], s->cam[index][4], s->cam[index][5]); 216a65f56eeSaurel32 /* Move to next entry */ 217a65f56eeSaurel32 s->regs[SONIC_CDC]--; 218a65f56eeSaurel32 s->regs[SONIC_CDP] += size; 219a65f56eeSaurel32 index++; 220a65f56eeSaurel32 } 221a65f56eeSaurel32 222a65f56eeSaurel32 /* Read CAM enable */ 223a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 224a65f56eeSaurel32 (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], 225a65f56eeSaurel32 (uint8_t *)data, size, 0); 226a65f56eeSaurel32 s->regs[SONIC_CE] = data[0 * width]; 227a65f56eeSaurel32 DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); 228a65f56eeSaurel32 229a65f56eeSaurel32 /* Done */ 230a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; 231a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_LCD; 232a65f56eeSaurel32 dp8393x_update_irq(s); 233a65f56eeSaurel32 } 234a65f56eeSaurel32 235a65f56eeSaurel32 static void do_read_rra(dp8393xState *s) 236a65f56eeSaurel32 { 237a65f56eeSaurel32 uint16_t data[8]; 238a65f56eeSaurel32 int width, size; 239a65f56eeSaurel32 240a65f56eeSaurel32 /* Read memory */ 241a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 242a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 243a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 244a65f56eeSaurel32 (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP], 245a65f56eeSaurel32 (uint8_t *)data, size, 0); 246a65f56eeSaurel32 247a65f56eeSaurel32 /* Update SONIC registers */ 248a65f56eeSaurel32 s->regs[SONIC_CRBA0] = data[0 * width]; 249a65f56eeSaurel32 s->regs[SONIC_CRBA1] = data[1 * width]; 250a65f56eeSaurel32 s->regs[SONIC_RBWC0] = data[2 * width]; 251a65f56eeSaurel32 s->regs[SONIC_RBWC1] = data[3 * width]; 252a65f56eeSaurel32 DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n", 253a65f56eeSaurel32 s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], 254a65f56eeSaurel32 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); 255a65f56eeSaurel32 256a65f56eeSaurel32 /* Go to next entry */ 257a65f56eeSaurel32 s->regs[SONIC_RRP] += size; 258a65f56eeSaurel32 259a65f56eeSaurel32 /* Handle wrap */ 260a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { 261a65f56eeSaurel32 s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; 262a65f56eeSaurel32 } 263a65f56eeSaurel32 264a65f56eeSaurel32 /* Check resource exhaustion */ 265a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) 266a65f56eeSaurel32 { 267a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RBE; 268a65f56eeSaurel32 dp8393x_update_irq(s); 269a65f56eeSaurel32 } 270a65f56eeSaurel32 271a65f56eeSaurel32 /* Done */ 272a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; 273a65f56eeSaurel32 } 274a65f56eeSaurel32 275a65f56eeSaurel32 static void do_software_reset(dp8393xState *s) 276a65f56eeSaurel32 { 277*bc72ad67SAlex Bligh timer_del(s->watchdog); 278a65f56eeSaurel32 279a65f56eeSaurel32 s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX); 280a65f56eeSaurel32 s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; 281a65f56eeSaurel32 } 282a65f56eeSaurel32 283a65f56eeSaurel32 static void set_next_tick(dp8393xState *s) 284a65f56eeSaurel32 { 285a65f56eeSaurel32 uint32_t ticks; 286a65f56eeSaurel32 int64_t delay; 287a65f56eeSaurel32 288a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 289*bc72ad67SAlex Bligh timer_del(s->watchdog); 290a65f56eeSaurel32 return; 291a65f56eeSaurel32 } 292a65f56eeSaurel32 293a65f56eeSaurel32 ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 294*bc72ad67SAlex Bligh s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 2956ee093c9SJuan Quintela delay = get_ticks_per_sec() * ticks / 5000000; 296*bc72ad67SAlex Bligh timer_mod(s->watchdog, s->wt_last_update + delay); 297a65f56eeSaurel32 } 298a65f56eeSaurel32 299a65f56eeSaurel32 static void update_wt_regs(dp8393xState *s) 300a65f56eeSaurel32 { 301a65f56eeSaurel32 int64_t elapsed; 302a65f56eeSaurel32 uint32_t val; 303a65f56eeSaurel32 304a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 305*bc72ad67SAlex Bligh timer_del(s->watchdog); 306a65f56eeSaurel32 return; 307a65f56eeSaurel32 } 308a65f56eeSaurel32 309*bc72ad67SAlex Bligh elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 310a65f56eeSaurel32 val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 311a65f56eeSaurel32 val -= elapsed / 5000000; 312a65f56eeSaurel32 s->regs[SONIC_WT1] = (val >> 16) & 0xffff; 313a65f56eeSaurel32 s->regs[SONIC_WT0] = (val >> 0) & 0xffff; 314a65f56eeSaurel32 set_next_tick(s); 315a65f56eeSaurel32 316a65f56eeSaurel32 } 317a65f56eeSaurel32 318a65f56eeSaurel32 static void do_start_timer(dp8393xState *s) 319a65f56eeSaurel32 { 320a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_STP; 321a65f56eeSaurel32 set_next_tick(s); 322a65f56eeSaurel32 } 323a65f56eeSaurel32 324a65f56eeSaurel32 static void do_stop_timer(dp8393xState *s) 325a65f56eeSaurel32 { 326a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_ST; 327a65f56eeSaurel32 update_wt_regs(s); 328a65f56eeSaurel32 } 329a65f56eeSaurel32 330a65f56eeSaurel32 static void do_receiver_enable(dp8393xState *s) 331a65f56eeSaurel32 { 332a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; 333a65f56eeSaurel32 } 334a65f56eeSaurel32 335a65f56eeSaurel32 static void do_receiver_disable(dp8393xState *s) 336a65f56eeSaurel32 { 337a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; 338a65f56eeSaurel32 } 339a65f56eeSaurel32 340a65f56eeSaurel32 static void do_transmit_packets(dp8393xState *s) 341a65f56eeSaurel32 { 342b356f76dSJason Wang NetClientState *nc = qemu_get_queue(s->nic); 343a65f56eeSaurel32 uint16_t data[12]; 344a65f56eeSaurel32 int width, size; 345a65f56eeSaurel32 int tx_len, len; 346a65f56eeSaurel32 uint16_t i; 347a65f56eeSaurel32 348a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 349a65f56eeSaurel32 350a65f56eeSaurel32 while (1) { 351a65f56eeSaurel32 /* Read memory */ 352a65f56eeSaurel32 DPRINTF("Transmit packet at %08x\n", 353a65f56eeSaurel32 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]); 354a65f56eeSaurel32 size = sizeof(uint16_t) * 6 * width; 355a65f56eeSaurel32 s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; 356a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 357a65f56eeSaurel32 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width, 358a65f56eeSaurel32 (uint8_t *)data, size, 0); 359a65f56eeSaurel32 tx_len = 0; 360a65f56eeSaurel32 361a65f56eeSaurel32 /* Update registers */ 362a65f56eeSaurel32 s->regs[SONIC_TCR] = data[0 * width] & 0xf000; 363a65f56eeSaurel32 s->regs[SONIC_TPS] = data[1 * width]; 364a65f56eeSaurel32 s->regs[SONIC_TFC] = data[2 * width]; 365a65f56eeSaurel32 s->regs[SONIC_TSA0] = data[3 * width]; 366a65f56eeSaurel32 s->regs[SONIC_TSA1] = data[4 * width]; 367a65f56eeSaurel32 s->regs[SONIC_TFS] = data[5 * width]; 368a65f56eeSaurel32 369a65f56eeSaurel32 /* Handle programmable interrupt */ 370a65f56eeSaurel32 if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { 371a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PINT; 372a65f56eeSaurel32 } else { 373a65f56eeSaurel32 s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; 374a65f56eeSaurel32 } 375a65f56eeSaurel32 376a65f56eeSaurel32 for (i = 0; i < s->regs[SONIC_TFC]; ) { 377a65f56eeSaurel32 /* Append fragment */ 378a65f56eeSaurel32 len = s->regs[SONIC_TFS]; 379a65f56eeSaurel32 if (tx_len + len > sizeof(s->tx_buffer)) { 380a65f56eeSaurel32 len = sizeof(s->tx_buffer) - tx_len; 381a65f56eeSaurel32 } 382a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 383a65f56eeSaurel32 (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0], 384a65f56eeSaurel32 &s->tx_buffer[tx_len], len, 0); 385a65f56eeSaurel32 tx_len += len; 386a65f56eeSaurel32 387a65f56eeSaurel32 i++; 388a65f56eeSaurel32 if (i != s->regs[SONIC_TFC]) { 389a65f56eeSaurel32 /* Read next fragment details */ 390a65f56eeSaurel32 size = sizeof(uint16_t) * 3 * width; 391a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 392a65f56eeSaurel32 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width, 393a65f56eeSaurel32 (uint8_t *)data, size, 0); 394a65f56eeSaurel32 s->regs[SONIC_TSA0] = data[0 * width]; 395a65f56eeSaurel32 s->regs[SONIC_TSA1] = data[1 * width]; 396a65f56eeSaurel32 s->regs[SONIC_TFS] = data[2 * width]; 397a65f56eeSaurel32 } 398a65f56eeSaurel32 } 399a65f56eeSaurel32 400a65f56eeSaurel32 /* Handle Ethernet checksum */ 401a65f56eeSaurel32 if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { 402a65f56eeSaurel32 /* Don't append FCS there, to look like slirp packets 403a65f56eeSaurel32 * which don't have one */ 404a65f56eeSaurel32 } else { 405a65f56eeSaurel32 /* Remove existing FCS */ 406a65f56eeSaurel32 tx_len -= 4; 407a65f56eeSaurel32 } 408a65f56eeSaurel32 409a65f56eeSaurel32 if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { 410a65f56eeSaurel32 /* Loopback */ 411a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; 412b356f76dSJason Wang if (nc->info->can_receive(nc)) { 413a65f56eeSaurel32 s->loopback_packet = 1; 414b356f76dSJason Wang nc->info->receive(nc, s->tx_buffer, tx_len); 415a65f56eeSaurel32 } 416a65f56eeSaurel32 } else { 417a65f56eeSaurel32 /* Transmit packet */ 418b356f76dSJason Wang qemu_send_packet(nc, s->tx_buffer, tx_len); 419a65f56eeSaurel32 } 420a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_PTX; 421a65f56eeSaurel32 422a65f56eeSaurel32 /* Write status */ 423a65f56eeSaurel32 data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */ 424a65f56eeSaurel32 size = sizeof(uint16_t) * width; 425a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 426a65f56eeSaurel32 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA], 427a65f56eeSaurel32 (uint8_t *)data, size, 1); 428a65f56eeSaurel32 429a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { 430a65f56eeSaurel32 /* Read footer of packet */ 431a65f56eeSaurel32 size = sizeof(uint16_t) * width; 432a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 433a65f56eeSaurel32 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width, 434a65f56eeSaurel32 (uint8_t *)data, size, 0); 435a65f56eeSaurel32 s->regs[SONIC_CTDA] = data[0 * width] & ~0x1; 436a65f56eeSaurel32 if (data[0 * width] & 0x1) { 437a65f56eeSaurel32 /* EOL detected */ 438a65f56eeSaurel32 break; 439a65f56eeSaurel32 } 440a65f56eeSaurel32 } 441a65f56eeSaurel32 } 442a65f56eeSaurel32 443a65f56eeSaurel32 /* Done */ 444a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_TXP; 445a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; 446a65f56eeSaurel32 dp8393x_update_irq(s); 447a65f56eeSaurel32 } 448a65f56eeSaurel32 449a65f56eeSaurel32 static void do_halt_transmission(dp8393xState *s) 450a65f56eeSaurel32 { 451a65f56eeSaurel32 /* Nothing to do */ 452a65f56eeSaurel32 } 453a65f56eeSaurel32 454a65f56eeSaurel32 static void do_command(dp8393xState *s, uint16_t command) 455a65f56eeSaurel32 { 456a65f56eeSaurel32 if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { 457a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RST; 458a65f56eeSaurel32 return; 459a65f56eeSaurel32 } 460a65f56eeSaurel32 461a65f56eeSaurel32 s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); 462a65f56eeSaurel32 463a65f56eeSaurel32 if (command & SONIC_CR_HTX) 464a65f56eeSaurel32 do_halt_transmission(s); 465a65f56eeSaurel32 if (command & SONIC_CR_TXP) 466a65f56eeSaurel32 do_transmit_packets(s); 467a65f56eeSaurel32 if (command & SONIC_CR_RXDIS) 468a65f56eeSaurel32 do_receiver_disable(s); 469a65f56eeSaurel32 if (command & SONIC_CR_RXEN) 470a65f56eeSaurel32 do_receiver_enable(s); 471a65f56eeSaurel32 if (command & SONIC_CR_STP) 472a65f56eeSaurel32 do_stop_timer(s); 473a65f56eeSaurel32 if (command & SONIC_CR_ST) 474a65f56eeSaurel32 do_start_timer(s); 475a65f56eeSaurel32 if (command & SONIC_CR_RST) 476a65f56eeSaurel32 do_software_reset(s); 477a65f56eeSaurel32 if (command & SONIC_CR_RRRA) 478a65f56eeSaurel32 do_read_rra(s); 479a65f56eeSaurel32 if (command & SONIC_CR_LCAM) 480a65f56eeSaurel32 do_load_cam(s); 481a65f56eeSaurel32 } 482a65f56eeSaurel32 483a65f56eeSaurel32 static uint16_t read_register(dp8393xState *s, int reg) 484a65f56eeSaurel32 { 485a65f56eeSaurel32 uint16_t val = 0; 486a65f56eeSaurel32 487a65f56eeSaurel32 switch (reg) { 488a65f56eeSaurel32 /* Update data before reading it */ 489a65f56eeSaurel32 case SONIC_WT0: 490a65f56eeSaurel32 case SONIC_WT1: 491a65f56eeSaurel32 update_wt_regs(s); 492a65f56eeSaurel32 val = s->regs[reg]; 493a65f56eeSaurel32 break; 494a65f56eeSaurel32 /* Accept read to some registers only when in reset mode */ 495a65f56eeSaurel32 case SONIC_CAP2: 496a65f56eeSaurel32 case SONIC_CAP1: 497a65f56eeSaurel32 case SONIC_CAP0: 498a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 499a65f56eeSaurel32 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8; 500a65f56eeSaurel32 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)]; 501a65f56eeSaurel32 } 502a65f56eeSaurel32 break; 503a65f56eeSaurel32 /* All other registers have no special contrainst */ 504a65f56eeSaurel32 default: 505a65f56eeSaurel32 val = s->regs[reg]; 506a65f56eeSaurel32 } 507a65f56eeSaurel32 508a65f56eeSaurel32 DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); 509a65f56eeSaurel32 510a65f56eeSaurel32 return val; 511a65f56eeSaurel32 } 512a65f56eeSaurel32 513a65f56eeSaurel32 static void write_register(dp8393xState *s, int reg, uint16_t val) 514a65f56eeSaurel32 { 515a65f56eeSaurel32 DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]); 516a65f56eeSaurel32 517a65f56eeSaurel32 switch (reg) { 518a65f56eeSaurel32 /* Command register */ 519a65f56eeSaurel32 case SONIC_CR: 520d1805896SHervé Poussineau do_command(s, val); 521a65f56eeSaurel32 break; 522a65f56eeSaurel32 /* Prevent write to read-only registers */ 523a65f56eeSaurel32 case SONIC_CAP2: 524a65f56eeSaurel32 case SONIC_CAP1: 525a65f56eeSaurel32 case SONIC_CAP0: 526a65f56eeSaurel32 case SONIC_SR: 527a65f56eeSaurel32 case SONIC_MDT: 528a65f56eeSaurel32 DPRINTF("writing to reg %d invalid\n", reg); 529a65f56eeSaurel32 break; 530a65f56eeSaurel32 /* Accept write to some registers only when in reset mode */ 531a65f56eeSaurel32 case SONIC_DCR: 532a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 533a65f56eeSaurel32 s->regs[reg] = val & 0xbfff; 534a65f56eeSaurel32 } else { 535a65f56eeSaurel32 DPRINTF("writing to DCR invalid\n"); 536a65f56eeSaurel32 } 537a65f56eeSaurel32 break; 538a65f56eeSaurel32 case SONIC_DCR2: 539a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 540a65f56eeSaurel32 s->regs[reg] = val & 0xf017; 541a65f56eeSaurel32 } else { 542a65f56eeSaurel32 DPRINTF("writing to DCR2 invalid\n"); 543a65f56eeSaurel32 } 544a65f56eeSaurel32 break; 545a65f56eeSaurel32 /* 12 lower bytes are Read Only */ 546a65f56eeSaurel32 case SONIC_TCR: 547a65f56eeSaurel32 s->regs[reg] = val & 0xf000; 548a65f56eeSaurel32 break; 549a65f56eeSaurel32 /* 9 lower bytes are Read Only */ 550a65f56eeSaurel32 case SONIC_RCR: 551a65f56eeSaurel32 s->regs[reg] = val & 0xffe0; 552a65f56eeSaurel32 break; 553a65f56eeSaurel32 /* Ignore most significant bit */ 554a65f56eeSaurel32 case SONIC_IMR: 555a65f56eeSaurel32 s->regs[reg] = val & 0x7fff; 556a65f56eeSaurel32 dp8393x_update_irq(s); 557a65f56eeSaurel32 break; 558a65f56eeSaurel32 /* Clear bits by writing 1 to them */ 559a65f56eeSaurel32 case SONIC_ISR: 560a65f56eeSaurel32 val &= s->regs[reg]; 561a65f56eeSaurel32 s->regs[reg] &= ~val; 562a65f56eeSaurel32 if (val & SONIC_ISR_RBE) { 563a65f56eeSaurel32 do_read_rra(s); 564a65f56eeSaurel32 } 565a65f56eeSaurel32 dp8393x_update_irq(s); 566a65f56eeSaurel32 break; 567a65f56eeSaurel32 /* Ignore least significant bit */ 568a65f56eeSaurel32 case SONIC_RSA: 569a65f56eeSaurel32 case SONIC_REA: 570a65f56eeSaurel32 case SONIC_RRP: 571a65f56eeSaurel32 case SONIC_RWP: 572a65f56eeSaurel32 s->regs[reg] = val & 0xfffe; 573a65f56eeSaurel32 break; 574a65f56eeSaurel32 /* Invert written value for some registers */ 575a65f56eeSaurel32 case SONIC_CRCT: 576a65f56eeSaurel32 case SONIC_FAET: 577a65f56eeSaurel32 case SONIC_MPT: 578a65f56eeSaurel32 s->regs[reg] = val ^ 0xffff; 579a65f56eeSaurel32 break; 580a65f56eeSaurel32 /* All other registers have no special contrainst */ 581a65f56eeSaurel32 default: 582a65f56eeSaurel32 s->regs[reg] = val; 583a65f56eeSaurel32 } 584a65f56eeSaurel32 585a65f56eeSaurel32 if (reg == SONIC_WT0 || reg == SONIC_WT1) { 586a65f56eeSaurel32 set_next_tick(s); 587a65f56eeSaurel32 } 588a65f56eeSaurel32 } 589a65f56eeSaurel32 590a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque) 591a65f56eeSaurel32 { 592a65f56eeSaurel32 dp8393xState *s = opaque; 593a65f56eeSaurel32 594a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 595a65f56eeSaurel32 return; 596a65f56eeSaurel32 } 597a65f56eeSaurel32 598a65f56eeSaurel32 s->regs[SONIC_WT1] = 0xffff; 599a65f56eeSaurel32 s->regs[SONIC_WT0] = 0xffff; 600a65f56eeSaurel32 set_next_tick(s); 601a65f56eeSaurel32 602a65f56eeSaurel32 /* Signal underflow */ 603a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TC; 604a65f56eeSaurel32 dp8393x_update_irq(s); 605a65f56eeSaurel32 } 606a65f56eeSaurel32 607a8170e5eSAvi Kivity static uint32_t dp8393x_readw(void *opaque, hwaddr addr) 608a65f56eeSaurel32 { 609a65f56eeSaurel32 dp8393xState *s = opaque; 610a65f56eeSaurel32 int reg; 611a65f56eeSaurel32 612a65f56eeSaurel32 if ((addr & ((1 << s->it_shift) - 1)) != 0) { 613a65f56eeSaurel32 return 0; 614a65f56eeSaurel32 } 615a65f56eeSaurel32 616a65f56eeSaurel32 reg = addr >> s->it_shift; 617a65f56eeSaurel32 return read_register(s, reg); 618a65f56eeSaurel32 } 619a65f56eeSaurel32 620a8170e5eSAvi Kivity static uint32_t dp8393x_readb(void *opaque, hwaddr addr) 621a65f56eeSaurel32 { 622a65f56eeSaurel32 uint16_t v = dp8393x_readw(opaque, addr & ~0x1); 623a65f56eeSaurel32 return (v >> (8 * (addr & 0x1))) & 0xff; 624a65f56eeSaurel32 } 625a65f56eeSaurel32 626a8170e5eSAvi Kivity static uint32_t dp8393x_readl(void *opaque, hwaddr addr) 627a65f56eeSaurel32 { 628a65f56eeSaurel32 uint32_t v; 629a65f56eeSaurel32 v = dp8393x_readw(opaque, addr); 630a65f56eeSaurel32 v |= dp8393x_readw(opaque, addr + 2) << 16; 631a65f56eeSaurel32 return v; 632a65f56eeSaurel32 } 633a65f56eeSaurel32 634a8170e5eSAvi Kivity static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val) 635a65f56eeSaurel32 { 636a65f56eeSaurel32 dp8393xState *s = opaque; 637a65f56eeSaurel32 int reg; 638a65f56eeSaurel32 639a65f56eeSaurel32 if ((addr & ((1 << s->it_shift) - 1)) != 0) { 640a65f56eeSaurel32 return; 641a65f56eeSaurel32 } 642a65f56eeSaurel32 643a65f56eeSaurel32 reg = addr >> s->it_shift; 644a65f56eeSaurel32 645a65f56eeSaurel32 write_register(s, reg, (uint16_t)val); 646a65f56eeSaurel32 } 647a65f56eeSaurel32 648a8170e5eSAvi Kivity static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val) 649a65f56eeSaurel32 { 650a65f56eeSaurel32 uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1); 651a65f56eeSaurel32 652a65f56eeSaurel32 switch (addr & 3) { 653a65f56eeSaurel32 case 0: 654a65f56eeSaurel32 val = val | (old_val & 0xff00); 655a65f56eeSaurel32 break; 656a65f56eeSaurel32 case 1: 657a65f56eeSaurel32 val = (val << 8) | (old_val & 0x00ff); 658a65f56eeSaurel32 break; 659a65f56eeSaurel32 } 660a65f56eeSaurel32 dp8393x_writew(opaque, addr & ~0x1, val); 661a65f56eeSaurel32 } 662a65f56eeSaurel32 663a8170e5eSAvi Kivity static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val) 664a65f56eeSaurel32 { 665a65f56eeSaurel32 dp8393x_writew(opaque, addr, val & 0xffff); 666a65f56eeSaurel32 dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff); 667a65f56eeSaurel32 } 668a65f56eeSaurel32 669024e5bb6SAvi Kivity static const MemoryRegionOps dp8393x_ops = { 670024e5bb6SAvi Kivity .old_mmio = { 671024e5bb6SAvi Kivity .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, }, 672024e5bb6SAvi Kivity .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, }, 673024e5bb6SAvi Kivity }, 674024e5bb6SAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 675a65f56eeSaurel32 }; 676a65f56eeSaurel32 6774e68f7a0SStefan Hajnoczi static int nic_can_receive(NetClientState *nc) 678a65f56eeSaurel32 { 679cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 680a65f56eeSaurel32 681a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) 682a65f56eeSaurel32 return 0; 683a65f56eeSaurel32 if (s->regs[SONIC_ISR] & SONIC_ISR_RBE) 684a65f56eeSaurel32 return 0; 685a65f56eeSaurel32 return 1; 686a65f56eeSaurel32 } 687a65f56eeSaurel32 688a65f56eeSaurel32 static int receive_filter(dp8393xState *s, const uint8_t * buf, int size) 689a65f56eeSaurel32 { 690a65f56eeSaurel32 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 691a65f56eeSaurel32 int i; 692a65f56eeSaurel32 693a65f56eeSaurel32 /* Check for runt packet (remember that checksum is not there) */ 694a65f56eeSaurel32 if (size < 64 - 4) { 695a65f56eeSaurel32 return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1; 696a65f56eeSaurel32 } 697a65f56eeSaurel32 698a65f56eeSaurel32 /* Check promiscuous mode */ 699a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { 700a65f56eeSaurel32 return 0; 701a65f56eeSaurel32 } 702a65f56eeSaurel32 703a65f56eeSaurel32 /* Check multicast packets */ 704a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { 705a65f56eeSaurel32 return SONIC_RCR_MC; 706a65f56eeSaurel32 } 707a65f56eeSaurel32 708a65f56eeSaurel32 /* Check broadcast */ 709a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) { 710a65f56eeSaurel32 return SONIC_RCR_BC; 711a65f56eeSaurel32 } 712a65f56eeSaurel32 713a65f56eeSaurel32 /* Check CAM */ 714a65f56eeSaurel32 for (i = 0; i < 16; i++) { 715a65f56eeSaurel32 if (s->regs[SONIC_CE] & (1 << i)) { 716a65f56eeSaurel32 /* Entry enabled */ 717a65f56eeSaurel32 if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) { 718a65f56eeSaurel32 return 0; 719a65f56eeSaurel32 } 720a65f56eeSaurel32 } 721a65f56eeSaurel32 } 722a65f56eeSaurel32 723a65f56eeSaurel32 return -1; 724a65f56eeSaurel32 } 725a65f56eeSaurel32 7264e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) 727a65f56eeSaurel32 { 728cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 729a65f56eeSaurel32 uint16_t data[10]; 730a65f56eeSaurel32 int packet_type; 731a65f56eeSaurel32 uint32_t available, address; 732a65f56eeSaurel32 int width, rx_len = size; 733a65f56eeSaurel32 uint32_t checksum; 734a65f56eeSaurel32 735a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 736a65f56eeSaurel32 737a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | 738a65f56eeSaurel32 SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); 739a65f56eeSaurel32 740a65f56eeSaurel32 packet_type = receive_filter(s, buf, size); 741a65f56eeSaurel32 if (packet_type < 0) { 742a65f56eeSaurel32 DPRINTF("packet not for netcard\n"); 7434f1c942bSMark McLoughlin return -1; 744a65f56eeSaurel32 } 745a65f56eeSaurel32 746a65f56eeSaurel32 /* XXX: Check byte ordering */ 747a65f56eeSaurel32 748a65f56eeSaurel32 /* Check for EOL */ 749a65f56eeSaurel32 if (s->regs[SONIC_LLFA] & 0x1) { 750a65f56eeSaurel32 /* Are we still in resource exhaustion? */ 751a65f56eeSaurel32 size = sizeof(uint16_t) * 1 * width; 752a65f56eeSaurel32 address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width; 753a65f56eeSaurel32 s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0); 754a65f56eeSaurel32 if (data[0 * width] & 0x1) { 755a65f56eeSaurel32 /* Still EOL ; stop reception */ 7564f1c942bSMark McLoughlin return -1; 757a65f56eeSaurel32 } else { 758a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 759a65f56eeSaurel32 } 760a65f56eeSaurel32 } 761a65f56eeSaurel32 762a65f56eeSaurel32 /* Save current position */ 763a65f56eeSaurel32 s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; 764a65f56eeSaurel32 s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; 765a65f56eeSaurel32 766a65f56eeSaurel32 /* Calculate the ethernet checksum */ 767a65f56eeSaurel32 #ifdef SONIC_CALCULATE_RXCRC 768a65f56eeSaurel32 checksum = cpu_to_le32(crc32(0, buf, rx_len)); 769a65f56eeSaurel32 #else 770a65f56eeSaurel32 checksum = 0; 771a65f56eeSaurel32 #endif 772a65f56eeSaurel32 773a65f56eeSaurel32 /* Put packet into RBA */ 774a65f56eeSaurel32 DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]); 775a65f56eeSaurel32 address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; 776a65f56eeSaurel32 s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1); 777a65f56eeSaurel32 address += rx_len; 778a65f56eeSaurel32 s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1); 779a65f56eeSaurel32 rx_len += 4; 780a65f56eeSaurel32 s->regs[SONIC_CRBA1] = address >> 16; 781a65f56eeSaurel32 s->regs[SONIC_CRBA0] = address & 0xffff; 782a65f56eeSaurel32 available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; 783a65f56eeSaurel32 available -= rx_len / 2; 784a65f56eeSaurel32 s->regs[SONIC_RBWC1] = available >> 16; 785a65f56eeSaurel32 s->regs[SONIC_RBWC0] = available & 0xffff; 786a65f56eeSaurel32 787a65f56eeSaurel32 /* Update status */ 788a65f56eeSaurel32 if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) { 789a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; 790a65f56eeSaurel32 } 791a65f56eeSaurel32 s->regs[SONIC_RCR] |= packet_type; 792a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_PRX; 793a65f56eeSaurel32 if (s->loopback_packet) { 794a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LBK; 795a65f56eeSaurel32 s->loopback_packet = 0; 796a65f56eeSaurel32 } 797a65f56eeSaurel32 798a65f56eeSaurel32 /* Write status to memory */ 799a65f56eeSaurel32 DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]); 800a65f56eeSaurel32 data[0 * width] = s->regs[SONIC_RCR]; /* status */ 801a65f56eeSaurel32 data[1 * width] = rx_len; /* byte count */ 802a65f56eeSaurel32 data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */ 803a65f56eeSaurel32 data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ 804a65f56eeSaurel32 data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ 805a65f56eeSaurel32 size = sizeof(uint16_t) * 5 * width; 806a65f56eeSaurel32 s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1); 807a65f56eeSaurel32 808a65f56eeSaurel32 /* Move to next descriptor */ 809a65f56eeSaurel32 size = sizeof(uint16_t) * width; 810a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 811a65f56eeSaurel32 ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width, 812a65f56eeSaurel32 (uint8_t *)data, size, 0); 813a65f56eeSaurel32 s->regs[SONIC_LLFA] = data[0 * width]; 814a65f56eeSaurel32 if (s->regs[SONIC_LLFA] & 0x1) { 815a65f56eeSaurel32 /* EOL detected */ 816a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RDE; 817a65f56eeSaurel32 } else { 818a65f56eeSaurel32 data[0 * width] = 0; /* in_use */ 819a65f56eeSaurel32 s->memory_rw(s->mem_opaque, 820a65f56eeSaurel32 ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width, 821a65f56eeSaurel32 (uint8_t *)data, size, 1); 822a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 823a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; 824a65f56eeSaurel32 s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); 825a65f56eeSaurel32 826a65f56eeSaurel32 if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { 827a65f56eeSaurel32 /* Read next RRA */ 828a65f56eeSaurel32 do_read_rra(s); 829a65f56eeSaurel32 } 830a65f56eeSaurel32 } 831a65f56eeSaurel32 832a65f56eeSaurel32 /* Done */ 833a65f56eeSaurel32 dp8393x_update_irq(s); 8344f1c942bSMark McLoughlin 8354f1c942bSMark McLoughlin return size; 836a65f56eeSaurel32 } 837a65f56eeSaurel32 838a65f56eeSaurel32 static void nic_reset(void *opaque) 839a65f56eeSaurel32 { 840a65f56eeSaurel32 dp8393xState *s = opaque; 841*bc72ad67SAlex Bligh timer_del(s->watchdog); 842a65f56eeSaurel32 843a65f56eeSaurel32 s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; 844a65f56eeSaurel32 s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); 845a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); 846a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; 847a65f56eeSaurel32 s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; 848a65f56eeSaurel32 s->regs[SONIC_IMR] = 0; 849a65f56eeSaurel32 s->regs[SONIC_ISR] = 0; 850a65f56eeSaurel32 s->regs[SONIC_DCR2] = 0; 851a65f56eeSaurel32 s->regs[SONIC_EOBC] = 0x02F8; 852a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 853a65f56eeSaurel32 s->regs[SONIC_CE] = 0; 854a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 855a65f56eeSaurel32 856a65f56eeSaurel32 /* Network cable is connected */ 857a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_CRS; 858a65f56eeSaurel32 859a65f56eeSaurel32 dp8393x_update_irq(s); 860a65f56eeSaurel32 } 861a65f56eeSaurel32 8624e68f7a0SStefan Hajnoczi static void nic_cleanup(NetClientState *nc) 863b946a153Saliguori { 864cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 865b946a153Saliguori 866024e5bb6SAvi Kivity memory_region_del_subregion(s->address_space, &s->mmio); 867024e5bb6SAvi Kivity memory_region_destroy(&s->mmio); 868b946a153Saliguori 869*bc72ad67SAlex Bligh timer_del(s->watchdog); 870*bc72ad67SAlex Bligh timer_free(s->watchdog); 871b946a153Saliguori 8727267c094SAnthony Liguori g_free(s); 873b946a153Saliguori } 874b946a153Saliguori 87505f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = { 8762be64a68SLaszlo Ersek .type = NET_CLIENT_OPTIONS_KIND_NIC, 87705f41fe3SMark McLoughlin .size = sizeof(NICState), 87805f41fe3SMark McLoughlin .can_receive = nic_can_receive, 87905f41fe3SMark McLoughlin .receive = nic_receive, 88005f41fe3SMark McLoughlin .cleanup = nic_cleanup, 88105f41fe3SMark McLoughlin }; 88205f41fe3SMark McLoughlin 883a8170e5eSAvi Kivity void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, 884024e5bb6SAvi Kivity MemoryRegion *address_space, 885a65f56eeSaurel32 qemu_irq irq, void* mem_opaque, 886a8170e5eSAvi Kivity void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)) 887a65f56eeSaurel32 { 888a65f56eeSaurel32 dp8393xState *s; 889a65f56eeSaurel32 890a65f56eeSaurel32 qemu_check_nic_model(nd, "dp83932"); 891a65f56eeSaurel32 8927267c094SAnthony Liguori s = g_malloc0(sizeof(dp8393xState)); 893a65f56eeSaurel32 894024e5bb6SAvi Kivity s->address_space = address_space; 895a65f56eeSaurel32 s->mem_opaque = mem_opaque; 896a65f56eeSaurel32 s->memory_rw = memory_rw; 897a65f56eeSaurel32 s->it_shift = it_shift; 898a65f56eeSaurel32 s->irq = irq; 899*bc72ad67SAlex Bligh s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); 900a65f56eeSaurel32 s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ 901a65f56eeSaurel32 9026eed1856SJan Kiszka s->conf.macaddr = nd->macaddr; 9031ceef9f2SJason Wang s->conf.peers.ncs[0] = nd->netdev; 904a65f56eeSaurel32 90505f41fe3SMark McLoughlin s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s); 90605f41fe3SMark McLoughlin 907b356f76dSJason Wang qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 908a08d4367SJan Kiszka qemu_register_reset(nic_reset, s); 909a65f56eeSaurel32 nic_reset(s); 910a65f56eeSaurel32 9112c9b15caSPaolo Bonzini memory_region_init_io(&s->mmio, NULL, &dp8393x_ops, s, 912024e5bb6SAvi Kivity "dp8393x", 0x40 << it_shift); 913024e5bb6SAvi Kivity memory_region_add_subregion(address_space, base, &s->mmio); 914a65f56eeSaurel32 } 915