static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
{
int i;
-@@ -2958,6 +2985,124 @@ bool airoha_is_valid_gdm_port(struct air
+@@ -2958,6 +2985,119 @@ bool airoha_is_valid_gdm_port(struct air
return false;
}
+ .mac_link_down = airoha_mac_link_down,
+};
+
++static int airoha_fill_available_pcs(struct phylink_config *config,
++ struct phylink_pcs **available_pcs,
++ unsigned int num_available_pcs)
++{
++ struct device *dev = config->dev;
++
++ return fwnode_phylink_pcs_parse(dev_fwnode(dev), available_pcs,
++ &num_available_pcs);
++}
++
+static int airoha_setup_phylink(struct net_device *dev)
+{
+ struct airoha_gdm_port *port = netdev_priv(dev);
+ struct device_node *np = dev->dev.of_node;
-+ struct phylink_pcs **available_pcs;
+ phy_interface_t phy_mode;
+ struct phylink *phylink;
-+ unsigned int num_pcs;
+ int err;
+
+ err = of_get_phy_mode(np, &phy_mode);
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD |
+ MAC_5000FD | MAC_10000FD;
+
-+ err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL, &num_pcs);
++ err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL,
++ &port->phylink_config.num_available_pcs);
+ if (err)
+ return err;
+
-+ available_pcs = kcalloc(num_pcs, sizeof(*available_pcs), GFP_KERNEL);
-+ if (!available_pcs)
-+ return -ENOMEM;
-+
-+ err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), available_pcs,
-+ &num_pcs);
-+ if (err)
-+ goto out;
-+
-+ port->phylink_config.available_pcs = available_pcs;
-+ port->phylink_config.num_available_pcs = num_pcs;
++ port->phylink_config.fill_available_pcs = airoha_fill_available_pcs;
+
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ port->phylink_config.supported_interfaces);
+ phylink = phylink_create(&port->phylink_config,
+ of_fwnode_handle(np),
+ phy_mode, &airoha_phylink_ops);
-+ if (IS_ERR(phylink)) {
-+ err = PTR_ERR(phylink);
-+ goto out;
-+ }
++ if (IS_ERR(phylink))
++ return PTR_ERR(phylink);
+
+ port->phylink = phylink;
-+out:
-+ kfree(available_pcs);
+
+ return err;
+}
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
{
-@@ -3031,6 +3176,12 @@ static int airoha_alloc_gdm_port(struct
+@@ -3031,6 +3171,12 @@ static int airoha_alloc_gdm_port(struct
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
return airoha_metadata_dst_alloc(port);
}
-@@ -3158,8 +3309,11 @@ error_napi_stop:
+@@ -3158,8 +3304,11 @@ error_napi_stop:
if (!port)
continue;
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
-@@ -3184,6 +3338,8 @@ static void airoha_remove(struct platfor
+@@ -3184,6 +3333,8 @@ static void airoha_remove(struct platfor
if (!port)
continue;
static void airoha_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)
-@@ -3105,6 +3112,7 @@ out:
+@@ -3100,6 +3107,7 @@ static int airoha_setup_phylink(struct n
return err;
}
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
-@@ -3179,11 +3187,13 @@ static int airoha_alloc_gdm_port(struct
+@@ -3174,11 +3182,13 @@ static int airoha_alloc_gdm_port(struct
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
return airoha_metadata_dst_alloc(port);
}
-@@ -3313,8 +3323,10 @@ error_napi_stop:
+@@ -3308,8 +3318,10 @@ error_napi_stop:
continue;
if (port->dev->reg_state == NETREG_REGISTERED) {
unregister_netdev(port->dev);
}
airoha_metadata_dst_free(port);
-@@ -3341,8 +3353,10 @@ static void airoha_remove(struct platfor
+@@ -3336,8 +3348,10 @@ static void airoha_remove(struct platfor
if (!port)
continue;
+ */
+ } else if (test_bit(state->interface, pl->config->pcs_interfaces)) {
+ bool pcs_found = false;
-+
+
+- pcs_changed = pl->pcs != pcs;
+ list_for_each_entry(pcs, &pl->pcs_list, list) {
+ if (!phylink_validate_pcs_interface(pcs,
+ state->interface)) {
+ phylink_err(pl,
+ "couldn't find a PCS for %s\n",
+ phy_modes(state->interface));
-
-- pcs_changed = pl->pcs != pcs;
++
+ pl->major_config_failed = true;
+ return;
+ }
pl->pcs = pcs;
}
-@@ -1931,8 +2006,9 @@ struct phylink *phylink_create(struct ph
+@@ -1909,6 +1984,44 @@ int phylink_set_fixed_link(struct phylin
+ }
+ EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+
++static int phylink_fill_available_pcs(struct phylink *pl,
++ struct phylink_config *config)
++{
++ struct phylink_pcs **pcss;
++ int i, ret;
++
++ if (!config->num_available_pcs)
++ return 0;
++
++ if (!config->fill_available_pcs) {
++ dev_err(config->dev,
++ "phylink: error: num_available_pcs defined but no fill_available_pcs\n");
++ return -EINVAL;
++ }
++
++ pcss = kcalloc(config->num_available_pcs, sizeof(*pcss), GFP_KERNEL);
++ if (!pcss)
++ return -ENOMEM;
++
++ ret = config->fill_available_pcs(config, pcss, config->num_available_pcs);
++ if (ret)
++ goto out;
++
++ for (i = 0; i < config->num_available_pcs; i++) {
++ struct phylink_pcs *pcs = pcss[i];
++
++ if (!pcs)
++ continue;
++
++ list_add(&pcs->list, &pl->pcs_list);
++ }
++
++out:
++ kfree(pcss);
++
++ return ret;
++}
++
+ /**
+ * phylink_create() - create a phylink instance
+ * @config: a pointer to the target &struct phylink_config
+@@ -1931,6 +2044,7 @@ struct phylink *phylink_create(struct ph
const struct phylink_mac_ops *mac_ops)
{
bool using_mac_select_pcs = false;
+ struct phylink_pcs *pcs;
struct phylink *pl;
-- int ret;
-+ int i, ret;
+ int ret;
- /* Validate the supplied configuration */
- if (phy_interface_empty(config->supported_interfaces)) {
-@@ -1952,9 +2028,21 @@ struct phylink *phylink_create(struct ph
+@@ -1952,9 +2066,21 @@ struct phylink *phylink_create(struct ph
mutex_init(&pl->state_mutex);
INIT_WORK(&pl->resolve, phylink_resolve);
+ INIT_LIST_HEAD(&pl->pcs_list);
+
+ /* Fill the PCS list with available PCS from phylink config */
-+ for (i = 0; i < config->num_available_pcs; i++) {
-+ pcs = config->available_pcs[i];
-+
-+ list_add(&pcs->list, &pl->pcs_list);
++ ret = phylink_fill_available_pcs(pl, config);
++ if (ret) {
++ kfree(pl);
++ return ERR_PTR(ret);
+ }
phy_interface_copy(pl->supported_interfaces,
pl->config = config;
if (config->type == PHYLINK_NETDEV) {
-@@ -2024,10 +2112,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
+@@ -2024,10 +2150,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
*/
void phylink_destroy(struct phylink *pl)
{
cancel_work_sync(&pl->resolve);
kfree(pl);
}
-@@ -2472,6 +2566,7 @@ static irqreturn_t phylink_link_handler(
+@@ -2472,6 +2604,7 @@ static irqreturn_t phylink_link_handler(
*/
void phylink_start(struct phylink *pl)
{
bool poll = false;
ASSERT_RTNL();
-@@ -2498,6 +2593,10 @@ void phylink_start(struct phylink *pl)
+@@ -2498,6 +2631,10 @@ void phylink_start(struct phylink *pl)
pl->pcs_state = PCS_STATE_STARTED;
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
-@@ -2542,6 +2641,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
+@@ -2542,6 +2679,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
*/
void phylink_stop(struct phylink *pl)
{
ASSERT_RTNL();
if (pl->sfp_bus)
-@@ -2559,6 +2660,14 @@ void phylink_stop(struct phylink *pl)
+@@ -2559,6 +2698,14 @@ void phylink_stop(struct phylink *pl)
pl->pcs_state = PCS_STATE_DOWN;
phylink_pcs_disable(pl->pcs);
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
-@@ -147,7 +147,11 @@ enum phylink_op_type {
+@@ -10,6 +10,7 @@ struct ethtool_cmd;
+ struct fwnode_handle;
+ struct net_device;
+ struct phylink;
++struct phylink_pcs;
+
+ enum {
+ MLO_PAUSE_NONE,
+@@ -147,7 +148,13 @@ enum phylink_op_type {
* if MAC link is at %MLO_AN_FIXED mode.
* @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
* are supported by the MAC/PCS.
+ * @pcs_interfaces: bitmap describing for which PHY_INTERFACE_MODE_xxx a
+ * dedicated PCS is required.
* @mac_capabilities: MAC pause/speed/duplex capabilities.
-+ * @available_pcs: array of available phylink_pcs PCS
+ * @num_available_pcs: num of available phylink_pcs PCS
++ * @fill_available_pcs: callback to fill the available PCS in the passed
++ * array struct of phylink_pcs PCS available_pcs up to
++ * num_available_pcs.
*/
struct phylink_config {
struct device *dev;
-@@ -159,7 +163,11 @@ struct phylink_config {
+@@ -159,7 +166,13 @@ struct phylink_config {
void (*get_fixed_state)(struct phylink_config *config,
struct phylink_link_state *state);
DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
+ DECLARE_PHY_INTERFACE_MASK(pcs_interfaces);
unsigned long mac_capabilities;
+
-+ struct phylink_pcs **available_pcs;
+ unsigned int num_available_pcs;
++ int (*fill_available_pcs)(struct phylink_config *config,
++ struct phylink_pcs **available_pcs,
++ unsigned int num_available_pcs);
};
void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
-@@ -417,6 +425,9 @@ struct phylink_pcs {
+@@ -417,6 +430,9 @@ struct phylink_pcs {
bool neg_mode;
bool poll;
bool rxc_always_on;
/* If configuration of the interface failed, force the link down
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
-@@ -632,6 +632,8 @@ void phylink_disconnect_phy(struct phyli
+@@ -637,6 +637,8 @@ void phylink_disconnect_phy(struct phyli
int phylink_set_fixed_link(struct phylink *,
const struct phylink_link_state *);
/* What interface are supported by the current link.
* Can change on removal or addition of new PCS.
-@@ -2039,6 +2041,51 @@ int phylink_set_fixed_link(struct phylin
+@@ -2077,6 +2079,51 @@ out:
+ return ret;
}
- EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+static int pcs_provider_notify(struct notifier_block *self,
+ unsigned long val, void *data)
/**
* phylink_create() - create a phylink instance
* @config: a pointer to the target &struct phylink_config
-@@ -2099,6 +2146,11 @@ struct phylink *phylink_create(struct ph
+@@ -2137,6 +2184,11 @@ struct phylink *phylink_create(struct ph
pl->supported_interfaces,
pcs->supported_interfaces);
phylink_info(pl, "Link is Down\n");
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
-@@ -443,6 +443,7 @@ struct phylink_pcs {
+@@ -448,6 +448,7 @@ struct phylink_pcs {
* @pcs_an_restart: restart 802.3z BaseX autonegotiation.
* @pcs_link_up: program the PCS for the resolved link configuration
* (where necessary).
* @pcs_pre_init: configure PCS components necessary for MAC hardware
* initialization e.g. RX clock for stmmac.
*/
-@@ -466,6 +467,7 @@ struct phylink_pcs_ops {
+@@ -471,6 +472,7 @@ struct phylink_pcs_ops {
void (*pcs_an_restart)(struct phylink_pcs *pcs);
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex);
-From d134e22b540226a7404cabb88c86a54857486b4f Mon Sep 17 00:00:00 2001
+From 2606833af0ba47d4740b004afbcae2ad10f2efb5 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Mon, 31 Mar 2025 16:03:26 +0200
-Subject: [PATCH 2/7] net: phylink: introduce internal phylink PCS handling
+Subject: [PATCH 1/9] net: phylink: introduce internal phylink PCS handling
Introduce internal handling of PCS for phylink. This is an alternative
-to .mac_select_pcs that moves the selection logic of the PCS entirely to
-phylink with the usage of supported_interface value in the PCS struct.
+way to .mac_select_pcs that moves the selection logic of the PCS entirely
+to phylink with the usage of the supported_interface value in the PCS
+struct.
-MAC should now provide an array of available PCS in phylink_config in
-.available_pcs and fill the .num_available_pcs with the number of
-elements in the array. MAC should also define a new bitmap,
+MAC should now provide a callback to fill the available PCS in
+phylink_config in .fill_available_pcs and fill the .num_available_pcs with
+the number of elements in the array. MAC should also define a new bitmap,
pcs_interfaces, in phylink_config to define for what interface mode a
dedicated PCS is required.
-On phylink_create() this array is parsed and a linked list of PCS is
-created based on the PCS passed in phylink_config.
+On phylink_create(), an array of PCS pointer is allocated of size
+.num_available_pcs from phylink_config and .fill_available_pcs from
+phylink_config is called passing as args the just allocated array and
+the numer of available element in it.
+
+MAC will fill this passed array with all the available PCS.
+
+This array is then parsed and a linked list of PCS is created based on
+the allocated PCS array filled by MAC via .fill_available_pcs().
Also the supported_interface value in phylink struct is updated with the
new supported_interface from the provided PCS.
phylink instance. This is done by setting the phylink value in
phylink_pcs struct to NULL.
-On phylink_stop(), every PCS in phylink PCS list is removed from the
-list.
-
phylink_validate_mac_and_pcs(), phylink_major_config() and
phylink_inband_caps() are updated to support this new implementation
with the PCS list stored in phylink.
for every PCS in the phylink PCS available list and find one that supports
the passed interface.
-phylink_validate_pcs_interface() apply the same logic of .mac_select_pcs
+phylink_validate_pcs_interface() applies the same logic of .mac_select_pcs
where if a supported_interface value is not set for the PCS struct, then
it's assumed every interface is supported.
-It's required for a MAC that implement either a .mac_select_pcs or make
-use of the PCS list implementation. Implementing both will result in a fail
+A MAC is required to implement either a .mac_select_pcs or make use of
+the PCS list implementation. Implementing both will result in a fail
on MAC/PCS validation.
+A MAC defining .num_available_pcs in phylink_config MUST also define a
+.fill_available_pcs or phylink_create() will fail with an negative error.
+
phylink value in phylink_pcs struct with this implementation is used to
track from PCS side when it's attached to a phylink instance. PCS driver
will make use of this information to correctly detach from a phylink
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
- drivers/net/phy/phylink.c | 149 +++++++++++++++++++++++++++++++++-----
- include/linux/phylink.h | 11 +++
- 2 files changed, 141 insertions(+), 19 deletions(-)
+ drivers/net/phy/phylink.c | 185 ++++++++++++++++++++++++++++++++++----
+ include/linux/phylink.h | 16 ++++
+ 2 files changed, 183 insertions(+), 18 deletions(-)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
+ */
+ } else if (test_bit(state->interface, pl->config->pcs_interfaces)) {
+ bool pcs_found = false;
-+
+
+- pcs_changed = pl->pcs != pcs;
+ list_for_each_entry(pcs, &pl->pcs_list, list) {
+ if (!phylink_validate_pcs_interface(pcs,
+ state->interface)) {
+ phylink_err(pl,
+ "couldn't find a PCS for %s\n",
+ phy_modes(state->interface));
-
-- pcs_changed = pl->pcs != pcs;
++
+ pl->major_config_failed = true;
+ return;
+ }
pl->pcs = pcs;
}
-@@ -1844,8 +1921,9 @@ struct phylink *phylink_create(struct ph
+@@ -1823,6 +1900,44 @@ int phylink_set_fixed_link(struct phylin
+ }
+ EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+
++static int phylink_fill_available_pcs(struct phylink *pl,
++ struct phylink_config *config)
++{
++ struct phylink_pcs **pcss;
++ int i, ret;
++
++ if (!config->num_available_pcs)
++ return 0;
++
++ if (!config->fill_available_pcs) {
++ dev_err(config->dev,
++ "phylink: error: num_available_pcs defined but no fill_available_pcs\n");
++ return -EINVAL;
++ }
++
++ pcss = kcalloc(config->num_available_pcs, sizeof(*pcss), GFP_KERNEL);
++ if (!pcss)
++ return -ENOMEM;
++
++ ret = config->fill_available_pcs(config, pcss, config->num_available_pcs);
++ if (ret)
++ goto out;
++
++ for (i = 0; i < config->num_available_pcs; i++) {
++ struct phylink_pcs *pcs = pcss[i];
++
++ if (!pcs)
++ continue;
++
++ list_add(&pcs->list, &pl->pcs_list);
++ }
++
++out:
++ kfree(pcss);
++
++ return ret;
++}
++
+ /**
+ * phylink_create() - create a phylink instance
+ * @config: a pointer to the target &struct phylink_config
+@@ -1844,6 +1959,7 @@ struct phylink *phylink_create(struct ph
phy_interface_t iface,
const struct phylink_mac_ops *mac_ops)
{
+ struct phylink_pcs *pcs;
struct phylink *pl;
-- int ret;
-+ int i, ret;
+ int ret;
- /* Validate the supplied configuration */
- if (phy_interface_empty(config->supported_interfaces)) {
-@@ -1861,9 +1939,21 @@ struct phylink *phylink_create(struct ph
+@@ -1861,9 +1977,21 @@ struct phylink *phylink_create(struct ph
mutex_init(&pl->phydev_mutex);
mutex_init(&pl->state_mutex);
INIT_WORK(&pl->resolve, phylink_resolve);
+ INIT_LIST_HEAD(&pl->pcs_list);
+
+ /* Fill the PCS list with available PCS from phylink config */
-+ for (i = 0; i < config->num_available_pcs; i++) {
-+ pcs = config->available_pcs[i];
-+
-+ list_add(&pcs->list, &pl->pcs_list);
++ ret = phylink_fill_available_pcs(pl, config);
++ if (ret) {
++ kfree(pl);
++ return ERR_PTR(ret);
+ }
phy_interface_copy(pl->supported_interfaces,
pl->config = config;
if (config->type == PHYLINK_NETDEV) {
-@@ -1942,10 +2032,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
+@@ -1942,10 +2070,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
*/
void phylink_destroy(struct phylink *pl)
{
cancel_work_sync(&pl->resolve);
kfree(pl);
}
-@@ -2447,6 +2543,7 @@ static irqreturn_t phylink_link_handler(
+@@ -2447,6 +2581,7 @@ static irqreturn_t phylink_link_handler(
*/
void phylink_start(struct phylink *pl)
{
bool poll = false;
ASSERT_RTNL();
-@@ -2473,6 +2570,10 @@ void phylink_start(struct phylink *pl)
+@@ -2473,6 +2608,10 @@ void phylink_start(struct phylink *pl)
pl->pcs_state = PCS_STATE_STARTED;
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
-@@ -2517,6 +2618,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
+@@ -2517,6 +2656,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
*/
void phylink_stop(struct phylink *pl)
{
ASSERT_RTNL();
if (pl->sfp_bus)
-@@ -2534,6 +2637,14 @@ void phylink_stop(struct phylink *pl)
+@@ -2534,6 +2675,14 @@ void phylink_stop(struct phylink *pl)
pl->pcs_state = PCS_STATE_DOWN;
phylink_pcs_disable(pl->pcs);
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
-@@ -150,12 +150,16 @@ enum phylink_op_type {
+@@ -12,6 +12,7 @@ struct ethtool_cmd;
+ struct fwnode_handle;
+ struct net_device;
+ struct phylink;
++struct phylink_pcs;
+
+ enum {
+ MLO_PAUSE_NONE,
+@@ -150,12 +151,18 @@ enum phylink_op_type {
* if MAC link is at %MLO_AN_FIXED mode.
* @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
* are supported by the MAC/PCS.
* @lpi_capabilities: MAC speeds which can support LPI signalling
* @lpi_timer_default: Default EEE LPI timer setting.
* @eee_enabled_default: If set, EEE will be enabled by phylink at creation time
-+ * @available_pcs: array of available phylink_pcs PCS
+ * @num_available_pcs: num of available phylink_pcs PCS
++ * @fill_available_pcs: callback to fill the available PCS in the passed
++ * array struct of phylink_pcs PCS available_pcs up to
++ * num_available_pcs.
*/
struct phylink_config {
struct device *dev;
-@@ -168,11 +172,15 @@ struct phylink_config {
+@@ -168,11 +175,17 @@ struct phylink_config {
void (*get_fixed_state)(struct phylink_config *config,
struct phylink_link_state *state);
DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
u32 lpi_timer_default;
bool eee_enabled_default;
+
-+ struct phylink_pcs **available_pcs;
+ unsigned int num_available_pcs;
++ int (*fill_available_pcs)(struct phylink_config *config,
++ struct phylink_pcs **available_pcs,
++ unsigned int num_available_pcs);
};
void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
-@@ -468,6 +476,9 @@ struct phylink_pcs {
+@@ -468,6 +481,9 @@ struct phylink_pcs {
struct phylink *phylink;
bool poll;
bool rxc_always_on;
/* If configuration of the interface failed, force the link down
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
-@@ -717,6 +717,8 @@ void phylink_disconnect_phy(struct phyli
+@@ -722,6 +722,8 @@ void phylink_disconnect_phy(struct phyli
int phylink_set_fixed_link(struct phylink *,
const struct phylink_link_state *);
/* What interface are supported by the current link.
* Can change on removal or addition of new PCS.
-@@ -1955,6 +1957,51 @@ int phylink_set_fixed_link(struct phylin
+@@ -1993,6 +1995,51 @@ out:
+ return ret;
}
- EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+static int pcs_provider_notify(struct notifier_block *self,
+ unsigned long val, void *data)
/**
* phylink_create() - create a phylink instance
* @config: a pointer to the target &struct phylink_config
-@@ -2010,6 +2057,11 @@ struct phylink *phylink_create(struct ph
+@@ -2048,6 +2095,11 @@ struct phylink *phylink_create(struct ph
pl->supported_interfaces,
pcs->supported_interfaces);
phylink_info(pl, "Link is Down\n");
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
-@@ -494,6 +494,7 @@ struct phylink_pcs {
+@@ -499,6 +499,7 @@ struct phylink_pcs {
* @pcs_an_restart: restart 802.3z BaseX autonegotiation.
* @pcs_link_up: program the PCS for the resolved link configuration
* (where necessary).
* @pcs_disable_eee: optional notification to PCS that EEE has been disabled
* at the MAC.
* @pcs_enable_eee: optional notification to PCS that EEE will be enabled at
-@@ -521,6 +522,7 @@ struct phylink_pcs_ops {
+@@ -526,6 +527,7 @@ struct phylink_pcs_ops {
void (*pcs_an_restart)(struct phylink_pcs *pcs);
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex);
.mac_config = mtk_mac_config,
.mac_finish = mtk_mac_finish,
.mac_link_down = mtk_mac_link_down,
-@@ -4991,7 +5024,8 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -4985,13 +5018,38 @@ static const struct net_device_ops mtk_n
+ .ndo_select_queue = mtk_select_queue,
+ };
+
++static int mtk_fill_available_pcs(struct phylink_config *config,
++ struct phylink_pcs **available_pcs,
++ unsigned int num_available_pcs)
++{
++ struct mtk_mac *mac = container_of(config, struct mtk_mac,
++ phylink_config);
++ struct mtk_eth *eth = mac->hw;
++ unsigned int sid;
++
++ if (mtk_is_netsys_v3_or_greater(eth)) {
++ return fwnode_phylink_pcs_parse(of_fwnode_handle(mac->of_node),
++ available_pcs,
++ &num_available_pcs);
++ } else {
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII))
++ sid = 0;
++ else
++ sid = mac->id;
++
++ available_pcs[0] = eth->sgmii_pcs[sid];
++ }
++
++ return 0;
++}
++
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ {
+ const __be32 *_id = of_get_property(np, "reg", NULL);
phy_interface_t phy_mode;
struct phylink *phylink;
struct mtk_mac *mac;
- int id, err;
+ int id, err, count;
-+ unsigned int sid;
int txqs = 1;
u32 val;
-@@ -5062,6 +5096,7 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -5062,6 +5120,8 @@ static int mtk_add_mac(struct mtk_eth *e
mac->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD |
MAC_2500FD;
mac->phylink_config.lpi_timer_default = 1000;
+ mac->phylink_config.num_available_pcs = 0;
++ mac->phylink_config.fill_available_pcs = mtk_fill_available_pcs;
/* MT7623 gmac0 is now missing its speed-specific PLL configuration
* in its .mac_config method (since state->speed is not valid there.
-@@ -5092,13 +5127,62 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -5092,13 +5152,53 @@ static int mtk_add_mac(struct mtk_eth *e
if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
__set_bit(PHY_INTERFACE_MODE_SGMII,
+ if (err)
+ goto free_netdev;
+
-+ err = fwnode_phylink_pcs_parse(of_fwnode_handle(np), mac->available_pcs, &count);
-+ if (err)
-+ goto free_netdev;
-+
-+ mac->phylink_config.available_pcs = mac->available_pcs;
+ mac->phylink_config.num_available_pcs = count;
+ } else {
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) {
+ err = -EBUSY;
+ goto free_netdev;
+ }
-+ sid = 0;
+ eth->shared_sgmii_used = true;
-+ } else {
-+ sid = id;
+ }
-+ mac->phylink_config.available_pcs = ð->sgmii_pcs[sid];
+ mac->phylink_config.num_available_pcs = 1;
+ }
+
if (mtk_is_netsys_v3_or_greater(mac->hw) &&
MTK_HAS_CAPS(mac->hw->soc->caps, MTK_ESW) &&
id == MTK_GMAC1_ID) {
-@@ -5108,18 +5192,16 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -5108,18 +5208,16 @@ static int mtk_add_mac(struct mtk_eth *e
phy_interface_zero(mac->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
mac->phylink_config.supported_interfaces);
if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) &&
id == MTK_GMAC2_ID)
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
-@@ -5142,6 +5224,16 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -5142,6 +5240,16 @@ static int mtk_add_mac(struct mtk_eth *e
eth->netdev[id]->irq = eth->irq[MTK_FE_IRQ_SHARED];
eth->netdev[id]->dev.of_node = np;
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
else
-@@ -5335,7 +5427,8 @@ static int mtk_probe(struct platform_dev
+@@ -5335,7 +5443,8 @@ static int mtk_probe(struct platform_dev
regmap_write(cci, 0, 3);
}
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -5702,7 +5702,7 @@ static const struct mtk_soc_data mt2701_
+@@ -5718,7 +5718,7 @@ static const struct mtk_soc_data mt2701_
DESC_SIZE(struct mtk_rx_dma),
.irq_done_mask = MTK_RX_DONE_INT,
.dma_l4_valid = RX_DMA_L4_VALID,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
-@@ -5730,7 +5730,7 @@ static const struct mtk_soc_data mt7621_
+@@ -5746,7 +5746,7 @@ static const struct mtk_soc_data mt7621_
DESC_SIZE(struct mtk_rx_dma),
.irq_done_mask = MTK_RX_DONE_INT,
.dma_l4_valid = RX_DMA_L4_VALID,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
-@@ -5760,7 +5760,7 @@ static const struct mtk_soc_data mt7622_
+@@ -5776,7 +5776,7 @@ static const struct mtk_soc_data mt7622_
DESC_SIZE(struct mtk_rx_dma),
.irq_done_mask = MTK_RX_DONE_INT,
.dma_l4_valid = RX_DMA_L4_VALID,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
-@@ -5789,7 +5789,7 @@ static const struct mtk_soc_data mt7623_
+@@ -5805,7 +5805,7 @@ static const struct mtk_soc_data mt7623_
DESC_SIZE(struct mtk_rx_dma),
.irq_done_mask = MTK_RX_DONE_INT,
.dma_l4_valid = RX_DMA_L4_VALID,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
-@@ -5815,7 +5815,7 @@ static const struct mtk_soc_data mt7629_
+@@ -5831,7 +5831,7 @@ static const struct mtk_soc_data mt7629_
DESC_SIZE(struct mtk_rx_dma),
.irq_done_mask = MTK_RX_DONE_INT,
.dma_l4_valid = RX_DMA_L4_VALID,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
-@@ -5847,7 +5847,7 @@ static const struct mtk_soc_data mt7981_
+@@ -5863,7 +5863,7 @@ static const struct mtk_soc_data mt7981_
.dma_l4_valid = RX_DMA_L4_VALID_V2,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
};
-@@ -5877,7 +5877,7 @@ static const struct mtk_soc_data mt7986_
+@@ -5893,7 +5893,7 @@ static const struct mtk_soc_data mt7986_
.dma_l4_valid = RX_DMA_L4_VALID_V2,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
},
};
-@@ -5930,7 +5930,7 @@ static const struct mtk_soc_data rt5350_
+@@ -5946,7 +5946,7 @@ static const struct mtk_soc_data rt5350_
.dma_l4_valid = RX_DMA_L4_VALID_PDMA,
.dma_max_len = MTK_TX_DMA_BUF_LEN,
.dma_len_offset = 16,
return NOTIFY_DONE;
if (mac->speed > 0 && mac->speed <= s.base.speed)
-@@ -5048,7 +5052,7 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -5072,7 +5076,7 @@ static int mtk_add_mac(struct mtk_eth *e
}
if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
eth->netdev[id] = alloc_etherdev_mqs(sizeof(*mac), txqs, 1);
if (!eth->netdev[id]) {
-@@ -5693,6 +5697,7 @@ static const struct mtk_soc_data mt2701_
+@@ -5709,6 +5713,7 @@ static const struct mtk_soc_data mt2701_
.required_clks = MT7623_CLKS_BITMAP,
.required_pctl = true,
.version = 1,
.tx = {
DESC_SIZE(struct mtk_tx_dma),
.dma_max_len = MTK_TX_DMA_BUF_LEN,
-@@ -5720,6 +5725,7 @@ static const struct mtk_soc_data mt7621_
+@@ -5736,6 +5741,7 @@ static const struct mtk_soc_data mt7621_
.offload_version = 1,
.ppe_num = 1,
.hash_offset = 2,
.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
.tx = {
DESC_SIZE(struct mtk_tx_dma),
-@@ -5750,6 +5756,7 @@ static const struct mtk_soc_data mt7622_
+@@ -5766,6 +5772,7 @@ static const struct mtk_soc_data mt7622_
.ppe_num = 1,
.hash_offset = 2,
.has_accounting = true,
.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
.tx = {
DESC_SIZE(struct mtk_tx_dma),
-@@ -5778,6 +5785,7 @@ static const struct mtk_soc_data mt7623_
+@@ -5794,6 +5801,7 @@ static const struct mtk_soc_data mt7623_
.offload_version = 1,
.ppe_num = 1,
.hash_offset = 2,
.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
.disable_pll_modes = true,
.tx = {
-@@ -5806,6 +5814,7 @@ static const struct mtk_soc_data mt7629_
+@@ -5822,6 +5830,7 @@ static const struct mtk_soc_data mt7629_
.required_pctl = false,
.has_accounting = true,
.version = 1,
.tx = {
DESC_SIZE(struct mtk_tx_dma),
.dma_max_len = MTK_TX_DMA_BUF_LEN,
-@@ -5835,6 +5844,7 @@ static const struct mtk_soc_data mt7981_
+@@ -5851,6 +5860,7 @@ static const struct mtk_soc_data mt7981_
.ppe_num = 2,
.hash_offset = 4,
.has_accounting = true,
.foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
.tx = {
DESC_SIZE(struct mtk_tx_dma_v2),
-@@ -5865,6 +5875,7 @@ static const struct mtk_soc_data mt7986_
+@@ -5881,6 +5891,7 @@ static const struct mtk_soc_data mt7986_
.ppe_num = 2,
.hash_offset = 4,
.has_accounting = true,
.foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
.tx = {
DESC_SIZE(struct mtk_tx_dma_v2),
-@@ -5895,6 +5906,7 @@ static const struct mtk_soc_data mt7988_
+@@ -5911,6 +5922,7 @@ static const struct mtk_soc_data mt7988_
.ppe_num = 3,
.hash_offset = 4,
.has_accounting = true,
if (__ethtool_get_link_ksettings(dev, &s))
return NOTIFY_DONE;
-@@ -5030,7 +5074,7 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -5055,7 +5099,7 @@ static int mtk_add_mac(struct mtk_eth *e
phy_interface_t phy_mode;
struct phylink *phylink;
struct mtk_mac *mac;
- int id, err, count;
+ int id, err, count, i;
- unsigned int sid;
int txqs = 1;
u32 val;
-@@ -5065,6 +5109,16 @@ static int mtk_add_mac(struct mtk_eth *e
+
+@@ -5089,6 +5133,16 @@ static int mtk_add_mac(struct mtk_eth *e
mac->hw = eth;
mac->of_node = np;
if (mtk_is_netsys_v1(eth))
val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
-@@ -6013,6 +6049,37 @@ static const struct mtk_soc_data mt7986_
+@@ -6029,6 +6065,37 @@ static const struct mtk_soc_data mt7986_
},
};
static const struct mtk_soc_data mt7988_data = {
.reg_map = &mt7988_reg_map,
.ana_rgc3 = 0x128,
-@@ -6075,6 +6142,7 @@ const struct of_device_id of_mtk_match[]
+@@ -6091,6 +6158,7 @@ const struct of_device_id of_mtk_match[]
{ .compatible = "mediatek,mt7629-eth", .data = &mt7629_data },
{ .compatible = "mediatek,mt7981-eth", .data = &mt7981_data },
{ .compatible = "mediatek,mt7986-eth", .data = &mt7986_data },