Commit 97b3ffa2 authored by Raviteja Garimella's avatar Raviteja Garimella Committed by Felipe Balbi

usb: gadget: udc: amd5536: split core and PCI layer

This patch splits the amd5536udc driver into two -- one that does
pci device registration and the other file that does the rest of
the driver tasks like the gadget/ep ops etc for Synopsys UDC.

This way of splitting helps in exporting core driver symbols which
can be used by any other platform/pci driver that is written for
the same Synopsys USB device controller.

The current patch also includes a change in the Kconfig and Makefile.
A new config option USB_SNP_CORE will be selected automatically when
any one of the platform or pci driver for the same UDC is selected.

Main changes:
- amd5536udc_pci.c: PCI device registration is moved to this file.

- amd5536udc.c:
  This file does rest of the core UDC fucntionality.
  9 symbols are exported so as to be used by amd5536udc_pci.c.
  Module parameter definitions are moved to header file.

- amd5536udc.h:
  Function declarations, module parameters definitions and few common
  header file includes are added to this file

- Kconfig:
  New USB_SNP_CORE option is added which will be auto selected when
  any pci or platform driver config option for the UDC is chosen.

- Makefile:
  Compiles the core and pci files separately.
Signed-off-by: default avatarRaviteja Garimella <raviteja.garimella@broadcom.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent cc995c9e
...@@ -254,6 +254,20 @@ config USB_MV_U3D ...@@ -254,6 +254,20 @@ config USB_MV_U3D
MARVELL PXA2128 Processor series include a super speed USB3.0 device MARVELL PXA2128 Processor series include a super speed USB3.0 device
controller, which support super speed USB peripheral. controller, which support super speed USB peripheral.
config USB_SNP_CORE
depends on USB_AMD5536UDC
tristate
help
This enables core driver support for Synopsys USB 2.0 Device
controller.
This will be enabled when PCI or Platform driver for this UDC is
selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
USB_AMD5536UDC options.
This IP is different to the High Speed OTG IP that can be enabled
by selecting USB_DWC2 or USB_DWC3 options.
# #
# Controllers available in both integrated and discrete versions # Controllers available in both integrated and discrete versions
# #
...@@ -279,6 +293,7 @@ source "drivers/usb/gadget/udc/bdc/Kconfig" ...@@ -279,6 +293,7 @@ source "drivers/usb/gadget/udc/bdc/Kconfig"
config USB_AMD5536UDC config USB_AMD5536UDC
tristate "AMD5536 UDC" tristate "AMD5536 UDC"
depends on PCI depends on PCI
select USB_SNP_CORE
help help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0 It is a USB Highspeed DMA capable USB device controller. Beside ep0
...@@ -286,6 +301,9 @@ config USB_AMD5536UDC ...@@ -286,6 +301,9 @@ config USB_AMD5536UDC
The UDC port supports OTG operation, and may be used as a host port The UDC port supports OTG operation, and may be used as a host port
if it's not being used to implement peripheral or OTG roles. if it's not being used to implement peripheral or OTG roles.
This UDC is based on Synopsys USB device controller IP and selects
CONFIG_USB_SNP_CORE option to build the core driver.
Say "y" to link the driver statically, or "m" to build a Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "amd5536udc" and force all dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked. gadget drivers to also be dynamically linked.
......
...@@ -10,7 +10,8 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o ...@@ -10,7 +10,8 @@ obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o obj-$(CONFIG_USB_SNP_CORE) += amd5536udc.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o
......
...@@ -11,27 +11,15 @@ ...@@ -11,27 +11,15 @@
*/ */
/* /*
* The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536. * This file does the core driver implementation for the UDC that is based
* It is a USB Highspeed DMA capable USB device controller. Beside ep0 it * on Synopsys device controller IP (different than HS OTG IP) that is either
* provides 4 IN and 4 OUT endpoints (bulk or interrupt type). * connected through PCI bus or integrated to SoC platforms.
*
* 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.
*/ */
/* debug control */
/* #define UDC_VERBOSE */
/* Driver strings */ /* Driver strings */
#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller" #define UDC_MOD_DESCRIPTION "Synopsys USB Device Controller"
#define UDC_DRIVER_VERSION_STRING "01.00.0206" #define UDC_DRIVER_VERSION_STRING "01.00.0206"
/* system */
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -46,23 +34,12 @@ ...@@ -46,23 +34,12 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/dmapool.h> #include <linux/dmapool.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/prefetch.h> #include <linux/prefetch.h>
#include <linux/moduleparam.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
/* gadget stack */
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/* udc specific */
#include "amd5536udc.h" #include "amd5536udc.h"
static void udc_tasklet_disconnect(unsigned long); static void udc_tasklet_disconnect(unsigned long);
static void empty_req_queue(struct udc_ep *); static void empty_req_queue(struct udc_ep *);
static void udc_setup_endpoints(struct udc *dev); static void udc_setup_endpoints(struct udc *dev);
...@@ -72,7 +49,7 @@ static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq); ...@@ -72,7 +49,7 @@ static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
/* description */ /* description */
static const char mod_desc[] = UDC_MOD_DESCRIPTION; static const char mod_desc[] = UDC_MOD_DESCRIPTION;
static const char name[] = "amd5536udc"; static const char name[] = "udc";
/* structure to hold endpoint function pointers */ /* structure to hold endpoint function pointers */
static const struct usb_ep_ops udc_ep_ops; static const struct usb_ep_ops udc_ep_ops;
...@@ -208,30 +185,11 @@ static const struct { ...@@ -208,30 +185,11 @@ static const struct {
#undef EP_INFO #undef EP_INFO
}; };
/* DMA usage flag */
static bool use_dma = 1;
/* packet per buffer dma */
static bool use_dma_ppb = 1;
/* with per descr. update */
static bool use_dma_ppb_du;
/* buffer fill mode */ /* buffer fill mode */
static int use_dma_bufferfill_mode; static int use_dma_bufferfill_mode;
/* full speed only mode */
static bool use_fullspeed;
/* tx buffer size for high speed */ /* tx buffer size for high speed */
static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE; static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
/* module parameters */
module_param(use_dma, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma, "true for DMA");
module_param(use_dma_ppb, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
module_param(use_dma_ppb_du, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma_ppb_du,
"true for DMA in packet per buffer mode with descriptor update");
module_param(use_fullspeed, bool, S_IRUGO);
MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Prints UDC device registers and endpoint irq registers */ /* Prints UDC device registers and endpoint irq registers */
static void print_regs(struct udc *dev) static void print_regs(struct udc *dev)
...@@ -267,7 +225,7 @@ static void print_regs(struct udc *dev) ...@@ -267,7 +225,7 @@ static void print_regs(struct udc *dev)
} }
/* Masks unused interrupts */ /* Masks unused interrupts */
static int udc_mask_unused_interrupts(struct udc *dev) int udc_mask_unused_interrupts(struct udc *dev)
{ {
u32 tmp; u32 tmp;
...@@ -287,6 +245,7 @@ static int udc_mask_unused_interrupts(struct udc *dev) ...@@ -287,6 +245,7 @@ static int udc_mask_unused_interrupts(struct udc *dev)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(udc_mask_unused_interrupts);
/* Enables endpoint 0 interrupts */ /* Enables endpoint 0 interrupts */
static int udc_enable_ep0_interrupts(struct udc *dev) static int udc_enable_ep0_interrupts(struct udc *dev)
...@@ -306,7 +265,7 @@ static int udc_enable_ep0_interrupts(struct udc *dev) ...@@ -306,7 +265,7 @@ static int udc_enable_ep0_interrupts(struct udc *dev)
} }
/* Enables device interrupts for SET_INTF and SET_CONFIG */ /* Enables device interrupts for SET_INTF and SET_CONFIG */
static int udc_enable_dev_setup_interrupts(struct udc *dev) int udc_enable_dev_setup_interrupts(struct udc *dev)
{ {
u32 tmp; u32 tmp;
...@@ -325,6 +284,7 @@ static int udc_enable_dev_setup_interrupts(struct udc *dev) ...@@ -325,6 +284,7 @@ static int udc_enable_dev_setup_interrupts(struct udc *dev)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(udc_enable_dev_setup_interrupts);
/* Calculates fifo start of endpoint based on preceding endpoints */ /* Calculates fifo start of endpoint based on preceding endpoints */
static int udc_set_txfifo_addr(struct udc_ep *ep) static int udc_set_txfifo_addr(struct udc_ep *ep)
...@@ -1503,7 +1463,7 @@ static void make_ep_lists(struct udc *dev) ...@@ -1503,7 +1463,7 @@ static void make_ep_lists(struct udc *dev)
} }
/* Inits UDC context */ /* Inits UDC context */
static void udc_basic_init(struct udc *dev) void udc_basic_init(struct udc *dev)
{ {
u32 tmp; u32 tmp;
...@@ -1539,6 +1499,7 @@ static void udc_basic_init(struct udc *dev) ...@@ -1539,6 +1499,7 @@ static void udc_basic_init(struct udc *dev)
dev->data_ep_enabled = 0; dev->data_ep_enabled = 0;
dev->data_ep_queued = 0; dev->data_ep_queued = 0;
} }
EXPORT_SYMBOL_GPL(udc_basic_init);
/* init registers at driver load time */ /* init registers at driver load time */
static int startup_registers(struct udc *dev) static int startup_registers(struct udc *dev)
...@@ -3027,7 +2988,7 @@ __acquires(dev->lock) ...@@ -3027,7 +2988,7 @@ __acquires(dev->lock)
} }
/* Interrupt Service Routine, see Linux Kernel Doc for parameters */ /* Interrupt Service Routine, see Linux Kernel Doc for parameters */
static irqreturn_t udc_irq(int irq, void *pdev) irqreturn_t udc_irq(int irq, void *pdev)
{ {
struct udc *dev = pdev; struct udc *dev = pdev;
u32 reg; u32 reg;
...@@ -3079,16 +3040,18 @@ static irqreturn_t udc_irq(int irq, void *pdev) ...@@ -3079,16 +3040,18 @@ static irqreturn_t udc_irq(int irq, void *pdev)
spin_unlock(&dev->lock); spin_unlock(&dev->lock);
return ret_val; return ret_val;
} }
EXPORT_SYMBOL_GPL(udc_irq);
/* Tears down device */ /* Tears down device */
static void gadget_release(struct device *pdev) void gadget_release(struct device *pdev)
{ {
struct amd5536udc *dev = dev_get_drvdata(pdev); struct amd5536udc *dev = dev_get_drvdata(pdev);
kfree(dev); kfree(dev);
} }
EXPORT_SYMBOL_GPL(gadget_release);
/* Cleanup on device remove */ /* Cleanup on device remove */
static void udc_remove(struct udc *dev) void udc_remove(struct udc *dev)
{ {
/* remove timer */ /* remove timer */
stop_timer++; stop_timer++;
...@@ -3104,9 +3067,10 @@ static void udc_remove(struct udc *dev) ...@@ -3104,9 +3067,10 @@ static void udc_remove(struct udc *dev)
del_timer_sync(&udc_pollstall_timer); del_timer_sync(&udc_pollstall_timer);
udc = NULL; udc = NULL;
} }
EXPORT_SYMBOL_GPL(udc_remove);
/* free all the dma pools */ /* free all the dma pools */
static void free_dma_pools(struct udc *dev) void free_dma_pools(struct udc *dev)
{ {
dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td, dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td,
dev->ep[UDC_EP0OUT_IX].td_phys); dev->ep[UDC_EP0OUT_IX].td_phys);
...@@ -3115,35 +3079,10 @@ static void free_dma_pools(struct udc *dev) ...@@ -3115,35 +3079,10 @@ static void free_dma_pools(struct udc *dev)
dma_pool_destroy(dev->stp_requests); dma_pool_destroy(dev->stp_requests);
dma_pool_destroy(dev->data_requests); dma_pool_destroy(dev->data_requests);
} }
EXPORT_SYMBOL_GPL(free_dma_pools);
/* 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);
}
/* create dma pools on init */ /* create dma pools on init */
static int init_dma_pools(struct udc *dev) int init_dma_pools(struct udc *dev)
{ {
struct udc_stp_dma *td_stp; struct udc_stp_dma *td_stp;
struct udc_data_dma *td_data; struct udc_data_dma *td_data;
...@@ -3206,9 +3145,10 @@ static int init_dma_pools(struct udc *dev) ...@@ -3206,9 +3145,10 @@ static int init_dma_pools(struct udc *dev)
dev->data_requests = NULL; dev->data_requests = NULL;
return retval; return retval;
} }
EXPORT_SYMBOL_GPL(init_dma_pools);
/* general probe */ /* general probe */
static int udc_probe(struct udc *dev) int udc_probe(struct udc *dev)
{ {
char tmp[128]; char tmp[128];
u32 reg; u32 reg;
...@@ -3272,137 +3212,7 @@ static int udc_probe(struct udc *dev) ...@@ -3272,137 +3212,7 @@ static int udc_probe(struct udc *dev)
finished: finished:
return retval; return retval;
} }
EXPORT_SYMBOL_GPL(udc_probe);
/* 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;
}
dev->virt_addr = ioremap_nocache(resource, len);
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);
/* init dma pools */
if (use_dma) {
retval = init_dma_pools(dev);
if (retval != 0)
goto err_dma;
}
dev->phys_addr = resource;
dev->irq = pdev->irq;
dev->pdev = pdev;
/* general probing */
if (udc_probe(dev)) {
retval = -ENODEV;
goto err_probe;
}
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 = {
.name = (char *) name,
.id_table = pci_id,
.probe = udc_pci_probe,
.remove = udc_pci_remove,
};
module_pci_driver(udc_pci_driver);
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
MODULE_AUTHOR("Thomas Dahlmann"); MODULE_AUTHOR("Thomas Dahlmann");
......
...@@ -13,6 +13,12 @@ ...@@ -13,6 +13,12 @@
#ifndef AMD5536UDC_H #ifndef AMD5536UDC_H
#define AMD5536UDC_H #define AMD5536UDC_H
/* debug control */
/* #define UDC_VERBOSE */
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/* various constants */ /* various constants */
#define UDC_RDE_TIMER_SECONDS 1 #define UDC_RDE_TIMER_SECONDS 1
#define UDC_RDE_TIMER_DIV 10 #define UDC_RDE_TIMER_DIV 10
...@@ -567,6 +573,36 @@ union udc_setup_data { ...@@ -567,6 +573,36 @@ union udc_setup_data {
struct usb_ctrlrequest request; struct usb_ctrlrequest request;
}; };
/* Function declarations */
int udc_enable_dev_setup_interrupts(struct udc *dev);
int udc_mask_unused_interrupts(struct udc *dev);
irqreturn_t udc_irq(int irq, void *pdev);
void gadget_release(struct device *pdev);
void udc_basic_init(struct udc *dev);
void free_dma_pools(struct udc *dev);
int init_dma_pools(struct udc *dev);
void udc_remove(struct udc *dev);
int udc_probe(struct udc *dev);
/* DMA usage flag */
static bool use_dma = 1;
/* packet per buffer dma */
static bool use_dma_ppb = 1;
/* with per descr. update */
static bool use_dma_ppb_du;
/* full speed only mode */
static bool use_fullspeed;
/* module parameters */
module_param(use_dma, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma, "true for DMA");
module_param(use_dma_ppb, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
module_param(use_dma_ppb_du, bool, S_IRUGO);
MODULE_PARM_DESC(use_dma_ppb_du,
"true for DMA in packet per buffer mode with descriptor update");
module_param(use_fullspeed, bool, S_IRUGO);
MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
/* /*
*--------------------------------------------------------------------------- *---------------------------------------------------------------------------
* SET and GET bitfields in u32 values * SET and GET bitfields in u32 values
......
/*
* amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller
*
* Copyright (C) 2005-2007 AMD (http://www.amd.com)
* Author: Thomas Dahlmann
*
* 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.
*/
/*
* 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 mod_desc[] = UDC_MOD_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;
}
dev->virt_addr = ioremap_nocache(resource, len);
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);
/* init dma pools */
if (use_dma) {
retval = init_dma_pools(dev);
if (retval != 0)
goto err_dma;
}
dev->phys_addr = resource;
dev->irq = pdev->irq;
dev->pdev = pdev;
/* general probing */
if (udc_probe(dev)) {
retval = -ENODEV;
goto err_probe;
}
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 = {
.name = (char *) name,
.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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment