diff mbox series

[RFC,v1,05/14] usb:cdns3: Added Wrapper to XCHI driver

Message ID 1541267487-3664-6-git-send-email-pawell@cadence.com (mailing list archive)
State Superseded
Headers show
Series Introduced new Cadence USBSS DRD Driver | expand

Commit Message

Pawel Laszczak Nov. 3, 2018, 5:51 p.m. UTC
Patch implements functions that allow to initialize, start and stop
XHCI host driver.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/cdns3/host.c | 230 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 227 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
index 37300985e2d6..f3b100a27cd6 100644
--- a/drivers/usb/cdns3/host.c
+++ b/drivers/usb/cdns3/host.c
@@ -9,20 +9,244 @@ 
  *	    Pawel Laszczak <pawell@cadence.com>
  */
 
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/of.h>
+
+#include "../host/xhci.h"
 #include "core.h"
+#include "host-export.h"
+
+static struct hc_driver __read_mostly xhci_cdns3_hc_driver;
+
+static void xhci_cdns3_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+	/*
+	 * As of now platform drivers don't provide MSI support so we ensure
+	 * here that the generic code does not try to make a pci_dev from our
+	 * dev struct in order to setup MSI
+	 */
+	xhci->quirks |= XHCI_PLAT;
+}
+
+static int xhci_cdns3_setup(struct usb_hcd *hcd)
+{
+	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
+	u32 command;
+	int ret;
+
+	ret = xhci_gen_setup(hcd, xhci_cdns3_quirks);
+	if (ret)
+		return ret;
+
+	/* set usbcmd.EU3S */
+	command = readl(&xhci->op_regs->command);
+	command |= CMD_PM_INDEX;
+	writel(command, &xhci->op_regs->command);
+
+	return 0;
+}
+
+static const struct xhci_driver_overrides xhci_cdns3_overrides __initconst = {
+	.extra_priv_size = sizeof(struct xhci_hcd),
+	.reset = xhci_cdns3_setup,
+};
+
+struct cdns3_host {
+	struct device dev;
+	struct usb_hcd *hcd;
+};
+
+static irqreturn_t cdns3_host_irq(struct cdns3 *cdns)
+{
+	struct device *dev = cdns->host_dev;
+	struct usb_hcd	*hcd;
+
+	if (dev)
+		hcd = dev_get_drvdata(dev);
+	else
+		return IRQ_NONE;
+
+	if (hcd)
+		return usb_hcd_irq(cdns->irq, hcd);
+	else
+		return IRQ_NONE;
+}
+
+static void cdns3_host_release(struct device *dev)
+{
+	struct cdns3_host *host = container_of(dev, struct cdns3_host, dev);
+
+	kfree(host);
+}
+
+static int cdns3_host_start(struct cdns3 *cdns)
+{
+	struct cdns3_host *host;
+	struct device *dev;
+	struct device *sysdev;
+	struct xhci_hcd	*xhci;
+	int ret;
+
+	host = kzalloc(sizeof(*host), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	dev = &host->dev;
+	dev->release = cdns3_host_release;
+	dev->parent = cdns->dev;
+	dev_set_name(dev, "xhci-cdns3");
+	cdns->host_dev = dev;
+	ret = device_register(dev);
+	if (ret)
+		goto err1;
+
+	sysdev = cdns->dev;
+	/* Try to set 64-bit DMA first */
+	if (WARN_ON(!sysdev->dma_mask))
+		/* Platform did not initialize dma_mask */
+		ret = dma_coerce_mask_and_coherent(sysdev,
+						   DMA_BIT_MASK(64));
+	else
+		ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
+
+	/* If setting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
+	if (ret) {
+		ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(32));
+		if (ret)
+			return ret;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_no_callbacks(dev);
+	pm_runtime_enable(dev);
+	host->hcd = __usb_create_hcd(&xhci_cdns3_hc_driver, sysdev, dev,
+				     dev_name(dev), NULL);
+	if (!host->hcd) {
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	host->hcd->regs = cdns->xhci_regs;
+	host->hcd->rsrc_start = cdns->xhci_res->start;
+	host->hcd->rsrc_len = resource_size(cdns->xhci_res);
+
+	device_wakeup_enable(host->hcd->self.controller);
+	xhci = hcd_to_xhci(host->hcd);
+
+	xhci->main_hcd = host->hcd;
+	xhci->shared_hcd = __usb_create_hcd(&xhci_cdns3_hc_driver, sysdev, dev,
+					    dev_name(dev), host->hcd);
+	if (!xhci->shared_hcd) {
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	host->hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
+	xhci->shared_hcd->tpl_support = host->hcd->tpl_support;
+	ret = usb_add_hcd(host->hcd, 0, IRQF_SHARED);
+	if (ret)
+		goto err4;
+
+	ret = usb_add_hcd(xhci->shared_hcd, 0, IRQF_SHARED);
+	if (ret)
+		goto err5;
+
+	device_set_wakeup_capable(dev, true);
+
+	return 0;
+
+err5:
+	usb_remove_hcd(host->hcd);
+err4:
+	usb_put_hcd(xhci->shared_hcd);
+err3:
+	usb_put_hcd(host->hcd);
+err2:
+	device_del(dev);
+err1:
+	put_device(dev);
+	cdns->host_dev = NULL;
+	return ret;
+}
+
+static void cdns3_host_stop(struct cdns3 *cdns)
+{
+	struct device *dev = cdns->host_dev;
+	struct xhci_hcd	*xhci;
+	struct usb_hcd	*hcd;
+
+	if (dev) {
+		hcd = dev_get_drvdata(dev);
+		xhci = hcd_to_xhci(hcd);
+		usb_remove_hcd(xhci->shared_hcd);
+		usb_remove_hcd(hcd);
+		synchronize_irq(cdns->irq);
+		usb_put_hcd(xhci->shared_hcd);
+		usb_put_hcd(hcd);
+		cdns->host_dev = NULL;
+		pm_runtime_set_suspended(dev);
+		pm_runtime_disable(dev);
+		device_del(dev);
+		put_device(dev);
+	}
+}
+
+static int cdns3_host_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+	struct device *dev = cdns->host_dev;
+	struct xhci_hcd	*xhci;
+
+	if (!dev)
+		return 0;
+
+	xhci = hcd_to_xhci(dev_get_drvdata(dev));
+	return xhci_suspend(xhci, do_wakeup);
+}
+
+static int cdns3_host_resume(struct cdns3 *cdns, bool hibernated)
+{
+	struct device *dev = cdns->host_dev;
+	struct xhci_hcd	*xhci;
+
+	if (!dev)
+		return 0;
+
+	xhci = hcd_to_xhci(dev_get_drvdata(dev));
+	return xhci_resume(xhci, hibernated);
+}
 
 int cdns3_host_init(struct cdns3 *cdns)
 {
-	//TODO: implements this function
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= cdns3_host_start;
+	rdrv->stop	= cdns3_host_stop;
+	rdrv->irq	= cdns3_host_irq;
+	rdrv->suspend	= cdns3_host_suspend;
+	rdrv->resume	= cdns3_host_resume;
+	rdrv->name	= "host";
+	cdns->roles[CDNS3_ROLE_HOST] = rdrv;
+
 	return 0;
 }
 
 void cdns3_host_remove(struct cdns3 *cdns)
 {
-  //TODO: implements this function
+	cdns3_host_stop(cdns);
 }
 
 void __init cdns3_host_driver_init(void)
 {
-	//TODO: implements this function
+	xhci_init_driver(&xhci_cdns3_hc_driver, &xhci_cdns3_overrides);
 }