1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include <linux/module.h> |
4 | #include <linux/phylink.h> |
5 | #include <linux/device.h> |
6 | #include <linux/netdevice.h> |
7 | #include <linux/phy/phy.h> |
8 | |
9 | #include "lan966x_main.h" |
10 | |
11 | static struct phylink_pcs *lan966x_phylink_mac_select(struct phylink_config *config, |
12 | phy_interface_t interface) |
13 | { |
14 | struct lan966x_port *port = netdev_priv(to_net_dev(config->dev)); |
15 | |
16 | return &port->phylink_pcs; |
17 | } |
18 | |
19 | static void lan966x_phylink_mac_config(struct phylink_config *config, |
20 | unsigned int mode, |
21 | const struct phylink_link_state *state) |
22 | { |
23 | } |
24 | |
25 | static int lan966x_phylink_mac_prepare(struct phylink_config *config, |
26 | unsigned int mode, |
27 | phy_interface_t iface) |
28 | { |
29 | struct lan966x_port *port = netdev_priv(to_net_dev(config->dev)); |
30 | phy_interface_t serdes_mode = iface; |
31 | int err; |
32 | |
33 | if (port->serdes) { |
34 | err = phy_set_mode_ext(phy: port->serdes, mode: PHY_MODE_ETHERNET, |
35 | submode: serdes_mode); |
36 | if (err) { |
37 | netdev_err(to_net_dev(config->dev), |
38 | format: "Could not set mode of SerDes\n" ); |
39 | return err; |
40 | } |
41 | } |
42 | |
43 | return 0; |
44 | } |
45 | |
46 | static void lan966x_phylink_mac_link_up(struct phylink_config *config, |
47 | struct phy_device *phy, |
48 | unsigned int mode, |
49 | phy_interface_t interface, |
50 | int speed, int duplex, |
51 | bool tx_pause, bool rx_pause) |
52 | { |
53 | struct lan966x_port *port = netdev_priv(to_net_dev(config->dev)); |
54 | struct lan966x_port_config *port_config = &port->config; |
55 | |
56 | port_config->duplex = duplex; |
57 | port_config->speed = speed; |
58 | port_config->pause = 0; |
59 | port_config->pause |= tx_pause ? MLO_PAUSE_TX : 0; |
60 | port_config->pause |= rx_pause ? MLO_PAUSE_RX : 0; |
61 | |
62 | if (phy_interface_mode_is_rgmii(mode: interface)) |
63 | phy_set_speed(phy: port->serdes, speed); |
64 | |
65 | lan966x_port_config_up(port); |
66 | } |
67 | |
68 | static void lan966x_phylink_mac_link_down(struct phylink_config *config, |
69 | unsigned int mode, |
70 | phy_interface_t interface) |
71 | { |
72 | struct lan966x_port *port = netdev_priv(to_net_dev(config->dev)); |
73 | struct lan966x *lan966x = port->lan966x; |
74 | |
75 | lan966x_port_config_down(port); |
76 | |
77 | /* Take PCS out of reset */ |
78 | lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) | |
79 | DEV_CLOCK_CFG_PCS_TX_RST_SET(0), |
80 | DEV_CLOCK_CFG_PCS_RX_RST | |
81 | DEV_CLOCK_CFG_PCS_TX_RST, |
82 | lan966x, DEV_CLOCK_CFG(port->chip_port)); |
83 | } |
84 | |
85 | static struct lan966x_port *lan966x_pcs_to_port(struct phylink_pcs *pcs) |
86 | { |
87 | return container_of(pcs, struct lan966x_port, phylink_pcs); |
88 | } |
89 | |
90 | static void lan966x_pcs_get_state(struct phylink_pcs *pcs, |
91 | struct phylink_link_state *state) |
92 | { |
93 | struct lan966x_port *port = lan966x_pcs_to_port(pcs); |
94 | |
95 | lan966x_port_status_get(port, state); |
96 | } |
97 | |
98 | static int lan966x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, |
99 | phy_interface_t interface, |
100 | const unsigned long *advertising, |
101 | bool permit_pause_to_mac) |
102 | { |
103 | struct lan966x_port *port = lan966x_pcs_to_port(pcs); |
104 | struct lan966x_port_config config; |
105 | int ret; |
106 | |
107 | config = port->config; |
108 | config.portmode = interface; |
109 | config.inband = neg_mode & PHYLINK_PCS_NEG_INBAND; |
110 | config.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; |
111 | config.advertising = advertising; |
112 | |
113 | ret = lan966x_port_pcs_set(port, config: &config); |
114 | if (ret) |
115 | netdev_err(dev: port->dev, format: "port PCS config failed: %d\n" , ret); |
116 | |
117 | return ret; |
118 | } |
119 | |
120 | static void lan966x_pcs_aneg_restart(struct phylink_pcs *pcs) |
121 | { |
122 | /* Currently not used */ |
123 | } |
124 | |
125 | const struct phylink_mac_ops lan966x_phylink_mac_ops = { |
126 | .mac_select_pcs = lan966x_phylink_mac_select, |
127 | .mac_config = lan966x_phylink_mac_config, |
128 | .mac_prepare = lan966x_phylink_mac_prepare, |
129 | .mac_link_down = lan966x_phylink_mac_link_down, |
130 | .mac_link_up = lan966x_phylink_mac_link_up, |
131 | }; |
132 | |
133 | const struct phylink_pcs_ops lan966x_phylink_pcs_ops = { |
134 | .pcs_get_state = lan966x_pcs_get_state, |
135 | .pcs_config = lan966x_pcs_config, |
136 | .pcs_an_restart = lan966x_pcs_aneg_restart, |
137 | }; |
138 | |