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 "qemu/osdep.h" |
31 | #include "hw/irq.h" |
32 | #include "hw/pci/pci.h" |
33 | #include "hw/qdev-properties.h" |
34 | #include "migration/vmstate.h" |
35 | #include "net/net.h" |
36 | #include "qemu/module.h" |
37 | #include "qemu/timer.h" |
38 | #include "sysemu/dma.h" |
39 | #include "sysemu/sysemu.h" |
40 | #include "trace.h" |
41 | |
42 | #include "pcnet.h" |
43 | |
44 | //#define PCNET_DEBUG |
45 | //#define PCNET_DEBUG_IO |
46 | //#define PCNET_DEBUG_BCR |
47 | //#define PCNET_DEBUG_CSR |
48 | //#define PCNET_DEBUG_RMD |
49 | //#define PCNET_DEBUG_TMD |
50 | //#define PCNET_DEBUG_MATCH |
51 | |
52 | #define TYPE_PCI_PCNET "pcnet" |
53 | |
54 | #define PCI_PCNET(obj) \ |
55 | OBJECT_CHECK(PCIPCNetState, (obj), TYPE_PCI_PCNET) |
56 | |
57 | typedef struct { |
58 | /*< private >*/ |
59 | PCIDevice parent_obj; |
60 | /*< public >*/ |
61 | |
62 | PCNetState state; |
63 | MemoryRegion io_bar; |
64 | } PCIPCNetState; |
65 | |
66 | static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) |
67 | { |
68 | PCNetState *s = opaque; |
69 | |
70 | trace_pcnet_aprom_writeb(opaque, addr, val); |
71 | if (BCR_APROMWE(s)) { |
72 | s->prom[addr & 15] = val; |
73 | } |
74 | } |
75 | |
76 | static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) |
77 | { |
78 | PCNetState *s = opaque; |
79 | uint32_t val = s->prom[addr & 15]; |
80 | |
81 | trace_pcnet_aprom_readb(opaque, addr, val); |
82 | return val; |
83 | } |
84 | |
85 | static uint64_t pcnet_ioport_read(void *opaque, hwaddr addr, |
86 | unsigned size) |
87 | { |
88 | PCNetState *d = opaque; |
89 | |
90 | trace_pcnet_ioport_read(opaque, addr, size); |
91 | if (addr < 0x10) { |
92 | if (!BCR_DWIO(d) && size == 1) { |
93 | return pcnet_aprom_readb(d, addr); |
94 | } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { |
95 | return pcnet_aprom_readb(d, addr) | |
96 | (pcnet_aprom_readb(d, addr + 1) << 8); |
97 | } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { |
98 | return pcnet_aprom_readb(d, addr) | |
99 | (pcnet_aprom_readb(d, addr + 1) << 8) | |
100 | (pcnet_aprom_readb(d, addr + 2) << 16) | |
101 | (pcnet_aprom_readb(d, addr + 3) << 24); |
102 | } |
103 | } else { |
104 | if (size == 2) { |
105 | return pcnet_ioport_readw(d, addr); |
106 | } else if (size == 4) { |
107 | return pcnet_ioport_readl(d, addr); |
108 | } |
109 | } |
110 | return ((uint64_t)1 << (size * 8)) - 1; |
111 | } |
112 | |
113 | static void pcnet_ioport_write(void *opaque, hwaddr addr, |
114 | uint64_t data, unsigned size) |
115 | { |
116 | PCNetState *d = opaque; |
117 | |
118 | trace_pcnet_ioport_write(opaque, addr, data, size); |
119 | if (addr < 0x10) { |
120 | if (!BCR_DWIO(d) && size == 1) { |
121 | pcnet_aprom_writeb(d, addr, data); |
122 | } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { |
123 | pcnet_aprom_writeb(d, addr, data & 0xff); |
124 | pcnet_aprom_writeb(d, addr + 1, data >> 8); |
125 | } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { |
126 | pcnet_aprom_writeb(d, addr, data & 0xff); |
127 | pcnet_aprom_writeb(d, addr + 1, (data >> 8) & 0xff); |
128 | pcnet_aprom_writeb(d, addr + 2, (data >> 16) & 0xff); |
129 | pcnet_aprom_writeb(d, addr + 3, data >> 24); |
130 | } |
131 | } else { |
132 | if (size == 2) { |
133 | pcnet_ioport_writew(d, addr, data); |
134 | } else if (size == 4) { |
135 | pcnet_ioport_writel(d, addr, data); |
136 | } |
137 | } |
138 | } |
139 | |
140 | static const MemoryRegionOps pcnet_io_ops = { |
141 | .read = pcnet_ioport_read, |
142 | .write = pcnet_ioport_write, |
143 | .endianness = DEVICE_LITTLE_ENDIAN, |
144 | }; |
145 | |
146 | static const VMStateDescription vmstate_pci_pcnet = { |
147 | .name = "pcnet" , |
148 | .version_id = 3, |
149 | .minimum_version_id = 2, |
150 | .fields = (VMStateField[]) { |
151 | VMSTATE_PCI_DEVICE(parent_obj, PCIPCNetState), |
152 | VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState), |
153 | VMSTATE_END_OF_LIST() |
154 | } |
155 | }; |
156 | |
157 | /* PCI interface */ |
158 | |
159 | static const MemoryRegionOps pcnet_mmio_ops = { |
160 | .read = pcnet_ioport_read, |
161 | .write = pcnet_ioport_write, |
162 | .valid.min_access_size = 1, |
163 | .valid.max_access_size = 4, |
164 | .impl.min_access_size = 1, |
165 | .impl.max_access_size = 4, |
166 | .endianness = DEVICE_LITTLE_ENDIAN, |
167 | }; |
168 | |
169 | static void pci_physical_memory_write(void *dma_opaque, hwaddr addr, |
170 | uint8_t *buf, int len, int do_bswap) |
171 | { |
172 | pci_dma_write(dma_opaque, addr, buf, len); |
173 | } |
174 | |
175 | static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, |
176 | uint8_t *buf, int len, int do_bswap) |
177 | { |
178 | pci_dma_read(dma_opaque, addr, buf, len); |
179 | } |
180 | |
181 | static void pci_pcnet_uninit(PCIDevice *dev) |
182 | { |
183 | PCIPCNetState *d = PCI_PCNET(dev); |
184 | |
185 | qemu_free_irq(d->state.irq); |
186 | timer_del(d->state.poll_timer); |
187 | timer_free(d->state.poll_timer); |
188 | qemu_del_nic(d->state.nic); |
189 | } |
190 | |
191 | static NetClientInfo net_pci_pcnet_info = { |
192 | .type = NET_CLIENT_DRIVER_NIC, |
193 | .size = sizeof(NICState), |
194 | .receive = pcnet_receive, |
195 | .link_status_changed = pcnet_set_link_status, |
196 | }; |
197 | |
198 | static void pci_pcnet_realize(PCIDevice *pci_dev, Error **errp) |
199 | { |
200 | PCIPCNetState *d = PCI_PCNET(pci_dev); |
201 | PCNetState *s = &d->state; |
202 | uint8_t *pci_conf; |
203 | |
204 | #if 0 |
205 | printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n" , |
206 | sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD)); |
207 | #endif |
208 | |
209 | pci_conf = pci_dev->config; |
210 | |
211 | pci_set_word(pci_conf + PCI_STATUS, |
212 | PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); |
213 | |
214 | pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0); |
215 | pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0); |
216 | |
217 | pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ |
218 | pci_conf[PCI_MIN_GNT] = 0x06; |
219 | pci_conf[PCI_MAX_LAT] = 0xff; |
220 | |
221 | /* Handler for memory-mapped I/O */ |
222 | memory_region_init_io(&d->state.mmio, OBJECT(d), &pcnet_mmio_ops, s, |
223 | "pcnet-mmio" , PCNET_PNPMMIO_SIZE); |
224 | |
225 | memory_region_init_io(&d->io_bar, OBJECT(d), &pcnet_io_ops, s, "pcnet-io" , |
226 | PCNET_IOPORT_SIZE); |
227 | pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io_bar); |
228 | |
229 | pci_register_bar(pci_dev, 1, 0, &s->mmio); |
230 | |
231 | s->irq = pci_allocate_irq(pci_dev); |
232 | s->phys_mem_read = pci_physical_memory_read; |
233 | s->phys_mem_write = pci_physical_memory_write; |
234 | s->dma_opaque = pci_dev; |
235 | |
236 | pcnet_common_init(DEVICE(pci_dev), s, &net_pci_pcnet_info); |
237 | } |
238 | |
239 | static void pci_reset(DeviceState *dev) |
240 | { |
241 | PCIPCNetState *d = PCI_PCNET(dev); |
242 | |
243 | pcnet_h_reset(&d->state); |
244 | } |
245 | |
246 | static void pcnet_instance_init(Object *obj) |
247 | { |
248 | PCIPCNetState *d = PCI_PCNET(obj); |
249 | PCNetState *s = &d->state; |
250 | |
251 | device_add_bootindex_property(obj, &s->conf.bootindex, |
252 | "bootindex" , "/ethernet-phy@0" , |
253 | DEVICE(obj), NULL); |
254 | } |
255 | |
256 | static Property pcnet_properties[] = { |
257 | DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), |
258 | DEFINE_PROP_END_OF_LIST(), |
259 | }; |
260 | |
261 | static void pcnet_class_init(ObjectClass *klass, void *data) |
262 | { |
263 | DeviceClass *dc = DEVICE_CLASS(klass); |
264 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
265 | |
266 | k->realize = pci_pcnet_realize; |
267 | k->exit = pci_pcnet_uninit; |
268 | k->romfile = "efi-pcnet.rom" , |
269 | k->vendor_id = PCI_VENDOR_ID_AMD; |
270 | k->device_id = PCI_DEVICE_ID_AMD_LANCE; |
271 | k->revision = 0x10; |
272 | k->class_id = PCI_CLASS_NETWORK_ETHERNET; |
273 | dc->reset = pci_reset; |
274 | dc->vmsd = &vmstate_pci_pcnet; |
275 | dc->props = pcnet_properties; |
276 | set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); |
277 | } |
278 | |
279 | static const TypeInfo pcnet_info = { |
280 | .name = TYPE_PCI_PCNET, |
281 | .parent = TYPE_PCI_DEVICE, |
282 | .instance_size = sizeof(PCIPCNetState), |
283 | .class_init = pcnet_class_init, |
284 | .instance_init = pcnet_instance_init, |
285 | .interfaces = (InterfaceInfo[]) { |
286 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
287 | { }, |
288 | }, |
289 | }; |
290 | |
291 | static void pci_pcnet_register_types(void) |
292 | { |
293 | type_register_static(&pcnet_info); |
294 | } |
295 | |
296 | type_init(pci_pcnet_register_types) |
297 | |