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 32 static void lan9118_phy_update_irq(Lan9118PhyState *s) 33 { 34 qemu_set_irq(s->irq, !!(s->ints & s->int_mask)); 35 } 36 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 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 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 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 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 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 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