171947923SIoana Ciornei // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
271947923SIoana Ciornei /* Copyright 2019 NXP */
371947923SIoana Ciornei
43264f599SCalvin Johnson #include <linux/acpi.h>
5e7026f15SColin Foster #include <linux/pcs-lynx.h>
6f978fe85SIoana Ciornei #include <linux/phy/phy.h>
73264f599SCalvin Johnson #include <linux/property.h>
83264f599SCalvin Johnson
971947923SIoana Ciornei #include "dpaa2-eth.h"
1071947923SIoana Ciornei #include "dpaa2-mac.h"
1171947923SIoana Ciornei
1271947923SIoana Ciornei #define phylink_to_dpaa2_mac(config) \
1371947923SIoana Ciornei container_of((config), struct dpaa2_mac, phylink_config)
1471947923SIoana Ciornei
15dff95381SIoana Ciornei #define DPMAC_PROTOCOL_CHANGE_VER_MAJOR 4
16dff95381SIoana Ciornei #define DPMAC_PROTOCOL_CHANGE_VER_MINOR 8
17dff95381SIoana Ciornei
18dff95381SIoana Ciornei #define DPAA2_MAC_FEATURE_PROTOCOL_CHANGE BIT(0)
19dff95381SIoana Ciornei
dpaa2_mac_cmp_ver(struct dpaa2_mac * mac,u16 ver_major,u16 ver_minor)20dff95381SIoana Ciornei static int dpaa2_mac_cmp_ver(struct dpaa2_mac *mac,
21dff95381SIoana Ciornei u16 ver_major, u16 ver_minor)
22dff95381SIoana Ciornei {
23dff95381SIoana Ciornei if (mac->ver_major == ver_major)
24dff95381SIoana Ciornei return mac->ver_minor - ver_minor;
25dff95381SIoana Ciornei return mac->ver_major - ver_major;
26dff95381SIoana Ciornei }
27dff95381SIoana Ciornei
dpaa2_mac_detect_features(struct dpaa2_mac * mac)28dff95381SIoana Ciornei static void dpaa2_mac_detect_features(struct dpaa2_mac *mac)
29dff95381SIoana Ciornei {
30dff95381SIoana Ciornei mac->features = 0;
31dff95381SIoana Ciornei
32dff95381SIoana Ciornei if (dpaa2_mac_cmp_ver(mac, DPMAC_PROTOCOL_CHANGE_VER_MAJOR,
33dff95381SIoana Ciornei DPMAC_PROTOCOL_CHANGE_VER_MINOR) >= 0)
34dff95381SIoana Ciornei mac->features |= DPAA2_MAC_FEATURE_PROTOCOL_CHANGE;
35dff95381SIoana Ciornei }
36dff95381SIoana Ciornei
phy_mode(enum dpmac_eth_if eth_if,phy_interface_t * if_mode)37226df3efSIoana Ciornei static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
3871947923SIoana Ciornei {
39226df3efSIoana Ciornei *if_mode = PHY_INTERFACE_MODE_NA;
40226df3efSIoana Ciornei
4171947923SIoana Ciornei switch (eth_if) {
4271947923SIoana Ciornei case DPMAC_ETH_IF_RGMII:
43226df3efSIoana Ciornei *if_mode = PHY_INTERFACE_MODE_RGMII;
44226df3efSIoana Ciornei break;
4594ae899bSIoana Ciornei case DPMAC_ETH_IF_USXGMII:
4694ae899bSIoana Ciornei *if_mode = PHY_INTERFACE_MODE_USXGMII;
4794ae899bSIoana Ciornei break;
4894ae899bSIoana Ciornei case DPMAC_ETH_IF_QSGMII:
4994ae899bSIoana Ciornei *if_mode = PHY_INTERFACE_MODE_QSGMII;
5094ae899bSIoana Ciornei break;
5194ae899bSIoana Ciornei case DPMAC_ETH_IF_SGMII:
5294ae899bSIoana Ciornei *if_mode = PHY_INTERFACE_MODE_SGMII;
5394ae899bSIoana Ciornei break;
5494ae899bSIoana Ciornei case DPMAC_ETH_IF_XFI:
5594ae899bSIoana Ciornei *if_mode = PHY_INTERFACE_MODE_10GBASER;
5694ae899bSIoana Ciornei break;
57*9a43827eSJosua Mayer case DPMAC_ETH_IF_CAUI:
58*9a43827eSJosua Mayer *if_mode = PHY_INTERFACE_MODE_25GBASER;
59*9a43827eSJosua Mayer break;
6071947923SIoana Ciornei default:
6171947923SIoana Ciornei return -EINVAL;
6271947923SIoana Ciornei }
63226df3efSIoana Ciornei
64226df3efSIoana Ciornei return 0;
6571947923SIoana Ciornei }
6671947923SIoana Ciornei
dpmac_eth_if_mode(phy_interface_t if_mode)67f978fe85SIoana Ciornei static enum dpmac_eth_if dpmac_eth_if_mode(phy_interface_t if_mode)
68f978fe85SIoana Ciornei {
69f978fe85SIoana Ciornei switch (if_mode) {
70f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_RGMII:
71f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_RGMII_ID:
72f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_RGMII_RXID:
73f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_RGMII_TXID:
74f978fe85SIoana Ciornei return DPMAC_ETH_IF_RGMII;
75f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_USXGMII:
76f978fe85SIoana Ciornei return DPMAC_ETH_IF_USXGMII;
77f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_QSGMII:
78f978fe85SIoana Ciornei return DPMAC_ETH_IF_QSGMII;
79f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_SGMII:
80f978fe85SIoana Ciornei return DPMAC_ETH_IF_SGMII;
81f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_10GBASER:
82f978fe85SIoana Ciornei return DPMAC_ETH_IF_XFI;
83f978fe85SIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX:
84f978fe85SIoana Ciornei return DPMAC_ETH_IF_1000BASEX;
85*9a43827eSJosua Mayer case PHY_INTERFACE_MODE_25GBASER:
86*9a43827eSJosua Mayer return DPMAC_ETH_IF_CAUI;
87f978fe85SIoana Ciornei default:
88f978fe85SIoana Ciornei return DPMAC_ETH_IF_MII;
89f978fe85SIoana Ciornei }
90f978fe85SIoana Ciornei }
91f978fe85SIoana Ciornei
dpaa2_mac_get_node(struct device * dev,u16 dpmac_id)923264f599SCalvin Johnson static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
933264f599SCalvin Johnson u16 dpmac_id)
9471947923SIoana Ciornei {
955b1e38c0SRobert-Ionut Alexa struct fwnode_handle *fwnode, *parent = NULL, *child = NULL;
963264f599SCalvin Johnson struct device_node *dpmacs = NULL;
9771947923SIoana Ciornei int err;
983264f599SCalvin Johnson u32 id;
9971947923SIoana Ciornei
1003264f599SCalvin Johnson fwnode = dev_fwnode(dev->parent);
1013264f599SCalvin Johnson if (is_of_node(fwnode)) {
10271947923SIoana Ciornei dpmacs = of_find_node_by_name(NULL, "dpmacs");
10371947923SIoana Ciornei if (!dpmacs)
10471947923SIoana Ciornei return NULL;
1053264f599SCalvin Johnson parent = of_fwnode_handle(dpmacs);
1063264f599SCalvin Johnson } else if (is_acpi_node(fwnode)) {
1073264f599SCalvin Johnson parent = fwnode;
1084e30e98cSIoana Ciornei } else {
1094e30e98cSIoana Ciornei /* The root dprc device didn't yet get to finalize it's probe,
1104e30e98cSIoana Ciornei * thus the fwnode field is not yet set. Defer probe if we are
1114e30e98cSIoana Ciornei * facing this situation.
1124e30e98cSIoana Ciornei */
11337fe9b98SSean Anderson dev_dbg(dev, "dprc not finished probing\n");
1144e30e98cSIoana Ciornei return ERR_PTR(-EPROBE_DEFER);
1153264f599SCalvin Johnson }
11671947923SIoana Ciornei
1173264f599SCalvin Johnson fwnode_for_each_child_node(parent, child) {
1183264f599SCalvin Johnson err = -EINVAL;
1193264f599SCalvin Johnson if (is_acpi_device_node(child))
1203264f599SCalvin Johnson err = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &id);
1213264f599SCalvin Johnson else if (is_of_node(child))
1223264f599SCalvin Johnson err = of_property_read_u32(to_of_node(child), "reg", &id);
12371947923SIoana Ciornei if (err)
12471947923SIoana Ciornei continue;
12571947923SIoana Ciornei
1263264f599SCalvin Johnson if (id == dpmac_id) {
12771947923SIoana Ciornei of_node_put(dpmacs);
1283264f599SCalvin Johnson return child;
1293264f599SCalvin Johnson }
1303264f599SCalvin Johnson }
1313264f599SCalvin Johnson of_node_put(dpmacs);
1323264f599SCalvin Johnson return NULL;
13371947923SIoana Ciornei }
13471947923SIoana Ciornei
dpaa2_mac_get_if_mode(struct fwnode_handle * dpmac_node,struct dpmac_attr attr)1353264f599SCalvin Johnson static int dpaa2_mac_get_if_mode(struct fwnode_handle *dpmac_node,
13671947923SIoana Ciornei struct dpmac_attr attr)
13771947923SIoana Ciornei {
1380c65b2b9SAndrew Lunn phy_interface_t if_mode;
1390c65b2b9SAndrew Lunn int err;
14071947923SIoana Ciornei
1413264f599SCalvin Johnson err = fwnode_get_phy_mode(dpmac_node);
1423264f599SCalvin Johnson if (err > 0)
1433264f599SCalvin Johnson return err;
14471947923SIoana Ciornei
145226df3efSIoana Ciornei err = phy_mode(attr.eth_if, &if_mode);
146226df3efSIoana Ciornei if (!err)
14771947923SIoana Ciornei return if_mode;
14871947923SIoana Ciornei
149226df3efSIoana Ciornei return err;
15071947923SIoana Ciornei }
15171947923SIoana Ciornei
dpaa2_mac_select_pcs(struct phylink_config * config,phy_interface_t interface)152c592286aSRussell King (Oracle) static struct phylink_pcs *dpaa2_mac_select_pcs(struct phylink_config *config,
153c592286aSRussell King (Oracle) phy_interface_t interface)
154c592286aSRussell King (Oracle) {
155c592286aSRussell King (Oracle) struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
156c592286aSRussell King (Oracle)
157c592286aSRussell King (Oracle) return mac->pcs;
158c592286aSRussell King (Oracle) }
159c592286aSRussell King (Oracle)
dpaa2_mac_config(struct phylink_config * config,unsigned int mode,const struct phylink_link_state * state)16071947923SIoana Ciornei static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
16171947923SIoana Ciornei const struct phylink_link_state *state)
16271947923SIoana Ciornei {
16371947923SIoana Ciornei struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
16471947923SIoana Ciornei struct dpmac_link_state *dpmac_state = &mac->state;
16571947923SIoana Ciornei int err;
16671947923SIoana Ciornei
16799d0f3a1SRussell King (Oracle) if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
16899d0f3a1SRussell King (Oracle) state->advertising))
16971947923SIoana Ciornei dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
17071947923SIoana Ciornei else
17171947923SIoana Ciornei dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
17271947923SIoana Ciornei
17371947923SIoana Ciornei err = dpmac_set_link_state(mac->mc_io, 0,
17471947923SIoana Ciornei mac->mc_dev->mc_handle, dpmac_state);
17571947923SIoana Ciornei if (err)
17637556a4aSRussell King netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
17737556a4aSRussell King __func__, err);
178f978fe85SIoana Ciornei
179f978fe85SIoana Ciornei if (!mac->serdes_phy)
180f978fe85SIoana Ciornei return;
181f978fe85SIoana Ciornei
182f978fe85SIoana Ciornei /* This happens only if we support changing of protocol at runtime */
183f978fe85SIoana Ciornei err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle,
184f978fe85SIoana Ciornei dpmac_eth_if_mode(state->interface));
185f978fe85SIoana Ciornei if (err)
186f978fe85SIoana Ciornei netdev_err(mac->net_dev, "dpmac_set_protocol() = %d\n", err);
187f978fe85SIoana Ciornei
188f978fe85SIoana Ciornei err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface);
189f978fe85SIoana Ciornei if (err)
190f978fe85SIoana Ciornei netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err);
19171947923SIoana Ciornei }
19271947923SIoana Ciornei
dpaa2_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)19391a208f2SRussell King static void dpaa2_mac_link_up(struct phylink_config *config,
19491a208f2SRussell King struct phy_device *phy,
19591a208f2SRussell King unsigned int mode, phy_interface_t interface,
19691a208f2SRussell King int speed, int duplex,
19791a208f2SRussell King bool tx_pause, bool rx_pause)
19871947923SIoana Ciornei {
19971947923SIoana Ciornei struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
20071947923SIoana Ciornei struct dpmac_link_state *dpmac_state = &mac->state;
20171947923SIoana Ciornei int err;
20271947923SIoana Ciornei
20371947923SIoana Ciornei dpmac_state->up = 1;
20437556a4aSRussell King
20537556a4aSRussell King dpmac_state->rate = speed;
20637556a4aSRussell King
20737556a4aSRussell King if (duplex == DUPLEX_HALF)
20837556a4aSRussell King dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
20937556a4aSRussell King else if (duplex == DUPLEX_FULL)
21037556a4aSRussell King dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
21137556a4aSRussell King
21237556a4aSRussell King if (rx_pause)
21337556a4aSRussell King dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
21437556a4aSRussell King else
21537556a4aSRussell King dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
21637556a4aSRussell King
21737556a4aSRussell King if (rx_pause ^ tx_pause)
21837556a4aSRussell King dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
21937556a4aSRussell King else
22037556a4aSRussell King dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
22137556a4aSRussell King
22271947923SIoana Ciornei err = dpmac_set_link_state(mac->mc_io, 0,
22371947923SIoana Ciornei mac->mc_dev->mc_handle, dpmac_state);
22471947923SIoana Ciornei if (err)
22537556a4aSRussell King netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
22637556a4aSRussell King __func__, err);
22771947923SIoana Ciornei }
22871947923SIoana Ciornei
dpaa2_mac_link_down(struct phylink_config * config,unsigned int mode,phy_interface_t interface)22971947923SIoana Ciornei static void dpaa2_mac_link_down(struct phylink_config *config,
23071947923SIoana Ciornei unsigned int mode,
23171947923SIoana Ciornei phy_interface_t interface)
23271947923SIoana Ciornei {
23371947923SIoana Ciornei struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
23471947923SIoana Ciornei struct dpmac_link_state *dpmac_state = &mac->state;
23571947923SIoana Ciornei int err;
23671947923SIoana Ciornei
23771947923SIoana Ciornei dpmac_state->up = 0;
23871947923SIoana Ciornei err = dpmac_set_link_state(mac->mc_io, 0,
23971947923SIoana Ciornei mac->mc_dev->mc_handle, dpmac_state);
24071947923SIoana Ciornei if (err)
24171947923SIoana Ciornei netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
24271947923SIoana Ciornei }
24371947923SIoana Ciornei
24471947923SIoana Ciornei static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
245c592286aSRussell King (Oracle) .mac_select_pcs = dpaa2_mac_select_pcs,
24671947923SIoana Ciornei .mac_config = dpaa2_mac_config,
24771947923SIoana Ciornei .mac_link_up = dpaa2_mac_link_up,
24871947923SIoana Ciornei .mac_link_down = dpaa2_mac_link_down,
24971947923SIoana Ciornei };
25071947923SIoana Ciornei
dpaa2_pcs_create(struct dpaa2_mac * mac,struct fwnode_handle * dpmac_node,int id)25194ae899bSIoana Ciornei static int dpaa2_pcs_create(struct dpaa2_mac *mac,
2523264f599SCalvin Johnson struct fwnode_handle *dpmac_node,
2533264f599SCalvin Johnson int id)
25494ae899bSIoana Ciornei {
25594ae899bSIoana Ciornei struct fwnode_handle *node;
2563264f599SCalvin Johnson struct phylink_pcs *pcs;
25794ae899bSIoana Ciornei
2583264f599SCalvin Johnson node = fwnode_find_reference(dpmac_node, "pcs-handle", 0);
2593264f599SCalvin Johnson if (IS_ERR(node)) {
26094ae899bSIoana Ciornei /* do not error out on old DTS files */
26194ae899bSIoana Ciornei netdev_warn(mac->net_dev, "pcs-handle node not found\n");
26294ae899bSIoana Ciornei return 0;
26394ae899bSIoana Ciornei }
26494ae899bSIoana Ciornei
2653264f599SCalvin Johnson pcs = lynx_pcs_create_fwnode(node);
26694ae899bSIoana Ciornei fwnode_handle_put(node);
2673264f599SCalvin Johnson
26894ae899bSIoana Ciornei if (pcs == ERR_PTR(-EPROBE_DEFER)) {
26994ae899bSIoana Ciornei netdev_dbg(mac->net_dev, "missing PCS device\n");
27094ae899bSIoana Ciornei return -EPROBE_DEFER;
2713264f599SCalvin Johnson }
2723264f599SCalvin Johnson
27337fe9b98SSean Anderson if (pcs == ERR_PTR(-ENODEV)) {
27437fe9b98SSean Anderson netdev_err(mac->net_dev, "pcs-handle node not available\n");
27594ae899bSIoana Ciornei return PTR_ERR(pcs);
27637fe9b98SSean Anderson }
27794ae899bSIoana Ciornei
27894ae899bSIoana Ciornei if (IS_ERR(pcs)) {
27994ae899bSIoana Ciornei netdev_err(mac->net_dev,
28094ae899bSIoana Ciornei "lynx_pcs_create_fwnode() failed: %pe\n", pcs);
28194ae899bSIoana Ciornei return PTR_ERR(pcs);
28294ae899bSIoana Ciornei }
28394ae899bSIoana Ciornei
28494ae899bSIoana Ciornei mac->pcs = pcs;
28594ae899bSIoana Ciornei
28694ae899bSIoana Ciornei return 0;
28794ae899bSIoana Ciornei }
28894ae899bSIoana Ciornei
dpaa2_pcs_destroy(struct dpaa2_mac * mac)28994ae899bSIoana Ciornei static void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
290e7026f15SColin Foster {
29194ae899bSIoana Ciornei struct phylink_pcs *phylink_pcs = mac->pcs;
292e7026f15SColin Foster
293e7026f15SColin Foster if (phylink_pcs) {
294e7026f15SColin Foster lynx_pcs_destroy(phylink_pcs);
295e7026f15SColin Foster mac->pcs = NULL;
296e7026f15SColin Foster }
29794ae899bSIoana Ciornei }
29894ae899bSIoana Ciornei
dpaa2_mac_set_supported_interfaces(struct dpaa2_mac * mac)29994ae899bSIoana Ciornei static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac)
30094ae899bSIoana Ciornei {
30194ae899bSIoana Ciornei int intf, err;
302aa95c371SIoana Ciornei
303aa95c371SIoana Ciornei /* We support the current interface mode, and if we have a PCS
304f978fe85SIoana Ciornei * similar interface modes that do not require the SerDes lane to be
305f978fe85SIoana Ciornei * reconfigured.
306aa95c371SIoana Ciornei */
307aa95c371SIoana Ciornei __set_bit(mac->if_mode, mac->phylink_config.supported_interfaces);
308aa95c371SIoana Ciornei if (mac->pcs) {
309aa95c371SIoana Ciornei switch (mac->if_mode) {
310aa95c371SIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX:
311aa95c371SIoana Ciornei case PHY_INTERFACE_MODE_SGMII:
312aa95c371SIoana Ciornei __set_bit(PHY_INTERFACE_MODE_1000BASEX,
313aa95c371SIoana Ciornei mac->phylink_config.supported_interfaces);
314aa95c371SIoana Ciornei __set_bit(PHY_INTERFACE_MODE_SGMII,
315aa95c371SIoana Ciornei mac->phylink_config.supported_interfaces);
316aa95c371SIoana Ciornei break;
317aa95c371SIoana Ciornei
318aa95c371SIoana Ciornei default:
319aa95c371SIoana Ciornei break;
320aa95c371SIoana Ciornei }
321aa95c371SIoana Ciornei }
322aa95c371SIoana Ciornei
323aa95c371SIoana Ciornei if (!mac->serdes_phy)
324aa95c371SIoana Ciornei return;
325f978fe85SIoana Ciornei
326f978fe85SIoana Ciornei /* In case we have access to the SerDes phy/lane, then ask the SerDes
327f978fe85SIoana Ciornei * driver what interfaces are supported based on the current PLL
328f978fe85SIoana Ciornei * configuration.
329f978fe85SIoana Ciornei */
330f978fe85SIoana Ciornei for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
331f978fe85SIoana Ciornei if (intf == PHY_INTERFACE_MODE_NA)
332f978fe85SIoana Ciornei continue;
333f978fe85SIoana Ciornei
334f978fe85SIoana Ciornei err = phy_validate(mac->serdes_phy, PHY_MODE_ETHERNET, intf, NULL);
335f978fe85SIoana Ciornei if (err)
336f978fe85SIoana Ciornei continue;
337f978fe85SIoana Ciornei
338f978fe85SIoana Ciornei __set_bit(intf, mac->phylink_config.supported_interfaces);
339f978fe85SIoana Ciornei }
340f978fe85SIoana Ciornei }
341f978fe85SIoana Ciornei
dpaa2_mac_start(struct dpaa2_mac * mac)342f978fe85SIoana Ciornei void dpaa2_mac_start(struct dpaa2_mac *mac)
343f978fe85SIoana Ciornei {
344f978fe85SIoana Ciornei ASSERT_RTNL();
345f978fe85SIoana Ciornei
346f978fe85SIoana Ciornei if (mac->serdes_phy)
34738533388SVladimir Oltean phy_power_on(mac->serdes_phy);
34838533388SVladimir Oltean
349f978fe85SIoana Ciornei phylink_start(mac->phylink);
350f978fe85SIoana Ciornei }
35138533388SVladimir Oltean
dpaa2_mac_stop(struct dpaa2_mac * mac)35238533388SVladimir Oltean void dpaa2_mac_stop(struct dpaa2_mac *mac)
353f978fe85SIoana Ciornei {
354f978fe85SIoana Ciornei ASSERT_RTNL();
355f978fe85SIoana Ciornei
356f978fe85SIoana Ciornei phylink_stop(mac->phylink);
35738533388SVladimir Oltean
35838533388SVladimir Oltean if (mac->serdes_phy)
35938533388SVladimir Oltean phy_power_off(mac->serdes_phy);
36038533388SVladimir Oltean }
361f978fe85SIoana Ciornei
dpaa2_mac_connect(struct dpaa2_mac * mac)362f978fe85SIoana Ciornei int dpaa2_mac_connect(struct dpaa2_mac *mac)
363aa95c371SIoana Ciornei {
364aa95c371SIoana Ciornei struct net_device *net_dev = mac->net_dev;
36571947923SIoana Ciornei struct fwnode_handle *dpmac_node;
36671947923SIoana Ciornei struct phy *serdes_phy = NULL;
36771947923SIoana Ciornei struct phylink *phylink;
3683264f599SCalvin Johnson int err;
369f978fe85SIoana Ciornei
37071947923SIoana Ciornei mac->if_link_type = mac->attr.link_type;
37171947923SIoana Ciornei
37271947923SIoana Ciornei dpmac_node = mac->fw_node;
373095dca16SIoana Ciornei if (!dpmac_node) {
374095dca16SIoana Ciornei netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id);
3753264f599SCalvin Johnson return -ENODEV;
376095dca16SIoana Ciornei }
377095dca16SIoana Ciornei
37871947923SIoana Ciornei err = dpaa2_mac_get_if_mode(dpmac_node, mac->attr);
37971947923SIoana Ciornei if (err < 0)
38071947923SIoana Ciornei return -EINVAL;
381095dca16SIoana Ciornei mac->if_mode = err;
382b193f2edSIoana Ciornei
383b193f2edSIoana Ciornei if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE &&
38471947923SIoana Ciornei !phy_interface_mode_is_rgmii(mac->if_mode) &&
38571947923SIoana Ciornei is_of_node(dpmac_node)) {
386f978fe85SIoana Ciornei serdes_phy = of_phy_get(to_of_node(dpmac_node), NULL);
387f978fe85SIoana Ciornei
388f978fe85SIoana Ciornei if (serdes_phy == ERR_PTR(-ENODEV))
389f978fe85SIoana Ciornei serdes_phy = NULL;
390f978fe85SIoana Ciornei else if (IS_ERR(serdes_phy))
391f978fe85SIoana Ciornei return PTR_ERR(serdes_phy);
392f978fe85SIoana Ciornei else
393f978fe85SIoana Ciornei phy_init(serdes_phy);
394f978fe85SIoana Ciornei }
395f978fe85SIoana Ciornei mac->serdes_phy = serdes_phy;
396f978fe85SIoana Ciornei
397f978fe85SIoana Ciornei /* The MAC does not have the capability to add RGMII delays so
398f978fe85SIoana Ciornei * error out if the interface mode requests them and there is no PHY
399f978fe85SIoana Ciornei * to act upon them
40071947923SIoana Ciornei */
40171947923SIoana Ciornei if (of_phy_is_fixed_link(to_of_node(dpmac_node)) &&
40271947923SIoana Ciornei (mac->if_mode == PHY_INTERFACE_MODE_RGMII_ID ||
40371947923SIoana Ciornei mac->if_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
4043264f599SCalvin Johnson mac->if_mode == PHY_INTERFACE_MODE_RGMII_TXID)) {
40571947923SIoana Ciornei netdev_err(net_dev, "RGMII delay not supported\n");
40671947923SIoana Ciornei return -EINVAL;
40771947923SIoana Ciornei }
40871947923SIoana Ciornei
409b193f2edSIoana Ciornei if ((mac->attr.link_type == DPMAC_LINK_TYPE_PHY &&
41071947923SIoana Ciornei mac->attr.eth_if != DPMAC_ETH_IF_RGMII) ||
41171947923SIoana Ciornei mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE) {
412085f1776SRussell King err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id);
413085f1776SRussell King if (err)
414085f1776SRussell King return err;
415095dca16SIoana Ciornei }
41694ae899bSIoana Ciornei
417b193f2edSIoana Ciornei memset(&mac->phylink_config, 0, sizeof(mac->phylink_config));
41894ae899bSIoana Ciornei mac->phylink_config.dev = &net_dev->dev;
41994ae899bSIoana Ciornei mac->phylink_config.type = PHYLINK_NETDEV;
42015d0b14cSRussell King
42171947923SIoana Ciornei mac->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
42271947923SIoana Ciornei MAC_10FD | MAC_100FD | MAC_1000FD | MAC_2500FD | MAC_5000FD |
42371947923SIoana Ciornei MAC_10000FD | MAC_25000FD;
4246d386f66SRussell King (Oracle)
4256d386f66SRussell King (Oracle) dpaa2_mac_set_supported_interfaces(mac);
426*9a43827eSJosua Mayer
4276d386f66SRussell King (Oracle) phylink = phylink_create(&mac->phylink_config,
428aa95c371SIoana Ciornei dpmac_node, mac->if_mode,
42915d0b14cSRussell King &dpaa2_mac_phylink_ops);
43071947923SIoana Ciornei if (IS_ERR(phylink)) {
4313264f599SCalvin Johnson err = PTR_ERR(phylink);
43271947923SIoana Ciornei goto err_pcs_destroy;
43371947923SIoana Ciornei }
43471947923SIoana Ciornei mac->phylink = phylink;
43594ae899bSIoana Ciornei
43671947923SIoana Ciornei rtnl_lock();
43771947923SIoana Ciornei err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
43871947923SIoana Ciornei rtnl_unlock();
43987db82cbSVladimir Oltean if (err) {
4403264f599SCalvin Johnson netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
44187db82cbSVladimir Oltean goto err_phylink_destroy;
44271947923SIoana Ciornei }
4433264f599SCalvin Johnson
44471947923SIoana Ciornei return 0;
44571947923SIoana Ciornei
44671947923SIoana Ciornei err_phylink_destroy:
44771947923SIoana Ciornei phylink_destroy(mac->phylink);
44871947923SIoana Ciornei err_pcs_destroy:
44971947923SIoana Ciornei dpaa2_pcs_destroy(mac);
45071947923SIoana Ciornei
45194ae899bSIoana Ciornei return err;
45294ae899bSIoana Ciornei }
453095dca16SIoana Ciornei
dpaa2_mac_disconnect(struct dpaa2_mac * mac)45471947923SIoana Ciornei void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
45571947923SIoana Ciornei {
45671947923SIoana Ciornei rtnl_lock();
45771947923SIoana Ciornei phylink_disconnect_phy(mac->phylink);
45871947923SIoana Ciornei rtnl_unlock();
45987db82cbSVladimir Oltean
46071947923SIoana Ciornei phylink_destroy(mac->phylink);
46187db82cbSVladimir Oltean dpaa2_pcs_destroy(mac);
46287db82cbSVladimir Oltean of_phy_put(mac->serdes_phy);
46371947923SIoana Ciornei mac->serdes_phy = NULL;
46494ae899bSIoana Ciornei }
465f978fe85SIoana Ciornei
dpaa2_mac_open(struct dpaa2_mac * mac)466f978fe85SIoana Ciornei int dpaa2_mac_open(struct dpaa2_mac *mac)
467095dca16SIoana Ciornei {
46894ae899bSIoana Ciornei struct fsl_mc_device *dpmac_dev = mac->mc_dev;
469095dca16SIoana Ciornei struct net_device *net_dev = mac->net_dev;
470095dca16SIoana Ciornei struct fwnode_handle *fw_node;
471095dca16SIoana Ciornei int err;
472095dca16SIoana Ciornei
4734e30e98cSIoana Ciornei err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id,
474095dca16SIoana Ciornei &dpmac_dev->mc_handle);
475095dca16SIoana Ciornei if (err || !dpmac_dev->mc_handle) {
476095dca16SIoana Ciornei netdev_err(net_dev, "dpmac_open() = %d\n", err);
477095dca16SIoana Ciornei return -ENODEV;
478095dca16SIoana Ciornei }
479095dca16SIoana Ciornei
480095dca16SIoana Ciornei err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle,
481095dca16SIoana Ciornei &mac->attr);
482095dca16SIoana Ciornei if (err) {
483095dca16SIoana Ciornei netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err);
484095dca16SIoana Ciornei goto err_close_dpmac;
485095dca16SIoana Ciornei }
486095dca16SIoana Ciornei
487095dca16SIoana Ciornei err = dpmac_get_api_version(mac->mc_io, 0, &mac->ver_major, &mac->ver_minor);
488095dca16SIoana Ciornei if (err) {
489095dca16SIoana Ciornei netdev_err(net_dev, "dpmac_get_api_version() = %d\n", err);
490dff95381SIoana Ciornei goto err_close_dpmac;
491dff95381SIoana Ciornei }
492dff95381SIoana Ciornei
493dff95381SIoana Ciornei dpaa2_mac_detect_features(mac);
494dff95381SIoana Ciornei
495dff95381SIoana Ciornei /* Find the device node representing the MAC device and link the device
496dff95381SIoana Ciornei * behind the associated netdev to it.
497dff95381SIoana Ciornei */
498b193f2edSIoana Ciornei fw_node = dpaa2_mac_get_node(&mac->mc_dev->dev, mac->attr.id);
499b193f2edSIoana Ciornei if (IS_ERR(fw_node)) {
500b193f2edSIoana Ciornei err = PTR_ERR(fw_node);
5014e30e98cSIoana Ciornei goto err_close_dpmac;
5024e30e98cSIoana Ciornei }
5034e30e98cSIoana Ciornei
5044e30e98cSIoana Ciornei mac->fw_node = fw_node;
5054e30e98cSIoana Ciornei net_dev->dev.of_node = to_of_node(mac->fw_node);
5064e30e98cSIoana Ciornei
5074e30e98cSIoana Ciornei return 0;
5083264f599SCalvin Johnson
509b193f2edSIoana Ciornei err_close_dpmac:
510095dca16SIoana Ciornei dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
511095dca16SIoana Ciornei return err;
512095dca16SIoana Ciornei }
513095dca16SIoana Ciornei
dpaa2_mac_close(struct dpaa2_mac * mac)514095dca16SIoana Ciornei void dpaa2_mac_close(struct dpaa2_mac *mac)
515095dca16SIoana Ciornei {
516095dca16SIoana Ciornei struct fsl_mc_device *dpmac_dev = mac->mc_dev;
517095dca16SIoana Ciornei
518095dca16SIoana Ciornei dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
519095dca16SIoana Ciornei if (mac->fw_node)
520095dca16SIoana Ciornei fwnode_handle_put(mac->fw_node);
521095dca16SIoana Ciornei }
5223264f599SCalvin Johnson
5233264f599SCalvin Johnson static char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = {
52471947923SIoana Ciornei [DPMAC_CNT_ING_ALL_FRAME] = "[mac] rx all frames",
525991df1fbSIoana Ciornei [DPMAC_CNT_ING_GOOD_FRAME] = "[mac] rx frames ok",
526991df1fbSIoana Ciornei [DPMAC_CNT_ING_ERR_FRAME] = "[mac] rx frame errors",
527991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_DISCARD] = "[mac] rx frame discards",
528991df1fbSIoana Ciornei [DPMAC_CNT_ING_UCAST_FRAME] = "[mac] rx u-cast",
529991df1fbSIoana Ciornei [DPMAC_CNT_ING_BCAST_FRAME] = "[mac] rx b-cast",
530991df1fbSIoana Ciornei [DPMAC_CNT_ING_MCAST_FRAME] = "[mac] rx m-cast",
531991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_64] = "[mac] rx 64 bytes",
532991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_127] = "[mac] rx 65-127 bytes",
533991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_255] = "[mac] rx 128-255 bytes",
534991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_511] = "[mac] rx 256-511 bytes",
535991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_1023] = "[mac] rx 512-1023 bytes",
536991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_1518] = "[mac] rx 1024-1518 bytes",
537991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAME_1519_MAX] = "[mac] rx 1519-max bytes",
538991df1fbSIoana Ciornei [DPMAC_CNT_ING_FRAG] = "[mac] rx frags",
539991df1fbSIoana Ciornei [DPMAC_CNT_ING_JABBER] = "[mac] rx jabber",
540991df1fbSIoana Ciornei [DPMAC_CNT_ING_ALIGN_ERR] = "[mac] rx align errors",
541991df1fbSIoana Ciornei [DPMAC_CNT_ING_OVERSIZED] = "[mac] rx oversized",
542991df1fbSIoana Ciornei [DPMAC_CNT_ING_VALID_PAUSE_FRAME] = "[mac] rx pause",
543991df1fbSIoana Ciornei [DPMAC_CNT_ING_BYTE] = "[mac] rx bytes",
544991df1fbSIoana Ciornei [DPMAC_CNT_EGR_GOOD_FRAME] = "[mac] tx frames ok",
545991df1fbSIoana Ciornei [DPMAC_CNT_EGR_UCAST_FRAME] = "[mac] tx u-cast",
546991df1fbSIoana Ciornei [DPMAC_CNT_EGR_MCAST_FRAME] = "[mac] tx m-cast",
547991df1fbSIoana Ciornei [DPMAC_CNT_EGR_BCAST_FRAME] = "[mac] tx b-cast",
548991df1fbSIoana Ciornei [DPMAC_CNT_EGR_ERR_FRAME] = "[mac] tx frame errors",
549991df1fbSIoana Ciornei [DPMAC_CNT_EGR_UNDERSIZED] = "[mac] tx undersized",
550991df1fbSIoana Ciornei [DPMAC_CNT_EGR_VALID_PAUSE_FRAME] = "[mac] tx b-pause",
551991df1fbSIoana Ciornei [DPMAC_CNT_EGR_BYTE] = "[mac] tx bytes",
552991df1fbSIoana Ciornei };
553991df1fbSIoana Ciornei
554991df1fbSIoana Ciornei #define DPAA2_MAC_NUM_STATS ARRAY_SIZE(dpaa2_mac_ethtool_stats)
555991df1fbSIoana Ciornei
dpaa2_mac_get_sset_count(void)556991df1fbSIoana Ciornei int dpaa2_mac_get_sset_count(void)
557991df1fbSIoana Ciornei {
558991df1fbSIoana Ciornei return DPAA2_MAC_NUM_STATS;
559991df1fbSIoana Ciornei }
560991df1fbSIoana Ciornei
dpaa2_mac_get_strings(u8 * data)561991df1fbSIoana Ciornei void dpaa2_mac_get_strings(u8 *data)
562991df1fbSIoana Ciornei {
563991df1fbSIoana Ciornei u8 *p = data;
564991df1fbSIoana Ciornei int i;
565991df1fbSIoana Ciornei
566991df1fbSIoana Ciornei for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) {
567991df1fbSIoana Ciornei strscpy(p, dpaa2_mac_ethtool_stats[i], ETH_GSTRING_LEN);
568991df1fbSIoana Ciornei p += ETH_GSTRING_LEN;
569991df1fbSIoana Ciornei }
570f029c781SWolfram Sang }
571991df1fbSIoana Ciornei
dpaa2_mac_get_ethtool_stats(struct dpaa2_mac * mac,u64 * data)572991df1fbSIoana Ciornei void dpaa2_mac_get_ethtool_stats(struct dpaa2_mac *mac, u64 *data)
573991df1fbSIoana Ciornei {
574991df1fbSIoana Ciornei struct fsl_mc_device *dpmac_dev = mac->mc_dev;
575991df1fbSIoana Ciornei int i, err;
576991df1fbSIoana Ciornei u64 value;
577991df1fbSIoana Ciornei
578991df1fbSIoana Ciornei for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) {
579991df1fbSIoana Ciornei err = dpmac_get_counter(mac->mc_io, 0, dpmac_dev->mc_handle,
580991df1fbSIoana Ciornei i, &value);
581991df1fbSIoana Ciornei if (err) {
582991df1fbSIoana Ciornei netdev_err_once(mac->net_dev,
583991df1fbSIoana Ciornei "dpmac_get_counter error %d\n", err);
584991df1fbSIoana Ciornei *(data + i) = U64_MAX;
585991df1fbSIoana Ciornei continue;
586991df1fbSIoana Ciornei }
587991df1fbSIoana Ciornei *(data + i) = value;
588991df1fbSIoana Ciornei }
589991df1fbSIoana Ciornei }
590991df1fbSIoana Ciornei