1 /* 2 * QEMU AMD PC-Net II (Am79C970A) PCI emulation 3 * 4 * Copyright (c) 2004 Antony T Curtis 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 /* This software was written to be compatible with the specification: 26 * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet 27 * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 28 */ 29 30 #include "hw/pci/pci.h" 31 #include "net/net.h" 32 #include "hw/loader.h" 33 #include "qemu/timer.h" 34 #include "sysemu/dma.h" 35 #include "sysemu/sysemu.h" 36 37 #include "pcnet.h" 38 39 //#define PCNET_DEBUG 40 //#define PCNET_DEBUG_IO 41 //#define PCNET_DEBUG_BCR 42 //#define PCNET_DEBUG_CSR 43 //#define PCNET_DEBUG_RMD 44 //#define PCNET_DEBUG_TMD 45 //#define PCNET_DEBUG_MATCH 46 47 #define TYPE_PCI_PCNET "pcnet" 48 49 #define PCI_PCNET(obj) \ 50 OBJECT_CHECK(PCIPCNetState, (obj), TYPE_PCI_PCNET) 51 52 typedef struct { 53 /*< private >*/ 54 PCIDevice parent_obj; 55 /*< public >*/ 56 57 PCNetState state; 58 MemoryRegion io_bar; 59 } PCIPCNetState; 60 61 static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) 62 { 63 PCNetState *s = opaque; 64 #ifdef PCNET_DEBUG 65 printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val); 66 #endif 67 if (BCR_APROMWE(s)) { 68 s->prom[addr & 15] = val; 69 } 70 } 71 72 static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) 73 { 74 PCNetState *s = opaque; 75 uint32_t val = s->prom[addr & 15]; 76 #ifdef PCNET_DEBUG 77 printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val); 78 #endif 79 return val; 80 } 81 82 static uint64_t pcnet_ioport_read(void *opaque, hwaddr addr, 83 unsigned size) 84 { 85 PCNetState *d = opaque; 86 87 if (addr < 0x10) { 88 if (!BCR_DWIO(d) && size == 1) { 89 return pcnet_aprom_readb(d, addr); 90 } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { 91 return pcnet_aprom_readb(d, addr) | 92 (pcnet_aprom_readb(d, addr + 1) << 8); 93 } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { 94 return pcnet_aprom_readb(d, addr) | 95 (pcnet_aprom_readb(d, addr + 1) << 8) | 96 (pcnet_aprom_readb(d, addr + 2) << 16) | 97 (pcnet_aprom_readb(d, addr + 3) << 24); 98 } 99 } else { 100 if (size == 2) { 101 return pcnet_ioport_readw(d, addr); 102 } else if (size == 4) { 103 return pcnet_ioport_readl(d, addr); 104 } 105 } 106 return ((uint64_t)1 << (size * 8)) - 1; 107 } 108 109 static void pcnet_ioport_write(void *opaque, hwaddr addr, 110 uint64_t data, unsigned size) 111 { 112 PCNetState *d = opaque; 113 114 if (addr < 0x10) { 115 if (!BCR_DWIO(d) && size == 1) { 116 pcnet_aprom_writeb(d, addr, data); 117 } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { 118 pcnet_aprom_writeb(d, addr, data & 0xff); 119 pcnet_aprom_writeb(d, addr + 1, data >> 8); 120 } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { 121 pcnet_aprom_writeb(d, addr, data & 0xff); 122 pcnet_aprom_writeb(d, addr + 1, (data >> 8) & 0xff); 123 pcnet_aprom_writeb(d, addr + 2, (data >> 16) & 0xff); 124 pcnet_aprom_writeb(d, addr + 3, data >> 24); 125 } 126 } else { 127 if (size == 2) { 128 pcnet_ioport_writew(d, addr, data); 129 } else if (size == 4) { 130 pcnet_ioport_writel(d, addr, data); 131 } 132 } 133 } 134 135 static const MemoryRegionOps pcnet_io_ops = { 136 .read = pcnet_ioport_read, 137 .write = pcnet_ioport_write, 138 .endianness = DEVICE_LITTLE_ENDIAN, 139 }; 140 141 static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) 142 { 143 PCNetState *d = opaque; 144 #ifdef PCNET_DEBUG_IO 145 printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr, 146 val); 147 #endif 148 if (!(addr & 0x10)) 149 pcnet_aprom_writeb(d, addr & 0x0f, val); 150 } 151 152 static uint32_t pcnet_mmio_readb(void *opaque, hwaddr addr) 153 { 154 PCNetState *d = opaque; 155 uint32_t val = -1; 156 if (!(addr & 0x10)) 157 val = pcnet_aprom_readb(d, addr & 0x0f); 158 #ifdef PCNET_DEBUG_IO 159 printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, 160 val & 0xff); 161 #endif 162 return val; 163 } 164 165 static void pcnet_mmio_writew(void *opaque, hwaddr addr, uint32_t val) 166 { 167 PCNetState *d = opaque; 168 #ifdef PCNET_DEBUG_IO 169 printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, 170 val); 171 #endif 172 if (addr & 0x10) 173 pcnet_ioport_writew(d, addr & 0x0f, val); 174 else { 175 addr &= 0x0f; 176 pcnet_aprom_writeb(d, addr, val & 0xff); 177 pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); 178 } 179 } 180 181 static uint32_t pcnet_mmio_readw(void *opaque, hwaddr addr) 182 { 183 PCNetState *d = opaque; 184 uint32_t val = -1; 185 if (addr & 0x10) 186 val = pcnet_ioport_readw(d, addr & 0x0f); 187 else { 188 addr &= 0x0f; 189 val = pcnet_aprom_readb(d, addr+1); 190 val <<= 8; 191 val |= pcnet_aprom_readb(d, addr); 192 } 193 #ifdef PCNET_DEBUG_IO 194 printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr, 195 val & 0xffff); 196 #endif 197 return val; 198 } 199 200 static void pcnet_mmio_writel(void *opaque, hwaddr addr, uint32_t val) 201 { 202 PCNetState *d = opaque; 203 #ifdef PCNET_DEBUG_IO 204 printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr, 205 val); 206 #endif 207 if (addr & 0x10) 208 pcnet_ioport_writel(d, addr & 0x0f, val); 209 else { 210 addr &= 0x0f; 211 pcnet_aprom_writeb(d, addr, val & 0xff); 212 pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); 213 pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16); 214 pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24); 215 } 216 } 217 218 static uint32_t pcnet_mmio_readl(void *opaque, hwaddr addr) 219 { 220 PCNetState *d = opaque; 221 uint32_t val; 222 if (addr & 0x10) 223 val = pcnet_ioport_readl(d, addr & 0x0f); 224 else { 225 addr &= 0x0f; 226 val = pcnet_aprom_readb(d, addr+3); 227 val <<= 8; 228 val |= pcnet_aprom_readb(d, addr+2); 229 val <<= 8; 230 val |= pcnet_aprom_readb(d, addr+1); 231 val <<= 8; 232 val |= pcnet_aprom_readb(d, addr); 233 } 234 #ifdef PCNET_DEBUG_IO 235 printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, 236 val); 237 #endif 238 return val; 239 } 240 241 static const VMStateDescription vmstate_pci_pcnet = { 242 .name = "pcnet", 243 .version_id = 3, 244 .minimum_version_id = 2, 245 .fields = (VMStateField[]) { 246 VMSTATE_PCI_DEVICE(parent_obj, PCIPCNetState), 247 VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState), 248 VMSTATE_END_OF_LIST() 249 } 250 }; 251 252 /* PCI interface */ 253 254 static const MemoryRegionOps pcnet_mmio_ops = { 255 .old_mmio = { 256 .read = { pcnet_mmio_readb, pcnet_mmio_readw, pcnet_mmio_readl }, 257 .write = { pcnet_mmio_writeb, pcnet_mmio_writew, pcnet_mmio_writel }, 258 }, 259 .endianness = DEVICE_LITTLE_ENDIAN, 260 }; 261 262 static void pci_physical_memory_write(void *dma_opaque, hwaddr addr, 263 uint8_t *buf, int len, int do_bswap) 264 { 265 pci_dma_write(dma_opaque, addr, buf, len); 266 } 267 268 static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, 269 uint8_t *buf, int len, int do_bswap) 270 { 271 pci_dma_read(dma_opaque, addr, buf, len); 272 } 273 274 static void pci_pcnet_cleanup(NetClientState *nc) 275 { 276 PCNetState *d = qemu_get_nic_opaque(nc); 277 278 pcnet_common_cleanup(d); 279 } 280 281 static void pci_pcnet_uninit(PCIDevice *dev) 282 { 283 PCIPCNetState *d = PCI_PCNET(dev); 284 285 qemu_free_irq(d->state.irq); 286 timer_del(d->state.poll_timer); 287 timer_free(d->state.poll_timer); 288 qemu_del_nic(d->state.nic); 289 } 290 291 static NetClientInfo net_pci_pcnet_info = { 292 .type = NET_CLIENT_OPTIONS_KIND_NIC, 293 .size = sizeof(NICState), 294 .can_receive = pcnet_can_receive, 295 .receive = pcnet_receive, 296 .link_status_changed = pcnet_set_link_status, 297 .cleanup = pci_pcnet_cleanup, 298 }; 299 300 static int pci_pcnet_init(PCIDevice *pci_dev) 301 { 302 PCIPCNetState *d = PCI_PCNET(pci_dev); 303 PCNetState *s = &d->state; 304 uint8_t *pci_conf; 305 306 #if 0 307 printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n", 308 sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD)); 309 #endif 310 311 pci_conf = pci_dev->config; 312 313 pci_set_word(pci_conf + PCI_STATUS, 314 PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); 315 316 pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0); 317 pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0); 318 319 pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ 320 pci_conf[PCI_MIN_GNT] = 0x06; 321 pci_conf[PCI_MAX_LAT] = 0xff; 322 323 /* Handler for memory-mapped I/O */ 324 memory_region_init_io(&d->state.mmio, OBJECT(d), &pcnet_mmio_ops, s, 325 "pcnet-mmio", PCNET_PNPMMIO_SIZE); 326 327 memory_region_init_io(&d->io_bar, OBJECT(d), &pcnet_io_ops, s, "pcnet-io", 328 PCNET_IOPORT_SIZE); 329 pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io_bar); 330 331 pci_register_bar(pci_dev, 1, 0, &s->mmio); 332 333 s->irq = pci_allocate_irq(pci_dev); 334 s->phys_mem_read = pci_physical_memory_read; 335 s->phys_mem_write = pci_physical_memory_write; 336 s->dma_opaque = pci_dev; 337 338 return pcnet_common_init(DEVICE(pci_dev), s, &net_pci_pcnet_info); 339 } 340 341 static void pci_reset(DeviceState *dev) 342 { 343 PCIPCNetState *d = PCI_PCNET(dev); 344 345 pcnet_h_reset(&d->state); 346 } 347 348 static void pcnet_instance_init(Object *obj) 349 { 350 PCIPCNetState *d = PCI_PCNET(obj); 351 PCNetState *s = &d->state; 352 353 device_add_bootindex_property(obj, &s->conf.bootindex, 354 "bootindex", "/ethernet-phy@0", 355 DEVICE(obj), NULL); 356 } 357 358 static Property pcnet_properties[] = { 359 DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), 360 DEFINE_PROP_END_OF_LIST(), 361 }; 362 363 static void pcnet_class_init(ObjectClass *klass, void *data) 364 { 365 DeviceClass *dc = DEVICE_CLASS(klass); 366 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 367 368 k->init = pci_pcnet_init; 369 k->exit = pci_pcnet_uninit; 370 k->romfile = "efi-pcnet.rom", 371 k->vendor_id = PCI_VENDOR_ID_AMD; 372 k->device_id = PCI_DEVICE_ID_AMD_LANCE; 373 k->revision = 0x10; 374 k->class_id = PCI_CLASS_NETWORK_ETHERNET; 375 dc->reset = pci_reset; 376 dc->vmsd = &vmstate_pci_pcnet; 377 dc->props = pcnet_properties; 378 set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 379 } 380 381 static const TypeInfo pcnet_info = { 382 .name = TYPE_PCI_PCNET, 383 .parent = TYPE_PCI_DEVICE, 384 .instance_size = sizeof(PCIPCNetState), 385 .class_init = pcnet_class_init, 386 .instance_init = pcnet_instance_init, 387 }; 388 389 static void pci_pcnet_register_types(void) 390 { 391 type_register_static(&pcnet_info); 392 } 393 394 type_init(pci_pcnet_register_types) 395