]>
Commit | Line | Data |
---|---|---|
b43848a1 EI |
1 | /* |
2 | * QEMU model of the Xilinx Ethernet Lite MAC. | |
3 | * | |
4 | * Copyright (c) 2009 Edgar E. Iglesias. | |
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 | #include "sysbus.h" | |
26 | #include "hw.h" | |
27 | #include "net.h" | |
28 | ||
29 | #define D(x) | |
30 | #define R_TX_BUF0 0 | |
31 | #define R_TX_LEN0 (0x07f4 / 4) | |
32 | #define R_TX_GIE0 (0x07f8 / 4) | |
33 | #define R_TX_CTRL0 (0x07fc / 4) | |
34 | #define R_TX_BUF1 (0x0800 / 4) | |
35 | #define R_TX_LEN1 (0x0ff4 / 4) | |
36 | #define R_TX_CTRL1 (0x0ffc / 4) | |
37 | ||
38 | #define R_RX_BUF0 (0x1000 / 4) | |
39 | #define R_RX_CTRL0 (0x17fc / 4) | |
40 | #define R_RX_BUF1 (0x1800 / 4) | |
41 | #define R_RX_CTRL1 (0x1ffc / 4) | |
42 | #define R_MAX (0x2000 / 4) | |
43 | ||
44 | #define GIE_GIE 0x80000000 | |
45 | ||
46 | #define CTRL_I 0x8 | |
47 | #define CTRL_P 0x2 | |
48 | #define CTRL_S 0x1 | |
49 | ||
50 | struct xlx_ethlite | |
51 | { | |
52 | SysBusDevice busdev; | |
53 | qemu_irq irq; | |
54 | VLANClientState *vc; | |
55 | ||
56 | unsigned int c_tx_pingpong; | |
57 | unsigned int c_rx_pingpong; | |
58 | unsigned int txbuf; | |
59 | unsigned int rxbuf; | |
60 | ||
61 | uint8_t macaddr[6]; | |
62 | uint32_t regs[R_MAX]; | |
63 | }; | |
64 | ||
65 | static inline void eth_pulse_irq(struct xlx_ethlite *s) | |
66 | { | |
67 | /* Only the first gie reg is active. */ | |
68 | if (s->regs[R_TX_GIE0] & GIE_GIE) { | |
69 | qemu_irq_pulse(s->irq); | |
70 | } | |
71 | } | |
72 | ||
73 | static uint32_t eth_readl (void *opaque, target_phys_addr_t addr) | |
74 | { | |
75 | struct xlx_ethlite *s = opaque; | |
76 | uint32_t r = 0; | |
77 | ||
78 | addr >>= 2; | |
79 | ||
80 | switch (addr) | |
81 | { | |
82 | case R_TX_GIE0: | |
83 | case R_TX_LEN0: | |
84 | case R_TX_LEN1: | |
85 | case R_TX_CTRL1: | |
86 | case R_TX_CTRL0: | |
87 | case R_RX_CTRL1: | |
88 | case R_RX_CTRL0: | |
89 | r = s->regs[addr]; | |
90 | D(qemu_log("%s %x=%x\n", __func__, addr * 4, r)); | |
91 | break; | |
92 | ||
93 | /* Rx packet data is endian fixed at the way into the rx rams. This | |
94 | * speeds things up because the ethlite MAC does not have a len | |
95 | * register. That means the CPU will issue MMIO reads for the entire | |
96 | * 2k rx buffer even for small packets. | |
97 | */ | |
98 | default: | |
99 | r = s->regs[addr]; | |
100 | break; | |
101 | } | |
102 | return r; | |
103 | } | |
104 | ||
105 | static void | |
106 | eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | |
107 | { | |
108 | struct xlx_ethlite *s = opaque; | |
109 | unsigned int base = 0; | |
110 | ||
111 | addr >>= 2; | |
112 | switch (addr) | |
113 | { | |
114 | case R_TX_CTRL0: | |
115 | case R_TX_CTRL1: | |
116 | if (addr == R_TX_CTRL1) | |
117 | base = 0x800 / 4; | |
118 | ||
119 | D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); | |
120 | if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { | |
121 | qemu_send_packet(s->vc, | |
122 | (void *) &s->regs[base], | |
123 | s->regs[base + R_TX_LEN0]); | |
124 | D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); | |
125 | if (s->regs[base + R_TX_CTRL0] & CTRL_I) | |
126 | eth_pulse_irq(s); | |
127 | } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { | |
128 | memcpy(&s->macaddr[0], &s->regs[base], 6); | |
129 | if (s->regs[base + R_TX_CTRL0] & CTRL_I) | |
130 | eth_pulse_irq(s); | |
131 | } | |
132 | ||
133 | /* We are fast and get ready pretty much immediately so | |
134 | we actually never flip the S nor P bits to one. */ | |
135 | s->regs[addr] = value & ~(CTRL_P | CTRL_S); | |
136 | break; | |
137 | ||
138 | /* Keep these native. */ | |
139 | case R_TX_LEN0: | |
140 | case R_TX_LEN1: | |
141 | case R_TX_GIE0: | |
142 | case R_RX_CTRL0: | |
143 | case R_RX_CTRL1: | |
144 | D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); | |
145 | s->regs[addr] = value; | |
146 | break; | |
147 | ||
148 | /* Packet data, make sure it stays BE. */ | |
149 | default: | |
150 | s->regs[addr] = cpu_to_be32(value); | |
151 | break; | |
152 | } | |
153 | } | |
154 | ||
155 | static CPUReadMemoryFunc *eth_read[] = { | |
156 | NULL, NULL, ð_readl, | |
157 | }; | |
158 | ||
159 | static CPUWriteMemoryFunc *eth_write[] = { | |
160 | NULL, NULL, ð_writel, | |
161 | }; | |
162 | ||
df12c1f5 | 163 | static int eth_can_rx(VLANClientState *vc) |
b43848a1 | 164 | { |
df12c1f5 | 165 | struct xlx_ethlite *s = vc->opaque; |
b43848a1 EI |
166 | int r; |
167 | r = !(s->regs[R_RX_CTRL0] & CTRL_S); | |
168 | qemu_log("%s %d\n", __func__, r); | |
169 | return r; | |
170 | } | |
171 | ||
df12c1f5 | 172 | static ssize_t eth_rx(VLANClientState *vc, const uint8_t *buf, size_t size) |
b43848a1 | 173 | { |
df12c1f5 | 174 | struct xlx_ethlite *s = vc->opaque; |
b43848a1 EI |
175 | unsigned int rxbase = s->rxbuf * (0x800 / 4); |
176 | int i; | |
177 | ||
178 | /* DA filter. */ | |
179 | if (!(buf[0] & 0x80) && memcmp(&s->macaddr[0], buf, 6)) | |
df12c1f5 | 180 | return size; |
b43848a1 EI |
181 | |
182 | if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { | |
183 | D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0])); | |
df12c1f5 | 184 | return -1; |
b43848a1 EI |
185 | } |
186 | ||
187 | D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase)); | |
188 | memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); | |
189 | ||
190 | /* Bring it into host endianess. */ | |
191 | for (i = 0; i < ((size + 3) / 4); i++) { | |
192 | uint32_t d = s->regs[rxbase + R_RX_BUF0 + i]; | |
193 | s->regs[rxbase + R_RX_BUF0 + i] = be32_to_cpu(d); | |
194 | } | |
195 | ||
196 | s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; | |
197 | if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I) | |
198 | eth_pulse_irq(s); | |
199 | ||
200 | /* If c_rx_pingpong was set flip buffers. */ | |
201 | s->rxbuf ^= s->c_rx_pingpong; | |
df12c1f5 | 202 | return size; |
b43848a1 EI |
203 | } |
204 | ||
205 | static void eth_cleanup(VLANClientState *vc) | |
206 | { | |
207 | struct xlx_ethlite *s = vc->opaque; | |
208 | qemu_free(s); | |
209 | } | |
210 | ||
211 | static void xilinx_ethlite_init(SysBusDevice *dev) | |
212 | { | |
213 | struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev); | |
214 | int regs; | |
215 | ||
216 | sysbus_init_irq(dev, &s->irq); | |
217 | s->c_tx_pingpong = qdev_get_prop_int(&dev->qdev, "txpingpong", 1); | |
218 | s->c_rx_pingpong = qdev_get_prop_int(&dev->qdev, "rxpingpong", 1); | |
219 | s->rxbuf = 0; | |
220 | ||
1eed09cb | 221 | regs = cpu_register_io_memory(eth_read, eth_write, s); |
b43848a1 EI |
222 | sysbus_init_mmio(dev, R_MAX * 4, regs); |
223 | ||
224 | qdev_get_macaddr(&dev->qdev, s->macaddr); | |
225 | s->vc = qdev_get_vlan_client(&dev->qdev, | |
df12c1f5 | 226 | eth_can_rx, eth_rx, NULL, eth_cleanup, s); |
b43848a1 EI |
227 | } |
228 | ||
229 | static void xilinx_ethlite_register(void) | |
230 | { | |
231 | sysbus_register_dev("xilinx,ethlite", sizeof (struct xlx_ethlite), | |
232 | xilinx_ethlite_init); | |
233 | } | |
234 | ||
235 | device_init(xilinx_ethlite_register) |