/* * drivers/usb/host/xhci_sunxi.c * (C) Copyright 2010-2015 * Allwinner Technology Co., Ltd. * wangjx, 2016-9-9, create this file * * SoftWinner XHCI Driver * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include "sunxi_hci.h" #include "xhci.h" #ifdef CONFIG_PM static void sunxi_xhci_resume_work(struct work_struct *work); #endif #define SUNXI_XHCI_NAME "sunxi-xhci" static const char xhci_name[] = SUNXI_XHCI_NAME; #define SUNXI_ALIGN_MASK (16 - 1) #ifdef CONFIG_USB_SUNXI_XHCI #define SUNXI_XHCI_OF_MATCH "allwinner,sunxi-xhci" #else #define SUNXI_XHCI_OF_MATCH "NULL" #endif static void sunxi_xhci_open_clock(struct sunxi_hci_hcd *sunxi_xhci); static void sunxi_set_mode(struct sunxi_hci_hcd *sunxi_xhci, u32 mode); static void sunxi_core_soft_reset(void __iomem *regs); static int sunxi_core_open_phy(void __iomem *regs); static struct sunxi_hci_hcd *g_sunxi_xhci; static struct sunxi_hci_hcd *g_dev_data; static ssize_t xhci_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sunxi_hci_hcd *sunxi_xhci = NULL; if (dev == NULL) { DMSG_PANIC("Argment is invalid\n"); return 0; } sunxi_xhci = dev->platform_data; if (sunxi_xhci == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return 0; } return sprintf(buf, "xhci:%d, probe:%u\n", sunxi_xhci->usbc_no, sunxi_xhci->probe); } static ssize_t xhci_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sunxi_hci_hcd *sunxi_xhci = NULL; int value = 0; int ret = 0; if (dev == NULL) { DMSG_PANIC("Argment is invalid\n"); return count; } sunxi_xhci = dev->platform_data; if (sunxi_xhci == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return count; } sunxi_xhci->host_init_state = 0; ret = kstrtoint(buf, 10, &value); if (ret != 0) return -EINVAL; if (value == 1) sunxi_usb_enable_xhci(); else if (value == 0) sunxi_usb_disable_xhci(); else DMSG_INFO("unknown value (%d)\n", value); return count; } static DEVICE_ATTR(xhci_enable, 0664, xhci_enable_show, xhci_enable_store); static int xhci_host2_test_mode(void __iomem *regs, int param) { int reg_value = 0; switch (param) { case TEST_J: DMSG_INFO("xhci_host2_test_mode: TEST_J\n"); break; case TEST_K: DMSG_INFO("xhci_host2_test_mode: TEST_K\n"); break; case TEST_SE0_NAK: DMSG_INFO("xhci_host2_test_mode: TEST_SE0_NAK\n"); break; case TEST_PACKET: DMSG_INFO("xhci_host2_test_mode: TEST_PACKET\n"); break; case TEST_FORCE_EN: DMSG_INFO("xhci_host2_test_mode: TEST_FORCE_EN\n"); break; default: DMSG_INFO("not support test mode(%d)\n", param); return -1; } reg_value = USBC_Readl(regs + XHCI_OP_REGS_HCPORT1SC); reg_value &= ~(0x1 << 9); USBC_Writel(reg_value, regs + XHCI_OP_REGS_HCPORT1SC); msleep(20); reg_value = USBC_Readl(regs + XHCI_OP_REGS_HCUSBCMD); reg_value &= ~(0x1 << 0); USBC_Writel(reg_value, regs + XHCI_OP_REGS_HCUSBCMD); msleep(20); reg_value = USBC_Readl(regs + XHCI_OP_REGS_HCUSBSTS); reg_value &= ~(0x1 << 0); USBC_Writel(reg_value, regs + XHCI_OP_REGS_HCUSBSTS); msleep(20); DMSG_INFO("Halted: 0x%x, param: %d\n", USBC_Readl(regs + XHCI_OP_REGS_HCUSBSTS), param); reg_value = USBC_Readl(regs + XHCI_OP_REGS_HCPORT1PMSC); reg_value |= (param << 28); USBC_Writel(reg_value, regs + XHCI_OP_REGS_HCPORT1PMSC); msleep(20); DMSG_INFO("test_code: %x\n", USBC_Readl(regs + XHCI_OP_REGS_HCPORT1PMSC)); return 0; } static ssize_t ed_test_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; if (dev == NULL) { DMSG_PANIC("Argment is invalid\n"); return 0; } sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return 0; } return sprintf(buf, "USB2.0 host test mode:\n" "echo:\ntest_j_state\ntest_k_state\ntest_se0_nak\n" "test_pack\ntest_force_enable\n\n" "USB3.0 host test mode:\n" "echo:\ntest_host3\n"); } static ssize_t ed_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; struct usb_hcd *hcd = NULL; u32 testmode = 0; if (dev == NULL) { DMSG_PANIC("Argment is invalid\n"); return count; } sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return count; } if (dev_data->probe == 0) { DMSG_INFO("[%s]: is disable, can not enter test mode\n", dev_data->hci_name); return count; } hcd = dev_get_drvdata(&sunxi_xhci->pdev->dev); if (hcd == NULL) { DMSG_PANIC("xhci hcd is null\n"); return count; } /* USB3.0 test mode */ if (!strncmp(buf, "test_host3", 10)) { DMSG_INFO("xhci usb3.0 host test mode\n"); sunxi_usb_disable_xhci(); sunxi_xhci_open_clock(dev_data); sunxi_core_open_phy(sunxi_xhci->regs); sunxi_core_soft_reset(sunxi_xhci->regs); sunxi_set_mode(sunxi_xhci, SUNXI_GCTL_PRTCAP_HOST); return count; } /* USB2.0 test mode */ if (!strncmp(buf, "test_j_state", 12)) testmode = TEST_J; else if (!strncmp(buf, "test_k_state", 12)) testmode = TEST_K; else if (!strncmp(buf, "test_se0_nak", 12)) testmode = TEST_SE0_NAK; else if (!strncmp(buf, "test_pack", 9)) testmode = TEST_PACKET; else if (!strncmp(buf, "test_force_enable", 17)) testmode = TEST_FORCE_EN; else testmode = 0; xhci_host2_test_mode(hcd->regs, testmode); return count; } static DEVICE_ATTR(ed_test, 0664, ed_test_show, ed_test_store); /** * sunxi_core_soft_reset - Issues core soft reset and PHY reset * @sunxi_xhci: pointer to our context structure */ static void sunxi_core_soft_reset(void __iomem *regs) { int reg = 0; /* Before Resetting PHY, put Core in Reset */ reg = USBC_Readl(regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); reg |= SUNXI_GCTL_CORESOFTRESET; USBC_Writel(reg, regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); /* Assert USB3 PHY reset */ reg = USBC_Readl(regs + (SUNXI_GUSB3PIPECTL(0) - SUNXI_GLOBALS_REGS_START)); reg |= SUNXI_USB3PIPECTL_PHYSOFTRST; USBC_Writel(reg, regs + (SUNXI_GUSB3PIPECTL(0) - SUNXI_GLOBALS_REGS_START)); /* Assert USB2 PHY reset */ reg = USBC_Readl(regs + (SUNXI_GUSB2PHYCFG(0) - SUNXI_GLOBALS_REGS_START)); reg |= SUNXI_USB2PHYCFG_PHYSOFTRST; USBC_Writel(reg, regs + (SUNXI_GUSB2PHYCFG(0) - SUNXI_GLOBALS_REGS_START)); mdelay(100); /* Clear USB3 PHY reset */ reg = USBC_Readl(regs + (SUNXI_GUSB3PIPECTL(0) - SUNXI_GLOBALS_REGS_START)); reg &= ~SUNXI_USB3PIPECTL_PHYSOFTRST; USBC_Writel(reg, regs + (SUNXI_GUSB3PIPECTL(0) - SUNXI_GLOBALS_REGS_START)); /* Clear USB2 PHY reset */ reg = USBC_Readl(regs + (SUNXI_GUSB2PHYCFG(0) - SUNXI_GLOBALS_REGS_START)); reg &= ~SUNXI_USB2PHYCFG_PHYSOFTRST; USBC_Writel(reg, regs + (SUNXI_GUSB2PHYCFG(0) - SUNXI_GLOBALS_REGS_START)); mdelay(100); /* After PHYs are stable we can take Core out of reset state */ reg = USBC_Readl(regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); reg &= ~SUNXI_GCTL_CORESOFTRESET; USBC_Writel(reg, regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); } static int sunxi_core_open_phy(void __iomem *regs) { int reg_val = 0; reg_val = USBC_Readl(regs + (SUNXI_PHY_EXTERNAL_CONTROL - SUNXI_GLOBALS_REGS_START)); reg_val |= SUNXI_PEC_EXTERN_VBUS; /* Use extern vbus to phy */ reg_val |= SUNXI_PEC_SSC_EN; /* SSC_EN */ reg_val |= SUNXI_PEC_REF_SSP_EN; /*REF_SSP_EN */ USBC_Writel(reg_val, regs + (SUNXI_PHY_EXTERNAL_CONTROL - SUNXI_GLOBALS_REGS_START)); reg_val = USBC_Readl(regs + (SUNXI_PIPE_CLOCK_CONTROL - SUNXI_GLOBALS_REGS_START)); reg_val |= SUNXI_PPC_PIPE_CLK_OPEN; /* open PIPE clock */ USBC_Writel(reg_val, regs + (SUNXI_PIPE_CLOCK_CONTROL - SUNXI_GLOBALS_REGS_START)); reg_val = USBC_Readl(regs + (SUNXI_APP - SUNXI_GLOBALS_REGS_START)); reg_val |= SUNXI_APP_FOCE_VBUS; /* open PIPE clock */ USBC_Writel(reg_val, regs + (SUNXI_APP - SUNXI_GLOBALS_REGS_START)); /* It is set 0x0047fc87 on bare-metal. */ USBC_Writel(0x0047fc87, regs + (SUNXI_PHY_TUNE_LOW - SUNXI_GLOBALS_REGS_START)); reg_val = USBC_Readl(regs + (SUNXI_PHY_TUNE_HIGH - SUNXI_GLOBALS_REGS_START)); reg_val |= SUNXI_TXVBOOSTLVL(0x7); reg_val |= SUNXI_LOS_BIAS(0x7); reg_val &= ~(SUNXI_TX_SWING_FULL(0x7f)); reg_val |= SUNXI_TX_SWING_FULL(0x55); reg_val &= ~(SUNXI_TX_DEEMPH_6DB(0x3f)); reg_val |= SUNXI_TX_DEEMPH_6DB(0x20); reg_val &= ~(SUNXI_TX_DEEMPH_3P5DB(0x3f)); reg_val |= SUNXI_TX_DEEMPH_3P5DB(0x15); USBC_Writel(reg_val, regs + (SUNXI_PHY_TUNE_HIGH - SUNXI_GLOBALS_REGS_START)); /* Enable USB2.0 PHY Suspend mode. */ reg_val = USBC_Readl(regs + (SUNXI_GUSB2PHYCFG(0) - SUNXI_GLOBALS_REGS_START)); reg_val |= SUNXI_USB2PHYCFG_SUSPHY; USBC_Writel(reg_val, regs + (SUNXI_GUSB2PHYCFG(0) - SUNXI_GLOBALS_REGS_START)); /* Enable SOFITPSYNC for suspend. */ reg_val = USBC_Readl(regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); reg_val |= SUNXI_GCTL_SOFITPSYNC; USBC_Writel(reg_val, regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); return 0; } static void sunxi_xhci_open_clock(struct sunxi_hci_hcd *sunxi_xhci) { sunxi_xhci->open_clock(sunxi_xhci, 0); } static void sunxi_xhci_set_vbus(struct sunxi_hci_hcd *sunxi_xhci, int is_on) { sunxi_xhci->set_power(sunxi_xhci, is_on); } static void sunxi_start_xhci(struct sunxi_hci_hcd *sunxi_xhci) { sunxi_xhci->open_clock(sunxi_xhci, 1); sunxi_xhci->set_power(sunxi_xhci, 1); } static void sunxi_stop_xhci(struct sunxi_hci_hcd *sunxi_xhci) { sunxi_xhci->set_power(sunxi_xhci, 0); sunxi_xhci->close_clock(sunxi_xhci, 0); } static void sunxi_set_mode(struct sunxi_hci_hcd *sunxi_xhci, u32 mode) { u32 reg; reg = USBC_Readl(sunxi_xhci->regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); reg &= ~(SUNXI_GCTL_PRTCAPDIR(SUNXI_GCTL_PRTCAP_OTG)); reg |= SUNXI_GCTL_PRTCAPDIR(mode); USBC_Writel(reg, sunxi_xhci->regs + (SUNXI_GLOBALS_REGS_GCTL - SUNXI_GLOBALS_REGS_START)); } int xhci_host_init(struct sunxi_hci_hcd *sunxi_xhci) { struct platform_device *xhci; int ret; xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); if (!xhci) { dev_err(sunxi_xhci->dev, "couldn't allocate xHCI device\n"); ret = -ENOMEM; goto err0; } dma_set_coherent_mask(&xhci->dev, sunxi_xhci->dev->coherent_dma_mask); xhci->dev.parent = sunxi_xhci->dev; xhci->dev.dma_mask = sunxi_xhci->dev->dma_mask; xhci->dev.dma_parms = sunxi_xhci->dev->dma_parms; xhci->dev.archdata.dma_ops = sunxi_xhci->dev->archdata.dma_ops; xhci->dev.archdata.dma_coherent = sunxi_xhci->dev->archdata.dma_coherent; sunxi_xhci->pdev = xhci; ret = platform_device_add_resources(xhci, sunxi_xhci->xhci_resources, XHCI_RESOURCES_NUM); if (ret) { dev_err(sunxi_xhci->dev, "couldn't add resources to xHCI device\n"); goto err1; } ret = platform_device_add(xhci); if (ret) { dev_err(sunxi_xhci->dev, "failed to register xHCI device\n"); goto err1; } return 0; err1: platform_device_put(xhci); err0: return ret; } void xhci_host_exit(struct sunxi_hci_hcd *sunxi_xhci) { platform_device_unregister(sunxi_xhci->pdev); } static int sunxi_xhci_hcd_probe(struct platform_device *pdev) { int ret = 0; struct sunxi_hci_hcd *sunxi_xhci = NULL; void *mem; struct device *dev = &pdev->dev; struct resource *res; void __iomem *regs; if (pdev == NULL) { DMSG_PANIC("%s, Argment is invalid\n", __func__); return -1; } /* if usb is disabled, can not probe */ if (usb_disabled()) { DMSG_PANIC("usb hcd is disabled\n"); return -ENODEV; } mem = devm_kzalloc(dev, sizeof(*sunxi_xhci) + SUNXI_ALIGN_MASK, GFP_KERNEL); if (!mem) { dev_err(dev, "not enough memory\n"); return -ENOMEM; } sunxi_xhci = PTR_ALIGN(mem, SUNXI_ALIGN_MASK + 1); sunxi_xhci->mem = mem; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "missing IRQ\n"); return -ENODEV; } sunxi_xhci->xhci_resources[1].start = res->start; sunxi_xhci->xhci_resources[1].end = res->end; sunxi_xhci->xhci_resources[1].flags = res->flags; sunxi_xhci->xhci_resources[1].name = res->name; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "missing memory resource\n"); return -ENODEV; } sunxi_xhci->xhci_resources[0].start = res->start; sunxi_xhci->xhci_resources[0].end = sunxi_xhci->xhci_resources[0].start + XHCI_REGS_END; sunxi_xhci->xhci_resources[0].flags = res->flags; sunxi_xhci->xhci_resources[0].name = res->name; /* * Request memory region but exclude xHCI regs, * since it will be requested by the xhci-plat driver. */ res = devm_request_mem_region(dev, res->start + SUNXI_GLOBALS_REGS_START, resource_size(res) - SUNXI_GLOBALS_REGS_START, dev_name(dev)); if (!res) { dev_err(dev, "can't request mem region\n"); return -ENOMEM; } regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); if (!regs) { dev_err(dev, "ioremap failed\n"); return -ENOMEM; } spin_lock_init(&sunxi_xhci->lock); sunxi_xhci->regs = regs; sunxi_xhci->regs_size = resource_size(res); sunxi_xhci->dev = dev; dev->dma_mask = dev->parent->dma_mask; dev->dma_parms = dev->parent->dma_parms; dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); ret = init_sunxi_hci(pdev, SUNXI_USB_XHCI); if (ret != 0) { dev_err(&pdev->dev, "init_sunxi_hci is fail\n"); return 0; } platform_set_drvdata(pdev, sunxi_xhci); sunxi_start_xhci(pdev->dev.platform_data); sunxi_core_open_phy(sunxi_xhci->regs); sunxi_set_mode(sunxi_xhci, SUNXI_GCTL_PRTCAP_HOST); xhci_host_init(sunxi_xhci); device_create_file(&pdev->dev, &dev_attr_xhci_enable); device_create_file(&pdev->dev, &dev_attr_ed_test); g_sunxi_xhci = sunxi_xhci; g_dev_data = pdev->dev.platform_data; g_dev_data->probe = 1; #ifdef CONFIG_PM if (!g_dev_data->wakeup_suspend) INIT_WORK(&g_dev_data->resume_work, sunxi_xhci_resume_work); #endif return 0; } static int sunxi_xhci_hcd_remove(struct platform_device *pdev) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; if (pdev == NULL) { DMSG_PANIC("%s, Argment is invalid\n", __func__); return -1; } sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL) { DMSG_PANIC("%s, sunxi_xhci is null\n", __func__); return -1; } device_remove_file(&pdev->dev, &dev_attr_xhci_enable); xhci_host_exit(sunxi_xhci); sunxi_stop_xhci(dev_data); dev_data->probe = 0; return 0; } static void sunxi_xhci_hcd_shutdown(struct platform_device *pdev) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; if (pdev == NULL) { DMSG_PANIC("%s, Argment is invalid\n", __func__); return; } sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL) { DMSG_PANIC("%s, is null\n", __func__); return; } if (dev_data->probe == 0) { DMSG_INFO("%s, %s is disable, need not shutdown\n", __func__, sunxi_xhci->hci_name); return; } DMSG_INFO("[%s]: xhci shutdown start\n", sunxi_xhci->hci_name); usb_hcd_platform_shutdown(sunxi_xhci->pdev); DMSG_INFO("[%s]: xhci shutdown end\n", sunxi_xhci->hci_name); return; } int sunxi_usb_disable_xhci(void) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL || dev_data == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return -1; } if (dev_data->probe == 0) { DMSG_PANIC("sunxi_xhci is disable, can not disable again\n"); return -1; } dev_data->probe = 0; DMSG_INFO("[%s]: sunxi_usb_disable_xhci\n", sunxi_xhci->hci_name); xhci_host_exit(sunxi_xhci); sunxi_stop_xhci(dev_data); return 0; } EXPORT_SYMBOL(sunxi_usb_disable_xhci); int sunxi_usb_enable_xhci(void) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL || dev_data == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return -1; } if (dev_data->probe == 1) { DMSG_PANIC("sunxi_xhci is already enable, can not enable again\n"); return -1; } dev_data->probe = 1; DMSG_INFO("[%s]: sunxi_usb_enable_xhci\n", sunxi_xhci->hci_name); sunxi_start_xhci(dev_data); sunxi_core_open_phy(sunxi_xhci->regs); sunxi_set_mode(sunxi_xhci, SUNXI_GCTL_PRTCAP_HOST); xhci_host_init(sunxi_xhci); return 0; } EXPORT_SYMBOL(sunxi_usb_enable_xhci); #ifdef CONFIG_PM static int sunxi_xhci_hcd_suspend(struct device *dev) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; struct usb_hcd *hcd = NULL; struct xhci_hcd *xhci = NULL; if (dev == NULL) { DMSG_PANIC("%s, Argment is invalid\n", __func__); return 0; } sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return 0; } if (dev_data->probe == 0) { DMSG_INFO("[%s]: is disable, need not suspend\n", dev_data->hci_name); return 0; } hcd = dev_get_drvdata(&sunxi_xhci->pdev->dev); if (hcd == NULL) { DMSG_PANIC("xhci hcd is null\n"); return 0; } xhci = hcd_to_xhci(hcd); if (xhci == NULL) { DMSG_PANIC("xhci is null\n"); return 0; } if (dev_data->wakeup_suspend) { DMSG_INFO("[%s]: not suspend\n", dev_data->hci_name); } else { DMSG_INFO("[%s]: sunxi_xhci_hcd_suspend\n", dev_data->hci_name); atomic_add(1, &g_sunxi_usb_super_standby); xhci_suspend(xhci, false); sunxi_stop_xhci(dev_data); cancel_work_sync(&dev_data->resume_work); } return 0; } static void sunxi_xhci_resume_work(struct work_struct *work) { struct sunxi_hci_hcd *dev_data = NULL; dev_data = container_of(work, struct sunxi_hci_hcd, resume_work); /* Waiting hci to resume. */ msleep(5000); sunxi_xhci_set_vbus(dev_data, 1); atomic_sub(1, &g_sunxi_usb_super_standby); } static int sunxi_xhci_hcd_resume(struct device *dev) { struct sunxi_hci_hcd *sunxi_xhci = NULL; struct sunxi_hci_hcd *dev_data = NULL; struct usb_hcd *hcd = NULL; struct xhci_hcd *xhci = NULL; if (dev == NULL) { DMSG_PANIC("Argment is invalid\n"); return 0; } sunxi_xhci = g_sunxi_xhci; dev_data = g_dev_data; if (sunxi_xhci == NULL) { DMSG_PANIC("sunxi_xhci is null\n"); return 0; } if (dev_data->probe == 0) { DMSG_INFO("[%s]: is disable, need not resume\n", dev_data->hci_name); return 0; } hcd = dev_get_drvdata(&sunxi_xhci->pdev->dev); if (hcd == NULL) { DMSG_PANIC("xhci hcd is null\n"); return 0; } xhci = hcd_to_xhci(hcd); if (xhci == NULL) { DMSG_PANIC("xhci is null\n"); return 0; } if (dev_data->wakeup_suspend) { DMSG_INFO("[%s]: controller not suspend, need not resume\n", dev_data->hci_name); } else { DMSG_INFO("[%s]: sunxi_xhci_hcd_resume\n", dev_data->hci_name); sunxi_xhci_open_clock(dev_data); sunxi_core_open_phy(sunxi_xhci->regs); sunxi_set_mode(sunxi_xhci, SUNXI_GCTL_PRTCAP_HOST); xhci_resume(xhci, false); schedule_work(&dev_data->resume_work); } return 0; } static const struct dev_pm_ops xhci_pmops = { .suspend = sunxi_xhci_hcd_suspend, .resume = sunxi_xhci_hcd_resume, }; #endif static const struct of_device_id sunxi_xhci_match[] = { {.compatible = SUNXI_XHCI_OF_MATCH, }, {}, }; MODULE_DEVICE_TABLE(of, sunxi_xhci_match); static struct platform_driver sunxi_xhci_hcd_driver = { .probe = sunxi_xhci_hcd_probe, .remove = sunxi_xhci_hcd_remove, .shutdown = sunxi_xhci_hcd_shutdown, .driver = { .name = xhci_name, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &xhci_pmops, #endif .of_match_table = sunxi_xhci_match, } }; module_platform_driver(sunxi_xhci_hcd_driver); MODULE_ALIAS("platform:sunxi xhci"); MODULE_AUTHOR("wangjx "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Allwinnertech Xhci Controller Driver");