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

Commit 50e944c5 authored by Rama Krishna Phani A's avatar Rama Krishna Phani A Committed by Yan He
Browse files

msm: ep_pcie: add support for BME in IRQ mode



PCIe EP driver currently polls on BME bit for 30 seconds to be set.
Sometimes host might take a long time to boot up, which might cause
the failure for this bit to get set by host during EP's polling and
cause link training failure on PCIe EP side. Add the support for
checking BME in IRQ mode.

Change-Id: Icada9c465e803601d1b42a970871ffb302580720
Signed-off-by: default avatarRama Krishna Phani A <rphani@codeaurora.org>
parent 8054a977
Loading
Loading
Loading
Loading
+1 −6
Original line number Diff line number Diff line
@@ -110,12 +110,7 @@ int ep_pcie_register_event(struct ep_pcie_hw *phandle,
	if (phandle)
		return phandle->register_event(reg);

	if (ep_pcie_dev.perst_enum)
	return ep_pcie_core_register_event(reg);

	pr_err("ep_pcie:%s: the input driver handle is NULL.",
		__func__);
	return -EINVAL;
}
EXPORT_SYMBOL(ep_pcie_register_event);

+3 −0
Original line number Diff line number Diff line
@@ -210,6 +210,7 @@ enum ep_pcie_irq {
	EP_PCIE_INT_LINK_UP,
	EP_PCIE_INT_LINK_DOWN,
	EP_PCIE_INT_BRIDGE_FLUSH_N,
	EP_PCIE_INT_BME,
	EP_PCIE_INT_GLOBAL,
	EP_PCIE_MAX_IRQ,
};
@@ -309,6 +310,7 @@ struct ep_pcie_dev_t {
	unsigned long                isr_save_flags;
	ulong                        linkdown_counter;
	ulong                        linkup_counter;
	ulong                        bme_counter;
	ulong                        pm_to_counter;
	ulong                        d0_counter;
	ulong                        d3_counter;
@@ -331,6 +333,7 @@ struct ep_pcie_dev_t {

	struct ep_pcie_register_event *event_reg;
	struct work_struct	     handle_perst_work;
	struct work_struct           handle_bme_work;
};

extern struct ep_pcie_dev_t ep_pcie_dev;
+144 −53
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ static const struct ep_pcie_irq_info_t ep_pcie_irq_info[EP_PCIE_MAX_IRQ] = {
	{"int_link_up",	0},
	{"int_link_down",	0},
	{"int_bridge_flush_n",	0},
	{"int_bme",	0},
	{"int_global",	0}
};

@@ -982,19 +983,47 @@ static void ep_pcie_release_resources(struct ep_pcie_dev_t *dev)
	dev->phy = NULL;
	dev->mmio = NULL;
	dev->msi = NULL;
}

	if (dev->bus_client) {
		msm_bus_scale_unregister_client(dev->bus_client);
		dev->bus_client = 0;
static void ep_pcie_enumeration_complete(struct ep_pcie_dev_t *dev)
{
	dev->enumerated = true;
	dev->link_status = EP_PCIE_LINK_ENABLED;

	if (dev->gpio[EP_PCIE_GPIO_MDM2AP].num) {
		/* assert MDM2AP Status GPIO */
		EP_PCIE_DBG2(dev, "PCIe V%d: assert MDM2AP Status.\n",
				dev->rev);
		EP_PCIE_DBG(dev,
			"PCIe V%d: MDM2APStatus GPIO initial:%d.\n",
			dev->rev,
			gpio_get_value(
			dev->gpio[EP_PCIE_GPIO_MDM2AP].num));
		gpio_set_value(dev->gpio[EP_PCIE_GPIO_MDM2AP].num,
			dev->gpio[EP_PCIE_GPIO_MDM2AP].on);
		EP_PCIE_DBG(dev,
			"PCIe V%d: MDM2APStatus GPIO after assertion:%d.\n",
			dev->rev,
			gpio_get_value(
			dev->gpio[EP_PCIE_GPIO_MDM2AP].num));
	}

	hw_drv.device_id = readl_relaxed(dev->dm_core);
	EP_PCIE_DBG(&ep_pcie_dev,
		"PCIe V%d: register driver for device 0x%x.\n",
		ep_pcie_dev.rev, hw_drv.device_id);
	ep_pcie_register_drv(&hw_drv);
	ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP);

	return;
}

int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt)
{
	int ret = 0;
	u32 val;
	u32 retries;
	u32 bme;
	u32 val = 0;
	u32 retries = 0;
	u32 bme = 0;
	bool ltssm_en = false;
	struct ep_pcie_dev_t *dev = &ep_pcie_dev;

@@ -1222,8 +1251,10 @@ checkbme:
					0x7FFFE000);
	}

	if (!(opt & EP_PCIE_OPT_ENUM_ASYNC)) {
		/* Wait for up to 1000ms for BME to be set */
		retries = 0;

		bme = readl_relaxed(dev->dm_core +
		PCIE20_COMMAND_STATUS) & BIT(2);
		while (!bme && (retries < BME_CHECK_MAX_COUNT)) {
@@ -1232,36 +1263,25 @@ checkbme:
			bme = readl_relaxed(dev->dm_core +
				PCIE20_COMMAND_STATUS) & BIT(2);
		}
	} else {
		EP_PCIE_DBG(dev,
			"PCIe V%d: EP_PCIE_OPT_ENUM_ASYNC is true.\n",
			dev->rev);
	}

	if (bme) {
		dev->link_status = EP_PCIE_LINK_ENABLED;
		EP_PCIE_DBG(dev,
			"PCIe V%d: PCIe link is up and BME is enabled after %d checkings (%d ms).\n",
			dev->rev, retries,
			BME_TIMEOUT_US_MIN * retries / 1000);

		if (dev->gpio[EP_PCIE_GPIO_MDM2AP].num) {
			/* assert MDM2AP Status GPIO */
			EP_PCIE_DBG2(dev, "PCIe V%d: assert MDM2AP Status .\n",
				dev->rev);
			EP_PCIE_DBG(dev,
				"PCIe V%d: MDM2APStatus GPIO initial:%d.\n",
				dev->rev,
				gpio_get_value(
					dev->gpio[EP_PCIE_GPIO_MDM2AP].num));
			gpio_set_value(dev->gpio[EP_PCIE_GPIO_MDM2AP].num,
					dev->gpio[EP_PCIE_GPIO_MDM2AP].on);
			EP_PCIE_DBG(dev,
				"PCIe V%d: MDM2APStatus GPIO after assertion:%d.\n",
				dev->rev,
				gpio_get_value(
					dev->gpio[EP_PCIE_GPIO_MDM2AP].num));
		}
		ep_pcie_enumeration_complete(dev);
	} else {
		if (!(opt & EP_PCIE_OPT_ENUM_ASYNC))
			EP_PCIE_ERR(dev,
				"PCIe V%d: PCIe link is up but BME is still disabled after max waiting time.\n",
				dev->rev);
		if (!ep_pcie_debug_keep_resource) {
		if (!ep_pcie_debug_keep_resource &&
				!(opt&EP_PCIE_OPT_ENUM_ASYNC)) {
			ret = EP_PCIE_ERROR;
			dev->link_status = EP_PCIE_LINK_DISABLED;
			goto link_fail;
@@ -1363,6 +1383,38 @@ int ep_pcie_core_mask_irq_event(enum ep_pcie_irq_event event,
	return rc;
}

static irqreturn_t ep_pcie_handle_bme_irq(int irq, void *data)
{
	struct ep_pcie_dev_t *dev = data;
	unsigned long irqsave_flags;

	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);

	dev->bme_counter++;
	EP_PCIE_DBG(dev,
		"PCIe V%d: No. %ld BME IRQ.\n", dev->rev, dev->bme_counter);

	if (readl_relaxed(dev->dm_core + PCIE20_COMMAND_STATUS) & BIT(2)) {
		/* BME has been enabled */
		if (!dev->enumerated) {
			EP_PCIE_DBG(dev,
				"PCIe V%d:BME is set. Enumeration is complete\n",
				dev->rev);
			schedule_work(&dev->handle_bme_work);
		} else {
			EP_PCIE_DBG(dev,
				"PCIe V%d:BME is set again after the enumeration has completed\n",
				dev->rev);
		}
	} else {
		EP_PCIE_DBG(dev,
				"PCIe V%d:BME is still disabled\n", dev->rev);
	}

	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
	return IRQ_HANDLED;
}

static irqreturn_t ep_pcie_handle_linkdown_irq(int irq, void *data)
{
	struct ep_pcie_dev_t *dev = data;
@@ -1497,19 +1549,19 @@ static int ep_pcie_enumeration(struct ep_pcie_dev_t *dev)
			"PCIe V%d: PCIe link enumeration failed.\n",
			ep_pcie_dev.rev);
	} else {
		if (dev->link_status == EP_PCIE_LINK_ENABLED) {
			EP_PCIE_INFO(&ep_pcie_dev,
				"PCIe V%d: PCIe link enumeration is successful with host side.\n",
				ep_pcie_dev.rev);

		dev->enumerated = true;

		hw_drv.device_id = readl_relaxed(dev->dm_core);
		EP_PCIE_DBG(&ep_pcie_dev,
			"PCIe V%d: register driver for device 0x%x.\n",
			ep_pcie_dev.rev, hw_drv.device_id);
		ep_pcie_register_drv(&hw_drv);

		ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP);
		} else if (dev->link_status == EP_PCIE_LINK_UP) {
			EP_PCIE_INFO(&ep_pcie_dev,
				"PCIe V%d: PCIe link training is successful with host side. Waiting for enumeration to complete.\n",
				ep_pcie_dev.rev);
		} else {
			EP_PCIE_ERR(&ep_pcie_dev,
				"PCIe V%d: PCIe link is in the unexpected status: %d\n",
				ep_pcie_dev.rev, dev->link_status);
		}
	}

	return ret;
@@ -1523,6 +1575,14 @@ static void handle_perst_func(struct work_struct *work)
	ep_pcie_enumeration(dev);
}

static void handle_bme_func(struct work_struct *work)
{
	struct ep_pcie_dev_t *dev = container_of(work,
			struct ep_pcie_dev_t, handle_bme_work);

	ep_pcie_enumeration_complete(dev);
}

static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data)
{
	struct ep_pcie_dev_t *dev = data;
@@ -1595,7 +1655,7 @@ static irqreturn_t ep_pcie_handle_global_irq(int irq, void *data)
				EP_PCIE_DUMP(dev,
					"PCIe V%d: handle BME event.\n",
					dev->rev);
				dev->link_status = EP_PCIE_LINK_ENABLED;
				ep_pcie_handle_bme_irq(irq, data);
				break;
			case EP_PCIE_INT_EVT_PM_TURNOFF:
				EP_PCIE_DUMP(dev,
@@ -1640,6 +1700,10 @@ int32_t ep_pcie_irq_init(struct ep_pcie_dev_t *dev)

	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);

	/* Initialize all works to be performed before registering for IRQs*/
	INIT_WORK(&dev->handle_perst_work, handle_perst_func);
	INIT_WORK(&dev->handle_bme_work, handle_bme_func);

	if (dev->aggregated_irq) {
		ret = devm_request_irq(pdev,
			dev->irq[EP_PCIE_INT_GLOBAL].num,
@@ -1653,12 +1717,41 @@ int32_t ep_pcie_irq_init(struct ep_pcie_dev_t *dev)
			return ret;
		}

		ret = enable_irq_wake(dev->irq[EP_PCIE_INT_GLOBAL].num);
		if (ret) {
			EP_PCIE_ERR(dev,
				"PCIe V%d: Unable to enable wake for Global interrupt\n",
				dev->rev);
			return ret;
		}

		EP_PCIE_DBG(dev,
			"PCIe V%d: request global interrupt %d\n",
			dev->rev, dev->irq[EP_PCIE_INT_GLOBAL].num);
		goto perst_irq;
	}

	/* register handler for BME interrupt */
	ret = devm_request_irq(pdev,
		dev->irq[EP_PCIE_INT_BME].num,
		ep_pcie_handle_bme_irq,
		IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_BME].name,
		dev);
	if (ret) {
		EP_PCIE_ERR(dev,
			"PCIe V%d: Unable to request BME interrupt %d\n",
			dev->rev, dev->irq[EP_PCIE_INT_BME].num);
		return ret;
	}

	ret = enable_irq_wake(dev->irq[EP_PCIE_INT_BME].num);
	if (ret) {
		EP_PCIE_ERR(dev,
			"PCIe V%d: Unable to enable wake for BME interrupt\n",
			dev->rev);
		return ret;
	}

	/* register handler for linkdown interrupt */
	ret = devm_request_irq(pdev,
		dev->irq[EP_PCIE_INT_LINK_DOWN].num,
@@ -1732,8 +1825,6 @@ perst_irq:
		return ret;
	}

	INIT_WORK(&dev->handle_perst_work, handle_perst_func);

	return 0;
}

+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015,2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -60,6 +60,7 @@ enum ep_pcie_options {
	EP_PCIE_OPT_AST_WAKE = 0x1,
	EP_PCIE_OPT_POWER_ON = 0x2,
	EP_PCIE_OPT_ENUM = 0x4,
	EP_PCIE_OPT_ENUM_ASYNC = 0x8,
	EP_PCIE_OPT_ALL = 0xFFFFFFFF,
};