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