#include <linux/linkmode.h>
#include <linux/of.h>
#include <linux/phy_port.h>
#include "phy-caps.h"
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);
void phy_port_destroy(struct phy_port *port)
{
kfree(port);
}
EXPORT_SYMBOL_GPL(phy_port_destroy);
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:
case 2:
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);
void phy_port_update_supported(struct phy_port *port)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = {0};
unsigned long mode;
int i;
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 (linkmode_empty(port->supported))
linkmode_copy(port->supported, supported);
else
linkmode_and(port->supported, supported, port->supported);
if (port->is_mii && !port->mediums) {
unsigned long interface, link_caps = 0;
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);
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);
}
int phy_port_restrict_mediums(struct phy_port *port, unsigned long mediums)
{
if (!(port->mediums & mediums))
return -EINVAL;
port->mediums &= mediums;
phy_port_filter_supported(port);
return 0;
}
EXPORT_SYMBOL_GPL(phy_port_restrict_mediums);
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);