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> 30db1015e9SEduardo Habkost #include "qom/object.h" 31*c0af04a4SMark Cave-Ayland #include "trace.h" 32a65f56eeSaurel32 3389ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000 34a65f56eeSaurel32 35a65f56eeSaurel32 static const char *reg_names[] = { 36a65f56eeSaurel32 "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA", 37a65f56eeSaurel32 "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0", 38a65f56eeSaurel32 "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP", 39a65f56eeSaurel32 "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA", 40a65f56eeSaurel32 "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC", 41a65f56eeSaurel32 "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT", 42a65f56eeSaurel32 "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", 43a65f56eeSaurel32 "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" }; 44a65f56eeSaurel32 45a65f56eeSaurel32 #define SONIC_CR 0x00 46a65f56eeSaurel32 #define SONIC_DCR 0x01 47a65f56eeSaurel32 #define SONIC_RCR 0x02 48a65f56eeSaurel32 #define SONIC_TCR 0x03 49a65f56eeSaurel32 #define SONIC_IMR 0x04 50a65f56eeSaurel32 #define SONIC_ISR 0x05 51a65f56eeSaurel32 #define SONIC_UTDA 0x06 52a65f56eeSaurel32 #define SONIC_CTDA 0x07 53a65f56eeSaurel32 #define SONIC_TPS 0x08 54a65f56eeSaurel32 #define SONIC_TFC 0x09 55a65f56eeSaurel32 #define SONIC_TSA0 0x0a 56a65f56eeSaurel32 #define SONIC_TSA1 0x0b 57a65f56eeSaurel32 #define SONIC_TFS 0x0c 58a65f56eeSaurel32 #define SONIC_URDA 0x0d 59a65f56eeSaurel32 #define SONIC_CRDA 0x0e 60a65f56eeSaurel32 #define SONIC_CRBA0 0x0f 61a65f56eeSaurel32 #define SONIC_CRBA1 0x10 62a65f56eeSaurel32 #define SONIC_RBWC0 0x11 63a65f56eeSaurel32 #define SONIC_RBWC1 0x12 64a65f56eeSaurel32 #define SONIC_EOBC 0x13 65a65f56eeSaurel32 #define SONIC_URRA 0x14 66a65f56eeSaurel32 #define SONIC_RSA 0x15 67a65f56eeSaurel32 #define SONIC_REA 0x16 68a65f56eeSaurel32 #define SONIC_RRP 0x17 69a65f56eeSaurel32 #define SONIC_RWP 0x18 70a65f56eeSaurel32 #define SONIC_TRBA0 0x19 71a65f56eeSaurel32 #define SONIC_TRBA1 0x1a 72a65f56eeSaurel32 #define SONIC_LLFA 0x1f 73a65f56eeSaurel32 #define SONIC_TTDA 0x20 74a65f56eeSaurel32 #define SONIC_CEP 0x21 75a65f56eeSaurel32 #define SONIC_CAP2 0x22 76a65f56eeSaurel32 #define SONIC_CAP1 0x23 77a65f56eeSaurel32 #define SONIC_CAP0 0x24 78a65f56eeSaurel32 #define SONIC_CE 0x25 79a65f56eeSaurel32 #define SONIC_CDP 0x26 80a65f56eeSaurel32 #define SONIC_CDC 0x27 81a65f56eeSaurel32 #define SONIC_SR 0x28 82a65f56eeSaurel32 #define SONIC_WT0 0x29 83a65f56eeSaurel32 #define SONIC_WT1 0x2a 84a65f56eeSaurel32 #define SONIC_RSC 0x2b 85a65f56eeSaurel32 #define SONIC_CRCT 0x2c 86a65f56eeSaurel32 #define SONIC_FAET 0x2d 87a65f56eeSaurel32 #define SONIC_MPT 0x2e 88a65f56eeSaurel32 #define SONIC_MDT 0x2f 89a65f56eeSaurel32 #define SONIC_DCR2 0x3f 90a65f56eeSaurel32 91a65f56eeSaurel32 #define SONIC_CR_HTX 0x0001 92a65f56eeSaurel32 #define SONIC_CR_TXP 0x0002 93a65f56eeSaurel32 #define SONIC_CR_RXDIS 0x0004 94a65f56eeSaurel32 #define SONIC_CR_RXEN 0x0008 95a65f56eeSaurel32 #define SONIC_CR_STP 0x0010 96a65f56eeSaurel32 #define SONIC_CR_ST 0x0020 97a65f56eeSaurel32 #define SONIC_CR_RST 0x0080 98a65f56eeSaurel32 #define SONIC_CR_RRRA 0x0100 99a65f56eeSaurel32 #define SONIC_CR_LCAM 0x0200 100a65f56eeSaurel32 #define SONIC_CR_MASK 0x03bf 101a65f56eeSaurel32 102a65f56eeSaurel32 #define SONIC_DCR_DW 0x0020 103a65f56eeSaurel32 #define SONIC_DCR_LBR 0x2000 104a65f56eeSaurel32 #define SONIC_DCR_EXBUS 0x8000 105a65f56eeSaurel32 106a65f56eeSaurel32 #define SONIC_RCR_PRX 0x0001 107a65f56eeSaurel32 #define SONIC_RCR_LBK 0x0002 108a65f56eeSaurel32 #define SONIC_RCR_FAER 0x0004 109a65f56eeSaurel32 #define SONIC_RCR_CRCR 0x0008 110a65f56eeSaurel32 #define SONIC_RCR_CRS 0x0020 111a65f56eeSaurel32 #define SONIC_RCR_LPKT 0x0040 112a65f56eeSaurel32 #define SONIC_RCR_BC 0x0080 113a65f56eeSaurel32 #define SONIC_RCR_MC 0x0100 114a65f56eeSaurel32 #define SONIC_RCR_LB0 0x0200 115a65f56eeSaurel32 #define SONIC_RCR_LB1 0x0400 116a65f56eeSaurel32 #define SONIC_RCR_AMC 0x0800 117a65f56eeSaurel32 #define SONIC_RCR_PRO 0x1000 118a65f56eeSaurel32 #define SONIC_RCR_BRD 0x2000 119a65f56eeSaurel32 #define SONIC_RCR_RNT 0x4000 120a65f56eeSaurel32 121a65f56eeSaurel32 #define SONIC_TCR_PTX 0x0001 122a65f56eeSaurel32 #define SONIC_TCR_BCM 0x0002 123a65f56eeSaurel32 #define SONIC_TCR_FU 0x0004 124a65f56eeSaurel32 #define SONIC_TCR_EXC 0x0040 125a65f56eeSaurel32 #define SONIC_TCR_CRSL 0x0080 126a65f56eeSaurel32 #define SONIC_TCR_NCRS 0x0100 127a65f56eeSaurel32 #define SONIC_TCR_EXD 0x0400 128a65f56eeSaurel32 #define SONIC_TCR_CRCI 0x2000 129a65f56eeSaurel32 #define SONIC_TCR_PINT 0x8000 130a65f56eeSaurel32 131ada74315SFinn Thain #define SONIC_ISR_RBAE 0x0010 132a65f56eeSaurel32 #define SONIC_ISR_RBE 0x0020 133a65f56eeSaurel32 #define SONIC_ISR_RDE 0x0040 134a65f56eeSaurel32 #define SONIC_ISR_TC 0x0080 135a65f56eeSaurel32 #define SONIC_ISR_TXDN 0x0200 136a65f56eeSaurel32 #define SONIC_ISR_PKTRX 0x0400 137a65f56eeSaurel32 #define SONIC_ISR_PINT 0x0800 138a65f56eeSaurel32 #define SONIC_ISR_LCD 0x1000 139a65f56eeSaurel32 14088f632fbSFinn Thain #define SONIC_DESC_EOL 0x0001 14188f632fbSFinn Thain #define SONIC_DESC_ADDR 0xFFFE 14288f632fbSFinn Thain 143104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x" 1448063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(dp8393xState, DP8393X) 145104655a5SHervé Poussineau 146db1015e9SEduardo Habkost struct dp8393xState { 147104655a5SHervé Poussineau SysBusDevice parent_obj; 148104655a5SHervé Poussineau 149a65f56eeSaurel32 /* Hardware */ 150104655a5SHervé Poussineau uint8_t it_shift; 151be920841SLaurent Vivier bool big_endian; 152c2279bd0SFinn Thain bool last_rba_is_full; 153a65f56eeSaurel32 qemu_irq irq; 154a65f56eeSaurel32 int irq_level; 155a65f56eeSaurel32 QEMUTimer *watchdog; 156a65f56eeSaurel32 int64_t wt_last_update; 15705f41fe3SMark McLoughlin NICConf conf; 15805f41fe3SMark McLoughlin NICState *nic; 159024e5bb6SAvi Kivity MemoryRegion mmio; 16089ae0ff9SHervé Poussineau MemoryRegion prom; 161a65f56eeSaurel32 162a65f56eeSaurel32 /* Registers */ 163a65f56eeSaurel32 uint8_t cam[16][6]; 164a65f56eeSaurel32 uint16_t regs[0x40]; 165a65f56eeSaurel32 166a65f56eeSaurel32 /* Temporaries */ 167a65f56eeSaurel32 uint8_t tx_buffer[0x10000]; 168af9f0be3SLaurent Vivier uint16_t data[12]; 169a65f56eeSaurel32 int loopback_packet; 170a65f56eeSaurel32 171a65f56eeSaurel32 /* Memory access */ 1723110ce81SMarc-André Lureau MemoryRegion *dma_mr; 173dd820513SHervé Poussineau AddressSpace as; 174db1015e9SEduardo Habkost }; 175a65f56eeSaurel32 1761ca82a8dSMark Cave-Ayland /* 1771ca82a8dSMark Cave-Ayland * Accessor functions for values which are formed by 178581f7b12SPeter Maydell * concatenating two 16 bit device registers. By putting these 179581f7b12SPeter Maydell * in their own functions with a uint32_t return type we avoid the 180581f7b12SPeter Maydell * pitfall of implicit sign extension where ((x << 16) | y) is a 181581f7b12SPeter Maydell * signed 32 bit integer that might get sign-extended to a 64 bit integer. 182581f7b12SPeter Maydell */ 183581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s) 184581f7b12SPeter Maydell { 185581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP]; 186581f7b12SPeter Maydell } 187581f7b12SPeter Maydell 188581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s) 189581f7b12SPeter Maydell { 190581f7b12SPeter Maydell return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; 191581f7b12SPeter Maydell } 192581f7b12SPeter Maydell 193581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s) 194581f7b12SPeter Maydell { 19588f632fbSFinn Thain return (s->regs[SONIC_URDA] << 16) | 19688f632fbSFinn Thain (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR); 197581f7b12SPeter Maydell } 198581f7b12SPeter Maydell 199581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s) 200581f7b12SPeter Maydell { 201581f7b12SPeter Maydell return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; 202581f7b12SPeter Maydell } 203581f7b12SPeter Maydell 204581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s) 205581f7b12SPeter Maydell { 206581f7b12SPeter Maydell return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP]; 207581f7b12SPeter Maydell } 208581f7b12SPeter Maydell 209581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s) 210581f7b12SPeter Maydell { 211581f7b12SPeter Maydell return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0]; 212581f7b12SPeter Maydell } 213581f7b12SPeter Maydell 214581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s) 215581f7b12SPeter Maydell { 21688f632fbSFinn Thain return (s->regs[SONIC_UTDA] << 16) | 21788f632fbSFinn Thain (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR); 218581f7b12SPeter Maydell } 219581f7b12SPeter Maydell 220581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s) 221581f7b12SPeter Maydell { 222581f7b12SPeter Maydell return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; 223581f7b12SPeter Maydell } 224581f7b12SPeter Maydell 225af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset) 226be920841SLaurent Vivier { 227be920841SLaurent Vivier uint16_t val; 228be920841SLaurent Vivier 229be920841SLaurent Vivier if (s->big_endian) { 230af9f0be3SLaurent Vivier val = be16_to_cpu(s->data[offset * width + width - 1]); 231be920841SLaurent Vivier } else { 232af9f0be3SLaurent Vivier val = le16_to_cpu(s->data[offset * width]); 233be920841SLaurent Vivier } 234be920841SLaurent Vivier return val; 235be920841SLaurent Vivier } 236be920841SLaurent Vivier 237af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset, 238be920841SLaurent Vivier uint16_t val) 239be920841SLaurent Vivier { 240be920841SLaurent Vivier if (s->big_endian) { 2413fe9a838SFinn Thain if (width == 2) { 2423fe9a838SFinn Thain s->data[offset * 2] = 0; 2433fe9a838SFinn Thain s->data[offset * 2 + 1] = cpu_to_be16(val); 244be920841SLaurent Vivier } else { 2453fe9a838SFinn Thain s->data[offset] = cpu_to_be16(val); 2463fe9a838SFinn Thain } 2473fe9a838SFinn Thain } else { 2483fe9a838SFinn Thain if (width == 2) { 2493fe9a838SFinn Thain s->data[offset * 2] = cpu_to_le16(val); 2503fe9a838SFinn Thain s->data[offset * 2 + 1] = 0; 2513fe9a838SFinn Thain } else { 2523fe9a838SFinn Thain s->data[offset] = cpu_to_le16(val); 2533fe9a838SFinn Thain } 254be920841SLaurent Vivier } 255be920841SLaurent Vivier } 256be920841SLaurent Vivier 257a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s) 258a65f56eeSaurel32 { 259a65f56eeSaurel32 int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; 260a65f56eeSaurel32 261a65f56eeSaurel32 if (level != s->irq_level) { 262a65f56eeSaurel32 s->irq_level = level; 263a65f56eeSaurel32 if (level) { 264*c0af04a4SMark Cave-Ayland trace_dp8393x_raise_irq(s->regs[SONIC_ISR]); 265a65f56eeSaurel32 } else { 266*c0af04a4SMark Cave-Ayland trace_dp8393x_lower_irq(); 267a65f56eeSaurel32 } 268a65f56eeSaurel32 } 269a65f56eeSaurel32 270a65f56eeSaurel32 qemu_set_irq(s->irq, level); 271a65f56eeSaurel32 } 272a65f56eeSaurel32 2733df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s) 274a65f56eeSaurel32 { 275a65f56eeSaurel32 int width, size; 276a65f56eeSaurel32 uint16_t index = 0; 277a65f56eeSaurel32 278a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 279a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 280a65f56eeSaurel32 281a65f56eeSaurel32 while (s->regs[SONIC_CDC] & 0x1f) { 282a65f56eeSaurel32 /* Fill current entry */ 28319f70347SPeter Maydell address_space_read(&s->as, dp8393x_cdp(s), 28419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 285af9f0be3SLaurent Vivier s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; 286af9f0be3SLaurent Vivier s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; 287af9f0be3SLaurent Vivier s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; 288af9f0be3SLaurent Vivier s->cam[index][3] = dp8393x_get(s, width, 2) >> 8; 289af9f0be3SLaurent Vivier s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff; 290af9f0be3SLaurent Vivier s->cam[index][5] = dp8393x_get(s, width, 3) >> 8; 291*c0af04a4SMark Cave-Ayland trace_dp8393x_load_cam(index, s->cam[index][0], s->cam[index][1], 292*c0af04a4SMark Cave-Ayland s->cam[index][2], s->cam[index][3], 293*c0af04a4SMark Cave-Ayland s->cam[index][4], s->cam[index][5]); 294a65f56eeSaurel32 /* Move to next entry */ 295a65f56eeSaurel32 s->regs[SONIC_CDC]--; 296a65f56eeSaurel32 s->regs[SONIC_CDP] += size; 297a65f56eeSaurel32 index++; 298a65f56eeSaurel32 } 299a65f56eeSaurel32 300a65f56eeSaurel32 /* Read CAM enable */ 30119f70347SPeter Maydell address_space_read(&s->as, dp8393x_cdp(s), 30219f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 303af9f0be3SLaurent Vivier s->regs[SONIC_CE] = dp8393x_get(s, width, 0); 304*c0af04a4SMark Cave-Ayland trace_dp8393x_load_cam_done(s->regs[SONIC_CE]); 305a65f56eeSaurel32 306a65f56eeSaurel32 /* Done */ 307a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; 308a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_LCD; 309a65f56eeSaurel32 dp8393x_update_irq(s); 310a65f56eeSaurel32 } 311a65f56eeSaurel32 3123df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s) 313a65f56eeSaurel32 { 314a65f56eeSaurel32 int width, size; 315a65f56eeSaurel32 316a65f56eeSaurel32 /* Read memory */ 317a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 318a65f56eeSaurel32 size = sizeof(uint16_t) * 4 * width; 31919f70347SPeter Maydell address_space_read(&s->as, dp8393x_rrp(s), 32019f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 321a65f56eeSaurel32 322a65f56eeSaurel32 /* Update SONIC registers */ 323af9f0be3SLaurent Vivier s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0); 324af9f0be3SLaurent Vivier s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1); 325af9f0be3SLaurent Vivier s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2); 326af9f0be3SLaurent Vivier s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3); 327*c0af04a4SMark Cave-Ayland trace_dp8393x_read_rra_regs(s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], 328a65f56eeSaurel32 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); 329a65f56eeSaurel32 330a65f56eeSaurel32 /* Go to next entry */ 331a65f56eeSaurel32 s->regs[SONIC_RRP] += size; 332a65f56eeSaurel32 333a65f56eeSaurel32 /* Handle wrap */ 334a65f56eeSaurel32 if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { 335a65f56eeSaurel32 s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; 336a65f56eeSaurel32 } 337a65f56eeSaurel32 338c2279bd0SFinn Thain /* Warn the host if CRBA now has the last available resource */ 3391ca82a8dSMark Cave-Ayland if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) { 340a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_RBE; 341a65f56eeSaurel32 dp8393x_update_irq(s); 342a65f56eeSaurel32 } 343c2279bd0SFinn Thain 344c2279bd0SFinn Thain /* Allow packet reception */ 345c2279bd0SFinn Thain s->last_rba_is_full = false; 346a65f56eeSaurel32 } 347a65f56eeSaurel32 3483df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s) 349a65f56eeSaurel32 { 350bc72ad67SAlex Bligh timer_del(s->watchdog); 351a65f56eeSaurel32 3521ca82a8dSMark Cave-Ayland s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | 3531ca82a8dSMark Cave-Ayland SONIC_CR_HTX); 354a65f56eeSaurel32 s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; 355a65f56eeSaurel32 } 356a65f56eeSaurel32 3573df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s) 358a65f56eeSaurel32 { 359a65f56eeSaurel32 uint32_t ticks; 360a65f56eeSaurel32 int64_t delay; 361a65f56eeSaurel32 362a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 363bc72ad67SAlex Bligh timer_del(s->watchdog); 364a65f56eeSaurel32 return; 365a65f56eeSaurel32 } 366a65f56eeSaurel32 367581f7b12SPeter Maydell ticks = dp8393x_wt(s); 368bc72ad67SAlex Bligh s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 36973bcb24dSRutuja Shah delay = NANOSECONDS_PER_SECOND * ticks / 5000000; 370bc72ad67SAlex Bligh timer_mod(s->watchdog, s->wt_last_update + delay); 371a65f56eeSaurel32 } 372a65f56eeSaurel32 3733df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s) 374a65f56eeSaurel32 { 375a65f56eeSaurel32 int64_t elapsed; 376a65f56eeSaurel32 uint32_t val; 377a65f56eeSaurel32 378a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 379bc72ad67SAlex Bligh timer_del(s->watchdog); 380a65f56eeSaurel32 return; 381a65f56eeSaurel32 } 382a65f56eeSaurel32 383bc72ad67SAlex Bligh elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 384581f7b12SPeter Maydell val = dp8393x_wt(s); 385a65f56eeSaurel32 val -= elapsed / 5000000; 386a65f56eeSaurel32 s->regs[SONIC_WT1] = (val >> 16) & 0xffff; 387a65f56eeSaurel32 s->regs[SONIC_WT0] = (val >> 0) & 0xffff; 3883df5de64SHervé Poussineau dp8393x_set_next_tick(s); 389a65f56eeSaurel32 390a65f56eeSaurel32 } 391a65f56eeSaurel32 3923df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s) 393a65f56eeSaurel32 { 394a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_STP; 3953df5de64SHervé Poussineau dp8393x_set_next_tick(s); 396a65f56eeSaurel32 } 397a65f56eeSaurel32 3983df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s) 399a65f56eeSaurel32 { 400a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_ST; 4013df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 402a65f56eeSaurel32 } 403a65f56eeSaurel32 404b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc); 4054594f93aSFam Zheng 4063df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s) 407a65f56eeSaurel32 { 408a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; 4094594f93aSFam Zheng if (dp8393x_can_receive(s->nic->ncs)) { 4104594f93aSFam Zheng qemu_flush_queued_packets(qemu_get_queue(s->nic)); 4114594f93aSFam Zheng } 412a65f56eeSaurel32 } 413a65f56eeSaurel32 4143df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s) 415a65f56eeSaurel32 { 416a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; 417a65f56eeSaurel32 } 418a65f56eeSaurel32 4193df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s) 420a65f56eeSaurel32 { 421b356f76dSJason Wang NetClientState *nc = qemu_get_queue(s->nic); 422a65f56eeSaurel32 int width, size; 423a65f56eeSaurel32 int tx_len, len; 424a65f56eeSaurel32 uint16_t i; 425a65f56eeSaurel32 426a65f56eeSaurel32 width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; 427a65f56eeSaurel32 428a65f56eeSaurel32 while (1) { 429a65f56eeSaurel32 /* Read memory */ 430a65f56eeSaurel32 size = sizeof(uint16_t) * 6 * width; 431a65f56eeSaurel32 s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; 432*c0af04a4SMark Cave-Ayland trace_dp8393x_transmit_packet(dp8393x_ttda(s)); 43319f70347SPeter Maydell address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, 43419f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 435a65f56eeSaurel32 tx_len = 0; 436a65f56eeSaurel32 437a65f56eeSaurel32 /* Update registers */ 438af9f0be3SLaurent Vivier s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000; 439af9f0be3SLaurent Vivier s->regs[SONIC_TPS] = dp8393x_get(s, width, 1); 440af9f0be3SLaurent Vivier s->regs[SONIC_TFC] = dp8393x_get(s, width, 2); 441af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3); 442af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4); 443af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 5); 444a65f56eeSaurel32 445a65f56eeSaurel32 /* Handle programmable interrupt */ 446a65f56eeSaurel32 if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { 447a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_PINT; 448a65f56eeSaurel32 } else { 449a65f56eeSaurel32 s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; 450a65f56eeSaurel32 } 451a65f56eeSaurel32 452a65f56eeSaurel32 for (i = 0; i < s->regs[SONIC_TFC]; ) { 453a65f56eeSaurel32 /* Append fragment */ 454a65f56eeSaurel32 len = s->regs[SONIC_TFS]; 455a65f56eeSaurel32 if (tx_len + len > sizeof(s->tx_buffer)) { 456a65f56eeSaurel32 len = sizeof(s->tx_buffer) - tx_len; 457a65f56eeSaurel32 } 45819f70347SPeter Maydell address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED, 45919f70347SPeter Maydell &s->tx_buffer[tx_len], len); 460a65f56eeSaurel32 tx_len += len; 461a65f56eeSaurel32 462a65f56eeSaurel32 i++; 463a65f56eeSaurel32 if (i != s->regs[SONIC_TFC]) { 464a65f56eeSaurel32 /* Read next fragment details */ 465a65f56eeSaurel32 size = sizeof(uint16_t) * 3 * width; 46619f70347SPeter Maydell address_space_read(&s->as, 46719f70347SPeter Maydell dp8393x_ttda(s) 46819f70347SPeter Maydell + sizeof(uint16_t) * width * (4 + 3 * i), 46919f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, 47019f70347SPeter Maydell size); 471af9f0be3SLaurent Vivier s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0); 472af9f0be3SLaurent Vivier s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1); 473af9f0be3SLaurent Vivier s->regs[SONIC_TFS] = dp8393x_get(s, width, 2); 474a65f56eeSaurel32 } 475a65f56eeSaurel32 } 476a65f56eeSaurel32 477a65f56eeSaurel32 /* Handle Ethernet checksum */ 478a65f56eeSaurel32 if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { 4791ca82a8dSMark Cave-Ayland /* 4801ca82a8dSMark Cave-Ayland * Don't append FCS there, to look like slirp packets 4811ca82a8dSMark Cave-Ayland * which don't have one 4821ca82a8dSMark Cave-Ayland */ 483a65f56eeSaurel32 } else { 484a65f56eeSaurel32 /* Remove existing FCS */ 485a65f56eeSaurel32 tx_len -= 4; 486915976bdSMauro Matteo Cascella if (tx_len < 0) { 487*c0af04a4SMark Cave-Ayland trace_dp8393x_transmit_txlen_error(tx_len); 488915976bdSMauro Matteo Cascella break; 489915976bdSMauro Matteo Cascella } 490a65f56eeSaurel32 } 491a65f56eeSaurel32 492a65f56eeSaurel32 if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { 493a65f56eeSaurel32 /* Loopback */ 494a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; 495b356f76dSJason Wang if (nc->info->can_receive(nc)) { 496a65f56eeSaurel32 s->loopback_packet = 1; 497331d2ac9SJason Wang qemu_receive_packet(nc, s->tx_buffer, tx_len); 498a65f56eeSaurel32 } 499a65f56eeSaurel32 } else { 500a65f56eeSaurel32 /* Transmit packet */ 501b356f76dSJason Wang qemu_send_packet(nc, s->tx_buffer, tx_len); 502a65f56eeSaurel32 } 503a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_PTX; 504a65f56eeSaurel32 505a65f56eeSaurel32 /* Write status */ 506af9f0be3SLaurent Vivier dp8393x_put(s, width, 0, 507be920841SLaurent Vivier s->regs[SONIC_TCR] & 0x0fff); /* status */ 508a65f56eeSaurel32 size = sizeof(uint16_t) * width; 50919f70347SPeter Maydell address_space_write(&s->as, dp8393x_ttda(s), 51019f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, size); 511a65f56eeSaurel32 512a65f56eeSaurel32 if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { 513a65f56eeSaurel32 /* Read footer of packet */ 514a65f56eeSaurel32 size = sizeof(uint16_t) * width; 51519f70347SPeter Maydell address_space_read(&s->as, 51619f70347SPeter Maydell dp8393x_ttda(s) 51719f70347SPeter Maydell + sizeof(uint16_t) * width 51819f70347SPeter Maydell * (4 + 3 * s->regs[SONIC_TFC]), 51919f70347SPeter Maydell MEMTXATTRS_UNSPECIFIED, s->data, 52019f70347SPeter Maydell size); 521a0cf4297SFinn Thain s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0); 522a0cf4297SFinn Thain if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) { 523a65f56eeSaurel32 /* EOL detected */ 524a65f56eeSaurel32 break; 525a65f56eeSaurel32 } 526a65f56eeSaurel32 } 527a65f56eeSaurel32 } 528a65f56eeSaurel32 529a65f56eeSaurel32 /* Done */ 530a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_TXP; 531a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; 532a65f56eeSaurel32 dp8393x_update_irq(s); 533a65f56eeSaurel32 } 534a65f56eeSaurel32 5353df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s) 536a65f56eeSaurel32 { 537a65f56eeSaurel32 /* Nothing to do */ 538a65f56eeSaurel32 } 539a65f56eeSaurel32 5403df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command) 541a65f56eeSaurel32 { 542a65f56eeSaurel32 if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { 543a65f56eeSaurel32 s->regs[SONIC_CR] &= ~SONIC_CR_RST; 544a65f56eeSaurel32 return; 545a65f56eeSaurel32 } 546a65f56eeSaurel32 547a65f56eeSaurel32 s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); 548a65f56eeSaurel32 5491ca82a8dSMark Cave-Ayland if (command & SONIC_CR_HTX) { 5503df5de64SHervé Poussineau dp8393x_do_halt_transmission(s); 5511ca82a8dSMark Cave-Ayland } 5521ca82a8dSMark Cave-Ayland if (command & SONIC_CR_TXP) { 5533df5de64SHervé Poussineau dp8393x_do_transmit_packets(s); 5541ca82a8dSMark Cave-Ayland } 5551ca82a8dSMark Cave-Ayland if (command & SONIC_CR_RXDIS) { 5563df5de64SHervé Poussineau dp8393x_do_receiver_disable(s); 5571ca82a8dSMark Cave-Ayland } 5581ca82a8dSMark Cave-Ayland if (command & SONIC_CR_RXEN) { 5593df5de64SHervé Poussineau dp8393x_do_receiver_enable(s); 5601ca82a8dSMark Cave-Ayland } 5611ca82a8dSMark Cave-Ayland if (command & SONIC_CR_STP) { 5623df5de64SHervé Poussineau dp8393x_do_stop_timer(s); 5631ca82a8dSMark Cave-Ayland } 5641ca82a8dSMark Cave-Ayland if (command & SONIC_CR_ST) { 5653df5de64SHervé Poussineau dp8393x_do_start_timer(s); 5661ca82a8dSMark Cave-Ayland } 5671ca82a8dSMark Cave-Ayland if (command & SONIC_CR_RST) { 5683df5de64SHervé Poussineau dp8393x_do_software_reset(s); 5691ca82a8dSMark Cave-Ayland } 570a3cce282SFinn Thain if (command & SONIC_CR_RRRA) { 5713df5de64SHervé Poussineau dp8393x_do_read_rra(s); 572a3cce282SFinn Thain s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; 573a3cce282SFinn Thain } 5741ca82a8dSMark Cave-Ayland if (command & SONIC_CR_LCAM) { 5753df5de64SHervé Poussineau dp8393x_do_load_cam(s); 576a65f56eeSaurel32 } 5771ca82a8dSMark Cave-Ayland } 578a65f56eeSaurel32 57984689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) 580a65f56eeSaurel32 { 58184689cbbSHervé Poussineau dp8393xState *s = opaque; 58284689cbbSHervé Poussineau int reg = addr >> s->it_shift; 583a65f56eeSaurel32 uint16_t val = 0; 584a65f56eeSaurel32 585a65f56eeSaurel32 switch (reg) { 586a65f56eeSaurel32 /* Update data before reading it */ 587a65f56eeSaurel32 case SONIC_WT0: 588a65f56eeSaurel32 case SONIC_WT1: 5893df5de64SHervé Poussineau dp8393x_update_wt_regs(s); 590a65f56eeSaurel32 val = s->regs[reg]; 591a65f56eeSaurel32 break; 592a65f56eeSaurel32 /* Accept read to some registers only when in reset mode */ 593a65f56eeSaurel32 case SONIC_CAP2: 594a65f56eeSaurel32 case SONIC_CAP1: 595a65f56eeSaurel32 case SONIC_CAP0: 596a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 597a65f56eeSaurel32 val = s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg) + 1] << 8; 598a65f56eeSaurel32 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg)]; 599a65f56eeSaurel32 } 600a65f56eeSaurel32 break; 6011ca82a8dSMark Cave-Ayland /* All other registers have no special contraints */ 602a65f56eeSaurel32 default: 603a65f56eeSaurel32 val = s->regs[reg]; 604a65f56eeSaurel32 } 605a65f56eeSaurel32 606*c0af04a4SMark Cave-Ayland trace_dp8393x_read(reg, reg_names[reg], val, size); 607a65f56eeSaurel32 6083fe9a838SFinn Thain return s->big_endian ? val << 16 : val; 609a65f56eeSaurel32 } 610a65f56eeSaurel32 61184689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, 61284689cbbSHervé Poussineau unsigned int size) 613a65f56eeSaurel32 { 61484689cbbSHervé Poussineau dp8393xState *s = opaque; 61584689cbbSHervé Poussineau int reg = addr >> s->it_shift; 6163fe9a838SFinn Thain uint32_t val = s->big_endian ? data >> 16 : data; 61784689cbbSHervé Poussineau 618*c0af04a4SMark Cave-Ayland trace_dp8393x_write(reg, reg_names[reg], val, size); 619a65f56eeSaurel32 620a65f56eeSaurel32 switch (reg) { 621a65f56eeSaurel32 /* Command register */ 622a65f56eeSaurel32 case SONIC_CR: 6233fe9a838SFinn Thain dp8393x_do_command(s, val); 624a65f56eeSaurel32 break; 625a65f56eeSaurel32 /* Prevent write to read-only registers */ 626a65f56eeSaurel32 case SONIC_CAP2: 627a65f56eeSaurel32 case SONIC_CAP1: 628a65f56eeSaurel32 case SONIC_CAP0: 629a65f56eeSaurel32 case SONIC_SR: 630a65f56eeSaurel32 case SONIC_MDT: 631*c0af04a4SMark Cave-Ayland trace_dp8393x_write_invalid(reg); 632a65f56eeSaurel32 break; 633a65f56eeSaurel32 /* Accept write to some registers only when in reset mode */ 634a65f56eeSaurel32 case SONIC_DCR: 635a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 6363fe9a838SFinn Thain s->regs[reg] = val & 0xbfff; 637a65f56eeSaurel32 } else { 638*c0af04a4SMark Cave-Ayland trace_dp8393x_write_invalid_dcr("DCR"); 639a65f56eeSaurel32 } 640a65f56eeSaurel32 break; 641a65f56eeSaurel32 case SONIC_DCR2: 642a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_RST) { 6433fe9a838SFinn Thain s->regs[reg] = val & 0xf017; 644a65f56eeSaurel32 } else { 645*c0af04a4SMark Cave-Ayland trace_dp8393x_write_invalid_dcr("DCR2"); 646a65f56eeSaurel32 } 647a65f56eeSaurel32 break; 648a65f56eeSaurel32 /* 12 lower bytes are Read Only */ 649a65f56eeSaurel32 case SONIC_TCR: 6503fe9a838SFinn Thain s->regs[reg] = val & 0xf000; 651a65f56eeSaurel32 break; 652a65f56eeSaurel32 /* 9 lower bytes are Read Only */ 653a65f56eeSaurel32 case SONIC_RCR: 6543fe9a838SFinn Thain s->regs[reg] = val & 0xffe0; 655a65f56eeSaurel32 break; 656a65f56eeSaurel32 /* Ignore most significant bit */ 657a65f56eeSaurel32 case SONIC_IMR: 6583fe9a838SFinn Thain s->regs[reg] = val & 0x7fff; 659a65f56eeSaurel32 dp8393x_update_irq(s); 660a65f56eeSaurel32 break; 661a65f56eeSaurel32 /* Clear bits by writing 1 to them */ 662a65f56eeSaurel32 case SONIC_ISR: 6633fe9a838SFinn Thain val &= s->regs[reg]; 6643fe9a838SFinn Thain s->regs[reg] &= ~val; 6653fe9a838SFinn Thain if (val & SONIC_ISR_RBE) { 6663df5de64SHervé Poussineau dp8393x_do_read_rra(s); 667a65f56eeSaurel32 } 668a65f56eeSaurel32 dp8393x_update_irq(s); 669a65f56eeSaurel32 break; 670ea227027SFinn Thain /* The guest is required to store aligned pointers here */ 671a65f56eeSaurel32 case SONIC_RSA: 672a65f56eeSaurel32 case SONIC_REA: 673a65f56eeSaurel32 case SONIC_RRP: 674a65f56eeSaurel32 case SONIC_RWP: 675ea227027SFinn Thain if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { 676ea227027SFinn Thain s->regs[reg] = val & 0xfffc; 677ea227027SFinn Thain } else { 6783fe9a838SFinn Thain s->regs[reg] = val & 0xfffe; 679ea227027SFinn Thain } 680a65f56eeSaurel32 break; 681a65f56eeSaurel32 /* Invert written value for some registers */ 682a65f56eeSaurel32 case SONIC_CRCT: 683a65f56eeSaurel32 case SONIC_FAET: 684a65f56eeSaurel32 case SONIC_MPT: 6853fe9a838SFinn Thain s->regs[reg] = val ^ 0xffff; 686a65f56eeSaurel32 break; 687a65f56eeSaurel32 /* All other registers have no special contrainst */ 688a65f56eeSaurel32 default: 6893fe9a838SFinn Thain s->regs[reg] = val; 690a65f56eeSaurel32 } 691a65f56eeSaurel32 692a65f56eeSaurel32 if (reg == SONIC_WT0 || reg == SONIC_WT1) { 6933df5de64SHervé Poussineau dp8393x_set_next_tick(s); 694a65f56eeSaurel32 } 695a65f56eeSaurel32 } 696a65f56eeSaurel32 69784689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = { 69884689cbbSHervé Poussineau .read = dp8393x_read, 69984689cbbSHervé Poussineau .write = dp8393x_write, 7003fe9a838SFinn Thain .impl.min_access_size = 4, 7013fe9a838SFinn Thain .impl.max_access_size = 4, 70284689cbbSHervé Poussineau .endianness = DEVICE_NATIVE_ENDIAN, 70384689cbbSHervé Poussineau }; 70484689cbbSHervé Poussineau 705a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque) 706a65f56eeSaurel32 { 707a65f56eeSaurel32 dp8393xState *s = opaque; 708a65f56eeSaurel32 709a65f56eeSaurel32 if (s->regs[SONIC_CR] & SONIC_CR_STP) { 710a65f56eeSaurel32 return; 711a65f56eeSaurel32 } 712a65f56eeSaurel32 713a65f56eeSaurel32 s->regs[SONIC_WT1] = 0xffff; 714a65f56eeSaurel32 s->regs[SONIC_WT0] = 0xffff; 7153df5de64SHervé Poussineau dp8393x_set_next_tick(s); 716a65f56eeSaurel32 717a65f56eeSaurel32 /* Signal underflow */ 718a65f56eeSaurel32 s->regs[SONIC_ISR] |= SONIC_ISR_TC; 719a65f56eeSaurel32 dp8393x_update_irq(s); 720a65f56eeSaurel32 } 721a65f56eeSaurel32 722b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc) 723a65f56eeSaurel32 { 724cc1f0f45SJason Wang dp8393xState *s = qemu_get_nic_opaque(nc); 725a65f56eeSaurel32 726b8c4b67eSPhilippe Mathieu-Daudé return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN); 727a65f56eeSaurel32 } 728a65f56eeSaurel32 7293df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, 7303df5de64SHervé Poussineau int size) 731a65f56eeSaurel32 { 732a65f56eeSaurel32 static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 733a65f56eeSaurel32 int i; 734a65f56eeSaurel32 735a65f56eeSaurel32 /* Check promiscuous mode */ 736a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { 737a65f56eeSaurel32 return 0; 738a65f56eeSaurel32 } 739a65f56eeSaurel32 740a65f56eeSaurel32 /* Check multicast packets */ 741a65f56eeSaurel32 if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { 742a65f56eeSaurel32 return SONIC_RCR_MC; 743a65f56eeSaurel32 } 744a65f56eeSaurel32 745a65f56eeSaurel32 /* Check broadcast */ 7461ca82a8dSMark Cave-Ayland if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && 7471ca82a8dSMark Cave-Ayland !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) { 791*c0af04a4SMark Cave-Ayland trace_dp8393x_receive_oversize(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) { 800*c0af04a4SMark Cave-Ayland trace_dp8393x_receive_not_netcard(); 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 */ 838*c0af04a4SMark Cave-Ayland trace_dp8393x_receive_packet(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 */ 876*c0af04a4SMark Cave-Ayland trace_dp8393x_receive_write_status(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); 9381ca82a8dSMark Cave-Ayland s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | 9391ca82a8dSMark Cave-Ayland SONIC_RCR_RNT); 940a65f56eeSaurel32 s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; 941a65f56eeSaurel32 s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; 942a65f56eeSaurel32 s->regs[SONIC_IMR] = 0; 943a65f56eeSaurel32 s->regs[SONIC_ISR] = 0; 944a65f56eeSaurel32 s->regs[SONIC_DCR2] = 0; 945a65f56eeSaurel32 s->regs[SONIC_EOBC] = 0x02F8; 946a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 947a65f56eeSaurel32 s->regs[SONIC_CE] = 0; 948a65f56eeSaurel32 s->regs[SONIC_RSC] = 0; 949a65f56eeSaurel32 950a65f56eeSaurel32 /* Network cable is connected */ 951a65f56eeSaurel32 s->regs[SONIC_RCR] |= SONIC_RCR_CRS; 952a65f56eeSaurel32 953a65f56eeSaurel32 dp8393x_update_irq(s); 954a65f56eeSaurel32 } 955a65f56eeSaurel32 95605f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = { 957f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 95805f41fe3SMark McLoughlin .size = sizeof(NICState), 9593df5de64SHervé Poussineau .can_receive = dp8393x_can_receive, 9603df5de64SHervé Poussineau .receive = dp8393x_receive, 96105f41fe3SMark McLoughlin }; 96205f41fe3SMark McLoughlin 963104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj) 964a65f56eeSaurel32 { 965104655a5SHervé Poussineau SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 966104655a5SHervé Poussineau dp8393xState *s = DP8393X(obj); 967a65f56eeSaurel32 968104655a5SHervé Poussineau sysbus_init_mmio(sbd, &s->mmio); 96989ae0ff9SHervé Poussineau sysbus_init_mmio(sbd, &s->prom); 970104655a5SHervé Poussineau sysbus_init_irq(sbd, &s->irq); 971104655a5SHervé Poussineau } 972a65f56eeSaurel32 973104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp) 974104655a5SHervé Poussineau { 975104655a5SHervé Poussineau dp8393xState *s = DP8393X(dev); 97689ae0ff9SHervé Poussineau int i, checksum; 97789ae0ff9SHervé Poussineau uint8_t *prom; 97852579c68SHervé Poussineau Error *local_err = NULL; 979a65f56eeSaurel32 980104655a5SHervé Poussineau address_space_init(&s->as, s->dma_mr, "dp8393x"); 981104655a5SHervé Poussineau memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, 982104655a5SHervé Poussineau "dp8393x-regs", 0x40 << s->it_shift); 983104655a5SHervé Poussineau 984104655a5SHervé Poussineau s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, 985104655a5SHervé Poussineau object_get_typename(OBJECT(dev)), dev->id, s); 986104655a5SHervé Poussineau qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 987104655a5SHervé Poussineau 988bc72ad67SAlex Bligh s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); 98989ae0ff9SHervé Poussineau 990fcd3b085SPhilippe Mathieu-Daudé memory_region_init_rom(&s->prom, OBJECT(dev), "dp8393x-prom", 991fcd3b085SPhilippe Mathieu-Daudé SONIC_PROM_SIZE, &local_err); 99252579c68SHervé Poussineau if (local_err) { 99352579c68SHervé Poussineau error_propagate(errp, local_err); 99452579c68SHervé Poussineau return; 99552579c68SHervé Poussineau } 99689ae0ff9SHervé Poussineau prom = memory_region_get_ram_ptr(&s->prom); 99789ae0ff9SHervé Poussineau checksum = 0; 99889ae0ff9SHervé Poussineau for (i = 0; i < 6; i++) { 99989ae0ff9SHervé Poussineau prom[i] = s->conf.macaddr.a[i]; 100089ae0ff9SHervé Poussineau checksum += prom[i]; 100189ae0ff9SHervé Poussineau if (checksum > 0xff) { 100289ae0ff9SHervé Poussineau checksum = (checksum + 1) & 0xff; 100389ae0ff9SHervé Poussineau } 100489ae0ff9SHervé Poussineau } 100589ae0ff9SHervé Poussineau prom[7] = 0xff - checksum; 1006a65f56eeSaurel32 } 1007104655a5SHervé Poussineau 10081670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = { 10091670735dSHervé Poussineau .name = "dp8393x", 10101670735dSHervé Poussineau .version_id = 0, 10111670735dSHervé Poussineau .minimum_version_id = 0, 10121670735dSHervé Poussineau .fields = (VMStateField []) { 10131670735dSHervé Poussineau VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), 10141670735dSHervé Poussineau VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), 10151670735dSHervé Poussineau VMSTATE_END_OF_LIST() 10161670735dSHervé Poussineau } 10171670735dSHervé Poussineau }; 10181670735dSHervé Poussineau 1019104655a5SHervé Poussineau static Property dp8393x_properties[] = { 1020104655a5SHervé Poussineau DEFINE_NIC_PROPERTIES(dp8393xState, conf), 10213110ce81SMarc-André Lureau DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr, 10223110ce81SMarc-André Lureau TYPE_MEMORY_REGION, MemoryRegion *), 1023104655a5SHervé Poussineau DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), 1024be920841SLaurent Vivier DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), 1025104655a5SHervé Poussineau DEFINE_PROP_END_OF_LIST(), 1026104655a5SHervé Poussineau }; 1027104655a5SHervé Poussineau 1028104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data) 1029104655a5SHervé Poussineau { 1030104655a5SHervé Poussineau DeviceClass *dc = DEVICE_CLASS(klass); 1031104655a5SHervé Poussineau 1032104655a5SHervé Poussineau set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 1033104655a5SHervé Poussineau dc->realize = dp8393x_realize; 1034104655a5SHervé Poussineau dc->reset = dp8393x_reset; 10351670735dSHervé Poussineau dc->vmsd = &vmstate_dp8393x; 10364f67d30bSMarc-André Lureau device_class_set_props(dc, dp8393x_properties); 1037104655a5SHervé Poussineau } 1038104655a5SHervé Poussineau 1039104655a5SHervé Poussineau static const TypeInfo dp8393x_info = { 1040104655a5SHervé Poussineau .name = TYPE_DP8393X, 1041104655a5SHervé Poussineau .parent = TYPE_SYS_BUS_DEVICE, 1042104655a5SHervé Poussineau .instance_size = sizeof(dp8393xState), 1043104655a5SHervé Poussineau .instance_init = dp8393x_instance_init, 1044104655a5SHervé Poussineau .class_init = dp8393x_class_init, 1045104655a5SHervé Poussineau }; 1046104655a5SHervé Poussineau 1047104655a5SHervé Poussineau static void dp8393x_register_types(void) 1048104655a5SHervé Poussineau { 1049104655a5SHervé Poussineau type_register_static(&dp8393x_info); 1050104655a5SHervé Poussineau } 1051104655a5SHervé Poussineau 1052104655a5SHervé Poussineau type_init(dp8393x_register_types) 1053