1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright Sunplus Technology Co., Ltd. 3 * All rights reserved. 4 */ 5 6 #include <linux/netdevice.h> 7 #include <linux/bitfield.h> 8 #include <linux/of_mdio.h> 9 10 #include "spl2sw_register.h" 11 #include "spl2sw_define.h" 12 #include "spl2sw_phy.h" 13 14 static void spl2sw_mii_link_change(struct net_device *ndev) 15 { 16 struct spl2sw_mac *mac = netdev_priv(ndev); 17 struct phy_device *phydev = ndev->phydev; 18 struct spl2sw_common *comm = mac->comm; 19 u32 reg; 20 21 reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE); 22 23 if (phydev->link) { 24 reg |= FIELD_PREP(MAC_FORCE_RMII_LINK, mac->lan_port); 25 26 if (phydev->speed == 100) { 27 reg |= FIELD_PREP(MAC_FORCE_RMII_SPD, mac->lan_port); 28 } else { 29 reg &= FIELD_PREP(MAC_FORCE_RMII_SPD, ~mac->lan_port) | 30 ~MAC_FORCE_RMII_SPD; 31 } 32 33 if (phydev->duplex) { 34 reg |= FIELD_PREP(MAC_FORCE_RMII_DPX, mac->lan_port); 35 } else { 36 reg &= FIELD_PREP(MAC_FORCE_RMII_DPX, ~mac->lan_port) | 37 ~MAC_FORCE_RMII_DPX; 38 } 39 40 if (phydev->pause) { 41 reg |= FIELD_PREP(MAC_FORCE_RMII_FC, mac->lan_port); 42 } else { 43 reg &= FIELD_PREP(MAC_FORCE_RMII_FC, ~mac->lan_port) | 44 ~MAC_FORCE_RMII_FC; 45 } 46 } else { 47 reg &= FIELD_PREP(MAC_FORCE_RMII_LINK, ~mac->lan_port) | 48 ~MAC_FORCE_RMII_LINK; 49 } 50 51 writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE); 52 53 phy_print_status(phydev); 54 } 55 56 int spl2sw_phy_connect(struct spl2sw_common *comm) 57 { 58 struct phy_device *phydev; 59 struct net_device *ndev; 60 struct spl2sw_mac *mac; 61 int i; 62 63 for (i = 0; i < MAX_NETDEV_NUM; i++) 64 if (comm->ndev[i]) { 65 ndev = comm->ndev[i]; 66 mac = netdev_priv(ndev); 67 phydev = of_phy_connect(ndev, mac->phy_node, spl2sw_mii_link_change, 68 0, mac->phy_mode); 69 if (!phydev) 70 return -ENODEV; 71 72 phy_support_asym_pause(phydev); 73 phy_attached_info(phydev); 74 } 75 76 return 0; 77 } 78 79 void spl2sw_phy_remove(struct spl2sw_common *comm) 80 { 81 struct net_device *ndev; 82 int i; 83 84 for (i = 0; i < MAX_NETDEV_NUM; i++) 85 if (comm->ndev[i]) { 86 ndev = comm->ndev[i]; 87 if (ndev) { 88 phy_disconnect(ndev->phydev); 89 ndev->phydev = NULL; 90 } 91 } 92 } 93