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 20e8d40465SPeter Maydell #include "qemu/osdep.h" 2164552b6bSMarkus Armbruster #include "hw/irq.h" 22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 23104655a5SHervé Poussineau #include "hw/sysbus.h" 24d6454270SMarkus Armbruster #include "migration/vmstate.h" 251422e32dSPaolo Bonzini #include "net/net.h" 26da34e65cSMarkus Armbruster #include "qapi/error.h" 270b8fa32fSMarkus Armbruster #include "qemu/module.h" 28104655a5SHervé Poussineau #include "qemu/timer.h" 29f2f62c4dSHervé Poussineau #include <zlib.h> 30a65f56eeSaurel32 31a65f56eeSaurel32 //#define DEBUG_SONIC 32a65f56eeSaurel32 3389ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000 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 14888f632fbSFinn Thain #define SONIC_DESC_EOL 0x0001 14988f632fbSFinn Thain #define SONIC_DESC_ADDR 0xFFFE 15088f632fbSFinn Thain 151104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x" 152104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) 153104655a5SHervé Poussineau 154a65f56eeSaurel32 typedef struct dp8393xState { 155104655a5SHervé Poussineau SysBusDevice parent_obj; 156104655a5SHervé Poussineau 157a65f56eeSaurel32 /* Hardware */ 158104655a5SHervé Poussineau uint8_t it_shift; 159be920841SLaurent Vivier bool big_endian; 160a65f56eeSaurel32 qemu_irq irq; 161a65f56eeSaurel32 #ifdef DEBUG_SONIC 162a65f56eeSaurel32 int irq_level; 163a65f56eeSaurel32 #endif 164a65f56eeSaurel32 QEMUTimer *watchdog; 165a65f56eeSaurel32 int64_t wt_last_update; 16605f41fe3SMark McLoughlin NICConf conf; 16705f41fe3SMark McLoughlin NICState *nic; 168024e5bb6SAvi Kivity MemoryRegion mmio; 16989ae0ff9SHervé Poussineau MemoryRegion prom; 170a65f56eeSaurel32 171a65f56eeSaurel32 /* Registers */ 172a65f56eeSaurel32 uint8_t cam[16][6]; 173a65f56eeSaurel32 uint16_t regs[0x40]; 174a65f56eeSaurel32 175a65f56eeSaurel32 /* Temporaries */ 176a65f56eeSaurel32 uint8_t tx_buffer[0x10000]; 177af9f0be3SLaurent Vivier uint16_t data[12]; 178a65f56eeSaurel32 int loopback_packet; 179a65f56eeSaurel32 180a65f56eeSaurel32 /* Memory access */ 1813110ce81SMarc-André Lureau MemoryRegion *dma_mr; 182dd820513SHervé Poussineau AddressSpace as; 183a65f56eeSaurel32 } dp8393xState; 184a65f56eeSaurel32 185581f7b12SPeter Maydell /* Accessor functions for values which are formed by 186581f7b12SPeter Maydell * concatenating two 16 bit device registers. By putting these 187581f7b12SPeter Maydell * in their own functions with a uint32_t return type we avoid the 188581f7b12SPeter Maydell * pitfall of implicit sign extension where ((x << 16) | y) is a 189581f7b12SPeter Maydell * signed 32 bit integer that might get sign-extended to a 64 bit integer. 190581f7b12SPeter Maydell */ 191581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s) 192581f7b12SPeter Maydell { 193581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP]; 194581f7b12SPeter Maydell } 195581f7b12SPeter Maydell 196581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s) 197581f7b12SPeter Maydell { 198581f7b12SPeter Maydell return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; 199581f7b12SPeter Maydell } 200581f7b12SPeter Maydell 201581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s) 202581f7b12SPeter Maydell { 20388f632fbSFinn Thain return (s->regs[SONIC_URDA] << 16) | 20488f632fbSFinn Thain (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR); 205581f7b12SPeter Maydell } 206581f7b12SPeter Maydell 207581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s) 208581f7b12SPeter Maydell { 209581f7b12SPeter Maydell return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; 210581f7b12SPeter Maydell } 211581f7b12SPeter Maydell 212581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s) 213581f7b12SPeter Maydell { 214581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP]; 215581f7b12SPeter Maydell } 216581f7b12SPeter Maydell 217581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s) 218581f7b12SPeter Maydell { 219581f7b12SPeter Maydell return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0]; 220581f7b12SPeter Maydell } 221581f7b12SPeter Maydell 222581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s) 223581f7b12SPeter Maydell { 22488f632fbSFinn Thain return (s->regs[SONIC_UTDA] << 16) | 22588f632fbSFinn Thain (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR); 226581f7b12SPeter Maydell } 227581f7b12SPeter Maydell 228581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s) 229581f7b12SPeter Maydell { 230581f7b12SPeter Maydell return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 231581f7b12SPeter Maydell } 232581f7b12SPeter Maydell 233af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset) 234be920841SLaurent Vivier { 235be920841SLaurent Vivier uint16_t val; 236be920841SLaurent Vivier 237be920841SLaurent Vivier if (s->big_endian) { 238af9f0be3SLaurent Vivier val = be16_to_cpu(s->data[offset * width + width - 1]); 239be920841SLaurent Vivier } else { 240af9f0be3SLaurent Vivier val = le16_to_cpu(s->data[offset * width]); 241be920841SLaurent Vivier } 242be920841SLaurent Vivier return val; 243be920841SLaurent Vivier } 244be920841SLaurent Vivier 245af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset, 246be920841SLaurent Vivier uint16_t val) 247be920841SLaurent Vivier { 248be920841SLaurent Vivier if (s->big_endian) { 2493fe9a838SFinn Thain if (width == 2) { 2503fe9a838SFinn Thain s->data[offset * 2] = 0; 2513fe9a838SFinn Thain s->data[offset * 2 + 1] = cpu_to_be16(val); 252be920841SLaurent Vivier } else { 2533fe9a838SFinn Thain s->data[offset] = cpu_to_be16(val); 2543fe9a838SFinn Thain } 2553fe9a838SFinn Thain } else { 2563fe9a838SFinn Thain if (width == 2) { 2573fe9a838SFinn Thain s->data[offset * 2] = cpu_to_le16(val); 2583fe9a838SFinn Thain s->data[offset * 2 + 1] = 0; 2593fe9a838SFinn Thain } else { 2603fe9a838SFinn Thain s->data[offset] = cpu_to_le16(val); 2613fe9a838SFinn Thain } 262be920841SLaurent Vivier } 263be920841SLaurent Vivier } 264be920841SLaurent Vivier 265a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s) 266a65f56eeSaurel32 { 267a65f56eeSaurel32 int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; 268a65f56eeSaurel32 269a65f56eeSaurel32 #ifdef DEBUG_SONIC 270a65f56eeSaurel32 if (level != s->irq_level) { 271a65f56eeSaurel32 s->irq_level = level; 272a65f56eeSaurel32 if (level) { 273a65f56eeSaurel32 DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]); 274a65f56eeSaurel32 } else { 275a65f56eeSaurel32 DPRINTF("lower irq\n"); 276a65f56eeSaurel32 } 277a65f56eeSaurel32 } 278a65f56eeSaurel32 #endif 279a65f56eeSaurel32 280a65f56eeSaurel32 qemu_set_irq(s->irq, level); 281a65f56eeSaurel32 } 282a65f56eeSaurel32 2833df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s) 284a65f56eeSaurel32 { 285a65f56eeSaurel32 int width, size; 286a65f56eeSaurel32 uint16_t index = 0; 287a65f56eeSaurel32 288a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 289a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 290a65f56eeSaurel32 291a65f56eeSaurel32 while (s->regs[SONIC_CDC] & 0x1f) { 292a65f56eeSaurel32 /* Fill current entry */ 29319f70347SPeter Maydell address_space_read(&s->as, dp8393x_cdp(s), 29419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 295af9f0be3SLaurent Vivier s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; 296af9f0be3SLaurent Vivier s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; 297af9f0be3SLaurent Vivier s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; 298af9f0be3SLaurent Vivier s->cam[index][3] = dp8393x_get(s, width, 2) >> 8; 299af9f0be3SLaurent Vivier s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff; 300af9f0be3SLaurent Vivier s->cam[index][5] = dp8393x_get(s, width, 3) >> 8; 301a65f56eeSaurel32 DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index, 302a65f56eeSaurel32 s->cam[index][0], s->cam[index][1], s->cam[index][2], 303a65f56eeSaurel32 s->cam[index][3], s->cam[index][4], s->cam[index][5]); 304a65f56eeSaurel32 /* Move to next entry */ 305a65f56eeSaurel32 s->regs[SONIC_CDC]--; 306a65f56eeSaurel32 s->regs[SONIC_CDP] += size; 307a65f56eeSaurel32 index++; 308a65f56eeSaurel32 } 309a65f56eeSaurel32 310a65f56eeSaurel32 /* Read CAM enable */ 31119f70347SPeter Maydell address_space_read(&s->as, dp8393x_cdp(s), 31219f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 313af9f0be3SLaurent Vivier s->regs[SONIC_CE] = dp8393x_get(s, width, 0); 314a65f56eeSaurel32 DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); 315a65f56eeSaurel32 316a65f56eeSaurel32 /* Done */ 317a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; 318a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_LCD; 319a65f56eeSaurel32 dp8393x_update_irq(s); 320a65f56eeSaurel32 } 321a65f56eeSaurel32 3223df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s) 323a65f56eeSaurel32 { 324a65f56eeSaurel32 int width, size; 325a65f56eeSaurel32 326a65f56eeSaurel32 /* Read memory */ 327a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 328a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 32919f70347SPeter Maydell address_space_read(&s->as, dp8393x_rrp(s), 33019f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 331a65f56eeSaurel32 332a65f56eeSaurel32 /* Update SONIC registers */ 333af9f0be3SLaurent Vivier s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0); 334af9f0be3SLaurent Vivier s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1); 335af9f0be3SLaurent Vivier s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2); 336af9f0be3SLaurent Vivier s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3); 337a65f56eeSaurel32 DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n", 338a65f56eeSaurel32 s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], 339a65f56eeSaurel32 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); 340a65f56eeSaurel32 341a65f56eeSaurel32 /* Go to next entry */ 342a65f56eeSaurel32 s->regs[SONIC_RRP] += size; 343a65f56eeSaurel32 344a65f56eeSaurel32 /* Handle wrap */ 345a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { 346a65f56eeSaurel32 s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; 347a65f56eeSaurel32 } 348a65f56eeSaurel32 349a65f56eeSaurel32 /* Check resource exhaustion */ 350a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) 351a65f56eeSaurel32 { 352a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RBE; 353a65f56eeSaurel32 dp8393x_update_irq(s); 354a65f56eeSaurel32 } 355a65f56eeSaurel32 356a65f56eeSaurel32 /* Done */ 357a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; 358a65f56eeSaurel32 } 359a65f56eeSaurel32 3603df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s) 361a65f56eeSaurel32 { 362bc72ad67SAlex Bligh timer_del(s->watchdog); 363a65f56eeSaurel32 364a65f56eeSaurel32 s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX); 365a65f56eeSaurel32 s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; 366a65f56eeSaurel32 } 367a65f56eeSaurel32 3683df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s) 369a65f56eeSaurel32 { 370a65f56eeSaurel32 uint32_t ticks; 371a65f56eeSaurel32 int64_t delay; 372a65f56eeSaurel32 373a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 374bc72ad67SAlex Bligh timer_del(s->watchdog); 375a65f56eeSaurel32 return; 376a65f56eeSaurel32 } 377a65f56eeSaurel32 378581f7b12SPeter Maydell ticks = dp8393x_wt(s); 379bc72ad67SAlex Bligh s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 38073bcb24dSRutuja Shah delay = NANOSECONDS_PER_SECOND * ticks / 5000000; 381bc72ad67SAlex Bligh timer_mod(s->watchdog, s->wt_last_update + delay); 382a65f56eeSaurel32 } 383a65f56eeSaurel32 3843df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s) 385a65f56eeSaurel32 { 386a65f56eeSaurel32 int64_t elapsed; 387a65f56eeSaurel32 uint32_t val; 388a65f56eeSaurel32 389a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 390bc72ad67SAlex Bligh timer_del(s->watchdog); 391a65f56eeSaurel32 return; 392a65f56eeSaurel32 } 393a65f56eeSaurel32 394bc72ad67SAlex Bligh elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 395581f7b12SPeter Maydell val = dp8393x_wt(s); 396a65f56eeSaurel32 val -= elapsed / 5000000; 397a65f56eeSaurel32 s->regs[SONIC_WT1] = (val >> 16) & 0xffff; 398a65f56eeSaurel32 s->regs[SONIC_WT0] = (val >> 0) & 0xffff; 3993df5de64SHervé Poussineau dp8393x_set_next_tick(s); 400a65f56eeSaurel32 401a65f56eeSaurel32 } 402a65f56eeSaurel32 4033df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s) 404a65f56eeSaurel32 { 405a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_STP; 4063df5de64SHervé Poussineau dp8393x_set_next_tick(s); 407a65f56eeSaurel32 } 408a65f56eeSaurel32 4093df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s) 410a65f56eeSaurel32 { 411a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_ST; 4123df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 413a65f56eeSaurel32 } 414a65f56eeSaurel32 4154594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc); 4164594f93aSFam Zheng 4173df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s) 418a65f56eeSaurel32 { 419a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; 4204594f93aSFam Zheng if (dp8393x_can_receive(s->nic->ncs)) { 4214594f93aSFam Zheng qemu_flush_queued_packets(qemu_get_queue(s->nic)); 4224594f93aSFam Zheng } 423a65f56eeSaurel32 } 424a65f56eeSaurel32 4253df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s) 426a65f56eeSaurel32 { 427a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; 428a65f56eeSaurel32 } 429a65f56eeSaurel32 4303df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s) 431a65f56eeSaurel32 { 432b356f76dSJason Wang NetClientState *nc = qemu_get_queue(s->nic); 433a65f56eeSaurel32 int width, size; 434a65f56eeSaurel32 int tx_len, len; 435a65f56eeSaurel32 uint16_t i; 436a65f56eeSaurel32 437a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 438a65f56eeSaurel32 439a65f56eeSaurel32 while (1) { 440a65f56eeSaurel32 /* Read memory */ 441a65f56eeSaurel32 size = sizeof(uint16_t) * 6 * width; 442a65f56eeSaurel32 s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; 443581f7b12SPeter Maydell DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s)); 44419f70347SPeter Maydell address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, 44519f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 446a65f56eeSaurel32 tx_len = 0; 447a65f56eeSaurel32 448a65f56eeSaurel32 /* Update registers */ 449af9f0be3SLaurent Vivier s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000; 450af9f0be3SLaurent Vivier s->regs[SONIC_TPS] = dp8393x_get(s, width, 1); 451af9f0be3SLaurent Vivier s->regs[SONIC_TFC] = dp8393x_get(s, width, 2); 452af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3); 453af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4); 454af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 5); 455a65f56eeSaurel32 456a65f56eeSaurel32 /* Handle programmable interrupt */ 457a65f56eeSaurel32 if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { 458a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PINT; 459a65f56eeSaurel32 } else { 460a65f56eeSaurel32 s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; 461a65f56eeSaurel32 } 462a65f56eeSaurel32 463a65f56eeSaurel32 for (i = 0; i < s->regs[SONIC_TFC]; ) { 464a65f56eeSaurel32 /* Append fragment */ 465a65f56eeSaurel32 len = s->regs[SONIC_TFS]; 466a65f56eeSaurel32 if (tx_len + len > sizeof(s->tx_buffer)) { 467a65f56eeSaurel32 len = sizeof(s->tx_buffer) - tx_len; 468a65f56eeSaurel32 } 46919f70347SPeter Maydell address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED, 47019f70347SPeter Maydell &s->tx_buffer[tx_len], len); 471a65f56eeSaurel32 tx_len += len; 472a65f56eeSaurel32 473a65f56eeSaurel32 i++; 474a65f56eeSaurel32 if (i != s->regs[SONIC_TFC]) { 475a65f56eeSaurel32 /* Read next fragment details */ 476a65f56eeSaurel32 size = sizeof(uint16_t) * 3 * width; 47719f70347SPeter Maydell address_space_read(&s->as, 47819f70347SPeter Maydell dp8393x_ttda(s) 47919f70347SPeter Maydell + sizeof(uint16_t) * width * (4 + 3 * i), 48019f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, 48119f70347SPeter Maydell size); 482af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0); 483af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1); 484af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 2); 485a65f56eeSaurel32 } 486a65f56eeSaurel32 } 487a65f56eeSaurel32 488a65f56eeSaurel32 /* Handle Ethernet checksum */ 489a65f56eeSaurel32 if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { 490a65f56eeSaurel32 /* Don't append FCS there, to look like slirp packets 491a65f56eeSaurel32 * which don't have one */ 492a65f56eeSaurel32 } else { 493a65f56eeSaurel32 /* Remove existing FCS */ 494a65f56eeSaurel32 tx_len -= 4; 495a65f56eeSaurel32 } 496a65f56eeSaurel32 497a65f56eeSaurel32 if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { 498a65f56eeSaurel32 /* Loopback */ 499a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; 500b356f76dSJason Wang if (nc->info->can_receive(nc)) { 501a65f56eeSaurel32 s->loopback_packet = 1; 502b356f76dSJason Wang nc->info->receive(nc, s->tx_buffer, tx_len); 503a65f56eeSaurel32 } 504a65f56eeSaurel32 } else { 505a65f56eeSaurel32 /* Transmit packet */ 506b356f76dSJason Wang qemu_send_packet(nc, s->tx_buffer, tx_len); 507a65f56eeSaurel32 } 508a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_PTX; 509a65f56eeSaurel32 510a65f56eeSaurel32 /* Write status */ 511af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, 512be920841SLaurent Vivier s->regs[SONIC_TCR] & 0x0fff); /* status */ 513a65f56eeSaurel32 size = sizeof(uint16_t) * width; 51419f70347SPeter Maydell address_space_write(&s->as, dp8393x_ttda(s), 51519f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 516a65f56eeSaurel32 517a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { 518a65f56eeSaurel32 /* Read footer of packet */ 519a65f56eeSaurel32 size = sizeof(uint16_t) * width; 52019f70347SPeter Maydell address_space_read(&s->as, 52119f70347SPeter Maydell dp8393x_ttda(s) 52219f70347SPeter Maydell + sizeof(uint16_t) * width 52319f70347SPeter Maydell * (4 + 3 * s->regs[SONIC_TFC]), 52419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, 52519f70347SPeter Maydell size); 526af9f0be3SLaurent Vivier s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0) & ~0x1; 52788f632fbSFinn Thain if (dp8393x_get(s, width, 0) & SONIC_DESC_EOL) { 528a65f56eeSaurel32 /* EOL detected */ 529a65f56eeSaurel32 break; 530a65f56eeSaurel32 } 531a65f56eeSaurel32 } 532a65f56eeSaurel32 } 533a65f56eeSaurel32 534a65f56eeSaurel32 /* Done */ 535a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_TXP; 536a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; 537a65f56eeSaurel32 dp8393x_update_irq(s); 538a65f56eeSaurel32 } 539a65f56eeSaurel32 5403df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s) 541a65f56eeSaurel32 { 542a65f56eeSaurel32 /* Nothing to do */ 543a65f56eeSaurel32 } 544a65f56eeSaurel32 5453df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command) 546a65f56eeSaurel32 { 547a65f56eeSaurel32 if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { 548a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RST; 549a65f56eeSaurel32 return; 550a65f56eeSaurel32 } 551a65f56eeSaurel32 552a65f56eeSaurel32 s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); 553a65f56eeSaurel32 554a65f56eeSaurel32 if (command & SONIC_CR_HTX) 5553df5de64SHervé Poussineau dp8393x_do_halt_transmission(s); 556a65f56eeSaurel32 if (command & SONIC_CR_TXP) 5573df5de64SHervé Poussineau dp8393x_do_transmit_packets(s); 558a65f56eeSaurel32 if (command & SONIC_CR_RXDIS) 5593df5de64SHervé Poussineau dp8393x_do_receiver_disable(s); 560a65f56eeSaurel32 if (command & SONIC_CR_RXEN) 5613df5de64SHervé Poussineau dp8393x_do_receiver_enable(s); 562a65f56eeSaurel32 if (command & SONIC_CR_STP) 5633df5de64SHervé Poussineau dp8393x_do_stop_timer(s); 564a65f56eeSaurel32 if (command & SONIC_CR_ST) 5653df5de64SHervé Poussineau dp8393x_do_start_timer(s); 566a65f56eeSaurel32 if (command & SONIC_CR_RST) 5673df5de64SHervé Poussineau dp8393x_do_software_reset(s); 568a65f56eeSaurel32 if (command & SONIC_CR_RRRA) 5693df5de64SHervé Poussineau dp8393x_do_read_rra(s); 570a65f56eeSaurel32 if (command & SONIC_CR_LCAM) 5713df5de64SHervé Poussineau dp8393x_do_load_cam(s); 572a65f56eeSaurel32 } 573a65f56eeSaurel32 57484689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) 575a65f56eeSaurel32 { 57684689cbbSHervé Poussineau dp8393xState *s = opaque; 57784689cbbSHervé Poussineau int reg = addr >> s->it_shift; 578a65f56eeSaurel32 uint16_t val = 0; 579a65f56eeSaurel32 580a65f56eeSaurel32 switch (reg) { 581a65f56eeSaurel32 /* Update data before reading it */ 582a65f56eeSaurel32 case SONIC_WT0: 583a65f56eeSaurel32 case SONIC_WT1: 5843df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 585a65f56eeSaurel32 val = s->regs[reg]; 586a65f56eeSaurel32 break; 587a65f56eeSaurel32 /* Accept read to some registers only when in reset mode */ 588a65f56eeSaurel32 case SONIC_CAP2: 589a65f56eeSaurel32 case SONIC_CAP1: 590a65f56eeSaurel32 case SONIC_CAP0: 591a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 592a65f56eeSaurel32 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8; 593a65f56eeSaurel32 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)]; 594a65f56eeSaurel32 } 595a65f56eeSaurel32 break; 596a65f56eeSaurel32 /* All other registers have no special contrainst */ 597a65f56eeSaurel32 default: 598a65f56eeSaurel32 val = s->regs[reg]; 599a65f56eeSaurel32 } 600a65f56eeSaurel32 601a65f56eeSaurel32 DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); 602a65f56eeSaurel32 6033fe9a838SFinn Thain return s->big_endian ? val << 16 : val; 604a65f56eeSaurel32 } 605a65f56eeSaurel32 60684689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, 60784689cbbSHervé Poussineau unsigned int size) 608a65f56eeSaurel32 { 60984689cbbSHervé Poussineau dp8393xState *s = opaque; 61084689cbbSHervé Poussineau int reg = addr >> s->it_shift; 6113fe9a838SFinn Thain uint32_t val = s->big_endian ? data >> 16 : data; 61284689cbbSHervé Poussineau 6133fe9a838SFinn Thain DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]); 614a65f56eeSaurel32 615a65f56eeSaurel32 switch (reg) { 616a65f56eeSaurel32 /* Command register */ 617a65f56eeSaurel32 case SONIC_CR: 6183fe9a838SFinn Thain dp8393x_do_command(s, val); 619a65f56eeSaurel32 break; 620a65f56eeSaurel32 /* Prevent write to read-only registers */ 621a65f56eeSaurel32 case SONIC_CAP2: 622a65f56eeSaurel32 case SONIC_CAP1: 623a65f56eeSaurel32 case SONIC_CAP0: 624a65f56eeSaurel32 case SONIC_SR: 625a65f56eeSaurel32 case SONIC_MDT: 626a65f56eeSaurel32 DPRINTF("writing to reg %d invalid\n", reg); 627a65f56eeSaurel32 break; 628a65f56eeSaurel32 /* Accept write to some registers only when in reset mode */ 629a65f56eeSaurel32 case SONIC_DCR: 630a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 6313fe9a838SFinn Thain s->regs[reg] = val & 0xbfff; 632a65f56eeSaurel32 } else { 633a65f56eeSaurel32 DPRINTF("writing to DCR invalid\n"); 634a65f56eeSaurel32 } 635a65f56eeSaurel32 break; 636a65f56eeSaurel32 case SONIC_DCR2: 637a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 6383fe9a838SFinn Thain s->regs[reg] = val & 0xf017; 639a65f56eeSaurel32 } else { 640a65f56eeSaurel32 DPRINTF("writing to DCR2 invalid\n"); 641a65f56eeSaurel32 } 642a65f56eeSaurel32 break; 643a65f56eeSaurel32 /* 12 lower bytes are Read Only */ 644a65f56eeSaurel32 case SONIC_TCR: 6453fe9a838SFinn Thain s->regs[reg] = val & 0xf000; 646a65f56eeSaurel32 break; 647a65f56eeSaurel32 /* 9 lower bytes are Read Only */ 648a65f56eeSaurel32 case SONIC_RCR: 6493fe9a838SFinn Thain s->regs[reg] = val & 0xffe0; 650a65f56eeSaurel32 break; 651a65f56eeSaurel32 /* Ignore most significant bit */ 652a65f56eeSaurel32 case SONIC_IMR: 6533fe9a838SFinn Thain s->regs[reg] = val & 0x7fff; 654a65f56eeSaurel32 dp8393x_update_irq(s); 655a65f56eeSaurel32 break; 656a65f56eeSaurel32 /* Clear bits by writing 1 to them */ 657a65f56eeSaurel32 case SONIC_ISR: 6583fe9a838SFinn Thain val &= s->regs[reg]; 6593fe9a838SFinn Thain s->regs[reg] &= ~val; 6603fe9a838SFinn Thain if (val & SONIC_ISR_RBE) { 6613df5de64SHervé Poussineau dp8393x_do_read_rra(s); 662a65f56eeSaurel32 } 663a65f56eeSaurel32 dp8393x_update_irq(s); 6644594f93aSFam Zheng if (dp8393x_can_receive(s->nic->ncs)) { 6654594f93aSFam Zheng qemu_flush_queued_packets(qemu_get_queue(s->nic)); 6664594f93aSFam Zheng } 667a65f56eeSaurel32 break; 668a65f56eeSaurel32 /* Ignore least significant bit */ 669a65f56eeSaurel32 case SONIC_RSA: 670a65f56eeSaurel32 case SONIC_REA: 671a65f56eeSaurel32 case SONIC_RRP: 672a65f56eeSaurel32 case SONIC_RWP: 6733fe9a838SFinn Thain s->regs[reg] = val & 0xfffe; 674a65f56eeSaurel32 break; 675a65f56eeSaurel32 /* Invert written value for some registers */ 676a65f56eeSaurel32 case SONIC_CRCT: 677a65f56eeSaurel32 case SONIC_FAET: 678a65f56eeSaurel32 case SONIC_MPT: 6793fe9a838SFinn Thain s->regs[reg] = val ^ 0xffff; 680a65f56eeSaurel32 break; 681a65f56eeSaurel32 /* All other registers have no special contrainst */ 682a65f56eeSaurel32 default: 6833fe9a838SFinn Thain s->regs[reg] = val; 684a65f56eeSaurel32 } 685a65f56eeSaurel32 686a65f56eeSaurel32 if (reg == SONIC_WT0 || reg == SONIC_WT1) { 6873df5de64SHervé Poussineau dp8393x_set_next_tick(s); 688a65f56eeSaurel32 } 689a65f56eeSaurel32 } 690a65f56eeSaurel32 69184689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = { 69284689cbbSHervé Poussineau .read = dp8393x_read, 69384689cbbSHervé Poussineau .write = dp8393x_write, 6943fe9a838SFinn Thain .impl.min_access_size = 4, 6953fe9a838SFinn Thain .impl.max_access_size = 4, 69684689cbbSHervé Poussineau .endianness = DEVICE_NATIVE_ENDIAN, 69784689cbbSHervé Poussineau }; 69884689cbbSHervé Poussineau 699a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque) 700a65f56eeSaurel32 { 701a65f56eeSaurel32 dp8393xState *s = opaque; 702a65f56eeSaurel32 703a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 704a65f56eeSaurel32 return; 705a65f56eeSaurel32 } 706a65f56eeSaurel32 707a65f56eeSaurel32 s->regs[SONIC_WT1] = 0xffff; 708a65f56eeSaurel32 s->regs[SONIC_WT0] = 0xffff; 7093df5de64SHervé Poussineau dp8393x_set_next_tick(s); 710a65f56eeSaurel32 711a65f56eeSaurel32 /* Signal underflow */ 712a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TC; 713a65f56eeSaurel32 dp8393x_update_irq(s); 714a65f56eeSaurel32 } 715a65f56eeSaurel32 7163df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc) 717a65f56eeSaurel32 { 718cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 719a65f56eeSaurel32 720a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) 721a65f56eeSaurel32 return 0; 722a65f56eeSaurel32 if (s->regs[SONIC_ISR] & SONIC_ISR_RBE) 723a65f56eeSaurel32 return 0; 724a65f56eeSaurel32 return 1; 725a65f56eeSaurel32 } 726a65f56eeSaurel32 7273df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, 7283df5de64SHervé Poussineau int size) 729a65f56eeSaurel32 { 730a65f56eeSaurel32 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 731a65f56eeSaurel32 int i; 732a65f56eeSaurel32 733a65f56eeSaurel32 /* Check promiscuous mode */ 734a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { 735a65f56eeSaurel32 return 0; 736a65f56eeSaurel32 } 737a65f56eeSaurel32 738a65f56eeSaurel32 /* Check multicast packets */ 739a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { 740a65f56eeSaurel32 return SONIC_RCR_MC; 741a65f56eeSaurel32 } 742a65f56eeSaurel32 743a65f56eeSaurel32 /* Check broadcast */ 744a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) { 745a65f56eeSaurel32 return SONIC_RCR_BC; 746a65f56eeSaurel32 } 747a65f56eeSaurel32 748a65f56eeSaurel32 /* Check CAM */ 749a65f56eeSaurel32 for (i = 0; i < 16; i++) { 750a65f56eeSaurel32 if (s->regs[SONIC_CE] & (1 << i)) { 751a65f56eeSaurel32 /* Entry enabled */ 752a65f56eeSaurel32 if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) { 753a65f56eeSaurel32 return 0; 754a65f56eeSaurel32 } 755a65f56eeSaurel32 } 756a65f56eeSaurel32 } 757a65f56eeSaurel32 758a65f56eeSaurel32 return -1; 759a65f56eeSaurel32 } 760a65f56eeSaurel32 7613df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, 762*9e3cd456SFinn Thain size_t pkt_size) 763a65f56eeSaurel32 { 764cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 765a65f56eeSaurel32 int packet_type; 766a65f56eeSaurel32 uint32_t available, address; 767*9e3cd456SFinn Thain int width, rx_len = pkt_size; 768a65f56eeSaurel32 uint32_t checksum; 769*9e3cd456SFinn Thain int size; 770a65f56eeSaurel32 771a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 772a65f56eeSaurel32 773a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | 774a65f56eeSaurel32 SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); 775a65f56eeSaurel32 776*9e3cd456SFinn Thain packet_type = dp8393x_receive_filter(s, buf, pkt_size); 777a65f56eeSaurel32 if (packet_type < 0) { 778a65f56eeSaurel32 DPRINTF("packet not for netcard\n"); 7794f1c942bSMark McLoughlin return -1; 780a65f56eeSaurel32 } 781a65f56eeSaurel32 782a65f56eeSaurel32 /* Check for EOL */ 78388f632fbSFinn Thain if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { 784a65f56eeSaurel32 /* Are we still in resource exhaustion? */ 785a65f56eeSaurel32 size = sizeof(uint16_t) * 1 * width; 786581f7b12SPeter Maydell address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; 78719f70347SPeter Maydell address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED, 78819f70347SPeter Maydell s->data, size); 78988f632fbSFinn Thain if (dp8393x_get(s, width, 0) & SONIC_DESC_EOL) { 790a65f56eeSaurel32 /* Still EOL ; stop reception */ 7914f1c942bSMark McLoughlin return -1; 792a65f56eeSaurel32 } else { 793a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 794a65f56eeSaurel32 } 795a65f56eeSaurel32 } 796a65f56eeSaurel32 797a65f56eeSaurel32 /* Save current position */ 798a65f56eeSaurel32 s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; 799a65f56eeSaurel32 s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; 800a65f56eeSaurel32 801a65f56eeSaurel32 /* Calculate the ethernet checksum */ 802a65f56eeSaurel32 checksum = cpu_to_le32(crc32(0, buf, rx_len)); 803a65f56eeSaurel32 804a65f56eeSaurel32 /* Put packet into RBA */ 805581f7b12SPeter Maydell DPRINTF("Receive packet at %08x\n", dp8393x_crba(s)); 806581f7b12SPeter Maydell address = dp8393x_crba(s); 80719f70347SPeter Maydell address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, 80819f70347SPeter Maydell buf, rx_len); 809a65f56eeSaurel32 address += rx_len; 81019f70347SPeter Maydell address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, 81119f70347SPeter Maydell &checksum, 4); 812a65f56eeSaurel32 rx_len += 4; 813a65f56eeSaurel32 s->regs[SONIC_CRBA1] = address >> 16; 814a65f56eeSaurel32 s->regs[SONIC_CRBA0] = address & 0xffff; 815581f7b12SPeter Maydell available = dp8393x_rbwc(s); 816a65f56eeSaurel32 available -= rx_len / 2; 817a65f56eeSaurel32 s->regs[SONIC_RBWC1] = available >> 16; 818a65f56eeSaurel32 s->regs[SONIC_RBWC0] = available & 0xffff; 819a65f56eeSaurel32 820a65f56eeSaurel32 /* Update status */ 821581f7b12SPeter Maydell if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) { 822a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; 823a65f56eeSaurel32 } 824a65f56eeSaurel32 s->regs[SONIC_RCR] |= packet_type; 825a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_PRX; 826a65f56eeSaurel32 if (s->loopback_packet) { 827a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LBK; 828a65f56eeSaurel32 s->loopback_packet = 0; 829a65f56eeSaurel32 } 830a65f56eeSaurel32 831a65f56eeSaurel32 /* Write status to memory */ 832581f7b12SPeter Maydell DPRINTF("Write status at %08x\n", dp8393x_crda(s)); 833af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */ 834af9f0be3SLaurent Vivier dp8393x_put(s, width, 1, rx_len); /* byte count */ 835af9f0be3SLaurent Vivier dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */ 836af9f0be3SLaurent Vivier dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */ 837af9f0be3SLaurent Vivier dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */ 838a65f56eeSaurel32 size = sizeof(uint16_t) * 5 * width; 83919f70347SPeter Maydell address_space_write(&s->as, dp8393x_crda(s), 84019f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, 84119f70347SPeter Maydell s->data, size); 842a65f56eeSaurel32 843a65f56eeSaurel32 /* Move to next descriptor */ 844a65f56eeSaurel32 size = sizeof(uint16_t) * width; 84519f70347SPeter Maydell address_space_read(&s->as, 84619f70347SPeter Maydell dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, 84719f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 848af9f0be3SLaurent Vivier s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); 84988f632fbSFinn Thain if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { 850a65f56eeSaurel32 /* EOL detected */ 851a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RDE; 852a65f56eeSaurel32 } else { 85346ffee9aSFinn Thain /* Clear in_use */ 85446ffee9aSFinn Thain size = sizeof(uint16_t) * width; 85546ffee9aSFinn Thain address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; 85646ffee9aSFinn Thain dp8393x_put(s, width, 0, 0); 85746ffee9aSFinn Thain address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, 85846ffee9aSFinn Thain s->data, size); 859a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 860a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; 861a65f56eeSaurel32 s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); 862a65f56eeSaurel32 863a65f56eeSaurel32 if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { 864a65f56eeSaurel32 /* Read next RRA */ 8653df5de64SHervé Poussineau dp8393x_do_read_rra(s); 866a65f56eeSaurel32 } 867a65f56eeSaurel32 } 868a65f56eeSaurel32 869a65f56eeSaurel32 /* Done */ 870a65f56eeSaurel32 dp8393x_update_irq(s); 8714f1c942bSMark McLoughlin 872*9e3cd456SFinn Thain return pkt_size; 873a65f56eeSaurel32 } 874a65f56eeSaurel32 875104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev) 876a65f56eeSaurel32 { 877104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 878bc72ad67SAlex Bligh timer_del(s->watchdog); 879a65f56eeSaurel32 880bd8f1ebcSHervé Poussineau memset(s->regs, 0, sizeof(s->regs)); 881a65f56eeSaurel32 s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; 882a65f56eeSaurel32 s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); 883a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); 884a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; 885a65f56eeSaurel32 s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; 886a65f56eeSaurel32 s->regs[SONIC_IMR] = 0; 887a65f56eeSaurel32 s->regs[SONIC_ISR] = 0; 888a65f56eeSaurel32 s->regs[SONIC_DCR2] = 0; 889a65f56eeSaurel32 s->regs[SONIC_EOBC] = 0x02F8; 890a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 891a65f56eeSaurel32 s->regs[SONIC_CE] = 0; 892a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 893a65f56eeSaurel32 894a65f56eeSaurel32 /* Network cable is connected */ 895a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_CRS; 896a65f56eeSaurel32 897a65f56eeSaurel32 dp8393x_update_irq(s); 898a65f56eeSaurel32 } 899a65f56eeSaurel32 90005f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = { 901f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 90205f41fe3SMark McLoughlin .size = sizeof(NICState), 9033df5de64SHervé Poussineau .can_receive = dp8393x_can_receive, 9043df5de64SHervé Poussineau .receive = dp8393x_receive, 90505f41fe3SMark McLoughlin }; 90605f41fe3SMark McLoughlin 907104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj) 908a65f56eeSaurel32 { 909104655a5SHervé Poussineau SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 910104655a5SHervé Poussineau dp8393xState *s = DP8393X(obj); 911a65f56eeSaurel32 912104655a5SHervé Poussineau sysbus_init_mmio(sbd, &s->mmio); 91389ae0ff9SHervé Poussineau sysbus_init_mmio(sbd, &s->prom); 914104655a5SHervé Poussineau sysbus_init_irq(sbd, &s->irq); 915104655a5SHervé Poussineau } 916a65f56eeSaurel32 917104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp) 918104655a5SHervé Poussineau { 919104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 92089ae0ff9SHervé Poussineau int i, checksum; 92189ae0ff9SHervé Poussineau uint8_t *prom; 92252579c68SHervé Poussineau Error *local_err = NULL; 923a65f56eeSaurel32 924104655a5SHervé Poussineau address_space_init(&s->as, s->dma_mr, "dp8393x"); 925104655a5SHervé Poussineau memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, 926104655a5SHervé Poussineau "dp8393x-regs", 0x40 << s->it_shift); 927104655a5SHervé Poussineau 928104655a5SHervé Poussineau s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, 929104655a5SHervé Poussineau object_get_typename(OBJECT(dev)), dev->id, s); 930104655a5SHervé Poussineau qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 931104655a5SHervé Poussineau 932bc72ad67SAlex Bligh s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); 933a65f56eeSaurel32 s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ 93489ae0ff9SHervé Poussineau 9358fad0a65SPeter Maydell memory_region_init_ram(&s->prom, OBJECT(dev), 93652579c68SHervé Poussineau "dp8393x-prom", SONIC_PROM_SIZE, &local_err); 93752579c68SHervé Poussineau if (local_err) { 93852579c68SHervé Poussineau error_propagate(errp, local_err); 93952579c68SHervé Poussineau return; 94052579c68SHervé Poussineau } 94152579c68SHervé Poussineau memory_region_set_readonly(&s->prom, true); 94289ae0ff9SHervé Poussineau prom = memory_region_get_ram_ptr(&s->prom); 94389ae0ff9SHervé Poussineau checksum = 0; 94489ae0ff9SHervé Poussineau for (i = 0; i < 6; i++) { 94589ae0ff9SHervé Poussineau prom[i] = s->conf.macaddr.a[i]; 94689ae0ff9SHervé Poussineau checksum += prom[i]; 94789ae0ff9SHervé Poussineau if (checksum > 0xff) { 94889ae0ff9SHervé Poussineau checksum = (checksum + 1) & 0xff; 94989ae0ff9SHervé Poussineau } 95089ae0ff9SHervé Poussineau } 95189ae0ff9SHervé Poussineau prom[7] = 0xff - checksum; 952a65f56eeSaurel32 } 953104655a5SHervé Poussineau 9541670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = { 9551670735dSHervé Poussineau .name = "dp8393x", 9561670735dSHervé Poussineau .version_id = 0, 9571670735dSHervé Poussineau .minimum_version_id = 0, 9581670735dSHervé Poussineau .fields = (VMStateField []) { 9591670735dSHervé Poussineau VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), 9601670735dSHervé Poussineau VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), 9611670735dSHervé Poussineau VMSTATE_END_OF_LIST() 9621670735dSHervé Poussineau } 9631670735dSHervé Poussineau }; 9641670735dSHervé Poussineau 965104655a5SHervé Poussineau static Property dp8393x_properties[] = { 966104655a5SHervé Poussineau DEFINE_NIC_PROPERTIES(dp8393xState, conf), 9673110ce81SMarc-André Lureau DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr, 9683110ce81SMarc-André Lureau TYPE_MEMORY_REGION, MemoryRegion *), 969104655a5SHervé Poussineau DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), 970be920841SLaurent Vivier DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), 971104655a5SHervé Poussineau DEFINE_PROP_END_OF_LIST(), 972104655a5SHervé Poussineau }; 973104655a5SHervé Poussineau 974104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data) 975104655a5SHervé Poussineau { 976104655a5SHervé Poussineau DeviceClass *dc = DEVICE_CLASS(klass); 977104655a5SHervé Poussineau 978104655a5SHervé Poussineau set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 979104655a5SHervé Poussineau dc->realize = dp8393x_realize; 980104655a5SHervé Poussineau dc->reset = dp8393x_reset; 9811670735dSHervé Poussineau dc->vmsd = &vmstate_dp8393x; 9824f67d30bSMarc-André Lureau device_class_set_props(dc, dp8393x_properties); 983104655a5SHervé Poussineau } 984104655a5SHervé Poussineau 985104655a5SHervé Poussineau static const TypeInfo dp8393x_info = { 986104655a5SHervé Poussineau .name = TYPE_DP8393X, 987104655a5SHervé Poussineau .parent = TYPE_SYS_BUS_DEVICE, 988104655a5SHervé Poussineau .instance_size = sizeof(dp8393xState), 989104655a5SHervé Poussineau .instance_init = dp8393x_instance_init, 990104655a5SHervé Poussineau .class_init = dp8393x_class_init, 991104655a5SHervé Poussineau }; 992104655a5SHervé Poussineau 993104655a5SHervé Poussineau static void dp8393x_register_types(void) 994104655a5SHervé Poussineau { 995104655a5SHervé Poussineau type_register_static(&dp8393x_info); 996104655a5SHervé Poussineau } 997104655a5SHervé Poussineau 998104655a5SHervé Poussineau type_init(dp8393x_register_types) 999