Statistics
| Branch: | Revision:

root / hw / net / allwinner_emac.c @ 216db403

History | View | Annotate | Download (15.2 kB)

1 22f90bcb Beniamino Galvani
/*
2 22f90bcb Beniamino Galvani
 * Emulation of Allwinner EMAC Fast Ethernet controller and
3 22f90bcb Beniamino Galvani
 * Realtek RTL8201CP PHY
4 22f90bcb Beniamino Galvani
 *
5 22f90bcb Beniamino Galvani
 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
6 22f90bcb Beniamino Galvani
 *
7 22f90bcb Beniamino Galvani
 * This model is based on reverse-engineering of Linux kernel driver.
8 22f90bcb Beniamino Galvani
 *
9 22f90bcb Beniamino Galvani
 * This program is free software; you can redistribute it and/or modify
10 22f90bcb Beniamino Galvani
 * it under the terms of the GNU General Public License version 2 as
11 22f90bcb Beniamino Galvani
 * published by the Free Software Foundation.
12 22f90bcb Beniamino Galvani
 *
13 22f90bcb Beniamino Galvani
 * This program is distributed in the hope that it will be useful,
14 22f90bcb Beniamino Galvani
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 22f90bcb Beniamino Galvani
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 22f90bcb Beniamino Galvani
 * GNU General Public License for more details.
17 22f90bcb Beniamino Galvani
 *
18 22f90bcb Beniamino Galvani
 */
19 22f90bcb Beniamino Galvani
#include "hw/sysbus.h"
20 22f90bcb Beniamino Galvani
#include "net/net.h"
21 22f90bcb Beniamino Galvani
#include "qemu/fifo8.h"
22 22f90bcb Beniamino Galvani
#include "hw/net/allwinner_emac.h"
23 22f90bcb Beniamino Galvani
#include <zlib.h>
24 22f90bcb Beniamino Galvani
25 22f90bcb Beniamino Galvani
static uint8_t padding[60];
26 22f90bcb Beniamino Galvani
27 22f90bcb Beniamino Galvani
static void mii_set_link(RTL8201CPState *mii, bool link_ok)
28 22f90bcb Beniamino Galvani
{
29 22f90bcb Beniamino Galvani
    if (link_ok) {
30 22f90bcb Beniamino Galvani
        mii->bmsr |= MII_BMSR_LINK_ST;
31 22f90bcb Beniamino Galvani
        mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 |
32 22f90bcb Beniamino Galvani
                       MII_ANAR_CSMACD;
33 22f90bcb Beniamino Galvani
    } else {
34 22f90bcb Beniamino Galvani
        mii->bmsr &= ~MII_BMSR_LINK_ST;
35 22f90bcb Beniamino Galvani
        mii->anlpar = MII_ANAR_TX;
36 22f90bcb Beniamino Galvani
    }
37 22f90bcb Beniamino Galvani
}
38 22f90bcb Beniamino Galvani
39 22f90bcb Beniamino Galvani
static void mii_reset(RTL8201CPState *mii, bool link_ok)
40 22f90bcb Beniamino Galvani
{
41 22f90bcb Beniamino Galvani
    mii->bmcr = MII_BMCR_FD | MII_BMCR_AUTOEN | MII_BMCR_SPEED;
42 22f90bcb Beniamino Galvani
    mii->bmsr = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD |
43 22f90bcb Beniamino Galvani
                MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AUTONEG;
44 22f90bcb Beniamino Galvani
    mii->anar = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 |
45 22f90bcb Beniamino Galvani
                MII_ANAR_CSMACD;
46 22f90bcb Beniamino Galvani
    mii->anlpar = MII_ANAR_TX;
47 22f90bcb Beniamino Galvani
48 22f90bcb Beniamino Galvani
    mii_set_link(mii, link_ok);
49 22f90bcb Beniamino Galvani
}
50 22f90bcb Beniamino Galvani
51 22f90bcb Beniamino Galvani
static uint16_t RTL8201CP_mdio_read(AwEmacState *s, uint8_t addr, uint8_t reg)
52 22f90bcb Beniamino Galvani
{
53 22f90bcb Beniamino Galvani
    RTL8201CPState *mii = &s->mii;
54 22f90bcb Beniamino Galvani
    uint16_t ret = 0xffff;
55 22f90bcb Beniamino Galvani
56 22f90bcb Beniamino Galvani
    if (addr == s->phy_addr) {
57 22f90bcb Beniamino Galvani
        switch (reg) {
58 22f90bcb Beniamino Galvani
        case MII_BMCR:
59 22f90bcb Beniamino Galvani
            return mii->bmcr;
60 22f90bcb Beniamino Galvani
        case MII_BMSR:
61 22f90bcb Beniamino Galvani
            return mii->bmsr;
62 22f90bcb Beniamino Galvani
        case MII_PHYID1:
63 22f90bcb Beniamino Galvani
            return RTL8201CP_PHYID1;
64 22f90bcb Beniamino Galvani
        case MII_PHYID2:
65 22f90bcb Beniamino Galvani
            return RTL8201CP_PHYID2;
66 22f90bcb Beniamino Galvani
        case MII_ANAR:
67 22f90bcb Beniamino Galvani
            return mii->anar;
68 22f90bcb Beniamino Galvani
        case MII_ANLPAR:
69 22f90bcb Beniamino Galvani
            return mii->anlpar;
70 22f90bcb Beniamino Galvani
        case MII_ANER:
71 22f90bcb Beniamino Galvani
        case MII_NSR:
72 22f90bcb Beniamino Galvani
        case MII_LBREMR:
73 22f90bcb Beniamino Galvani
        case MII_REC:
74 22f90bcb Beniamino Galvani
        case MII_SNRDR:
75 22f90bcb Beniamino Galvani
        case MII_TEST:
76 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_UNIMP,
77 22f90bcb Beniamino Galvani
                          "allwinner_emac: read from unimpl. mii reg 0x%x\n",
78 22f90bcb Beniamino Galvani
                          reg);
79 22f90bcb Beniamino Galvani
            return 0;
80 22f90bcb Beniamino Galvani
        default:
81 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_GUEST_ERROR,
82 22f90bcb Beniamino Galvani
                          "allwinner_emac: read from invalid mii reg 0x%x\n",
83 22f90bcb Beniamino Galvani
                          reg);
84 22f90bcb Beniamino Galvani
            return 0;
85 22f90bcb Beniamino Galvani
        }
86 22f90bcb Beniamino Galvani
    }
87 22f90bcb Beniamino Galvani
    return ret;
88 22f90bcb Beniamino Galvani
}
89 22f90bcb Beniamino Galvani
90 22f90bcb Beniamino Galvani
static void RTL8201CP_mdio_write(AwEmacState *s, uint8_t addr, uint8_t reg,
91 22f90bcb Beniamino Galvani
                                 uint16_t value)
92 22f90bcb Beniamino Galvani
{
93 22f90bcb Beniamino Galvani
    RTL8201CPState *mii = &s->mii;
94 22f90bcb Beniamino Galvani
    NetClientState *nc;
95 22f90bcb Beniamino Galvani
96 22f90bcb Beniamino Galvani
    if (addr == s->phy_addr) {
97 22f90bcb Beniamino Galvani
        switch (reg) {
98 22f90bcb Beniamino Galvani
        case MII_BMCR:
99 22f90bcb Beniamino Galvani
            if (value & MII_BMCR_RESET) {
100 22f90bcb Beniamino Galvani
                nc = qemu_get_queue(s->nic);
101 22f90bcb Beniamino Galvani
                mii_reset(mii, !nc->link_down);
102 22f90bcb Beniamino Galvani
            } else {
103 22f90bcb Beniamino Galvani
                mii->bmcr = value;
104 22f90bcb Beniamino Galvani
            }
105 22f90bcb Beniamino Galvani
            break;
106 22f90bcb Beniamino Galvani
        case MII_ANAR:
107 22f90bcb Beniamino Galvani
            mii->anar = value;
108 22f90bcb Beniamino Galvani
            break;
109 22f90bcb Beniamino Galvani
        case MII_BMSR:
110 22f90bcb Beniamino Galvani
        case MII_PHYID1:
111 22f90bcb Beniamino Galvani
        case MII_PHYID2:
112 22f90bcb Beniamino Galvani
        case MII_ANLPAR:
113 22f90bcb Beniamino Galvani
        case MII_ANER:
114 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_GUEST_ERROR,
115 22f90bcb Beniamino Galvani
                          "allwinner_emac: write to read-only mii reg 0x%x\n",
116 22f90bcb Beniamino Galvani
                          reg);
117 22f90bcb Beniamino Galvani
            break;
118 22f90bcb Beniamino Galvani
        case MII_NSR:
119 22f90bcb Beniamino Galvani
        case MII_LBREMR:
120 22f90bcb Beniamino Galvani
        case MII_REC:
121 22f90bcb Beniamino Galvani
        case MII_SNRDR:
122 22f90bcb Beniamino Galvani
        case MII_TEST:
123 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_UNIMP,
124 22f90bcb Beniamino Galvani
                          "allwinner_emac: write to unimpl. mii reg 0x%x\n",
125 22f90bcb Beniamino Galvani
                          reg);
126 22f90bcb Beniamino Galvani
            break;
127 22f90bcb Beniamino Galvani
        default:
128 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_GUEST_ERROR,
129 22f90bcb Beniamino Galvani
                          "allwinner_emac: write to invalid mii reg 0x%x\n",
130 22f90bcb Beniamino Galvani
                          reg);
131 22f90bcb Beniamino Galvani
        }
132 22f90bcb Beniamino Galvani
    }
133 22f90bcb Beniamino Galvani
}
134 22f90bcb Beniamino Galvani
135 22f90bcb Beniamino Galvani
static void aw_emac_update_irq(AwEmacState *s)
136 22f90bcb Beniamino Galvani
{
137 22f90bcb Beniamino Galvani
    qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0);
138 22f90bcb Beniamino Galvani
}
139 22f90bcb Beniamino Galvani
140 22f90bcb Beniamino Galvani
static void aw_emac_tx_reset(AwEmacState *s, int chan)
141 22f90bcb Beniamino Galvani
{
142 22f90bcb Beniamino Galvani
    fifo8_reset(&s->tx_fifo[chan]);
143 22f90bcb Beniamino Galvani
    s->tx_length[chan] = 0;
144 22f90bcb Beniamino Galvani
}
145 22f90bcb Beniamino Galvani
146 22f90bcb Beniamino Galvani
static void aw_emac_rx_reset(AwEmacState *s)
147 22f90bcb Beniamino Galvani
{
148 22f90bcb Beniamino Galvani
    fifo8_reset(&s->rx_fifo);
149 22f90bcb Beniamino Galvani
    s->rx_num_packets = 0;
150 22f90bcb Beniamino Galvani
    s->rx_packet_size = 0;
151 22f90bcb Beniamino Galvani
    s->rx_packet_pos = 0;
152 22f90bcb Beniamino Galvani
}
153 22f90bcb Beniamino Galvani
154 22f90bcb Beniamino Galvani
static void fifo8_push_word(Fifo8 *fifo, uint32_t val)
155 22f90bcb Beniamino Galvani
{
156 22f90bcb Beniamino Galvani
    fifo8_push(fifo, val);
157 22f90bcb Beniamino Galvani
    fifo8_push(fifo, val >> 8);
158 22f90bcb Beniamino Galvani
    fifo8_push(fifo, val >> 16);
159 22f90bcb Beniamino Galvani
    fifo8_push(fifo, val >> 24);
160 22f90bcb Beniamino Galvani
}
161 22f90bcb Beniamino Galvani
162 22f90bcb Beniamino Galvani
static uint32_t fifo8_pop_word(Fifo8 *fifo)
163 22f90bcb Beniamino Galvani
{
164 22f90bcb Beniamino Galvani
    uint32_t ret;
165 22f90bcb Beniamino Galvani
166 22f90bcb Beniamino Galvani
    ret = fifo8_pop(fifo);
167 22f90bcb Beniamino Galvani
    ret |= fifo8_pop(fifo) << 8;
168 22f90bcb Beniamino Galvani
    ret |= fifo8_pop(fifo) << 16;
169 22f90bcb Beniamino Galvani
    ret |= fifo8_pop(fifo) << 24;
170 22f90bcb Beniamino Galvani
171 22f90bcb Beniamino Galvani
    return ret;
172 22f90bcb Beniamino Galvani
}
173 22f90bcb Beniamino Galvani
174 22f90bcb Beniamino Galvani
static int aw_emac_can_receive(NetClientState *nc)
175 22f90bcb Beniamino Galvani
{
176 22f90bcb Beniamino Galvani
    AwEmacState *s = qemu_get_nic_opaque(nc);
177 22f90bcb Beniamino Galvani
178 22f90bcb Beniamino Galvani
    /*
179 22f90bcb Beniamino Galvani
     * To avoid packet drops, allow reception only when there is space
180 22f90bcb Beniamino Galvani
     * for a full frame: 1522 + 8 (rx headers) + 2 (padding).
181 22f90bcb Beniamino Galvani
     */
182 22f90bcb Beniamino Galvani
    return (s->ctl & EMAC_CTL_RX_EN) && (fifo8_num_free(&s->rx_fifo) >= 1532);
183 22f90bcb Beniamino Galvani
}
184 22f90bcb Beniamino Galvani
185 22f90bcb Beniamino Galvani
static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
186 22f90bcb Beniamino Galvani
                               size_t size)
187 22f90bcb Beniamino Galvani
{
188 22f90bcb Beniamino Galvani
    AwEmacState *s = qemu_get_nic_opaque(nc);
189 22f90bcb Beniamino Galvani
    Fifo8 *fifo = &s->rx_fifo;
190 22f90bcb Beniamino Galvani
    size_t padded_size, total_size;
191 22f90bcb Beniamino Galvani
    uint32_t crc;
192 22f90bcb Beniamino Galvani
193 22f90bcb Beniamino Galvani
    padded_size = size > 60 ? size : 60;
194 22f90bcb Beniamino Galvani
    total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4);
195 22f90bcb Beniamino Galvani
196 22f90bcb Beniamino Galvani
    if (!(s->ctl & EMAC_CTL_RX_EN) || (fifo8_num_free(fifo) < total_size)) {
197 22f90bcb Beniamino Galvani
        return -1;
198 22f90bcb Beniamino Galvani
    }
199 22f90bcb Beniamino Galvani
200 22f90bcb Beniamino Galvani
    fifo8_push_word(fifo, EMAC_UNDOCUMENTED_MAGIC);
201 22f90bcb Beniamino Galvani
    fifo8_push_word(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE,
202 22f90bcb Beniamino Galvani
                                         EMAC_RX_IO_DATA_STATUS_OK));
203 22f90bcb Beniamino Galvani
    fifo8_push_all(fifo, buf, size);
204 22f90bcb Beniamino Galvani
    crc = crc32(~0, buf, size);
205 22f90bcb Beniamino Galvani
206 22f90bcb Beniamino Galvani
    if (padded_size != size) {
207 22f90bcb Beniamino Galvani
        fifo8_push_all(fifo, padding, padded_size - size);
208 22f90bcb Beniamino Galvani
        crc = crc32(crc, padding, padded_size - size);
209 22f90bcb Beniamino Galvani
    }
210 22f90bcb Beniamino Galvani
211 22f90bcb Beniamino Galvani
    fifo8_push_word(fifo, crc);
212 22f90bcb Beniamino Galvani
    fifo8_push_all(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size);
213 22f90bcb Beniamino Galvani
    s->rx_num_packets++;
214 22f90bcb Beniamino Galvani
215 22f90bcb Beniamino Galvani
    s->int_sta |= EMAC_INT_RX;
216 22f90bcb Beniamino Galvani
    aw_emac_update_irq(s);
217 22f90bcb Beniamino Galvani
218 22f90bcb Beniamino Galvani
    return size;
219 22f90bcb Beniamino Galvani
}
220 22f90bcb Beniamino Galvani
221 22f90bcb Beniamino Galvani
static void aw_emac_cleanup(NetClientState *nc)
222 22f90bcb Beniamino Galvani
{
223 22f90bcb Beniamino Galvani
    AwEmacState *s = qemu_get_nic_opaque(nc);
224 22f90bcb Beniamino Galvani
225 22f90bcb Beniamino Galvani
    s->nic = NULL;
226 22f90bcb Beniamino Galvani
}
227 22f90bcb Beniamino Galvani
228 22f90bcb Beniamino Galvani
static void aw_emac_reset(DeviceState *dev)
229 22f90bcb Beniamino Galvani
{
230 22f90bcb Beniamino Galvani
    AwEmacState *s = AW_EMAC(dev);
231 22f90bcb Beniamino Galvani
    NetClientState *nc = qemu_get_queue(s->nic);
232 22f90bcb Beniamino Galvani
233 22f90bcb Beniamino Galvani
    s->ctl = 0;
234 22f90bcb Beniamino Galvani
    s->tx_mode = 0;
235 22f90bcb Beniamino Galvani
    s->int_ctl = 0;
236 22f90bcb Beniamino Galvani
    s->int_sta = 0;
237 22f90bcb Beniamino Galvani
    s->tx_channel = 0;
238 22f90bcb Beniamino Galvani
    s->phy_target = 0;
239 22f90bcb Beniamino Galvani
240 22f90bcb Beniamino Galvani
    aw_emac_tx_reset(s, 0);
241 22f90bcb Beniamino Galvani
    aw_emac_tx_reset(s, 1);
242 22f90bcb Beniamino Galvani
    aw_emac_rx_reset(s);
243 22f90bcb Beniamino Galvani
244 22f90bcb Beniamino Galvani
    mii_reset(&s->mii, !nc->link_down);
245 22f90bcb Beniamino Galvani
}
246 22f90bcb Beniamino Galvani
247 22f90bcb Beniamino Galvani
static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
248 22f90bcb Beniamino Galvani
{
249 22f90bcb Beniamino Galvani
    AwEmacState *s = opaque;
250 22f90bcb Beniamino Galvani
    Fifo8 *fifo = &s->rx_fifo;
251 22f90bcb Beniamino Galvani
    NetClientState *nc;
252 22f90bcb Beniamino Galvani
    uint64_t ret;
253 22f90bcb Beniamino Galvani
254 22f90bcb Beniamino Galvani
    switch (offset) {
255 22f90bcb Beniamino Galvani
    case EMAC_CTL_REG:
256 22f90bcb Beniamino Galvani
        return s->ctl;
257 22f90bcb Beniamino Galvani
    case EMAC_TX_MODE_REG:
258 22f90bcb Beniamino Galvani
        return s->tx_mode;
259 22f90bcb Beniamino Galvani
    case EMAC_TX_INS_REG:
260 22f90bcb Beniamino Galvani
        return s->tx_channel;
261 22f90bcb Beniamino Galvani
    case EMAC_RX_CTL_REG:
262 22f90bcb Beniamino Galvani
        return s->rx_ctl;
263 22f90bcb Beniamino Galvani
    case EMAC_RX_IO_DATA_REG:
264 22f90bcb Beniamino Galvani
        if (!s->rx_num_packets) {
265 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_GUEST_ERROR,
266 22f90bcb Beniamino Galvani
                          "Read IO data register when no packet available");
267 22f90bcb Beniamino Galvani
            return 0;
268 22f90bcb Beniamino Galvani
        }
269 22f90bcb Beniamino Galvani
270 22f90bcb Beniamino Galvani
        ret = fifo8_pop_word(fifo);
271 22f90bcb Beniamino Galvani
272 22f90bcb Beniamino Galvani
        switch (s->rx_packet_pos) {
273 22f90bcb Beniamino Galvani
        case 0:     /* Word is magic header */
274 22f90bcb Beniamino Galvani
            s->rx_packet_pos += 4;
275 22f90bcb Beniamino Galvani
            break;
276 22f90bcb Beniamino Galvani
        case 4:     /* Word is rx info header */
277 22f90bcb Beniamino Galvani
            s->rx_packet_pos += 4;
278 22f90bcb Beniamino Galvani
            s->rx_packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4);
279 22f90bcb Beniamino Galvani
            break;
280 22f90bcb Beniamino Galvani
        default:    /* Word is packet data */
281 22f90bcb Beniamino Galvani
            s->rx_packet_pos += 4;
282 22f90bcb Beniamino Galvani
            s->rx_packet_size -= 4;
283 22f90bcb Beniamino Galvani
284 22f90bcb Beniamino Galvani
            if (!s->rx_packet_size) {
285 22f90bcb Beniamino Galvani
                s->rx_packet_pos = 0;
286 22f90bcb Beniamino Galvani
                s->rx_num_packets--;
287 22f90bcb Beniamino Galvani
                nc = qemu_get_queue(s->nic);
288 22f90bcb Beniamino Galvani
                if (aw_emac_can_receive(nc)) {
289 22f90bcb Beniamino Galvani
                    qemu_flush_queued_packets(nc);
290 22f90bcb Beniamino Galvani
                }
291 22f90bcb Beniamino Galvani
            }
292 22f90bcb Beniamino Galvani
        }
293 22f90bcb Beniamino Galvani
        return ret;
294 22f90bcb Beniamino Galvani
    case EMAC_RX_FBC_REG:
295 22f90bcb Beniamino Galvani
        return s->rx_num_packets;
296 22f90bcb Beniamino Galvani
    case EMAC_INT_CTL_REG:
297 22f90bcb Beniamino Galvani
        return s->int_ctl;
298 22f90bcb Beniamino Galvani
    case EMAC_INT_STA_REG:
299 22f90bcb Beniamino Galvani
        return s->int_sta;
300 22f90bcb Beniamino Galvani
    case EMAC_MAC_MRDD_REG:
301 22f90bcb Beniamino Galvani
        return RTL8201CP_mdio_read(s,
302 22f90bcb Beniamino Galvani
                                   extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
303 22f90bcb Beniamino Galvani
                                   extract32(s->phy_target, PHY_REG_SHIFT, 8));
304 22f90bcb Beniamino Galvani
    default:
305 22f90bcb Beniamino Galvani
        qemu_log_mask(LOG_UNIMP,
306 22f90bcb Beniamino Galvani
                      "allwinner_emac: read access to unknown register 0x"
307 22f90bcb Beniamino Galvani
                      TARGET_FMT_plx "\n", offset);
308 22f90bcb Beniamino Galvani
        ret = 0;
309 22f90bcb Beniamino Galvani
    }
310 22f90bcb Beniamino Galvani
311 22f90bcb Beniamino Galvani
    return ret;
312 22f90bcb Beniamino Galvani
}
313 22f90bcb Beniamino Galvani
314 22f90bcb Beniamino Galvani
static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value,
315 22f90bcb Beniamino Galvani
                          unsigned size)
316 22f90bcb Beniamino Galvani
{
317 22f90bcb Beniamino Galvani
    AwEmacState *s = opaque;
318 22f90bcb Beniamino Galvani
    Fifo8 *fifo;
319 22f90bcb Beniamino Galvani
    NetClientState *nc = qemu_get_queue(s->nic);
320 22f90bcb Beniamino Galvani
    int chan;
321 22f90bcb Beniamino Galvani
322 22f90bcb Beniamino Galvani
    switch (offset) {
323 22f90bcb Beniamino Galvani
    case EMAC_CTL_REG:
324 22f90bcb Beniamino Galvani
        if (value & EMAC_CTL_RESET) {
325 22f90bcb Beniamino Galvani
            aw_emac_reset(DEVICE(s));
326 22f90bcb Beniamino Galvani
            value &= ~EMAC_CTL_RESET;
327 22f90bcb Beniamino Galvani
        }
328 22f90bcb Beniamino Galvani
        s->ctl = value;
329 22f90bcb Beniamino Galvani
        if (aw_emac_can_receive(nc)) {
330 22f90bcb Beniamino Galvani
            qemu_flush_queued_packets(nc);
331 22f90bcb Beniamino Galvani
        }
332 22f90bcb Beniamino Galvani
        break;
333 22f90bcb Beniamino Galvani
    case EMAC_TX_MODE_REG:
334 22f90bcb Beniamino Galvani
        s->tx_mode = value;
335 22f90bcb Beniamino Galvani
        break;
336 22f90bcb Beniamino Galvani
    case EMAC_TX_CTL0_REG:
337 22f90bcb Beniamino Galvani
    case EMAC_TX_CTL1_REG:
338 22f90bcb Beniamino Galvani
        chan = (offset == EMAC_TX_CTL0_REG ? 0 : 1);
339 22f90bcb Beniamino Galvani
        if ((value & 1) && (s->ctl & EMAC_CTL_TX_EN)) {
340 22f90bcb Beniamino Galvani
            uint32_t len, ret;
341 22f90bcb Beniamino Galvani
            const uint8_t *data;
342 22f90bcb Beniamino Galvani
343 22f90bcb Beniamino Galvani
            fifo = &s->tx_fifo[chan];
344 22f90bcb Beniamino Galvani
            len = s->tx_length[chan];
345 22f90bcb Beniamino Galvani
346 22f90bcb Beniamino Galvani
            if (len > fifo8_num_used(fifo)) {
347 22f90bcb Beniamino Galvani
                len = fifo8_num_used(fifo);
348 22f90bcb Beniamino Galvani
                qemu_log_mask(LOG_GUEST_ERROR,
349 22f90bcb Beniamino Galvani
                              "allwinner_emac: TX length > fifo data length\n");
350 22f90bcb Beniamino Galvani
            }
351 22f90bcb Beniamino Galvani
            if (len > 0) {
352 22f90bcb Beniamino Galvani
                data = fifo8_pop_buf(fifo, len, &ret);
353 22f90bcb Beniamino Galvani
                qemu_send_packet(nc, data, ret);
354 22f90bcb Beniamino Galvani
                aw_emac_tx_reset(s, chan);
355 22f90bcb Beniamino Galvani
                /* Raise TX interrupt */
356 22f90bcb Beniamino Galvani
                s->int_sta |= EMAC_INT_TX_CHAN(chan);
357 22f90bcb Beniamino Galvani
                aw_emac_update_irq(s);
358 22f90bcb Beniamino Galvani
            }
359 22f90bcb Beniamino Galvani
        }
360 22f90bcb Beniamino Galvani
        break;
361 22f90bcb Beniamino Galvani
    case EMAC_TX_INS_REG:
362 22f90bcb Beniamino Galvani
        s->tx_channel = value < NUM_TX_FIFOS ? value : 0;
363 22f90bcb Beniamino Galvani
        break;
364 22f90bcb Beniamino Galvani
    case EMAC_TX_PL0_REG:
365 22f90bcb Beniamino Galvani
    case EMAC_TX_PL1_REG:
366 22f90bcb Beniamino Galvani
        chan = (offset == EMAC_TX_PL0_REG ? 0 : 1);
367 22f90bcb Beniamino Galvani
        if (value > TX_FIFO_SIZE) {
368 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_GUEST_ERROR,
369 22f90bcb Beniamino Galvani
                          "allwinner_emac: invalid TX frame length %d\n",
370 22f90bcb Beniamino Galvani
                          (int)value);
371 22f90bcb Beniamino Galvani
            value = TX_FIFO_SIZE;
372 22f90bcb Beniamino Galvani
        }
373 22f90bcb Beniamino Galvani
        s->tx_length[chan] = value;
374 22f90bcb Beniamino Galvani
        break;
375 22f90bcb Beniamino Galvani
    case EMAC_TX_IO_DATA_REG:
376 22f90bcb Beniamino Galvani
        fifo = &s->tx_fifo[s->tx_channel];
377 22f90bcb Beniamino Galvani
        if (fifo8_num_free(fifo) < 4) {
378 22f90bcb Beniamino Galvani
            qemu_log_mask(LOG_GUEST_ERROR,
379 22f90bcb Beniamino Galvani
                          "allwinner_emac: TX data overruns fifo\n");
380 22f90bcb Beniamino Galvani
            break;
381 22f90bcb Beniamino Galvani
        }
382 22f90bcb Beniamino Galvani
        fifo8_push_word(fifo, value);
383 22f90bcb Beniamino Galvani
        break;
384 22f90bcb Beniamino Galvani
    case EMAC_RX_CTL_REG:
385 22f90bcb Beniamino Galvani
        s->rx_ctl = value;
386 22f90bcb Beniamino Galvani
        break;
387 22f90bcb Beniamino Galvani
    case EMAC_RX_FBC_REG:
388 22f90bcb Beniamino Galvani
        if (value == 0) {
389 22f90bcb Beniamino Galvani
            aw_emac_rx_reset(s);
390 22f90bcb Beniamino Galvani
        }
391 22f90bcb Beniamino Galvani
        break;
392 22f90bcb Beniamino Galvani
    case EMAC_INT_CTL_REG:
393 22f90bcb Beniamino Galvani
        s->int_ctl = value;
394 22f90bcb Beniamino Galvani
        break;
395 22f90bcb Beniamino Galvani
    case EMAC_INT_STA_REG:
396 22f90bcb Beniamino Galvani
        s->int_sta &= ~value;
397 22f90bcb Beniamino Galvani
        break;
398 22f90bcb Beniamino Galvani
    case EMAC_MAC_MADR_REG:
399 22f90bcb Beniamino Galvani
        s->phy_target = value;
400 22f90bcb Beniamino Galvani
        break;
401 22f90bcb Beniamino Galvani
    case EMAC_MAC_MWTD_REG:
402 22f90bcb Beniamino Galvani
        RTL8201CP_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
403 22f90bcb Beniamino Galvani
                             extract32(s->phy_target, PHY_REG_SHIFT, 8), value);
404 22f90bcb Beniamino Galvani
        break;
405 22f90bcb Beniamino Galvani
    default:
406 22f90bcb Beniamino Galvani
        qemu_log_mask(LOG_UNIMP,
407 22f90bcb Beniamino Galvani
                      "allwinner_emac: write access to unknown register 0x"
408 22f90bcb Beniamino Galvani
                      TARGET_FMT_plx "\n", offset);
409 22f90bcb Beniamino Galvani
    }
410 22f90bcb Beniamino Galvani
}
411 22f90bcb Beniamino Galvani
412 22f90bcb Beniamino Galvani
static void aw_emac_set_link(NetClientState *nc)
413 22f90bcb Beniamino Galvani
{
414 22f90bcb Beniamino Galvani
    AwEmacState *s = qemu_get_nic_opaque(nc);
415 22f90bcb Beniamino Galvani
416 22f90bcb Beniamino Galvani
    mii_set_link(&s->mii, !nc->link_down);
417 22f90bcb Beniamino Galvani
}
418 22f90bcb Beniamino Galvani
419 22f90bcb Beniamino Galvani
static const MemoryRegionOps aw_emac_mem_ops = {
420 22f90bcb Beniamino Galvani
    .read = aw_emac_read,
421 22f90bcb Beniamino Galvani
    .write = aw_emac_write,
422 22f90bcb Beniamino Galvani
    .endianness = DEVICE_NATIVE_ENDIAN,
423 22f90bcb Beniamino Galvani
    .valid = {
424 22f90bcb Beniamino Galvani
        .min_access_size = 4,
425 22f90bcb Beniamino Galvani
        .max_access_size = 4,
426 22f90bcb Beniamino Galvani
    },
427 22f90bcb Beniamino Galvani
};
428 22f90bcb Beniamino Galvani
429 22f90bcb Beniamino Galvani
static NetClientInfo net_aw_emac_info = {
430 22f90bcb Beniamino Galvani
    .type = NET_CLIENT_OPTIONS_KIND_NIC,
431 22f90bcb Beniamino Galvani
    .size = sizeof(NICState),
432 22f90bcb Beniamino Galvani
    .can_receive = aw_emac_can_receive,
433 22f90bcb Beniamino Galvani
    .receive = aw_emac_receive,
434 22f90bcb Beniamino Galvani
    .cleanup = aw_emac_cleanup,
435 22f90bcb Beniamino Galvani
    .link_status_changed = aw_emac_set_link,
436 22f90bcb Beniamino Galvani
};
437 22f90bcb Beniamino Galvani
438 22f90bcb Beniamino Galvani
static void aw_emac_init(Object *obj)
439 22f90bcb Beniamino Galvani
{
440 22f90bcb Beniamino Galvani
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
441 22f90bcb Beniamino Galvani
    AwEmacState *s = AW_EMAC(obj);
442 22f90bcb Beniamino Galvani
443 22f90bcb Beniamino Galvani
    memory_region_init_io(&s->iomem, OBJECT(s), &aw_emac_mem_ops, s,
444 22f90bcb Beniamino Galvani
                          "aw_emac", 0x1000);
445 22f90bcb Beniamino Galvani
    sysbus_init_mmio(sbd, &s->iomem);
446 22f90bcb Beniamino Galvani
    sysbus_init_irq(sbd, &s->irq);
447 22f90bcb Beniamino Galvani
}
448 22f90bcb Beniamino Galvani
449 22f90bcb Beniamino Galvani
static void aw_emac_realize(DeviceState *dev, Error **errp)
450 22f90bcb Beniamino Galvani
{
451 22f90bcb Beniamino Galvani
    AwEmacState *s = AW_EMAC(dev);
452 22f90bcb Beniamino Galvani
453 22f90bcb Beniamino Galvani
    qemu_macaddr_default_if_unset(&s->conf.macaddr);
454 22f90bcb Beniamino Galvani
    s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf,
455 22f90bcb Beniamino Galvani
                          object_get_typename(OBJECT(dev)), dev->id, s);
456 22f90bcb Beniamino Galvani
    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
457 22f90bcb Beniamino Galvani
458 22f90bcb Beniamino Galvani
    fifo8_create(&s->rx_fifo, RX_FIFO_SIZE);
459 22f90bcb Beniamino Galvani
    fifo8_create(&s->tx_fifo[0], TX_FIFO_SIZE);
460 22f90bcb Beniamino Galvani
    fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE);
461 22f90bcb Beniamino Galvani
}
462 22f90bcb Beniamino Galvani
463 22f90bcb Beniamino Galvani
static Property aw_emac_properties[] = {
464 22f90bcb Beniamino Galvani
    DEFINE_NIC_PROPERTIES(AwEmacState, conf),
465 22f90bcb Beniamino Galvani
    DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0),
466 22f90bcb Beniamino Galvani
    DEFINE_PROP_END_OF_LIST(),
467 22f90bcb Beniamino Galvani
};
468 22f90bcb Beniamino Galvani
469 22f90bcb Beniamino Galvani
static const VMStateDescription vmstate_mii = {
470 22f90bcb Beniamino Galvani
    .name = "rtl8201cp",
471 22f90bcb Beniamino Galvani
    .version_id = 1,
472 22f90bcb Beniamino Galvani
    .minimum_version_id = 1,
473 22f90bcb Beniamino Galvani
    .fields = (VMStateField[]) {
474 22f90bcb Beniamino Galvani
        VMSTATE_UINT16(bmcr, RTL8201CPState),
475 22f90bcb Beniamino Galvani
        VMSTATE_UINT16(bmsr, RTL8201CPState),
476 22f90bcb Beniamino Galvani
        VMSTATE_UINT16(anar, RTL8201CPState),
477 22f90bcb Beniamino Galvani
        VMSTATE_UINT16(anlpar, RTL8201CPState),
478 22f90bcb Beniamino Galvani
        VMSTATE_END_OF_LIST()
479 22f90bcb Beniamino Galvani
    }
480 22f90bcb Beniamino Galvani
};
481 22f90bcb Beniamino Galvani
482 22f90bcb Beniamino Galvani
static int aw_emac_post_load(void *opaque, int version_id)
483 22f90bcb Beniamino Galvani
{
484 22f90bcb Beniamino Galvani
    AwEmacState *s = opaque;
485 22f90bcb Beniamino Galvani
486 22f90bcb Beniamino Galvani
    aw_emac_set_link(qemu_get_queue(s->nic));
487 22f90bcb Beniamino Galvani
488 22f90bcb Beniamino Galvani
    return 0;
489 22f90bcb Beniamino Galvani
}
490 22f90bcb Beniamino Galvani
491 22f90bcb Beniamino Galvani
static const VMStateDescription vmstate_aw_emac = {
492 22f90bcb Beniamino Galvani
    .name = "allwinner_emac",
493 22f90bcb Beniamino Galvani
    .version_id = 1,
494 22f90bcb Beniamino Galvani
    .minimum_version_id = 1,
495 22f90bcb Beniamino Galvani
    .post_load = aw_emac_post_load,
496 22f90bcb Beniamino Galvani
    .fields = (VMStateField[]) {
497 22f90bcb Beniamino Galvani
        VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState),
498 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(ctl, AwEmacState),
499 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(tx_mode, AwEmacState),
500 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(rx_ctl, AwEmacState),
501 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(int_ctl, AwEmacState),
502 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(int_sta, AwEmacState),
503 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(phy_target, AwEmacState),
504 22f90bcb Beniamino Galvani
        VMSTATE_FIFO8(rx_fifo, AwEmacState),
505 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(rx_num_packets, AwEmacState),
506 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(rx_packet_size, AwEmacState),
507 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(rx_packet_pos, AwEmacState),
508 22f90bcb Beniamino Galvani
        VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, NUM_TX_FIFOS, 1,
509 22f90bcb Beniamino Galvani
                             vmstate_fifo8, Fifo8),
510 22f90bcb Beniamino Galvani
        VMSTATE_UINT32_ARRAY(tx_length, AwEmacState, NUM_TX_FIFOS),
511 22f90bcb Beniamino Galvani
        VMSTATE_UINT32(tx_channel, AwEmacState),
512 22f90bcb Beniamino Galvani
        VMSTATE_END_OF_LIST()
513 22f90bcb Beniamino Galvani
    }
514 22f90bcb Beniamino Galvani
};
515 22f90bcb Beniamino Galvani
516 22f90bcb Beniamino Galvani
static void aw_emac_class_init(ObjectClass *klass, void *data)
517 22f90bcb Beniamino Galvani
{
518 22f90bcb Beniamino Galvani
    DeviceClass *dc = DEVICE_CLASS(klass);
519 22f90bcb Beniamino Galvani
520 22f90bcb Beniamino Galvani
    dc->realize = aw_emac_realize;
521 22f90bcb Beniamino Galvani
    dc->props = aw_emac_properties;
522 22f90bcb Beniamino Galvani
    dc->reset = aw_emac_reset;
523 22f90bcb Beniamino Galvani
    dc->vmsd = &vmstate_aw_emac;
524 22f90bcb Beniamino Galvani
}
525 22f90bcb Beniamino Galvani
526 22f90bcb Beniamino Galvani
static const TypeInfo aw_emac_info = {
527 22f90bcb Beniamino Galvani
    .name           = TYPE_AW_EMAC,
528 22f90bcb Beniamino Galvani
    .parent         = TYPE_SYS_BUS_DEVICE,
529 22f90bcb Beniamino Galvani
    .instance_size  = sizeof(AwEmacState),
530 22f90bcb Beniamino Galvani
    .instance_init   = aw_emac_init,
531 22f90bcb Beniamino Galvani
    .class_init     = aw_emac_class_init,
532 22f90bcb Beniamino Galvani
};
533 22f90bcb Beniamino Galvani
534 22f90bcb Beniamino Galvani
static void aw_emac_register_types(void)
535 22f90bcb Beniamino Galvani
{
536 22f90bcb Beniamino Galvani
    type_register_static(&aw_emac_info);
537 22f90bcb Beniamino Galvani
}
538 22f90bcb Beniamino Galvani
539 22f90bcb Beniamino Galvani
type_init(aw_emac_register_types)