[][kernel][mt7988][pcie][Add soft off and soft on API]

[Description]
Add soft off and soft on API

[Release-log]
NA


Change-Id: Iab24bf39bca084a7c1e7ab061eb9f842035f7a5f
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9185179
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c
index 6b50c0c..dcc67f1 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c
@@ -196,6 +196,7 @@
 	struct mtk_msi_set msi_sets[PCIE_MSI_SET_NUM];
 	enum mtk_msi_group_type msi_group_type;
 	struct mutex lock;
+	bool soft_off;
 	DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM);
 };
 
@@ -234,6 +235,11 @@
 static int mtk_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
 				int where, int size, u32 *val)
 {
+	struct mtk_pcie_port *port = bus->sysdata;
+
+	if (port->soft_off)
+		return 0;
+
 	mtk_pcie_config_tlp_header(bus, devfn, where, size);
 
 	return pci_generic_config_read32(bus, devfn, where, size, val);
@@ -242,6 +248,11 @@
 static int mtk_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
 				 int where, int size, u32 val)
 {
+	struct mtk_pcie_port *port = bus->sysdata;
+
+	if (port->soft_off)
+		return 0;
+
 	mtk_pcie_config_tlp_header(bus, devfn, where, size);
 
 	if (size <= 2)
@@ -1256,6 +1267,95 @@
 				   PCIE_LTSSM_STATE_L2_IDLE), 20,
 				   50 * USEC_PER_MSEC);
 }
+
+int mtk_pcie_soft_off(struct pci_bus *bus)
+{
+	struct pci_host_bridge *host;
+	struct mtk_pcie_port *port;
+	struct pci_dev *dev;
+	int ret;
+	u32 val;
+
+	if (!bus) {
+		dev_err(port->dev, "There is no bus, please check the host driver\n");
+		return -ENODEV;
+	}
+
+	port = bus->sysdata;
+	if (port->soft_off) {
+		dev_err(port->dev, "The soft_off is true, can't soft off\n");
+		return -EPERM;
+	}
+
+	host = pci_host_bridge_from_priv(port);
+	dev = pci_get_slot(host->bus, 0);
+	if (!dev) {
+		dev_err(port->dev, "Failed to get device from bus\n");
+		return -ENODEV;
+	}
+
+	/* Trigger link to L2 state */
+	ret = mtk_pcie_turn_off_link(port);
+
+	pci_save_state(dev);
+	pci_dev_put(dev);
+	mtk_pcie_irq_save(port);
+	port->soft_off = true;
+	mtk_pcie_power_down(port);
+
+	dev_info(port->dev, "mtk pcie soft off done\n");
+
+	return ret;
+}
+EXPORT_SYMBOL(mtk_pcie_soft_off);
+
+int mtk_pcie_soft_on(struct pci_bus *bus)
+{
+	struct pci_host_bridge *host;
+	struct mtk_pcie_port *port;
+	struct pci_dev *dev;
+	int ret;
+
+	if (!bus) {
+		dev_err(port->dev, "There is no bus, please check the host driver\n");
+		return -ENODEV;
+	}
+
+	port = bus->sysdata;
+	if (!port->soft_off) {
+		dev_err(port->dev, "The soft_off is false, can't soft on\n");
+		return -EPERM;
+	}
+
+	host = pci_host_bridge_from_priv(port);
+	dev = pci_get_slot(host->bus, 0);
+	if (!dev) {
+		dev_err(port->dev, "Failed to get device from bus\n");
+		return -ENODEV;
+	}
+
+	ret = mtk_pcie_power_up(port);
+	if (ret) {
+		dev_err(port->dev, "Failed to power up RC\n");
+		return ret;
+	}
+
+	ret = mtk_pcie_startup_port(port);
+	if (ret) {
+		dev_err(port->dev, "Failed to detect EP\n");
+		return ret;
+	}
+
+	port->soft_off = false;
+	mtk_pcie_irq_restore(port);
+	pci_restore_state(dev);
+	pci_dev_put(dev);
+
+	dev_info(port->dev, "mtk pcie soft on done\n");
+
+	return ret;
+}
+EXPORT_SYMBOL(mtk_pcie_soft_on);
 
 static int __maybe_unused mtk_pcie_suspend_noirq(struct device *dev)
 {