Hello,
This series adds support for Modem Host Interface (MHI) Endpoint function
driver and few updates to the PCI endpoint core.
MHI
===
MHI is the communication protocol used by the host machines to control and
communicate with the Qualcomm modems/WLAN devices over any high speed physical
bus like PCIe. In Linux kernel, MHI is modeled as a bus driver [1] and there
are two instances of MHI used in a typical setup.
1. MHI host - MHI implementation for the host machines like x86/ARM64.
2. MHI Endpoint - MHI implementation for the endpoint devices like modems.
MHI EPF
=======
The MHI Endpoint function driver (MHI EPF) is used on the MHI endpoint devices
like modems. The MHI EPF driver sits in between the PCIe EP and MHI EP bus and
carries out all of the PCIe related activities like BAR config, PCIe Event
handling, MMIO read/write etc,... for the MHI EP bus.
Below is the simple representation of the setup:
+----------------------------------------------------+
| Endpoint CPU |
| |
+------------+ | +------------+ +-----------+ +-----------+ |
| | | | | | | | | |
| | | | MHI EP | | | | | | PCIe Bus
| Modem DSP +---+---+ Bus +---+ MHI EPF +---+ PCIe EP +---+---------
| | | | | | | | | |
| | | | | | | | | |
+------------+ | +------------+ +-----------+ +-----------+ |
| |
| |
+----------------------------------------------------+
The data packets will be read from the Modem DSP by the MHI stack and will be
transmitted to the host machine over PCIe bus with the help of MHI EPF driver.
Test setup
==========
This series has been tested on Snapdragon X55 modem a.k.a SDX55 connected to
the ARM64 host machine.
Thanks,
Mani
[1] https://www.kernel.org/doc/html/latest/mhi/mhi.html
Changes in v5:
* Moved the PCI EPF driver match logic to pci_epf_match_id() function and used
that to get the matched driver ID for passing to driver probe instead of
storing the id during match.
* Added a patch to fix the missing documentation about MSI/MSI-X start vector.
* Addressed the review comments on the MHI EPF driver. Most notably, got rid of
local variable for tracking MHI registration and used the mhi_dev pointer.
Also, modified the MSI vector increment comment to make it clear.
* Added a patch for adding MHI EPF driver to MAINTAINERS file
Changes in v4:
* Collected review tag from Kishon
* Changed the IP_SW0 channel numbers as per latest MHI spec
Changes in v3:
* Fixed the probe function of EPF_VNTB driver
Changes in v2:
* Rebased on top of v6.3-rc1
* Switched to the new callback interface for passing events from EPC to EPF
* Dropped one patch related to notifier
Manivannan Sadhasivam (9):
MAINTAINERS: Add entry for MHI networking drivers under MHI bus
PCI: endpoint: Add missing documentation about the MSI/MSI-X range
PCI: endpoint: Pass EPF device ID to the probe function
PCI: endpoint: Warn and return if EPC is started/stopped multiple
times
PCI: endpoint: Add linkdown notifier support
PCI: endpoint: Add BME notifier support
PCI: qcom-ep: Add support for Link down notification
PCI: qcom-ep: Add support for BME notification
PCI: endpoint: Add PCI Endpoint function driver for MHI bus
MAINTAINERS | 1 +
drivers/pci/controller/dwc/pcie-qcom-ep.c | 2 +
drivers/pci/endpoint/functions/Kconfig | 10 +
drivers/pci/endpoint/functions/Makefile | 1 +
drivers/pci/endpoint/functions/pci-epf-mhi.c | 462 ++++++++++++++++++
drivers/pci/endpoint/functions/pci-epf-ntb.c | 3 +-
drivers/pci/endpoint/functions/pci-epf-test.c | 2 +-
drivers/pci/endpoint/functions/pci-epf-vntb.c | 2 +-
drivers/pci/endpoint/pci-ep-cfs.c | 3 +
drivers/pci/endpoint/pci-epc-core.c | 56 ++-
drivers/pci/endpoint/pci-epf-core.c | 20 +-
include/linux/pci-epc.h | 2 +
include/linux/pci-epf.h | 8 +-
13 files changed, 559 insertions(+), 13 deletions(-)
create mode 100644 drivers/pci/endpoint/functions/pci-epf-mhi.c
--
2.25.1
Currently, the EPF probe function doesn't get the device ID argument needed
to correctly identify the device table ID of the EPF device.
When multiple entries are added to the "struct pci_epf_device_id" table,
the probe function needs to identify the correct one. This is achieved by
modifying the pci_epf_match_id() function to return the match ID pointer
and passing it to the driver's probe function.
pci_epf_device_match() function can return bool based on the return value
of pci_epf_match_id().
Reviewed-by: Kishon Vijay Abraham I <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/pci/endpoint/functions/pci-epf-ntb.c | 3 ++-
drivers/pci/endpoint/functions/pci-epf-test.c | 2 +-
drivers/pci/endpoint/functions/pci-epf-vntb.c | 2 +-
drivers/pci/endpoint/pci-epf-core.c | 20 ++++++++++++-------
include/linux/pci-epf.h | 4 +++-
5 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c
index 9a00448c7e61..980b4ecf19a2 100644
--- a/drivers/pci/endpoint/functions/pci-epf-ntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c
@@ -2075,11 +2075,12 @@ static struct config_group *epf_ntb_add_cfs(struct pci_epf *epf,
/**
* epf_ntb_probe() - Probe NTB function driver
* @epf: NTB endpoint function device
+ * @id: NTB endpoint function device ID
*
* Probe NTB function driver when endpoint function bus detects a NTB
* endpoint function.
*/
-static int epf_ntb_probe(struct pci_epf *epf)
+static int epf_ntb_probe(struct pci_epf *epf, const struct pci_epf_device_id *id)
{
struct epf_ntb *ntb;
struct device *dev;
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 0f9d2ec822ac..d5fcc78a5b73 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -980,7 +980,7 @@ static const struct pci_epf_device_id pci_epf_test_ids[] = {
{},
};
-static int pci_epf_test_probe(struct pci_epf *epf)
+static int pci_epf_test_probe(struct pci_epf *epf, const struct pci_epf_device_id *id)
{
struct pci_epf_test *epf_test;
struct device *dev = &epf->dev;
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index b7c7a8af99f4..122eb7a12028 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -1401,7 +1401,7 @@ static struct pci_epf_ops epf_ntb_ops = {
*
* Returns: Zero for success, or an error code in case of failure
*/
-static int epf_ntb_probe(struct pci_epf *epf)
+static int epf_ntb_probe(struct pci_epf *epf, const struct pci_epf_device_id *id)
{
struct epf_ntb *ntb;
struct device *dev;
diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c
index 2036e38be093..7307e052136f 100644
--- a/drivers/pci/endpoint/pci-epf-core.c
+++ b/drivers/pci/endpoint/pci-epf-core.c
@@ -493,16 +493,16 @@ static const struct device_type pci_epf_type = {
.release = pci_epf_dev_release,
};
-static int
-pci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf)
+static const struct pci_epf_device_id
+*pci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf)
{
while (id->name[0]) {
if (strcmp(epf->name, id->name) == 0)
- return true;
+ return id;
id++;
}
- return false;
+ return NULL;
}
static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
@@ -510,8 +510,12 @@ static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
struct pci_epf *epf = to_pci_epf(dev);
struct pci_epf_driver *driver = to_pci_epf_driver(drv);
- if (driver->id_table)
- return pci_epf_match_id(driver->id_table, epf);
+ if (driver->id_table) {
+ if (pci_epf_match_id(driver->id_table, epf))
+ return true;
+ else
+ return false;
+ }
return !strcmp(epf->name, drv->name);
}
@@ -520,13 +524,15 @@ static int pci_epf_device_probe(struct device *dev)
{
struct pci_epf *epf = to_pci_epf(dev);
struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
+ const struct pci_epf_device_id *id;
if (!driver->probe)
return -ENODEV;
epf->driver = driver;
+ id = pci_epf_match_id(driver->id_table, epf);
- return driver->probe(epf);
+ return driver->probe(epf, id);
}
static void pci_epf_device_remove(struct device *dev)
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index a215dc8ce693..bc613f0df7e3 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -89,7 +89,7 @@ struct pci_epc_event_ops {
* @id_table: identifies EPF devices for probing
*/
struct pci_epf_driver {
- int (*probe)(struct pci_epf *epf);
+ int (*probe)(struct pci_epf *epf, const struct pci_epf_device_id *id);
void (*remove)(struct pci_epf *epf);
struct device_driver driver;
@@ -131,6 +131,7 @@ struct pci_epf_bar {
* @epc: the EPC device to which this EPF device is bound
* @epf_pf: the physical EPF device to which this virtual EPF device is bound
* @driver: the EPF driver to which this EPF device is bound
+ * @id: Pointer to the EPF device ID
* @list: to add pci_epf as a list of PCI endpoint functions to pci_epc
* @lock: mutex to protect pci_epf_ops
* @sec_epc: the secondary EPC device to which this EPF device is bound
@@ -158,6 +159,7 @@ struct pci_epf {
struct pci_epc *epc;
struct pci_epf *epf_pf;
struct pci_epf_driver *driver;
+ const struct pci_epf_device_id *id;
struct list_head list;
/* mutex to protect against concurrent access of pci_epf_ops */
struct mutex lock;
--
2.25.1
Both pci_epc_raise_irq() and pci_epc_map_msi_irq() APIs expects the
MSI/MSI-X vectors to start from 1 but it is not documented. Add the
range info to the kdoc of the APIs to make it clear.
Fixes: 5e8cb4033807 ("PCI: endpoint: Add EP core layer to enable EP controller and EP functions")
Fixes: 87d5972e476f ("PCI: endpoint: Add pci_epc_ops to map MSI IRQ")
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/pci/endpoint/pci-epc-core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 46c9a5c3ca14..0cf602c83d4a 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -213,7 +213,7 @@ EXPORT_SYMBOL_GPL(pci_epc_start);
* @func_no: the physical endpoint function number in the EPC device
* @vfunc_no: the virtual endpoint function number in the physical function
* @type: specify the type of interrupt; legacy, MSI or MSI-X
- * @interrupt_num: the MSI or MSI-X interrupt number
+ * @interrupt_num: the MSI or MSI-X interrupt number with range (1-N)
*
* Invoke to raise an legacy, MSI or MSI-X interrupt
*/
@@ -246,7 +246,7 @@ EXPORT_SYMBOL_GPL(pci_epc_raise_irq);
* @func_no: the physical endpoint function number in the EPC device
* @vfunc_no: the virtual endpoint function number in the physical function
* @phys_addr: the physical address of the outbound region
- * @interrupt_num: the MSI interrupt number
+ * @interrupt_num: the MSI interrupt number with range (1-N)
* @entry_size: Size of Outbound address region for each interrupt
* @msi_data: the data that should be written in order to raise MSI interrupt
* with interrupt number as 'interrupt num'
--
2.25.1
Add support to notify the EPF device about the linkdown event from the
EPC device.
Reviewed-by: Kishon Vijay Abraham I <[email protected]>
Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/pci/endpoint/pci-epc-core.c | 26 ++++++++++++++++++++++++++
include/linux/pci-epc.h | 1 +
include/linux/pci-epf.h | 2 ++
3 files changed, 29 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 0cf602c83d4a..e0570b52698d 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -706,6 +706,32 @@ void pci_epc_linkup(struct pci_epc *epc)
}
EXPORT_SYMBOL_GPL(pci_epc_linkup);
+/**
+ * pci_epc_linkdown() - Notify the EPF device that EPC device has dropped the
+ * connection with the Root Complex.
+ * @epc: the EPC device which has dropped the link with the host
+ *
+ * Invoke to Notify the EPF device that the EPC device has dropped the
+ * connection with the Root Complex.
+ */
+void pci_epc_linkdown(struct pci_epc *epc)
+{
+ struct pci_epf *epf;
+
+ if (!epc || IS_ERR(epc))
+ return;
+
+ mutex_lock(&epc->list_lock);
+ list_for_each_entry(epf, &epc->pci_epf, list) {
+ mutex_lock(&epf->lock);
+ if (epf->event_ops && epf->event_ops->link_down)
+ epf->event_ops->link_down(epf);
+ mutex_unlock(&epf->lock);
+ }
+ mutex_unlock(&epc->list_lock);
+}
+EXPORT_SYMBOL_GPL(pci_epc_linkdown);
+
/**
* pci_epc_init_notify() - Notify the EPF device that EPC device's core
* initialization is completed.
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 301bb0e53707..63a6cc5e5282 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -203,6 +203,7 @@ void pci_epc_destroy(struct pci_epc *epc);
int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type);
void pci_epc_linkup(struct pci_epc *epc);
+void pci_epc_linkdown(struct pci_epc *epc);
void pci_epc_init_notify(struct pci_epc *epc);
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type);
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index bc613f0df7e3..f8e5a63d0c83 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -71,10 +71,12 @@ struct pci_epf_ops {
* struct pci_epf_event_ops - Callbacks for capturing the EPC events
* @core_init: Callback for the EPC initialization complete event
* @link_up: Callback for the EPC link up event
+ * @link_down: Callback for the EPC link down event
*/
struct pci_epc_event_ops {
int (*core_init)(struct pci_epf *epf);
int (*link_up)(struct pci_epf *epf);
+ int (*link_down)(struct pci_epf *epf);
};
/**
--
2.25.1
On 6/1/23 23:57, Manivannan Sadhasivam wrote:
> Both pci_epc_raise_irq() and pci_epc_map_msi_irq() APIs expects the
> MSI/MSI-X vectors to start from 1 but it is not documented. Add the
> range info to the kdoc of the APIs to make it clear.
>
> Fixes: 5e8cb4033807 ("PCI: endpoint: Add EP core layer to enable EP controller and EP functions")
> Fixes: 87d5972e476f ("PCI: endpoint: Add pci_epc_ops to map MSI IRQ")
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
Looks good.
Reviewed-by: Damien Le Moal <[email protected]>
--
Damien Le Moal
Western Digital Research
On 6/1/23 23:57, Manivannan Sadhasivam wrote:
> Currently, the EPF probe function doesn't get the device ID argument needed
> to correctly identify the device table ID of the EPF device.
>
> When multiple entries are added to the "struct pci_epf_device_id" table,
> the probe function needs to identify the correct one. This is achieved by
> modifying the pci_epf_match_id() function to return the match ID pointer
> and passing it to the driver's probe function.
>
> pci_epf_device_match() function can return bool based on the return value
> of pci_epf_match_id().
>
> Reviewed-by: Kishon Vijay Abraham I <[email protected]>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
[...]
> static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
> @@ -510,8 +510,12 @@ static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
> struct pci_epf *epf = to_pci_epf(dev);
> struct pci_epf_driver *driver = to_pci_epf_driver(drv);
>
> - if (driver->id_table)
> - return pci_epf_match_id(driver->id_table, epf);
> + if (driver->id_table) {
> + if (pci_epf_match_id(driver->id_table, epf))
> + return true;
> + else
> + return false;
return pci_epf_match_id(driver->id_table, epf) != NULL;
is simpler. If you do not like this, at least drop the "else" as it is not
necessary at all.
> + }
>
> return !strcmp(epf->name, drv->name);
> }
> @@ -520,13 +524,15 @@ static int pci_epf_device_probe(struct device *dev)
> {
> struct pci_epf *epf = to_pci_epf(dev);
> struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
> + const struct pci_epf_device_id *id;
>
> if (!driver->probe)
> return -ENODEV;
>
> epf->driver = driver;
> + id = pci_epf_match_id(driver->id_table, epf);
Not sure that the id variable is that useful.
>
> - return driver->probe(epf);
> + return driver->probe(epf, id);
> }
--
Damien Le Moal
Western Digital Research
On 6/1/23 23:57, Manivannan Sadhasivam wrote:
> Add support to notify the EPF device about the linkdown event from the
> EPC device.
>
> Reviewed-by: Kishon Vijay Abraham I <[email protected]>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>
Looks good.
Reviewed-by: Damien Le Moal <[email protected]>
--
Damien Le Moal
Western Digital Research
On Fri, Jun 02, 2023 at 08:16:45AM +0900, Damien Le Moal wrote:
> On 6/1/23 23:57, Manivannan Sadhasivam wrote:
> > Currently, the EPF probe function doesn't get the device ID argument needed
> > to correctly identify the device table ID of the EPF device.
> >
> > When multiple entries are added to the "struct pci_epf_device_id" table,
> > the probe function needs to identify the correct one. This is achieved by
> > modifying the pci_epf_match_id() function to return the match ID pointer
> > and passing it to the driver's probe function.
> >
> > pci_epf_device_match() function can return bool based on the return value
> > of pci_epf_match_id().
> >
> > Reviewed-by: Kishon Vijay Abraham I <[email protected]>
> > Signed-off-by: Manivannan Sadhasivam <[email protected]>
>
> [...]
>
> > static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
> > @@ -510,8 +510,12 @@ static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
> > struct pci_epf *epf = to_pci_epf(dev);
> > struct pci_epf_driver *driver = to_pci_epf_driver(drv);
> >
> > - if (driver->id_table)
> > - return pci_epf_match_id(driver->id_table, epf);
> > + if (driver->id_table) {
> > + if (pci_epf_match_id(driver->id_table, epf))
> > + return true;
> > + else
> > + return false;
>
> return pci_epf_match_id(driver->id_table, epf) != NULL;
>
> is simpler. If you do not like this, at least drop the "else" as it is not
> necessary at all.
>
I settle for simplicity :) Also, there is a theoretical possibility of passing
NULL as the id->name, so doesn't want to rule out that.
> > + }
> >
> > return !strcmp(epf->name, drv->name);
> > }
> > @@ -520,13 +524,15 @@ static int pci_epf_device_probe(struct device *dev)
> > {
> > struct pci_epf *epf = to_pci_epf(dev);
> > struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
> > + const struct pci_epf_device_id *id;
> >
> > if (!driver->probe)
> > return -ENODEV;
> >
> > epf->driver = driver;
> > + id = pci_epf_match_id(driver->id_table, epf);
>
> Not sure that the id variable is that useful.
>
Thought it makes the code clear but looking again, it doesn't hurt to call match
directly in probe. Will change it in next iteration.
- Mani
> >
> > - return driver->probe(epf);
> > + return driver->probe(epf, id);
> > }
>
> --
> Damien Le Moal
> Western Digital Research
>
--
மணிவண்ணன் சதாசிவம்