xref: /openbmc/qemu/hw/net/lan9118_phy.c (revision c0cf6b412ecb099d49fe040d32fd5dd149f770d7)
1 /*
2  * SMSC LAN9118 PHY emulation
3  *
4  * Copyright (c) 2009 CodeSourcery, LLC.
5  * Written by Paul Brook
6  *
7  * This code is licensed under the GNU GPL v2
8  *
9  * Contributions after 2012-01-13 are licensed under the terms of the
10  * GNU GPL, version 2 or (at your option) any later version.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "hw/net/lan9118_phy.h"
15 #include "hw/irq.h"
16 #include "hw/resettable.h"
17 #include "migration/vmstate.h"
18 #include "qemu/log.h"
19 
20 #define PHY_INT_ENERGYON            (1 << 7)
21 #define PHY_INT_AUTONEG_COMPLETE    (1 << 6)
22 #define PHY_INT_FAULT               (1 << 5)
23 #define PHY_INT_DOWN                (1 << 4)
24 #define PHY_INT_AUTONEG_LP          (1 << 3)
25 #define PHY_INT_PARFAULT            (1 << 2)
26 #define PHY_INT_AUTONEG_PAGE        (1 << 1)
27 
28 static void lan9118_phy_update_irq(Lan9118PhyState *s)
29 {
30     qemu_set_irq(s->irq, !!(s->ints & s->int_mask));
31 }
32 
33 uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg)
34 {
35     uint16_t val;
36 
37     switch (reg) {
38     case 0: /* Basic Control */
39         return s->control;
40     case 1: /* Basic Status */
41         return s->status;
42     case 2: /* ID1 */
43         return 0x0007;
44     case 3: /* ID2 */
45         return 0xc0d1;
46     case 4: /* Auto-neg advertisement */
47         return s->advertise;
48     case 5: /* Auto-neg Link Partner Ability */
49         return 0x0f71;
50     case 6: /* Auto-neg Expansion */
51         return 1;
52         /* TODO 17, 18, 27, 29, 30, 31 */
53     case 29: /* Interrupt source. */
54         val = s->ints;
55         s->ints = 0;
56         lan9118_phy_update_irq(s);
57         return val;
58     case 30: /* Interrupt mask */
59         return s->int_mask;
60     default:
61         qemu_log_mask(LOG_GUEST_ERROR,
62                       "lan9118_phy_read: PHY read reg %d\n", reg);
63         return 0;
64     }
65 }
66 
67 void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val)
68 {
69     switch (reg) {
70     case 0: /* Basic Control */
71         if (val & 0x8000) {
72             lan9118_phy_reset(s);
73             break;
74         }
75         s->control = val & 0x7980;
76         /* Complete autonegotiation immediately. */
77         if (val & 0x1000) {
78             s->status |= 0x0020;
79         }
80         break;
81     case 4: /* Auto-neg advertisement */
82         s->advertise = (val & 0x2d7f) | 0x80;
83         break;
84         /* TODO 17, 18, 27, 31 */
85     case 30: /* Interrupt mask */
86         s->int_mask = val & 0xff;
87         lan9118_phy_update_irq(s);
88         break;
89     default:
90         qemu_log_mask(LOG_GUEST_ERROR,
91                       "lan9118_phy_write: PHY write reg %d = 0x%04x\n", reg, val);
92     }
93 }
94 
95 void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down)
96 {
97     s->link_down = link_down;
98 
99     /* Autonegotiation status mirrors link status. */
100     if (link_down) {
101         s->status &= ~0x0024;
102         s->ints |= PHY_INT_DOWN;
103     } else {
104         s->status |= 0x0024;
105         s->ints |= PHY_INT_ENERGYON;
106         s->ints |= PHY_INT_AUTONEG_COMPLETE;
107     }
108     lan9118_phy_update_irq(s);
109 }
110 
111 void lan9118_phy_reset(Lan9118PhyState *s)
112 {
113     s->control = 0x3000;
114     s->status = 0x7809;
115     s->advertise = 0x01e1;
116     s->int_mask = 0;
117     s->ints = 0;
118     lan9118_phy_update_link(s, s->link_down);
119 }
120 
121 static void lan9118_phy_reset_hold(Object *obj, ResetType type)
122 {
123     Lan9118PhyState *s = LAN9118_PHY(obj);
124 
125     lan9118_phy_reset(s);
126 }
127 
128 static void lan9118_phy_init(Object *obj)
129 {
130     Lan9118PhyState *s = LAN9118_PHY(obj);
131 
132     qdev_init_gpio_out(DEVICE(s), &s->irq, 1);
133 }
134 
135 static const VMStateDescription vmstate_lan9118_phy = {
136     .name = "lan9118-phy",
137     .version_id = 1,
138     .minimum_version_id = 1,
139     .fields = (const VMStateField[]) {
140         VMSTATE_UINT16(control, Lan9118PhyState),
141         VMSTATE_UINT16(status, Lan9118PhyState),
142         VMSTATE_UINT16(advertise, Lan9118PhyState),
143         VMSTATE_UINT16(ints, Lan9118PhyState),
144         VMSTATE_UINT16(int_mask, Lan9118PhyState),
145         VMSTATE_BOOL(link_down, Lan9118PhyState),
146         VMSTATE_END_OF_LIST()
147     }
148 };
149 
150 static void lan9118_phy_class_init(ObjectClass *klass, void *data)
151 {
152     ResettableClass *rc = RESETTABLE_CLASS(klass);
153     DeviceClass *dc = DEVICE_CLASS(klass);
154 
155     rc->phases.hold = lan9118_phy_reset_hold;
156     dc->vmsd = &vmstate_lan9118_phy;
157 }
158 
159 static const TypeInfo types[] = {
160     {
161         .name          = TYPE_LAN9118_PHY,
162         .parent        = TYPE_SYS_BUS_DEVICE,
163         .instance_size = sizeof(Lan9118PhyState),
164         .instance_init = lan9118_phy_init,
165         .class_init    = lan9118_phy_class_init,
166     }
167 };
168 
169 DEFINE_TYPES(types)
170