|
19 | 19 | #include <net/dsa.h> |
20 | 20 | #include <linux/dsa/8021q.h> |
21 | 21 | #include <linux/stddef.h> |
| 22 | +#include <linux/gpio/consumer.h> |
| 23 | +#include <linux/of_net.h> |
22 | 24 |
|
23 | 25 | #include "mxl862xx.h" |
24 | 26 | #include "mxl862xx-api.h" |
@@ -3696,12 +3698,221 @@ static const struct dsa_switch_ops mxl862xx_switch_ops = { |
3696 | 3698 | .setup = mxl862xx_setup, |
3697 | 3699 | }; |
3698 | 3700 |
|
| 3701 | +static void sfp_monitor_work_func(struct work_struct *work) |
| 3702 | +{ |
| 3703 | + struct combo_port_mux *mux = container_of(work, struct combo_port_mux, sfp_monitor_work.work); |
| 3704 | + struct dsa_switch *ds = mux->dp->ds; |
| 3705 | + struct dsa_port *dp = mux->dp; |
| 3706 | + struct net_device *dev = mux->dp->user; |
| 3707 | + unsigned int new_channel; |
| 3708 | + int sfp_present; |
| 3709 | + |
| 3710 | + if (IS_ERR(mux->mod_def0_gpio) || IS_ERR(mux->chan_sel_gpio)) |
| 3711 | + goto reschedule; |
| 3712 | + |
| 3713 | + if (!netif_running(dev)) |
| 3714 | + goto reschedule; |
| 3715 | + |
| 3716 | + sfp_present = gpiod_get_value_cansleep(mux->mod_def0_gpio); |
| 3717 | + new_channel = sfp_present ? mux->sfp_present_channel : !mux->sfp_present_channel; |
| 3718 | + |
| 3719 | + if (mux->initialized && mux->channel == new_channel) |
| 3720 | + goto reschedule; |
| 3721 | + |
| 3722 | + rtnl_lock(); |
| 3723 | + |
| 3724 | + phylink_stop(dp->pl); |
| 3725 | + phylink_disconnect_phy(dp->pl); |
| 3726 | + |
| 3727 | + dp->dn = mux->data[new_channel]->of_node; |
| 3728 | + dp->pl = mux->data[new_channel]->phylink; |
| 3729 | + |
| 3730 | + phylink_of_phy_connect(dp->pl, dp->dn, 0); |
| 3731 | + phylink_start(dp->pl); |
| 3732 | + |
| 3733 | + dev_info(ds->dev, "dsa mux: switch to channel%d\n", new_channel); |
| 3734 | + |
| 3735 | + gpiod_set_value_cansleep(mux->chan_sel_gpio, new_channel); |
| 3736 | + |
| 3737 | + rtnl_unlock(); |
| 3738 | + |
| 3739 | + mux->channel = new_channel; |
| 3740 | + mux->initialized = true; |
| 3741 | + |
| 3742 | +reschedule: |
| 3743 | + mod_delayed_work(system_wq, &mux->sfp_monitor_work, msecs_to_jiffies(100)); |
| 3744 | +} |
| 3745 | + |
| 3746 | +static int ds_add_mux_channel(struct combo_port_mux *mux, struct device_node *np) |
| 3747 | +{ |
| 3748 | + const __be32 *_id = of_get_property(np, "reg", NULL); |
| 3749 | + struct dsa_switch *ds = mux->dp->ds; |
| 3750 | + struct dp_mux_data *data; |
| 3751 | + struct phylink *phylink; |
| 3752 | + phy_interface_t phy_mode; |
| 3753 | + int id, err; |
| 3754 | + |
| 3755 | + if (!_id) { |
| 3756 | + dev_err(ds->dev, "missing mux channel id\n"); |
| 3757 | + return -EINVAL; |
| 3758 | + } |
| 3759 | + |
| 3760 | + id = be32_to_cpup(_id); |
| 3761 | + if (id < 0 || id > 1) { |
| 3762 | + dev_err(ds->dev, "%d is not a valid mux channel id\n", id); |
| 3763 | + return -EINVAL; |
| 3764 | + } |
| 3765 | + |
| 3766 | + data = kmalloc(sizeof(*data), GFP_KERNEL); |
| 3767 | + if (unlikely(!data)) { |
| 3768 | + dev_err(ds->dev, "failed to create mux data structure\n"); |
| 3769 | + return -ENOMEM; |
| 3770 | + } |
| 3771 | + |
| 3772 | + err = of_get_phy_mode(np, &phy_mode); |
| 3773 | + if (err) { |
| 3774 | + dev_err(ds->dev, "incorrect phy-mode\n"); |
| 3775 | + goto err_free_data; |
| 3776 | + } |
| 3777 | + |
| 3778 | + phylink = phylink_create(&mux->dp->pl_config, |
| 3779 | + of_fwnode_handle(np), |
| 3780 | + phy_mode, ds->phylink_mac_ops); |
| 3781 | + if (IS_ERR(phylink)) { |
| 3782 | + dev_err(ds->dev, "failed to create phylink structure\n"); |
| 3783 | + err = PTR_ERR(phylink); |
| 3784 | + goto err_free_data; |
| 3785 | + } |
| 3786 | + |
| 3787 | + data->of_node = np; |
| 3788 | + data->phylink = phylink; |
| 3789 | + mux->data[id] = data; |
| 3790 | + |
| 3791 | + return 0; |
| 3792 | + |
| 3793 | +err_free_data: |
| 3794 | + kfree(data); |
| 3795 | + return err; |
| 3796 | +} |
| 3797 | + |
| 3798 | +static int ds_add_mux(struct mxl862xx_priv *priv, struct device_node *np) |
| 3799 | +{ |
| 3800 | + const __be32 *_id = of_get_property(np, "reg", NULL); |
| 3801 | + struct device_node *child; |
| 3802 | + struct combo_port_mux *mux; |
| 3803 | + unsigned int id; |
| 3804 | + int err; |
| 3805 | + |
| 3806 | + if (!_id) { |
| 3807 | + dev_err(priv->dev, "missing attach dp id\n"); |
| 3808 | + return -EINVAL; |
| 3809 | + } |
| 3810 | + |
| 3811 | + id = be32_to_cpup(_id); |
| 3812 | + if (id < 0 || id >= MAX_PORTS) { |
| 3813 | + dev_err(priv->dev, "%d is not a valid attach dp id\n", id); |
| 3814 | + return -EINVAL; |
| 3815 | + } |
| 3816 | + |
| 3817 | + mux = kmalloc(sizeof(struct combo_port_mux), GFP_KERNEL); |
| 3818 | + if (unlikely(!mux)) { |
| 3819 | + dev_err(priv->dev, "failed to create mux structure\n"); |
| 3820 | + return -ENOMEM; |
| 3821 | + } |
| 3822 | + |
| 3823 | + mux->mod_def0_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), |
| 3824 | + "mod-def0", 0, GPIOD_IN | |
| 3825 | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, "?"); |
| 3826 | + |
| 3827 | + if (IS_ERR(mux->mod_def0_gpio)) { |
| 3828 | + dev_err(priv->dev, "failed to requset gpio for mod-def0\n"); |
| 3829 | + err = PTR_ERR(mux->mod_def0_gpio); |
| 3830 | + goto err_free_mux; |
| 3831 | + } |
| 3832 | + |
| 3833 | + mux->chan_sel_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), |
| 3834 | + "chan-sel", 0, GPIOD_OUT_LOW, "?"); |
| 3835 | + |
| 3836 | + if (IS_ERR(mux->chan_sel_gpio)) { |
| 3837 | + dev_err(priv->dev, "failed to requset gpio for chan-sel\n"); |
| 3838 | + err = PTR_ERR(mux->chan_sel_gpio); |
| 3839 | + goto err_put_mod_def0; |
| 3840 | + } |
| 3841 | + |
| 3842 | + of_property_read_u32(np, "sfp-present-channel", |
| 3843 | + &mux->sfp_present_channel); |
| 3844 | + |
| 3845 | + priv->ds_mux[id] = mux; |
| 3846 | + mux->dp = dsa_to_port(priv->ds, id); |
| 3847 | + /* configure default channel to 10G PHY */ |
| 3848 | + mux->channel = !mux->sfp_present_channel; |
| 3849 | + mux->initialized = false; |
| 3850 | + |
| 3851 | + for_each_child_of_node(np, child) { |
| 3852 | + err = ds_add_mux_channel(mux, child); |
| 3853 | + if (err) { |
| 3854 | + dev_err(priv->dev, "failed to add ds_mux\n"); |
| 3855 | + of_node_put(child); |
| 3856 | + goto err_put_chan_sel; |
| 3857 | + } |
| 3858 | + } |
| 3859 | + |
| 3860 | + INIT_DELAYED_WORK(&mux->sfp_monitor_work, sfp_monitor_work_func); |
| 3861 | + mod_delayed_work(system_wq, &mux->sfp_monitor_work, msecs_to_jiffies(3000)); |
| 3862 | + |
| 3863 | + return 0; |
| 3864 | + |
| 3865 | +err_put_chan_sel: |
| 3866 | + gpiod_put(mux->chan_sel_gpio); |
| 3867 | +err_put_mod_def0: |
| 3868 | + gpiod_put(mux->mod_def0_gpio); |
| 3869 | +err_free_mux: |
| 3870 | + kfree(mux); |
| 3871 | + priv->ds_mux[id] = NULL; |
| 3872 | + return err; |
| 3873 | +} |
| 3874 | + |
| 3875 | +static void mxl862xx_release_mux(struct mxl862xx_priv *priv, int id) |
| 3876 | +{ |
| 3877 | + struct combo_port_mux *mux = priv->ds_mux[id]; |
| 3878 | + int i; |
| 3879 | + |
| 3880 | + if (!mux) |
| 3881 | + return; |
| 3882 | + |
| 3883 | + cancel_delayed_work_sync(&mux->sfp_monitor_work); |
| 3884 | + |
| 3885 | + if (!IS_ERR_OR_NULL(mux->mod_def0_gpio)) |
| 3886 | + gpiod_put(mux->mod_def0_gpio); |
| 3887 | + |
| 3888 | + if (!IS_ERR_OR_NULL(mux->chan_sel_gpio)) |
| 3889 | + gpiod_put(mux->chan_sel_gpio); |
| 3890 | + |
| 3891 | + for (i = 0; i < 2; i++) { |
| 3892 | + if (mux->data[i]) { |
| 3893 | + if (mux->data[i]->phylink) |
| 3894 | + phylink_destroy(mux->data[i]->phylink); |
| 3895 | + kfree(mux->data[i]); |
| 3896 | + } |
| 3897 | + } |
| 3898 | + kfree(mux); |
| 3899 | + priv->ds_mux[id] = NULL; |
| 3900 | +} |
| 3901 | + |
| 3902 | +static void mxl862xx_release_all_muxes(struct mxl862xx_priv *priv) |
| 3903 | +{ |
| 3904 | + int i; |
| 3905 | + for (i = 0; i < MAX_PORTS; i++) |
| 3906 | + mxl862xx_release_mux(priv, i); |
| 3907 | +} |
| 3908 | + |
3699 | 3909 | static int mxl862xx_probe(struct mdio_device *mdiodev) |
3700 | 3910 | { |
3701 | 3911 | struct mxl862xx_sys_fw_image_version fw_version; |
3702 | 3912 | struct device *dev = &mdiodev->dev; |
3703 | 3913 | struct mxl862xx_priv *priv; |
3704 | 3914 | struct dsa_switch *ds; |
| 3915 | + struct device_node *mux_np; |
3705 | 3916 | int ret; |
3706 | 3917 |
|
3707 | 3918 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
@@ -3766,13 +3977,34 @@ static int mxl862xx_probe(struct mdio_device *mdiodev) |
3766 | 3977 | dev_info(dev, "%s:%u: Enable force isolate", __func__, __LINE__); |
3767 | 3978 | } |
3768 | 3979 |
|
| 3980 | + mux_np = of_get_child_by_name(priv->ds->dev->of_node, "ds-mux-bus"); |
| 3981 | + if (mux_np) { |
| 3982 | + struct device_node *child; |
| 3983 | + |
| 3984 | + for_each_available_child_of_node(mux_np, child) { |
| 3985 | + if (!of_device_is_compatible(child, |
| 3986 | + "mxl862xx,ds-mux")) |
| 3987 | + continue; |
| 3988 | + |
| 3989 | + if (!of_device_is_available(child)) |
| 3990 | + continue; |
| 3991 | + |
| 3992 | + ret = ds_add_mux(priv, child); |
| 3993 | + if (ret) |
| 3994 | + dev_err(dev, "failed to add mux\n"); |
| 3995 | + |
| 3996 | + of_node_put(mux_np); |
| 3997 | + }; |
| 3998 | + } |
| 3999 | + |
3769 | 4000 | return 0; |
3770 | 4001 | } |
3771 | 4002 |
|
3772 | 4003 | static void mxl862xx_remove(struct mdio_device *mdiodev) |
3773 | 4004 | { |
3774 | 4005 | struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); |
3775 | 4006 |
|
| 4007 | + mxl862xx_release_all_muxes(ds->priv); |
3776 | 4008 | dsa_unregister_switch(ds); |
3777 | 4009 | } |
3778 | 4010 |
|
|
0 commit comments