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