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