diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/main.c b/drivers/net/ethernet/yunsilicon/xsc/net/main.c
index 6df7ed3bb..65d17d311 100644
--- a/drivers/net/ethernet/yunsilicon/xsc/net/main.c
+++ b/drivers/net/ethernet/yunsilicon/xsc/net/main.c
@@ -1668,6 +1668,134 @@ static int xsc_eth_set_mac(struct net_device *netdev, void *addr)
 	return 0;
 }
 
+static void xsc_eth_rss_params_change(struct xsc_adapter *adapter, u32 change, void *modify)
+{
+	struct xsc_core_device *xdev = adapter->xdev;
+	struct xsc_rss_params *rss = &adapter->rss_param;
+	struct xsc_eth_params *params = &adapter->nic_param;
+	struct xsc_cmd_modify_nic_hca_mbox_in *in =
+		(struct xsc_cmd_modify_nic_hca_mbox_in *)modify;
+	u32 hash_field = 0;
+	int key_len;
+	u8 rss_caps_mask = 0;
+
+	if (xsc_get_user_mode(xdev))
+		return;
+
+	if (change & BIT(XSC_RSS_RXQ_DROP)) {
+		in->rss.rqn_base = cpu_to_be16(adapter->channels.rqn_base -
+				xdev->caps.raweth_rss_qp_id_base);
+		in->rss.rqn_num = 0;
+		rss_caps_mask |= BIT(XSC_RSS_RXQ_DROP);
+		goto rss_caps;
+	}
+
+	if (change & BIT(XSC_RSS_RXQ_UPDATE)) {
+		in->rss.rqn_base = cpu_to_be16(adapter->channels.rqn_base -
+				xdev->caps.raweth_rss_qp_id_base);
+		in->rss.rqn_num = cpu_to_be16(params->num_channels);
+		rss_caps_mask |= BIT(XSC_RSS_RXQ_UPDATE);
+	}
+
+	if (change & BIT(XSC_RSS_HASH_KEY_UPDATE)) {
+		key_len = min(sizeof(in->rss.hash_key), sizeof(rss->toeplitz_hash_key));
+		memcpy(&in->rss.hash_key, rss->toeplitz_hash_key, key_len);
+		rss_caps_mask |= BIT(XSC_RSS_HASH_KEY_UPDATE);
+	}
+
+	if (change & BIT(XSC_RSS_HASH_TEMP_UPDATE)) {
+		hash_field = rss->rx_hash_fields[XSC_TT_IPV4_TCP] |
+				rss->rx_hash_fields[XSC_TT_IPV6_TCP];
+		in->rss.hash_tmpl = cpu_to_be32(hash_field);
+		rss_caps_mask |= BIT(XSC_RSS_HASH_TEMP_UPDATE);
+	}
+
+	if (change & BIT(XSC_RSS_HASH_FUNC_UPDATE)) {
+		in->rss.hfunc = xsc_hash_func_type(rss->hfunc);
+		rss_caps_mask |= BIT(XSC_RSS_HASH_FUNC_UPDATE);
+	}
+
+rss_caps:
+	if (rss_caps_mask) {
+		in->rss.caps_mask = rss_caps_mask;
+		in->rss.rss_en = 1;
+		in->nic.caps_mask = cpu_to_be16(BIT(XSC_TBM_CAP_RSS));
+		in->nic.caps = in->nic.caps_mask;
+	}
+}
+
+static int xsc_eth_modify_nic_hca(struct xsc_adapter *adapter, u32 flags)
+{
+	struct xsc_core_device *xdev = adapter->xdev;
+	struct xsc_cmd_modify_nic_hca_mbox_in in = {};
+	struct xsc_cmd_modify_nic_hca_mbox_out out = {};
+	int err = 0;
+
+	in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_MODIFY_NIC_HCA);
+
+	xsc_eth_rss_params_change(adapter, flags, &in);
+	if (in.rss.caps_mask) {
+		err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+		if (err || out.hdr.status) {
+			xsc_core_err(xdev, "failed!! err=%d, status=%u\n",
+				     err, out.hdr.status);
+			return -ENOEXEC;
+		}
+	}
+
+	return 0;
+}
+
+static int xsc_safe_switch_channels(struct xsc_adapter *adapter,
+				    xsc_eth_fp_preactivate preactivate)
+{
+	struct net_device *netdev = adapter->netdev;
+	int carrier_ok;
+	int ret = 0;
+
+	adapter->status = XSCALE_ETH_DRIVER_CLOSE;
+
+	carrier_ok = netif_carrier_ok(netdev);
+	netif_carrier_off(netdev);
+	ret = xsc_eth_modify_nic_hca(adapter, BIT(XSC_RSS_RXQ_DROP));
+	if (ret)
+		goto close_channels;
+
+	xsc_eth_deactivate_priv_channels(adapter);
+	xsc_eth_close_channels(adapter);
+
+	if (preactivate) {
+		ret = preactivate(adapter);
+		if (ret)
+			goto out;
+	}
+
+	ret = xsc_eth_open_channels(adapter);
+	if (ret)
+		goto close_channels;
+
+	xsc_eth_activate_priv_channels(adapter);
+	ret = xsc_eth_modify_nic_hca(adapter, BIT(XSC_RSS_RXQ_UPDATE));
+	if (ret)
+		goto close_channels;
+
+	adapter->status = XSCALE_ETH_DRIVER_OK;
+
+	goto out;
+
+close_channels:
+	xsc_eth_deactivate_priv_channels(adapter);
+	xsc_eth_close_channels(adapter);
+
+out:
+	if (carrier_ok)
+		netif_carrier_on(netdev);
+	xsc_core_dbg(adapter->xdev, "channels=%d, mtu=%d, err=%d\n",
+		     adapter->nic_param.num_channels,
+		     adapter->nic_param.mtu, ret);
+	return ret;
+}
+
 static int xsc_eth_set_hw_mtu(struct xsc_core_device *xdev, u16 mtu, u16 rx_buf_sz)
 {
 	struct xsc_set_mtu_mbox_in in;
@@ -1693,12 +1821,69 @@ static int xsc_eth_set_hw_mtu(struct xsc_core_device *xdev, u16 mtu, u16 rx_buf_
 	return ret;
 }
 
+static int xsc_eth_nic_mtu_changed(struct xsc_adapter *priv)
+{
+	u32 new_mtu = priv->nic_param.mtu;
+	int ret;
+
+	ret = xsc_eth_set_hw_mtu(priv->xdev, XSC_SW2HW_MTU(new_mtu),
+				 XSC_SW2HW_RX_PKT_LEN(new_mtu));
+
+	return ret;
+}
+
+static int xsc_eth_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	struct xsc_adapter *adapter = netdev_priv(netdev);
+	int old_mtu = netdev->mtu;
+	int ret = 0;
+	int max_buf_len = 0;
+
+	if (new_mtu > netdev->max_mtu || new_mtu < netdev->min_mtu) {
+		netdev_err(netdev, "%s: Bad MTU (%d), valid range is: [%d..%d]\n",
+			   __func__, new_mtu, netdev->min_mtu, netdev->max_mtu);
+		return -EINVAL;
+	}
+
+	if (!xsc_rx_is_linear_skb(new_mtu)) {
+		max_buf_len = adapter->xdev->caps.recv_ds_num * PAGE_SIZE;
+		if (new_mtu > max_buf_len) {
+			netdev_err(netdev, "Bad MTU (%d), max buf len is %d\n",
+				   new_mtu, max_buf_len);
+			return -EINVAL;
+		}
+	}
+	mutex_lock(&adapter->status_lock);
+	adapter->nic_param.mtu = new_mtu;
+	if (adapter->status != XSCALE_ETH_DRIVER_OK) {
+		ret = xsc_eth_nic_mtu_changed(adapter);
+		if (ret)
+			adapter->nic_param.mtu = old_mtu;
+		else
+			netdev->mtu = adapter->nic_param.mtu;
+		goto out;
+	}
+
+	ret = xsc_safe_switch_channels(adapter, xsc_eth_nic_mtu_changed);
+	if (ret)
+		goto out;
+
+	netdev->mtu = adapter->nic_param.mtu;
+
+out:
+	mutex_unlock(&adapter->status_lock);
+	xsc_core_info(adapter->xdev, "mtu change from %d to %d, new_mtu=%d, err=%d\n",
+		      old_mtu, netdev->mtu, new_mtu, ret);
+	return ret;
+}
+
 static const struct net_device_ops xsc_netdev_ops = {
 	.ndo_open		= xsc_eth_open,
 	.ndo_stop		= xsc_eth_close,
 	.ndo_start_xmit		= xsc_eth_xmit_start,
 	.ndo_get_stats64	= xsc_eth_get_stats,
 	.ndo_set_mac_address	= xsc_eth_set_mac,
+	.ndo_change_mtu		= xsc_eth_change_mtu,
 };
 
 static void xsc_eth_build_nic_netdev(struct xsc_adapter *adapter)
diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth.h b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth.h
index 45d8a8cbe..3d0eb95af 100644
--- a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth.h
+++ b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth.h
@@ -53,4 +53,6 @@ struct xsc_adapter {
 	struct xsc_stats *stats;
 };
 
+typedef int (*xsc_eth_fp_preactivate)(struct xsc_adapter *priv);
+
 #endif /* XSC_ETH_H */
