1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3 
4 #include <linux/phy.h>
5 #include <linux/phylink.h>
6 
7 #include "fbnic.h"
8 #include "fbnic_mac.h"
9 #include "fbnic_netdev.h"
10 
11 static struct fbnic_net *
fbnic_pcs_to_net(struct phylink_pcs * pcs)12 fbnic_pcs_to_net(struct phylink_pcs *pcs)
13 {
14 	return container_of(pcs, struct fbnic_net, phylink_pcs);
15 }
16 
17 static void
fbnic_phylink_pcs_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)18 fbnic_phylink_pcs_get_state(struct phylink_pcs *pcs,
19 			    struct phylink_link_state *state)
20 {
21 	struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
22 	struct fbnic_dev *fbd = fbn->fbd;
23 
24 	/* For now we use hard-coded defaults and FW config to determine
25 	 * the current values. In future patches we will add support for
26 	 * reconfiguring these values and changing link settings.
27 	 */
28 	switch (fbd->fw_cap.link_speed) {
29 	case FBNIC_FW_LINK_SPEED_25R1:
30 		state->speed = SPEED_25000;
31 		break;
32 	case FBNIC_FW_LINK_SPEED_50R2:
33 		state->speed = SPEED_50000;
34 		break;
35 	case FBNIC_FW_LINK_SPEED_100R2:
36 		state->speed = SPEED_100000;
37 		break;
38 	default:
39 		state->speed = SPEED_UNKNOWN;
40 		break;
41 	}
42 
43 	state->duplex = DUPLEX_FULL;
44 
45 	state->link = fbd->mac->pcs_get_link(fbd);
46 }
47 
48 static int
fbnic_phylink_pcs_enable(struct phylink_pcs * pcs)49 fbnic_phylink_pcs_enable(struct phylink_pcs *pcs)
50 {
51 	struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
52 	struct fbnic_dev *fbd = fbn->fbd;
53 
54 	return fbd->mac->pcs_enable(fbd);
55 }
56 
57 static void
fbnic_phylink_pcs_disable(struct phylink_pcs * pcs)58 fbnic_phylink_pcs_disable(struct phylink_pcs *pcs)
59 {
60 	struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
61 	struct fbnic_dev *fbd = fbn->fbd;
62 
63 	return fbd->mac->pcs_disable(fbd);
64 }
65 
66 static int
fbnic_phylink_pcs_config(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)67 fbnic_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
68 			 phy_interface_t interface,
69 			 const unsigned long *advertising,
70 			 bool permit_pause_to_mac)
71 {
72 	return 0;
73 }
74 
75 static const struct phylink_pcs_ops fbnic_phylink_pcs_ops = {
76 	.pcs_config = fbnic_phylink_pcs_config,
77 	.pcs_enable = fbnic_phylink_pcs_enable,
78 	.pcs_disable = fbnic_phylink_pcs_disable,
79 	.pcs_get_state = fbnic_phylink_pcs_get_state,
80 };
81 
82 static struct phylink_pcs *
fbnic_phylink_mac_select_pcs(struct phylink_config * config,phy_interface_t interface)83 fbnic_phylink_mac_select_pcs(struct phylink_config *config,
84 			     phy_interface_t interface)
85 {
86 	struct net_device *netdev = to_net_dev(config->dev);
87 	struct fbnic_net *fbn = netdev_priv(netdev);
88 
89 	return &fbn->phylink_pcs;
90 }
91 
92 static void
fbnic_phylink_mac_config(struct phylink_config * config,unsigned int mode,const struct phylink_link_state * state)93 fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode,
94 			 const struct phylink_link_state *state)
95 {
96 }
97 
98 static void
fbnic_phylink_mac_link_down(struct phylink_config * config,unsigned int mode,phy_interface_t interface)99 fbnic_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
100 			    phy_interface_t interface)
101 {
102 	struct net_device *netdev = to_net_dev(config->dev);
103 	struct fbnic_net *fbn = netdev_priv(netdev);
104 	struct fbnic_dev *fbd = fbn->fbd;
105 
106 	fbd->mac->link_down(fbd);
107 
108 	fbn->link_down_events++;
109 }
110 
111 static void
fbnic_phylink_mac_link_up(struct phylink_config * config,struct phy_device * phy,unsigned int mode,phy_interface_t interface,int speed,int duplex,bool tx_pause,bool rx_pause)112 fbnic_phylink_mac_link_up(struct phylink_config *config,
113 			  struct phy_device *phy, unsigned int mode,
114 			  phy_interface_t interface, int speed, int duplex,
115 			  bool tx_pause, bool rx_pause)
116 {
117 	struct net_device *netdev = to_net_dev(config->dev);
118 	struct fbnic_net *fbn = netdev_priv(netdev);
119 	struct fbnic_dev *fbd = fbn->fbd;
120 
121 	fbd->mac->link_up(fbd, tx_pause, rx_pause);
122 }
123 
124 static const struct phylink_mac_ops fbnic_phylink_mac_ops = {
125 	.mac_select_pcs = fbnic_phylink_mac_select_pcs,
126 	.mac_config = fbnic_phylink_mac_config,
127 	.mac_link_down = fbnic_phylink_mac_link_down,
128 	.mac_link_up = fbnic_phylink_mac_link_up,
129 };
130 
fbnic_phylink_init(struct net_device * netdev)131 int fbnic_phylink_init(struct net_device *netdev)
132 {
133 	struct fbnic_net *fbn = netdev_priv(netdev);
134 	struct phylink *phylink;
135 
136 	fbn->phylink_pcs.neg_mode = true;
137 	fbn->phylink_pcs.ops = &fbnic_phylink_pcs_ops;
138 
139 	fbn->phylink_config.dev = &netdev->dev;
140 	fbn->phylink_config.type = PHYLINK_NETDEV;
141 	fbn->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
142 					       MAC_10000FD | MAC_25000FD |
143 					       MAC_40000FD | MAC_50000FD |
144 					       MAC_100000FD;
145 	fbn->phylink_config.default_an_inband = true;
146 
147 	__set_bit(PHY_INTERFACE_MODE_XGMII,
148 		  fbn->phylink_config.supported_interfaces);
149 	__set_bit(PHY_INTERFACE_MODE_XLGMII,
150 		  fbn->phylink_config.supported_interfaces);
151 
152 	phylink = phylink_create(&fbn->phylink_config, NULL,
153 				 PHY_INTERFACE_MODE_XLGMII,
154 				 &fbnic_phylink_mac_ops);
155 	if (IS_ERR(phylink))
156 		return PTR_ERR(phylink);
157 
158 	fbn->phylink = phylink;
159 
160 	return 0;
161 }
162