Skip to content

Commit afdf59a

Browse files
Bo-Cun Chenfrank-w
authored andcommitted
net: ethernet: mtk_eth_soc: support ethernet passive mux
In order to support SFP/PHY dynamic switching, we add a passive mux to the SerDes path that connects with the SFP cage and PHY. We then use the GPIO to control this mux, which will switch to the SFP framework when the SFP module is inserted and will switch to the PHY framework when the SFP module is removed. Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
1 parent 80f2ddb commit afdf59a

File tree

2 files changed

+248
-1
lines changed

2 files changed

+248
-1
lines changed

drivers/net/ethernet/mediatek/mtk_eth_soc.c

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/pcs/pcs-mtk-lynxi.h>
2525
#include <linux/pcs/pcs.h>
2626
#include <linux/phy/phy.h>
27+
#include <linux/gpio/consumer.h>
2728
#include <linux/jhash.h>
2829
#include <linux/bitfield.h>
2930
#include <net/dsa.h>
@@ -4918,6 +4919,207 @@ static const struct net_device_ops mtk_netdev_ops = {
49184919
.ndo_select_queue = mtk_select_queue,
49194920
};
49204921

4922+
static void mux_poll(struct work_struct *work)
4923+
{
4924+
struct mtk_mux *mux = container_of(work, struct mtk_mux, poll.work);
4925+
struct mtk_mac *mac = mux->mac;
4926+
struct mtk_eth *eth = mac->hw;
4927+
struct net_device *dev = eth->netdev[mac->id];
4928+
unsigned int new_channel;
4929+
int sfp_present;
4930+
4931+
if (IS_ERR(mux->mod_def0_gpio) || IS_ERR(mux->chan_sel_gpio))
4932+
goto reschedule;
4933+
4934+
sfp_present = gpiod_get_value_cansleep(mux->mod_def0_gpio);
4935+
new_channel = sfp_present ? mux->sfp_present_channel : !mux->sfp_present_channel;
4936+
4937+
if (mux->channel == new_channel || !netif_running(dev))
4938+
goto reschedule;
4939+
4940+
rtnl_lock();
4941+
4942+
mtk_stop(dev);
4943+
4944+
mac->of_node = mux->data[new_channel]->of_node;
4945+
mac->phylink = mux->data[new_channel]->phylink;
4946+
4947+
dev_info(eth->dev, "ethernet mux: switch to channel%d\n", new_channel);
4948+
4949+
gpiod_set_value_cansleep(mux->chan_sel_gpio, new_channel);
4950+
4951+
mtk_open(dev);
4952+
4953+
rtnl_unlock();
4954+
4955+
mux->channel = new_channel;
4956+
4957+
reschedule:
4958+
mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(100));
4959+
}
4960+
4961+
static int mtk_add_mux_channel(struct mtk_mux *mux, struct device_node *np)
4962+
{
4963+
const __be32 *_id = of_get_property(np, "reg", NULL);
4964+
struct mtk_mac *mac = mux->mac;
4965+
struct mtk_eth *eth = mac->hw;
4966+
struct mtk_mux_data *data;
4967+
struct phylink *phylink;
4968+
phy_interface_t phy_mode;
4969+
int id, err;
4970+
4971+
if (!_id) {
4972+
dev_err(eth->dev, "missing mux channel id\n");
4973+
return -EINVAL;
4974+
}
4975+
4976+
id = be32_to_cpup(_id);
4977+
if (id < 0 || id > 1) {
4978+
dev_err(eth->dev, "%d is not a valid mux channel id\n", id);
4979+
return -EINVAL;
4980+
}
4981+
4982+
data = kmalloc(sizeof(*data), GFP_KERNEL);
4983+
if (unlikely(!data)) {
4984+
dev_err(eth->dev, "failed to create mux data structure\n");
4985+
return -ENOMEM;
4986+
}
4987+
4988+
err = of_get_phy_mode(np, &phy_mode);
4989+
if (err) {
4990+
dev_err(eth->dev, "incorrect phy-mode\n");
4991+
goto err_free_data;
4992+
}
4993+
4994+
phylink = phylink_create(&mux->mac->phylink_config,
4995+
of_fwnode_handle(np),
4996+
phy_mode, &mtk_phylink_ops);
4997+
if (IS_ERR(phylink)) {
4998+
dev_err(eth->dev, "failed to create phylink structure\n");
4999+
err = PTR_ERR(phylink);
5000+
goto err_free_data;
5001+
}
5002+
5003+
data->of_node = np;
5004+
data->phylink = phylink;
5005+
mux->data[id] = data;
5006+
5007+
return 0;
5008+
5009+
err_free_data:
5010+
kfree(data);
5011+
return err;
5012+
}
5013+
5014+
static void mtk_release_mux(struct mtk_eth *eth, int id)
5015+
{
5016+
struct mtk_mux *mux = eth->mux[id];
5017+
int i;
5018+
5019+
if (!mux)
5020+
return;
5021+
5022+
cancel_delayed_work_sync(&mux->poll);
5023+
5024+
if (!IS_ERR_OR_NULL(mux->mod_def0_gpio))
5025+
gpiod_put(mux->mod_def0_gpio);
5026+
5027+
if (!IS_ERR_OR_NULL(mux->chan_sel_gpio))
5028+
gpiod_put(mux->chan_sel_gpio);
5029+
5030+
for (i = 0; i < 2; i++) {
5031+
if (mux->data[i]) {
5032+
if (mux->data[i]->phylink)
5033+
phylink_destroy(mux->data[i]->phylink);
5034+
kfree(mux->data[i]);
5035+
}
5036+
}
5037+
kfree(mux);
5038+
eth->mux[id] = NULL;
5039+
}
5040+
5041+
static void mtk_release_all_muxes(struct mtk_eth *eth)
5042+
{
5043+
int i;
5044+
for (i = 0; i < MTK_MAX_DEVS; i++)
5045+
mtk_release_mux(eth, i);
5046+
}
5047+
5048+
static int mtk_add_mux(struct mtk_eth *eth, struct device_node *np)
5049+
{
5050+
const __be32 *_id = of_get_property(np, "reg", NULL);
5051+
struct device_node *child;
5052+
struct mtk_mux *mux;
5053+
unsigned int id;
5054+
int err;
5055+
5056+
if (!_id) {
5057+
dev_err(eth->dev, "missing attach mac id\n");
5058+
return -EINVAL;
5059+
}
5060+
5061+
id = be32_to_cpup(_id);
5062+
if (id < 0 || id >= MTK_MAX_DEVS) {
5063+
dev_err(eth->dev, "%d is not a valid attach mac id\n", id);
5064+
return -EINVAL;
5065+
}
5066+
5067+
mux = kmalloc(sizeof(struct mtk_mux), GFP_KERNEL);
5068+
if (unlikely(!mux)) {
5069+
dev_err(eth->dev, "failed to create mux structure\n");
5070+
return -ENOMEM;
5071+
}
5072+
5073+
eth->mux[id] = mux;
5074+
mux->mac = eth->mac[id];
5075+
mux->channel = 0;
5076+
5077+
mux->mod_def0_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np),
5078+
"mod-def0", 0, GPIOD_IN |
5079+
GPIOD_FLAGS_BIT_NONEXCLUSIVE, "?");
5080+
5081+
if (IS_ERR(mux->mod_def0_gpio)) {
5082+
dev_err(eth->dev, "failed to requset gpio for mod-def0\n");
5083+
err = PTR_ERR(mux->mod_def0_gpio);
5084+
goto err_free_mux;
5085+
}
5086+
5087+
mux->chan_sel_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np),
5088+
"chan-sel", 0, GPIOD_OUT_LOW, "?");
5089+
5090+
if (IS_ERR(mux->chan_sel_gpio)) {
5091+
dev_err(eth->dev, "failed to requset gpio for chan-sel\n");
5092+
err = PTR_ERR(mux->chan_sel_gpio);
5093+
goto err_put_mod_def0;
5094+
}
5095+
5096+
of_property_read_u32(np, "sfp-present-channel",
5097+
&mux->sfp_present_channel);
5098+
5099+
for_each_child_of_node(np, child) {
5100+
err = mtk_add_mux_channel(mux, child);
5101+
if (err) {
5102+
dev_err(eth->dev, "failed to add mtk_mux\n");
5103+
of_node_put(child);
5104+
goto err_put_chan_sel;
5105+
}
5106+
}
5107+
5108+
INIT_DELAYED_WORK(&mux->poll, mux_poll);
5109+
mod_delayed_work(system_wq, &mux->poll, msecs_to_jiffies(3000));
5110+
5111+
return 0;
5112+
5113+
err_put_chan_sel:
5114+
gpiod_put(mux->chan_sel_gpio);
5115+
err_put_mod_def0:
5116+
gpiod_put(mux->mod_def0_gpio);
5117+
err_free_mux:
5118+
kfree(mux);
5119+
eth->mux[id] = NULL;
5120+
return err;
5121+
}
5122+
49215123
static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
49225124
{
49235125
const __be32 *_id = of_get_property(np, "reg", NULL);
@@ -5248,7 +5450,7 @@ static int mtk_setup_legacy_sram(struct mtk_eth *eth, struct resource *res)
52485450
static int mtk_probe(struct platform_device *pdev)
52495451
{
52505452
struct resource *res = NULL;
5251-
struct device_node *mac_np;
5453+
struct device_node *mac_np, *mux_np;
52525454
struct mtk_eth *eth;
52535455
int err, i;
52545456

@@ -5453,6 +5655,26 @@ static int mtk_probe(struct platform_device *pdev)
54535655
goto err_free_dev;
54545656
}
54555657

5658+
mux_np = of_get_child_by_name(eth->dev->of_node, "mux-bus");
5659+
if (mux_np) {
5660+
struct device_node *child;
5661+
5662+
for_each_available_child_of_node(mux_np, child) {
5663+
if (!of_device_is_compatible(child,
5664+
"mediatek,eth-mux"))
5665+
continue;
5666+
5667+
if (!of_device_is_available(child))
5668+
continue;
5669+
5670+
err = mtk_add_mux(eth, child);
5671+
if (err)
5672+
dev_err(&pdev->dev, "failed to add mux\n");
5673+
5674+
of_node_put(mux_np);
5675+
};
5676+
}
5677+
54565678
if (eth->soc->offload_version) {
54575679
u8 ppe_num = eth->soc->ppe_num;
54585680

@@ -5510,6 +5732,7 @@ static int mtk_probe(struct platform_device *pdev)
55105732
mtk_unreg_dev(eth);
55115733
err_deinit_ppe:
55125734
mtk_ppe_deinit(eth);
5735+
mtk_release_all_muxes(eth);
55135736
mtk_mdio_cleanup(eth);
55145737
err_free_dev:
55155738
mtk_free_dev(eth);
@@ -5546,6 +5769,7 @@ static void mtk_remove(struct platform_device *pdev)
55465769
mtk_cleanup(eth);
55475770
free_netdev(eth->dummy_dev);
55485771
mtk_mdio_cleanup(eth);
5772+
mtk_release_all_muxes(eth);
55495773
}
55505774

55515775
static const struct mtk_soc_data mt2701_data = {

drivers/net/ethernet/mediatek/mtk_eth_soc.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ struct mtk_tx_dma_v2 {
725725

726726
struct mtk_eth;
727727
struct mtk_mac;
728+
struct mtk_mux;
728729

729730
struct mtk_xdp_stats {
730731
u64 rx_xdp_redirect;
@@ -1390,6 +1391,7 @@ struct mtk_eth {
13901391
struct net_device *dummy_dev;
13911392
struct net_device *netdev[MTK_MAX_DEVS];
13921393
struct mtk_mac *mac[MTK_MAX_DEVS];
1394+
struct mtk_mux *mux[MTK_MAX_DEVS];
13931395
int irq[MTK_FE_IRQ_NUM];
13941396
u32 msg_enable;
13951397
unsigned long sysclk;
@@ -1476,6 +1478,27 @@ struct mtk_mac {
14761478
struct notifier_block device_notifier;
14771479
};
14781480

1481+
/* struct mtk_mux_data - the structure that holds the private data about the
1482+
* Passive MUXs of the SoC
1483+
*/
1484+
struct mtk_mux_data {
1485+
struct device_node *of_node;
1486+
struct phylink *phylink;
1487+
};
1488+
1489+
/* struct mtk_mux - the structure that holds the info about the Passive MUXs of the
1490+
* SoC
1491+
*/
1492+
struct mtk_mux {
1493+
struct delayed_work poll;
1494+
struct gpio_desc *mod_def0_gpio;
1495+
struct gpio_desc *chan_sel_gpio;
1496+
struct mtk_mux_data *data[2];
1497+
struct mtk_mac *mac;
1498+
unsigned int channel;
1499+
unsigned int sfp_present_channel;
1500+
};
1501+
14791502
/* the struct describing the SoC. these are declared in the soc_xyz.c files */
14801503
extern const struct of_device_id of_mtk_match[];
14811504

0 commit comments

Comments
 (0)