2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-12-12 21:25:29 +04:00
/**
* dwc3 - keystone . c - Keystone Specific Glue layer
*
* Copyright ( C ) 2010 - 2013 Texas Instruments Incorporated - http : //www.ti.com
*
* Author : WingMan Kwok < w - kwok2 @ ti . com >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/dma-mapping.h>
# include <linux/io.h>
# include <linux/of_platform.h>
2017-08-02 23:17:22 +03:00
# include <linux/pm_runtime.h>
2013-12-12 21:25:29 +04:00
/* USBSS register offsets */
# define USBSS_REVISION 0x0000
# define USBSS_SYSCONFIG 0x0010
# define USBSS_IRQ_EOI 0x0018
# define USBSS_IRQSTATUS_RAW_0 0x0020
# define USBSS_IRQSTATUS_0 0x0024
# define USBSS_IRQENABLE_SET_0 0x0028
# define USBSS_IRQENABLE_CLR_0 0x002c
/* IRQ register bits */
# define USBSS_IRQ_EOI_LINE(n) BIT(n)
# define USBSS_IRQ_EVENT_ST BIT(0)
# define USBSS_IRQ_COREIRQ_EN BIT(0)
# define USBSS_IRQ_COREIRQ_CLR BIT(0)
struct dwc3_keystone {
struct device * dev ;
void __iomem * usbss ;
} ;
static inline u32 kdwc3_readl ( void __iomem * base , u32 offset )
{
return readl ( base + offset ) ;
}
static inline void kdwc3_writel ( void __iomem * base , u32 offset , u32 value )
{
writel ( value , base + offset ) ;
}
static void kdwc3_enable_irqs ( struct dwc3_keystone * kdwc )
{
u32 val ;
val = kdwc3_readl ( kdwc - > usbss , USBSS_IRQENABLE_SET_0 ) ;
val | = USBSS_IRQ_COREIRQ_EN ;
kdwc3_writel ( kdwc - > usbss , USBSS_IRQENABLE_SET_0 , val ) ;
}
static void kdwc3_disable_irqs ( struct dwc3_keystone * kdwc )
{
u32 val ;
val = kdwc3_readl ( kdwc - > usbss , USBSS_IRQENABLE_SET_0 ) ;
val & = ~ USBSS_IRQ_COREIRQ_EN ;
kdwc3_writel ( kdwc - > usbss , USBSS_IRQENABLE_SET_0 , val ) ;
}
static irqreturn_t dwc3_keystone_interrupt ( int irq , void * _kdwc )
{
struct dwc3_keystone * kdwc = _kdwc ;
kdwc3_writel ( kdwc - > usbss , USBSS_IRQENABLE_CLR_0 , USBSS_IRQ_COREIRQ_CLR ) ;
kdwc3_writel ( kdwc - > usbss , USBSS_IRQSTATUS_0 , USBSS_IRQ_EVENT_ST ) ;
kdwc3_writel ( kdwc - > usbss , USBSS_IRQENABLE_SET_0 , USBSS_IRQ_COREIRQ_EN ) ;
kdwc3_writel ( kdwc - > usbss , USBSS_IRQ_EOI , USBSS_IRQ_EOI_LINE ( 0 ) ) ;
return IRQ_HANDLED ;
}
static int kdwc3_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * node = pdev - > dev . of_node ;
struct dwc3_keystone * kdwc ;
int error , irq ;
kdwc = devm_kzalloc ( dev , sizeof ( * kdwc ) , GFP_KERNEL ) ;
if ( ! kdwc )
return - ENOMEM ;
platform_set_drvdata ( pdev , kdwc ) ;
kdwc - > dev = dev ;
2019-08-02 16:01:04 +03:00
kdwc - > usbss = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-12-12 21:25:29 +04:00
if ( IS_ERR ( kdwc - > usbss ) )
return PTR_ERR ( kdwc - > usbss ) ;
2017-08-02 23:17:22 +03:00
pm_runtime_enable ( kdwc - > dev ) ;
2013-12-12 21:25:29 +04:00
2017-08-02 23:17:22 +03:00
error = pm_runtime_get_sync ( kdwc - > dev ) ;
2013-12-12 21:25:29 +04:00
if ( error < 0 ) {
2017-08-02 23:17:22 +03:00
dev_err ( kdwc - > dev , " pm_runtime_get_sync failed, error %d \n " ,
2013-12-12 21:25:29 +04:00
error ) ;
2017-08-02 23:17:22 +03:00
goto err_irq ;
2013-12-12 21:25:29 +04:00
}
2019-01-10 18:04:30 +03:00
/* IRQ processing not required currently for AM65 */
if ( of_device_is_compatible ( node , " ti,am654-dwc3 " ) )
goto skip_irq ;
2013-12-12 21:25:29 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
2014-11-20 20:33:58 +03:00
error = irq ;
2013-12-12 21:25:29 +04:00
goto err_irq ;
}
error = devm_request_irq ( dev , irq , dwc3_keystone_interrupt , IRQF_SHARED ,
dev_name ( dev ) , kdwc ) ;
if ( error ) {
dev_err ( dev , " failed to request IRQ #%d --> %d \n " ,
irq , error ) ;
goto err_irq ;
}
kdwc3_enable_irqs ( kdwc ) ;
2019-01-10 18:04:30 +03:00
skip_irq :
2013-12-12 21:25:29 +04:00
error = of_platform_populate ( node , NULL , NULL , dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to create dwc3 core \n " ) ;
goto err_core ;
}
return 0 ;
err_core :
kdwc3_disable_irqs ( kdwc ) ;
err_irq :
2017-08-02 23:17:22 +03:00
pm_runtime_put_sync ( kdwc - > dev ) ;
pm_runtime_disable ( kdwc - > dev ) ;
2013-12-12 21:25:29 +04:00
return error ;
}
static int kdwc3_remove_core ( struct device * dev , void * c )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
platform_device_unregister ( pdev ) ;
return 0 ;
}
static int kdwc3_remove ( struct platform_device * pdev )
{
struct dwc3_keystone * kdwc = platform_get_drvdata ( pdev ) ;
2019-01-10 18:04:30 +03:00
struct device_node * node = pdev - > dev . of_node ;
if ( ! of_device_is_compatible ( node , " ti,am654-dwc3 " ) )
kdwc3_disable_irqs ( kdwc ) ;
2013-12-12 21:25:29 +04:00
device_for_each_child ( & pdev - > dev , NULL , kdwc3_remove_core ) ;
2017-08-02 23:17:22 +03:00
pm_runtime_put_sync ( kdwc - > dev ) ;
pm_runtime_disable ( kdwc - > dev ) ;
2013-12-12 21:25:29 +04:00
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
static const struct of_device_id kdwc3_of_match [ ] = {
{ . compatible = " ti,keystone-dwc3 " , } ,
2019-01-10 18:04:30 +03:00
{ . compatible = " ti,am654-dwc3 " } ,
2013-12-12 21:25:29 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , kdwc3_of_match ) ;
static struct platform_driver kdwc3_driver = {
. probe = kdwc3_probe ,
. remove = kdwc3_remove ,
. driver = {
. name = " keystone-dwc3 " ,
. of_match_table = kdwc3_of_match ,
} ,
} ;
module_platform_driver ( kdwc3_driver ) ;
MODULE_ALIAS ( " platform:keystone-dwc3 " ) ;
MODULE_AUTHOR ( " WingMan Kwok <w-kwok2@ti.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " DesignWare USB3 KEYSTONE Glue Layer " ) ;