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 20104655a5SHervé Poussineau #include "hw/sysbus.h" 21104655a5SHervé Poussineau #include "hw/devices.h" 221422e32dSPaolo Bonzini #include "net/net.h" 23104655a5SHervé Poussineau #include "qemu/timer.h" 24f2f62c4dSHervé Poussineau #include <zlib.h> 25a65f56eeSaurel32 26a65f56eeSaurel32 //#define DEBUG_SONIC 27a65f56eeSaurel32 28*89ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000 29a65f56eeSaurel32 30a65f56eeSaurel32 #ifdef DEBUG_SONIC 31001faf32SBlue Swirl #define DPRINTF(fmt, ...) \ 32001faf32SBlue Swirl do { printf("sonic: " fmt , ## __VA_ARGS__); } while (0) 33a65f56eeSaurel32 static const char* reg_names[] = { 34a65f56eeSaurel32 "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA", 35a65f56eeSaurel32 "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0", 36a65f56eeSaurel32 "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP", 37a65f56eeSaurel32 "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA", 38a65f56eeSaurel32 "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC", 39a65f56eeSaurel32 "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT", 40a65f56eeSaurel32 "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", 41a65f56eeSaurel32 "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" }; 42a65f56eeSaurel32 #else 43001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0) 44a65f56eeSaurel32 #endif 45a65f56eeSaurel32 46001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \ 47001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) 48a65f56eeSaurel32 49a65f56eeSaurel32 #define SONIC_CR 0x00 50a65f56eeSaurel32 #define SONIC_DCR 0x01 51a65f56eeSaurel32 #define SONIC_RCR 0x02 52a65f56eeSaurel32 #define SONIC_TCR 0x03 53a65f56eeSaurel32 #define SONIC_IMR 0x04 54a65f56eeSaurel32 #define SONIC_ISR 0x05 55a65f56eeSaurel32 #define SONIC_UTDA 0x06 56a65f56eeSaurel32 #define SONIC_CTDA 0x07 57a65f56eeSaurel32 #define SONIC_TPS 0x08 58a65f56eeSaurel32 #define SONIC_TFC 0x09 59a65f56eeSaurel32 #define SONIC_TSA0 0x0a 60a65f56eeSaurel32 #define SONIC_TSA1 0x0b 61a65f56eeSaurel32 #define SONIC_TFS 0x0c 62a65f56eeSaurel32 #define SONIC_URDA 0x0d 63a65f56eeSaurel32 #define SONIC_CRDA 0x0e 64a65f56eeSaurel32 #define SONIC_CRBA0 0x0f 65a65f56eeSaurel32 #define SONIC_CRBA1 0x10 66a65f56eeSaurel32 #define SONIC_RBWC0 0x11 67a65f56eeSaurel32 #define SONIC_RBWC1 0x12 68a65f56eeSaurel32 #define SONIC_EOBC 0x13 69a65f56eeSaurel32 #define SONIC_URRA 0x14 70a65f56eeSaurel32 #define SONIC_RSA 0x15 71a65f56eeSaurel32 #define SONIC_REA 0x16 72a65f56eeSaurel32 #define SONIC_RRP 0x17 73a65f56eeSaurel32 #define SONIC_RWP 0x18 74a65f56eeSaurel32 #define SONIC_TRBA0 0x19 75a65f56eeSaurel32 #define SONIC_TRBA1 0x1a 76a65f56eeSaurel32 #define SONIC_LLFA 0x1f 77a65f56eeSaurel32 #define SONIC_TTDA 0x20 78a65f56eeSaurel32 #define SONIC_CEP 0x21 79a65f56eeSaurel32 #define SONIC_CAP2 0x22 80a65f56eeSaurel32 #define SONIC_CAP1 0x23 81a65f56eeSaurel32 #define SONIC_CAP0 0x24 82a65f56eeSaurel32 #define SONIC_CE 0x25 83a65f56eeSaurel32 #define SONIC_CDP 0x26 84a65f56eeSaurel32 #define SONIC_CDC 0x27 85a65f56eeSaurel32 #define SONIC_SR 0x28 86a65f56eeSaurel32 #define SONIC_WT0 0x29 87a65f56eeSaurel32 #define SONIC_WT1 0x2a 88a65f56eeSaurel32 #define SONIC_RSC 0x2b 89a65f56eeSaurel32 #define SONIC_CRCT 0x2c 90a65f56eeSaurel32 #define SONIC_FAET 0x2d 91a65f56eeSaurel32 #define SONIC_MPT 0x2e 92a65f56eeSaurel32 #define SONIC_MDT 0x2f 93a65f56eeSaurel32 #define SONIC_DCR2 0x3f 94a65f56eeSaurel32 95a65f56eeSaurel32 #define SONIC_CR_HTX 0x0001 96a65f56eeSaurel32 #define SONIC_CR_TXP 0x0002 97a65f56eeSaurel32 #define SONIC_CR_RXDIS 0x0004 98a65f56eeSaurel32 #define SONIC_CR_RXEN 0x0008 99a65f56eeSaurel32 #define SONIC_CR_STP 0x0010 100a65f56eeSaurel32 #define SONIC_CR_ST 0x0020 101a65f56eeSaurel32 #define SONIC_CR_RST 0x0080 102a65f56eeSaurel32 #define SONIC_CR_RRRA 0x0100 103a65f56eeSaurel32 #define SONIC_CR_LCAM 0x0200 104a65f56eeSaurel32 #define SONIC_CR_MASK 0x03bf 105a65f56eeSaurel32 106a65f56eeSaurel32 #define SONIC_DCR_DW 0x0020 107a65f56eeSaurel32 #define SONIC_DCR_LBR 0x2000 108a65f56eeSaurel32 #define SONIC_DCR_EXBUS 0x8000 109a65f56eeSaurel32 110a65f56eeSaurel32 #define SONIC_RCR_PRX 0x0001 111a65f56eeSaurel32 #define SONIC_RCR_LBK 0x0002 112a65f56eeSaurel32 #define SONIC_RCR_FAER 0x0004 113a65f56eeSaurel32 #define SONIC_RCR_CRCR 0x0008 114a65f56eeSaurel32 #define SONIC_RCR_CRS 0x0020 115a65f56eeSaurel32 #define SONIC_RCR_LPKT 0x0040 116a65f56eeSaurel32 #define SONIC_RCR_BC 0x0080 117a65f56eeSaurel32 #define SONIC_RCR_MC 0x0100 118a65f56eeSaurel32 #define SONIC_RCR_LB0 0x0200 119a65f56eeSaurel32 #define SONIC_RCR_LB1 0x0400 120a65f56eeSaurel32 #define SONIC_RCR_AMC 0x0800 121a65f56eeSaurel32 #define SONIC_RCR_PRO 0x1000 122a65f56eeSaurel32 #define SONIC_RCR_BRD 0x2000 123a65f56eeSaurel32 #define SONIC_RCR_RNT 0x4000 124a65f56eeSaurel32 125a65f56eeSaurel32 #define SONIC_TCR_PTX 0x0001 126a65f56eeSaurel32 #define SONIC_TCR_BCM 0x0002 127a65f56eeSaurel32 #define SONIC_TCR_FU 0x0004 128a65f56eeSaurel32 #define SONIC_TCR_EXC 0x0040 129a65f56eeSaurel32 #define SONIC_TCR_CRSL 0x0080 130a65f56eeSaurel32 #define SONIC_TCR_NCRS 0x0100 131a65f56eeSaurel32 #define SONIC_TCR_EXD 0x0400 132a65f56eeSaurel32 #define SONIC_TCR_CRCI 0x2000 133a65f56eeSaurel32 #define SONIC_TCR_PINT 0x8000 134a65f56eeSaurel32 135a65f56eeSaurel32 #define SONIC_ISR_RBE 0x0020 136a65f56eeSaurel32 #define SONIC_ISR_RDE 0x0040 137a65f56eeSaurel32 #define SONIC_ISR_TC 0x0080 138a65f56eeSaurel32 #define SONIC_ISR_TXDN 0x0200 139a65f56eeSaurel32 #define SONIC_ISR_PKTRX 0x0400 140a65f56eeSaurel32 #define SONIC_ISR_PINT 0x0800 141a65f56eeSaurel32 #define SONIC_ISR_LCD 0x1000 142a65f56eeSaurel32 143104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x" 144104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) 145104655a5SHervé Poussineau 146a65f56eeSaurel32 typedef struct dp8393xState { 147104655a5SHervé Poussineau SysBusDevice parent_obj; 148104655a5SHervé Poussineau 149a65f56eeSaurel32 /* Hardware */ 150104655a5SHervé Poussineau uint8_t 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 mmio; 160*89ae0ff9SHervé Poussineau MemoryRegion prom; 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 */ 171104655a5SHervé Poussineau void *dma_mr; 172dd820513SHervé Poussineau AddressSpace as; 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 1933df5de64SHervé Poussineau static void dp8393x_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 */ 204dd820513SHervé Poussineau address_space_rw(&s->as, 205a65f56eeSaurel32 (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], 206dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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 */ 223dd820513SHervé Poussineau address_space_rw(&s->as, 224a65f56eeSaurel32 (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], 225dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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 2353df5de64SHervé Poussineau static void dp8393x_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; 243dd820513SHervé Poussineau address_space_rw(&s->as, 244a65f56eeSaurel32 (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP], 245dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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 2753df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s) 276a65f56eeSaurel32 { 277bc72ad67SAlex 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 2833df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s) 284a65f56eeSaurel32 { 285a65f56eeSaurel32 uint32_t ticks; 286a65f56eeSaurel32 int64_t delay; 287a65f56eeSaurel32 288a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 289bc72ad67SAlex Bligh timer_del(s->watchdog); 290a65f56eeSaurel32 return; 291a65f56eeSaurel32 } 292a65f56eeSaurel32 293a65f56eeSaurel32 ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 294bc72ad67SAlex Bligh s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 2956ee093c9SJuan Quintela delay = get_ticks_per_sec() * ticks / 5000000; 296bc72ad67SAlex Bligh timer_mod(s->watchdog, s->wt_last_update + delay); 297a65f56eeSaurel32 } 298a65f56eeSaurel32 2993df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s) 300a65f56eeSaurel32 { 301a65f56eeSaurel32 int64_t elapsed; 302a65f56eeSaurel32 uint32_t val; 303a65f56eeSaurel32 304a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 305bc72ad67SAlex Bligh timer_del(s->watchdog); 306a65f56eeSaurel32 return; 307a65f56eeSaurel32 } 308a65f56eeSaurel32 309bc72ad67SAlex 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; 3143df5de64SHervé Poussineau dp8393x_set_next_tick(s); 315a65f56eeSaurel32 316a65f56eeSaurel32 } 317a65f56eeSaurel32 3183df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s) 319a65f56eeSaurel32 { 320a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_STP; 3213df5de64SHervé Poussineau dp8393x_set_next_tick(s); 322a65f56eeSaurel32 } 323a65f56eeSaurel32 3243df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s) 325a65f56eeSaurel32 { 326a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_ST; 3273df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 328a65f56eeSaurel32 } 329a65f56eeSaurel32 3303df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s) 331a65f56eeSaurel32 { 332a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; 333a65f56eeSaurel32 } 334a65f56eeSaurel32 3353df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s) 336a65f56eeSaurel32 { 337a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; 338a65f56eeSaurel32 } 339a65f56eeSaurel32 3403df5de64SHervé Poussineau static void dp8393x_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]; 356dd820513SHervé Poussineau address_space_rw(&s->as, 357a65f56eeSaurel32 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width, 358dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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 } 382dd820513SHervé Poussineau address_space_rw(&s->as, 383a65f56eeSaurel32 (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0], 384dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, &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; 391dd820513SHervé Poussineau address_space_rw(&s->as, 392a65f56eeSaurel32 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width, 393dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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; 425dd820513SHervé Poussineau address_space_rw(&s->as, 426a65f56eeSaurel32 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA], 427dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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; 432dd820513SHervé Poussineau address_space_rw(&s->as, 433a65f56eeSaurel32 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width, 434dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (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 4493df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s) 450a65f56eeSaurel32 { 451a65f56eeSaurel32 /* Nothing to do */ 452a65f56eeSaurel32 } 453a65f56eeSaurel32 4543df5de64SHervé Poussineau static void dp8393x_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) 4643df5de64SHervé Poussineau dp8393x_do_halt_transmission(s); 465a65f56eeSaurel32 if (command & SONIC_CR_TXP) 4663df5de64SHervé Poussineau dp8393x_do_transmit_packets(s); 467a65f56eeSaurel32 if (command & SONIC_CR_RXDIS) 4683df5de64SHervé Poussineau dp8393x_do_receiver_disable(s); 469a65f56eeSaurel32 if (command & SONIC_CR_RXEN) 4703df5de64SHervé Poussineau dp8393x_do_receiver_enable(s); 471a65f56eeSaurel32 if (command & SONIC_CR_STP) 4723df5de64SHervé Poussineau dp8393x_do_stop_timer(s); 473a65f56eeSaurel32 if (command & SONIC_CR_ST) 4743df5de64SHervé Poussineau dp8393x_do_start_timer(s); 475a65f56eeSaurel32 if (command & SONIC_CR_RST) 4763df5de64SHervé Poussineau dp8393x_do_software_reset(s); 477a65f56eeSaurel32 if (command & SONIC_CR_RRRA) 4783df5de64SHervé Poussineau dp8393x_do_read_rra(s); 479a65f56eeSaurel32 if (command & SONIC_CR_LCAM) 4803df5de64SHervé Poussineau dp8393x_do_load_cam(s); 481a65f56eeSaurel32 } 482a65f56eeSaurel32 48384689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) 484a65f56eeSaurel32 { 48584689cbbSHervé Poussineau dp8393xState *s = opaque; 48684689cbbSHervé Poussineau int reg = addr >> s->it_shift; 487a65f56eeSaurel32 uint16_t val = 0; 488a65f56eeSaurel32 489a65f56eeSaurel32 switch (reg) { 490a65f56eeSaurel32 /* Update data before reading it */ 491a65f56eeSaurel32 case SONIC_WT0: 492a65f56eeSaurel32 case SONIC_WT1: 4933df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 494a65f56eeSaurel32 val = s->regs[reg]; 495a65f56eeSaurel32 break; 496a65f56eeSaurel32 /* Accept read to some registers only when in reset mode */ 497a65f56eeSaurel32 case SONIC_CAP2: 498a65f56eeSaurel32 case SONIC_CAP1: 499a65f56eeSaurel32 case SONIC_CAP0: 500a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 501a65f56eeSaurel32 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8; 502a65f56eeSaurel32 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)]; 503a65f56eeSaurel32 } 504a65f56eeSaurel32 break; 505a65f56eeSaurel32 /* All other registers have no special contrainst */ 506a65f56eeSaurel32 default: 507a65f56eeSaurel32 val = s->regs[reg]; 508a65f56eeSaurel32 } 509a65f56eeSaurel32 510a65f56eeSaurel32 DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); 511a65f56eeSaurel32 512a65f56eeSaurel32 return val; 513a65f56eeSaurel32 } 514a65f56eeSaurel32 51584689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, 51684689cbbSHervé Poussineau unsigned int size) 517a65f56eeSaurel32 { 51884689cbbSHervé Poussineau dp8393xState *s = opaque; 51984689cbbSHervé Poussineau int reg = addr >> s->it_shift; 52084689cbbSHervé Poussineau 52184689cbbSHervé Poussineau DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]); 522a65f56eeSaurel32 523a65f56eeSaurel32 switch (reg) { 524a65f56eeSaurel32 /* Command register */ 525a65f56eeSaurel32 case SONIC_CR: 5263df5de64SHervé Poussineau dp8393x_do_command(s, data); 527a65f56eeSaurel32 break; 528a65f56eeSaurel32 /* Prevent write to read-only registers */ 529a65f56eeSaurel32 case SONIC_CAP2: 530a65f56eeSaurel32 case SONIC_CAP1: 531a65f56eeSaurel32 case SONIC_CAP0: 532a65f56eeSaurel32 case SONIC_SR: 533a65f56eeSaurel32 case SONIC_MDT: 534a65f56eeSaurel32 DPRINTF("writing to reg %d invalid\n", reg); 535a65f56eeSaurel32 break; 536a65f56eeSaurel32 /* Accept write to some registers only when in reset mode */ 537a65f56eeSaurel32 case SONIC_DCR: 538a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 53984689cbbSHervé Poussineau s->regs[reg] = data & 0xbfff; 540a65f56eeSaurel32 } else { 541a65f56eeSaurel32 DPRINTF("writing to DCR invalid\n"); 542a65f56eeSaurel32 } 543a65f56eeSaurel32 break; 544a65f56eeSaurel32 case SONIC_DCR2: 545a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 54684689cbbSHervé Poussineau s->regs[reg] = data & 0xf017; 547a65f56eeSaurel32 } else { 548a65f56eeSaurel32 DPRINTF("writing to DCR2 invalid\n"); 549a65f56eeSaurel32 } 550a65f56eeSaurel32 break; 551a65f56eeSaurel32 /* 12 lower bytes are Read Only */ 552a65f56eeSaurel32 case SONIC_TCR: 55384689cbbSHervé Poussineau s->regs[reg] = data & 0xf000; 554a65f56eeSaurel32 break; 555a65f56eeSaurel32 /* 9 lower bytes are Read Only */ 556a65f56eeSaurel32 case SONIC_RCR: 55784689cbbSHervé Poussineau s->regs[reg] = data & 0xffe0; 558a65f56eeSaurel32 break; 559a65f56eeSaurel32 /* Ignore most significant bit */ 560a65f56eeSaurel32 case SONIC_IMR: 56184689cbbSHervé Poussineau s->regs[reg] = data & 0x7fff; 562a65f56eeSaurel32 dp8393x_update_irq(s); 563a65f56eeSaurel32 break; 564a65f56eeSaurel32 /* Clear bits by writing 1 to them */ 565a65f56eeSaurel32 case SONIC_ISR: 56684689cbbSHervé Poussineau data &= s->regs[reg]; 56784689cbbSHervé Poussineau s->regs[reg] &= ~data; 56884689cbbSHervé Poussineau if (data & SONIC_ISR_RBE) { 5693df5de64SHervé Poussineau dp8393x_do_read_rra(s); 570a65f56eeSaurel32 } 571a65f56eeSaurel32 dp8393x_update_irq(s); 572a65f56eeSaurel32 break; 573a65f56eeSaurel32 /* Ignore least significant bit */ 574a65f56eeSaurel32 case SONIC_RSA: 575a65f56eeSaurel32 case SONIC_REA: 576a65f56eeSaurel32 case SONIC_RRP: 577a65f56eeSaurel32 case SONIC_RWP: 57884689cbbSHervé Poussineau s->regs[reg] = data & 0xfffe; 579a65f56eeSaurel32 break; 580a65f56eeSaurel32 /* Invert written value for some registers */ 581a65f56eeSaurel32 case SONIC_CRCT: 582a65f56eeSaurel32 case SONIC_FAET: 583a65f56eeSaurel32 case SONIC_MPT: 58484689cbbSHervé Poussineau s->regs[reg] = data ^ 0xffff; 585a65f56eeSaurel32 break; 586a65f56eeSaurel32 /* All other registers have no special contrainst */ 587a65f56eeSaurel32 default: 58884689cbbSHervé Poussineau s->regs[reg] = data; 589a65f56eeSaurel32 } 590a65f56eeSaurel32 591a65f56eeSaurel32 if (reg == SONIC_WT0 || reg == SONIC_WT1) { 5923df5de64SHervé Poussineau dp8393x_set_next_tick(s); 593a65f56eeSaurel32 } 594a65f56eeSaurel32 } 595a65f56eeSaurel32 59684689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = { 59784689cbbSHervé Poussineau .read = dp8393x_read, 59884689cbbSHervé Poussineau .write = dp8393x_write, 59984689cbbSHervé Poussineau .impl.min_access_size = 2, 60084689cbbSHervé Poussineau .impl.max_access_size = 2, 60184689cbbSHervé Poussineau .endianness = DEVICE_NATIVE_ENDIAN, 60284689cbbSHervé Poussineau }; 60384689cbbSHervé Poussineau 604a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque) 605a65f56eeSaurel32 { 606a65f56eeSaurel32 dp8393xState *s = opaque; 607a65f56eeSaurel32 608a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 609a65f56eeSaurel32 return; 610a65f56eeSaurel32 } 611a65f56eeSaurel32 612a65f56eeSaurel32 s->regs[SONIC_WT1] = 0xffff; 613a65f56eeSaurel32 s->regs[SONIC_WT0] = 0xffff; 6143df5de64SHervé Poussineau dp8393x_set_next_tick(s); 615a65f56eeSaurel32 616a65f56eeSaurel32 /* Signal underflow */ 617a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TC; 618a65f56eeSaurel32 dp8393x_update_irq(s); 619a65f56eeSaurel32 } 620a65f56eeSaurel32 6213df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc) 622a65f56eeSaurel32 { 623cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 624a65f56eeSaurel32 625a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) 626a65f56eeSaurel32 return 0; 627a65f56eeSaurel32 if (s->regs[SONIC_ISR] & SONIC_ISR_RBE) 628a65f56eeSaurel32 return 0; 629a65f56eeSaurel32 return 1; 630a65f56eeSaurel32 } 631a65f56eeSaurel32 6323df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, 6333df5de64SHervé Poussineau int size) 634a65f56eeSaurel32 { 635a65f56eeSaurel32 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 636a65f56eeSaurel32 int i; 637a65f56eeSaurel32 638a65f56eeSaurel32 /* Check for runt packet (remember that checksum is not there) */ 639a65f56eeSaurel32 if (size < 64 - 4) { 640a65f56eeSaurel32 return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1; 641a65f56eeSaurel32 } 642a65f56eeSaurel32 643a65f56eeSaurel32 /* Check promiscuous mode */ 644a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { 645a65f56eeSaurel32 return 0; 646a65f56eeSaurel32 } 647a65f56eeSaurel32 648a65f56eeSaurel32 /* Check multicast packets */ 649a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { 650a65f56eeSaurel32 return SONIC_RCR_MC; 651a65f56eeSaurel32 } 652a65f56eeSaurel32 653a65f56eeSaurel32 /* Check broadcast */ 654a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) { 655a65f56eeSaurel32 return SONIC_RCR_BC; 656a65f56eeSaurel32 } 657a65f56eeSaurel32 658a65f56eeSaurel32 /* Check CAM */ 659a65f56eeSaurel32 for (i = 0; i < 16; i++) { 660a65f56eeSaurel32 if (s->regs[SONIC_CE] & (1 << i)) { 661a65f56eeSaurel32 /* Entry enabled */ 662a65f56eeSaurel32 if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) { 663a65f56eeSaurel32 return 0; 664a65f56eeSaurel32 } 665a65f56eeSaurel32 } 666a65f56eeSaurel32 } 667a65f56eeSaurel32 668a65f56eeSaurel32 return -1; 669a65f56eeSaurel32 } 670a65f56eeSaurel32 6713df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, 6723df5de64SHervé Poussineau size_t size) 673a65f56eeSaurel32 { 674cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 675a65f56eeSaurel32 uint16_t data[10]; 676a65f56eeSaurel32 int packet_type; 677a65f56eeSaurel32 uint32_t available, address; 678a65f56eeSaurel32 int width, rx_len = size; 679a65f56eeSaurel32 uint32_t checksum; 680a65f56eeSaurel32 681a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 682a65f56eeSaurel32 683a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | 684a65f56eeSaurel32 SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); 685a65f56eeSaurel32 6863df5de64SHervé Poussineau packet_type = dp8393x_receive_filter(s, buf, size); 687a65f56eeSaurel32 if (packet_type < 0) { 688a65f56eeSaurel32 DPRINTF("packet not for netcard\n"); 6894f1c942bSMark McLoughlin return -1; 690a65f56eeSaurel32 } 691a65f56eeSaurel32 692a65f56eeSaurel32 /* XXX: Check byte ordering */ 693a65f56eeSaurel32 694a65f56eeSaurel32 /* Check for EOL */ 695a65f56eeSaurel32 if (s->regs[SONIC_LLFA] & 0x1) { 696a65f56eeSaurel32 /* Are we still in resource exhaustion? */ 697a65f56eeSaurel32 size = sizeof(uint16_t) * 1 * width; 698a65f56eeSaurel32 address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width; 699dd820513SHervé Poussineau address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, 700dd820513SHervé Poussineau (uint8_t *)data, size, 0); 701a65f56eeSaurel32 if (data[0 * width] & 0x1) { 702a65f56eeSaurel32 /* Still EOL ; stop reception */ 7034f1c942bSMark McLoughlin return -1; 704a65f56eeSaurel32 } else { 705a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 706a65f56eeSaurel32 } 707a65f56eeSaurel32 } 708a65f56eeSaurel32 709a65f56eeSaurel32 /* Save current position */ 710a65f56eeSaurel32 s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; 711a65f56eeSaurel32 s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; 712a65f56eeSaurel32 713a65f56eeSaurel32 /* Calculate the ethernet checksum */ 714a65f56eeSaurel32 checksum = cpu_to_le32(crc32(0, buf, rx_len)); 715a65f56eeSaurel32 716a65f56eeSaurel32 /* Put packet into RBA */ 717a65f56eeSaurel32 DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]); 718a65f56eeSaurel32 address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; 719dd820513SHervé Poussineau address_space_rw(&s->as, address, 720dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); 721a65f56eeSaurel32 address += rx_len; 722dd820513SHervé Poussineau address_space_rw(&s->as, address, 723dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1); 724a65f56eeSaurel32 rx_len += 4; 725a65f56eeSaurel32 s->regs[SONIC_CRBA1] = address >> 16; 726a65f56eeSaurel32 s->regs[SONIC_CRBA0] = address & 0xffff; 727a65f56eeSaurel32 available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; 728a65f56eeSaurel32 available -= rx_len / 2; 729a65f56eeSaurel32 s->regs[SONIC_RBWC1] = available >> 16; 730a65f56eeSaurel32 s->regs[SONIC_RBWC0] = available & 0xffff; 731a65f56eeSaurel32 732a65f56eeSaurel32 /* Update status */ 733a65f56eeSaurel32 if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) { 734a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; 735a65f56eeSaurel32 } 736a65f56eeSaurel32 s->regs[SONIC_RCR] |= packet_type; 737a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_PRX; 738a65f56eeSaurel32 if (s->loopback_packet) { 739a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LBK; 740a65f56eeSaurel32 s->loopback_packet = 0; 741a65f56eeSaurel32 } 742a65f56eeSaurel32 743a65f56eeSaurel32 /* Write status to memory */ 744a65f56eeSaurel32 DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]); 745a65f56eeSaurel32 data[0 * width] = s->regs[SONIC_RCR]; /* status */ 746a65f56eeSaurel32 data[1 * width] = rx_len; /* byte count */ 747a65f56eeSaurel32 data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */ 748a65f56eeSaurel32 data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ 749a65f56eeSaurel32 data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ 750a65f56eeSaurel32 size = sizeof(uint16_t) * 5 * width; 751dd820513SHervé Poussineau address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], 752dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); 753a65f56eeSaurel32 754a65f56eeSaurel32 /* Move to next descriptor */ 755a65f56eeSaurel32 size = sizeof(uint16_t) * width; 756dd820513SHervé Poussineau address_space_rw(&s->as, 757a65f56eeSaurel32 ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width, 758dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); 759a65f56eeSaurel32 s->regs[SONIC_LLFA] = data[0 * width]; 760a65f56eeSaurel32 if (s->regs[SONIC_LLFA] & 0x1) { 761a65f56eeSaurel32 /* EOL detected */ 762a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RDE; 763a65f56eeSaurel32 } else { 764a65f56eeSaurel32 data[0 * width] = 0; /* in_use */ 765dd820513SHervé Poussineau address_space_rw(&s->as, 766a65f56eeSaurel32 ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width, 767dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); 768a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 769a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; 770a65f56eeSaurel32 s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); 771a65f56eeSaurel32 772a65f56eeSaurel32 if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { 773a65f56eeSaurel32 /* Read next RRA */ 7743df5de64SHervé Poussineau dp8393x_do_read_rra(s); 775a65f56eeSaurel32 } 776a65f56eeSaurel32 } 777a65f56eeSaurel32 778a65f56eeSaurel32 /* Done */ 779a65f56eeSaurel32 dp8393x_update_irq(s); 7804f1c942bSMark McLoughlin 7814f1c942bSMark McLoughlin return size; 782a65f56eeSaurel32 } 783a65f56eeSaurel32 784104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev) 785a65f56eeSaurel32 { 786104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 787bc72ad67SAlex Bligh timer_del(s->watchdog); 788a65f56eeSaurel32 789a65f56eeSaurel32 s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; 790a65f56eeSaurel32 s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); 791a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); 792a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; 793a65f56eeSaurel32 s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; 794a65f56eeSaurel32 s->regs[SONIC_IMR] = 0; 795a65f56eeSaurel32 s->regs[SONIC_ISR] = 0; 796a65f56eeSaurel32 s->regs[SONIC_DCR2] = 0; 797a65f56eeSaurel32 s->regs[SONIC_EOBC] = 0x02F8; 798a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 799a65f56eeSaurel32 s->regs[SONIC_CE] = 0; 800a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 801a65f56eeSaurel32 802a65f56eeSaurel32 /* Network cable is connected */ 803a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_CRS; 804a65f56eeSaurel32 805a65f56eeSaurel32 dp8393x_update_irq(s); 806a65f56eeSaurel32 } 807a65f56eeSaurel32 80805f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = { 8092be64a68SLaszlo Ersek .type = NET_CLIENT_OPTIONS_KIND_NIC, 81005f41fe3SMark McLoughlin .size = sizeof(NICState), 8113df5de64SHervé Poussineau .can_receive = dp8393x_can_receive, 8123df5de64SHervé Poussineau .receive = dp8393x_receive, 81305f41fe3SMark McLoughlin }; 81405f41fe3SMark McLoughlin 815104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj) 816a65f56eeSaurel32 { 817104655a5SHervé Poussineau SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 818104655a5SHervé Poussineau dp8393xState *s = DP8393X(obj); 819a65f56eeSaurel32 820104655a5SHervé Poussineau sysbus_init_mmio(sbd, &s->mmio); 821*89ae0ff9SHervé Poussineau sysbus_init_mmio(sbd, &s->prom); 822104655a5SHervé Poussineau sysbus_init_irq(sbd, &s->irq); 823104655a5SHervé Poussineau } 824a65f56eeSaurel32 825104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp) 826104655a5SHervé Poussineau { 827104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 828*89ae0ff9SHervé Poussineau int i, checksum; 829*89ae0ff9SHervé Poussineau uint8_t *prom; 830a65f56eeSaurel32 831104655a5SHervé Poussineau address_space_init(&s->as, s->dma_mr, "dp8393x"); 832104655a5SHervé Poussineau memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, 833104655a5SHervé Poussineau "dp8393x-regs", 0x40 << s->it_shift); 834104655a5SHervé Poussineau 835104655a5SHervé Poussineau s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, 836104655a5SHervé Poussineau object_get_typename(OBJECT(dev)), dev->id, s); 837104655a5SHervé Poussineau qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 838104655a5SHervé Poussineau 839bc72ad67SAlex Bligh s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); 840a65f56eeSaurel32 s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ 841*89ae0ff9SHervé Poussineau 842*89ae0ff9SHervé Poussineau memory_region_init_rom_device(&s->prom, OBJECT(dev), NULL, NULL, 843*89ae0ff9SHervé Poussineau "dp8393x-prom", SONIC_PROM_SIZE, NULL); 844*89ae0ff9SHervé Poussineau prom = memory_region_get_ram_ptr(&s->prom); 845*89ae0ff9SHervé Poussineau checksum = 0; 846*89ae0ff9SHervé Poussineau for (i = 0; i < 6; i++) { 847*89ae0ff9SHervé Poussineau prom[i] = s->conf.macaddr.a[i]; 848*89ae0ff9SHervé Poussineau checksum += prom[i]; 849*89ae0ff9SHervé Poussineau if (checksum > 0xff) { 850*89ae0ff9SHervé Poussineau checksum = (checksum + 1) & 0xff; 851*89ae0ff9SHervé Poussineau } 852*89ae0ff9SHervé Poussineau } 853*89ae0ff9SHervé Poussineau prom[7] = 0xff - checksum; 854a65f56eeSaurel32 } 855104655a5SHervé Poussineau 856104655a5SHervé Poussineau static Property dp8393x_properties[] = { 857104655a5SHervé Poussineau DEFINE_NIC_PROPERTIES(dp8393xState, conf), 858104655a5SHervé Poussineau DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr), 859104655a5SHervé Poussineau DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), 860104655a5SHervé Poussineau DEFINE_PROP_END_OF_LIST(), 861104655a5SHervé Poussineau }; 862104655a5SHervé Poussineau 863104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data) 864104655a5SHervé Poussineau { 865104655a5SHervé Poussineau DeviceClass *dc = DEVICE_CLASS(klass); 866104655a5SHervé Poussineau 867104655a5SHervé Poussineau set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 868104655a5SHervé Poussineau dc->realize = dp8393x_realize; 869104655a5SHervé Poussineau dc->reset = dp8393x_reset; 870104655a5SHervé Poussineau dc->props = dp8393x_properties; 871104655a5SHervé Poussineau } 872104655a5SHervé Poussineau 873104655a5SHervé Poussineau static const TypeInfo dp8393x_info = { 874104655a5SHervé Poussineau .name = TYPE_DP8393X, 875104655a5SHervé Poussineau .parent = TYPE_SYS_BUS_DEVICE, 876104655a5SHervé Poussineau .instance_size = sizeof(dp8393xState), 877104655a5SHervé Poussineau .instance_init = dp8393x_instance_init, 878104655a5SHervé Poussineau .class_init = dp8393x_class_init, 879104655a5SHervé Poussineau }; 880104655a5SHervé Poussineau 881104655a5SHervé Poussineau static void dp8393x_register_types(void) 882104655a5SHervé Poussineau { 883104655a5SHervé Poussineau type_register_static(&dp8393x_info); 884104655a5SHervé Poussineau } 885104655a5SHervé Poussineau 886104655a5SHervé Poussineau type_init(dp8393x_register_types) 887