Commit d943fa61 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'mhi-for-v6.18' of...

Merge tag 'mhi-for-v6.18' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mani/mhi into char-misc-next

Manivannan writes:

MHI Host
========

- Add support for all Foxconn T99W696 SKU variants

- Fix accessing the uninitialized 'dev' pointer in mhi_init_irq_setup()

- Notify the MHI Execution Environment (EE) change to userspace using uevent

- Add support for Virtual Functions (VFs) in SR-IOV capable QDU100 device from
  Qualcomm. For adding SR-IOV support, MHI pci_generic driver has been modified
  to apply different configurations for PFs and VFs.

MHI Endpoint
============

- Fix the handling of chained transfers in EP MHI driver that leads to reading
  past the host transfer buffers causing IOMMU faults in the host and other
  issues.

* tag 'mhi-for-v6.18' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mani/mhi:
  bus: mhi: host: pci_generic: Set DMA mask for VFs
  bus: mhi: core: Improve mhi_sync_power_up handling for SYS_ERR state
  bus: mhi: host: pci_generic: Reset QDU100 while the MHI driver is removed
  bus: mhi: host: pci_generic: Add SRIOV support
  bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status
  bus: mhi: host: Add support for separate controller configurations for VF and PF
  bus: mhi: ep: Fix chained transfer handling in read path
  bus: mhi: host: Notify EE change via uevent
  bus: mhi: host: Do not use uninitialized 'dev' pointer in mhi_init_irq_setup()
  bus: mhi: host: pci_generic: Add support for all Foxconn T99W696 SKU variants
parents fc3e44e4 54c67740
Loading
Loading
Loading
Loading
+12 −25
Original line number Diff line number Diff line
@@ -403,17 +403,13 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
{
	struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id];
	struct device *dev = &mhi_cntrl->mhi_dev->dev;
	size_t tr_len, read_offset, write_offset;
	size_t tr_len, read_offset;
	struct mhi_ep_buf_info buf_info = {};
	u32 len = MHI_EP_DEFAULT_MTU;
	struct mhi_ring_element *el;
	bool tr_done = false;
	void *buf_addr;
	u32 buf_left;
	int ret;

	buf_left = len;

	do {
		/* Don't process the transfer ring if the channel is not in RUNNING state */
		if (mhi_chan->state != MHI_CH_STATE_RUNNING) {
@@ -426,24 +422,23 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
		/* Check if there is data pending to be read from previous read operation */
		if (mhi_chan->tre_bytes_left) {
			dev_dbg(dev, "TRE bytes remaining: %u\n", mhi_chan->tre_bytes_left);
			tr_len = min(buf_left, mhi_chan->tre_bytes_left);
			tr_len = min(len, mhi_chan->tre_bytes_left);
		} else {
			mhi_chan->tre_loc = MHI_TRE_DATA_GET_PTR(el);
			mhi_chan->tre_size = MHI_TRE_DATA_GET_LEN(el);
			mhi_chan->tre_bytes_left = mhi_chan->tre_size;

			tr_len = min(buf_left, mhi_chan->tre_size);
			tr_len = min(len, mhi_chan->tre_size);
		}

		read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left;
		write_offset = len - buf_left;

		buf_addr = kmem_cache_zalloc(mhi_cntrl->tre_buf_cache, GFP_KERNEL);
		if (!buf_addr)
			return -ENOMEM;

		buf_info.host_addr = mhi_chan->tre_loc + read_offset;
		buf_info.dev_addr = buf_addr + write_offset;
		buf_info.dev_addr = buf_addr;
		buf_info.size = tr_len;
		buf_info.cb = mhi_ep_read_completion;
		buf_info.cb_buf = buf_addr;
@@ -459,16 +454,12 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl,
			goto err_free_buf_addr;
		}

		buf_left -= tr_len;
		mhi_chan->tre_bytes_left -= tr_len;

		if (!mhi_chan->tre_bytes_left) {
			if (MHI_TRE_DATA_GET_IEOT(el))
				tr_done = true;

		if (!mhi_chan->tre_bytes_left)
			mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size;
		}
	} while (buf_left && !tr_done);
	/* Read until the some buffer is left or the ring becomes not empty */
	} while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));

	return 0;

@@ -502,15 +493,11 @@ static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring)
		mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
	} else {
		/* UL channel */
		do {
		ret = mhi_ep_read_channel(mhi_cntrl, ring);
		if (ret < 0) {
			dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n");
			return ret;
		}

			/* Read until the ring becomes empty */
		} while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE));
	}

	return 0;
+2 −3
Original line number Diff line number Diff line
@@ -194,7 +194,6 @@ static void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
{
	struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
	struct device *dev = &mhi_cntrl->mhi_dev->dev;
	unsigned long irq_flags = IRQF_SHARED | IRQF_NO_SUSPEND;
	int i, ret;

@@ -221,7 +220,7 @@ static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
			continue;

		if (mhi_event->irq >= mhi_cntrl->nr_irqs) {
			dev_err(dev, "irq %d not available for event ring\n",
			dev_err(mhi_cntrl->cntrl_dev, "irq %d not available for event ring\n",
				mhi_event->irq);
			ret = -EINVAL;
			goto error_request;
@@ -232,7 +231,7 @@ static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
				  irq_flags,
				  "mhi", mhi_event);
		if (ret) {
			dev_err(dev, "Error requesting irq:%d for ev:%d\n",
			dev_err(mhi_cntrl->cntrl_dev, "Error requesting irq:%d for ev:%d\n",
				mhi_cntrl->irq[mhi_event->irq], i);
			goto error_request;
		}
+3 −0
Original line number Diff line number Diff line
@@ -170,6 +170,8 @@ enum mhi_pm_state {
							MHI_PM_IN_ERROR_STATE(pm_state))
#define MHI_PM_IN_SUSPEND_STATE(pm_state)		(pm_state & \
							(MHI_PM_M3_ENTER | MHI_PM_M3))
#define MHI_PM_FATAL_ERROR(pm_state)			((pm_state == MHI_PM_FW_DL_ERR) || \
							(pm_state >= MHI_PM_SYS_ERR_FAIL))

#define NR_OF_CMD_RINGS					1
#define CMD_EL_PER_RING					128
@@ -403,6 +405,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
				struct mhi_event *mhi_event, u32 event_quota);
int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
			     struct mhi_event *mhi_event, u32 event_quota);
void mhi_uevent_notify(struct mhi_controller *mhi_cntrl, enum mhi_ee_type ee);

/* ISR handlers */
irqreturn_t mhi_irq_handler(int irq_number, void *dev);
+1 −0
Original line number Diff line number Diff line
@@ -512,6 +512,7 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
		if (mhi_cntrl->rddm_image && mhi_is_active(mhi_cntrl)) {
			mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM);
			mhi_cntrl->ee = ee;
			mhi_uevent_notify(mhi_cntrl, mhi_cntrl->ee);
			wake_up_all(&mhi_cntrl->state_event);
		}
		break;
+57 −28
Original line number Diff line number Diff line
@@ -34,28 +34,34 @@
/**
 * struct mhi_pci_dev_info - MHI PCI device specific information
 * @config: MHI controller configuration
 * @vf_config: MHI controller configuration for Virtual function (optional)
 * @name: name of the PCI module
 * @fw: firmware path (if any)
 * @edl: emergency download mode firmware path (if any)
 * @edl_trigger: capable of triggering EDL mode in the device (if supported)
 * @bar_num: PCI base address register to use for MHI MMIO register space
 * @dma_data_width: DMA transfer word size (32 or 64 bits)
 * @vf_dma_data_width: DMA transfer word size for VF's (optional)
 * @mru_default: default MRU size for MBIM network packets
 * @sideband_wake: Devices using dedicated sideband GPIO for wakeup instead
 *		   of inband wake support (such as sdx24)
 * @no_m3: M3 not supported
 * @reset_on_remove: Set true for devices that require SoC during driver removal
 */
struct mhi_pci_dev_info {
	const struct mhi_controller_config *config;
	const struct mhi_controller_config *vf_config;
	const char *name;
	const char *fw;
	const char *edl;
	bool edl_trigger;
	unsigned int bar_num;
	unsigned int dma_data_width;
	unsigned int vf_dma_data_width;
	unsigned int mru_default;
	bool sideband_wake;
	bool no_m3;
	bool reset_on_remove;
};

#define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \
@@ -296,8 +302,10 @@ static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = {
	.config = &mhi_qcom_qdu100_config,
	.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
	.dma_data_width = 32,
	.vf_dma_data_width = 40,
	.sideband_wake = false,
	.no_m3 = true,
	.reset_on_remove = true,
};

static const struct mhi_channel_config mhi_qcom_sa8775p_channels[] = {
@@ -917,20 +925,8 @@ static const struct pci_device_id mhi_pci_id_table[] = {
	/* Telit FE990A */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015),
		.driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info },
	/* Foxconn T99W696.01, Lenovo Generic SKU */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe142),
		.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
	/* Foxconn T99W696.02, Lenovo X1 Carbon SKU */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe143),
		.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
	/* Foxconn T99W696.03, Lenovo X1 2in1 SKU */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe144),
		.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
	/* Foxconn T99W696.04, Lenovo PRC SKU */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe145),
		.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
	/* Foxconn T99W696.00, Foxconn SKU */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe146),
	/* Foxconn T99W696, all variants */
	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, PCI_ANY_ID),
		.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
	{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
		.driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
@@ -1037,6 +1033,7 @@ struct mhi_pci_device {
	struct work_struct recovery_work;
	struct timer_list health_check_timer;
	unsigned long status;
	bool reset_on_remove;
};

static int mhi_pci_read_reg(struct mhi_controller *mhi_cntrl,
@@ -1092,7 +1089,7 @@ static bool mhi_pci_is_alive(struct mhi_controller *mhi_cntrl)
	struct pci_dev *pdev = to_pci_dev(mhi_cntrl->cntrl_dev);
	u16 vendor = 0;

	if (pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor))
	if (pci_read_config_word(pci_physfn(pdev), PCI_VENDOR_ID, &vendor))
		return false;

	if (vendor == (u16) ~0 || vendor == 0)
@@ -1203,7 +1200,9 @@ static void mhi_pci_recovery_work(struct work_struct *work)

	dev_warn(&pdev->dev, "device recovery started\n");

	if (pdev->is_physfn)
		timer_delete(&mhi_pdev->health_check_timer);

	pm_runtime_forbid(&pdev->dev);

	/* Clean up MHI state */
@@ -1230,7 +1229,10 @@ static void mhi_pci_recovery_work(struct work_struct *work)
	dev_dbg(&pdev->dev, "Recovery completed\n");

	set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);

	if (pdev->is_physfn)
		mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);

	return;

err_unprepare:
@@ -1301,6 +1303,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	const struct mhi_controller_config *mhi_cntrl_config;
	struct mhi_pci_device *mhi_pdev;
	struct mhi_controller *mhi_cntrl;
	unsigned int dma_data_width;
	int err;

	dev_info(&pdev->dev, "MHI PCI device found: %s\n", info->name);
@@ -1311,14 +1314,24 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
		return -ENOMEM;

	INIT_WORK(&mhi_pdev->recovery_work, mhi_pci_recovery_work);
	timer_setup(&mhi_pdev->health_check_timer, health_check, 0);

	if (pdev->is_virtfn && info->vf_config)
		mhi_cntrl_config = info->vf_config;
	else
		mhi_cntrl_config = info->config;

	/* Initialize health check monitor only for Physical functions */
	if (pdev->is_physfn)
		timer_setup(&mhi_pdev->health_check_timer, health_check, 0);

	mhi_cntrl = &mhi_pdev->mhi_cntrl;

	dma_data_width = (pdev->is_virtfn && info->vf_dma_data_width) ?
			  info->vf_dma_data_width : info->dma_data_width;

	mhi_cntrl->cntrl_dev = &pdev->dev;
	mhi_cntrl->iova_start = 0;
	mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(info->dma_data_width);
	mhi_cntrl->iova_stop = (dma_addr_t)DMA_BIT_MASK(dma_data_width);
	mhi_cntrl->fw_image = info->fw;
	mhi_cntrl->edl_image = info->edl;

@@ -1330,6 +1343,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	mhi_cntrl->mru = info->mru_default;
	mhi_cntrl->name = info->name;

	if (pdev->is_physfn)
		mhi_pdev->reset_on_remove = info->reset_on_remove;

	if (info->edl_trigger)
		mhi_cntrl->edl_trigger = mhi_pci_generic_edl_trigger;

@@ -1339,7 +1355,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
		mhi_cntrl->wake_toggle = mhi_pci_wake_toggle_nop;
	}

	err = mhi_pci_claim(mhi_cntrl, info->bar_num, DMA_BIT_MASK(info->dma_data_width));
	err = mhi_pci_claim(mhi_cntrl, info->bar_num, DMA_BIT_MASK(dma_data_width));
	if (err)
		return err;

@@ -1376,6 +1392,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);

	/* start health check */
	if (pdev->is_physfn)
		mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);

	/* Allow runtime suspend only if both PME from D3Hot and M3 are supported */
@@ -1401,6 +1418,9 @@ static void mhi_pci_remove(struct pci_dev *pdev)
	struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev);
	struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl;

	pci_disable_sriov(pdev);

	if (pdev->is_physfn)
		timer_delete_sync(&mhi_pdev->health_check_timer);
	cancel_work_sync(&mhi_pdev->recovery_work);

@@ -1413,6 +1433,9 @@ static void mhi_pci_remove(struct pci_dev *pdev)
	if (pci_pme_capable(pdev, PCI_D3hot))
		pm_runtime_get_noresume(&pdev->dev);

	if (mhi_pdev->reset_on_remove)
		mhi_soc_reset(mhi_cntrl);

	mhi_unregister_controller(mhi_cntrl);
}

@@ -1429,6 +1452,7 @@ static void mhi_pci_reset_prepare(struct pci_dev *pdev)

	dev_info(&pdev->dev, "reset\n");

	if (pdev->is_physfn)
		timer_delete(&mhi_pdev->health_check_timer);

	/* Clean up MHI state */
@@ -1474,6 +1498,7 @@ static void mhi_pci_reset_done(struct pci_dev *pdev)
	}

	set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status);
	if (pdev->is_physfn)
		mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
}

@@ -1539,7 +1564,9 @@ static int __maybe_unused mhi_pci_runtime_suspend(struct device *dev)
	if (test_and_set_bit(MHI_PCI_DEV_SUSPENDED, &mhi_pdev->status))
		return 0;

	if (pdev->is_physfn)
		timer_delete(&mhi_pdev->health_check_timer);

	cancel_work_sync(&mhi_pdev->recovery_work);

	if (!test_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status) ||
@@ -1590,6 +1617,7 @@ static int __maybe_unused mhi_pci_runtime_resume(struct device *dev)
	}

	/* Resume health check */
	if (pdev->is_physfn)
		mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);

	/* It can be a remote wakeup (no mhi runtime_get), update access time */
@@ -1676,7 +1704,8 @@ static struct pci_driver mhi_pci_driver = {
	.remove		= mhi_pci_remove,
	.shutdown	= mhi_pci_shutdown,
	.err_handler	= &mhi_pci_err_handler,
	.driver.pm	= &mhi_pci_pm_ops
	.driver.pm	= &mhi_pci_pm_ops,
	.sriov_configure = pci_sriov_configure_simple,
};
module_pci_driver(mhi_pci_driver);

Loading