15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2943befc3SWingMan Kwok /** 3943befc3SWingMan Kwok * dwc3-keystone.c - Keystone Specific Glue layer 4943befc3SWingMan Kwok * 5943befc3SWingMan Kwok * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com 6943befc3SWingMan Kwok * 7943befc3SWingMan Kwok * Author: WingMan Kwok <w-kwok2@ti.com> 8943befc3SWingMan Kwok */ 9943befc3SWingMan Kwok 10943befc3SWingMan Kwok #include <linux/module.h> 11943befc3SWingMan Kwok #include <linux/kernel.h> 12943befc3SWingMan Kwok #include <linux/interrupt.h> 13943befc3SWingMan Kwok #include <linux/platform_device.h> 14943befc3SWingMan Kwok #include <linux/dma-mapping.h> 15943befc3SWingMan Kwok #include <linux/io.h> 16943befc3SWingMan Kwok #include <linux/of_platform.h> 17c14af973SFranklin S Cooper Jr #include <linux/pm_runtime.h> 18943befc3SWingMan Kwok 19943befc3SWingMan Kwok /* USBSS register offsets */ 20943befc3SWingMan Kwok #define USBSS_REVISION 0x0000 21943befc3SWingMan Kwok #define USBSS_SYSCONFIG 0x0010 22943befc3SWingMan Kwok #define USBSS_IRQ_EOI 0x0018 23943befc3SWingMan Kwok #define USBSS_IRQSTATUS_RAW_0 0x0020 24943befc3SWingMan Kwok #define USBSS_IRQSTATUS_0 0x0024 25943befc3SWingMan Kwok #define USBSS_IRQENABLE_SET_0 0x0028 26943befc3SWingMan Kwok #define USBSS_IRQENABLE_CLR_0 0x002c 27943befc3SWingMan Kwok 28943befc3SWingMan Kwok /* IRQ register bits */ 29943befc3SWingMan Kwok #define USBSS_IRQ_EOI_LINE(n) BIT(n) 30943befc3SWingMan Kwok #define USBSS_IRQ_EVENT_ST BIT(0) 31943befc3SWingMan Kwok #define USBSS_IRQ_COREIRQ_EN BIT(0) 32943befc3SWingMan Kwok #define USBSS_IRQ_COREIRQ_CLR BIT(0) 33943befc3SWingMan Kwok 34943befc3SWingMan Kwok struct dwc3_keystone { 35943befc3SWingMan Kwok struct device *dev; 36943befc3SWingMan Kwok void __iomem *usbss; 37943befc3SWingMan Kwok }; 38943befc3SWingMan Kwok 39943befc3SWingMan Kwok static inline u32 kdwc3_readl(void __iomem *base, u32 offset) 40943befc3SWingMan Kwok { 41943befc3SWingMan Kwok return readl(base + offset); 42943befc3SWingMan Kwok } 43943befc3SWingMan Kwok 44943befc3SWingMan Kwok static inline void kdwc3_writel(void __iomem *base, u32 offset, u32 value) 45943befc3SWingMan Kwok { 46943befc3SWingMan Kwok writel(value, base + offset); 47943befc3SWingMan Kwok } 48943befc3SWingMan Kwok 49943befc3SWingMan Kwok static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc) 50943befc3SWingMan Kwok { 51943befc3SWingMan Kwok u32 val; 52943befc3SWingMan Kwok 53943befc3SWingMan Kwok val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0); 54943befc3SWingMan Kwok val |= USBSS_IRQ_COREIRQ_EN; 55943befc3SWingMan Kwok kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val); 56943befc3SWingMan Kwok } 57943befc3SWingMan Kwok 58943befc3SWingMan Kwok static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc) 59943befc3SWingMan Kwok { 60943befc3SWingMan Kwok u32 val; 61943befc3SWingMan Kwok 62943befc3SWingMan Kwok val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0); 63943befc3SWingMan Kwok val &= ~USBSS_IRQ_COREIRQ_EN; 64943befc3SWingMan Kwok kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val); 65943befc3SWingMan Kwok } 66943befc3SWingMan Kwok 67943befc3SWingMan Kwok static irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc) 68943befc3SWingMan Kwok { 69943befc3SWingMan Kwok struct dwc3_keystone *kdwc = _kdwc; 70943befc3SWingMan Kwok 71943befc3SWingMan Kwok kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_CLR_0, USBSS_IRQ_COREIRQ_CLR); 72943befc3SWingMan Kwok kdwc3_writel(kdwc->usbss, USBSS_IRQSTATUS_0, USBSS_IRQ_EVENT_ST); 73943befc3SWingMan Kwok kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, USBSS_IRQ_COREIRQ_EN); 74943befc3SWingMan Kwok kdwc3_writel(kdwc->usbss, USBSS_IRQ_EOI, USBSS_IRQ_EOI_LINE(0)); 75943befc3SWingMan Kwok 76943befc3SWingMan Kwok return IRQ_HANDLED; 77943befc3SWingMan Kwok } 78943befc3SWingMan Kwok 79943befc3SWingMan Kwok static int kdwc3_probe(struct platform_device *pdev) 80943befc3SWingMan Kwok { 81943befc3SWingMan Kwok struct device *dev = &pdev->dev; 82943befc3SWingMan Kwok struct device_node *node = pdev->dev.of_node; 83943befc3SWingMan Kwok struct dwc3_keystone *kdwc; 84943befc3SWingMan Kwok int error, irq; 85943befc3SWingMan Kwok 86943befc3SWingMan Kwok kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL); 87943befc3SWingMan Kwok if (!kdwc) 88943befc3SWingMan Kwok return -ENOMEM; 89943befc3SWingMan Kwok 90943befc3SWingMan Kwok platform_set_drvdata(pdev, kdwc); 91943befc3SWingMan Kwok 92943befc3SWingMan Kwok kdwc->dev = dev; 93943befc3SWingMan Kwok 94*5b76f6a0SYueHaibing kdwc->usbss = devm_platform_ioremap_resource(pdev, 0); 95943befc3SWingMan Kwok if (IS_ERR(kdwc->usbss)) 96943befc3SWingMan Kwok return PTR_ERR(kdwc->usbss); 97943befc3SWingMan Kwok 98c14af973SFranklin S Cooper Jr pm_runtime_enable(kdwc->dev); 99943befc3SWingMan Kwok 100c14af973SFranklin S Cooper Jr error = pm_runtime_get_sync(kdwc->dev); 101943befc3SWingMan Kwok if (error < 0) { 102c14af973SFranklin S Cooper Jr dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n", 103943befc3SWingMan Kwok error); 104c14af973SFranklin S Cooper Jr goto err_irq; 105943befc3SWingMan Kwok } 106943befc3SWingMan Kwok 107eca6b494SRoger Quadros /* IRQ processing not required currently for AM65 */ 108eca6b494SRoger Quadros if (of_device_is_compatible(node, "ti,am654-dwc3")) 109eca6b494SRoger Quadros goto skip_irq; 110eca6b494SRoger Quadros 111943befc3SWingMan Kwok irq = platform_get_irq(pdev, 0); 112943befc3SWingMan Kwok if (irq < 0) { 113943befc3SWingMan Kwok dev_err(&pdev->dev, "missing irq\n"); 11462c60697SJulia Lawall error = irq; 115943befc3SWingMan Kwok goto err_irq; 116943befc3SWingMan Kwok } 117943befc3SWingMan Kwok 118943befc3SWingMan Kwok error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED, 119943befc3SWingMan Kwok dev_name(dev), kdwc); 120943befc3SWingMan Kwok if (error) { 121943befc3SWingMan Kwok dev_err(dev, "failed to request IRQ #%d --> %d\n", 122943befc3SWingMan Kwok irq, error); 123943befc3SWingMan Kwok goto err_irq; 124943befc3SWingMan Kwok } 125943befc3SWingMan Kwok 126943befc3SWingMan Kwok kdwc3_enable_irqs(kdwc); 127943befc3SWingMan Kwok 128eca6b494SRoger Quadros skip_irq: 129943befc3SWingMan Kwok error = of_platform_populate(node, NULL, NULL, dev); 130943befc3SWingMan Kwok if (error) { 131943befc3SWingMan Kwok dev_err(&pdev->dev, "failed to create dwc3 core\n"); 132943befc3SWingMan Kwok goto err_core; 133943befc3SWingMan Kwok } 134943befc3SWingMan Kwok 135943befc3SWingMan Kwok return 0; 136943befc3SWingMan Kwok 137943befc3SWingMan Kwok err_core: 138943befc3SWingMan Kwok kdwc3_disable_irqs(kdwc); 139943befc3SWingMan Kwok err_irq: 140c14af973SFranklin S Cooper Jr pm_runtime_put_sync(kdwc->dev); 141c14af973SFranklin S Cooper Jr pm_runtime_disable(kdwc->dev); 142943befc3SWingMan Kwok 143943befc3SWingMan Kwok return error; 144943befc3SWingMan Kwok } 145943befc3SWingMan Kwok 146943befc3SWingMan Kwok static int kdwc3_remove_core(struct device *dev, void *c) 147943befc3SWingMan Kwok { 148943befc3SWingMan Kwok struct platform_device *pdev = to_platform_device(dev); 149943befc3SWingMan Kwok 150943befc3SWingMan Kwok platform_device_unregister(pdev); 151943befc3SWingMan Kwok 152943befc3SWingMan Kwok return 0; 153943befc3SWingMan Kwok } 154943befc3SWingMan Kwok 155943befc3SWingMan Kwok static int kdwc3_remove(struct platform_device *pdev) 156943befc3SWingMan Kwok { 157943befc3SWingMan Kwok struct dwc3_keystone *kdwc = platform_get_drvdata(pdev); 158eca6b494SRoger Quadros struct device_node *node = pdev->dev.of_node; 159943befc3SWingMan Kwok 160eca6b494SRoger Quadros if (!of_device_is_compatible(node, "ti,am654-dwc3")) 161943befc3SWingMan Kwok kdwc3_disable_irqs(kdwc); 162eca6b494SRoger Quadros 163943befc3SWingMan Kwok device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core); 164c14af973SFranklin S Cooper Jr pm_runtime_put_sync(kdwc->dev); 165c14af973SFranklin S Cooper Jr pm_runtime_disable(kdwc->dev); 166c14af973SFranklin S Cooper Jr 167943befc3SWingMan Kwok platform_set_drvdata(pdev, NULL); 168943befc3SWingMan Kwok 169943befc3SWingMan Kwok return 0; 170943befc3SWingMan Kwok } 171943befc3SWingMan Kwok 172943befc3SWingMan Kwok static const struct of_device_id kdwc3_of_match[] = { 173943befc3SWingMan Kwok { .compatible = "ti,keystone-dwc3", }, 174eca6b494SRoger Quadros { .compatible = "ti,am654-dwc3" }, 175943befc3SWingMan Kwok {}, 176943befc3SWingMan Kwok }; 177943befc3SWingMan Kwok MODULE_DEVICE_TABLE(of, kdwc3_of_match); 178943befc3SWingMan Kwok 179943befc3SWingMan Kwok static struct platform_driver kdwc3_driver = { 180943befc3SWingMan Kwok .probe = kdwc3_probe, 181943befc3SWingMan Kwok .remove = kdwc3_remove, 182943befc3SWingMan Kwok .driver = { 183943befc3SWingMan Kwok .name = "keystone-dwc3", 184943befc3SWingMan Kwok .of_match_table = kdwc3_of_match, 185943befc3SWingMan Kwok }, 186943befc3SWingMan Kwok }; 187943befc3SWingMan Kwok 188943befc3SWingMan Kwok module_platform_driver(kdwc3_driver); 189943befc3SWingMan Kwok 190943befc3SWingMan Kwok MODULE_ALIAS("platform:keystone-dwc3"); 191943befc3SWingMan Kwok MODULE_AUTHOR("WingMan Kwok <w-kwok2@ti.com>"); 192943befc3SWingMan Kwok MODULE_LICENSE("GPL v2"); 193943befc3SWingMan Kwok MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer"); 194