|
20 | 20 | #include <linux/phylink.h> |
21 | 21 | #include <linux/dsa/8021q.h> |
22 | 22 | #include <net/dsa.h> |
| 23 | +#include <linux/stddef.h> |
| 24 | +#include <linux/gpio/consumer.h> |
| 25 | +#include <linux/of_net.h> |
23 | 26 |
|
24 | 27 | #include "mxl862xx.h" |
25 | 28 | #include "mxl862xx-api.h" |
@@ -4516,10 +4519,219 @@ static const struct dsa_switch_ops mxl862xx_switch_ops = { |
4516 | 4519 | .devlink_flash_update = mxl862xx_devlink_flash_update, |
4517 | 4520 | }; |
4518 | 4521 |
|
| 4522 | +static void sfp_monitor_work_func(struct work_struct *work) |
| 4523 | +{ |
| 4524 | + struct combo_port_mux *mux = container_of(work, struct combo_port_mux, sfp_monitor_work.work); |
| 4525 | + struct dsa_switch *ds = mux->dp->ds; |
| 4526 | + struct dsa_port *dp = mux->dp; |
| 4527 | + struct net_device *dev = mux->dp->user; |
| 4528 | + unsigned int new_channel; |
| 4529 | + int sfp_present; |
| 4530 | + |
| 4531 | + if (IS_ERR(mux->mod_def0_gpio) || IS_ERR(mux->chan_sel_gpio)) |
| 4532 | + goto reschedule; |
| 4533 | + |
| 4534 | + if (!netif_running(dev)) |
| 4535 | + goto reschedule; |
| 4536 | + |
| 4537 | + sfp_present = gpiod_get_value_cansleep(mux->mod_def0_gpio); |
| 4538 | + new_channel = sfp_present ? mux->sfp_present_channel : !mux->sfp_present_channel; |
| 4539 | + |
| 4540 | + if (mux->initialized && mux->channel == new_channel) |
| 4541 | + goto reschedule; |
| 4542 | + |
| 4543 | + rtnl_lock(); |
| 4544 | + |
| 4545 | + phylink_stop(dp->pl); |
| 4546 | + phylink_disconnect_phy(dp->pl); |
| 4547 | + |
| 4548 | + dp->dn = mux->data[new_channel]->of_node; |
| 4549 | + dp->pl = mux->data[new_channel]->phylink; |
| 4550 | + |
| 4551 | + phylink_of_phy_connect(dp->pl, dp->dn, 0); |
| 4552 | + phylink_start(dp->pl); |
| 4553 | + |
| 4554 | + dev_info(ds->dev, "dsa mux: switch to channel%d\n", new_channel); |
| 4555 | + |
| 4556 | + gpiod_set_value_cansleep(mux->chan_sel_gpio, new_channel); |
| 4557 | + |
| 4558 | + rtnl_unlock(); |
| 4559 | + |
| 4560 | + mux->channel = new_channel; |
| 4561 | + mux->initialized = true; |
| 4562 | + |
| 4563 | +reschedule: |
| 4564 | + mod_delayed_work(system_wq, &mux->sfp_monitor_work, msecs_to_jiffies(100)); |
| 4565 | +} |
| 4566 | + |
| 4567 | +static int ds_add_mux_channel(struct combo_port_mux *mux, struct device_node *np) |
| 4568 | +{ |
| 4569 | + const __be32 *_id = of_get_property(np, "reg", NULL); |
| 4570 | + struct dsa_switch *ds = mux->dp->ds; |
| 4571 | + struct dp_mux_data *data; |
| 4572 | + struct phylink *phylink; |
| 4573 | + phy_interface_t phy_mode; |
| 4574 | + int id, err; |
| 4575 | + |
| 4576 | + if (!_id) { |
| 4577 | + dev_err(ds->dev, "missing mux channel id\n"); |
| 4578 | + return -EINVAL; |
| 4579 | + } |
| 4580 | + |
| 4581 | + id = be32_to_cpup(_id); |
| 4582 | + if (id < 0 || id > 1) { |
| 4583 | + dev_err(ds->dev, "%d is not a valid mux channel id\n", id); |
| 4584 | + return -EINVAL; |
| 4585 | + } |
| 4586 | + |
| 4587 | + data = kmalloc(sizeof(*data), GFP_KERNEL); |
| 4588 | + if (unlikely(!data)) { |
| 4589 | + dev_err(ds->dev, "failed to create mux data structure\n"); |
| 4590 | + return -ENOMEM; |
| 4591 | + } |
| 4592 | + |
| 4593 | + err = of_get_phy_mode(np, &phy_mode); |
| 4594 | + if (err) { |
| 4595 | + dev_err(ds->dev, "incorrect phy-mode\n"); |
| 4596 | + goto err_free_data; |
| 4597 | + } |
| 4598 | + |
| 4599 | + phylink = phylink_create(&mux->dp->pl_config, |
| 4600 | + of_fwnode_handle(np), |
| 4601 | + phy_mode, ds->phylink_mac_ops); |
| 4602 | + if (IS_ERR(phylink)) { |
| 4603 | + dev_err(ds->dev, "failed to create phylink structure\n"); |
| 4604 | + err = PTR_ERR(phylink); |
| 4605 | + goto err_free_data; |
| 4606 | + } |
| 4607 | + |
| 4608 | + data->of_node = np; |
| 4609 | + data->phylink = phylink; |
| 4610 | + mux->data[id] = data; |
| 4611 | + |
| 4612 | + return 0; |
| 4613 | + |
| 4614 | +err_free_data: |
| 4615 | + kfree(data); |
| 4616 | + return err; |
| 4617 | +} |
| 4618 | + |
| 4619 | +static int ds_add_mux(struct mxl862xx_priv *priv, struct device_node *np) |
| 4620 | +{ |
| 4621 | + const __be32 *_id = of_get_property(np, "reg", NULL); |
| 4622 | + struct device_node *child; |
| 4623 | + struct combo_port_mux *mux; |
| 4624 | + unsigned int id; |
| 4625 | + int err; |
| 4626 | + |
| 4627 | + if (!_id) { |
| 4628 | + dev_err(priv->ds->dev, "missing attach dp id\n"); |
| 4629 | + return -EINVAL; |
| 4630 | + } |
| 4631 | + |
| 4632 | + id = be32_to_cpup(_id); |
| 4633 | + if (id < 0 || id >= MXL862XX_MAX_PORTS) { |
| 4634 | + dev_err(priv->ds->dev, "%d is not a valid attach dp id\n", id); |
| 4635 | + return -EINVAL; |
| 4636 | + } |
| 4637 | + |
| 4638 | + mux = kmalloc(sizeof(struct combo_port_mux), GFP_KERNEL); |
| 4639 | + if (unlikely(!mux)) { |
| 4640 | + dev_err(priv->ds->dev, "failed to create mux structure\n"); |
| 4641 | + return -ENOMEM; |
| 4642 | + } |
| 4643 | + |
| 4644 | + mux->mod_def0_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), |
| 4645 | + "mod-def0", 0, GPIOD_IN | |
| 4646 | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, "?"); |
| 4647 | + |
| 4648 | + if (IS_ERR(mux->mod_def0_gpio)) { |
| 4649 | + dev_err(priv->ds->dev, "failed to requset gpio for mod-def0\n"); |
| 4650 | + err = PTR_ERR(mux->mod_def0_gpio); |
| 4651 | + goto err_free_mux; |
| 4652 | + } |
| 4653 | + |
| 4654 | + mux->chan_sel_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), |
| 4655 | + "chan-sel", 0, GPIOD_OUT_LOW, "?"); |
| 4656 | + |
| 4657 | + if (IS_ERR(mux->chan_sel_gpio)) { |
| 4658 | + dev_err(priv->ds->dev, "failed to requset gpio for chan-sel\n"); |
| 4659 | + err = PTR_ERR(mux->chan_sel_gpio); |
| 4660 | + goto err_put_mod_def0; |
| 4661 | + } |
| 4662 | + |
| 4663 | + of_property_read_u32(np, "sfp-present-channel", |
| 4664 | + &mux->sfp_present_channel); |
| 4665 | + |
| 4666 | + priv->ds_mux[id] = mux; |
| 4667 | + mux->dp = dsa_to_port(priv->ds, id); |
| 4668 | + /* configure default channel to 10G PHY */ |
| 4669 | + mux->channel = !mux->sfp_present_channel; |
| 4670 | + mux->initialized = false; |
| 4671 | + |
| 4672 | + for_each_child_of_node(np, child) { |
| 4673 | + err = ds_add_mux_channel(mux, child); |
| 4674 | + if (err) { |
| 4675 | + dev_err(priv->ds->dev, "failed to add ds_mux\n"); |
| 4676 | + of_node_put(child); |
| 4677 | + goto err_put_chan_sel; |
| 4678 | + } |
| 4679 | + } |
| 4680 | + |
| 4681 | + INIT_DELAYED_WORK(&mux->sfp_monitor_work, sfp_monitor_work_func); |
| 4682 | + mod_delayed_work(system_wq, &mux->sfp_monitor_work, msecs_to_jiffies(3000)); |
| 4683 | + |
| 4684 | + return 0; |
| 4685 | + |
| 4686 | +err_put_chan_sel: |
| 4687 | + gpiod_put(mux->chan_sel_gpio); |
| 4688 | +err_put_mod_def0: |
| 4689 | + gpiod_put(mux->mod_def0_gpio); |
| 4690 | +err_free_mux: |
| 4691 | + kfree(mux); |
| 4692 | + priv->ds_mux[id] = NULL; |
| 4693 | + return err; |
| 4694 | +} |
| 4695 | + |
| 4696 | +static void mxl862xx_release_mux(struct mxl862xx_priv *priv, int id) |
| 4697 | +{ |
| 4698 | + struct combo_port_mux *mux = priv->ds_mux[id]; |
| 4699 | + int i; |
| 4700 | + |
| 4701 | + if (!mux) |
| 4702 | + return; |
| 4703 | + |
| 4704 | + cancel_delayed_work_sync(&mux->sfp_monitor_work); |
| 4705 | + |
| 4706 | + if (!IS_ERR_OR_NULL(mux->mod_def0_gpio)) |
| 4707 | + gpiod_put(mux->mod_def0_gpio); |
| 4708 | + |
| 4709 | + if (!IS_ERR_OR_NULL(mux->chan_sel_gpio)) |
| 4710 | + gpiod_put(mux->chan_sel_gpio); |
| 4711 | + |
| 4712 | + for (i = 0; i < 2; i++) { |
| 4713 | + if (mux->data[i]) { |
| 4714 | + if (mux->data[i]->phylink) |
| 4715 | + phylink_destroy(mux->data[i]->phylink); |
| 4716 | + kfree(mux->data[i]); |
| 4717 | + } |
| 4718 | + } |
| 4719 | + kfree(mux); |
| 4720 | + priv->ds_mux[id] = NULL; |
| 4721 | +} |
| 4722 | + |
| 4723 | +static void mxl862xx_release_all_muxes(struct mxl862xx_priv *priv) |
| 4724 | +{ |
| 4725 | + int i; |
| 4726 | + for (i = 0; i < MXL862XX_MAX_PORTS; i++) |
| 4727 | + mxl862xx_release_mux(priv, i); |
| 4728 | +} |
| 4729 | + |
4519 | 4730 | static int mxl862xx_probe(struct mdio_device *mdiodev) |
4520 | 4731 | { |
4521 | 4732 | struct device *dev = &mdiodev->dev; |
4522 | 4733 | struct mxl862xx_priv *priv; |
| 4734 | + struct device_node *mux_np; |
4523 | 4735 | struct dsa_switch *ds; |
4524 | 4736 | int err, i; |
4525 | 4737 |
|
@@ -4565,9 +4777,30 @@ static int mxl862xx_probe(struct mdio_device *mdiodev) |
4565 | 4777 | mxl862xx_host_shutdown(priv); |
4566 | 4778 | for (i = 0; i < MXL862XX_MAX_PORTS; i++) |
4567 | 4779 | cancel_work_sync(&priv->ports[i].host_flood_work); |
| 4780 | + return err; |
4568 | 4781 | } |
4569 | 4782 |
|
4570 | | - return err; |
| 4783 | + mux_np = of_get_child_by_name(priv->ds->dev->of_node, "ds-mux-bus"); |
| 4784 | + if (mux_np) { |
| 4785 | + struct device_node *child; |
| 4786 | + |
| 4787 | + for_each_available_child_of_node(mux_np, child) { |
| 4788 | + if (!of_device_is_compatible(child, |
| 4789 | + "mxl862xx,ds-mux")) |
| 4790 | + continue; |
| 4791 | + |
| 4792 | + if (!of_device_is_available(child)) |
| 4793 | + continue; |
| 4794 | + |
| 4795 | + err = ds_add_mux(priv, child); |
| 4796 | + if (err) |
| 4797 | + dev_err(dev, "failed to add mux\n"); |
| 4798 | + |
| 4799 | + of_node_put(mux_np); |
| 4800 | + }; |
| 4801 | + } |
| 4802 | + |
| 4803 | + return 0; |
4571 | 4804 | } |
4572 | 4805 |
|
4573 | 4806 | static void mxl862xx_remove(struct mdio_device *mdiodev) |
@@ -4597,6 +4830,7 @@ static void mxl862xx_remove(struct mdio_device *mdiodev) |
4597 | 4830 | rtnl_unlock(); |
4598 | 4831 | } |
4599 | 4832 |
|
| 4833 | + mxl862xx_release_all_muxes(ds->priv); |
4600 | 4834 | dsa_unregister_switch(ds); |
4601 | 4835 |
|
4602 | 4836 | mxl862xx_host_shutdown(priv); |
|
0 commit comments