2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2011-02-24 16:56:28 +03:00
/*
* PMC MSP EHCI ( Host Controller Driver ) for USB .
*
* ( C ) Copyright 2006 - 2010 PMC - Sierra Inc
*/
/* includes */
# include <linux/platform_device.h>
# include <linux/gpio.h>
# include <linux/usb.h>
# include <msp_usb.h>
/* stream disable*/
# define USB_CTRL_MODE_STREAM_DISABLE 0x10
/* threshold */
# define USB_CTRL_FIFO_THRESH 0x00300000
/* register offset for usb_mode */
# define USB_EHCI_REG_USB_MODE 0x68
/* register offset for usb fifo */
# define USB_EHCI_REG_USB_FIFO 0x24
/* register offset for usb status */
# define USB_EHCI_REG_USB_STATUS 0x44
/* serial/parallel transceiver */
# define USB_EHCI_REG_BIT_STAT_STS (1<<29)
/* TWI USB0 host device pin */
# define MSP_PIN_USB0_HOST_DEV 49
/* TWI USB1 host device pin */
# define MSP_PIN_USB1_HOST_DEV 50
static void usb_hcd_tdi_set_mode ( struct ehci_hcd * ehci )
{
u8 * base ;
u8 * statreg ;
u8 * fiforeg ;
u32 val ;
struct ehci_regs * reg_base = ehci - > regs ;
/* get register base */
base = ( u8 * ) reg_base + USB_EHCI_REG_USB_MODE ;
statreg = ( u8 * ) reg_base + USB_EHCI_REG_USB_STATUS ;
fiforeg = ( u8 * ) reg_base + USB_EHCI_REG_USB_FIFO ;
/* Disable controller mode stream */
val = ehci_readl ( ehci , ( u32 * ) base ) ;
ehci_writel ( ehci , ( val | USB_CTRL_MODE_STREAM_DISABLE ) ,
( u32 * ) base ) ;
/* clear STS to select parallel transceiver interface */
val = ehci_readl ( ehci , ( u32 * ) statreg ) ;
val = val & ~ USB_EHCI_REG_BIT_STAT_STS ;
ehci_writel ( ehci , val , ( u32 * ) statreg ) ;
/* write to set the proper fifo threshold */
ehci_writel ( ehci , USB_CTRL_FIFO_THRESH , ( u32 * ) fiforeg ) ;
/* set TWI GPIO USB_HOST_DEV pin high */
gpio_direction_output ( MSP_PIN_USB0_HOST_DEV , 1 ) ;
}
/* called during probe() after chip reset completes */
static int ehci_msp_setup ( struct usb_hcd * hcd )
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
int retval ;
2012-07-09 23:55:14 +04:00
2011-02-24 16:56:28 +03:00
ehci - > big_endian_mmio = 1 ;
ehci - > big_endian_desc = 1 ;
ehci - > caps = hcd - > regs ;
hcd - > has_tt = 1 ;
2012-07-09 23:55:14 +04:00
retval = ehci_setup ( hcd ) ;
2011-02-24 16:56:28 +03:00
if ( retval )
return retval ;
usb_hcd_tdi_set_mode ( ehci ) ;
return retval ;
}
/* configure so an HC device and id are always provided
* always called with process context ; sleeping is OK
*/
static int usb_hcd_msp_map_regs ( struct mspusb_device * dev )
{
struct resource * res ;
struct platform_device * pdev = & dev - > dev ;
u32 res_len ;
int retval ;
/* MAB register space */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( res = = NULL )
return - ENOMEM ;
2011-06-09 20:13:32 +04:00
res_len = resource_size ( res ) ;
2011-02-24 16:56:28 +03:00
if ( ! request_mem_region ( res - > start , res_len , " mab regs " ) )
return - EBUSY ;
2020-01-06 11:43:50 +03:00
dev - > mab_regs = ioremap ( res - > start , res_len ) ;
2011-02-24 16:56:28 +03:00
if ( dev - > mab_regs = = NULL ) {
retval = - ENOMEM ;
goto err1 ;
}
/* MSP USB register space */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 2 ) ;
if ( res = = NULL ) {
retval = - ENOMEM ;
goto err2 ;
}
2011-06-09 20:13:32 +04:00
res_len = resource_size ( res ) ;
2011-02-24 16:56:28 +03:00
if ( ! request_mem_region ( res - > start , res_len , " usbid regs " ) ) {
retval = - EBUSY ;
goto err2 ;
}
2020-01-06 11:43:50 +03:00
dev - > usbid_regs = ioremap ( res - > start , res_len ) ;
2011-02-24 16:56:28 +03:00
if ( dev - > usbid_regs = = NULL ) {
retval = - ENOMEM ;
goto err3 ;
}
return 0 ;
err3 :
res = platform_get_resource ( pdev , IORESOURCE_MEM , 2 ) ;
2011-06-09 20:13:32 +04:00
res_len = resource_size ( res ) ;
2011-02-24 16:56:28 +03:00
release_mem_region ( res - > start , res_len ) ;
err2 :
iounmap ( dev - > mab_regs ) ;
err1 :
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
2011-06-09 20:13:32 +04:00
res_len = resource_size ( res ) ;
2011-02-24 16:56:28 +03:00
release_mem_region ( res - > start , res_len ) ;
dev_err ( & pdev - > dev , " Failed to map non-EHCI regs. \n " ) ;
return retval ;
}
/**
* usb_hcd_msp_probe - initialize PMC MSP - based HCDs
* Context : ! in_interrupt ( )
*
* Allocates basic resources for this USB host controller , and
* then invokes the start ( ) method for the HCD associated with it
* through the hotplug entry ' s driver_data .
*
*/
int usb_hcd_msp_probe ( const struct hc_driver * driver ,
struct platform_device * dev )
{
int retval ;
struct usb_hcd * hcd ;
struct resource * res ;
struct ehci_hcd * ehci ;
hcd = usb_create_hcd ( driver , & dev - > dev , " pmcmsp " ) ;
if ( ! hcd )
return - ENOMEM ;
res = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
pr_debug ( " No IOMEM resource info for %s. \n " , dev - > name ) ;
retval = - ENOMEM ;
goto err1 ;
}
hcd - > rsrc_start = res - > start ;
2011-06-09 20:13:32 +04:00
hcd - > rsrc_len = resource_size ( res ) ;
2011-02-24 16:56:28 +03:00
if ( ! request_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len , dev - > name ) ) {
retval = - EBUSY ;
goto err1 ;
}
2020-01-06 11:43:50 +03:00
hcd - > regs = ioremap ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
2011-02-24 16:56:28 +03:00
if ( ! hcd - > regs ) {
pr_debug ( " ioremap failed " ) ;
retval = - ENOMEM ;
goto err2 ;
}
res = platform_get_resource ( dev , IORESOURCE_IRQ , 0 ) ;
if ( res = = NULL ) {
dev_err ( & dev - > dev , " No IRQ resource info for %s. \n " , dev - > name ) ;
retval = - ENOMEM ;
goto err3 ;
}
/* Map non-EHCI register spaces */
retval = usb_hcd_msp_map_regs ( to_mspusb_device ( dev ) ) ;
if ( retval ! = 0 )
goto err3 ;
ehci = hcd_to_ehci ( hcd ) ;
ehci - > big_endian_mmio = 1 ;
ehci - > big_endian_desc = 1 ;
retval = usb_add_hcd ( hcd , res - > start , IRQF_SHARED ) ;
2013-11-05 06:46:02 +04:00
if ( retval = = 0 ) {
device_wakeup_enable ( hcd - > self . controller ) ;
2011-02-24 16:56:28 +03:00
return 0 ;
2013-11-05 06:46:02 +04:00
}
2011-02-24 16:56:28 +03:00
usb_remove_hcd ( hcd ) ;
err3 :
iounmap ( hcd - > regs ) ;
err2 :
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
err1 :
usb_put_hcd ( hcd ) ;
return retval ;
}
/**
* usb_hcd_msp_remove - shutdown processing for PMC MSP - based HCDs
* @ dev : USB Host Controller being removed
* Context : ! in_interrupt ( )
*
* Reverses the effect of usb_hcd_msp_probe ( ) , first invoking
* the HCD ' s stop ( ) method . It is always called from a thread
* context , normally " rmmod " , " apmd " , or something similar .
*
* may be called without controller electrically present
* may be called with controller , bus , and devices active
*/
void usb_hcd_msp_remove ( struct usb_hcd * hcd , struct platform_device * dev )
{
usb_remove_hcd ( hcd ) ;
iounmap ( hcd - > regs ) ;
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
usb_put_hcd ( hcd ) ;
}
static const struct hc_driver ehci_msp_hc_driver = {
. description = hcd_name ,
. product_desc = " PMC MSP EHCI " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ehci_irq ,
2019-08-16 09:24:32 +03:00
. flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH ,
2011-02-24 16:56:28 +03:00
/*
* basic lifecycle operations
*/
2013-06-16 15:46:38 +04:00
. reset = ehci_msp_setup ,
2011-02-24 16:56:28 +03:00
. shutdown = ehci_shutdown ,
. start = ehci_run ,
. stop = ehci_stop ,
/*
* managing i / o requests and associated device resources
*/
. urb_enqueue = ehci_urb_enqueue ,
. urb_dequeue = ehci_urb_dequeue ,
. endpoint_disable = ehci_endpoint_disable ,
. endpoint_reset = ehci_endpoint_reset ,
/*
* scheduling support
*/
. get_frame_number = ehci_get_frame ,
/*
* root hub support
*/
. hub_status_data = ehci_hub_status_data ,
. hub_control = ehci_hub_control ,
. bus_suspend = ehci_bus_suspend ,
. bus_resume = ehci_bus_resume ,
. relinquish_port = ehci_relinquish_port ,
. port_handed_over = ehci_port_handed_over ,
. clear_tt_buffer_complete = ehci_clear_tt_buffer_complete ,
} ;
static int ehci_hcd_msp_drv_probe ( struct platform_device * pdev )
{
int ret ;
pr_debug ( " In ehci_hcd_msp_drv_probe " ) ;
if ( usb_disabled ( ) )
return - ENODEV ;
gpio_request ( MSP_PIN_USB0_HOST_DEV , " USB0_HOST_DEV_GPIO " ) ;
ret = usb_hcd_msp_probe ( & ehci_msp_hc_driver , pdev ) ;
return ret ;
}
static int ehci_hcd_msp_drv_remove ( struct platform_device * pdev )
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
usb_hcd_msp_remove ( hcd , pdev ) ;
/* free TWI GPIO USB_HOST_DEV pin */
gpio_free ( MSP_PIN_USB0_HOST_DEV ) ;
return 0 ;
}
MODULE_ALIAS ( " pmcmsp-ehci " ) ;
static struct platform_driver ehci_hcd_msp_driver = {
. probe = ehci_hcd_msp_drv_probe ,
. remove = ehci_hcd_msp_drv_remove ,
. driver = {
. name = " pmcmsp-ehci " ,
} ,
} ;