linux/drivers/net/phy/phy_port.c
Linus Torvalds bf4afc53b7 Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
This was done entirely with mindless brute force, using

    git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
        xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'

to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.

Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.

For the same reason the 'flex' versions will be done as a separate
conversion.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-02-21 17:09:51 -08:00

218 lines
5.7 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/* Framework to drive Ethernet ports
*
* Copyright (c) 2024 Maxime Chevallier <maxime.chevallier@bootlin.com>
*/
#include <linux/linkmode.h>
#include <linux/of.h>
#include <linux/phy_port.h>
#include "phy-caps.h"
/**
* phy_port_alloc() - Allocate a new phy_port
*
* Returns: a newly allocated struct phy_port, or NULL.
*/
struct phy_port *phy_port_alloc(void)
{
struct phy_port *port;
port = kzalloc_obj(*port);
if (!port)
return NULL;
linkmode_zero(port->supported);
INIT_LIST_HEAD(&port->head);
return port;
}
EXPORT_SYMBOL_GPL(phy_port_alloc);
/**
* phy_port_destroy() - Free a struct phy_port
* @port: The port to destroy
*/
void phy_port_destroy(struct phy_port *port)
{
kfree(port);
}
EXPORT_SYMBOL_GPL(phy_port_destroy);
/**
* phy_of_parse_port() - Create a phy_port from a firmware representation
* @dn: device_node representation of the port, following the
* ethernet-connector.yaml binding
*
* Returns: a newly allocated and initialized phy_port pointer, or an ERR_PTR.
*/
struct phy_port *phy_of_parse_port(struct device_node *dn)
{
struct fwnode_handle *fwnode = of_fwnode_handle(dn);
enum ethtool_link_medium medium;
struct phy_port *port;
const char *med_str;
u32 pairs = 0;
int ret;
ret = fwnode_property_read_string(fwnode, "media", &med_str);
if (ret)
return ERR_PTR(ret);
medium = ethtool_str_to_medium(med_str);
if (medium == ETHTOOL_LINK_MEDIUM_NONE)
return ERR_PTR(-EINVAL);
if (medium == ETHTOOL_LINK_MEDIUM_BASET) {
ret = fwnode_property_read_u32(fwnode, "pairs", &pairs);
if (ret)
return ERR_PTR(ret);
switch (pairs) {
case 1: /* BaseT1 */
case 2: /* 100BaseTX */
case 4:
break;
default:
pr_err("%u is not a valid number of pairs\n", pairs);
return ERR_PTR(-EINVAL);
}
}
if (pairs && medium != ETHTOOL_LINK_MEDIUM_BASET) {
pr_err("pairs property is only compatible with BaseT medium\n");
return ERR_PTR(-EINVAL);
}
port = phy_port_alloc();
if (!port)
return ERR_PTR(-ENOMEM);
port->pairs = pairs;
port->mediums = BIT(medium);
return port;
}
EXPORT_SYMBOL_GPL(phy_of_parse_port);
/**
* phy_port_update_supported() - Setup the port->supported field
* @port: the port to update
*
* Once the port's medium list and number of pairs has been configured based
* on firmware, straps and vendor-specific properties, this function may be
* called to update the port's supported linkmodes list.
*
* Any mode that was manually set in the port's supported list remains set.
*/
void phy_port_update_supported(struct phy_port *port)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = {0};
unsigned long mode;
int i;
/* If there's no pairs specified, we grab the default number of
* pairs as the max of the default pairs for each linkmode
*/
if (!port->pairs)
for_each_set_bit(mode, port->supported,
__ETHTOOL_LINK_MODE_MASK_NBITS)
port->pairs = max_t(int, port->pairs,
ethtool_linkmode_n_pairs(mode));
for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST) {
__ETHTOOL_DECLARE_LINK_MODE_MASK(med_supported) = {0};
phy_caps_medium_get_supported(med_supported, i, port->pairs);
linkmode_or(supported, supported, med_supported);
}
/* If port->supported is already populated, filter it out with the
* medium/pair support. Otherwise, let's just use this medium-based
* support as the port's supported list.
*/
if (linkmode_empty(port->supported))
linkmode_copy(port->supported, supported);
else
linkmode_and(port->supported, supported, port->supported);
/* Serdes ports supported through SFP may not have any medium set,
* as they will output PHY_INTERFACE_MODE_XXX modes. In that case, derive
* the supported list based on these interfaces
*/
if (port->is_mii && !port->mediums) {
unsigned long interface, link_caps = 0;
/* Get each interface's caps */
for_each_set_bit(interface, port->interfaces,
PHY_INTERFACE_MODE_MAX)
link_caps |= phy_caps_from_interface(interface);
phy_caps_linkmodes(link_caps, port->supported);
}
}
EXPORT_SYMBOL_GPL(phy_port_update_supported);
/**
* phy_port_filter_supported() - Make sure that port->supported match port->mediums
* @port: The port to filter
*
* After updating a port's mediums to a more restricted subset, this helper will
* make sure that port->supported only contains linkmodes that are compatible
* with port->mediums.
*/
static void phy_port_filter_supported(struct phy_port *port)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0 };
int i;
for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST)
phy_caps_medium_get_supported(supported, i, port->pairs);
linkmode_and(port->supported, port->supported, supported);
}
/**
* phy_port_restrict_mediums - Mask away some of the port's supported mediums
* @port: The port to act upon
* @mediums: A mask of mediums to support on the port
*
* This helper allows removing some mediums from a port's list of supported
* mediums, which occurs once we have enough information about the port to
* know its nature.
*
* Returns: 0 if the change was donne correctly, a negative value otherwise.
*/
int phy_port_restrict_mediums(struct phy_port *port, unsigned long mediums)
{
/* We forbid ending-up with a port with empty mediums */
if (!(port->mediums & mediums))
return -EINVAL;
port->mediums &= mediums;
phy_port_filter_supported(port);
return 0;
}
EXPORT_SYMBOL_GPL(phy_port_restrict_mediums);
/**
* phy_port_get_type() - get the PORT_* attribute for that port.
* @port: The port we want the information from
*
* Returns: A PORT_XXX value.
*/
int phy_port_get_type(struct phy_port *port)
{
if (port->mediums & BIT(ETHTOOL_LINK_MEDIUM_BASET))
return PORT_TP;
if (phy_port_is_fiber(port))
return PORT_FIBRE;
return PORT_OTHER;
}
EXPORT_SYMBOL_GPL(phy_port_get_type);