 973a2fac48
			
		
	
	
		973a2fac48
		
	
	
	
	
		
			
			The real device advertises this mode and the device model already advertises 100 mbps half duplex and 10 mbps full+half duplex. So advertise this mode to make the model more realistic. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Bernhard Beschow <shentey@gmail.com> Tested-by: Guenter Roeck <linux@roeck-us.net> Message-id: 20241102125724.532843-6-shentey@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
		
			
				
	
	
		
			223 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * SMSC LAN9118 PHY emulation
 | |
|  *
 | |
|  * Copyright (c) 2009 CodeSourcery, LLC.
 | |
|  * Written by Paul Brook
 | |
|  *
 | |
|  * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
 | |
|  *
 | |
|  * This code is licensed under the GNU GPL v2
 | |
|  *
 | |
|  * Contributions after 2012-01-13 are licensed under the terms of the
 | |
|  * GNU GPL, version 2 or (at your option) any later version.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "hw/net/lan9118_phy.h"
 | |
| #include "hw/net/mii.h"
 | |
| #include "hw/irq.h"
 | |
| #include "hw/resettable.h"
 | |
| #include "migration/vmstate.h"
 | |
| #include "qemu/log.h"
 | |
| #include "trace.h"
 | |
| 
 | |
| #define PHY_INT_ENERGYON            (1 << 7)
 | |
| #define PHY_INT_AUTONEG_COMPLETE    (1 << 6)
 | |
| #define PHY_INT_FAULT               (1 << 5)
 | |
| #define PHY_INT_DOWN                (1 << 4)
 | |
| #define PHY_INT_AUTONEG_LP          (1 << 3)
 | |
| #define PHY_INT_PARFAULT            (1 << 2)
 | |
| #define PHY_INT_AUTONEG_PAGE        (1 << 1)
 | |
| 
 | |
| static void lan9118_phy_update_irq(Lan9118PhyState *s)
 | |
| {
 | |
|     qemu_set_irq(s->irq, !!(s->ints & s->int_mask));
 | |
| }
 | |
| 
 | |
| uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg)
 | |
| {
 | |
|     uint16_t val;
 | |
| 
 | |
|     switch (reg) {
 | |
|     case MII_BMCR:
 | |
|         val = s->control;
 | |
|         break;
 | |
|     case MII_BMSR:
 | |
|         val = s->status;
 | |
|         break;
 | |
|     case MII_PHYID1:
 | |
|         val = SMSCLAN9118_PHYID1;
 | |
|         break;
 | |
|     case MII_PHYID2:
 | |
|         val = SMSCLAN9118_PHYID2;
 | |
|         break;
 | |
|     case MII_ANAR:
 | |
|         val = s->advertise;
 | |
|         break;
 | |
|     case MII_ANLPAR:
 | |
|         val = MII_ANLPAR_PAUSEASY | MII_ANLPAR_PAUSE | MII_ANLPAR_T4 |
 | |
|               MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD |
 | |
|               MII_ANLPAR_10 | MII_ANLPAR_CSMACD;
 | |
|         break;
 | |
|     case MII_ANER:
 | |
|         val = MII_ANER_NWAY;
 | |
|         break;
 | |
|     case 29: /* Interrupt source. */
 | |
|         val = s->ints;
 | |
|         s->ints = 0;
 | |
|         lan9118_phy_update_irq(s);
 | |
|         break;
 | |
|     case 30: /* Interrupt mask */
 | |
|         val = s->int_mask;
 | |
|         break;
 | |
|     case 17:
 | |
|     case 18:
 | |
|     case 27:
 | |
|     case 31:
 | |
|         qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
 | |
|                       __func__, reg);
 | |
|         val = 0;
 | |
|         break;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
 | |
|                       __func__, reg);
 | |
|         val = 0;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     trace_lan9118_phy_read(val, reg);
 | |
| 
 | |
|     return val;
 | |
| }
 | |
| 
 | |
| void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val)
 | |
| {
 | |
|     trace_lan9118_phy_write(val, reg);
 | |
| 
 | |
|     switch (reg) {
 | |
|     case MII_BMCR:
 | |
|         if (val & MII_BMCR_RESET) {
 | |
|             lan9118_phy_reset(s);
 | |
|         } else {
 | |
|             s->control = val & (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 |
 | |
|                                 MII_BMCR_AUTOEN | MII_BMCR_PDOWN | MII_BMCR_FD |
 | |
|                                 MII_BMCR_CTST);
 | |
|             /* Complete autonegotiation immediately. */
 | |
|             if (val & MII_BMCR_AUTOEN) {
 | |
|                 s->status |= MII_BMSR_AN_COMP;
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
|     case MII_ANAR:
 | |
|         s->advertise = (val & (MII_ANAR_RFAULT | MII_ANAR_PAUSE_ASYM |
 | |
|                                MII_ANAR_PAUSE | MII_ANAR_TXFD | MII_ANAR_10FD |
 | |
|                                MII_ANAR_10 | MII_ANAR_SELECT))
 | |
|                      | MII_ANAR_TX;
 | |
|         break;
 | |
|     case 30: /* Interrupt mask */
 | |
|         s->int_mask = val & 0xff;
 | |
|         lan9118_phy_update_irq(s);
 | |
|         break;
 | |
|     case 17:
 | |
|     case 18:
 | |
|     case 27:
 | |
|     case 31:
 | |
|         qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
 | |
|                       __func__, reg);
 | |
|         break;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
 | |
|                       __func__, reg);
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down)
 | |
| {
 | |
|     s->link_down = link_down;
 | |
| 
 | |
|     /* Autonegotiation status mirrors link status. */
 | |
|     if (link_down) {
 | |
|         trace_lan9118_phy_update_link("down");
 | |
|         s->status &= ~(MII_BMSR_AN_COMP | MII_BMSR_LINK_ST);
 | |
|         s->ints |= PHY_INT_DOWN;
 | |
|     } else {
 | |
|         trace_lan9118_phy_update_link("up");
 | |
|         s->status |= MII_BMSR_AN_COMP | MII_BMSR_LINK_ST;
 | |
|         s->ints |= PHY_INT_ENERGYON;
 | |
|         s->ints |= PHY_INT_AUTONEG_COMPLETE;
 | |
|     }
 | |
|     lan9118_phy_update_irq(s);
 | |
| }
 | |
| 
 | |
| void lan9118_phy_reset(Lan9118PhyState *s)
 | |
| {
 | |
|     trace_lan9118_phy_reset();
 | |
| 
 | |
|     s->control = MII_BMCR_AUTOEN | MII_BMCR_SPEED100;
 | |
|     s->status = MII_BMSR_100TX_FD
 | |
|                 | MII_BMSR_100TX_HD
 | |
|                 | MII_BMSR_10T_FD
 | |
|                 | MII_BMSR_10T_HD
 | |
|                 | MII_BMSR_AUTONEG
 | |
|                 | MII_BMSR_EXTCAP;
 | |
|     s->advertise = MII_ANAR_TXFD
 | |
|                    | MII_ANAR_TX
 | |
|                    | MII_ANAR_10FD
 | |
|                    | MII_ANAR_10
 | |
|                    | MII_ANAR_CSMACD;
 | |
|     s->int_mask = 0;
 | |
|     s->ints = 0;
 | |
|     lan9118_phy_update_link(s, s->link_down);
 | |
| }
 | |
| 
 | |
| static void lan9118_phy_reset_hold(Object *obj, ResetType type)
 | |
| {
 | |
|     Lan9118PhyState *s = LAN9118_PHY(obj);
 | |
| 
 | |
|     lan9118_phy_reset(s);
 | |
| }
 | |
| 
 | |
| static void lan9118_phy_init(Object *obj)
 | |
| {
 | |
|     Lan9118PhyState *s = LAN9118_PHY(obj);
 | |
| 
 | |
|     qdev_init_gpio_out(DEVICE(s), &s->irq, 1);
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_lan9118_phy = {
 | |
|     .name = "lan9118-phy",
 | |
|     .version_id = 1,
 | |
|     .minimum_version_id = 1,
 | |
|     .fields = (const VMStateField[]) {
 | |
|         VMSTATE_UINT16(status, Lan9118PhyState),
 | |
|         VMSTATE_UINT16(control, Lan9118PhyState),
 | |
|         VMSTATE_UINT16(advertise, Lan9118PhyState),
 | |
|         VMSTATE_UINT16(ints, Lan9118PhyState),
 | |
|         VMSTATE_UINT16(int_mask, Lan9118PhyState),
 | |
|         VMSTATE_BOOL(link_down, Lan9118PhyState),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void lan9118_phy_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     ResettableClass *rc = RESETTABLE_CLASS(klass);
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
| 
 | |
|     rc->phases.hold = lan9118_phy_reset_hold;
 | |
|     dc->vmsd = &vmstate_lan9118_phy;
 | |
| }
 | |
| 
 | |
| static const TypeInfo types[] = {
 | |
|     {
 | |
|         .name          = TYPE_LAN9118_PHY,
 | |
|         .parent        = TYPE_SYS_BUS_DEVICE,
 | |
|         .instance_size = sizeof(Lan9118PhyState),
 | |
|         .instance_init = lan9118_phy_init,
 | |
|         .class_init    = lan9118_phy_class_init,
 | |
|     }
 | |
| };
 | |
| 
 | |
| DEFINE_TYPES(types)
 |