diff mbox

[v1,2/4] mhi_bus: controller: MHI support for QCOM modems

Message ID 1524795811-21399-3-git-send-email-sdias@codeaurora.org (mailing list archive)
State New, archived
Delegated to: Andy Gross
Headers show

Commit Message

Sujeev Dias April 27, 2018, 2:23 a.m. UTC
QCOM PCIe based modems uses MHI as the communication protocol.
MHI control driver is the bus master for such modems. As the bus
master driver, it oversees power management operations
such as suspend, resume, powering on and off the device.

Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
---
 Documentation/devicetree/bindings/bus/mhi_qcom.txt | 110 ++++
 drivers/bus/Kconfig                                |   1 +
 drivers/bus/mhi/Makefile                           |   2 +-
 drivers/bus/mhi/controllers/Kconfig                |  10 +
 drivers/bus/mhi/controllers/Makefile               |   1 +
 drivers/bus/mhi/controllers/mhi_qcom.c             | 686 +++++++++++++++++++++
 drivers/bus/mhi/controllers/mhi_qcom.h             |  85 +++
 7 files changed, 894 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/bus/mhi_qcom.txt
 create mode 100644 drivers/bus/mhi/controllers/Kconfig
 create mode 100644 drivers/bus/mhi/controllers/Makefile
 create mode 100644 drivers/bus/mhi/controllers/mhi_qcom.c
 create mode 100644 drivers/bus/mhi/controllers/mhi_qcom.h

Comments

Arnd Bergmann April 27, 2018, 11:32 a.m. UTC | #1
On Fri, Apr 27, 2018 at 4:23 AM, Sujeev Dias <sdias@codeaurora.org> wrote:
> QCOM PCIe based modems uses MHI as the communication protocol.
> MHI control driver is the bus master for such modems. As the bus
> master driver, it oversees power management operations
> such as suspend, resume, powering on and off the device.
>

> +- compatible
> +  Usage: required
> +  Value type: <string>
> +  Definition: "qcom,mhi"
> +
> +- qcom,pci-dev-id
> +  Usage: optional
> +  Value type: <u32>
> +  Definition: PCIe device id of external modem to bind. If not set, any
> +       device is compatible with this node.
> +
> +- qcom,pci-domain
> +  Usage: required
> +  Value type: <u32>
> +  Definition: PCIe root complex external modem connected to
> +
> +- qcom,pci-bus
> +  Usage: required
> +  Value type: <u32>
> +  Definition: PCIe bus external modem connected to
> +
> +- qcom,pci-slot
> +  Usage: required
> +  Value type: <u32>
> +  Definition: PCIe slot as assigned by pci framework to external modem

These don't seem to make any sense: You seem to have access to
a regular pci_device already, so why do you need to duplicate the
information about it in DT?

> +- qcom,smmu-cfg
> +  Usage: required
> +  Value type: <u32>
> +  Definition: Required SMMU configuration bitmask for PCIe bus.
> +       BIT mask:
> +       BIT(0) : Attach address mapping to endpoint device
> +       BIT(1) : Set attribute S1_BYPASS
> +       BIT(2) : Set attribute FAST
> +       BIT(3) : Set attribute ATOMIC
> +       BIT(4) : Set attribute FORCE_COHERENT
> +
> +- qcom,addr-win
> +  Usage: required if SMMU S1 translation is enabled
> +  Value type: Array of <u64>
> +  Definition: Pair of values describing iova start and stop address

Why do you need these? Can't that be handled by the PCI
layer?

> +- qcom,msm-bus,name
> +  Usage: required
> +  Value type: <string>
> +  Definition: string representing the bus scale client name to register

This probably belongs into a separate binding for the bus
scale driver, right?

> +static struct pci_driver mhi_pcie_driver;

Please try to reorder the symbols to avoid forward declarations.

> +static int mhi_platform_probe(struct platform_device *pdev)
> +{
> +       struct mhi_controller *mhi_cntrl;
> +       struct mhi_dev *mhi_dev;
> +       struct device_node *of_node = pdev->dev.of_node;
> +       u64 addr_win[2];
> +       int ret;
> +
> +       if (!of_node)
> +               return -ENODEV;
> +
> +       mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
> +       if (!mhi_cntrl)
> +               return -ENOMEM;
> +
> +       mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
> +
> +       /* get pci bus topology for this node */
> +       ret = of_property_read_u32(of_node, "qcom,pci-dev-id",
> +                                  &mhi_cntrl->dev_id);
> +       if (ret)
> +               mhi_cntrl->dev_id = PCI_ANY_ID;
> +
> +       ret = of_property_read_u32(of_node, "qcom,pci-domain",
> +                                  &mhi_cntrl->domain);
> +       if (ret)
> +               goto error_probe;
> +
> +       ret = of_property_read_u32(of_node, "qcom,pci-bus", &mhi_cntrl->bus);
> +       if (ret)
> +               goto error_probe;
> +
> +       ret = of_property_read_u32(of_node, "qcom,pci-slot", &mhi_cntrl->slot);
> +       if (ret)
> +               goto error_probe;

Please explain what you are trying to do here, why do you register
two device drivers? It looks like they both refer to the same
hardware, so why isn't it sufficient to have the pci_driver?

       Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
kernel test robot April 28, 2018, 3:05 a.m. UTC | #2
Hi Sujeev,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.17-rc2 next-20180426]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sujeev-Dias/mhi_bus-core-Add-support-for-MHI-host-interface/20180428-065959
config: alpha-allmodconfig (attached as .config)
compiler: alpha-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=alpha 

All errors (new ones prefixed by >>):

   In file included from drivers/bus/mhi/controllers/mhi_qcom.c:25:0:
   include/linux/mhi.h:658:15: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'int'
    static inlint int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl)
                  ^~~
   drivers/bus/mhi/controllers/mhi_qcom.c: In function 'mhi_platform_probe':
>> drivers/bus/mhi/controllers/mhi_qcom.c:587:17: error: implicit declaration of function 'memblock_start_of_DRAM'; did you mean 'memblock_alloc'? [-Werror=implicit-function-declaration]
      addr_win[0] = memblock_start_of_DRAM();
                    ^~~~~~~~~~~~~~~~~~~~~~
                    memblock_alloc
>> drivers/bus/mhi/controllers/mhi_qcom.c:588:17: error: implicit declaration of function 'memblock_end_of_DRAM'; did you mean 'memblock_alloc'? [-Werror=implicit-function-declaration]
      addr_win[1] = memblock_end_of_DRAM();
                    ^~~~~~~~~~~~~~~~~~~~
                    memblock_alloc
   At top level:
   drivers/bus/mhi/controllers/mhi_qcom.c:238:12: warning: 'mhi_system_resume' defined but not used [-Wunused-function]
    static int mhi_system_resume(struct device *dev)
               ^~~~~~~~~~~~~~~~~
   drivers/bus/mhi/controllers/mhi_qcom.c:186:12: warning: 'mhi_runtime_idle' defined but not used [-Wunused-function]
    static int mhi_runtime_idle(struct device *dev)
               ^~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +587 drivers/bus/mhi/controllers/mhi_qcom.c

   533	
   534	static int mhi_platform_probe(struct platform_device *pdev)
   535	{
   536		struct mhi_controller *mhi_cntrl;
   537		struct mhi_dev *mhi_dev;
   538		struct device_node *of_node = pdev->dev.of_node;
   539		u64 addr_win[2];
   540		int ret;
   541	
   542		if (!of_node)
   543			return -ENODEV;
   544	
   545		mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
   546		if (!mhi_cntrl)
   547			return -ENOMEM;
   548	
   549		mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
   550	
   551		/* get pci bus topology for this node */
   552		ret = of_property_read_u32(of_node, "qcom,pci-dev-id",
   553					   &mhi_cntrl->dev_id);
   554		if (ret)
   555			mhi_cntrl->dev_id = PCI_ANY_ID;
   556	
   557		ret = of_property_read_u32(of_node, "qcom,pci-domain",
   558					   &mhi_cntrl->domain);
   559		if (ret)
   560			goto error_probe;
   561	
   562		ret = of_property_read_u32(of_node, "qcom,pci-bus", &mhi_cntrl->bus);
   563		if (ret)
   564			goto error_probe;
   565	
   566		ret = of_property_read_u32(of_node, "qcom,pci-slot", &mhi_cntrl->slot);
   567		if (ret)
   568			goto error_probe;
   569	
   570		ret = of_property_read_u32(of_node, "qcom,smmu-cfg",
   571					   &mhi_dev->smmu_cfg);
   572		if (ret)
   573			goto error_probe;
   574	
   575		/* if s1 translation enabled pull iova addr from dt */
   576		if (mhi_dev->smmu_cfg & MHI_SMMU_ATTACH &&
   577		    !(mhi_dev->smmu_cfg & MHI_SMMU_S1_BYPASS)) {
   578			ret = of_property_count_elems_of_size(of_node, "qcom,addr-win",
   579							      sizeof(addr_win));
   580			if (ret != 1)
   581				goto error_probe;
   582			ret = of_property_read_u64_array(of_node, "qcom,addr-win",
   583							 addr_win, 2);
   584			if (ret)
   585				goto error_probe;
   586		} else {
 > 587			addr_win[0] = memblock_start_of_DRAM();
 > 588			addr_win[1] = memblock_end_of_DRAM();
   589		}
   590	
   591		mhi_dev->iova_start = addr_win[0];
   592		mhi_dev->iova_stop = addr_win[1];
   593	
   594		/*
   595		 * if S1 is enabled, set MHI_CTRL start address to 0 so we can use low
   596		 * level mapping api to map buffers outside of smmu domain
   597		 */
   598		if (mhi_dev->smmu_cfg & MHI_SMMU_ATTACH &&
   599		    !(mhi_dev->smmu_cfg & MHI_SMMU_S1_BYPASS))
   600			mhi_cntrl->iova_start = 0;
   601		else
   602			mhi_cntrl->iova_start = addr_win[0];
   603	
   604		mhi_cntrl->iova_stop = mhi_dev->iova_stop;
   605		mhi_cntrl->of_node = of_node;
   606	
   607		/* setup power management apis */
   608		mhi_cntrl->status_cb = mhi_status_cb;
   609		mhi_cntrl->runtime_get = mhi_runtime_get;
   610		mhi_cntrl->runtime_put = mhi_runtime_put;
   611		mhi_cntrl->link_status = mhi_link_status;
   612	
   613		mhi_dev->pdev = pdev;
   614	
   615		ret = mhi_arch_platform_init(mhi_dev);
   616		if (ret)
   617			goto error_probe;
   618	
   619		ret = of_register_mhi_controller(mhi_cntrl);
   620		if (ret)
   621			goto error_register;
   622	
   623		if (mhi_cntrl->parent)
   624			debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent,
   625					    mhi_cntrl, &debugfs_debug_ops);
   626	
   627		return 0;
   628	
   629	error_register:
   630		mhi_arch_platform_deinit(mhi_dev);
   631	
   632	error_probe:
   633		mhi_free_controller(mhi_cntrl);
   634	
   635		return -EINVAL;
   636	};
   637	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot April 28, 2018, 3:12 a.m. UTC | #3
Hi Sujeev,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.17-rc2 next-20180426]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Sujeev-Dias/mhi_bus-core-Add-support-for-MHI-host-interface/20180428-065959
config: openrisc-allmodconfig (attached as .config)
compiler: or1k-linux-gcc (GCC) 6.0.0 20160327 (experimental)
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=openrisc 

All errors (new ones prefixed by >>):

   In file included from drivers/bus/mhi/controllers/mhi_qcom.c:25:0:
   include/linux/mhi.h:658:15: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'int'
    static inlint int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl)
                  ^~~
   drivers/bus/mhi/controllers/mhi_qcom.c: In function 'mhi_deinit_pci_dev':
>> drivers/bus/mhi/controllers/mhi_qcom.c:46:2: error: implicit declaration of function 'pci_free_irq_vectors' [-Werror=implicit-function-declaration]
     pci_free_irq_vectors(pci_dev);
     ^~~~~~~~~~~~~~~~~~~~
>> drivers/bus/mhi/controllers/mhi_qcom.c:51:2: error: implicit declaration of function 'pci_clear_master' [-Werror=implicit-function-declaration]
     pci_clear_master(pci_dev);
     ^~~~~~~~~~~~~~~~
>> drivers/bus/mhi/controllers/mhi_qcom.c:52:2: error: implicit declaration of function 'pci_release_region' [-Werror=implicit-function-declaration]
     pci_release_region(pci_dev, mhi_dev->resn);
     ^~~~~~~~~~~~~~~~~~
   drivers/bus/mhi/controllers/mhi_qcom.c: In function 'mhi_init_pci_dev':
>> drivers/bus/mhi/controllers/mhi_qcom.c:77:8: error: implicit declaration of function 'pci_request_region' [-Werror=implicit-function-declaration]
     ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
           ^~~~~~~~~~~~~~~~~~
>> drivers/bus/mhi/controllers/mhi_qcom.c:93:8: error: implicit declaration of function 'pci_alloc_irq_vectors' [-Werror=implicit-function-declaration]
     ret = pci_alloc_irq_vectors(pci_dev, mhi_cntrl->msi_required,
           ^~~~~~~~~~~~~~~~~~~~~
>> drivers/bus/mhi/controllers/mhi_qcom.c:94:34: error: 'PCI_IRQ_MSI' undeclared (first use in this function)
            mhi_cntrl->msi_required, PCI_IRQ_MSI);
                                     ^~~~~~~~~~~
   drivers/bus/mhi/controllers/mhi_qcom.c:94:34: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/bus/mhi/controllers/mhi_qcom.c:109:23: error: implicit declaration of function 'pci_irq_vector' [-Werror=implicit-function-declaration]
      mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
                          ^~~~~~~~~~~~~~
   At top level:
   drivers/bus/mhi/controllers/mhi_qcom.c:238:12: warning: 'mhi_system_resume' defined but not used [-Wunused-function]
    static int mhi_system_resume(struct device *dev)
               ^~~~~~~~~~~~~~~~~
   drivers/bus/mhi/controllers/mhi_qcom.c:186:12: warning: 'mhi_runtime_idle' defined but not used [-Wunused-function]
    static int mhi_runtime_idle(struct device *dev)
               ^~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +/pci_free_irq_vectors +46 drivers/bus/mhi/controllers/mhi_qcom.c

  > 25	#include <linux/mhi.h>
    26	#include "mhi_qcom.h"
    27	
    28	static struct pci_device_id mhi_pcie_device_id[] = {
    29		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0300)},
    30		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0301)},
    31		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0302)},
    32		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)},
    33		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)},
    34		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)},
    35		{PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)},
    36		{0},
    37	};
    38	
    39	static struct pci_driver mhi_pcie_driver;
    40	
    41	void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
    42	{
    43		struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
    44		struct pci_dev *pci_dev = mhi_dev->pci_dev;
    45	
  > 46		pci_free_irq_vectors(pci_dev);
    47		kfree(mhi_cntrl->irq);
    48		mhi_cntrl->irq = NULL;
    49		iounmap(mhi_cntrl->regs);
    50		mhi_cntrl->regs = NULL;
  > 51		pci_clear_master(pci_dev);
  > 52		pci_release_region(pci_dev, mhi_dev->resn);
    53		pci_disable_device(pci_dev);
    54	}
    55	
    56	static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)
    57	{
    58		struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
    59		struct pci_dev *pci_dev = mhi_dev->pci_dev;
    60		int ret;
    61		resource_size_t start, len;
    62		int i;
    63	
    64		mhi_dev->resn = MHI_PCI_BAR_NUM;
    65		ret = pci_assign_resource(pci_dev, mhi_dev->resn);
    66		if (ret) {
    67			MHI_ERR("Error assign pci resources, ret:%d\n", ret);
    68			return ret;
    69		}
    70	
    71		ret = pci_enable_device(pci_dev);
    72		if (ret) {
    73			MHI_ERR("Error enabling device, ret:%d\n", ret);
    74			goto error_enable_device;
    75		}
    76	
  > 77		ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
    78		if (ret) {
    79			MHI_ERR("Error pci_request_region, ret:%d\n", ret);
    80			goto error_request_region;
    81		}
    82	
    83		pci_set_master(pci_dev);
    84	
    85		start = pci_resource_start(pci_dev, mhi_dev->resn);
    86		len = pci_resource_len(pci_dev, mhi_dev->resn);
    87		mhi_cntrl->regs = ioremap_nocache(start, len);
    88		if (!mhi_cntrl->regs) {
    89			MHI_ERR("Error ioremap region\n");
    90			goto error_ioremap;
    91		}
    92	
  > 93		ret = pci_alloc_irq_vectors(pci_dev, mhi_cntrl->msi_required,
  > 94					    mhi_cntrl->msi_required, PCI_IRQ_MSI);
    95		if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) {
    96			MHI_ERR("Failed to enable MSI, ret:%d\n", ret);
    97			goto error_req_msi;
    98		}
    99	
   100		mhi_cntrl->msi_allocated = ret;
   101		mhi_cntrl->irq = kmalloc_array(mhi_cntrl->msi_allocated,
   102					       sizeof(*mhi_cntrl->irq), GFP_KERNEL);
   103		if (!mhi_cntrl->irq) {
   104			ret = -ENOMEM;
   105			goto error_alloc_msi_vec;
   106		}
   107	
   108		for (i = 0; i < mhi_cntrl->msi_allocated; i++) {
 > 109			mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
   110			if (mhi_cntrl->irq[i] < 0) {
   111				ret = mhi_cntrl->irq[i];
   112				goto error_get_irq_vec;
   113			}
   114		}
   115	
   116		dev_set_drvdata(&pci_dev->dev, mhi_cntrl);
   117	
   118		/* configure runtime pm */
   119		pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS);
   120		pm_runtime_use_autosuspend(&pci_dev->dev);
   121		pm_suspend_ignore_children(&pci_dev->dev, true);
   122	
   123		/*
   124		 * pci framework will increment usage count (twice) before
   125		 * calling local device driver probe function.
   126		 * 1st pci.c pci_pm_init() calls pm_runtime_forbid
   127		 * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync
   128		 * Framework expect pci device driver to call
   129		 * pm_runtime_put_noidle to decrement usage count after
   130		 * successful probe and and call pm_runtime_allow to enable
   131		 * runtime suspend.
   132		 */
   133		pm_runtime_mark_last_busy(&pci_dev->dev);
   134		pm_runtime_put_noidle(&pci_dev->dev);
   135	
   136		return 0;
   137	
   138	error_get_irq_vec:
   139		kfree(mhi_cntrl->irq);
   140		mhi_cntrl->irq = NULL;
   141	
   142	error_alloc_msi_vec:
   143		pci_free_irq_vectors(pci_dev);
   144	
   145	error_req_msi:
   146		iounmap(mhi_cntrl->regs);
   147	
   148	error_ioremap:
   149		pci_clear_master(pci_dev);
   150	
   151	error_request_region:
   152		pci_disable_device(pci_dev);
   153	
   154	error_enable_device:
   155		pci_release_region(pci_dev, mhi_dev->resn);
   156	
   157		return ret;
   158	}
   159	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Sujeev Dias April 28, 2018, 3:40 p.m. UTC | #4
On 04/27/2018 04:32 AM, Arnd Bergmann wrote:
> On Fri, Apr 27, 2018 at 4:23 AM, Sujeev Dias <sdias@codeaurora.org> wrote:
>> QCOM PCIe based modems uses MHI as the communication protocol.
>> MHI control driver is the bus master for such modems. As the bus
>> master driver, it oversees power management operations
>> such as suspend, resume, powering on and off the device.
>>
>> +- compatible
>> +  Usage: required
>> +  Value type: <string>
>> +  Definition: "qcom,mhi"
>> +
>> +- qcom,pci-dev-id
>> +  Usage: optional
>> +  Value type: <u32>
>> +  Definition: PCIe device id of external modem to bind. If not set, any
>> +       device is compatible with this node.
>> +
>> +- qcom,pci-domain
>> +  Usage: required
>> +  Value type: <u32>
>> +  Definition: PCIe root complex external modem connected to
>> +
>> +- qcom,pci-bus
>> +  Usage: required
>> +  Value type: <u32>
>> +  Definition: PCIe bus external modem connected to
>> +
>> +- qcom,pci-slot
>> +  Usage: required
>> +  Value type: <u32>
>> +  Definition: PCIe slot as assigned by pci framework to external modem
> These don't seem to make any sense: You seem to have access to
> a regular pci_device already, so why do you need to duplicate the
> information about it in DT?
>
I will remove the platform device, original hardware design we had a 
complicated power on
sequence that require platform device to come up first and follow a 
strict power on sequence to power on modem
before pci device can enumerate.  I stored the BDF in DT to correlate 
the platform device with pci device. platform device
is no longer needed so I can remove it.
>> +- qcom,smmu-cfg
>> +  Usage: required
>> +  Value type: <u32>
>> +  Definition: Required SMMU configuration bitmask for PCIe bus.
>> +       BIT mask:
>> +       BIT(0) : Attach address mapping to endpoint device
>> +       BIT(1) : Set attribute S1_BYPASS
>> +       BIT(2) : Set attribute FAST
>> +       BIT(3) : Set attribute ATOMIC
>> +       BIT(4) : Set attribute FORCE_COHERENT
>> +
>> +- qcom,addr-win
>> +  Usage: required if SMMU S1 translation is enabled
>> +  Value type: Array of <u64>
>> +  Definition: Pair of values describing iova start and stop address
> Why do you need these? Can't that be handled by the PCI
> layer?
I will move this to end point DT.  PCIe end point driver does the iommu 
configuration
>> +- qcom,msm-bus,name
>> +  Usage: required
>> +  Value type: <string>
>> +  Definition: string representing the bus scale client name to register
> This probably belongs into a separate binding for the bus
> scale driver, right?
Yes, this is for qcom bus scale driver which I don't think is upstreamed 
yet. Will confirm.
>> +static struct pci_driver mhi_pcie_driver;
> Please try to reorder the symbols to avoid forward declarations.
>
>> +static int mhi_platform_probe(struct platform_device *pdev)
>> +{
>> +       struct mhi_controller *mhi_cntrl;
>> +       struct mhi_dev *mhi_dev;
>> +       struct device_node *of_node = pdev->dev.of_node;
>> +       u64 addr_win[2];
>> +       int ret;
>> +
>> +       if (!of_node)
>> +               return -ENODEV;
>> +
>> +       mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
>> +       if (!mhi_cntrl)
>> +               return -ENOMEM;
>> +
>> +       mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
>> +
>> +       /* get pci bus topology for this node */
>> +       ret = of_property_read_u32(of_node, "qcom,pci-dev-id",
>> +                                  &mhi_cntrl->dev_id);
>> +       if (ret)
>> +               mhi_cntrl->dev_id = PCI_ANY_ID;
>> +
>> +       ret = of_property_read_u32(of_node, "qcom,pci-domain",
>> +                                  &mhi_cntrl->domain);
>> +       if (ret)
>> +               goto error_probe;
>> +
>> +       ret = of_property_read_u32(of_node, "qcom,pci-bus", &mhi_cntrl->bus);
>> +       if (ret)
>> +               goto error_probe;
>> +
>> +       ret = of_property_read_u32(of_node, "qcom,pci-slot", &mhi_cntrl->slot);
>> +       if (ret)
>> +               goto error_probe;
> Please explain what you are trying to do here, why do you register
> two device drivers? It looks like they both refer to the same
> hardware, so why isn't it sufficient to have the pci_driver?
As I explained earlier, it's now. Original hardware design we had 
chicken egg situation where
some driver has to come up and power on device before pcie enumeration 
can take place.

>
>         Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Thanks again
Sujeev
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/bus/mhi_qcom.txt b/Documentation/devicetree/bindings/bus/mhi_qcom.txt
new file mode 100644
index 0000000..c0f8d86
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/mhi_qcom.txt
@@ -0,0 +1,110 @@ 
+Qualcomm Technologies Inc MHI Bus controller
+
+MHI control driver enables clients to communicate with external mode
+using MHI protocol.
+
+==============
+Node Structure
+==============
+
+Main node properties:
+
+- compatible
+  Usage: required
+  Value type: <string>
+  Definition: "qcom,mhi"
+
+- qcom,pci-dev-id
+  Usage: optional
+  Value type: <u32>
+  Definition: PCIe device id of external modem to bind. If not set, any
+	device is compatible with this node.
+
+- qcom,pci-domain
+  Usage: required
+  Value type: <u32>
+  Definition: PCIe root complex external modem connected to
+
+- qcom,pci-bus
+  Usage: required
+  Value type: <u32>
+  Definition: PCIe bus external modem connected to
+
+- qcom,pci-slot
+  Usage: required
+  Value type: <u32>
+  Definition: PCIe slot as assigned by pci framework to external modem
+
+- qcom,smmu-cfg
+  Usage: required
+  Value type: <u32>
+  Definition: Required SMMU configuration bitmask for PCIe bus.
+	BIT mask:
+	BIT(0) : Attach address mapping to endpoint device
+	BIT(1) : Set attribute S1_BYPASS
+	BIT(2) : Set attribute FAST
+	BIT(3) : Set attribute ATOMIC
+	BIT(4) : Set attribute FORCE_COHERENT
+
+- qcom,addr-win
+  Usage: required if SMMU S1 translation is enabled
+  Value type: Array of <u64>
+  Definition: Pair of values describing iova start and stop address
+
+- qcom,msm-bus,name
+  Usage: required
+  Value type: <string>
+  Definition: string representing the bus scale client name to register
+
+- qcom,msm-bus,num-cases
+  Usage: required
+  Value type: <u32>
+  Definition: Must be set to two, MHI support two scales
+
+- qcom,msm-bus,num-paths
+  Usage: required
+  Value type: <u32>
+  Definition: Total number of master-slave pairs MHI host will vote. Must be set
+	to one.
+
+- qcom,msm-bus,vectors-KBps
+  Usage: required
+  Value type: Array of <u32>
+  Definition: Array of tuples which define the bus bandwidth requirements.
+	Each tuple is of length 4, values are master-id, slave-id,
+	arbitrated bandwidth in KBps, and instantaneous bandwidth in
+	KBps.
+
+- esoc-names
+  Usage: optional
+  Value type: <string>
+  Definition: if external modem managed by esoc framework, set string to "mdm"
+
+- esoc-0
+  Usage: required if device is managed by esoc framework
+  Value type: phandle
+  Definition: A esoc phandle pointing to external modem
+
+- MHI bus settings
+  Usage: required
+  Values: as defined by mhi.txt
+  Definition: Per definition of devicetree/bindings/bus/mhi.txt, define device
+	specific MHI configuration parameters.
+
+========
+Example:
+========
+qcom,mhi {
+	compatible = "qcom,mhi";
+	qcom,pci-domain = <0>;
+	qcom,pci-bus = <1>;
+	qcom,pci-slot = <0>;
+	qcom,smmu-cfg = <0x3d>;
+	qcom,addr-win = <0x0 0x20000000 0x0 0x3fffffff>;
+	qcom,msm-bus,name = "mhi";
+	qcom,msm-bus,num-cases = <2>;
+	qcom,msm-bus,num-paths = <1>;
+	qcom,msm-bus,vectors-KBps = <45 512 0 0>,
+				    <45 512 1200000000 650000000>;
+	<mhi bus configurations>
+};
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index e15d56d..fb28002 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -189,5 +189,6 @@  config MHI_DEBUG
 	   will be logged.
 
 source "drivers/bus/fsl-mc/Kconfig"
+source drivers/bus/mhi/controllers/Kconfig
 
 endmenu
diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile
index 9f8f3ac..c6a2a91 100644
--- a/drivers/bus/mhi/Makefile
+++ b/drivers/bus/mhi/Makefile
@@ -4,5 +4,5 @@ 
 
 # core layer
 obj-y += core/
-#obj-y += controllers/
+obj-y += controllers/
 #obj-y += devices/
diff --git a/drivers/bus/mhi/controllers/Kconfig b/drivers/bus/mhi/controllers/Kconfig
new file mode 100644
index 0000000..e8a90b6
--- /dev/null
+++ b/drivers/bus/mhi/controllers/Kconfig
@@ -0,0 +1,10 @@ 
+menu "MHI controllers"
+
+config MHI_QCOM
+       tristate "MHI QCOM"
+       depends on MHI_BUS
+       help
+	  If you say yes to this option, MHI bus support for QCOM modem chipsets
+	  will be enabled.
+
+endmenu
diff --git a/drivers/bus/mhi/controllers/Makefile b/drivers/bus/mhi/controllers/Makefile
new file mode 100644
index 0000000..292f696
--- /dev/null
+++ b/drivers/bus/mhi/controllers/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_MHI_QCOM) += mhi_qcom.o
diff --git a/drivers/bus/mhi/controllers/mhi_qcom.c b/drivers/bus/mhi/controllers/mhi_qcom.c
new file mode 100644
index 0000000..642981a
--- /dev/null
+++ b/drivers/bus/mhi/controllers/mhi_qcom.c
@@ -0,0 +1,686 @@ 
+/* Copyright (c) 2018, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-direction.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mhi.h>
+#include "mhi_qcom.h"
+
+static struct pci_device_id mhi_pcie_device_id[] = {
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0300)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0301)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0302)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)},
+	{0},
+};
+
+static struct pci_driver mhi_pcie_driver;
+
+void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
+{
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	struct pci_dev *pci_dev = mhi_dev->pci_dev;
+
+	pci_free_irq_vectors(pci_dev);
+	kfree(mhi_cntrl->irq);
+	mhi_cntrl->irq = NULL;
+	iounmap(mhi_cntrl->regs);
+	mhi_cntrl->regs = NULL;
+	pci_clear_master(pci_dev);
+	pci_release_region(pci_dev, mhi_dev->resn);
+	pci_disable_device(pci_dev);
+}
+
+static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)
+{
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	struct pci_dev *pci_dev = mhi_dev->pci_dev;
+	int ret;
+	resource_size_t start, len;
+	int i;
+
+	mhi_dev->resn = MHI_PCI_BAR_NUM;
+	ret = pci_assign_resource(pci_dev, mhi_dev->resn);
+	if (ret) {
+		MHI_ERR("Error assign pci resources, ret:%d\n", ret);
+		return ret;
+	}
+
+	ret = pci_enable_device(pci_dev);
+	if (ret) {
+		MHI_ERR("Error enabling device, ret:%d\n", ret);
+		goto error_enable_device;
+	}
+
+	ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
+	if (ret) {
+		MHI_ERR("Error pci_request_region, ret:%d\n", ret);
+		goto error_request_region;
+	}
+
+	pci_set_master(pci_dev);
+
+	start = pci_resource_start(pci_dev, mhi_dev->resn);
+	len = pci_resource_len(pci_dev, mhi_dev->resn);
+	mhi_cntrl->regs = ioremap_nocache(start, len);
+	if (!mhi_cntrl->regs) {
+		MHI_ERR("Error ioremap region\n");
+		goto error_ioremap;
+	}
+
+	ret = pci_alloc_irq_vectors(pci_dev, mhi_cntrl->msi_required,
+				    mhi_cntrl->msi_required, PCI_IRQ_MSI);
+	if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) {
+		MHI_ERR("Failed to enable MSI, ret:%d\n", ret);
+		goto error_req_msi;
+	}
+
+	mhi_cntrl->msi_allocated = ret;
+	mhi_cntrl->irq = kmalloc_array(mhi_cntrl->msi_allocated,
+				       sizeof(*mhi_cntrl->irq), GFP_KERNEL);
+	if (!mhi_cntrl->irq) {
+		ret = -ENOMEM;
+		goto error_alloc_msi_vec;
+	}
+
+	for (i = 0; i < mhi_cntrl->msi_allocated; i++) {
+		mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
+		if (mhi_cntrl->irq[i] < 0) {
+			ret = mhi_cntrl->irq[i];
+			goto error_get_irq_vec;
+		}
+	}
+
+	dev_set_drvdata(&pci_dev->dev, mhi_cntrl);
+
+	/* configure runtime pm */
+	pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS);
+	pm_runtime_use_autosuspend(&pci_dev->dev);
+	pm_suspend_ignore_children(&pci_dev->dev, true);
+
+	/*
+	 * pci framework will increment usage count (twice) before
+	 * calling local device driver probe function.
+	 * 1st pci.c pci_pm_init() calls pm_runtime_forbid
+	 * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync
+	 * Framework expect pci device driver to call
+	 * pm_runtime_put_noidle to decrement usage count after
+	 * successful probe and and call pm_runtime_allow to enable
+	 * runtime suspend.
+	 */
+	pm_runtime_mark_last_busy(&pci_dev->dev);
+	pm_runtime_put_noidle(&pci_dev->dev);
+
+	return 0;
+
+error_get_irq_vec:
+	kfree(mhi_cntrl->irq);
+	mhi_cntrl->irq = NULL;
+
+error_alloc_msi_vec:
+	pci_free_irq_vectors(pci_dev);
+
+error_req_msi:
+	iounmap(mhi_cntrl->regs);
+
+error_ioremap:
+	pci_clear_master(pci_dev);
+
+error_request_region:
+	pci_disable_device(pci_dev);
+
+error_enable_device:
+	pci_release_region(pci_dev, mhi_dev->resn);
+
+	return ret;
+}
+
+static int mhi_runtime_suspend(struct device *dev)
+{
+	int ret = 0;
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	MHI_LOG("Enter\n");
+
+	mutex_lock(&mhi_cntrl->pm_mutex);
+
+	ret = mhi_pm_suspend(mhi_cntrl);
+	if (ret) {
+		MHI_LOG("Abort due to ret:%d\n", ret);
+		goto exit_runtime_suspend;
+	}
+
+	ret = mhi_arch_link_off(mhi_cntrl, true);
+	if (ret)
+		MHI_ERR("Failed to Turn off link ret:%d\n", ret);
+
+exit_runtime_suspend:
+	mutex_unlock(&mhi_cntrl->pm_mutex);
+	MHI_LOG("Exited with ret:%d\n", ret);
+
+	return ret;
+}
+
+static int mhi_runtime_idle(struct device *dev)
+{
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	MHI_LOG("Entered returning -EBUSY\n");
+
+	/*
+	 * RPM framework during runtime resume always calls
+	 * rpm_idle to see if device ready to suspend.
+	 * If dev.power usage_count count is 0, rpm fw will call
+	 * rpm_idle cb to see if device is ready to suspend.
+	 * if cb return 0, or cb not defined the framework will
+	 * assume device driver is ready to suspend;
+	 * therefore, fw will schedule runtime suspend.
+	 * In MHI power management, MHI host shall go to
+	 * runtime suspend only after entering MHI State M2, even if
+	 * usage count is 0.  Return -EBUSY to disable automatic suspend.
+	 */
+	return -EBUSY;
+}
+
+static int mhi_runtime_resume(struct device *dev)
+{
+	int ret = 0;
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	MHI_LOG("Enter\n");
+
+	mutex_lock(&mhi_cntrl->pm_mutex);
+
+	if (!mhi_dev->powered_on) {
+		MHI_LOG("Not fully powered, return success\n");
+		mutex_unlock(&mhi_cntrl->pm_mutex);
+		return 0;
+	}
+
+	/* turn on link */
+	ret = mhi_arch_link_on(mhi_cntrl);
+	if (ret)
+		goto rpm_resume_exit;
+
+	/* enter M0 state */
+	ret = mhi_pm_resume(mhi_cntrl);
+
+rpm_resume_exit:
+	mutex_unlock(&mhi_cntrl->pm_mutex);
+	MHI_LOG("Exited with :%d\n", ret);
+
+	return ret;
+}
+
+static int mhi_system_resume(struct device *dev)
+{
+	int ret = 0;
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	ret = mhi_runtime_resume(dev);
+	if (ret) {
+		MHI_ERR("Failed to resume link\n");
+	} else {
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+	}
+
+	return ret;
+}
+
+int mhi_system_suspend(struct device *dev)
+{
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	MHI_LOG("Entered\n");
+
+	/* if rpm status still active then force suspend */
+	if (!pm_runtime_status_suspended(dev))
+		return mhi_runtime_suspend(dev);
+
+	pm_runtime_set_suspended(dev);
+	pm_runtime_disable(dev);
+
+	MHI_LOG("Exit\n");
+	return 0;
+}
+
+/* checks if link is down */
+static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
+{
+	struct mhi_dev *mhi_dev = priv;
+	u16 dev_id;
+	int ret;
+
+	/* try reading device id, if dev id don't match, link is down */
+	ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id);
+
+	return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0;
+}
+
+static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv)
+{
+	struct mhi_dev *mhi_dev = priv;
+	struct device *dev = &mhi_dev->pci_dev->dev;
+
+	return pm_runtime_get(dev);
+}
+
+static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv)
+{
+	struct mhi_dev *mhi_dev = priv;
+	struct device *dev = &mhi_dev->pci_dev->dev;
+
+	pm_runtime_put_noidle(dev);
+}
+
+static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
+			  void *priv,
+			  enum MHI_CB reason)
+{
+	struct mhi_dev *mhi_dev = priv;
+	struct device *dev = &mhi_dev->pci_dev->dev;
+
+	if (reason == MHI_CB_IDLE) {
+		MHI_LOG("Schedule runtime suspend 1\n");
+		pm_runtime_mark_last_busy(dev);
+		pm_request_autosuspend(dev);
+	}
+}
+
+int mhi_debugfs_trigger_m0(void *data, u64 val)
+{
+	struct mhi_controller *mhi_cntrl = data;
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	MHI_LOG("Trigger M3 Exit\n");
+	pm_runtime_get(&mhi_dev->pci_dev->dev);
+	pm_runtime_put(&mhi_dev->pci_dev->dev);
+
+	return 0;
+}
+
+int mhi_debugfs_trigger_m3(void *data, u64 val)
+{
+	struct mhi_controller *mhi_cntrl = data;
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	MHI_LOG("Trigger M3 Entry\n");
+	pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev);
+	pm_request_autosuspend(&mhi_dev->pci_dev->dev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL,
+			mhi_debugfs_trigger_m0, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL,
+			mhi_debugfs_trigger_m3, "%llu\n");
+
+static int mhi_init_debugfs_trigger_go(void *data, u64 val)
+{
+	struct mhi_controller *mhi_cntrl = data;
+
+	MHI_LOG("Trigger power up sequence\n");
+
+	mhi_async_power_up(mhi_cntrl);
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL,
+			mhi_init_debugfs_trigger_go, "%llu\n");
+
+
+int mhi_init_debugfs_debug_show(struct seq_file *m, void *d)
+{
+	seq_puts(m, "Enable debug mode to debug  external soc\n");
+	seq_puts(m,
+		 "Usage:  echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n");
+	seq_puts(m, "No spaces between parameters\n");
+	seq_puts(m, "\t1.  devid : 0 or pci device id to register\n");
+	seq_puts(m, "\t2.  timeout: mhi cmd/state transition timeout\n");
+	seq_puts(m, "\t3.  domain: Rootcomplex\n");
+	seq_puts(m, "\t4.  smmu_cfg: smmu configuration mask:\n");
+	seq_puts(m, "\t\t- BIT0: ATTACH\n");
+	seq_puts(m, "\t\t- BIT1: S1 BYPASS\n");
+	seq_puts(m, "\t\t-BIT2: FAST_MAP\n");
+	seq_puts(m, "\t\t-BIT3: ATOMIC\n");
+	seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n");
+	seq_puts(m, "\t\t-BIT5: GEOMETRY\n");
+	seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n");
+	seq_puts(m, "Examples inputs: '0x307,10000'\n");
+	seq_puts(m, "\techo '0,10000,1'\n");
+	seq_puts(m, "\techo '0x307,10000,0,0x3d'\n");
+	seq_puts(m, "firmware image name will be changed to debug.mbn\n");
+
+	return 0;
+}
+
+static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file)
+{
+	return single_open(file, mhi_init_debugfs_debug_show, NULL);
+}
+
+static ssize_t mhi_init_debugfs_debug_write(struct file *fp,
+					    const char __user *ubuf,
+					    size_t count,
+					    loff_t *pos)
+{
+	char *buf = kmalloc(count + 1, GFP_KERNEL);
+	/* #,devid,timeout,domain,smmu-cfg */
+	int args[5] = {0};
+	static char const *dbg_fw = "debug.mbn";
+	int ret;
+	struct mhi_controller *mhi_cntrl = fp->f_inode->i_private;
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	struct pci_device_id *id;
+
+	if (!buf)
+		return -ENOMEM;
+
+	ret = copy_from_user(buf, ubuf, count);
+	if (ret)
+		goto error_read;
+	buf[count] = 0;
+	get_options(buf, ARRAY_SIZE(args), args);
+	kfree(buf);
+
+	/* override default parameters */
+	mhi_cntrl->fw_image = dbg_fw;
+	mhi_cntrl->edl_image = dbg_fw;
+
+	if (args[0] >= 2 && args[2])
+		mhi_cntrl->timeout_ms = args[2];
+
+	if (args[0] >= 3 && args[3])
+		mhi_cntrl->domain = args[3];
+
+	if (args[0] >= 4 && args[4])
+		mhi_dev->smmu_cfg = args[4];
+
+	/* If it's a new device id register it */
+	if (args[0] && args[1]) {
+		/* find the debug_id  and overwrite it */
+		for (id = mhi_pcie_device_id; id->vendor; id++)
+			if (id->device == MHI_PCIE_DEBUG_ID) {
+				id->device = args[1];
+				pci_unregister_driver(&mhi_pcie_driver);
+				ret = pci_register_driver(&mhi_pcie_driver);
+			}
+	}
+
+	mhi_dev->debug_mode = true;
+	debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl,
+			    &mhi_init_debugfs_trigger_go_fops);
+	pr_info(
+		"%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n",
+		__func__, ret, args[1], mhi_dev->smmu_cfg,
+		mhi_cntrl->timeout_ms);
+	return count;
+
+error_read:
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations debugfs_debug_ops = {
+	.open = mhi_init_debugfs_debug_open,
+	.release = single_release,
+	.read = seq_read,
+	.write = mhi_init_debugfs_debug_write,
+};
+
+int mhi_pci_probe(struct pci_dev *pci_dev,
+		  const struct pci_device_id *device_id)
+{
+	struct mhi_controller *mhi_cntrl = NULL;
+	u32 domain = pci_domain_nr(pci_dev->bus);
+	u32 bus = pci_dev->bus->number;
+	/* first match to exact DT node, if not match to any free DT */
+	u32 dev_id[] = {pci_dev->device, PCI_ANY_ID};
+	u32 slot = PCI_SLOT(pci_dev->devfn);
+	struct mhi_dev *mhi_dev;
+	int i, ret;
+
+	/* find a matching controller */
+	for (i = 0; i < ARRAY_SIZE(dev_id); i++) {
+		mhi_cntrl = mhi_bdf_to_controller(domain, bus, slot, dev_id[i]);
+		if (mhi_cntrl)
+			break;
+	}
+
+	if (!mhi_cntrl)
+		return -EPROBE_DEFER;
+
+	mhi_cntrl->dev_id = pci_dev->device;
+	mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	mhi_dev->pci_dev = pci_dev;
+	mhi_dev->powered_on = true;
+
+	ret = mhi_arch_pcie_init(mhi_cntrl);
+	if (ret)
+		return ret;
+
+	ret = mhi_arch_iommu_init(mhi_cntrl);
+	if (ret)
+		goto error_iommu_init;
+
+	ret = mhi_init_pci_dev(mhi_cntrl);
+	if (ret)
+		goto error_init_pci;
+
+	/* start power up sequence if not in debug mode */
+	if (!mhi_dev->debug_mode) {
+		ret = mhi_async_power_up(mhi_cntrl);
+		if (ret)
+			goto error_power_up;
+	}
+
+	pm_runtime_mark_last_busy(&pci_dev->dev);
+	pm_runtime_allow(&pci_dev->dev);
+
+	if (mhi_cntrl->dentry) {
+		debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl,
+				    &debugfs_trigger_m0_fops);
+		debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl,
+				    &debugfs_trigger_m3_fops);
+	}
+
+	MHI_LOG("Return successful\n");
+
+	return 0;
+
+error_power_up:
+	mhi_deinit_pci_dev(mhi_cntrl);
+
+error_init_pci:
+	mhi_arch_iommu_deinit(mhi_cntrl);
+
+error_iommu_init:
+	mhi_arch_pcie_deinit(mhi_cntrl);
+
+	return ret;
+}
+
+static const struct of_device_id mhi_plat_match[] = {
+	{ .compatible = "qcom,mhi" },
+	{},
+};
+
+static int mhi_platform_probe(struct platform_device *pdev)
+{
+	struct mhi_controller *mhi_cntrl;
+	struct mhi_dev *mhi_dev;
+	struct device_node *of_node = pdev->dev.of_node;
+	u64 addr_win[2];
+	int ret;
+
+	if (!of_node)
+		return -ENODEV;
+
+	mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
+	if (!mhi_cntrl)
+		return -ENOMEM;
+
+	mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	/* get pci bus topology for this node */
+	ret = of_property_read_u32(of_node, "qcom,pci-dev-id",
+				   &mhi_cntrl->dev_id);
+	if (ret)
+		mhi_cntrl->dev_id = PCI_ANY_ID;
+
+	ret = of_property_read_u32(of_node, "qcom,pci-domain",
+				   &mhi_cntrl->domain);
+	if (ret)
+		goto error_probe;
+
+	ret = of_property_read_u32(of_node, "qcom,pci-bus", &mhi_cntrl->bus);
+	if (ret)
+		goto error_probe;
+
+	ret = of_property_read_u32(of_node, "qcom,pci-slot", &mhi_cntrl->slot);
+	if (ret)
+		goto error_probe;
+
+	ret = of_property_read_u32(of_node, "qcom,smmu-cfg",
+				   &mhi_dev->smmu_cfg);
+	if (ret)
+		goto error_probe;
+
+	/* if s1 translation enabled pull iova addr from dt */
+	if (mhi_dev->smmu_cfg & MHI_SMMU_ATTACH &&
+	    !(mhi_dev->smmu_cfg & MHI_SMMU_S1_BYPASS)) {
+		ret = of_property_count_elems_of_size(of_node, "qcom,addr-win",
+						      sizeof(addr_win));
+		if (ret != 1)
+			goto error_probe;
+		ret = of_property_read_u64_array(of_node, "qcom,addr-win",
+						 addr_win, 2);
+		if (ret)
+			goto error_probe;
+	} else {
+		addr_win[0] = memblock_start_of_DRAM();
+		addr_win[1] = memblock_end_of_DRAM();
+	}
+
+	mhi_dev->iova_start = addr_win[0];
+	mhi_dev->iova_stop = addr_win[1];
+
+	/*
+	 * if S1 is enabled, set MHI_CTRL start address to 0 so we can use low
+	 * level mapping api to map buffers outside of smmu domain
+	 */
+	if (mhi_dev->smmu_cfg & MHI_SMMU_ATTACH &&
+	    !(mhi_dev->smmu_cfg & MHI_SMMU_S1_BYPASS))
+		mhi_cntrl->iova_start = 0;
+	else
+		mhi_cntrl->iova_start = addr_win[0];
+
+	mhi_cntrl->iova_stop = mhi_dev->iova_stop;
+	mhi_cntrl->of_node = of_node;
+
+	/* setup power management apis */
+	mhi_cntrl->status_cb = mhi_status_cb;
+	mhi_cntrl->runtime_get = mhi_runtime_get;
+	mhi_cntrl->runtime_put = mhi_runtime_put;
+	mhi_cntrl->link_status = mhi_link_status;
+
+	mhi_dev->pdev = pdev;
+
+	ret = mhi_arch_platform_init(mhi_dev);
+	if (ret)
+		goto error_probe;
+
+	ret = of_register_mhi_controller(mhi_cntrl);
+	if (ret)
+		goto error_register;
+
+	if (mhi_cntrl->parent)
+		debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent,
+				    mhi_cntrl, &debugfs_debug_ops);
+
+	return 0;
+
+error_register:
+	mhi_arch_platform_deinit(mhi_dev);
+
+error_probe:
+	mhi_free_controller(mhi_cntrl);
+
+	return -EINVAL;
+};
+
+static struct platform_driver mhi_platform_driver = {
+	.probe = mhi_platform_probe,
+	.driver = {
+		.name = "mhi",
+		.owner = THIS_MODULE,
+		.of_match_table = mhi_plat_match,
+	},
+};
+
+static const struct dev_pm_ops pm_ops = {
+	SET_RUNTIME_PM_OPS(mhi_runtime_suspend,
+			   mhi_runtime_resume,
+			   mhi_runtime_idle)
+	SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume)
+};
+
+static struct pci_driver mhi_pcie_driver = {
+	.name = "mhi",
+	.id_table = mhi_pcie_device_id,
+	.probe = mhi_pci_probe,
+	.driver = {
+		.pm = &pm_ops
+	}
+};
+
+static int __init mhi_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&mhi_platform_driver);
+	if (ret)
+		return ret;
+
+	ret = pci_register_driver(&mhi_pcie_driver);
+	if (ret)
+		goto pci_reg_error;
+
+	return ret;
+
+pci_reg_error:
+	platform_driver_unregister(&mhi_platform_driver);
+
+	return ret;
+};
+module_init(mhi_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("MHI_CORE");
+MODULE_DESCRIPTION("MHI Host Driver");
diff --git a/drivers/bus/mhi/controllers/mhi_qcom.h b/drivers/bus/mhi/controllers/mhi_qcom.h
new file mode 100644
index 0000000..4b90916
--- /dev/null
+++ b/drivers/bus/mhi/controllers/mhi_qcom.h
@@ -0,0 +1,85 @@ 
+/* Copyright (c) 2018, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MHI_QCOM_
+#define _MHI_QCOM_
+
+/* iova cfg bitmask */
+#define MHI_SMMU_ATTACH BIT(0)
+#define MHI_SMMU_S1_BYPASS BIT(1)
+#define MHI_SMMU_FAST BIT(2)
+#define MHI_SMMU_ATOMIC BIT(3)
+#define MHI_SMMU_FORCE_COHERENT BIT(4)
+
+#define MHI_PCIE_VENDOR_ID (0x17cb)
+#define MHI_PCIE_DEBUG_ID (0xffff)
+#define MHI_RPM_SUSPEND_TMR_MS (1000)
+#define MHI_PCI_BAR_NUM (0)
+
+struct mhi_dev {
+	struct platform_device *pdev;
+	struct pci_dev *pci_dev;
+	u32 smmu_cfg;
+	int resn;
+	void *arch_info;
+	bool powered_on;
+	bool debug_mode;
+	dma_addr_t iova_start;
+	dma_addr_t iova_stop;
+};
+
+void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
+int mhi_pci_probe(struct pci_dev *pci_dev,
+		  const struct pci_device_id *device_id);
+
+static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
+{
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
+
+	return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
+}
+
+static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
+{
+	return 0;
+}
+
+static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
+{
+	return 0;
+}
+
+static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
+{
+}
+
+static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
+				    bool graceful)
+{
+	return 0;
+}
+
+static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
+{
+	return 0;
+}
+
+#endif /* _MHI_QCOM_ */