From 81f3d7443d3d0de0c90c85311718bdd08239c30e Mon Sep 17 00:00:00 2001 From: "Daniel P. Carvalho" Date: Sat, 3 Oct 2026 21:45:00 -0300 Subject: [PATCH] arch/mips/pic32mz: add PHY ioctls and link interrupts to the Ethernet driver. The driver had no d_ioctl, so CONFIG_NETDEV_PHY_IOCTL had no effect. Implement SIOCGMIIPHY, SIOCGMIIREG and SIOCSMIIREG and, with CONFIG_ARCH_PHY_INTERRUPT, SIOCMIINOTIFY. SIOCMIINOTIFY subscribes through phy_notify_subscribe() (the board provides arch_phy_irq()) and enables the PHY link down and auto-negotiation complete interrupts. This is what CONFIG_NETINIT_MONITOR needs. The PHY interrupt is implemented for the LAN8720 and LAN8740; add their interrupt source/mask register bits to mii.h. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Daniel P. Carvalho --- arch/mips/src/pic32mz/pic32mz_ethernet.c | 142 +++++++++++++++++++++++ include/nuttx/net/mii.h | 10 ++ 2 files changed, 152 insertions(+) diff --git a/arch/mips/src/pic32mz/pic32mz_ethernet.c b/arch/mips/src/pic32mz/pic32mz_ethernet.c index bcdc9f4b151..27ee10ed6e7 100644 --- a/arch/mips/src/pic32mz/pic32mz_ethernet.c +++ b/arch/mips/src/pic32mz/pic32mz_ethernet.c @@ -46,6 +46,10 @@ #include #include +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +# include +#endif + #ifdef CONFIG_NET_PKT # include #endif @@ -284,6 +288,16 @@ # undef PIC32MZ_HAVE_PHY #endif +/* PHY interrupt source/mask registers and the link up/down events */ + +#if defined(CONFIG_ETH0_PHY_LAN8720) || defined(CONFIG_ETH0_PHY_LAN8740) || \ + defined(CONFIG_ETH0_PHY_LAN8740A) +# define PIC32MZ_PHY_ISR MII_LAN8720_ISR +# define PIC32MZ_PHY_IMR MII_LAN8720_IMR +# define PIC32MZ_PHY_INTEN (MII_LAN8720_INT_LINKDOWN | \ + MII_LAN8720_INT_ANCOMPLETE) +#endif + /* These definitions are used to remember the speed/duplex settings */ #define PIC32MZ_SPEED_MASK 0x01 @@ -471,6 +485,10 @@ static int pic32mz_txavail(struct net_driver_s *dev); static int pic32mz_addmac(struct net_driver_s *dev, const uint8_t *mac); static int pic32mz_rmmac(struct net_driver_s *dev, const uint8_t *mac); #endif +#if defined(CONFIG_NETDEV_IOCTL) && defined(PIC32MZ_HAVE_PHY) +static int pic32mz_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif /* PHY initialization functions */ @@ -492,6 +510,9 @@ static inline int pic32mz_phyautoneg(uint8_t phyaddr); static int pic32mz_phymode(uint8_t phyaddr, uint8_t mode); # endif static inline int pic32mz_phyinit(struct pic32mz_driver_s *priv); +# if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +static int pic32mz_phyintenable(struct pic32mz_driver_s *priv); +# endif #else # define pic32mz_phyinit(priv) #endif @@ -2556,6 +2577,94 @@ static int pic32mz_rmmac(struct net_driver_s *dev, const uint8_t *mac) } #endif +/**************************************************************************** + * Function: pic32mz_ioctl + * + * Description: + * Handle network IOCTL commands directed to this device. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - The IOCTL command + * arg - The argument for the IOCTL command + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * The network device is locked. + * + ****************************************************************************/ + +#if defined(CONFIG_NETDEV_IOCTL) && defined(PIC32MZ_HAVE_PHY) +static int pic32mz_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_NETDEV_PHY_IOCTL + struct pic32mz_driver_s *priv = (struct pic32mz_driver_s *)dev->d_private; +#endif + int ret; + + switch (cmd) + { +#ifdef CONFIG_NETDEV_PHY_IOCTL +#ifdef CONFIG_ARCH_PHY_INTERRUPT + case SIOCMIINOTIFY: /* Set up for PHY event notifications */ + { + struct mii_ioctl_notify_s *req = + (struct mii_ioctl_notify_s *)((uintptr_t)arg); + + ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event); + if (ret == OK) + { + /* Enable PHY link up/down interrupts */ + + ret = pic32mz_phyintenable(priv); + } + } + break; +#endif + + case SIOCGMIIPHY: /* Get MII PHY address */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + + req->phy_id = priv->pd_phyaddr; + ret = OK; + } + break; + + case SIOCGMIIREG: /* Get register from MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + + req->val_out = pic32mz_phyread(req->phy_id, req->reg_num); + ret = OK; + } + break; + + case SIOCSMIIREG: /* Set register in MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + + pic32mz_phywrite(req->phy_id, req->reg_num, req->val_in); + ret = OK; + } + break; +#endif /* CONFIG_NETDEV_PHY_IOCTL */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif + /**************************************************************************** * Name: pic32mz_showmii * @@ -2587,6 +2696,36 @@ static void pic32mz_showmii(uint8_t phyaddr, const char *msg) } #endif +/**************************************************************************** + * Function: pic32mz_phyintenable + * + * Description: + * Enable the PHY link up/down interrupts. Reading the interrupt source + * register clears any pending event, so the PHY interrupt output is + * released before the new events are enabled. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; -ENOSYS if the PHY interrupts are not supported. + * + ****************************************************************************/ + +#if defined(PIC32MZ_HAVE_PHY) && defined(CONFIG_NETDEV_PHY_IOCTL) && \ + defined(CONFIG_ARCH_PHY_INTERRUPT) +static int pic32mz_phyintenable(struct pic32mz_driver_s *priv) +{ +#ifdef PIC32MZ_PHY_INTEN + pic32mz_phyread(priv->pd_phyaddr, PIC32MZ_PHY_ISR); + pic32mz_phywrite(priv->pd_phyaddr, PIC32MZ_PHY_IMR, PIC32MZ_PHY_INTEN); + return OK; +#else + return -ENOSYS; +#endif +} +#endif + /**************************************************************************** * Function: pic32mz_phybusywait * @@ -3395,6 +3534,9 @@ static inline int pic32mz_ethinitialize(int intf) #ifdef CONFIG_NET_MCASTGROUP priv->pd_dev.d_addmac = pic32mz_addmac; /* Add multicast MAC address */ priv->pd_dev.d_rmmac = pic32mz_rmmac; /* Remove multicast MAC address */ +#endif +#if defined(CONFIG_NETDEV_IOCTL) && defined(PIC32MZ_HAVE_PHY) + priv->pd_dev.d_ioctl = pic32mz_ioctl; /* Support PHY ioctl() calls */ #endif priv->pd_dev.d_private = priv; /* Used to recover private state from dev */ diff --git a/include/nuttx/net/mii.h b/include/nuttx/net/mii.h index d6b2a651ac2..23b2274333f 100644 --- a/include/nuttx/net/mii.h +++ b/include/nuttx/net/mii.h @@ -468,6 +468,16 @@ #define MII_LAN8720_SPSCR_GPIO1 (1 << 8) /* Bit 8: GPIO1 */ #define MII_LAN8720_SPSCR_GPIO2 (1 << 9) /* Bit 9: GPIO2 */ /* Bit 10-11: Reserved */ + +/* SMSC LAN8720 ISR and IMR register bits (same layout on the LAN8740) */ + +#define MII_LAN8720_INT_ANPAGE (1 << 1) /* Bit 1: Auto-negotiation page received */ +#define MII_LAN8720_INT_PDFAULT (1 << 2) /* Bit 2: Parallel detection fault */ +#define MII_LAN8720_INT_ANLPACK (1 << 3) /* Bit 3: Auto-negotiation LP acknowledge */ +#define MII_LAN8720_INT_LINKDOWN (1 << 4) /* Bit 4: Link down */ +#define MII_LAN8720_INT_REMFAULT (1 << 5) /* Bit 5: Remote fault detected */ +#define MII_LAN8720_INT_ANCOMPLETE (1 << 6) /* Bit 6: Auto-negotiation complete */ +#define MII_LAN8720_INT_ENERGYON (1 << 7) /* Bit 7: ENERGYON generated */ #define MII_LAN8720_SPSCR_ANEGDONE (1 << 12) /* Bit 12: Autonegotiation complete */ /* Bits 13-15: Reserved */