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