Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f61db23 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: pcie: support power transition by PCIe clients"

parents f31ca4f1 447621f8
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -15,4 +15,25 @@

#include <linux/types.h>

enum msm_pcie_pm_opt {
	MSM_PCIE_SUSPEND,
	MSM_PCIE_RESUME
};

/**
 * msm_pcie_pm_control - control the power state of a PCIe link.
 * @pm_opt:	power management operation
 * @busnr:	bus number of PCIe endpoint
 * @user:	handle of the caller
 * @data:	private data from the caller
 * @options:	options for pm control
 *
 * This function gives PCIe endpoint device drivers the control to change
 * the power state of a PCIe link for their device.
 *
 * Return: 0 on success, negative value on error
 */
int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
			void *data, u32 options);

#endif
+178 −21
Original line number Diff line number Diff line
@@ -271,12 +271,16 @@ static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
	if (!msm_pcie_dev[rc_idx].cfg_access) {
		PCIE_DBG("access denied for %d:0x%02x + 0x%04x[%d]\n",
			 bus->number, devfn, where, size);
		*val = ~0;
		rv = PCIBIOS_DEVICE_NOT_FOUND;
		goto unlock;
	}

	/* check if the link is up for endpoint */
	if (((bus->number == 1) || (bus->number == 3))
		&& (!msm_pcie_is_link_up(rc_idx))) {
			pr_err("%s fail, link down - bus %d devfn %d\n",
				(oper == RD) ? "rd" : "wr", bus->number, devfn);
			*val = ~0;
			rv = PCIBIOS_DEVICE_NOT_FOUND;
			goto unlock;
@@ -291,18 +295,24 @@ static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,

	if (oper == RD) {
		*val = ((rd_val & mask) >> (8 * byte_offset));
		PCIE_DBG("%d:0x%02x + 0x%04x[%d] -> 0x%08x; rd 0x%08x\n",
			bus->number, devfn, where, size, *val, rd_val);
	} else {
		wr_val = (rd_val & ~mask) |
				((*val << (8 * byte_offset)) & mask);
		writel_relaxed(wr_val, config_base + word_offset);
		wmb(); /* ensure config data is written to hardware register */

		PCIE_DBG(
			"%d:0x%02x + 0x%04x[%d] <- 0x%08x; rd 0x%08x val 0x%08x\n",
			bus->number, devfn, where, size, wr_val, rd_val, *val);
	}

unlock:
	spin_unlock_irqrestore(&msm_pcie_dev[rc_idx].cfg_lock,
				msm_pcie_dev[rc_idx].irqsave_flags);
out:
	return 0;
	return rv;
}

static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
@@ -334,6 +344,8 @@ static int msm_pcie_gpio_init(u32 rc_idx)
	int rc, i;
	struct msm_pcie_gpio_info_t *info;

	PCIE_DBG("\n");

	for (i = 0; i < msm_pcie_dev[rc_idx].gpio_n; i++) {
		info = &msm_pcie_dev[rc_idx].gpio[i];

@@ -379,6 +391,8 @@ static int msm_pcie_vreg_init(u32 rc_idx, struct device *dev)
	struct regulator *vreg;
	struct msm_pcie_vreg_info_t *info;

	PCIE_DBG("\n");

	if (pcie_drv.vreg_on) {
		PCIE_DBG("Vreg has been turned on.\n");
		return rc;
@@ -450,6 +464,8 @@ static int msm_pcie_clk_init(u32 rc_idx, struct device *dev)
	int i, rc = 0;
	struct msm_pcie_clk_info_t *info;

	PCIE_DBG("\n");

	if (dev) {
		PCIE_DBG("dev name:%s\n", dev_name(dev));
	} else {
@@ -508,6 +524,8 @@ static int msm_pcie_pipe_clk_init(u32 rc_idx, struct device *dev)
	int i, rc = 0;
	struct msm_pcie_clk_info_t *info;

	PCIE_DBG("\n");

	if (dev) {
		PCIE_DBG("dev name:%s\n", dev_name(dev));
	} else {
@@ -560,6 +578,8 @@ static void msm_pcie_config_controller(u32 rc_idx)
	struct resource *axi_conf = dev->res[MSM_PCIE_RES_CONF].resource;
	u32 dev_conf, upper, lower, limit;

	PCIE_DBG("\n");

	if (IS_ENABLED(CONFIG_ARM_LPAE)) {
		lower = PCIE_LOWER_ADDR(axi_conf->start);
		upper = PCIE_UPPER_ADDR(axi_conf->start);
@@ -625,6 +645,8 @@ static int msm_pcie_get_resources(u32 rc_idx, struct platform_device *pdev)
	struct msm_pcie_irq_info_t *irq_info;
	struct msm_pcie_dev_t *dev = &msm_pcie_dev[rc_idx];

	PCIE_DBG("\n");

	for (i = 0; i < MSM_PCIE_MAX_VREG; i++) {
		vreg_info = &dev->vreg[i];
		vreg_info->hdl =
@@ -762,6 +784,8 @@ static int msm_pcie_enable(u32 rc_idx, u32 options)
	uint32_t val;
	long int retries = 0;

	PCIE_DBG("\n");

	mutex_lock(&setup_lock);

	dev = &msm_pcie_dev[rc_idx];
@@ -894,6 +918,8 @@ void msm_pcie_disable(u32 rc_idx, u32 options)
{
	struct msm_pcie_dev_t *dev = &msm_pcie_dev[rc_idx];

	PCIE_DBG("\n");

	gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
				dev->gpio[MSM_PCIE_GPIO_PERST].on);

@@ -990,6 +1016,8 @@ static int msm_pcie_probe(struct platform_device *pdev)
	int rc_idx;
	struct msm_pcie_dev_t *dev;

	PCIE_DBG("\n");

	mutex_lock(&pcie_drv.drv_lock);

	ret = of_property_read_u32((&pdev->dev)->of_node,
@@ -1029,6 +1057,8 @@ static int msm_pcie_probe(struct platform_device *pdev)
	msm_pcie_dev[rc_idx].parf_deemph = 0;
	msm_pcie_dev[rc_idx].parf_swing = 0;
	msm_pcie_dev[rc_idx].link_status = MSM_PCIE_LINK_DEINIT;
	msm_pcie_dev[rc_idx].user_suspend = false;
	msm_pcie_dev[rc_idx].saved_state = NULL;
	memcpy(msm_pcie_dev[rc_idx].vreg, msm_pcie_vreg_info,
				sizeof(msm_pcie_vreg_info));
	memcpy(msm_pcie_dev[rc_idx].gpio, msm_pcie_gpio_info,
@@ -1073,6 +1103,9 @@ static int msm_pcie_probe(struct platform_device *pdev)
		pci_common_init(&msm_pci);
	}

	msm_pcie_dev[rc_idx].dev = pci_get_device(PCIE_VENDOR_ID_RCP,
					PCIE_DEVICE_ID_RCP, NULL);

	PCIE_DBG("PCIE probed %s\n", dev_name(&(pdev->dev)));
	mutex_unlock(&pcie_drv.drv_lock);
	return 0;
@@ -1173,17 +1206,34 @@ static void msm_pcie_fixup_early(struct pci_dev *dev)
DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
			msm_pcie_fixup_early);

/* enable wake_n interrupt during suspend */
static void msm_pcie_fixup_suspend(struct pci_dev *dev)
/* Suspend the PCIe link */
static int msm_pcie_pm_suspend(struct pci_dev *dev, u32 rc_idx,
			void *user, void *data, u32 options)
{
	int ret = 0;
	uint32_t val = 0;
	u32 rc_idx = pcie_drv.current_rc; /* need to be replaced later */
	u32 val = 0;
	int ret_l23;

	if (msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_ENABLED)
		return;
	if (dev) {
		ret = pci_save_state(dev);
		if (msm_pcie_dev[rc_idx].user_suspend)
			msm_pcie_dev[rc_idx].saved_state =
						pci_store_saved_state(dev);
	}
	if (ret) {
		pr_err("PCIe: fail to save state of RC 0x%p:%d.\n",
				dev, ret);
		return ret;
	}

	pci_save_state(dev);
	if (user) {
		ret = pci_save_state((struct pci_dev *) user);
		if (ret) {
			pr_err("PCIe: fail to save state of EP 0x%p:%d.\n",
					user, ret);
			return ret;
		}
	}

	spin_lock_irqsave(&msm_pcie_dev[rc_idx].cfg_lock,
				msm_pcie_dev[rc_idx].irqsave_flags);
@@ -1196,11 +1246,11 @@ static void msm_pcie_fixup_suspend(struct pci_dev *dev)

	PCIE_DBG("PME_TURNOFF_MSG is sent out\n");

	ret = readl_poll_timeout((msm_pcie_dev[rc_idx].parf
	ret_l23 = readl_poll_timeout((msm_pcie_dev[rc_idx].parf
		+ PCIE20_PARF_PM_STTS), val, (val & BIT(6)), 10000, 100000);

	/* check L23_Ready */
	if (!ret)
	if (!ret_l23)
		PCIE_DBG("PM_Enter_L23 is received\n");
	else
		PCIE_DBG("PM_Enter_L23 is NOT received\n");
@@ -1208,22 +1258,33 @@ static void msm_pcie_fixup_suspend(struct pci_dev *dev)
	msm_pcie_disable(rc_idx, PM_PIPE_CLK | PM_CLK | PM_VREG);

	PCIE_DBG("enabling wake_n\n");

	enable_irq(msm_pcie_dev[rc_idx].wake_n);

	return ret;
}
DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
			  msm_pcie_fixup_suspend);

/* disable wake_n interrupt when system is not in suspend */
void msm_pcie_fixup_resume_early(struct pci_dev *dev)
static void msm_pcie_fixup_suspend(struct pci_dev *dev)
{
	u32 rc_idx = pcie_drv.current_rc; /* need to be replaced later */
	int ret;
	u32 rc_idx = pcie_drv.current_rc;

	if (msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_DISABLED)
	if (msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_ENABLED)
		return;

	PCIE_DBG("disabling wake_n\n");
	ret = msm_pcie_pm_suspend(dev, rc_idx, NULL, NULL, 0);
	if (ret)
		pr_err("PCIe: got failure in suspend:%d.\n", ret);
}
DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
			  msm_pcie_fixup_suspend);

/* Resume the PCIe link */
static int msm_pcie_pm_resume(struct pci_dev *dev, u32 rc_idx,
			void *user, void *data, u32 options)
{
	int ret;

	PCIE_DBG("disabling wake_n\n");
	disable_irq(msm_pcie_dev[rc_idx].wake_n);

	spin_lock_irqsave(&msm_pcie_dev[rc_idx].cfg_lock,
@@ -1232,13 +1293,43 @@ void msm_pcie_fixup_resume_early(struct pci_dev *dev)
	spin_unlock_irqrestore(&msm_pcie_dev[rc_idx].cfg_lock,
				msm_pcie_dev[rc_idx].irqsave_flags);

	if (msm_pcie_enable(rc_idx, PM_PIPE_CLK | PM_CLK | PM_VREG))
		pr_err("PCIe:failed to enable PCIe link in resume\n");
	else
	ret = msm_pcie_enable(rc_idx, PM_PIPE_CLK | PM_CLK | PM_VREG);
	if (ret) {
		pr_err("PCIe:fail to enable PCIe link in resume\n");
		return ret;
	} else {
		PCIE_DBG("dev->bus->number = %d dev->bus->primary = %d\n",
			 dev->bus->number, dev->bus->primary);

		if (msm_pcie_dev[rc_idx].user_suspend)
			pci_load_and_free_saved_state(dev,
					&msm_pcie_dev[rc_idx].saved_state);

		pci_restore_state(dev);

		if (user)
			pci_restore_state((struct pci_dev *) user);
	}

	return ret;
}

void msm_pcie_fixup_resume_early(struct pci_dev *dev)
{
	int ret;
	u32 rc_idx = pcie_drv.current_rc;

	if ((msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_DISABLED) ||
		msm_pcie_dev[rc_idx].user_suspend)
		return;

	ret = msm_pcie_pm_resume(dev, rc_idx, NULL, NULL, 0);
	if (ret)
		pr_err("PCIe: got failure in resume:%d.\n", ret);
}
DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
				 msm_pcie_fixup_resume_early);

static void msm_pcie_fixup_final(struct pci_dev *dev)
{
	PCIE_DBG("\n");
@@ -1248,3 +1339,69 @@ static void msm_pcie_fixup_final(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, msm_pcie_fixup_final);

int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
			void *data, u32 options)
{
	int ret = 0;
	struct pci_dev *dev;
	u32 rc_idx;

	switch (busnr) {
	case 1:
		if (pcie_drv.rc_expected > 1)
			rc_idx = 0;
		else
			rc_idx = pcie_drv.current_rc;
		break;
	case 3:
		if (pcie_drv.rc_expected > 1)
			rc_idx = 1;
		else
			rc_idx = pcie_drv.current_rc;
		break;
	default:
		pr_err("PCIe: unsupported bus number.\n");
		ret = PCIBIOS_DEVICE_NOT_FOUND;
		goto out;
	}

	dev = msm_pcie_dev[rc_idx].dev;

	switch (pm_opt) {
	case MSM_PCIE_SUSPEND:
		if (msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_ENABLED) {
			pr_err(
				"PCIe: requested to suspend when link is not enabled:%d.\n",
				msm_pcie_dev[rc_idx].link_status);
			break;
		}
		msm_pcie_dev[rc_idx].user_suspend = true;
		ret = msm_pcie_pm_suspend(dev, rc_idx, user, data, options);
		if (ret) {
			pr_err("PCIe: user failed to suspend the link.\n");
			msm_pcie_dev[rc_idx].user_suspend = false;
		}
		break;
	case MSM_PCIE_RESUME:
		if (msm_pcie_dev[rc_idx].link_status !=
					MSM_PCIE_LINK_DISABLED) {
			pr_err(
				"PCIe: requested to resume when link is not disabled:%d.\n",
				msm_pcie_dev[rc_idx].link_status);
			break;
		}
		ret = msm_pcie_pm_resume(dev, rc_idx, user, data, options);
		if (ret)
			pr_err("PCIe: user failed to resume the link.\n");
		else
			msm_pcie_dev[rc_idx].user_suspend = false;
		break;
	default:
		pr_err("PCIe: unsupported pm operation:%d.\n", pm_opt);
		ret = -ENODEV;
		goto out;
	}

out:
	return ret;
}
+3 −1
Original line number Diff line number Diff line
@@ -125,7 +125,7 @@ struct msm_pcie_irq_info_t {
/* msm pcie device structure */
struct msm_pcie_dev_t {
	struct platform_device       *pdev;

	struct pci_dev *dev;
	struct regulator *gdsc;
	struct msm_pcie_vreg_info_t  vreg[MSM_PCIE_MAX_VREG];
	struct msm_pcie_gpio_info_t  gpio[MSM_PCIE_MAX_GPIO];
@@ -160,6 +160,8 @@ struct msm_pcie_dev_t {
	DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_NR_IRQS);

	enum msm_pcie_link_status    link_status;
	bool                         user_suspend;
	struct pci_saved_state	     *saved_state;
};

extern void msm_pcie_config_msi_controller(struct msm_pcie_dev_t *dev);