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