xref: /qemu/hw/net/lan9118_phy.c (revision c01194e17a01b2a17805dfa0c710aad7c05eab69)
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/irq.h"
18 #include "hw/resettable.h"
19 #include "migration/vmstate.h"
20 #include "qemu/log.h"
21 #include "trace.h"
22 
23 #define PHY_INT_ENERGYON            (1 << 7)
24 #define PHY_INT_AUTONEG_COMPLETE    (1 << 6)
25 #define PHY_INT_FAULT               (1 << 5)
26 #define PHY_INT_DOWN                (1 << 4)
27 #define PHY_INT_AUTONEG_LP          (1 << 3)
28 #define PHY_INT_PARFAULT            (1 << 2)
29 #define PHY_INT_AUTONEG_PAGE        (1 << 1)
30 
31 static void lan9118_phy_update_irq(Lan9118PhyState *s)
32 {
33     qemu_set_irq(s->irq, !!(s->ints & s->int_mask));
34 }
35 
36 uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg)
37 {
38     uint16_t val;
39 
40     switch (reg) {
41     case 0: /* Basic Control */
42         val = s->control;
43         break;
44     case 1: /* Basic Status */
45         val = s->status;
46         break;
47     case 2: /* ID1 */
48         val = 0x0007;
49         break;
50     case 3: /* ID2 */
51         val = 0xc0d1;
52         break;
53     case 4: /* Auto-neg advertisement */
54         val = s->advertise;
55         break;
56     case 5: /* Auto-neg Link Partner Ability */
57         val = 0x0f71;
58         break;
59     case 6: /* Auto-neg Expansion */
60         val = 1;
61         break;
62     case 29: /* Interrupt source. */
63         val = s->ints;
64         s->ints = 0;
65         lan9118_phy_update_irq(s);
66         break;
67     case 30: /* Interrupt mask */
68         val = s->int_mask;
69         break;
70     case 17:
71     case 18:
72     case 27:
73     case 31:
74         qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
75                       __func__, reg);
76         val = 0;
77         break;
78     default:
79         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
80                       __func__, reg);
81         val = 0;
82         break;
83     }
84 
85     trace_lan9118_phy_read(val, reg);
86 
87     return val;
88 }
89 
90 void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val)
91 {
92     trace_lan9118_phy_write(val, reg);
93 
94     switch (reg) {
95     case 0: /* Basic Control */
96         if (val & 0x8000) {
97             lan9118_phy_reset(s);
98         } else {
99             s->control = val & 0x7980;
100             /* Complete autonegotiation immediately. */
101             if (val & 0x1000) {
102                 s->status |= 0x0020;
103             }
104         }
105         break;
106     case 4: /* Auto-neg advertisement */
107         s->advertise = (val & 0x2d7f) | 0x80;
108         break;
109     case 30: /* Interrupt mask */
110         s->int_mask = val & 0xff;
111         lan9118_phy_update_irq(s);
112         break;
113     case 17:
114     case 18:
115     case 27:
116     case 31:
117         qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
118                       __func__, reg);
119         break;
120     default:
121         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
122                       __func__, reg);
123         break;
124     }
125 }
126 
127 void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down)
128 {
129     s->link_down = link_down;
130 
131     /* Autonegotiation status mirrors link status. */
132     if (link_down) {
133         trace_lan9118_phy_update_link("down");
134         s->status &= ~0x0024;
135         s->ints |= PHY_INT_DOWN;
136     } else {
137         trace_lan9118_phy_update_link("up");
138         s->status |= 0x0024;
139         s->ints |= PHY_INT_ENERGYON;
140         s->ints |= PHY_INT_AUTONEG_COMPLETE;
141     }
142     lan9118_phy_update_irq(s);
143 }
144 
145 void lan9118_phy_reset(Lan9118PhyState *s)
146 {
147     trace_lan9118_phy_reset();
148 
149     s->control = 0x3000;
150     s->status = 0x7809;
151     s->advertise = 0x01e1;
152     s->int_mask = 0;
153     s->ints = 0;
154     lan9118_phy_update_link(s, s->link_down);
155 }
156 
157 static void lan9118_phy_reset_hold(Object *obj, ResetType type)
158 {
159     Lan9118PhyState *s = LAN9118_PHY(obj);
160 
161     lan9118_phy_reset(s);
162 }
163 
164 static void lan9118_phy_init(Object *obj)
165 {
166     Lan9118PhyState *s = LAN9118_PHY(obj);
167 
168     qdev_init_gpio_out(DEVICE(s), &s->irq, 1);
169 }
170 
171 static const VMStateDescription vmstate_lan9118_phy = {
172     .name = "lan9118-phy",
173     .version_id = 1,
174     .minimum_version_id = 1,
175     .fields = (const VMStateField[]) {
176         VMSTATE_UINT16(status, Lan9118PhyState),
177         VMSTATE_UINT16(control, Lan9118PhyState),
178         VMSTATE_UINT16(advertise, Lan9118PhyState),
179         VMSTATE_UINT16(ints, Lan9118PhyState),
180         VMSTATE_UINT16(int_mask, Lan9118PhyState),
181         VMSTATE_BOOL(link_down, Lan9118PhyState),
182         VMSTATE_END_OF_LIST()
183     }
184 };
185 
186 static void lan9118_phy_class_init(ObjectClass *klass, void *data)
187 {
188     ResettableClass *rc = RESETTABLE_CLASS(klass);
189     DeviceClass *dc = DEVICE_CLASS(klass);
190 
191     rc->phases.hold = lan9118_phy_reset_hold;
192     dc->vmsd = &vmstate_lan9118_phy;
193 }
194 
195 static const TypeInfo types[] = {
196     {
197         .name          = TYPE_LAN9118_PHY,
198         .parent        = TYPE_SYS_BUS_DEVICE,
199         .instance_size = sizeof(Lan9118PhyState),
200         .instance_init = lan9118_phy_init,
201         .class_init    = lan9118_phy_class_init,
202     }
203 };
204 
205 DEFINE_TYPES(types)
206