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
lan9118_phy_update_irq(Lan9118PhyState * s)32 static void lan9118_phy_update_irq(Lan9118PhyState *s)
33 {
34 qemu_set_irq(s->irq, !!(s->ints & s->int_mask));
35 }
36
lan9118_phy_read(Lan9118PhyState * s,int reg)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
lan9118_phy_write(Lan9118PhyState * s,int reg,uint16_t val)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
lan9118_phy_update_link(Lan9118PhyState * s,bool link_down)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
lan9118_phy_reset(Lan9118PhyState * s)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
lan9118_phy_reset_hold(Object * obj,ResetType type)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
lan9118_phy_init(Object * obj)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
lan9118_phy_class_init(ObjectClass * klass,const void * data)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