123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- // SPDX-License-Identifier: GPL-2.0
- /**
- * dwc3-pci.c - PCI Specific glue layer
- *
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
- *
- * Authors: Felipe Balbi <balbi@ti.com>,
- * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/pci.h>
- #include <linux/workqueue.h>
- #include <linux/pm_runtime.h>
- #include <linux/platform_device.h>
- #include <linux/gpio/consumer.h>
- #include <linux/gpio/machine.h>
- #include <linux/acpi.h>
- #include <linux/delay.h>
- #define PCI_DEVICE_ID_INTEL_BYT 0x0f37
- #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
- #define PCI_DEVICE_ID_INTEL_BSW 0x22b7
- #define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
- #define PCI_DEVICE_ID_INTEL_SPTH 0xa130
- #define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
- #define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa
- #define PCI_DEVICE_ID_INTEL_APL 0x5aaa
- #define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
- #define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee
- #define PCI_DEVICE_ID_INTEL_CMLH 0x06ee
- #define PCI_DEVICE_ID_INTEL_GLK 0x31aa
- #define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee
- #define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
- #define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
- #define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee
- #define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
- #define PCI_INTEL_BXT_FUNC_PMU_PWR 4
- #define PCI_INTEL_BXT_STATE_D0 0
- #define PCI_INTEL_BXT_STATE_D3 3
- #define GP_RWBAR 1
- #define GP_RWREG1 0xa0
- #define GP_RWREG1_ULPI_REFCLK_DISABLE (1 << 17)
- /**
- * struct dwc3_pci - Driver private structure
- * @dwc3: child dwc3 platform_device
- * @pci: our link to PCI bus
- * @guid: _DSM GUID
- * @has_dsm_for_pm: true for devices which need to run _DSM on runtime PM
- * @wakeup_work: work for asynchronous resume
- */
- struct dwc3_pci {
- struct platform_device *dwc3;
- struct pci_dev *pci;
- guid_t guid;
- unsigned int has_dsm_for_pm:1;
- struct work_struct wakeup_work;
- };
- static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
- static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
- static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
- { "reset-gpios", &reset_gpios, 1 },
- { "cs-gpios", &cs_gpios, 1 },
- { },
- };
- static struct gpiod_lookup_table platform_bytcr_gpios = {
- .dev_id = "0000:00:16.0",
- .table = {
- GPIO_LOOKUP("INT33FC:00", 54, "reset", GPIO_ACTIVE_HIGH),
- GPIO_LOOKUP("INT33FC:02", 14, "cs", GPIO_ACTIVE_HIGH),
- {}
- },
- };
- static int dwc3_byt_enable_ulpi_refclock(struct pci_dev *pci)
- {
- void __iomem *reg;
- u32 value;
- reg = pcim_iomap(pci, GP_RWBAR, 0);
- if (!reg)
- return -ENOMEM;
- value = readl(reg + GP_RWREG1);
- if (!(value & GP_RWREG1_ULPI_REFCLK_DISABLE))
- goto unmap; /* ULPI refclk already enabled */
- value &= ~GP_RWREG1_ULPI_REFCLK_DISABLE;
- writel(value, reg + GP_RWREG1);
- /* This comes from the Intel Android x86 tree w/o any explanation */
- msleep(100);
- unmap:
- pcim_iounmap(pci, reg);
- return 0;
- }
- static const struct property_entry dwc3_pci_intel_properties[] = {
- PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
- PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
- {}
- };
- static const struct property_entry dwc3_pci_mrfld_properties[] = {
- PROPERTY_ENTRY_STRING("dr_mode", "otg"),
- PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
- {}
- };
- static const struct property_entry dwc3_pci_amd_properties[] = {
- PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
- PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf),
- PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"),
- PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"),
- PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"),
- PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"),
- PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"),
- PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"),
- PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"),
- PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"),
- PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1),
- /* FIXME these quirks should be removed when AMD NL tapes out */
- PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
- PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
- PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
- PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
- {}
- };
- static int dwc3_pci_quirks(struct dwc3_pci *dwc)
- {
- struct pci_dev *pdev = dwc->pci;
- if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
- if (pdev->device == PCI_DEVICE_ID_INTEL_BXT ||
- pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) {
- guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid);
- dwc->has_dsm_for_pm = true;
- }
- if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
- struct gpio_desc *gpio;
- int ret;
- /* On BYT the FW does not always enable the refclock */
- ret = dwc3_byt_enable_ulpi_refclock(pdev);
- if (ret)
- return ret;
- ret = devm_acpi_dev_add_driver_gpios(&pdev->dev,
- acpi_dwc3_byt_gpios);
- if (ret)
- dev_dbg(&pdev->dev, "failed to add mapping table\n");
- /*
- * A lot of BYT devices lack ACPI resource entries for
- * the GPIOs, add a fallback mapping to the reference
- * design GPIOs which all boards seem to use.
- */
- gpiod_add_lookup_table(&platform_bytcr_gpios);
- /*
- * These GPIOs will turn on the USB2 PHY. Note that we have to
- * put the gpio descriptors again here because the phy driver
- * might want to grab them, too.
- */
- gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
- if (IS_ERR(gpio))
- return PTR_ERR(gpio);
- gpiod_set_value_cansleep(gpio, 1);
- gpiod_put(gpio);
- gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(gpio))
- return PTR_ERR(gpio);
- if (gpio) {
- gpiod_set_value_cansleep(gpio, 1);
- gpiod_put(gpio);
- usleep_range(10000, 11000);
- }
- }
- }
- return 0;
- }
- #ifdef CONFIG_PM
- static void dwc3_pci_resume_work(struct work_struct *work)
- {
- struct dwc3_pci *dwc = container_of(work, struct dwc3_pci, wakeup_work);
- struct platform_device *dwc3 = dwc->dwc3;
- int ret;
- ret = pm_runtime_get_sync(&dwc3->dev);
- if (ret)
- return;
- pm_runtime_mark_last_busy(&dwc3->dev);
- pm_runtime_put_sync_autosuspend(&dwc3->dev);
- }
- #endif
- static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
- {
- struct property_entry *p = (struct property_entry *)id->driver_data;
- struct dwc3_pci *dwc;
- struct resource res[2];
- int ret;
- struct device *dev = &pci->dev;
- ret = pcim_enable_device(pci);
- if (ret) {
- dev_err(dev, "failed to enable pci device\n");
- return -ENODEV;
- }
- pci_set_master(pci);
- dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
- if (!dwc)
- return -ENOMEM;
- dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
- if (!dwc->dwc3)
- return -ENOMEM;
- memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
- res[0].start = pci_resource_start(pci, 0);
- res[0].end = pci_resource_end(pci, 0);
- res[0].name = "dwc_usb3";
- res[0].flags = IORESOURCE_MEM;
- res[1].start = pci->irq;
- res[1].name = "dwc_usb3";
- res[1].flags = IORESOURCE_IRQ;
- ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
- if (ret) {
- dev_err(dev, "couldn't add resources to dwc3 device\n");
- goto err;
- }
- dwc->pci = pci;
- dwc->dwc3->dev.parent = dev;
- ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev));
- ret = platform_device_add_properties(dwc->dwc3, p);
- if (ret < 0)
- goto err;
- ret = dwc3_pci_quirks(dwc);
- if (ret)
- goto err;
- ret = platform_device_add(dwc->dwc3);
- if (ret) {
- dev_err(dev, "failed to register dwc3 device\n");
- goto err;
- }
- device_init_wakeup(dev, true);
- pci_set_drvdata(pci, dwc);
- pm_runtime_put(dev);
- #ifdef CONFIG_PM
- INIT_WORK(&dwc->wakeup_work, dwc3_pci_resume_work);
- #endif
- return 0;
- err:
- platform_device_put(dwc->dwc3);
- return ret;
- }
- static void dwc3_pci_remove(struct pci_dev *pci)
- {
- struct dwc3_pci *dwc = pci_get_drvdata(pci);
- struct pci_dev *pdev = dwc->pci;
- if (pdev->device == PCI_DEVICE_ID_INTEL_BYT)
- gpiod_remove_lookup_table(&platform_bytcr_gpios);
- #ifdef CONFIG_PM
- cancel_work_sync(&dwc->wakeup_work);
- #endif
- device_init_wakeup(&pci->dev, false);
- pm_runtime_get(&pci->dev);
- platform_device_unregister(dwc->dwc3);
- }
- static const struct pci_device_id dwc3_pci_id_table[] = {
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BSW),
- (kernel_ulong_t) &dwc3_pci_intel_properties },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BYT),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD),
- (kernel_ulong_t) &dwc3_pci_mrfld_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLLP),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLH),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTLP),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTH),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT_M),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_APL),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_KBP),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_GLK),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPLP),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPH),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPV),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP),
- (kernel_ulong_t) &dwc3_pci_intel_properties, },
- { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB),
- (kernel_ulong_t) &dwc3_pci_amd_properties, },
- { } /* Terminating Entry */
- };
- MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
- #if defined(CONFIG_PM) || defined(CONFIG_PM_SLEEP)
- static int dwc3_pci_dsm(struct dwc3_pci *dwc, int param)
- {
- union acpi_object *obj;
- union acpi_object tmp;
- union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
- if (!dwc->has_dsm_for_pm)
- return 0;
- tmp.type = ACPI_TYPE_INTEGER;
- tmp.integer.value = param;
- obj = acpi_evaluate_dsm(ACPI_HANDLE(&dwc->pci->dev), &dwc->guid,
- 1, PCI_INTEL_BXT_FUNC_PMU_PWR, &argv4);
- if (!obj) {
- dev_err(&dwc->pci->dev, "failed to evaluate _DSM\n");
- return -EIO;
- }
- ACPI_FREE(obj);
- return 0;
- }
- #endif /* CONFIG_PM || CONFIG_PM_SLEEP */
- #ifdef CONFIG_PM
- static int dwc3_pci_runtime_suspend(struct device *dev)
- {
- struct dwc3_pci *dwc = dev_get_drvdata(dev);
- if (device_can_wakeup(dev))
- return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
- return -EBUSY;
- }
- static int dwc3_pci_runtime_resume(struct device *dev)
- {
- struct dwc3_pci *dwc = dev_get_drvdata(dev);
- int ret;
- ret = dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0);
- if (ret)
- return ret;
- queue_work(pm_wq, &dwc->wakeup_work);
- return 0;
- }
- #endif /* CONFIG_PM */
- #ifdef CONFIG_PM_SLEEP
- static int dwc3_pci_suspend(struct device *dev)
- {
- struct dwc3_pci *dwc = dev_get_drvdata(dev);
- return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
- }
- static int dwc3_pci_resume(struct device *dev)
- {
- struct dwc3_pci *dwc = dev_get_drvdata(dev);
- return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0);
- }
- #endif /* CONFIG_PM_SLEEP */
- static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
- SET_RUNTIME_PM_OPS(dwc3_pci_runtime_suspend, dwc3_pci_runtime_resume,
- NULL)
- };
- static struct pci_driver dwc3_pci_driver = {
- .name = "dwc3-pci",
- .id_table = dwc3_pci_id_table,
- .probe = dwc3_pci_probe,
- .remove = dwc3_pci_remove,
- .driver = {
- .pm = &dwc3_pci_dev_pm_ops,
- }
- };
- MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
- module_pci_driver(dwc3_pci_driver);
|