1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Framework to drive Ethernet ports 3 * 4 * Copyright (c) 2024 Maxime Chevallier <maxime.chevallier@bootlin.com> 5 */ 6 7 #include <linux/linkmode.h> 8 #include <linux/of.h> 9 #include <linux/phy_port.h> 10 11 #include "phy-caps.h" 12 13 /** 14 * phy_port_alloc() - Allocate a new phy_port 15 * 16 * Returns: a newly allocated struct phy_port, or NULL. 17 */ 18 struct phy_port *phy_port_alloc(void) 19 { 20 struct phy_port *port; 21 22 port = kzalloc(sizeof(*port), GFP_KERNEL); 23 if (!port) 24 return NULL; 25 26 linkmode_zero(port->supported); 27 INIT_LIST_HEAD(&port->head); 28 29 return port; 30 } 31 EXPORT_SYMBOL_GPL(phy_port_alloc); 32 33 /** 34 * phy_port_destroy() - Free a struct phy_port 35 * @port: The port to destroy 36 */ 37 void phy_port_destroy(struct phy_port *port) 38 { 39 kfree(port); 40 } 41 EXPORT_SYMBOL_GPL(phy_port_destroy); 42 43 /** 44 * phy_of_parse_port() - Create a phy_port from a firmware representation 45 * @dn: device_node representation of the port, following the 46 * ethernet-connector.yaml binding 47 * 48 * Returns: a newly allocated and initialized phy_port pointer, or an ERR_PTR. 49 */ 50 struct phy_port *phy_of_parse_port(struct device_node *dn) 51 { 52 struct fwnode_handle *fwnode = of_fwnode_handle(dn); 53 enum ethtool_link_medium medium; 54 struct phy_port *port; 55 const char *med_str; 56 u32 pairs = 0, mediums = 0; 57 int ret; 58 59 ret = fwnode_property_read_string(fwnode, "media", &med_str); 60 if (ret) 61 return ERR_PTR(ret); 62 63 medium = ethtool_str_to_medium(med_str); 64 if (medium == ETHTOOL_LINK_MEDIUM_NONE) 65 return ERR_PTR(-EINVAL); 66 67 if (medium == ETHTOOL_LINK_MEDIUM_BASET) { 68 ret = fwnode_property_read_u32(fwnode, "pairs", &pairs); 69 if (ret) 70 return ERR_PTR(ret); 71 72 switch (pairs) { 73 case 1: /* BaseT1 */ 74 case 2: /* 100BaseTX */ 75 case 4: 76 break; 77 default: 78 pr_err("%u is not a valid number of pairs\n", pairs); 79 return ERR_PTR(-EINVAL); 80 } 81 } 82 83 if (pairs && medium != ETHTOOL_LINK_MEDIUM_BASET) { 84 pr_err("pairs property is only compatible with BaseT medium\n"); 85 return ERR_PTR(-EINVAL); 86 } 87 88 mediums |= BIT(medium); 89 90 if (!mediums) 91 return ERR_PTR(-EINVAL); 92 93 port = phy_port_alloc(); 94 if (!port) 95 return ERR_PTR(-ENOMEM); 96 97 port->pairs = pairs; 98 port->mediums = mediums; 99 100 return port; 101 } 102 EXPORT_SYMBOL_GPL(phy_of_parse_port); 103 104 /** 105 * phy_port_update_supported() - Setup the port->supported field 106 * @port: the port to update 107 * 108 * Once the port's medium list and number of pairs has been configured based 109 * on firmware, straps and vendor-specific properties, this function may be 110 * called to update the port's supported linkmodes list. 111 * 112 * Any mode that was manually set in the port's supported list remains set. 113 */ 114 void phy_port_update_supported(struct phy_port *port) 115 { 116 __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0 }; 117 unsigned long mode; 118 int i; 119 120 for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST) { 121 linkmode_zero(supported); 122 phy_caps_medium_get_supported(supported, i, port->pairs); 123 linkmode_or(port->supported, port->supported, supported); 124 } 125 126 /* If there's no pairs specified, we grab the default number of 127 * pairs as the max of the default pairs for each linkmode 128 */ 129 if (!port->pairs) 130 for_each_set_bit(mode, port->supported, 131 __ETHTOOL_LINK_MODE_MASK_NBITS) 132 port->pairs = max_t(int, port->pairs, 133 ethtool_linkmode_n_pairs(mode)); 134 135 /* Serdes ports supported through SFP may not have any medium set, 136 * as they will output PHY_INTERFACE_MODE_XXX modes. In that case, derive 137 * the supported list based on these interfaces 138 */ 139 if (port->is_mii && !port->mediums) { 140 unsigned long interface, link_caps = 0; 141 142 /* Get each interface's caps */ 143 for_each_set_bit(interface, port->interfaces, 144 PHY_INTERFACE_MODE_MAX) 145 link_caps |= phy_caps_from_interface(interface); 146 147 phy_caps_linkmodes(link_caps, port->supported); 148 } 149 } 150 EXPORT_SYMBOL_GPL(phy_port_update_supported); 151 152 /** 153 * phy_port_filter_supported() - Make sure that port->supported match port->mediums 154 * @port: The port to filter 155 * 156 * After updating a port's mediums to a more restricted subset, this helper will 157 * make sure that port->supported only contains linkmodes that are compatible 158 * with port->mediums. 159 */ 160 static void phy_port_filter_supported(struct phy_port *port) 161 { 162 __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0 }; 163 int i; 164 165 for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST) 166 phy_caps_medium_get_supported(supported, i, port->pairs); 167 168 linkmode_and(port->supported, port->supported, supported); 169 } 170 171 /** 172 * phy_port_restrict_mediums - Mask away some of the port's supported mediums 173 * @port: The port to act upon 174 * @mediums: A mask of mediums to support on the port 175 * 176 * This helper allows removing some mediums from a port's list of supported 177 * mediums, which occurs once we have enough information about the port to 178 * know its nature. 179 * 180 * Returns: 0 if the change was donne correctly, a negative value otherwise. 181 */ 182 int phy_port_restrict_mediums(struct phy_port *port, unsigned long mediums) 183 { 184 /* We forbid ending-up with a port with empty mediums */ 185 if (!(port->mediums & mediums)) 186 return -EINVAL; 187 188 port->mediums &= mediums; 189 190 phy_port_filter_supported(port); 191 192 return 0; 193 } 194 EXPORT_SYMBOL_GPL(phy_port_restrict_mediums); 195 196 /** 197 * phy_port_get_type() - get the PORT_* attribute for that port. 198 * @port: The port we want the information from 199 * 200 * Returns: A PORT_XXX value. 201 */ 202 int phy_port_get_type(struct phy_port *port) 203 { 204 if (port->mediums & BIT(ETHTOOL_LINK_MEDIUM_BASET)) 205 return PORT_TP; 206 207 if (phy_port_is_fiber(port)) 208 return PORT_FIBRE; 209 210 return PORT_OTHER; 211 } 212 EXPORT_SYMBOL_GPL(phy_port_get_type); 213