edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 1 | /* |
| 2 | * QEMU ETRAX Ethernet Controller. |
| 3 | * |
| 4 | * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. |
| 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 | |
Peter Maydell | e8d4046 | 2016-01-26 18:17:11 +0000 | [diff] [blame] | 25 | #include "qemu/osdep.h" |
Cédric Le Goater | c24828b | 2018-10-01 08:37:53 +0200 | [diff] [blame] | 26 | #include "qapi/error.h" |
Paolo Bonzini | 83c9f4c | 2013-02-04 15:40:22 +0100 | [diff] [blame] | 27 | #include "hw/sysbus.h" |
Paolo Bonzini | 1422e32 | 2012-10-24 08:43:34 +0200 | [diff] [blame] | 28 | #include "net/net.h" |
Paolo Bonzini | 0d09e41 | 2013-02-05 17:06:20 +0100 | [diff] [blame] | 29 | #include "hw/cris/etraxfs.h" |
Markus Armbruster | 5a8de10 | 2015-12-17 17:35:12 +0100 | [diff] [blame] | 30 | #include "qemu/error-report.h" |
Markus Armbruster | 0b8fa32 | 2019-05-23 16:35:07 +0200 | [diff] [blame] | 31 | #include "qemu/module.h" |
Philippe Mathieu-Daudé | 4b46fdd | 2018-06-21 14:12:55 -0300 | [diff] [blame] | 32 | #include "trace.h" |
Eduardo Habkost | db1015e | 2020-09-03 16:43:22 -0400 | [diff] [blame] | 33 | #include "qom/object.h" |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 34 | |
| 35 | #define D(x) |
| 36 | |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 37 | /* Advertisement control register. */ |
| 38 | #define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ |
| 39 | #define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ |
| 40 | #define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ |
| 41 | #define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ |
| 42 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 43 | /* |
| 44 | * The MDIO extensions in the TDK PHY model were reversed engineered from the |
edgar_igl | 2e56350 | 2008-05-13 23:51:49 +0000 | [diff] [blame] | 45 | * linux driver (PHYID and Diagnostics reg). |
| 46 | * TODO: Add friendly names for the register nums. |
| 47 | */ |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 48 | struct qemu_phy |
| 49 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 50 | uint32_t regs[32]; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 51 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 52 | int link; |
edgar_igl | 94410b7 | 2009-01-09 00:04:35 +0000 | [diff] [blame] | 53 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 54 | unsigned int (*read)(struct qemu_phy *phy, unsigned int req); |
| 55 | void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 56 | }; |
| 57 | |
| 58 | static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) |
| 59 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 60 | int regnum; |
| 61 | unsigned r = 0; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 62 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 63 | regnum = req & 0x1f; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 64 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 65 | switch (regnum) { |
| 66 | case 1: |
| 67 | if (!phy->link) { |
| 68 | break; |
| 69 | } |
| 70 | /* MR1. */ |
| 71 | /* Speeds and modes. */ |
| 72 | r |= (1 << 13) | (1 << 14); |
| 73 | r |= (1 << 11) | (1 << 12); |
| 74 | r |= (1 << 5); /* Autoneg complete. */ |
| 75 | r |= (1 << 3); /* Autoneg able. */ |
| 76 | r |= (1 << 2); /* link. */ |
| 77 | break; |
| 78 | case 5: |
| 79 | /* Link partner ability. |
| 80 | We are kind; always agree with whatever best mode |
| 81 | the guest advertises. */ |
| 82 | r = 1 << 14; /* Success. */ |
| 83 | /* Copy advertised modes. */ |
| 84 | r |= phy->regs[4] & (15 << 5); |
| 85 | /* Autoneg support. */ |
| 86 | r |= 1; |
| 87 | break; |
| 88 | case 18: |
| 89 | { |
| 90 | /* Diagnostics reg. */ |
| 91 | int duplex = 0; |
| 92 | int speed_100 = 0; |
edgar_igl | 2e56350 | 2008-05-13 23:51:49 +0000 | [diff] [blame] | 93 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 94 | if (!phy->link) { |
| 95 | break; |
| 96 | } |
edgar_igl | 94410b7 | 2009-01-09 00:04:35 +0000 | [diff] [blame] | 97 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 98 | /* Are we advertising 100 half or 100 duplex ? */ |
| 99 | speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); |
| 100 | speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 101 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 102 | /* Are we advertising 10 duplex or 100 duplex ? */ |
| 103 | duplex = !!(phy->regs[4] & ADVERTISE_100FULL); |
| 104 | duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); |
| 105 | r = (speed_100 << 10) | (duplex << 11); |
| 106 | } |
| 107 | break; |
edgar_igl | 2e56350 | 2008-05-13 23:51:49 +0000 | [diff] [blame] | 108 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 109 | default: |
| 110 | r = phy->regs[regnum]; |
| 111 | break; |
| 112 | } |
Philippe Mathieu-Daudé | 4b46fdd | 2018-06-21 14:12:55 -0300 | [diff] [blame] | 113 | trace_mdio_phy_read(regnum, r); |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 114 | return r; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 115 | } |
| 116 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 117 | static void |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 118 | tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) |
| 119 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 120 | int regnum; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 121 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 122 | regnum = req & 0x1f; |
Philippe Mathieu-Daudé | 4b46fdd | 2018-06-21 14:12:55 -0300 | [diff] [blame] | 123 | trace_mdio_phy_write(regnum, data); |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 124 | switch (regnum) { |
| 125 | default: |
| 126 | phy->regs[regnum] = data; |
| 127 | break; |
| 128 | } |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 129 | } |
| 130 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 131 | static void |
Cédric Le Goater | 56dff42 | 2018-10-01 08:37:54 +0200 | [diff] [blame] | 132 | tdk_reset(struct qemu_phy *phy) |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 133 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 134 | phy->regs[0] = 0x3100; |
| 135 | /* PHY Id. */ |
| 136 | phy->regs[2] = 0x0300; |
| 137 | phy->regs[3] = 0xe400; |
| 138 | /* Autonegotiation advertisement reg. */ |
| 139 | phy->regs[4] = 0x01E1; |
| 140 | phy->link = 1; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | struct qemu_mdio |
| 144 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 145 | /* bus. */ |
| 146 | int mdc; |
| 147 | int mdio; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 148 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 149 | /* decoder. */ |
| 150 | enum { |
| 151 | PREAMBLE, |
| 152 | SOF, |
| 153 | OPC, |
| 154 | ADDR, |
| 155 | REQ, |
| 156 | TURNAROUND, |
| 157 | DATA |
| 158 | } state; |
| 159 | unsigned int drive; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 160 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 161 | unsigned int cnt; |
| 162 | unsigned int addr; |
| 163 | unsigned int opc; |
| 164 | unsigned int req; |
| 165 | unsigned int data; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 166 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 167 | struct qemu_phy *devs[32]; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 168 | }; |
| 169 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 170 | static void |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 171 | mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) |
| 172 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 173 | bus->devs[addr & 0x1f] = phy; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 174 | } |
| 175 | |
edgar_igl | d297f46 | 2008-06-30 08:59:49 +0000 | [diff] [blame] | 176 | #ifdef USE_THIS_DEAD_CODE |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 177 | static void |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 178 | mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) |
| 179 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 180 | bus->devs[addr & 0x1f] = NULL; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 181 | } |
edgar_igl | d297f46 | 2008-06-30 08:59:49 +0000 | [diff] [blame] | 182 | #endif |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 183 | |
| 184 | static void mdio_read_req(struct qemu_mdio *bus) |
| 185 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 186 | struct qemu_phy *phy; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 187 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 188 | phy = bus->devs[bus->addr]; |
| 189 | if (phy && phy->read) { |
| 190 | bus->data = phy->read(phy, bus->req); |
| 191 | } else { |
| 192 | bus->data = 0xffff; |
| 193 | } |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | static void mdio_write_req(struct qemu_mdio *bus) |
| 197 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 198 | struct qemu_phy *phy; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 199 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 200 | phy = bus->devs[bus->addr]; |
| 201 | if (phy && phy->write) { |
| 202 | phy->write(phy, bus->req, bus->data); |
| 203 | } |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | static void mdio_cycle(struct qemu_mdio *bus) |
| 207 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 208 | bus->cnt++; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 209 | |
Philippe Mathieu-Daudé | 4b46fdd | 2018-06-21 14:12:55 -0300 | [diff] [blame] | 210 | trace_mdio_bitbang(bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive); |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 211 | #if 0 |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 212 | if (bus->mdc) { |
| 213 | printf("%d", bus->mdio); |
| 214 | } |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 215 | #endif |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 216 | switch (bus->state) { |
| 217 | case PREAMBLE: |
| 218 | if (bus->mdc) { |
| 219 | if (bus->cnt >= (32 * 2) && !bus->mdio) { |
| 220 | bus->cnt = 0; |
| 221 | bus->state = SOF; |
| 222 | bus->data = 0; |
| 223 | } |
| 224 | } |
| 225 | break; |
| 226 | case SOF: |
| 227 | if (bus->mdc) { |
| 228 | if (bus->mdio != 1) { |
| 229 | printf("WARNING: no SOF\n"); |
| 230 | } |
| 231 | if (bus->cnt == 1*2) { |
| 232 | bus->cnt = 0; |
| 233 | bus->opc = 0; |
| 234 | bus->state = OPC; |
| 235 | } |
| 236 | } |
| 237 | break; |
| 238 | case OPC: |
| 239 | if (bus->mdc) { |
| 240 | bus->opc <<= 1; |
| 241 | bus->opc |= bus->mdio & 1; |
| 242 | if (bus->cnt == 2*2) { |
| 243 | bus->cnt = 0; |
| 244 | bus->addr = 0; |
| 245 | bus->state = ADDR; |
| 246 | } |
| 247 | } |
| 248 | break; |
| 249 | case ADDR: |
| 250 | if (bus->mdc) { |
| 251 | bus->addr <<= 1; |
| 252 | bus->addr |= bus->mdio & 1; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 253 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 254 | if (bus->cnt == 5*2) { |
| 255 | bus->cnt = 0; |
| 256 | bus->req = 0; |
| 257 | bus->state = REQ; |
| 258 | } |
| 259 | } |
| 260 | break; |
| 261 | case REQ: |
| 262 | if (bus->mdc) { |
| 263 | bus->req <<= 1; |
| 264 | bus->req |= bus->mdio & 1; |
| 265 | if (bus->cnt == 5*2) { |
| 266 | bus->cnt = 0; |
| 267 | bus->state = TURNAROUND; |
| 268 | } |
| 269 | } |
| 270 | break; |
| 271 | case TURNAROUND: |
| 272 | if (bus->mdc && bus->cnt == 2*2) { |
| 273 | bus->mdio = 0; |
| 274 | bus->cnt = 0; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 275 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 276 | if (bus->opc == 2) { |
| 277 | bus->drive = 1; |
| 278 | mdio_read_req(bus); |
| 279 | bus->mdio = bus->data & 1; |
| 280 | } |
| 281 | bus->state = DATA; |
| 282 | } |
| 283 | break; |
| 284 | case DATA: |
| 285 | if (!bus->mdc) { |
| 286 | if (bus->drive) { |
| 287 | bus->mdio = !!(bus->data & (1 << 15)); |
| 288 | bus->data <<= 1; |
| 289 | } |
| 290 | } else { |
| 291 | if (!bus->drive) { |
| 292 | bus->data <<= 1; |
| 293 | bus->data |= bus->mdio; |
| 294 | } |
| 295 | if (bus->cnt == 16 * 2) { |
| 296 | bus->cnt = 0; |
| 297 | bus->state = PREAMBLE; |
| 298 | if (!bus->drive) { |
| 299 | mdio_write_req(bus); |
| 300 | } |
| 301 | bus->drive = 0; |
| 302 | } |
| 303 | } |
| 304 | break; |
| 305 | default: |
| 306 | break; |
| 307 | } |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 308 | } |
| 309 | |
edgar_igl | 2e56350 | 2008-05-13 23:51:49 +0000 | [diff] [blame] | 310 | /* ETRAX-FS Ethernet MAC block starts here. */ |
| 311 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 312 | #define RW_MA0_LO 0x00 |
| 313 | #define RW_MA0_HI 0x01 |
| 314 | #define RW_MA1_LO 0x02 |
| 315 | #define RW_MA1_HI 0x03 |
| 316 | #define RW_GA_LO 0x04 |
| 317 | #define RW_GA_HI 0x05 |
| 318 | #define RW_GEN_CTRL 0x06 |
| 319 | #define RW_REC_CTRL 0x07 |
| 320 | #define RW_TR_CTRL 0x08 |
| 321 | #define RW_CLR_ERR 0x09 |
| 322 | #define RW_MGM_CTRL 0x0a |
| 323 | #define R_STAT 0x0b |
| 324 | #define FS_ETH_MAX_REGS 0x17 |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 325 | |
Andreas Färber | 8784dfa | 2013-07-27 11:44:01 +0200 | [diff] [blame] | 326 | #define TYPE_ETRAX_FS_ETH "etraxfs-eth" |
Eduardo Habkost | 8063396 | 2020-09-16 14:25:19 -0400 | [diff] [blame] | 327 | OBJECT_DECLARE_SIMPLE_TYPE(ETRAXFSEthState, ETRAX_FS_ETH) |
Andreas Färber | 8784dfa | 2013-07-27 11:44:01 +0200 | [diff] [blame] | 328 | |
Eduardo Habkost | db1015e | 2020-09-03 16:43:22 -0400 | [diff] [blame] | 329 | struct ETRAXFSEthState { |
Andreas Färber | 8784dfa | 2013-07-27 11:44:01 +0200 | [diff] [blame] | 330 | SysBusDevice parent_obj; |
| 331 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 332 | MemoryRegion mmio; |
| 333 | NICState *nic; |
| 334 | NICConf conf; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 335 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 336 | /* Two addrs in the filter. */ |
| 337 | uint8_t macaddr[2][6]; |
| 338 | uint32_t regs[FS_ETH_MAX_REGS]; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 339 | |
Marc-André Lureau | 3af6eb8 | 2019-10-17 17:49:22 +0200 | [diff] [blame] | 340 | struct etraxfs_dma_client *dma_out; |
| 341 | struct etraxfs_dma_client *dma_in; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 342 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 343 | /* MDIO bus. */ |
| 344 | struct qemu_mdio mdio_bus; |
| 345 | unsigned int phyaddr; |
| 346 | int duplex_mismatch; |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 347 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 348 | /* PHY. */ |
| 349 | struct qemu_phy phy; |
Eduardo Habkost | db1015e | 2020-09-03 16:43:22 -0400 | [diff] [blame] | 350 | }; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 351 | |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 352 | static void eth_validate_duplex(ETRAXFSEthState *eth) |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 353 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 354 | struct qemu_phy *phy; |
| 355 | unsigned int phy_duplex; |
| 356 | unsigned int mac_duplex; |
| 357 | int new_mm = 0; |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 358 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 359 | phy = eth->mdio_bus.devs[eth->phyaddr]; |
| 360 | phy_duplex = !!(phy->read(phy, 18) & (1 << 11)); |
| 361 | mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128); |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 362 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 363 | if (mac_duplex != phy_duplex) { |
| 364 | new_mm = 1; |
| 365 | } |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 366 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 367 | if (eth->regs[RW_GEN_CTRL] & 1) { |
| 368 | if (new_mm != eth->duplex_mismatch) { |
| 369 | if (new_mm) { |
| 370 | printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n", |
| 371 | mac_duplex, phy_duplex); |
| 372 | } else { |
| 373 | printf("HW: ETH duplex ok.\n"); |
| 374 | } |
| 375 | } |
| 376 | eth->duplex_mismatch = new_mm; |
| 377 | } |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 378 | } |
| 379 | |
Edgar E. Iglesias | 06dccb8 | 2011-08-11 13:47:48 +0200 | [diff] [blame] | 380 | static uint64_t |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 381 | eth_read(void *opaque, hwaddr addr, unsigned int size) |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 382 | { |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 383 | ETRAXFSEthState *eth = opaque; |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 384 | uint32_t r = 0; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 385 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 386 | addr >>= 2; |
edgar_igl | 35ef81d | 2009-01-07 14:00:33 +0000 | [diff] [blame] | 387 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 388 | switch (addr) { |
| 389 | case R_STAT: |
| 390 | r = eth->mdio_bus.mdio & 1; |
| 391 | break; |
| 392 | default: |
| 393 | r = eth->regs[addr]; |
| 394 | D(printf("%s %x\n", __func__, addr * 4)); |
| 395 | break; |
| 396 | } |
| 397 | return r; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 398 | } |
| 399 | |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 400 | static void eth_update_ma(ETRAXFSEthState *eth, int ma) |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 401 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 402 | int reg; |
| 403 | int i = 0; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 404 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 405 | ma &= 1; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 406 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 407 | reg = RW_MA0_LO; |
| 408 | if (ma) { |
| 409 | reg = RW_MA1_LO; |
| 410 | } |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 411 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 412 | eth->macaddr[ma][i++] = eth->regs[reg]; |
| 413 | eth->macaddr[ma][i++] = eth->regs[reg] >> 8; |
| 414 | eth->macaddr[ma][i++] = eth->regs[reg] >> 16; |
| 415 | eth->macaddr[ma][i++] = eth->regs[reg] >> 24; |
| 416 | eth->macaddr[ma][i++] = eth->regs[reg + 1]; |
| 417 | eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 418 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 419 | D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, |
| 420 | eth->macaddr[ma][0], eth->macaddr[ma][1], |
| 421 | eth->macaddr[ma][2], eth->macaddr[ma][3], |
| 422 | eth->macaddr[ma][4], eth->macaddr[ma][5])); |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 423 | } |
| 424 | |
| 425 | static void |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 426 | eth_write(void *opaque, hwaddr addr, |
Edgar E. Iglesias | 06dccb8 | 2011-08-11 13:47:48 +0200 | [diff] [blame] | 427 | uint64_t val64, unsigned int size) |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 428 | { |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 429 | ETRAXFSEthState *eth = opaque; |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 430 | uint32_t value = val64; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 431 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 432 | addr >>= 2; |
| 433 | switch (addr) { |
| 434 | case RW_MA0_LO: |
| 435 | case RW_MA0_HI: |
| 436 | eth->regs[addr] = value; |
| 437 | eth_update_ma(eth, 0); |
| 438 | break; |
| 439 | case RW_MA1_LO: |
| 440 | case RW_MA1_HI: |
| 441 | eth->regs[addr] = value; |
| 442 | eth_update_ma(eth, 1); |
| 443 | break; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 444 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 445 | case RW_MGM_CTRL: |
| 446 | /* Attach an MDIO/PHY abstraction. */ |
| 447 | if (value & 2) { |
| 448 | eth->mdio_bus.mdio = value & 1; |
| 449 | } |
| 450 | if (eth->mdio_bus.mdc != (value & 4)) { |
| 451 | mdio_cycle(ð->mdio_bus); |
| 452 | eth_validate_duplex(eth); |
| 453 | } |
| 454 | eth->mdio_bus.mdc = !!(value & 4); |
| 455 | eth->regs[addr] = value; |
| 456 | break; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 457 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 458 | case RW_REC_CTRL: |
| 459 | eth->regs[addr] = value; |
| 460 | eth_validate_duplex(eth); |
| 461 | break; |
edgar_igl | c648826 | 2008-09-22 20:34:18 +0000 | [diff] [blame] | 462 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 463 | default: |
| 464 | eth->regs[addr] = value; |
| 465 | D(printf("%s %x %x\n", __func__, addr, value)); |
| 466 | break; |
| 467 | } |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 468 | } |
| 469 | |
| 470 | /* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 471 | filter dropping group addresses we have not joined. The filter has 64 |
| 472 | bits (m). The has function is a simple nible xor of the group addr. */ |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 473 | static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa) |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 474 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 475 | unsigned int hsh; |
| 476 | int m_individual = eth->regs[RW_REC_CTRL] & 4; |
| 477 | int match; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 478 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 479 | /* First bit on the wire of a MAC address signals multicast or |
| 480 | physical address. */ |
| 481 | if (!m_individual && !(sa[0] & 1)) { |
| 482 | return 0; |
| 483 | } |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 484 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 485 | /* Calculate the hash index for the GA registers. */ |
| 486 | hsh = 0; |
| 487 | hsh ^= (*sa) & 0x3f; |
| 488 | hsh ^= ((*sa) >> 6) & 0x03; |
| 489 | ++sa; |
| 490 | hsh ^= ((*sa) << 2) & 0x03c; |
| 491 | hsh ^= ((*sa) >> 4) & 0xf; |
| 492 | ++sa; |
| 493 | hsh ^= ((*sa) << 4) & 0x30; |
| 494 | hsh ^= ((*sa) >> 2) & 0x3f; |
| 495 | ++sa; |
| 496 | hsh ^= (*sa) & 0x3f; |
| 497 | hsh ^= ((*sa) >> 6) & 0x03; |
| 498 | ++sa; |
| 499 | hsh ^= ((*sa) << 2) & 0x03c; |
| 500 | hsh ^= ((*sa) >> 4) & 0xf; |
| 501 | ++sa; |
| 502 | hsh ^= ((*sa) << 4) & 0x30; |
| 503 | hsh ^= ((*sa) >> 2) & 0x3f; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 504 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 505 | hsh &= 63; |
| 506 | if (hsh > 31) { |
| 507 | match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); |
| 508 | } else { |
| 509 | match = eth->regs[RW_GA_LO] & (1 << hsh); |
| 510 | } |
| 511 | D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, |
| 512 | eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); |
| 513 | return match; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 514 | } |
| 515 | |
Stefan Hajnoczi | 4e68f7a | 2012-07-24 16:35:13 +0100 | [diff] [blame] | 516 | static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 517 | { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 518 | unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 519 | ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 520 | int use_ma0 = eth->regs[RW_REC_CTRL] & 1; |
| 521 | int use_ma1 = eth->regs[RW_REC_CTRL] & 2; |
| 522 | int r_bcast = eth->regs[RW_REC_CTRL] & 8; |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 523 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 524 | if (size < 12) { |
| 525 | return -1; |
| 526 | } |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 527 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 528 | D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", |
| 529 | buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], |
| 530 | use_ma0, use_ma1, r_bcast)); |
edgar_igl | f6953f1 | 2008-05-18 08:50:32 +0000 | [diff] [blame] | 531 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 532 | /* Does the frame get through the address filters? */ |
| 533 | if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) |
| 534 | && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) |
| 535 | && (!r_bcast || memcmp(buf, sa_bcast, 6)) |
| 536 | && !eth_match_groupaddr(eth, buf)) { |
| 537 | return size; |
| 538 | } |
| 539 | |
| 540 | /* FIXME: Find another way to pass on the fake csum. */ |
| 541 | etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1); |
Mark McLoughlin | 4f1c942 | 2009-05-18 13:40:55 +0100 | [diff] [blame] | 542 | |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 543 | return size; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 544 | } |
| 545 | |
Lars Persson | 73a511d | 2011-12-21 15:11:35 +0100 | [diff] [blame] | 546 | static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop) |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 547 | { |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 548 | ETRAXFSEthState *eth = opaque; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 549 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 550 | D(printf("%s buf=%p len=%d\n", __func__, buf, len)); |
Jason Wang | b356f76 | 2013-01-30 19:12:22 +0800 | [diff] [blame] | 551 | qemu_send_packet(qemu_get_queue(eth->nic), buf, len); |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 552 | return len; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 553 | } |
| 554 | |
Stefan Hajnoczi | 4e68f7a | 2012-07-24 16:35:13 +0100 | [diff] [blame] | 555 | static void eth_set_link(NetClientState *nc) |
edgar_igl | 94410b7 | 2009-01-09 00:04:35 +0000 | [diff] [blame] | 556 | { |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 557 | ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 558 | D(printf("%s %d\n", __func__, nc->link_down)); |
| 559 | eth->phy.link = !nc->link_down; |
edgar_igl | 94410b7 | 2009-01-09 00:04:35 +0000 | [diff] [blame] | 560 | } |
| 561 | |
Edgar E. Iglesias | 06dccb8 | 2011-08-11 13:47:48 +0200 | [diff] [blame] | 562 | static const MemoryRegionOps eth_ops = { |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 563 | .read = eth_read, |
| 564 | .write = eth_write, |
| 565 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 566 | .valid = { |
| 567 | .min_access_size = 4, |
| 568 | .max_access_size = 4 |
| 569 | } |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 570 | }; |
| 571 | |
Mark McLoughlin | 163bf3a | 2009-11-25 18:49:18 +0000 | [diff] [blame] | 572 | static NetClientInfo net_etraxfs_info = { |
Eric Blake | f394b2e | 2016-07-13 21:50:23 -0600 | [diff] [blame] | 573 | .type = NET_CLIENT_DRIVER_NIC, |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 574 | .size = sizeof(NICState), |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 575 | .receive = eth_receive, |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 576 | .link_status_changed = eth_set_link, |
Mark McLoughlin | 163bf3a | 2009-11-25 18:49:18 +0000 | [diff] [blame] | 577 | }; |
| 578 | |
Cédric Le Goater | 56dff42 | 2018-10-01 08:37:54 +0200 | [diff] [blame] | 579 | static void etraxfs_eth_reset(DeviceState *dev) |
| 580 | { |
| 581 | ETRAXFSEthState *s = ETRAX_FS_ETH(dev); |
| 582 | |
| 583 | memset(s->regs, 0, sizeof(s->regs)); |
| 584 | memset(s->macaddr, 0, sizeof(s->macaddr)); |
| 585 | s->duplex_mismatch = 0; |
| 586 | |
| 587 | s->mdio_bus.mdc = 0; |
| 588 | s->mdio_bus.mdio = 0; |
| 589 | s->mdio_bus.state = 0; |
| 590 | s->mdio_bus.drive = 0; |
| 591 | s->mdio_bus.cnt = 0; |
| 592 | s->mdio_bus.addr = 0; |
| 593 | s->mdio_bus.opc = 0; |
| 594 | s->mdio_bus.req = 0; |
| 595 | s->mdio_bus.data = 0; |
| 596 | |
| 597 | tdk_reset(&s->phy); |
| 598 | } |
| 599 | |
Cédric Le Goater | c24828b | 2018-10-01 08:37:53 +0200 | [diff] [blame] | 600 | static void etraxfs_eth_realize(DeviceState *dev, Error **errp) |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 601 | { |
Cédric Le Goater | c24828b | 2018-10-01 08:37:53 +0200 | [diff] [blame] | 602 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 603 | ETRAXFSEthState *s = ETRAX_FS_ETH(dev); |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 604 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 605 | if (!s->dma_out || !s->dma_in) { |
Cédric Le Goater | c24828b | 2018-10-01 08:37:53 +0200 | [diff] [blame] | 606 | error_setg(errp, "Unconnected ETRAX-FS Ethernet MAC"); |
| 607 | return; |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 608 | } |
aliguori | 0ae18ce | 2009-01-13 19:39:36 +0000 | [diff] [blame] | 609 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 610 | s->dma_out->client.push = eth_tx_push; |
| 611 | s->dma_out->client.opaque = s; |
| 612 | s->dma_in->client.opaque = s; |
| 613 | s->dma_in->client.pull = NULL; |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 614 | |
Paolo Bonzini | eedfac6 | 2013-06-06 21:25:08 -0400 | [diff] [blame] | 615 | memory_region_init_io(&s->mmio, OBJECT(dev), ð_ops, s, |
| 616 | "etraxfs-eth", 0x5c); |
Andreas Färber | 8784dfa | 2013-07-27 11:44:01 +0200 | [diff] [blame] | 617 | sysbus_init_mmio(sbd, &s->mmio); |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 618 | |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 619 | qemu_macaddr_default_if_unset(&s->conf.macaddr); |
| 620 | s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, |
Andreas Färber | 8784dfa | 2013-07-27 11:44:01 +0200 | [diff] [blame] | 621 | object_get_typename(OBJECT(s)), dev->id, s); |
Jason Wang | b356f76 | 2013-01-30 19:12:22 +0800 | [diff] [blame] | 622 | qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); |
| 623 | |
Cédric Le Goater | 56dff42 | 2018-10-01 08:37:54 +0200 | [diff] [blame] | 624 | s->phy.read = tdk_read; |
| 625 | s->phy.write = tdk_write; |
Grant Likely | 9fc7577 | 2013-01-23 16:15:25 +0000 | [diff] [blame] | 626 | mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); |
edgar_igl | a3ea5df | 2008-05-11 15:04:22 +0000 | [diff] [blame] | 627 | } |
Edgar E. Iglesias | d949396 | 2011-08-09 13:24:04 +0200 | [diff] [blame] | 628 | |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 629 | static Property etraxfs_eth_properties[] = { |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 630 | DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1), |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 631 | DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf), |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 632 | DEFINE_PROP_END_OF_LIST(), |
| 633 | }; |
| 634 | |
| 635 | static void etraxfs_eth_class_init(ObjectClass *klass, void *data) |
| 636 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 637 | DeviceClass *dc = DEVICE_CLASS(klass); |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 638 | |
Cédric Le Goater | c24828b | 2018-10-01 08:37:53 +0200 | [diff] [blame] | 639 | dc->realize = etraxfs_eth_realize; |
Cédric Le Goater | 56dff42 | 2018-10-01 08:37:54 +0200 | [diff] [blame] | 640 | dc->reset = etraxfs_eth_reset; |
Marc-André Lureau | 4f67d30 | 2020-01-10 19:30:32 +0400 | [diff] [blame] | 641 | device_class_set_props(dc, etraxfs_eth_properties); |
Marc-André Lureau | 3af6eb8 | 2019-10-17 17:49:22 +0200 | [diff] [blame] | 642 | /* Reason: dma_out, dma_in are not user settable */ |
Eduardo Habkost | e90f2a8 | 2017-05-03 17:35:44 -0300 | [diff] [blame] | 643 | dc->user_creatable = false; |
Anthony Liguori | 999e12b | 2012-01-24 13:12:29 -0600 | [diff] [blame] | 644 | } |
| 645 | |
Marc-André Lureau | 3af6eb8 | 2019-10-17 17:49:22 +0200 | [diff] [blame] | 646 | |
| 647 | /* Instantiate an ETRAXFS Ethernet MAC. */ |
| 648 | DeviceState * |
| 649 | etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, |
| 650 | struct etraxfs_dma_client *dma_out, |
| 651 | struct etraxfs_dma_client *dma_in) |
| 652 | { |
| 653 | DeviceState *dev; |
| 654 | qemu_check_nic_model(nd, "fseth"); |
| 655 | |
Markus Armbruster | 3e80f69 | 2020-06-10 07:31:58 +0200 | [diff] [blame] | 656 | dev = qdev_new("etraxfs-eth"); |
Marc-André Lureau | 3af6eb8 | 2019-10-17 17:49:22 +0200 | [diff] [blame] | 657 | qdev_set_nic_properties(dev, nd); |
| 658 | qdev_prop_set_uint32(dev, "phyaddr", phyaddr); |
| 659 | |
| 660 | /* |
| 661 | * TODO: QOM design, define a QOM interface for "I am an etraxfs |
| 662 | * DMA client" (which replaces the current 'struct |
| 663 | * etraxfs_dma_client' ad-hoc interface), implement it on the |
| 664 | * ethernet device, and then have QOM link properties on the DMA |
| 665 | * controller device so that you can pass the interface |
| 666 | * implementations to it. |
| 667 | */ |
| 668 | ETRAX_FS_ETH(dev)->dma_out = dma_out; |
| 669 | ETRAX_FS_ETH(dev)->dma_in = dma_in; |
Markus Armbruster | 3c6ef47 | 2020-06-10 07:32:34 +0200 | [diff] [blame] | 670 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
Marc-André Lureau | 3af6eb8 | 2019-10-17 17:49:22 +0200 | [diff] [blame] | 671 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); |
| 672 | |
| 673 | return dev; |
| 674 | } |
| 675 | |
Andreas Färber | 8c43a6f | 2013-01-10 16:19:07 +0100 | [diff] [blame] | 676 | static const TypeInfo etraxfs_eth_info = { |
Andreas Färber | 8784dfa | 2013-07-27 11:44:01 +0200 | [diff] [blame] | 677 | .name = TYPE_ETRAX_FS_ETH, |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 678 | .parent = TYPE_SYS_BUS_DEVICE, |
Andreas Färber | 5807649 | 2013-07-27 11:48:42 +0200 | [diff] [blame] | 679 | .instance_size = sizeof(ETRAXFSEthState), |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 680 | .class_init = etraxfs_eth_class_init, |
Edgar E. Iglesias | d949396 | 2011-08-09 13:24:04 +0200 | [diff] [blame] | 681 | }; |
| 682 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 683 | static void etraxfs_eth_register_types(void) |
Edgar E. Iglesias | d949396 | 2011-08-09 13:24:04 +0200 | [diff] [blame] | 684 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 685 | type_register_static(&etraxfs_eth_info); |
Edgar E. Iglesias | d949396 | 2011-08-09 13:24:04 +0200 | [diff] [blame] | 686 | } |
| 687 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 688 | type_init(etraxfs_eth_register_types) |