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 148104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x" 149104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) 150104655a5SHervé Poussineau 151a65f56eeSaurel32 typedef struct dp8393xState { 152104655a5SHervé Poussineau SysBusDevice parent_obj; 153104655a5SHervé Poussineau 154a65f56eeSaurel32 /* Hardware */ 155104655a5SHervé Poussineau uint8_t it_shift; 156be920841SLaurent Vivier bool big_endian; 157a65f56eeSaurel32 qemu_irq irq; 158a65f56eeSaurel32 #ifdef DEBUG_SONIC 159a65f56eeSaurel32 int irq_level; 160a65f56eeSaurel32 #endif 161a65f56eeSaurel32 QEMUTimer *watchdog; 162a65f56eeSaurel32 int64_t wt_last_update; 16305f41fe3SMark McLoughlin NICConf conf; 16405f41fe3SMark McLoughlin NICState *nic; 165024e5bb6SAvi Kivity MemoryRegion mmio; 16689ae0ff9SHervé Poussineau MemoryRegion prom; 167a65f56eeSaurel32 168a65f56eeSaurel32 /* Registers */ 169a65f56eeSaurel32 uint8_t cam[16][6]; 170a65f56eeSaurel32 uint16_t regs[0x40]; 171a65f56eeSaurel32 172a65f56eeSaurel32 /* Temporaries */ 173a65f56eeSaurel32 uint8_t tx_buffer[0x10000]; 174*af9f0be3SLaurent Vivier uint16_t data[12]; 175a65f56eeSaurel32 int loopback_packet; 176a65f56eeSaurel32 177a65f56eeSaurel32 /* Memory access */ 178104655a5SHervé Poussineau void *dma_mr; 179dd820513SHervé Poussineau AddressSpace as; 180a65f56eeSaurel32 } dp8393xState; 181a65f56eeSaurel32 182581f7b12SPeter Maydell /* Accessor functions for values which are formed by 183581f7b12SPeter Maydell * concatenating two 16 bit device registers. By putting these 184581f7b12SPeter Maydell * in their own functions with a uint32_t return type we avoid the 185581f7b12SPeter Maydell * pitfall of implicit sign extension where ((x << 16) | y) is a 186581f7b12SPeter Maydell * signed 32 bit integer that might get sign-extended to a 64 bit integer. 187581f7b12SPeter Maydell */ 188581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s) 189581f7b12SPeter Maydell { 190581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP]; 191581f7b12SPeter Maydell } 192581f7b12SPeter Maydell 193581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s) 194581f7b12SPeter Maydell { 195581f7b12SPeter Maydell return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; 196581f7b12SPeter Maydell } 197581f7b12SPeter Maydell 198581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s) 199581f7b12SPeter Maydell { 200581f7b12SPeter Maydell return (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]; 201581f7b12SPeter Maydell } 202581f7b12SPeter Maydell 203581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s) 204581f7b12SPeter Maydell { 205581f7b12SPeter Maydell return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; 206581f7b12SPeter Maydell } 207581f7b12SPeter Maydell 208581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s) 209581f7b12SPeter Maydell { 210581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP]; 211581f7b12SPeter Maydell } 212581f7b12SPeter Maydell 213581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s) 214581f7b12SPeter Maydell { 215581f7b12SPeter Maydell return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0]; 216581f7b12SPeter Maydell } 217581f7b12SPeter Maydell 218581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s) 219581f7b12SPeter Maydell { 220581f7b12SPeter Maydell return (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]; 221581f7b12SPeter Maydell } 222581f7b12SPeter Maydell 223581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s) 224581f7b12SPeter Maydell { 225581f7b12SPeter Maydell return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 226581f7b12SPeter Maydell } 227581f7b12SPeter Maydell 228*af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset) 229be920841SLaurent Vivier { 230be920841SLaurent Vivier uint16_t val; 231be920841SLaurent Vivier 232be920841SLaurent Vivier if (s->big_endian) { 233*af9f0be3SLaurent Vivier val = be16_to_cpu(s->data[offset * width + width - 1]); 234be920841SLaurent Vivier } else { 235*af9f0be3SLaurent Vivier val = le16_to_cpu(s->data[offset * width]); 236be920841SLaurent Vivier } 237be920841SLaurent Vivier return val; 238be920841SLaurent Vivier } 239be920841SLaurent Vivier 240*af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset, 241be920841SLaurent Vivier uint16_t val) 242be920841SLaurent Vivier { 243be920841SLaurent Vivier if (s->big_endian) { 244*af9f0be3SLaurent Vivier s->data[offset * width + width - 1] = cpu_to_be16(val); 245be920841SLaurent Vivier } else { 246*af9f0be3SLaurent Vivier s->data[offset * width] = cpu_to_le16(val); 247be920841SLaurent Vivier } 248be920841SLaurent Vivier } 249be920841SLaurent Vivier 250a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s) 251a65f56eeSaurel32 { 252a65f56eeSaurel32 int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; 253a65f56eeSaurel32 254a65f56eeSaurel32 #ifdef DEBUG_SONIC 255a65f56eeSaurel32 if (level != s->irq_level) { 256a65f56eeSaurel32 s->irq_level = level; 257a65f56eeSaurel32 if (level) { 258a65f56eeSaurel32 DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]); 259a65f56eeSaurel32 } else { 260a65f56eeSaurel32 DPRINTF("lower irq\n"); 261a65f56eeSaurel32 } 262a65f56eeSaurel32 } 263a65f56eeSaurel32 #endif 264a65f56eeSaurel32 265a65f56eeSaurel32 qemu_set_irq(s->irq, level); 266a65f56eeSaurel32 } 267a65f56eeSaurel32 2683df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s) 269a65f56eeSaurel32 { 270a65f56eeSaurel32 int width, size; 271a65f56eeSaurel32 uint16_t index = 0; 272a65f56eeSaurel32 273a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 274a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 275a65f56eeSaurel32 276a65f56eeSaurel32 while (s->regs[SONIC_CDC] & 0x1f) { 277a65f56eeSaurel32 /* Fill current entry */ 278581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_cdp(s), 279*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 280*af9f0be3SLaurent Vivier s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; 281*af9f0be3SLaurent Vivier s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; 282*af9f0be3SLaurent Vivier s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; 283*af9f0be3SLaurent Vivier s->cam[index][3] = dp8393x_get(s, width, 2) >> 8; 284*af9f0be3SLaurent Vivier s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff; 285*af9f0be3SLaurent Vivier s->cam[index][5] = dp8393x_get(s, width, 3) >> 8; 286a65f56eeSaurel32 DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index, 287a65f56eeSaurel32 s->cam[index][0], s->cam[index][1], s->cam[index][2], 288a65f56eeSaurel32 s->cam[index][3], s->cam[index][4], s->cam[index][5]); 289a65f56eeSaurel32 /* Move to next entry */ 290a65f56eeSaurel32 s->regs[SONIC_CDC]--; 291a65f56eeSaurel32 s->regs[SONIC_CDP] += size; 292a65f56eeSaurel32 index++; 293a65f56eeSaurel32 } 294a65f56eeSaurel32 295a65f56eeSaurel32 /* Read CAM enable */ 296581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_cdp(s), 297*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 298*af9f0be3SLaurent Vivier s->regs[SONIC_CE] = dp8393x_get(s, width, 0); 299a65f56eeSaurel32 DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); 300a65f56eeSaurel32 301a65f56eeSaurel32 /* Done */ 302a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; 303a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_LCD; 304a65f56eeSaurel32 dp8393x_update_irq(s); 305a65f56eeSaurel32 } 306a65f56eeSaurel32 3073df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s) 308a65f56eeSaurel32 { 309a65f56eeSaurel32 int width, size; 310a65f56eeSaurel32 311a65f56eeSaurel32 /* Read memory */ 312a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 313a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 314581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_rrp(s), 315*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 316a65f56eeSaurel32 317a65f56eeSaurel32 /* Update SONIC registers */ 318*af9f0be3SLaurent Vivier s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0); 319*af9f0be3SLaurent Vivier s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1); 320*af9f0be3SLaurent Vivier s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2); 321*af9f0be3SLaurent Vivier s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3); 322a65f56eeSaurel32 DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n", 323a65f56eeSaurel32 s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], 324a65f56eeSaurel32 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); 325a65f56eeSaurel32 326a65f56eeSaurel32 /* Go to next entry */ 327a65f56eeSaurel32 s->regs[SONIC_RRP] += size; 328a65f56eeSaurel32 329a65f56eeSaurel32 /* Handle wrap */ 330a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { 331a65f56eeSaurel32 s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; 332a65f56eeSaurel32 } 333a65f56eeSaurel32 334a65f56eeSaurel32 /* Check resource exhaustion */ 335a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) 336a65f56eeSaurel32 { 337a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RBE; 338a65f56eeSaurel32 dp8393x_update_irq(s); 339a65f56eeSaurel32 } 340a65f56eeSaurel32 341a65f56eeSaurel32 /* Done */ 342a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; 343a65f56eeSaurel32 } 344a65f56eeSaurel32 3453df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s) 346a65f56eeSaurel32 { 347bc72ad67SAlex Bligh timer_del(s->watchdog); 348a65f56eeSaurel32 349a65f56eeSaurel32 s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX); 350a65f56eeSaurel32 s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; 351a65f56eeSaurel32 } 352a65f56eeSaurel32 3533df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s) 354a65f56eeSaurel32 { 355a65f56eeSaurel32 uint32_t ticks; 356a65f56eeSaurel32 int64_t delay; 357a65f56eeSaurel32 358a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 359bc72ad67SAlex Bligh timer_del(s->watchdog); 360a65f56eeSaurel32 return; 361a65f56eeSaurel32 } 362a65f56eeSaurel32 363581f7b12SPeter Maydell ticks = dp8393x_wt(s); 364bc72ad67SAlex Bligh s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 36573bcb24dSRutuja Shah delay = NANOSECONDS_PER_SECOND * ticks / 5000000; 366bc72ad67SAlex Bligh timer_mod(s->watchdog, s->wt_last_update + delay); 367a65f56eeSaurel32 } 368a65f56eeSaurel32 3693df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s) 370a65f56eeSaurel32 { 371a65f56eeSaurel32 int64_t elapsed; 372a65f56eeSaurel32 uint32_t val; 373a65f56eeSaurel32 374a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 375bc72ad67SAlex Bligh timer_del(s->watchdog); 376a65f56eeSaurel32 return; 377a65f56eeSaurel32 } 378a65f56eeSaurel32 379bc72ad67SAlex Bligh elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 380581f7b12SPeter Maydell val = dp8393x_wt(s); 381a65f56eeSaurel32 val -= elapsed / 5000000; 382a65f56eeSaurel32 s->regs[SONIC_WT1] = (val >> 16) & 0xffff; 383a65f56eeSaurel32 s->regs[SONIC_WT0] = (val >> 0) & 0xffff; 3843df5de64SHervé Poussineau dp8393x_set_next_tick(s); 385a65f56eeSaurel32 386a65f56eeSaurel32 } 387a65f56eeSaurel32 3883df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s) 389a65f56eeSaurel32 { 390a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_STP; 3913df5de64SHervé Poussineau dp8393x_set_next_tick(s); 392a65f56eeSaurel32 } 393a65f56eeSaurel32 3943df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s) 395a65f56eeSaurel32 { 396a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_ST; 3973df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 398a65f56eeSaurel32 } 399a65f56eeSaurel32 4004594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc); 4014594f93aSFam Zheng 4023df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s) 403a65f56eeSaurel32 { 404a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; 4054594f93aSFam Zheng if (dp8393x_can_receive(s->nic->ncs)) { 4064594f93aSFam Zheng qemu_flush_queued_packets(qemu_get_queue(s->nic)); 4074594f93aSFam Zheng } 408a65f56eeSaurel32 } 409a65f56eeSaurel32 4103df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s) 411a65f56eeSaurel32 { 412a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; 413a65f56eeSaurel32 } 414a65f56eeSaurel32 4153df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s) 416a65f56eeSaurel32 { 417b356f76dSJason Wang NetClientState *nc = qemu_get_queue(s->nic); 418a65f56eeSaurel32 int width, size; 419a65f56eeSaurel32 int tx_len, len; 420a65f56eeSaurel32 uint16_t i; 421a65f56eeSaurel32 422a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 423a65f56eeSaurel32 424a65f56eeSaurel32 while (1) { 425a65f56eeSaurel32 /* Read memory */ 426a65f56eeSaurel32 size = sizeof(uint16_t) * 6 * width; 427a65f56eeSaurel32 s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; 428581f7b12SPeter Maydell DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s)); 429*af9f0be3SLaurent Vivier address_space_rw(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, 430*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 431a65f56eeSaurel32 tx_len = 0; 432a65f56eeSaurel32 433a65f56eeSaurel32 /* Update registers */ 434*af9f0be3SLaurent Vivier s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000; 435*af9f0be3SLaurent Vivier s->regs[SONIC_TPS] = dp8393x_get(s, width, 1); 436*af9f0be3SLaurent Vivier s->regs[SONIC_TFC] = dp8393x_get(s, width, 2); 437*af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3); 438*af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4); 439*af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 5); 440a65f56eeSaurel32 441a65f56eeSaurel32 /* Handle programmable interrupt */ 442a65f56eeSaurel32 if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { 443a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PINT; 444a65f56eeSaurel32 } else { 445a65f56eeSaurel32 s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; 446a65f56eeSaurel32 } 447a65f56eeSaurel32 448a65f56eeSaurel32 for (i = 0; i < s->regs[SONIC_TFC]; ) { 449a65f56eeSaurel32 /* Append fragment */ 450a65f56eeSaurel32 len = s->regs[SONIC_TFS]; 451a65f56eeSaurel32 if (tx_len + len > sizeof(s->tx_buffer)) { 452a65f56eeSaurel32 len = sizeof(s->tx_buffer) - tx_len; 453a65f56eeSaurel32 } 454581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_tsa(s), 455dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0); 456a65f56eeSaurel32 tx_len += len; 457a65f56eeSaurel32 458a65f56eeSaurel32 i++; 459a65f56eeSaurel32 if (i != s->regs[SONIC_TFC]) { 460a65f56eeSaurel32 /* Read next fragment details */ 461a65f56eeSaurel32 size = sizeof(uint16_t) * 3 * width; 462dd820513SHervé Poussineau address_space_rw(&s->as, 463581f7b12SPeter Maydell dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width, 464*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 465*af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0); 466*af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1); 467*af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 2); 468a65f56eeSaurel32 } 469a65f56eeSaurel32 } 470a65f56eeSaurel32 471a65f56eeSaurel32 /* Handle Ethernet checksum */ 472a65f56eeSaurel32 if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { 473a65f56eeSaurel32 /* Don't append FCS there, to look like slirp packets 474a65f56eeSaurel32 * which don't have one */ 475a65f56eeSaurel32 } else { 476a65f56eeSaurel32 /* Remove existing FCS */ 477a65f56eeSaurel32 tx_len -= 4; 478a65f56eeSaurel32 } 479a65f56eeSaurel32 480a65f56eeSaurel32 if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { 481a65f56eeSaurel32 /* Loopback */ 482a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; 483b356f76dSJason Wang if (nc->info->can_receive(nc)) { 484a65f56eeSaurel32 s->loopback_packet = 1; 485b356f76dSJason Wang nc->info->receive(nc, s->tx_buffer, tx_len); 486a65f56eeSaurel32 } 487a65f56eeSaurel32 } else { 488a65f56eeSaurel32 /* Transmit packet */ 489b356f76dSJason Wang qemu_send_packet(nc, s->tx_buffer, tx_len); 490a65f56eeSaurel32 } 491a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_PTX; 492a65f56eeSaurel32 493a65f56eeSaurel32 /* Write status */ 494*af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, 495be920841SLaurent Vivier s->regs[SONIC_TCR] & 0x0fff); /* status */ 496a65f56eeSaurel32 size = sizeof(uint16_t) * width; 497dd820513SHervé Poussineau address_space_rw(&s->as, 498581f7b12SPeter Maydell dp8393x_ttda(s), 499*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 1); 500a65f56eeSaurel32 501a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { 502a65f56eeSaurel32 /* Read footer of packet */ 503a65f56eeSaurel32 size = sizeof(uint16_t) * width; 504dd820513SHervé Poussineau address_space_rw(&s->as, 505581f7b12SPeter Maydell dp8393x_ttda(s) + 506581f7b12SPeter Maydell sizeof(uint16_t) * 507581f7b12SPeter Maydell (4 + 3 * s->regs[SONIC_TFC]) * width, 508*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 509*af9f0be3SLaurent Vivier s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0) & ~0x1; 510*af9f0be3SLaurent Vivier if (dp8393x_get(s, width, 0) & 0x1) { 511a65f56eeSaurel32 /* EOL detected */ 512a65f56eeSaurel32 break; 513a65f56eeSaurel32 } 514a65f56eeSaurel32 } 515a65f56eeSaurel32 } 516a65f56eeSaurel32 517a65f56eeSaurel32 /* Done */ 518a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_TXP; 519a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; 520a65f56eeSaurel32 dp8393x_update_irq(s); 521a65f56eeSaurel32 } 522a65f56eeSaurel32 5233df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s) 524a65f56eeSaurel32 { 525a65f56eeSaurel32 /* Nothing to do */ 526a65f56eeSaurel32 } 527a65f56eeSaurel32 5283df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command) 529a65f56eeSaurel32 { 530a65f56eeSaurel32 if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { 531a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RST; 532a65f56eeSaurel32 return; 533a65f56eeSaurel32 } 534a65f56eeSaurel32 535a65f56eeSaurel32 s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); 536a65f56eeSaurel32 537a65f56eeSaurel32 if (command & SONIC_CR_HTX) 5383df5de64SHervé Poussineau dp8393x_do_halt_transmission(s); 539a65f56eeSaurel32 if (command & SONIC_CR_TXP) 5403df5de64SHervé Poussineau dp8393x_do_transmit_packets(s); 541a65f56eeSaurel32 if (command & SONIC_CR_RXDIS) 5423df5de64SHervé Poussineau dp8393x_do_receiver_disable(s); 543a65f56eeSaurel32 if (command & SONIC_CR_RXEN) 5443df5de64SHervé Poussineau dp8393x_do_receiver_enable(s); 545a65f56eeSaurel32 if (command & SONIC_CR_STP) 5463df5de64SHervé Poussineau dp8393x_do_stop_timer(s); 547a65f56eeSaurel32 if (command & SONIC_CR_ST) 5483df5de64SHervé Poussineau dp8393x_do_start_timer(s); 549a65f56eeSaurel32 if (command & SONIC_CR_RST) 5503df5de64SHervé Poussineau dp8393x_do_software_reset(s); 551a65f56eeSaurel32 if (command & SONIC_CR_RRRA) 5523df5de64SHervé Poussineau dp8393x_do_read_rra(s); 553a65f56eeSaurel32 if (command & SONIC_CR_LCAM) 5543df5de64SHervé Poussineau dp8393x_do_load_cam(s); 555a65f56eeSaurel32 } 556a65f56eeSaurel32 55784689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) 558a65f56eeSaurel32 { 55984689cbbSHervé Poussineau dp8393xState *s = opaque; 56084689cbbSHervé Poussineau int reg = addr >> s->it_shift; 561a65f56eeSaurel32 uint16_t val = 0; 562a65f56eeSaurel32 563a65f56eeSaurel32 switch (reg) { 564a65f56eeSaurel32 /* Update data before reading it */ 565a65f56eeSaurel32 case SONIC_WT0: 566a65f56eeSaurel32 case SONIC_WT1: 5673df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 568a65f56eeSaurel32 val = s->regs[reg]; 569a65f56eeSaurel32 break; 570a65f56eeSaurel32 /* Accept read to some registers only when in reset mode */ 571a65f56eeSaurel32 case SONIC_CAP2: 572a65f56eeSaurel32 case SONIC_CAP1: 573a65f56eeSaurel32 case SONIC_CAP0: 574a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 575a65f56eeSaurel32 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8; 576a65f56eeSaurel32 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)]; 577a65f56eeSaurel32 } 578a65f56eeSaurel32 break; 579a65f56eeSaurel32 /* All other registers have no special contrainst */ 580a65f56eeSaurel32 default: 581a65f56eeSaurel32 val = s->regs[reg]; 582a65f56eeSaurel32 } 583a65f56eeSaurel32 584a65f56eeSaurel32 DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); 585a65f56eeSaurel32 586a65f56eeSaurel32 return val; 587a65f56eeSaurel32 } 588a65f56eeSaurel32 58984689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, 59084689cbbSHervé Poussineau unsigned int size) 591a65f56eeSaurel32 { 59284689cbbSHervé Poussineau dp8393xState *s = opaque; 59384689cbbSHervé Poussineau int reg = addr >> s->it_shift; 59484689cbbSHervé Poussineau 59584689cbbSHervé Poussineau DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]); 596a65f56eeSaurel32 597a65f56eeSaurel32 switch (reg) { 598a65f56eeSaurel32 /* Command register */ 599a65f56eeSaurel32 case SONIC_CR: 6003df5de64SHervé Poussineau dp8393x_do_command(s, data); 601a65f56eeSaurel32 break; 602a65f56eeSaurel32 /* Prevent write to read-only registers */ 603a65f56eeSaurel32 case SONIC_CAP2: 604a65f56eeSaurel32 case SONIC_CAP1: 605a65f56eeSaurel32 case SONIC_CAP0: 606a65f56eeSaurel32 case SONIC_SR: 607a65f56eeSaurel32 case SONIC_MDT: 608a65f56eeSaurel32 DPRINTF("writing to reg %d invalid\n", reg); 609a65f56eeSaurel32 break; 610a65f56eeSaurel32 /* Accept write to some registers only when in reset mode */ 611a65f56eeSaurel32 case SONIC_DCR: 612a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 61384689cbbSHervé Poussineau s->regs[reg] = data & 0xbfff; 614a65f56eeSaurel32 } else { 615a65f56eeSaurel32 DPRINTF("writing to DCR invalid\n"); 616a65f56eeSaurel32 } 617a65f56eeSaurel32 break; 618a65f56eeSaurel32 case SONIC_DCR2: 619a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 62084689cbbSHervé Poussineau s->regs[reg] = data & 0xf017; 621a65f56eeSaurel32 } else { 622a65f56eeSaurel32 DPRINTF("writing to DCR2 invalid\n"); 623a65f56eeSaurel32 } 624a65f56eeSaurel32 break; 625a65f56eeSaurel32 /* 12 lower bytes are Read Only */ 626a65f56eeSaurel32 case SONIC_TCR: 62784689cbbSHervé Poussineau s->regs[reg] = data & 0xf000; 628a65f56eeSaurel32 break; 629a65f56eeSaurel32 /* 9 lower bytes are Read Only */ 630a65f56eeSaurel32 case SONIC_RCR: 63184689cbbSHervé Poussineau s->regs[reg] = data & 0xffe0; 632a65f56eeSaurel32 break; 633a65f56eeSaurel32 /* Ignore most significant bit */ 634a65f56eeSaurel32 case SONIC_IMR: 63584689cbbSHervé Poussineau s->regs[reg] = data & 0x7fff; 636a65f56eeSaurel32 dp8393x_update_irq(s); 637a65f56eeSaurel32 break; 638a65f56eeSaurel32 /* Clear bits by writing 1 to them */ 639a65f56eeSaurel32 case SONIC_ISR: 64084689cbbSHervé Poussineau data &= s->regs[reg]; 64184689cbbSHervé Poussineau s->regs[reg] &= ~data; 64284689cbbSHervé Poussineau if (data & SONIC_ISR_RBE) { 6433df5de64SHervé Poussineau dp8393x_do_read_rra(s); 644a65f56eeSaurel32 } 645a65f56eeSaurel32 dp8393x_update_irq(s); 6464594f93aSFam Zheng if (dp8393x_can_receive(s->nic->ncs)) { 6474594f93aSFam Zheng qemu_flush_queued_packets(qemu_get_queue(s->nic)); 6484594f93aSFam Zheng } 649a65f56eeSaurel32 break; 650a65f56eeSaurel32 /* Ignore least significant bit */ 651a65f56eeSaurel32 case SONIC_RSA: 652a65f56eeSaurel32 case SONIC_REA: 653a65f56eeSaurel32 case SONIC_RRP: 654a65f56eeSaurel32 case SONIC_RWP: 65584689cbbSHervé Poussineau s->regs[reg] = data & 0xfffe; 656a65f56eeSaurel32 break; 657a65f56eeSaurel32 /* Invert written value for some registers */ 658a65f56eeSaurel32 case SONIC_CRCT: 659a65f56eeSaurel32 case SONIC_FAET: 660a65f56eeSaurel32 case SONIC_MPT: 66184689cbbSHervé Poussineau s->regs[reg] = data ^ 0xffff; 662a65f56eeSaurel32 break; 663a65f56eeSaurel32 /* All other registers have no special contrainst */ 664a65f56eeSaurel32 default: 66584689cbbSHervé Poussineau s->regs[reg] = data; 666a65f56eeSaurel32 } 667a65f56eeSaurel32 668a65f56eeSaurel32 if (reg == SONIC_WT0 || reg == SONIC_WT1) { 6693df5de64SHervé Poussineau dp8393x_set_next_tick(s); 670a65f56eeSaurel32 } 671a65f56eeSaurel32 } 672a65f56eeSaurel32 67384689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = { 67484689cbbSHervé Poussineau .read = dp8393x_read, 67584689cbbSHervé Poussineau .write = dp8393x_write, 67684689cbbSHervé Poussineau .impl.min_access_size = 2, 67784689cbbSHervé Poussineau .impl.max_access_size = 2, 67884689cbbSHervé Poussineau .endianness = DEVICE_NATIVE_ENDIAN, 67984689cbbSHervé Poussineau }; 68084689cbbSHervé Poussineau 681a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque) 682a65f56eeSaurel32 { 683a65f56eeSaurel32 dp8393xState *s = opaque; 684a65f56eeSaurel32 685a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 686a65f56eeSaurel32 return; 687a65f56eeSaurel32 } 688a65f56eeSaurel32 689a65f56eeSaurel32 s->regs[SONIC_WT1] = 0xffff; 690a65f56eeSaurel32 s->regs[SONIC_WT0] = 0xffff; 6913df5de64SHervé Poussineau dp8393x_set_next_tick(s); 692a65f56eeSaurel32 693a65f56eeSaurel32 /* Signal underflow */ 694a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TC; 695a65f56eeSaurel32 dp8393x_update_irq(s); 696a65f56eeSaurel32 } 697a65f56eeSaurel32 6983df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc) 699a65f56eeSaurel32 { 700cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 701a65f56eeSaurel32 702a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) 703a65f56eeSaurel32 return 0; 704a65f56eeSaurel32 if (s->regs[SONIC_ISR] & SONIC_ISR_RBE) 705a65f56eeSaurel32 return 0; 706a65f56eeSaurel32 return 1; 707a65f56eeSaurel32 } 708a65f56eeSaurel32 7093df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, 7103df5de64SHervé Poussineau int size) 711a65f56eeSaurel32 { 712a65f56eeSaurel32 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 713a65f56eeSaurel32 int i; 714a65f56eeSaurel32 715a65f56eeSaurel32 /* Check promiscuous mode */ 716a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { 717a65f56eeSaurel32 return 0; 718a65f56eeSaurel32 } 719a65f56eeSaurel32 720a65f56eeSaurel32 /* Check multicast packets */ 721a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { 722a65f56eeSaurel32 return SONIC_RCR_MC; 723a65f56eeSaurel32 } 724a65f56eeSaurel32 725a65f56eeSaurel32 /* Check broadcast */ 726a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) { 727a65f56eeSaurel32 return SONIC_RCR_BC; 728a65f56eeSaurel32 } 729a65f56eeSaurel32 730a65f56eeSaurel32 /* Check CAM */ 731a65f56eeSaurel32 for (i = 0; i < 16; i++) { 732a65f56eeSaurel32 if (s->regs[SONIC_CE] & (1 << i)) { 733a65f56eeSaurel32 /* Entry enabled */ 734a65f56eeSaurel32 if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) { 735a65f56eeSaurel32 return 0; 736a65f56eeSaurel32 } 737a65f56eeSaurel32 } 738a65f56eeSaurel32 } 739a65f56eeSaurel32 740a65f56eeSaurel32 return -1; 741a65f56eeSaurel32 } 742a65f56eeSaurel32 7433df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, 7443df5de64SHervé Poussineau size_t size) 745a65f56eeSaurel32 { 746cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 747a65f56eeSaurel32 int packet_type; 748a65f56eeSaurel32 uint32_t available, address; 749a65f56eeSaurel32 int width, rx_len = size; 750a65f56eeSaurel32 uint32_t checksum; 751a65f56eeSaurel32 752a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 753a65f56eeSaurel32 754a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | 755a65f56eeSaurel32 SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); 756a65f56eeSaurel32 7573df5de64SHervé Poussineau packet_type = dp8393x_receive_filter(s, buf, size); 758a65f56eeSaurel32 if (packet_type < 0) { 759a65f56eeSaurel32 DPRINTF("packet not for netcard\n"); 7604f1c942bSMark McLoughlin return -1; 761a65f56eeSaurel32 } 762a65f56eeSaurel32 763a65f56eeSaurel32 /* XXX: Check byte ordering */ 764a65f56eeSaurel32 765a65f56eeSaurel32 /* Check for EOL */ 766a65f56eeSaurel32 if (s->regs[SONIC_LLFA] & 0x1) { 767a65f56eeSaurel32 /* Are we still in resource exhaustion? */ 768a65f56eeSaurel32 size = sizeof(uint16_t) * 1 * width; 769581f7b12SPeter Maydell address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; 770dd820513SHervé Poussineau address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, 771*af9f0be3SLaurent Vivier (uint8_t *)s->data, size, 0); 772*af9f0be3SLaurent Vivier if (dp8393x_get(s, width, 0) & 0x1) { 773a65f56eeSaurel32 /* Still EOL ; stop reception */ 7744f1c942bSMark McLoughlin return -1; 775a65f56eeSaurel32 } else { 776a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 777a65f56eeSaurel32 } 778a65f56eeSaurel32 } 779a65f56eeSaurel32 780a65f56eeSaurel32 /* Save current position */ 781a65f56eeSaurel32 s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; 782a65f56eeSaurel32 s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; 783a65f56eeSaurel32 784a65f56eeSaurel32 /* Calculate the ethernet checksum */ 785a65f56eeSaurel32 checksum = cpu_to_le32(crc32(0, buf, rx_len)); 786a65f56eeSaurel32 787a65f56eeSaurel32 /* Put packet into RBA */ 788581f7b12SPeter Maydell DPRINTF("Receive packet at %08x\n", dp8393x_crba(s)); 789581f7b12SPeter Maydell address = dp8393x_crba(s); 790dd820513SHervé Poussineau address_space_rw(&s->as, address, 791dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); 792a65f56eeSaurel32 address += rx_len; 793dd820513SHervé Poussineau address_space_rw(&s->as, address, 794dd820513SHervé Poussineau MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1); 795a65f56eeSaurel32 rx_len += 4; 796a65f56eeSaurel32 s->regs[SONIC_CRBA1] = address >> 16; 797a65f56eeSaurel32 s->regs[SONIC_CRBA0] = address & 0xffff; 798581f7b12SPeter Maydell available = dp8393x_rbwc(s); 799a65f56eeSaurel32 available -= rx_len / 2; 800a65f56eeSaurel32 s->regs[SONIC_RBWC1] = available >> 16; 801a65f56eeSaurel32 s->regs[SONIC_RBWC0] = available & 0xffff; 802a65f56eeSaurel32 803a65f56eeSaurel32 /* Update status */ 804581f7b12SPeter Maydell if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) { 805a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; 806a65f56eeSaurel32 } 807a65f56eeSaurel32 s->regs[SONIC_RCR] |= packet_type; 808a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_PRX; 809a65f56eeSaurel32 if (s->loopback_packet) { 810a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LBK; 811a65f56eeSaurel32 s->loopback_packet = 0; 812a65f56eeSaurel32 } 813a65f56eeSaurel32 814a65f56eeSaurel32 /* Write status to memory */ 815581f7b12SPeter Maydell DPRINTF("Write status at %08x\n", dp8393x_crda(s)); 816*af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */ 817*af9f0be3SLaurent Vivier dp8393x_put(s, width, 1, rx_len); /* byte count */ 818*af9f0be3SLaurent Vivier dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */ 819*af9f0be3SLaurent Vivier dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */ 820*af9f0be3SLaurent Vivier dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */ 821a65f56eeSaurel32 size = sizeof(uint16_t) * 5 * width; 822581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_crda(s), 823*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 1); 824a65f56eeSaurel32 825a65f56eeSaurel32 /* Move to next descriptor */ 826a65f56eeSaurel32 size = sizeof(uint16_t) * width; 827581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, 828*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); 829*af9f0be3SLaurent Vivier s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); 830a65f56eeSaurel32 if (s->regs[SONIC_LLFA] & 0x1) { 831a65f56eeSaurel32 /* EOL detected */ 832a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RDE; 833a65f56eeSaurel32 } else { 834*af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, 0); /* in_use */ 835581f7b12SPeter Maydell address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width, 836*af9f0be3SLaurent Vivier MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, sizeof(uint16_t), 1); 837a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 838a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; 839a65f56eeSaurel32 s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); 840a65f56eeSaurel32 841a65f56eeSaurel32 if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { 842a65f56eeSaurel32 /* Read next RRA */ 8433df5de64SHervé Poussineau dp8393x_do_read_rra(s); 844a65f56eeSaurel32 } 845a65f56eeSaurel32 } 846a65f56eeSaurel32 847a65f56eeSaurel32 /* Done */ 848a65f56eeSaurel32 dp8393x_update_irq(s); 8494f1c942bSMark McLoughlin 8504f1c942bSMark McLoughlin return size; 851a65f56eeSaurel32 } 852a65f56eeSaurel32 853104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev) 854a65f56eeSaurel32 { 855104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 856bc72ad67SAlex Bligh timer_del(s->watchdog); 857a65f56eeSaurel32 858bd8f1ebcSHervé Poussineau memset(s->regs, 0, sizeof(s->regs)); 859a65f56eeSaurel32 s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; 860a65f56eeSaurel32 s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); 861a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); 862a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; 863a65f56eeSaurel32 s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; 864a65f56eeSaurel32 s->regs[SONIC_IMR] = 0; 865a65f56eeSaurel32 s->regs[SONIC_ISR] = 0; 866a65f56eeSaurel32 s->regs[SONIC_DCR2] = 0; 867a65f56eeSaurel32 s->regs[SONIC_EOBC] = 0x02F8; 868a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 869a65f56eeSaurel32 s->regs[SONIC_CE] = 0; 870a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 871a65f56eeSaurel32 872a65f56eeSaurel32 /* Network cable is connected */ 873a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_CRS; 874a65f56eeSaurel32 875a65f56eeSaurel32 dp8393x_update_irq(s); 876a65f56eeSaurel32 } 877a65f56eeSaurel32 87805f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = { 879f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 88005f41fe3SMark McLoughlin .size = sizeof(NICState), 8813df5de64SHervé Poussineau .can_receive = dp8393x_can_receive, 8823df5de64SHervé Poussineau .receive = dp8393x_receive, 88305f41fe3SMark McLoughlin }; 88405f41fe3SMark McLoughlin 885104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj) 886a65f56eeSaurel32 { 887104655a5SHervé Poussineau SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 888104655a5SHervé Poussineau dp8393xState *s = DP8393X(obj); 889a65f56eeSaurel32 890104655a5SHervé Poussineau sysbus_init_mmio(sbd, &s->mmio); 89189ae0ff9SHervé Poussineau sysbus_init_mmio(sbd, &s->prom); 892104655a5SHervé Poussineau sysbus_init_irq(sbd, &s->irq); 893104655a5SHervé Poussineau } 894a65f56eeSaurel32 895104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp) 896104655a5SHervé Poussineau { 897104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 89889ae0ff9SHervé Poussineau int i, checksum; 89989ae0ff9SHervé Poussineau uint8_t *prom; 90052579c68SHervé Poussineau Error *local_err = NULL; 901a65f56eeSaurel32 902104655a5SHervé Poussineau address_space_init(&s->as, s->dma_mr, "dp8393x"); 903104655a5SHervé Poussineau memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, 904104655a5SHervé Poussineau "dp8393x-regs", 0x40 << s->it_shift); 905104655a5SHervé Poussineau 906104655a5SHervé Poussineau s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, 907104655a5SHervé Poussineau object_get_typename(OBJECT(dev)), dev->id, s); 908104655a5SHervé Poussineau qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 909104655a5SHervé Poussineau 910bc72ad67SAlex Bligh s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); 911a65f56eeSaurel32 s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ 91289ae0ff9SHervé Poussineau 9138fad0a65SPeter Maydell memory_region_init_ram(&s->prom, OBJECT(dev), 91452579c68SHervé Poussineau "dp8393x-prom", SONIC_PROM_SIZE, &local_err); 91552579c68SHervé Poussineau if (local_err) { 91652579c68SHervé Poussineau error_propagate(errp, local_err); 91752579c68SHervé Poussineau return; 91852579c68SHervé Poussineau } 91952579c68SHervé Poussineau memory_region_set_readonly(&s->prom, true); 92089ae0ff9SHervé Poussineau prom = memory_region_get_ram_ptr(&s->prom); 92189ae0ff9SHervé Poussineau checksum = 0; 92289ae0ff9SHervé Poussineau for (i = 0; i < 6; i++) { 92389ae0ff9SHervé Poussineau prom[i] = s->conf.macaddr.a[i]; 92489ae0ff9SHervé Poussineau checksum += prom[i]; 92589ae0ff9SHervé Poussineau if (checksum > 0xff) { 92689ae0ff9SHervé Poussineau checksum = (checksum + 1) & 0xff; 92789ae0ff9SHervé Poussineau } 92889ae0ff9SHervé Poussineau } 92989ae0ff9SHervé Poussineau prom[7] = 0xff - checksum; 930a65f56eeSaurel32 } 931104655a5SHervé Poussineau 9321670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = { 9331670735dSHervé Poussineau .name = "dp8393x", 9341670735dSHervé Poussineau .version_id = 0, 9351670735dSHervé Poussineau .minimum_version_id = 0, 9361670735dSHervé Poussineau .fields = (VMStateField []) { 9371670735dSHervé Poussineau VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), 9381670735dSHervé Poussineau VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), 9391670735dSHervé Poussineau VMSTATE_END_OF_LIST() 9401670735dSHervé Poussineau } 9411670735dSHervé Poussineau }; 9421670735dSHervé Poussineau 943104655a5SHervé Poussineau static Property dp8393x_properties[] = { 944104655a5SHervé Poussineau DEFINE_NIC_PROPERTIES(dp8393xState, conf), 945104655a5SHervé Poussineau DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr), 946104655a5SHervé Poussineau DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), 947be920841SLaurent Vivier DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), 948104655a5SHervé Poussineau DEFINE_PROP_END_OF_LIST(), 949104655a5SHervé Poussineau }; 950104655a5SHervé Poussineau 951104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data) 952104655a5SHervé Poussineau { 953104655a5SHervé Poussineau DeviceClass *dc = DEVICE_CLASS(klass); 954104655a5SHervé Poussineau 955104655a5SHervé Poussineau set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 956104655a5SHervé Poussineau dc->realize = dp8393x_realize; 957104655a5SHervé Poussineau dc->reset = dp8393x_reset; 9581670735dSHervé Poussineau dc->vmsd = &vmstate_dp8393x; 959104655a5SHervé Poussineau dc->props = dp8393x_properties; 960f6351288SHervé Poussineau /* Reason: dma_mr property can't be set */ 961e90f2a8cSEduardo Habkost dc->user_creatable = false; 962104655a5SHervé Poussineau } 963104655a5SHervé Poussineau 964104655a5SHervé Poussineau static const TypeInfo dp8393x_info = { 965104655a5SHervé Poussineau .name = TYPE_DP8393X, 966104655a5SHervé Poussineau .parent = TYPE_SYS_BUS_DEVICE, 967104655a5SHervé Poussineau .instance_size = sizeof(dp8393xState), 968104655a5SHervé Poussineau .instance_init = dp8393x_instance_init, 969104655a5SHervé Poussineau .class_init = dp8393x_class_init, 970104655a5SHervé Poussineau }; 971104655a5SHervé Poussineau 972104655a5SHervé Poussineau static void dp8393x_register_types(void) 973104655a5SHervé Poussineau { 974104655a5SHervé Poussineau type_register_static(&dp8393x_info); 975104655a5SHervé Poussineau } 976104655a5SHervé Poussineau 977104655a5SHervé Poussineau type_init(dp8393x_register_types) 978