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> 30*db1015e9SEduardo Habkost #include "qom/object.h" 31a65f56eeSaurel32 32a65f56eeSaurel32 //#define DEBUG_SONIC 33a65f56eeSaurel32 3489ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000 35a65f56eeSaurel32 36a65f56eeSaurel32 #ifdef DEBUG_SONIC 37001faf32SBlue Swirl #define DPRINTF(fmt, ...) \ 38001faf32SBlue Swirl do { printf("sonic: " fmt , ## __VA_ARGS__); } while (0) 39a65f56eeSaurel32 static const char* reg_names[] = { 40a65f56eeSaurel32 "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA", 41a65f56eeSaurel32 "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0", 42a65f56eeSaurel32 "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP", 43a65f56eeSaurel32 "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA", 44a65f56eeSaurel32 "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC", 45a65f56eeSaurel32 "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT", 46a65f56eeSaurel32 "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", 47a65f56eeSaurel32 "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" }; 48a65f56eeSaurel32 #else 49001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0) 50a65f56eeSaurel32 #endif 51a65f56eeSaurel32 52001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \ 53001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) 54a65f56eeSaurel32 55a65f56eeSaurel32 #define SONIC_CR 0x00 56a65f56eeSaurel32 #define SONIC_DCR 0x01 57a65f56eeSaurel32 #define SONIC_RCR 0x02 58a65f56eeSaurel32 #define SONIC_TCR 0x03 59a65f56eeSaurel32 #define SONIC_IMR 0x04 60a65f56eeSaurel32 #define SONIC_ISR 0x05 61a65f56eeSaurel32 #define SONIC_UTDA 0x06 62a65f56eeSaurel32 #define SONIC_CTDA 0x07 63a65f56eeSaurel32 #define SONIC_TPS 0x08 64a65f56eeSaurel32 #define SONIC_TFC 0x09 65a65f56eeSaurel32 #define SONIC_TSA0 0x0a 66a65f56eeSaurel32 #define SONIC_TSA1 0x0b 67a65f56eeSaurel32 #define SONIC_TFS 0x0c 68a65f56eeSaurel32 #define SONIC_URDA 0x0d 69a65f56eeSaurel32 #define SONIC_CRDA 0x0e 70a65f56eeSaurel32 #define SONIC_CRBA0 0x0f 71a65f56eeSaurel32 #define SONIC_CRBA1 0x10 72a65f56eeSaurel32 #define SONIC_RBWC0 0x11 73a65f56eeSaurel32 #define SONIC_RBWC1 0x12 74a65f56eeSaurel32 #define SONIC_EOBC 0x13 75a65f56eeSaurel32 #define SONIC_URRA 0x14 76a65f56eeSaurel32 #define SONIC_RSA 0x15 77a65f56eeSaurel32 #define SONIC_REA 0x16 78a65f56eeSaurel32 #define SONIC_RRP 0x17 79a65f56eeSaurel32 #define SONIC_RWP 0x18 80a65f56eeSaurel32 #define SONIC_TRBA0 0x19 81a65f56eeSaurel32 #define SONIC_TRBA1 0x1a 82a65f56eeSaurel32 #define SONIC_LLFA 0x1f 83a65f56eeSaurel32 #define SONIC_TTDA 0x20 84a65f56eeSaurel32 #define SONIC_CEP 0x21 85a65f56eeSaurel32 #define SONIC_CAP2 0x22 86a65f56eeSaurel32 #define SONIC_CAP1 0x23 87a65f56eeSaurel32 #define SONIC_CAP0 0x24 88a65f56eeSaurel32 #define SONIC_CE 0x25 89a65f56eeSaurel32 #define SONIC_CDP 0x26 90a65f56eeSaurel32 #define SONIC_CDC 0x27 91a65f56eeSaurel32 #define SONIC_SR 0x28 92a65f56eeSaurel32 #define SONIC_WT0 0x29 93a65f56eeSaurel32 #define SONIC_WT1 0x2a 94a65f56eeSaurel32 #define SONIC_RSC 0x2b 95a65f56eeSaurel32 #define SONIC_CRCT 0x2c 96a65f56eeSaurel32 #define SONIC_FAET 0x2d 97a65f56eeSaurel32 #define SONIC_MPT 0x2e 98a65f56eeSaurel32 #define SONIC_MDT 0x2f 99a65f56eeSaurel32 #define SONIC_DCR2 0x3f 100a65f56eeSaurel32 101a65f56eeSaurel32 #define SONIC_CR_HTX 0x0001 102a65f56eeSaurel32 #define SONIC_CR_TXP 0x0002 103a65f56eeSaurel32 #define SONIC_CR_RXDIS 0x0004 104a65f56eeSaurel32 #define SONIC_CR_RXEN 0x0008 105a65f56eeSaurel32 #define SONIC_CR_STP 0x0010 106a65f56eeSaurel32 #define SONIC_CR_ST 0x0020 107a65f56eeSaurel32 #define SONIC_CR_RST 0x0080 108a65f56eeSaurel32 #define SONIC_CR_RRRA 0x0100 109a65f56eeSaurel32 #define SONIC_CR_LCAM 0x0200 110a65f56eeSaurel32 #define SONIC_CR_MASK 0x03bf 111a65f56eeSaurel32 112a65f56eeSaurel32 #define SONIC_DCR_DW 0x0020 113a65f56eeSaurel32 #define SONIC_DCR_LBR 0x2000 114a65f56eeSaurel32 #define SONIC_DCR_EXBUS 0x8000 115a65f56eeSaurel32 116a65f56eeSaurel32 #define SONIC_RCR_PRX 0x0001 117a65f56eeSaurel32 #define SONIC_RCR_LBK 0x0002 118a65f56eeSaurel32 #define SONIC_RCR_FAER 0x0004 119a65f56eeSaurel32 #define SONIC_RCR_CRCR 0x0008 120a65f56eeSaurel32 #define SONIC_RCR_CRS 0x0020 121a65f56eeSaurel32 #define SONIC_RCR_LPKT 0x0040 122a65f56eeSaurel32 #define SONIC_RCR_BC 0x0080 123a65f56eeSaurel32 #define SONIC_RCR_MC 0x0100 124a65f56eeSaurel32 #define SONIC_RCR_LB0 0x0200 125a65f56eeSaurel32 #define SONIC_RCR_LB1 0x0400 126a65f56eeSaurel32 #define SONIC_RCR_AMC 0x0800 127a65f56eeSaurel32 #define SONIC_RCR_PRO 0x1000 128a65f56eeSaurel32 #define SONIC_RCR_BRD 0x2000 129a65f56eeSaurel32 #define SONIC_RCR_RNT 0x4000 130a65f56eeSaurel32 131a65f56eeSaurel32 #define SONIC_TCR_PTX 0x0001 132a65f56eeSaurel32 #define SONIC_TCR_BCM 0x0002 133a65f56eeSaurel32 #define SONIC_TCR_FU 0x0004 134a65f56eeSaurel32 #define SONIC_TCR_EXC 0x0040 135a65f56eeSaurel32 #define SONIC_TCR_CRSL 0x0080 136a65f56eeSaurel32 #define SONIC_TCR_NCRS 0x0100 137a65f56eeSaurel32 #define SONIC_TCR_EXD 0x0400 138a65f56eeSaurel32 #define SONIC_TCR_CRCI 0x2000 139a65f56eeSaurel32 #define SONIC_TCR_PINT 0x8000 140a65f56eeSaurel32 141ada74315SFinn Thain #define SONIC_ISR_RBAE 0x0010 142a65f56eeSaurel32 #define SONIC_ISR_RBE 0x0020 143a65f56eeSaurel32 #define SONIC_ISR_RDE 0x0040 144a65f56eeSaurel32 #define SONIC_ISR_TC 0x0080 145a65f56eeSaurel32 #define SONIC_ISR_TXDN 0x0200 146a65f56eeSaurel32 #define SONIC_ISR_PKTRX 0x0400 147a65f56eeSaurel32 #define SONIC_ISR_PINT 0x0800 148a65f56eeSaurel32 #define SONIC_ISR_LCD 0x1000 149a65f56eeSaurel32 15088f632fbSFinn Thain #define SONIC_DESC_EOL 0x0001 15188f632fbSFinn Thain #define SONIC_DESC_ADDR 0xFFFE 15288f632fbSFinn Thain 153104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x" 154*db1015e9SEduardo Habkost typedef struct dp8393xState dp8393xState; 155104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) 156104655a5SHervé Poussineau 157*db1015e9SEduardo Habkost struct dp8393xState { 158104655a5SHervé Poussineau SysBusDevice parent_obj; 159104655a5SHervé Poussineau 160a65f56eeSaurel32 /* Hardware */ 161104655a5SHervé Poussineau uint8_t it_shift; 162be920841SLaurent Vivier bool big_endian; 163c2279bd0SFinn Thain bool last_rba_is_full; 164a65f56eeSaurel32 qemu_irq irq; 165a65f56eeSaurel32 #ifdef DEBUG_SONIC 166a65f56eeSaurel32 int irq_level; 167a65f56eeSaurel32 #endif 168a65f56eeSaurel32 QEMUTimer *watchdog; 169a65f56eeSaurel32 int64_t wt_last_update; 17005f41fe3SMark McLoughlin NICConf conf; 17105f41fe3SMark McLoughlin NICState *nic; 172024e5bb6SAvi Kivity MemoryRegion mmio; 17389ae0ff9SHervé Poussineau MemoryRegion prom; 174a65f56eeSaurel32 175a65f56eeSaurel32 /* Registers */ 176a65f56eeSaurel32 uint8_t cam[16][6]; 177a65f56eeSaurel32 uint16_t regs[0x40]; 178a65f56eeSaurel32 179a65f56eeSaurel32 /* Temporaries */ 180a65f56eeSaurel32 uint8_t tx_buffer[0x10000]; 181af9f0be3SLaurent Vivier uint16_t data[12]; 182a65f56eeSaurel32 int loopback_packet; 183a65f56eeSaurel32 184a65f56eeSaurel32 /* Memory access */ 1853110ce81SMarc-André Lureau MemoryRegion *dma_mr; 186dd820513SHervé Poussineau AddressSpace as; 187*db1015e9SEduardo Habkost }; 188a65f56eeSaurel32 189581f7b12SPeter Maydell /* Accessor functions for values which are formed by 190581f7b12SPeter Maydell * concatenating two 16 bit device registers. By putting these 191581f7b12SPeter Maydell * in their own functions with a uint32_t return type we avoid the 192581f7b12SPeter Maydell * pitfall of implicit sign extension where ((x << 16) | y) is a 193581f7b12SPeter Maydell * signed 32 bit integer that might get sign-extended to a 64 bit integer. 194581f7b12SPeter Maydell */ 195581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s) 196581f7b12SPeter Maydell { 197581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP]; 198581f7b12SPeter Maydell } 199581f7b12SPeter Maydell 200581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s) 201581f7b12SPeter Maydell { 202581f7b12SPeter Maydell return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; 203581f7b12SPeter Maydell } 204581f7b12SPeter Maydell 205581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s) 206581f7b12SPeter Maydell { 20788f632fbSFinn Thain return (s->regs[SONIC_URDA] << 16) | 20888f632fbSFinn Thain (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR); 209581f7b12SPeter Maydell } 210581f7b12SPeter Maydell 211581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s) 212581f7b12SPeter Maydell { 213581f7b12SPeter Maydell return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; 214581f7b12SPeter Maydell } 215581f7b12SPeter Maydell 216581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s) 217581f7b12SPeter Maydell { 218581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP]; 219581f7b12SPeter Maydell } 220581f7b12SPeter Maydell 221581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s) 222581f7b12SPeter Maydell { 223581f7b12SPeter Maydell return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0]; 224581f7b12SPeter Maydell } 225581f7b12SPeter Maydell 226581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s) 227581f7b12SPeter Maydell { 22888f632fbSFinn Thain return (s->regs[SONIC_UTDA] << 16) | 22988f632fbSFinn Thain (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR); 230581f7b12SPeter Maydell } 231581f7b12SPeter Maydell 232581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s) 233581f7b12SPeter Maydell { 234581f7b12SPeter Maydell return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 235581f7b12SPeter Maydell } 236581f7b12SPeter Maydell 237af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset) 238be920841SLaurent Vivier { 239be920841SLaurent Vivier uint16_t val; 240be920841SLaurent Vivier 241be920841SLaurent Vivier if (s->big_endian) { 242af9f0be3SLaurent Vivier val = be16_to_cpu(s->data[offset * width + width - 1]); 243be920841SLaurent Vivier } else { 244af9f0be3SLaurent Vivier val = le16_to_cpu(s->data[offset * width]); 245be920841SLaurent Vivier } 246be920841SLaurent Vivier return val; 247be920841SLaurent Vivier } 248be920841SLaurent Vivier 249af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset, 250be920841SLaurent Vivier uint16_t val) 251be920841SLaurent Vivier { 252be920841SLaurent Vivier if (s->big_endian) { 2533fe9a838SFinn Thain if (width == 2) { 2543fe9a838SFinn Thain s->data[offset * 2] = 0; 2553fe9a838SFinn Thain s->data[offset * 2 + 1] = cpu_to_be16(val); 256be920841SLaurent Vivier } else { 2573fe9a838SFinn Thain s->data[offset] = cpu_to_be16(val); 2583fe9a838SFinn Thain } 2593fe9a838SFinn Thain } else { 2603fe9a838SFinn Thain if (width == 2) { 2613fe9a838SFinn Thain s->data[offset * 2] = cpu_to_le16(val); 2623fe9a838SFinn Thain s->data[offset * 2 + 1] = 0; 2633fe9a838SFinn Thain } else { 2643fe9a838SFinn Thain s->data[offset] = cpu_to_le16(val); 2653fe9a838SFinn Thain } 266be920841SLaurent Vivier } 267be920841SLaurent Vivier } 268be920841SLaurent Vivier 269a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s) 270a65f56eeSaurel32 { 271a65f56eeSaurel32 int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; 272a65f56eeSaurel32 273a65f56eeSaurel32 #ifdef DEBUG_SONIC 274a65f56eeSaurel32 if (level != s->irq_level) { 275a65f56eeSaurel32 s->irq_level = level; 276a65f56eeSaurel32 if (level) { 277a65f56eeSaurel32 DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]); 278a65f56eeSaurel32 } else { 279a65f56eeSaurel32 DPRINTF("lower irq\n"); 280a65f56eeSaurel32 } 281a65f56eeSaurel32 } 282a65f56eeSaurel32 #endif 283a65f56eeSaurel32 284a65f56eeSaurel32 qemu_set_irq(s->irq, level); 285a65f56eeSaurel32 } 286a65f56eeSaurel32 2873df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s) 288a65f56eeSaurel32 { 289a65f56eeSaurel32 int width, size; 290a65f56eeSaurel32 uint16_t index = 0; 291a65f56eeSaurel32 292a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 293a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 294a65f56eeSaurel32 295a65f56eeSaurel32 while (s->regs[SONIC_CDC] & 0x1f) { 296a65f56eeSaurel32 /* Fill current entry */ 29719f70347SPeter Maydell address_space_read(&s->as, dp8393x_cdp(s), 29819f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 299af9f0be3SLaurent Vivier s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; 300af9f0be3SLaurent Vivier s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; 301af9f0be3SLaurent Vivier s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; 302af9f0be3SLaurent Vivier s->cam[index][3] = dp8393x_get(s, width, 2) >> 8; 303af9f0be3SLaurent Vivier s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff; 304af9f0be3SLaurent Vivier s->cam[index][5] = dp8393x_get(s, width, 3) >> 8; 305a65f56eeSaurel32 DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index, 306a65f56eeSaurel32 s->cam[index][0], s->cam[index][1], s->cam[index][2], 307a65f56eeSaurel32 s->cam[index][3], s->cam[index][4], s->cam[index][5]); 308a65f56eeSaurel32 /* Move to next entry */ 309a65f56eeSaurel32 s->regs[SONIC_CDC]--; 310a65f56eeSaurel32 s->regs[SONIC_CDP] += size; 311a65f56eeSaurel32 index++; 312a65f56eeSaurel32 } 313a65f56eeSaurel32 314a65f56eeSaurel32 /* Read CAM enable */ 31519f70347SPeter Maydell address_space_read(&s->as, dp8393x_cdp(s), 31619f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 317af9f0be3SLaurent Vivier s->regs[SONIC_CE] = dp8393x_get(s, width, 0); 318a65f56eeSaurel32 DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); 319a65f56eeSaurel32 320a65f56eeSaurel32 /* Done */ 321a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; 322a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_LCD; 323a65f56eeSaurel32 dp8393x_update_irq(s); 324a65f56eeSaurel32 } 325a65f56eeSaurel32 3263df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s) 327a65f56eeSaurel32 { 328a65f56eeSaurel32 int width, size; 329a65f56eeSaurel32 330a65f56eeSaurel32 /* Read memory */ 331a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 332a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 33319f70347SPeter Maydell address_space_read(&s->as, dp8393x_rrp(s), 33419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 335a65f56eeSaurel32 336a65f56eeSaurel32 /* Update SONIC registers */ 337af9f0be3SLaurent Vivier s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0); 338af9f0be3SLaurent Vivier s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1); 339af9f0be3SLaurent Vivier s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2); 340af9f0be3SLaurent Vivier s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3); 341a65f56eeSaurel32 DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n", 342a65f56eeSaurel32 s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], 343a65f56eeSaurel32 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); 344a65f56eeSaurel32 345a65f56eeSaurel32 /* Go to next entry */ 346a65f56eeSaurel32 s->regs[SONIC_RRP] += size; 347a65f56eeSaurel32 348a65f56eeSaurel32 /* Handle wrap */ 349a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { 350a65f56eeSaurel32 s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; 351a65f56eeSaurel32 } 352a65f56eeSaurel32 353c2279bd0SFinn Thain /* Warn the host if CRBA now has the last available resource */ 354a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) 355a65f56eeSaurel32 { 356a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RBE; 357a65f56eeSaurel32 dp8393x_update_irq(s); 358a65f56eeSaurel32 } 359c2279bd0SFinn Thain 360c2279bd0SFinn Thain /* Allow packet reception */ 361c2279bd0SFinn Thain s->last_rba_is_full = false; 362a65f56eeSaurel32 } 363a65f56eeSaurel32 3643df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s) 365a65f56eeSaurel32 { 366bc72ad67SAlex Bligh timer_del(s->watchdog); 367a65f56eeSaurel32 368a65f56eeSaurel32 s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX); 369a65f56eeSaurel32 s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; 370a65f56eeSaurel32 } 371a65f56eeSaurel32 3723df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s) 373a65f56eeSaurel32 { 374a65f56eeSaurel32 uint32_t ticks; 375a65f56eeSaurel32 int64_t delay; 376a65f56eeSaurel32 377a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 378bc72ad67SAlex Bligh timer_del(s->watchdog); 379a65f56eeSaurel32 return; 380a65f56eeSaurel32 } 381a65f56eeSaurel32 382581f7b12SPeter Maydell ticks = dp8393x_wt(s); 383bc72ad67SAlex Bligh s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 38473bcb24dSRutuja Shah delay = NANOSECONDS_PER_SECOND * ticks / 5000000; 385bc72ad67SAlex Bligh timer_mod(s->watchdog, s->wt_last_update + delay); 386a65f56eeSaurel32 } 387a65f56eeSaurel32 3883df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s) 389a65f56eeSaurel32 { 390a65f56eeSaurel32 int64_t elapsed; 391a65f56eeSaurel32 uint32_t val; 392a65f56eeSaurel32 393a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 394bc72ad67SAlex Bligh timer_del(s->watchdog); 395a65f56eeSaurel32 return; 396a65f56eeSaurel32 } 397a65f56eeSaurel32 398bc72ad67SAlex Bligh elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 399581f7b12SPeter Maydell val = dp8393x_wt(s); 400a65f56eeSaurel32 val -= elapsed / 5000000; 401a65f56eeSaurel32 s->regs[SONIC_WT1] = (val >> 16) & 0xffff; 402a65f56eeSaurel32 s->regs[SONIC_WT0] = (val >> 0) & 0xffff; 4033df5de64SHervé Poussineau dp8393x_set_next_tick(s); 404a65f56eeSaurel32 405a65f56eeSaurel32 } 406a65f56eeSaurel32 4073df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s) 408a65f56eeSaurel32 { 409a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_STP; 4103df5de64SHervé Poussineau dp8393x_set_next_tick(s); 411a65f56eeSaurel32 } 412a65f56eeSaurel32 4133df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s) 414a65f56eeSaurel32 { 415a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_ST; 4163df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 417a65f56eeSaurel32 } 418a65f56eeSaurel32 419b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc); 4204594f93aSFam Zheng 4213df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s) 422a65f56eeSaurel32 { 423a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; 4244594f93aSFam Zheng if (dp8393x_can_receive(s->nic->ncs)) { 4254594f93aSFam Zheng qemu_flush_queued_packets(qemu_get_queue(s->nic)); 4264594f93aSFam Zheng } 427a65f56eeSaurel32 } 428a65f56eeSaurel32 4293df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s) 430a65f56eeSaurel32 { 431a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; 432a65f56eeSaurel32 } 433a65f56eeSaurel32 4343df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s) 435a65f56eeSaurel32 { 436b356f76dSJason Wang NetClientState *nc = qemu_get_queue(s->nic); 437a65f56eeSaurel32 int width, size; 438a65f56eeSaurel32 int tx_len, len; 439a65f56eeSaurel32 uint16_t i; 440a65f56eeSaurel32 441a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 442a65f56eeSaurel32 443a65f56eeSaurel32 while (1) { 444a65f56eeSaurel32 /* Read memory */ 445a65f56eeSaurel32 size = sizeof(uint16_t) * 6 * width; 446a65f56eeSaurel32 s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; 447581f7b12SPeter Maydell DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s)); 44819f70347SPeter Maydell address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, 44919f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 450a65f56eeSaurel32 tx_len = 0; 451a65f56eeSaurel32 452a65f56eeSaurel32 /* Update registers */ 453af9f0be3SLaurent Vivier s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000; 454af9f0be3SLaurent Vivier s->regs[SONIC_TPS] = dp8393x_get(s, width, 1); 455af9f0be3SLaurent Vivier s->regs[SONIC_TFC] = dp8393x_get(s, width, 2); 456af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3); 457af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4); 458af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 5); 459a65f56eeSaurel32 460a65f56eeSaurel32 /* Handle programmable interrupt */ 461a65f56eeSaurel32 if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { 462a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PINT; 463a65f56eeSaurel32 } else { 464a65f56eeSaurel32 s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; 465a65f56eeSaurel32 } 466a65f56eeSaurel32 467a65f56eeSaurel32 for (i = 0; i < s->regs[SONIC_TFC]; ) { 468a65f56eeSaurel32 /* Append fragment */ 469a65f56eeSaurel32 len = s->regs[SONIC_TFS]; 470a65f56eeSaurel32 if (tx_len + len > sizeof(s->tx_buffer)) { 471a65f56eeSaurel32 len = sizeof(s->tx_buffer) - tx_len; 472a65f56eeSaurel32 } 47319f70347SPeter Maydell address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED, 47419f70347SPeter Maydell &s->tx_buffer[tx_len], len); 475a65f56eeSaurel32 tx_len += len; 476a65f56eeSaurel32 477a65f56eeSaurel32 i++; 478a65f56eeSaurel32 if (i != s->regs[SONIC_TFC]) { 479a65f56eeSaurel32 /* Read next fragment details */ 480a65f56eeSaurel32 size = sizeof(uint16_t) * 3 * width; 48119f70347SPeter Maydell address_space_read(&s->as, 48219f70347SPeter Maydell dp8393x_ttda(s) 48319f70347SPeter Maydell + sizeof(uint16_t) * width * (4 + 3 * i), 48419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, 48519f70347SPeter Maydell size); 486af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0); 487af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1); 488af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 2); 489a65f56eeSaurel32 } 490a65f56eeSaurel32 } 491a65f56eeSaurel32 492a65f56eeSaurel32 /* Handle Ethernet checksum */ 493a65f56eeSaurel32 if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { 494a65f56eeSaurel32 /* Don't append FCS there, to look like slirp packets 495a65f56eeSaurel32 * which don't have one */ 496a65f56eeSaurel32 } else { 497a65f56eeSaurel32 /* Remove existing FCS */ 498a65f56eeSaurel32 tx_len -= 4; 499a65f56eeSaurel32 } 500a65f56eeSaurel32 501a65f56eeSaurel32 if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { 502a65f56eeSaurel32 /* Loopback */ 503a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; 504b356f76dSJason Wang if (nc->info->can_receive(nc)) { 505a65f56eeSaurel32 s->loopback_packet = 1; 506b356f76dSJason Wang nc->info->receive(nc, s->tx_buffer, tx_len); 507a65f56eeSaurel32 } 508a65f56eeSaurel32 } else { 509a65f56eeSaurel32 /* Transmit packet */ 510b356f76dSJason Wang qemu_send_packet(nc, s->tx_buffer, tx_len); 511a65f56eeSaurel32 } 512a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_PTX; 513a65f56eeSaurel32 514a65f56eeSaurel32 /* Write status */ 515af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, 516be920841SLaurent Vivier s->regs[SONIC_TCR] & 0x0fff); /* status */ 517a65f56eeSaurel32 size = sizeof(uint16_t) * width; 51819f70347SPeter Maydell address_space_write(&s->as, dp8393x_ttda(s), 51919f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 520a65f56eeSaurel32 521a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { 522a65f56eeSaurel32 /* Read footer of packet */ 523a65f56eeSaurel32 size = sizeof(uint16_t) * width; 52419f70347SPeter Maydell address_space_read(&s->as, 52519f70347SPeter Maydell dp8393x_ttda(s) 52619f70347SPeter Maydell + sizeof(uint16_t) * width 52719f70347SPeter Maydell * (4 + 3 * s->regs[SONIC_TFC]), 52819f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, 52919f70347SPeter Maydell size); 530a0cf4297SFinn Thain s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0); 531a0cf4297SFinn Thain if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) { 532a65f56eeSaurel32 /* EOL detected */ 533a65f56eeSaurel32 break; 534a65f56eeSaurel32 } 535a65f56eeSaurel32 } 536a65f56eeSaurel32 } 537a65f56eeSaurel32 538a65f56eeSaurel32 /* Done */ 539a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_TXP; 540a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; 541a65f56eeSaurel32 dp8393x_update_irq(s); 542a65f56eeSaurel32 } 543a65f56eeSaurel32 5443df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s) 545a65f56eeSaurel32 { 546a65f56eeSaurel32 /* Nothing to do */ 547a65f56eeSaurel32 } 548a65f56eeSaurel32 5493df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command) 550a65f56eeSaurel32 { 551a65f56eeSaurel32 if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { 552a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RST; 553a65f56eeSaurel32 return; 554a65f56eeSaurel32 } 555a65f56eeSaurel32 556a65f56eeSaurel32 s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); 557a65f56eeSaurel32 558a65f56eeSaurel32 if (command & SONIC_CR_HTX) 5593df5de64SHervé Poussineau dp8393x_do_halt_transmission(s); 560a65f56eeSaurel32 if (command & SONIC_CR_TXP) 5613df5de64SHervé Poussineau dp8393x_do_transmit_packets(s); 562a65f56eeSaurel32 if (command & SONIC_CR_RXDIS) 5633df5de64SHervé Poussineau dp8393x_do_receiver_disable(s); 564a65f56eeSaurel32 if (command & SONIC_CR_RXEN) 5653df5de64SHervé Poussineau dp8393x_do_receiver_enable(s); 566a65f56eeSaurel32 if (command & SONIC_CR_STP) 5673df5de64SHervé Poussineau dp8393x_do_stop_timer(s); 568a65f56eeSaurel32 if (command & SONIC_CR_ST) 5693df5de64SHervé Poussineau dp8393x_do_start_timer(s); 570a65f56eeSaurel32 if (command & SONIC_CR_RST) 5713df5de64SHervé Poussineau dp8393x_do_software_reset(s); 572a3cce282SFinn Thain if (command & SONIC_CR_RRRA) { 5733df5de64SHervé Poussineau dp8393x_do_read_rra(s); 574a3cce282SFinn Thain s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; 575a3cce282SFinn Thain } 576a65f56eeSaurel32 if (command & SONIC_CR_LCAM) 5773df5de64SHervé Poussineau dp8393x_do_load_cam(s); 578a65f56eeSaurel32 } 579a65f56eeSaurel32 58084689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) 581a65f56eeSaurel32 { 58284689cbbSHervé Poussineau dp8393xState *s = opaque; 58384689cbbSHervé Poussineau int reg = addr >> s->it_shift; 584a65f56eeSaurel32 uint16_t val = 0; 585a65f56eeSaurel32 586a65f56eeSaurel32 switch (reg) { 587a65f56eeSaurel32 /* Update data before reading it */ 588a65f56eeSaurel32 case SONIC_WT0: 589a65f56eeSaurel32 case SONIC_WT1: 5903df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 591a65f56eeSaurel32 val = s->regs[reg]; 592a65f56eeSaurel32 break; 593a65f56eeSaurel32 /* Accept read to some registers only when in reset mode */ 594a65f56eeSaurel32 case SONIC_CAP2: 595a65f56eeSaurel32 case SONIC_CAP1: 596a65f56eeSaurel32 case SONIC_CAP0: 597a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 598a65f56eeSaurel32 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8; 599a65f56eeSaurel32 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)]; 600a65f56eeSaurel32 } 601a65f56eeSaurel32 break; 602a65f56eeSaurel32 /* All other registers have no special contrainst */ 603a65f56eeSaurel32 default: 604a65f56eeSaurel32 val = s->regs[reg]; 605a65f56eeSaurel32 } 606a65f56eeSaurel32 607a65f56eeSaurel32 DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); 608a65f56eeSaurel32 6093fe9a838SFinn Thain return s->big_endian ? val << 16 : val; 610a65f56eeSaurel32 } 611a65f56eeSaurel32 61284689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, 61384689cbbSHervé Poussineau unsigned int size) 614a65f56eeSaurel32 { 61584689cbbSHervé Poussineau dp8393xState *s = opaque; 61684689cbbSHervé Poussineau int reg = addr >> s->it_shift; 6173fe9a838SFinn Thain uint32_t val = s->big_endian ? data >> 16 : data; 61884689cbbSHervé Poussineau 6193fe9a838SFinn Thain DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]); 620a65f56eeSaurel32 621a65f56eeSaurel32 switch (reg) { 622a65f56eeSaurel32 /* Command register */ 623a65f56eeSaurel32 case SONIC_CR: 6243fe9a838SFinn Thain dp8393x_do_command(s, val); 625a65f56eeSaurel32 break; 626a65f56eeSaurel32 /* Prevent write to read-only registers */ 627a65f56eeSaurel32 case SONIC_CAP2: 628a65f56eeSaurel32 case SONIC_CAP1: 629a65f56eeSaurel32 case SONIC_CAP0: 630a65f56eeSaurel32 case SONIC_SR: 631a65f56eeSaurel32 case SONIC_MDT: 632a65f56eeSaurel32 DPRINTF("writing to reg %d invalid\n", reg); 633a65f56eeSaurel32 break; 634a65f56eeSaurel32 /* Accept write to some registers only when in reset mode */ 635a65f56eeSaurel32 case SONIC_DCR: 636a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 6373fe9a838SFinn Thain s->regs[reg] = val & 0xbfff; 638a65f56eeSaurel32 } else { 639a65f56eeSaurel32 DPRINTF("writing to DCR invalid\n"); 640a65f56eeSaurel32 } 641a65f56eeSaurel32 break; 642a65f56eeSaurel32 case SONIC_DCR2: 643a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 6443fe9a838SFinn Thain s->regs[reg] = val & 0xf017; 645a65f56eeSaurel32 } else { 646a65f56eeSaurel32 DPRINTF("writing to DCR2 invalid\n"); 647a65f56eeSaurel32 } 648a65f56eeSaurel32 break; 649a65f56eeSaurel32 /* 12 lower bytes are Read Only */ 650a65f56eeSaurel32 case SONIC_TCR: 6513fe9a838SFinn Thain s->regs[reg] = val & 0xf000; 652a65f56eeSaurel32 break; 653a65f56eeSaurel32 /* 9 lower bytes are Read Only */ 654a65f56eeSaurel32 case SONIC_RCR: 6553fe9a838SFinn Thain s->regs[reg] = val & 0xffe0; 656a65f56eeSaurel32 break; 657a65f56eeSaurel32 /* Ignore most significant bit */ 658a65f56eeSaurel32 case SONIC_IMR: 6593fe9a838SFinn Thain s->regs[reg] = val & 0x7fff; 660a65f56eeSaurel32 dp8393x_update_irq(s); 661a65f56eeSaurel32 break; 662a65f56eeSaurel32 /* Clear bits by writing 1 to them */ 663a65f56eeSaurel32 case SONIC_ISR: 6643fe9a838SFinn Thain val &= s->regs[reg]; 6653fe9a838SFinn Thain s->regs[reg] &= ~val; 6663fe9a838SFinn Thain if (val & SONIC_ISR_RBE) { 6673df5de64SHervé Poussineau dp8393x_do_read_rra(s); 668a65f56eeSaurel32 } 669a65f56eeSaurel32 dp8393x_update_irq(s); 670a65f56eeSaurel32 break; 671ea227027SFinn Thain /* The guest is required to store aligned pointers here */ 672a65f56eeSaurel32 case SONIC_RSA: 673a65f56eeSaurel32 case SONIC_REA: 674a65f56eeSaurel32 case SONIC_RRP: 675a65f56eeSaurel32 case SONIC_RWP: 676ea227027SFinn Thain if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { 677ea227027SFinn Thain s->regs[reg] = val & 0xfffc; 678ea227027SFinn Thain } else { 6793fe9a838SFinn Thain s->regs[reg] = val & 0xfffe; 680ea227027SFinn Thain } 681a65f56eeSaurel32 break; 682a65f56eeSaurel32 /* Invert written value for some registers */ 683a65f56eeSaurel32 case SONIC_CRCT: 684a65f56eeSaurel32 case SONIC_FAET: 685a65f56eeSaurel32 case SONIC_MPT: 6863fe9a838SFinn Thain s->regs[reg] = val ^ 0xffff; 687a65f56eeSaurel32 break; 688a65f56eeSaurel32 /* All other registers have no special contrainst */ 689a65f56eeSaurel32 default: 6903fe9a838SFinn Thain s->regs[reg] = val; 691a65f56eeSaurel32 } 692a65f56eeSaurel32 693a65f56eeSaurel32 if (reg == SONIC_WT0 || reg == SONIC_WT1) { 6943df5de64SHervé Poussineau dp8393x_set_next_tick(s); 695a65f56eeSaurel32 } 696a65f56eeSaurel32 } 697a65f56eeSaurel32 69884689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = { 69984689cbbSHervé Poussineau .read = dp8393x_read, 70084689cbbSHervé Poussineau .write = dp8393x_write, 7013fe9a838SFinn Thain .impl.min_access_size = 4, 7023fe9a838SFinn Thain .impl.max_access_size = 4, 70384689cbbSHervé Poussineau .endianness = DEVICE_NATIVE_ENDIAN, 70484689cbbSHervé Poussineau }; 70584689cbbSHervé Poussineau 706a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque) 707a65f56eeSaurel32 { 708a65f56eeSaurel32 dp8393xState *s = opaque; 709a65f56eeSaurel32 710a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 711a65f56eeSaurel32 return; 712a65f56eeSaurel32 } 713a65f56eeSaurel32 714a65f56eeSaurel32 s->regs[SONIC_WT1] = 0xffff; 715a65f56eeSaurel32 s->regs[SONIC_WT0] = 0xffff; 7163df5de64SHervé Poussineau dp8393x_set_next_tick(s); 717a65f56eeSaurel32 718a65f56eeSaurel32 /* Signal underflow */ 719a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TC; 720a65f56eeSaurel32 dp8393x_update_irq(s); 721a65f56eeSaurel32 } 722a65f56eeSaurel32 723b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc) 724a65f56eeSaurel32 { 725cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 726a65f56eeSaurel32 727b8c4b67eSPhilippe Mathieu-Daudé return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN); 728a65f56eeSaurel32 } 729a65f56eeSaurel32 7303df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, 7313df5de64SHervé Poussineau int size) 732a65f56eeSaurel32 { 733a65f56eeSaurel32 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 734a65f56eeSaurel32 int i; 735a65f56eeSaurel32 736a65f56eeSaurel32 /* Check promiscuous mode */ 737a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { 738a65f56eeSaurel32 return 0; 739a65f56eeSaurel32 } 740a65f56eeSaurel32 741a65f56eeSaurel32 /* Check multicast packets */ 742a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { 743a65f56eeSaurel32 return SONIC_RCR_MC; 744a65f56eeSaurel32 } 745a65f56eeSaurel32 746a65f56eeSaurel32 /* Check broadcast */ 747a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) { 748a65f56eeSaurel32 return SONIC_RCR_BC; 749a65f56eeSaurel32 } 750a65f56eeSaurel32 751a65f56eeSaurel32 /* Check CAM */ 752a65f56eeSaurel32 for (i = 0; i < 16; i++) { 753a65f56eeSaurel32 if (s->regs[SONIC_CE] & (1 << i)) { 754a65f56eeSaurel32 /* Entry enabled */ 755a65f56eeSaurel32 if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) { 756a65f56eeSaurel32 return 0; 757a65f56eeSaurel32 } 758a65f56eeSaurel32 } 759a65f56eeSaurel32 } 760a65f56eeSaurel32 761a65f56eeSaurel32 return -1; 762a65f56eeSaurel32 } 763a65f56eeSaurel32 7643df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, 7659e3cd456SFinn Thain size_t pkt_size) 766a65f56eeSaurel32 { 767cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 768a65f56eeSaurel32 int packet_type; 769a65f56eeSaurel32 uint32_t available, address; 770350e7d9aSFinn Thain int width, rx_len, padded_len; 771a65f56eeSaurel32 uint32_t checksum; 7729e3cd456SFinn Thain int size; 773a65f56eeSaurel32 774a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | 775a65f56eeSaurel32 SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); 776a65f56eeSaurel32 777c2279bd0SFinn Thain if (s->last_rba_is_full) { 778c2279bd0SFinn Thain return pkt_size; 779c2279bd0SFinn Thain } 780c2279bd0SFinn Thain 781350e7d9aSFinn Thain rx_len = pkt_size + sizeof(checksum); 782350e7d9aSFinn Thain if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { 783350e7d9aSFinn Thain width = 2; 784350e7d9aSFinn Thain padded_len = ((rx_len - 1) | 3) + 1; 785350e7d9aSFinn Thain } else { 786350e7d9aSFinn Thain width = 1; 787350e7d9aSFinn Thain padded_len = ((rx_len - 1) | 1) + 1; 788350e7d9aSFinn Thain } 789350e7d9aSFinn Thain 790350e7d9aSFinn Thain if (padded_len > dp8393x_rbwc(s) * 2) { 791ada74315SFinn Thain DPRINTF("oversize packet, pkt_size is %d\n", pkt_size); 792ada74315SFinn Thain s->regs[SONIC_ISR] |= SONIC_ISR_RBAE; 793ada74315SFinn Thain dp8393x_update_irq(s); 794c2279bd0SFinn Thain s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; 795c2279bd0SFinn Thain goto done; 796ada74315SFinn Thain } 797ada74315SFinn Thain 7989e3cd456SFinn Thain packet_type = dp8393x_receive_filter(s, buf, pkt_size); 799a65f56eeSaurel32 if (packet_type < 0) { 800a65f56eeSaurel32 DPRINTF("packet not for netcard\n"); 8014f1c942bSMark McLoughlin return -1; 802a65f56eeSaurel32 } 803a65f56eeSaurel32 804a65f56eeSaurel32 /* Check for EOL */ 80588f632fbSFinn Thain if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { 806a65f56eeSaurel32 /* Are we still in resource exhaustion? */ 807a65f56eeSaurel32 size = sizeof(uint16_t) * 1 * width; 808581f7b12SPeter Maydell address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; 80919f70347SPeter Maydell address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED, 81019f70347SPeter Maydell s->data, size); 8115b0c98fcSFinn Thain s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); 8125b0c98fcSFinn Thain if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { 813a65f56eeSaurel32 /* Still EOL ; stop reception */ 8144f1c942bSMark McLoughlin return -1; 815a65f56eeSaurel32 } 8165b0c98fcSFinn Thain /* Link has been updated by host */ 817d9fae131SFinn Thain 818d9fae131SFinn Thain /* Clear in_use */ 819d9fae131SFinn Thain size = sizeof(uint16_t) * width; 820d9fae131SFinn Thain address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; 821d9fae131SFinn Thain dp8393x_put(s, width, 0, 0); 822d9fae131SFinn Thain address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, 823d9fae131SFinn Thain (uint8_t *)s->data, size, 1); 824d9fae131SFinn Thain 825d9fae131SFinn Thain /* Move to next descriptor */ 8265b0c98fcSFinn Thain s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 827d9fae131SFinn Thain s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; 828a65f56eeSaurel32 } 829a65f56eeSaurel32 830a65f56eeSaurel32 /* Save current position */ 831a65f56eeSaurel32 s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; 832a65f56eeSaurel32 s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; 833a65f56eeSaurel32 834a65f56eeSaurel32 /* Calculate the ethernet checksum */ 835350e7d9aSFinn Thain checksum = cpu_to_le32(crc32(0, buf, pkt_size)); 836a65f56eeSaurel32 837a65f56eeSaurel32 /* Put packet into RBA */ 838581f7b12SPeter Maydell DPRINTF("Receive packet at %08x\n", dp8393x_crba(s)); 839581f7b12SPeter Maydell address = dp8393x_crba(s); 84019f70347SPeter Maydell address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, 841350e7d9aSFinn Thain buf, pkt_size); 842350e7d9aSFinn Thain address += pkt_size; 843350e7d9aSFinn Thain 844350e7d9aSFinn Thain /* Put frame checksum into RBA */ 84519f70347SPeter Maydell address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, 846350e7d9aSFinn Thain &checksum, sizeof(checksum)); 847350e7d9aSFinn Thain address += sizeof(checksum); 848350e7d9aSFinn Thain 849350e7d9aSFinn Thain /* Pad short packets to keep pointers aligned */ 850350e7d9aSFinn Thain if (rx_len < padded_len) { 851350e7d9aSFinn Thain size = padded_len - rx_len; 852350e7d9aSFinn Thain address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, 853350e7d9aSFinn Thain (uint8_t *)"\xFF\xFF\xFF", size, 1); 854350e7d9aSFinn Thain address += size; 855350e7d9aSFinn Thain } 856350e7d9aSFinn Thain 857a65f56eeSaurel32 s->regs[SONIC_CRBA1] = address >> 16; 858a65f56eeSaurel32 s->regs[SONIC_CRBA0] = address & 0xffff; 859581f7b12SPeter Maydell available = dp8393x_rbwc(s); 860350e7d9aSFinn Thain available -= padded_len >> 1; 861a65f56eeSaurel32 s->regs[SONIC_RBWC1] = available >> 16; 862a65f56eeSaurel32 s->regs[SONIC_RBWC0] = available & 0xffff; 863a65f56eeSaurel32 864a65f56eeSaurel32 /* Update status */ 865581f7b12SPeter Maydell if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) { 866a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; 867a65f56eeSaurel32 } 868a65f56eeSaurel32 s->regs[SONIC_RCR] |= packet_type; 869a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_PRX; 870a65f56eeSaurel32 if (s->loopback_packet) { 871a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_LBK; 872a65f56eeSaurel32 s->loopback_packet = 0; 873a65f56eeSaurel32 } 874a65f56eeSaurel32 875a65f56eeSaurel32 /* Write status to memory */ 876581f7b12SPeter Maydell DPRINTF("Write status at %08x\n", dp8393x_crda(s)); 877af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */ 878af9f0be3SLaurent Vivier dp8393x_put(s, width, 1, rx_len); /* byte count */ 879af9f0be3SLaurent Vivier dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */ 880af9f0be3SLaurent Vivier dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */ 881af9f0be3SLaurent Vivier dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */ 882a65f56eeSaurel32 size = sizeof(uint16_t) * 5 * width; 88319f70347SPeter Maydell address_space_write(&s->as, dp8393x_crda(s), 88419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, 88519f70347SPeter Maydell s->data, size); 886a65f56eeSaurel32 8875b0c98fcSFinn Thain /* Check link field */ 888a65f56eeSaurel32 size = sizeof(uint16_t) * width; 88919f70347SPeter Maydell address_space_read(&s->as, 89019f70347SPeter Maydell dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, 89119f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 892af9f0be3SLaurent Vivier s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); 89388f632fbSFinn Thain if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { 894a65f56eeSaurel32 /* EOL detected */ 895a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RDE; 896a65f56eeSaurel32 } else { 89746ffee9aSFinn Thain /* Clear in_use */ 89846ffee9aSFinn Thain size = sizeof(uint16_t) * width; 89946ffee9aSFinn Thain address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; 90046ffee9aSFinn Thain dp8393x_put(s, width, 0, 0); 90146ffee9aSFinn Thain address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, 90246ffee9aSFinn Thain s->data, size); 9035b0c98fcSFinn Thain 9045b0c98fcSFinn Thain /* Move to next descriptor */ 905a65f56eeSaurel32 s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; 906a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; 90780b60673SFinn Thain } 90880b60673SFinn Thain 909c2279bd0SFinn Thain dp8393x_update_irq(s); 910c2279bd0SFinn Thain 91180b60673SFinn Thain s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | 91280b60673SFinn Thain ((s->regs[SONIC_RSC] + 1) & 0x00ff); 913a65f56eeSaurel32 914c2279bd0SFinn Thain done: 915c2279bd0SFinn Thain 916a65f56eeSaurel32 if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { 917c2279bd0SFinn Thain if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) { 918c2279bd0SFinn Thain /* Stop packet reception */ 919c2279bd0SFinn Thain s->last_rba_is_full = true; 920c2279bd0SFinn Thain } else { 921c2279bd0SFinn Thain /* Read next resource */ 9223df5de64SHervé Poussineau dp8393x_do_read_rra(s); 923a65f56eeSaurel32 } 924c2279bd0SFinn Thain } 9254f1c942bSMark McLoughlin 9269e3cd456SFinn Thain return pkt_size; 927a65f56eeSaurel32 } 928a65f56eeSaurel32 929104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev) 930a65f56eeSaurel32 { 931104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 932bc72ad67SAlex Bligh timer_del(s->watchdog); 933a65f56eeSaurel32 934bd8f1ebcSHervé Poussineau memset(s->regs, 0, sizeof(s->regs)); 935083e21bbSFinn Thain s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */ 936a65f56eeSaurel32 s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; 937a65f56eeSaurel32 s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); 938a65f56eeSaurel32 s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); 939a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; 940a65f56eeSaurel32 s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; 941a65f56eeSaurel32 s->regs[SONIC_IMR] = 0; 942a65f56eeSaurel32 s->regs[SONIC_ISR] = 0; 943a65f56eeSaurel32 s->regs[SONIC_DCR2] = 0; 944a65f56eeSaurel32 s->regs[SONIC_EOBC] = 0x02F8; 945a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 946a65f56eeSaurel32 s->regs[SONIC_CE] = 0; 947a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 948a65f56eeSaurel32 949a65f56eeSaurel32 /* Network cable is connected */ 950a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_CRS; 951a65f56eeSaurel32 952a65f56eeSaurel32 dp8393x_update_irq(s); 953a65f56eeSaurel32 } 954a65f56eeSaurel32 95505f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = { 956f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 95705f41fe3SMark McLoughlin .size = sizeof(NICState), 9583df5de64SHervé Poussineau .can_receive = dp8393x_can_receive, 9593df5de64SHervé Poussineau .receive = dp8393x_receive, 96005f41fe3SMark McLoughlin }; 96105f41fe3SMark McLoughlin 962104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj) 963a65f56eeSaurel32 { 964104655a5SHervé Poussineau SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 965104655a5SHervé Poussineau dp8393xState *s = DP8393X(obj); 966a65f56eeSaurel32 967104655a5SHervé Poussineau sysbus_init_mmio(sbd, &s->mmio); 96889ae0ff9SHervé Poussineau sysbus_init_mmio(sbd, &s->prom); 969104655a5SHervé Poussineau sysbus_init_irq(sbd, &s->irq); 970104655a5SHervé Poussineau } 971a65f56eeSaurel32 972104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp) 973104655a5SHervé Poussineau { 974104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 97589ae0ff9SHervé Poussineau int i, checksum; 97689ae0ff9SHervé Poussineau uint8_t *prom; 97752579c68SHervé Poussineau Error *local_err = NULL; 978a65f56eeSaurel32 979104655a5SHervé Poussineau address_space_init(&s->as, s->dma_mr, "dp8393x"); 980104655a5SHervé Poussineau memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, 981104655a5SHervé Poussineau "dp8393x-regs", 0x40 << s->it_shift); 982104655a5SHervé Poussineau 983104655a5SHervé Poussineau s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, 984104655a5SHervé Poussineau object_get_typename(OBJECT(dev)), dev->id, s); 985104655a5SHervé Poussineau qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 986104655a5SHervé Poussineau 987bc72ad67SAlex Bligh s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); 98889ae0ff9SHervé Poussineau 989fcd3b085SPhilippe Mathieu-Daudé memory_region_init_rom(&s->prom, OBJECT(dev), "dp8393x-prom", 990fcd3b085SPhilippe Mathieu-Daudé SONIC_PROM_SIZE, &local_err); 99152579c68SHervé Poussineau if (local_err) { 99252579c68SHervé Poussineau error_propagate(errp, local_err); 99352579c68SHervé Poussineau return; 99452579c68SHervé Poussineau } 99589ae0ff9SHervé Poussineau prom = memory_region_get_ram_ptr(&s->prom); 99689ae0ff9SHervé Poussineau checksum = 0; 99789ae0ff9SHervé Poussineau for (i = 0; i < 6; i++) { 99889ae0ff9SHervé Poussineau prom[i] = s->conf.macaddr.a[i]; 99989ae0ff9SHervé Poussineau checksum += prom[i]; 100089ae0ff9SHervé Poussineau if (checksum > 0xff) { 100189ae0ff9SHervé Poussineau checksum = (checksum + 1) & 0xff; 100289ae0ff9SHervé Poussineau } 100389ae0ff9SHervé Poussineau } 100489ae0ff9SHervé Poussineau prom[7] = 0xff - checksum; 1005a65f56eeSaurel32 } 1006104655a5SHervé Poussineau 10071670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = { 10081670735dSHervé Poussineau .name = "dp8393x", 10091670735dSHervé Poussineau .version_id = 0, 10101670735dSHervé Poussineau .minimum_version_id = 0, 10111670735dSHervé Poussineau .fields = (VMStateField []) { 10121670735dSHervé Poussineau VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), 10131670735dSHervé Poussineau VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), 10141670735dSHervé Poussineau VMSTATE_END_OF_LIST() 10151670735dSHervé Poussineau } 10161670735dSHervé Poussineau }; 10171670735dSHervé Poussineau 1018104655a5SHervé Poussineau static Property dp8393x_properties[] = { 1019104655a5SHervé Poussineau DEFINE_NIC_PROPERTIES(dp8393xState, conf), 10203110ce81SMarc-André Lureau DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr, 10213110ce81SMarc-André Lureau TYPE_MEMORY_REGION, MemoryRegion *), 1022104655a5SHervé Poussineau DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), 1023be920841SLaurent Vivier DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), 1024104655a5SHervé Poussineau DEFINE_PROP_END_OF_LIST(), 1025104655a5SHervé Poussineau }; 1026104655a5SHervé Poussineau 1027104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data) 1028104655a5SHervé Poussineau { 1029104655a5SHervé Poussineau DeviceClass *dc = DEVICE_CLASS(klass); 1030104655a5SHervé Poussineau 1031104655a5SHervé Poussineau set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 1032104655a5SHervé Poussineau dc->realize = dp8393x_realize; 1033104655a5SHervé Poussineau dc->reset = dp8393x_reset; 10341670735dSHervé Poussineau dc->vmsd = &vmstate_dp8393x; 10354f67d30bSMarc-André Lureau device_class_set_props(dc, dp8393x_properties); 1036104655a5SHervé Poussineau } 1037104655a5SHervé Poussineau 1038104655a5SHervé Poussineau static const TypeInfo dp8393x_info = { 1039104655a5SHervé Poussineau .name = TYPE_DP8393X, 1040104655a5SHervé Poussineau .parent = TYPE_SYS_BUS_DEVICE, 1041104655a5SHervé Poussineau .instance_size = sizeof(dp8393xState), 1042104655a5SHervé Poussineau .instance_init = dp8393x_instance_init, 1043104655a5SHervé Poussineau .class_init = dp8393x_class_init, 1044104655a5SHervé Poussineau }; 1045104655a5SHervé Poussineau 1046104655a5SHervé Poussineau static void dp8393x_register_types(void) 1047104655a5SHervé Poussineau { 1048104655a5SHervé Poussineau type_register_static(&dp8393x_info); 1049104655a5SHervé Poussineau } 1050104655a5SHervé Poussineau 1051104655a5SHervé Poussineau type_init(dp8393x_register_types) 1052