2018-06-13 00:26:43 +03:00
// SPDX-License-Identifier: GPL-2.0
/**
* dwc3 - haps . c - Synopsys HAPS PCI Specific glue layer
*
* Copyright ( C ) 2018 Synopsys , Inc .
*
* Authors : Thinh Nguyen < thinhn @ synopsys . com > ,
* John Youn < johnyoun @ synopsys . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/pci.h>
# include <linux/platform_device.h>
# include <linux/property.h>
/**
* struct dwc3_haps - Driver private structure
* @ dwc3 : child dwc3 platform_device
* @ pci : our link to PCI bus
*/
struct dwc3_haps {
struct platform_device * dwc3 ;
struct pci_dev * pci ;
} ;
static const struct property_entry initial_properties [ ] = {
PROPERTY_ENTRY_BOOL ( " snps,usb3_lpm_capable " ) ,
PROPERTY_ENTRY_BOOL ( " snps,has-lpm-erratum " ) ,
PROPERTY_ENTRY_BOOL ( " snps,dis_enblslpm_quirk " ) ,
PROPERTY_ENTRY_BOOL ( " linux,sysdev_is_parent " ) ,
{ } ,
} ;
static int dwc3_haps_probe ( struct pci_dev * pci ,
const struct pci_device_id * id )
{
struct dwc3_haps * dwc ;
struct device * dev = & pci - > dev ;
struct resource res [ 2 ] ;
int ret ;
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 ;
ret = platform_device_add_properties ( dwc - > dwc3 , initial_properties ) ;
if ( ret )
goto err ;
ret = platform_device_add ( dwc - > dwc3 ) ;
if ( ret ) {
dev_err ( dev , " failed to register dwc3 device \n " ) ;
goto err ;
}
pci_set_drvdata ( pci , dwc ) ;
return 0 ;
err :
platform_device_put ( dwc - > dwc3 ) ;
return ret ;
}
static void dwc3_haps_remove ( struct pci_dev * pci )
{
struct dwc3_haps * dwc = pci_get_drvdata ( pci ) ;
platform_device_unregister ( dwc - > dwc3 ) ;
}
static const struct pci_device_id dwc3_haps_id_table [ ] = {
{
PCI_DEVICE ( PCI_VENDOR_ID_SYNOPSYS ,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 ) ,
2019-02-06 21:49:26 +03:00
/*
* i . MX6QP and i . MX7D platform use a PCIe controller with the
* same VID and PID as this USB controller . The system may
* incorrectly match this driver to that PCIe controller . To
* workaround this , specifically use class type USB to prevent
* incorrect driver matching .
*/
. class = ( PCI_CLASS_SERIAL_USB < < 8 ) ,
. class_mask = 0xffff00 ,
2018-06-13 00:26:43 +03:00
} ,
{
PCI_DEVICE ( PCI_VENDOR_ID_SYNOPSYS ,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI ) ,
} ,
{
PCI_DEVICE ( PCI_VENDOR_ID_SYNOPSYS ,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 ) ,
} ,
{ } /* Terminating Entry */
} ;
MODULE_DEVICE_TABLE ( pci , dwc3_haps_id_table ) ;
static struct pci_driver dwc3_haps_driver = {
. name = " dwc3-haps " ,
. id_table = dwc3_haps_id_table ,
. probe = dwc3_haps_probe ,
. remove = dwc3_haps_remove ,
} ;
MODULE_AUTHOR ( " Thinh Nguyen <thinhn@synopsys.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Synopsys HAPS PCI Glue Layer " ) ;
module_pci_driver ( dwc3_haps_driver ) ;