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 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 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 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 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 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 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 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 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