2013-04-23 01:00:19 +04:00
/*
* platform . c - DesignWare HS OTG Controller platform driver
*
* Copyright ( C ) Matthijs Kooijman < matthijs @ stdin . nl >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The names of the above - listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* ALTERNATIVELY , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) as published by the Free Software
* Foundation ; either version 2 of the License , or ( at your option ) any
* later version .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS
* IS " AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
* PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/dma-mapping.h>
2013-11-27 05:58:01 +04:00
# include <linux/of_device.h>
2014-11-21 17:14:48 +03:00
# include <linux/mutex.h>
2013-04-23 01:00:19 +04:00
# include <linux/platform_device.h>
2014-08-06 05:01:50 +04:00
# include <linux/usb/of.h>
2013-04-23 01:00:19 +04:00
# include "core.h"
# include "hcd.h"
2015-04-29 23:08:59 +03:00
# include "debug.h"
2013-04-23 01:00:19 +04:00
static const char dwc2_driver_name [ ] = " dwc2 " ;
2013-11-27 05:58:01 +04:00
static const struct dwc2_core_params params_bcm2835 = {
. otg_cap = 0 , /* HNP/SRP capable */
. otg_ver = 0 , /* 1.3 */
. dma_enable = 1 ,
. dma_desc_enable = 0 ,
. speed = 0 , /* High Speed */
. enable_dynamic_fifo = 1 ,
. en_multiple_tx_fifo = 1 ,
. host_rx_fifo_size = 774 , /* 774 DWORDs */
. host_nperio_tx_fifo_size = 256 , /* 256 DWORDs */
. host_perio_tx_fifo_size = 512 , /* 512 DWORDs */
. max_transfer_size = 65535 ,
. max_packet_count = 511 ,
. host_channels = 8 ,
. phy_type = 1 , /* UTMI */
. phy_utmi_width = 8 , /* 8 bits */
. phy_ulpi_ddr = 0 , /* Single */
. phy_ulpi_ext_vbus = 0 ,
. i2c_enable = 0 ,
. ulpi_fs_ls = 0 ,
. host_support_fs_ls_low_power = 0 ,
. host_ls_low_power_phy_clk = 0 , /* 48 MHz */
. ts_dline = 0 ,
. reload_ctl = 0 ,
. ahbcfg = 0x10 ,
2013-12-04 07:56:05 +04:00
. uframe_sched = 0 ,
2013-11-27 05:58:01 +04:00
} ;
2014-08-08 07:55:57 +04:00
static const struct dwc2_core_params params_rk3066 = {
. otg_cap = 2 , /* non-HNP/non-SRP */
. otg_ver = - 1 ,
. dma_enable = - 1 ,
. dma_desc_enable = 0 ,
. speed = - 1 ,
. enable_dynamic_fifo = 1 ,
. en_multiple_tx_fifo = - 1 ,
. host_rx_fifo_size = 520 , /* 520 DWORDs */
. host_nperio_tx_fifo_size = 128 , /* 128 DWORDs */
. host_perio_tx_fifo_size = 256 , /* 256 DWORDs */
. max_transfer_size = 65535 ,
. max_packet_count = - 1 ,
. host_channels = - 1 ,
. phy_type = - 1 ,
. phy_utmi_width = - 1 ,
. phy_ulpi_ddr = - 1 ,
. phy_ulpi_ext_vbus = - 1 ,
. i2c_enable = - 1 ,
. ulpi_fs_ls = - 1 ,
. host_support_fs_ls_low_power = - 1 ,
. host_ls_low_power_phy_clk = - 1 ,
. ts_dline = - 1 ,
. reload_ctl = - 1 ,
. ahbcfg = 0x7 , /* INCR16 */
. uframe_sched = - 1 ,
} ;
2013-04-23 01:00:19 +04:00
/**
* dwc2_driver_remove ( ) - Called when the DWC_otg core is unregistered with the
* DWC_otg driver
*
* @ dev : Platform device
*
* This routine is called , for example , when the rmmod command is executed . The
* device may or may not be electrically present . If it is present , the driver
* stops device processing . Any resources used on behalf of this device are
* freed .
*/
static int dwc2_driver_remove ( struct platform_device * dev )
{
struct dwc2_hsotg * hsotg = platform_get_drvdata ( dev ) ;
2015-04-29 23:08:59 +03:00
dwc2_debugfs_exit ( hsotg ) ;
2015-03-10 15:41:10 +03:00
if ( hsotg - > hcd_enabled )
dwc2_hcd_remove ( hsotg ) ;
if ( hsotg - > gadget_enabled )
s3c_hsotg_remove ( hsotg ) ;
2013-04-23 01:00:19 +04:00
return 0 ;
}
2013-11-27 05:58:01 +04:00
static const struct of_device_id dwc2_of_match_table [ ] = {
{ . compatible = " brcm,bcm2835-usb " , . data = & params_bcm2835 } ,
2014-08-08 07:55:57 +04:00
{ . compatible = " rockchip,rk3066-usb " , . data = & params_rk3066 } ,
2013-11-27 05:58:01 +04:00
{ . compatible = " snps,dwc2 " , . data = NULL } ,
2014-11-11 20:13:34 +03:00
{ . compatible = " samsung,s3c6400-hsotg " , . data = NULL } ,
2013-11-27 05:58:01 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dwc2_of_match_table ) ;
2013-04-23 01:00:19 +04:00
/**
* dwc2_driver_probe ( ) - Called when the DWC_otg core is bound to the DWC_otg
* driver
*
* @ dev : Platform device
*
* This routine creates the driver components required to control the device
* ( core , HCD , and PCD ) and initializes the device . The driver components are
* stored in a dwc2_hsotg structure . A reference to the dwc2_hsotg is saved
* in the device private data . This allows the driver to access the dwc2_hsotg
* structure on subsequent calls to driver methods for this device .
*/
static int dwc2_driver_probe ( struct platform_device * dev )
{
2013-11-27 05:58:01 +04:00
const struct of_device_id * match ;
const struct dwc2_core_params * params ;
struct dwc2_core_params defparams ;
2013-04-23 01:00:19 +04:00
struct dwc2_hsotg * hsotg ;
struct resource * res ;
2014-12-08 12:46:26 +03:00
struct phy * phy ;
struct usb_phy * uphy ;
2013-04-23 01:00:19 +04:00
int retval ;
int irq ;
2013-11-27 05:58:01 +04:00
match = of_match_device ( dwc2_of_match_table , & dev - > dev ) ;
if ( match & & match - > data ) {
params = match - > data ;
} else {
/* Default all params to autodetect */
dwc2_set_all_params ( & defparams , - 1 ) ;
params = & defparams ;
2014-05-07 17:30:33 +04:00
/*
* Disable descriptor dma mode by default as the HW can support
* it , but does not support it for SPLIT transactions .
*/
defparams . dma_desc_enable = 0 ;
2013-11-27 05:58:01 +04:00
}
2013-04-23 01:00:19 +04:00
hsotg = devm_kzalloc ( & dev - > dev , sizeof ( * hsotg ) , GFP_KERNEL ) ;
if ( ! hsotg )
return - ENOMEM ;
hsotg - > dev = & dev - > dev ;
2013-05-17 12:52:55 +04:00
/*
* Use reasonable defaults so platforms don ' t have to provide these .
*/
if ( ! dev - > dev . dma_mask )
dev - > dev . dma_mask = & dev - > dev . coherent_dma_mask ;
2013-06-10 19:56:16 +04:00
retval = dma_set_coherent_mask ( & dev - > dev , DMA_BIT_MASK ( 32 ) ) ;
if ( retval )
return retval ;
2013-05-17 12:52:55 +04:00
2013-04-23 01:00:19 +04:00
irq = platform_get_irq ( dev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & dev - > dev , " missing IRQ resource \n " ) ;
2013-10-26 21:42:19 +04:00
return irq ;
2013-04-23 01:00:19 +04:00
}
2014-11-11 20:13:37 +03:00
dev_dbg ( hsotg - > dev , " registering common handler for irq%d \n " ,
irq ) ;
retval = devm_request_irq ( hsotg - > dev , irq ,
dwc2_handle_common_intr , IRQF_SHARED ,
dev_name ( hsotg - > dev ) , hsotg ) ;
if ( retval )
return retval ;
2013-04-23 01:00:19 +04:00
res = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
hsotg - > regs = devm_ioremap_resource ( & dev - > dev , res ) ;
if ( IS_ERR ( hsotg - > regs ) )
return PTR_ERR ( hsotg - > regs ) ;
dev_dbg ( & dev - > dev , " mapped PA %08lx to VA %p \n " ,
( unsigned long ) res - > start , hsotg - > regs ) ;
2014-08-06 05:01:50 +04:00
hsotg - > dr_mode = of_usb_get_dr_mode ( dev - > dev . of_node ) ;
2014-12-08 12:46:26 +03:00
/*
* Attempt to find a generic PHY , then look for an old style
* USB PHY
*/
phy = devm_phy_get ( & dev - > dev , " usb2-phy " ) ;
if ( IS_ERR ( phy ) ) {
hsotg - > phy = NULL ;
uphy = devm_usb_get_phy ( & dev - > dev , USB_PHY_TYPE_USB2 ) ;
if ( IS_ERR ( uphy ) )
hsotg - > uphy = NULL ;
else
hsotg - > uphy = uphy ;
} else {
hsotg - > phy = phy ;
phy_power_on ( hsotg - > phy ) ;
phy_init ( hsotg - > phy ) ;
}
2014-11-11 20:13:34 +03:00
spin_lock_init ( & hsotg - > lock ) ;
2014-11-21 17:14:48 +03:00
mutex_init ( & hsotg - > init_mutex ) ;
2015-03-10 15:41:10 +03:00
if ( hsotg - > dr_mode ! = USB_DR_MODE_HOST ) {
retval = dwc2_gadget_init ( hsotg , irq ) ;
if ( retval )
return retval ;
hsotg - > gadget_enabled = 1 ;
}
if ( hsotg - > dr_mode ! = USB_DR_MODE_PERIPHERAL ) {
retval = dwc2_hcd_init ( hsotg , irq , params ) ;
if ( retval ) {
if ( hsotg - > gadget_enabled )
s3c_hsotg_remove ( hsotg ) ;
return retval ;
}
hsotg - > hcd_enabled = 1 ;
}
2013-04-23 01:00:19 +04:00
platform_set_drvdata ( dev , hsotg ) ;
2015-04-29 23:08:59 +03:00
dwc2_debugfs_init ( hsotg ) ;
2013-04-23 01:00:19 +04:00
return retval ;
}
2014-11-19 17:37:53 +03:00
static int __maybe_unused dwc2_suspend ( struct device * dev )
2014-11-11 20:13:34 +03:00
{
2014-11-11 20:13:35 +03:00
struct dwc2_hsotg * dwc2 = dev_get_drvdata ( dev ) ;
2014-11-11 20:13:34 +03:00
int ret = 0 ;
2014-12-08 12:46:26 +03:00
if ( dwc2_is_device_mode ( dwc2 ) ) {
2014-11-11 20:13:34 +03:00
ret = s3c_hsotg_suspend ( dwc2 ) ;
2014-12-08 12:46:26 +03:00
} else {
if ( dwc2 - > lx_state = = DWC2_L0 )
return 0 ;
phy_exit ( dwc2 - > phy ) ;
phy_power_off ( dwc2 - > phy ) ;
}
2014-11-11 20:13:34 +03:00
return ret ;
}
2014-11-19 17:37:53 +03:00
static int __maybe_unused dwc2_resume ( struct device * dev )
2014-11-11 20:13:34 +03:00
{
2014-11-11 20:13:35 +03:00
struct dwc2_hsotg * dwc2 = dev_get_drvdata ( dev ) ;
2014-11-11 20:13:34 +03:00
int ret = 0 ;
2014-12-08 12:46:26 +03:00
if ( dwc2_is_device_mode ( dwc2 ) ) {
2014-11-11 20:13:34 +03:00
ret = s3c_hsotg_resume ( dwc2 ) ;
2014-12-08 12:46:26 +03:00
} else {
phy_power_on ( dwc2 - > phy ) ;
phy_init ( dwc2 - > phy ) ;
}
2014-11-11 20:13:34 +03:00
return ret ;
}
2014-11-11 20:13:35 +03:00
static const struct dev_pm_ops dwc2_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( dwc2_suspend , dwc2_resume )
} ;
2013-04-23 01:00:19 +04:00
static struct platform_driver dwc2_platform_driver = {
. driver = {
2013-11-12 23:07:19 +04:00
. name = dwc2_driver_name ,
2013-04-23 01:00:19 +04:00
. of_match_table = dwc2_of_match_table ,
2014-11-11 20:13:35 +03:00
. pm = & dwc2_dev_pm_ops ,
2013-04-23 01:00:19 +04:00
} ,
. probe = dwc2_driver_probe ,
. remove = dwc2_driver_remove ,
} ;
module_platform_driver ( dwc2_platform_driver ) ;
MODULE_DESCRIPTION ( " DESIGNWARE HS OTG Platform Glue " ) ;
MODULE_AUTHOR ( " Matthijs Kooijman <matthijs@stdin.nl> " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;