xref: /qemu/hw/net/lan9118_phy.c (revision d64db833d6e3cbe9ea5f36342480f920f3675cea)
1 /*
2  * SMSC LAN9118 PHY emulation
3  *
4  * Copyright (c) 2009 CodeSourcery, LLC.
5  * Written by Paul Brook
6  *
7  * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
8  *
9  * This code is licensed under the GNU GPL v2
10  *
11  * Contributions after 2012-01-13 are licensed under the terms of the
12  * GNU GPL, version 2 or (at your option) any later version.
13  */
14 
15 #include "qemu/osdep.h"
16 #include "hw/net/lan9118_phy.h"
17 #include "hw/net/mii.h"
18 #include "hw/irq.h"
19 #include "hw/resettable.h"
20 #include "migration/vmstate.h"
21 #include "qemu/log.h"
22 #include "trace.h"
23 
24 #define PHY_INT_ENERGYON            (1 << 7)
25 #define PHY_INT_AUTONEG_COMPLETE    (1 << 6)
26 #define PHY_INT_FAULT               (1 << 5)
27 #define PHY_INT_DOWN                (1 << 4)
28 #define PHY_INT_AUTONEG_LP          (1 << 3)
29 #define PHY_INT_PARFAULT            (1 << 2)
30 #define PHY_INT_AUTONEG_PAGE        (1 << 1)
31 
32 static void lan9118_phy_update_irq(Lan9118PhyState *s)
33 {
34     qemu_set_irq(s->irq, !!(s->ints & s->int_mask));
35 }
36 
37 uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg)
38 {
39     uint16_t val;
40 
41     switch (reg) {
42     case MII_BMCR:
43         val = s->control;
44         break;
45     case MII_BMSR:
46         val = s->status;
47         break;
48     case MII_PHYID1:
49         val = SMSCLAN9118_PHYID1;
50         break;
51     case MII_PHYID2:
52         val = SMSCLAN9118_PHYID2;
53         break;
54     case MII_ANAR:
55         val = s->advertise;
56         break;
57     case MII_ANLPAR:
58         val = MII_ANLPAR_PAUSEASY | MII_ANLPAR_PAUSE | MII_ANLPAR_T4 |
59               MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD |
60               MII_ANLPAR_10 | MII_ANLPAR_CSMACD;
61         break;
62     case MII_ANER:
63         val = MII_ANER_NWAY;
64         break;
65     case 29: /* Interrupt source. */
66         val = s->ints;
67         s->ints = 0;
68         lan9118_phy_update_irq(s);
69         break;
70     case 30: /* Interrupt mask */
71         val = s->int_mask;
72         break;
73     case 17:
74     case 18:
75     case 27:
76     case 31:
77         qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
78                       __func__, reg);
79         val = 0;
80         break;
81     default:
82         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
83                       __func__, reg);
84         val = 0;
85         break;
86     }
87 
88     trace_lan9118_phy_read(val, reg);
89 
90     return val;
91 }
92 
93 void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val)
94 {
95     trace_lan9118_phy_write(val, reg);
96 
97     switch (reg) {
98     case MII_BMCR:
99         if (val & MII_BMCR_RESET) {
100             lan9118_phy_reset(s);
101         } else {
102             s->control = val & (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 |
103                                 MII_BMCR_AUTOEN | MII_BMCR_PDOWN | MII_BMCR_FD |
104                                 MII_BMCR_CTST);
105             /* Complete autonegotiation immediately. */
106             if (val & MII_BMCR_AUTOEN) {
107                 s->status |= MII_BMSR_AN_COMP;
108             }
109         }
110         break;
111     case MII_ANAR:
112         s->advertise = (val & (MII_ANAR_RFAULT | MII_ANAR_PAUSE_ASYM |
113                                MII_ANAR_PAUSE | MII_ANAR_TXFD | MII_ANAR_10FD |
114                                MII_ANAR_10 | MII_ANAR_SELECT))
115                      | MII_ANAR_TX;
116         break;
117     case 30: /* Interrupt mask */
118         s->int_mask = val & 0xff;
119         lan9118_phy_update_irq(s);
120         break;
121     case 17:
122     case 18:
123     case 27:
124     case 31:
125         qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
126                       __func__, reg);
127         break;
128     default:
129         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
130                       __func__, reg);
131         break;
132     }
133 }
134 
135 void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down)
136 {
137     s->link_down = link_down;
138 
139     /* Autonegotiation status mirrors link status. */
140     if (link_down) {
141         trace_lan9118_phy_update_link("down");
142         s->status &= ~(MII_BMSR_AN_COMP | MII_BMSR_LINK_ST);
143         s->ints |= PHY_INT_DOWN;
144     } else {
145         trace_lan9118_phy_update_link("up");
146         s->status |= MII_BMSR_AN_COMP | MII_BMSR_LINK_ST;
147         s->ints |= PHY_INT_ENERGYON;
148         s->ints |= PHY_INT_AUTONEG_COMPLETE;
149     }
150     lan9118_phy_update_irq(s);
151 }
152 
153 void lan9118_phy_reset(Lan9118PhyState *s)
154 {
155     trace_lan9118_phy_reset();
156 
157     s->control = MII_BMCR_AUTOEN | MII_BMCR_SPEED100;
158     s->status = MII_BMSR_100TX_FD
159                 | MII_BMSR_100TX_HD
160                 | MII_BMSR_10T_FD
161                 | MII_BMSR_10T_HD
162                 | MII_BMSR_AUTONEG
163                 | MII_BMSR_EXTCAP;
164     s->advertise = MII_ANAR_TXFD
165                    | MII_ANAR_TX
166                    | MII_ANAR_10FD
167                    | MII_ANAR_10
168                    | MII_ANAR_CSMACD;
169     s->int_mask = 0;
170     s->ints = 0;
171     lan9118_phy_update_link(s, s->link_down);
172 }
173 
174 static void lan9118_phy_reset_hold(Object *obj, ResetType type)
175 {
176     Lan9118PhyState *s = LAN9118_PHY(obj);
177 
178     lan9118_phy_reset(s);
179 }
180 
181 static void lan9118_phy_init(Object *obj)
182 {
183     Lan9118PhyState *s = LAN9118_PHY(obj);
184 
185     qdev_init_gpio_out(DEVICE(s), &s->irq, 1);
186 }
187 
188 static const VMStateDescription vmstate_lan9118_phy = {
189     .name = "lan9118-phy",
190     .version_id = 1,
191     .minimum_version_id = 1,
192     .fields = (const VMStateField[]) {
193         VMSTATE_UINT16(status, Lan9118PhyState),
194         VMSTATE_UINT16(control, Lan9118PhyState),
195         VMSTATE_UINT16(advertise, Lan9118PhyState),
196         VMSTATE_UINT16(ints, Lan9118PhyState),
197         VMSTATE_UINT16(int_mask, Lan9118PhyState),
198         VMSTATE_BOOL(link_down, Lan9118PhyState),
199         VMSTATE_END_OF_LIST()
200     }
201 };
202 
203 static void lan9118_phy_class_init(ObjectClass *klass, const void *data)
204 {
205     ResettableClass *rc = RESETTABLE_CLASS(klass);
206     DeviceClass *dc = DEVICE_CLASS(klass);
207 
208     rc->phases.hold = lan9118_phy_reset_hold;
209     dc->vmsd = &vmstate_lan9118_phy;
210 }
211 
212 static const TypeInfo types[] = {
213     {
214         .name          = TYPE_LAN9118_PHY,
215         .parent        = TYPE_SYS_BUS_DEVICE,
216         .instance_size = sizeof(Lan9118PhyState),
217         .instance_init = lan9118_phy_init,
218         .class_init    = lan9118_phy_class_init,
219     }
220 };
221 
222 DEFINE_TYPES(types)
223