xref: /linux/drivers/net/ethernet/sunplus/spl2sw_phy.c (revision da1d9caf95def6f0320819cf941c9fd1069ba9e1)
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