2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2017-03-28 16:32:03 +05:30
/*
* amd5536udc_pci . c - - AMD 5536 UDC high / full speed USB device controller
*
2020-07-10 21:09:19 +02:00
* Copyright ( C ) 2005 - 2007 AMD ( https : //www.amd.com)
2017-03-28 16:32:03 +05:30
* Author : Thomas Dahlmann
*/
/*
* The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536 .
* It is a USB Highspeed DMA capable USB device controller . Beside ep0 it
* provides 4 IN and 4 OUT endpoints ( bulk or interrupt type ) .
*
* Make sure that UDC is assigned to port 4 by BIOS settings ( port can also
* be used as host port ) and UOC bits PAD_EN and APU are set ( should be done
* by BIOS init ) .
*
* UDC DMA requires 32 - bit aligned buffers so DMA with gadget ether does not
* work without updating NET_IP_ALIGN . Or PIO mode ( module param " use_dma=0 " )
* can be used with gadget ether .
*
* This file does pci device registration , and the core driver implementation
* is done in amd5536udc . c
*
* The driver is split so as to use the core UDC driver which is based on
* Synopsys device controller IP ( different than HS OTG IP ) in UDCs
* integrated to SoC platforms .
*
*/
/* Driver strings */
# define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
/* system */
# include <linux/device.h>
# include <linux/dmapool.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/prefetch.h>
# include <linux/pci.h>
/* udc specific */
# include "amd5536udc.h"
/* pointer to device object */
static struct udc * udc ;
/* description */
static const char name [ ] = " amd5536udc-pci " ;
/* Reset all pci context */
static void udc_pci_remove ( struct pci_dev * pdev )
{
struct udc * dev ;
dev = pci_get_drvdata ( pdev ) ;
usb_del_gadget_udc ( & udc - > gadget ) ;
/* gadget driver must not be registered */
if ( WARN_ON ( dev - > driver ) )
return ;
/* dma pool cleanup */
free_dma_pools ( dev ) ;
/* reset controller */
writel ( AMD_BIT ( UDC_DEVCFG_SOFTRESET ) , & dev - > regs - > cfg ) ;
free_irq ( pdev - > irq , dev ) ;
iounmap ( dev - > virt_addr ) ;
release_mem_region ( pci_resource_start ( pdev , 0 ) ,
pci_resource_len ( pdev , 0 ) ) ;
pci_disable_device ( pdev ) ;
udc_remove ( dev ) ;
}
/* Called by pci bus driver to init pci context */
static int udc_pci_probe (
struct pci_dev * pdev ,
const struct pci_device_id * id
)
{
struct udc * dev ;
unsigned long resource ;
unsigned long len ;
int retval = 0 ;
/* one udc only */
if ( udc ) {
dev_dbg ( & pdev - > dev , " already probed \n " ) ;
return - EBUSY ;
}
/* init */
dev = kzalloc ( sizeof ( struct udc ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
/* pci setup */
if ( pci_enable_device ( pdev ) < 0 ) {
retval = - ENODEV ;
goto err_pcidev ;
}
/* PCI resource allocation */
resource = pci_resource_start ( pdev , 0 ) ;
len = pci_resource_len ( pdev , 0 ) ;
if ( ! request_mem_region ( resource , len , name ) ) {
dev_dbg ( & pdev - > dev , " pci device used already \n " ) ;
retval = - EBUSY ;
goto err_memreg ;
}
2020-01-06 09:43:50 +01:00
dev - > virt_addr = ioremap ( resource , len ) ;
2017-03-28 16:32:03 +05:30
if ( ! dev - > virt_addr ) {
dev_dbg ( & pdev - > dev , " start address cannot be mapped \n " ) ;
retval = - EFAULT ;
goto err_ioremap ;
}
if ( ! pdev - > irq ) {
dev_err ( & pdev - > dev , " irq not set \n " ) ;
retval = - ENODEV ;
goto err_irq ;
}
spin_lock_init ( & dev - > lock ) ;
/* udc csr registers base */
dev - > csr = dev - > virt_addr + UDC_CSR_ADDR ;
/* dev registers base */
dev - > regs = dev - > virt_addr + UDC_DEVCFG_ADDR ;
/* ep registers base */
dev - > ep_regs = dev - > virt_addr + UDC_EPREGS_ADDR ;
/* fifo's base */
dev - > rxfifo = ( u32 __iomem * ) ( dev - > virt_addr + UDC_RXFIFO_ADDR ) ;
dev - > txfifo = ( u32 __iomem * ) ( dev - > virt_addr + UDC_TXFIFO_ADDR ) ;
if ( request_irq ( pdev - > irq , udc_irq , IRQF_SHARED , name , dev ) ! = 0 ) {
dev_dbg ( & pdev - > dev , " request_irq(%d) fail \n " , pdev - > irq ) ;
retval = - EBUSY ;
goto err_irq ;
}
pci_set_drvdata ( pdev , dev ) ;
/* chip revision for Hs AMD5536 */
dev - > chiprev = pdev - > revision ;
pci_set_master ( pdev ) ;
pci_try_set_mwi ( pdev ) ;
2021-03-17 19:04:00 -04:00
dev - > phys_addr = resource ;
dev - > irq = pdev - > irq ;
dev - > pdev = pdev ;
dev - > dev = & pdev - > dev ;
2017-03-28 16:32:03 +05:30
/* init dma pools */
if ( use_dma ) {
retval = init_dma_pools ( dev ) ;
if ( retval ! = 0 )
goto err_dma ;
}
/* general probing */
if ( udc_probe ( dev ) ) {
retval = - ENODEV ;
goto err_probe ;
}
2023-05-25 18:38:37 +03:00
udc = dev ;
2017-03-28 16:32:03 +05:30
return 0 ;
err_probe :
if ( use_dma )
free_dma_pools ( dev ) ;
err_dma :
free_irq ( pdev - > irq , dev ) ;
err_irq :
iounmap ( dev - > virt_addr ) ;
err_ioremap :
release_mem_region ( resource , len ) ;
err_memreg :
pci_disable_device ( pdev ) ;
err_pcidev :
kfree ( dev ) ;
return retval ;
}
/* PCI device parameters */
static const struct pci_device_id pci_id [ ] = {
{
PCI_DEVICE ( PCI_VENDOR_ID_AMD , 0x2096 ) ,
. class = PCI_CLASS_SERIAL_USB_DEVICE ,
. class_mask = 0xffffffff ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( pci , pci_id ) ;
/* PCI functions */
static struct pci_driver udc_pci_driver = {
2020-02-18 19:32:46 +00:00
. name = name ,
2017-03-28 16:32:03 +05:30
. id_table = pci_id ,
. probe = udc_pci_probe ,
. remove = udc_pci_remove ,
} ;
module_pci_driver ( udc_pci_driver ) ;
MODULE_DESCRIPTION ( UDC_MOD_DESCRIPTION ) ;
MODULE_AUTHOR ( " Thomas Dahlmann " ) ;
MODULE_LICENSE ( " GPL " ) ;