[][kernel][common][eth][Add a Ethernet passive Mux support]
[Description]
Add a Ethernet passive Mux support.
If your board that XGMAC1 connects a 10G PHY and a SFP cage through a
passive Mux, please apply the patch as follows.
==============================================================
phy-handle = <&phy8>;
};
};
+
+ mux: mux-bus {
+ mux1: ethernet-mux@1 {
+ compatible = "mediatek,eth-mux";
+ reg = <1>;
+
+ chan-sel-gpios = <&pio 30 0>;
+ mod-def0-gpios = <&pio 82 1>;
+
+ channel0: channel@0 {
+ reg = <0>;
+ mac-type = "xgdm";
+ phy-mode = "usxgmii";
+ phy-handle = <&phy0>;
+ };
+
+ channel1: channel@1 {
+ reg = <1>;
+ mac-type = "xgdm";
+ phy-mode = "10gbase-kr";
+ managed = "in-band-status";
+ sfp = <&sfp_esp1>;
+ };
+ };
+ };
mdio: mdio-bus {
#address-cells = <1>;
#size-cells = <0>;
==============================================================
After applying this patch, the ETH driver has the ability to dynamically
switch the network interface via a passive Mux.
Without this patch, the ETH driver is unable to dynamically switch the
network interface between the 10G PHY and the SFP cage.
[Release-log]
N/A
Change-Id: I9d55370ca6952dd08d28e4151c1139737c4918a2
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7979960
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 409b834..b0cfc66 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -4575,6 +4575,151 @@
#endif
};
+static void mux_poll(struct work_struct *work)
+{
+ struct mtk_mux *mux = container_of(work, struct mtk_mux, poll.work);
+ struct mtk_mac *mac = mux->mac;
+ struct mtk_eth *eth = mac->hw;
+ struct net_device *dev = eth->netdev[mac->id];
+ unsigned int channel;
+
+ if (IS_ERR(mux->gpio[0]) || IS_ERR(mux->gpio[1]))
+ goto exit;
+
+ channel = gpiod_get_value_cansleep(mux->gpio[0]);
+ if (mux->channel == channel || !netif_running(dev))
+ goto exit;
+
+ rtnl_lock();
+
+ mtk_stop(dev);
+
+ if (channel == 0 || channel == 1) {
+ mac->of_node = mux->data[channel]->of_node;
+ mac->phylink = mux->data[channel]->phylink;
+ };
+
+ dev_info(eth->dev, "ethernet mux: switch to channel%d\n", channel);
+
+ gpiod_set_value_cansleep(mux->gpio[1], channel);
+
+ mtk_open(dev);
+
+ rtnl_unlock();
+
+ mux->channel = channel;
+
+exit:
+ mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(100));
+}
+
+static int mtk_add_mux_channel(struct mtk_mux *mux, struct device_node *np)
+{
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+ struct mtk_mac *mac = mux->mac;
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_mux_data *data;
+ struct phylink *phylink;
+ int phy_mode, id;
+
+ if (!_id) {
+ dev_err(eth->dev, "missing mux channel id\n");
+ return -EINVAL;
+ }
+
+ id = be32_to_cpup(_id);
+ if (id < 0 || id > 1) {
+ dev_err(eth->dev, "%d is not a valid mux channel id\n", id);
+ return -EINVAL;
+ }
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (unlikely(!data)) {
+ dev_err(eth->dev, "failed to create mux data structure\n");
+ return -ENOMEM;
+ }
+
+ mux->data[id] = data;
+
+ /* phylink create */
+ phy_mode = of_get_phy_mode(np);
+ if (phy_mode < 0) {
+ dev_err(eth->dev, "incorrect phy-mode\n");
+ return -EINVAL;
+ }
+
+ phylink = phylink_create(&mux->mac->phylink_config,
+ of_fwnode_handle(np),
+ phy_mode, &mtk_phylink_ops);
+ if (IS_ERR(phylink)) {
+ dev_err(eth->dev, "failed to create phylink structure\n");
+ return PTR_ERR(phylink);
+ }
+
+ data->of_node = np;
+ data->phylink = phylink;
+
+ return 0;
+}
+
+static int mtk_add_mux(struct mtk_eth *eth, struct device_node *np)
+{
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+ struct device_node *child;
+ struct mtk_mux *mux;
+ unsigned int id;
+ int err;
+
+ if (!_id) {
+ dev_err(eth->dev, "missing attach mac id\n");
+ return -EINVAL;
+ }
+
+ id = be32_to_cpup(_id);
+ if (id < 0 || id >= MTK_MAX_DEVS) {
+ dev_err(eth->dev, "%d is not a valid attach mac id\n", id);
+ return -EINVAL;
+ }
+
+ mux = kmalloc(sizeof(struct mtk_mux), GFP_KERNEL);
+ if (unlikely(!mux)) {
+ dev_err(eth->dev, "failed to create mux structure\n");
+ return -ENOMEM;
+ }
+
+ eth->mux[id] = mux;
+
+ mux->mac = eth->mac[id];
+ mux->channel = 0;
+
+ mux->gpio[0] = fwnode_get_named_gpiod(of_fwnode_handle(np),
+ "mod-def0-gpios", 0,
+ GPIOD_IN, "?");
+ if (IS_ERR(mux->gpio[0]))
+ dev_err(eth->dev, "failed to requset gpio for mod-def0-gpios\n");
+
+ mux->gpio[1] = fwnode_get_named_gpiod(of_fwnode_handle(np),
+ "chan-sel-gpios", 0,
+ GPIOD_OUT_LOW, "?");
+ if (IS_ERR(mux->gpio[1]))
+ dev_err(eth->dev, "failed to requset gpio for chan-sel-gpios\n");
+
+ for_each_child_of_node(np, child) {
+ err = mtk_add_mux_channel(mux, child);
+ if (err) {
+ dev_err(eth->dev, "failed to add mtk_mux\n");
+ of_node_put(child);
+ return -ECHILD;
+ }
+ of_node_put(child);
+ }
+
+ INIT_DELAYED_WORK(&mux->poll, mux_poll);
+ mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(3000));
+
+ return 0;
+}
+
static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
{
const __be32 *_id = of_get_property(np, "reg", NULL);
@@ -4772,7 +4917,7 @@
static int mtk_probe(struct platform_device *pdev)
{
- struct device_node *mac_np;
+ struct device_node *mac_np, *mux_np;
struct mtk_eth *eth;
int err, i;
@@ -4943,6 +5088,26 @@
}
}
+ mux_np = of_get_child_by_name(eth->dev->of_node, "mux-bus");
+ if (mux_np) {
+ struct device_node *child;
+
+ for_each_available_child_of_node(mux_np, child) {
+ if (!of_device_is_compatible(child,
+ "mediatek,eth-mux"))
+ continue;
+
+ if (!of_device_is_available(child))
+ continue;
+
+ err = mtk_add_mux(eth, child);
+ if (err)
+ dev_err(&pdev->dev, "failed to add mux\n");
+
+ of_node_put(mux_np);
+ };
+ }
+
err = mtk_napi_init(eth);
if (err)
goto err_free_dev;
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 668b77c..88df3b2 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -1040,6 +1040,7 @@
struct mtk_eth;
struct mtk_mac;
+struct mtk_mux;
/* struct mtk_hw_stats - the structure that holds the traffic statistics.
* @stats_lock: make sure that stats operations are atomic
@@ -1808,6 +1809,7 @@
struct net_device dummy_dev;
struct net_device *netdev[MTK_MAX_DEVS];
struct mtk_mac *mac[MTK_MAX_DEVS];
+ struct mtk_mux *mux[MTK_MAX_DEVS];
int irq_fe[MTK_FE_IRQ_NUM];
int irq_pdma[MTK_PDMA_IRQ_NUM];
u8 hwver;
@@ -1872,6 +1874,25 @@
u32 tx_lpi_timer;
};
+/* struct mtk_mux_data - the structure that holds the private data about the
+ * Passive MUXs of the SoC
+ */
+struct mtk_mux_data {
+ struct device_node *of_node;
+ struct phylink *phylink;
+};
+
+/* struct mtk_mux - the structure that holds the info about the Passive MUXs of the
+ * SoC
+ */
+struct mtk_mux {
+ struct delayed_work poll;
+ struct gpio_desc *gpio[2];
+ struct mtk_mux_data *data[2];
+ struct mtk_mac *mac;
+ unsigned int channel;
+};
+
/* the struct describing the SoC. these are declared in the soc_xyz.c files */
extern struct mtk_eth *g_eth;
extern const struct of_device_id of_mtk_match[];
diff --git a/target/linux/mediatek/patches-5.4/999-2730-net-phy-sfp-change-shared-mod-def0.patch b/target/linux/mediatek/patches-5.4/999-2730-net-phy-sfp-change-shared-mod-def0.patch
new file mode 100644
index 0000000..479f58b
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/999-2730-net-phy-sfp-change-shared-mod-def0.patch
@@ -0,0 +1,43 @@
+From c576ce67488bc8b0285933f315cb183c9171f199 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Thu, 7 Sep 2023 12:01:57 +0800
+Subject: [PATCH] 999-2730-net-phy-sfp-change-shared-mod-def0
+
+---
+ drivers/net/phy/sfp.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 0c335b1..d49a825 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -152,7 +152,7 @@ static const char *gpio_of_names[] = {
+ };
+
+ static const enum gpiod_flags gpio_flags[] = {
+- GPIOD_IN,
++ GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+ GPIOD_IN,
+ GPIOD_IN,
+ GPIOD_ASIS,
+@@ -512,7 +512,7 @@ static unsigned int sfp_gpio_get_state(struct sfp *sfp)
+ unsigned int i, state, v;
+
+ for (i = state = 0; i < GPIO_MAX; i++) {
+- if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++ if (((gpio_flags[i] & GPIOD_IN) != GPIOD_IN) || !sfp->gpio[i])
+ continue;
+
+ v = gpiod_get_value_cansleep(sfp->gpio[i]);
+@@ -2757,7 +2757,7 @@ static int sfp_probe(struct platform_device *pdev)
+ }
+
+ for (i = 0; i < GPIO_MAX; i++) {
+- if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++ if (((gpio_flags[i] & GPIOD_IN) != GPIOD_IN) || !sfp->gpio[i])
+ continue;
+
+ sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
+--
+2.18.0
+