2018-06-20 09:33:04 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Nuvoton NPCM7xx driver for EHCI HCD
*
* Copyright ( C ) 2018 Nuvoton Technologies ,
* Avi Fishman < avi . fishman @ nuvoton . com > < avifishman70 @ gmail . com >
* Tomer Maimon < tomer . maimon @ nuvoton . com > < tmaimon77 @ gmail . com >
*
* Based on various ehci - spear . c driver
*/
# include <linux/dma-mapping.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pm.h>
# include <linux/usb.h>
# include <linux/usb/hcd.h>
# include "ehci.h"
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
# define DRIVER_DESC "EHCI npcm7xx driver"
static const char hcd_name [ ] = " npcm7xx-ehci " ;
# define USB2PHYCTL_OFFSET 0x144
# define IPSRST2_OFFSET 0x24
# define IPSRST3_OFFSET 0x34
static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver ;
2020-09-03 13:25:37 +02:00
static int __maybe_unused ehci_npcm7xx_drv_suspend ( struct device * dev )
2018-06-20 09:33:04 +03:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
bool do_wakeup = device_may_wakeup ( dev ) ;
return ehci_suspend ( hcd , do_wakeup ) ;
}
2020-09-03 13:25:37 +02:00
static int __maybe_unused ehci_npcm7xx_drv_resume ( struct device * dev )
2018-06-20 09:33:04 +03:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
ehci_resume ( hcd , false ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( ehci_npcm7xx_pm_ops , ehci_npcm7xx_drv_suspend ,
ehci_npcm7xx_drv_resume ) ;
static int npcm7xx_ehci_hcd_drv_probe ( struct platform_device * pdev )
{
struct usb_hcd * hcd ;
struct resource * res ;
struct regmap * gcr_regmap ;
struct regmap * rst_regmap ;
const struct hc_driver * driver = & ehci_npcm7xx_hc_driver ;
int irq ;
int retval ;
dev_dbg ( & pdev - > dev , " initializing npcm7xx ehci USB Controller \n " ) ;
gcr_regmap = syscon_regmap_lookup_by_compatible ( " nuvoton,npcm750-gcr " ) ;
if ( IS_ERR ( gcr_regmap ) ) {
dev_err ( & pdev - > dev , " %s: failed to find nuvoton,npcm750-gcr \n " ,
__func__ ) ;
2018-06-27 11:37:05 +03:00
return PTR_ERR ( gcr_regmap ) ;
2018-06-20 09:33:04 +03:00
}
rst_regmap = syscon_regmap_lookup_by_compatible ( " nuvoton,npcm750-rst " ) ;
if ( IS_ERR ( rst_regmap ) ) {
dev_err ( & pdev - > dev , " %s: failed to find nuvoton,npcm750-rst \n " ,
__func__ ) ;
2018-06-27 11:37:05 +03:00
return PTR_ERR ( rst_regmap ) ;
2018-06-20 09:33:04 +03:00
}
/********* phy init ******/
// reset usb host
regmap_update_bits ( rst_regmap , IPSRST2_OFFSET ,
( 0x1 < < 26 ) , ( 0x1 < < 26 ) ) ;
regmap_update_bits ( rst_regmap , IPSRST3_OFFSET ,
( 0x1 < < 25 ) , ( 0x1 < < 25 ) ) ;
regmap_update_bits ( gcr_regmap , USB2PHYCTL_OFFSET ,
( 0x1 < < 28 ) , 0 ) ;
udelay ( 1 ) ;
// enable phy
regmap_update_bits ( rst_regmap , IPSRST3_OFFSET ,
( 0x1 < < 25 ) , 0 ) ;
udelay ( 50 ) ; // enable phy
regmap_update_bits ( gcr_regmap , USB2PHYCTL_OFFSET ,
( 0x1 < < 28 ) , ( 0x1 < < 28 ) ) ;
// enable host
regmap_update_bits ( rst_regmap , IPSRST2_OFFSET ,
( 0x1 < < 26 ) , 0 ) ;
if ( usb_disabled ( ) )
return - ENODEV ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
retval = irq ;
goto fail ;
}
/*
* Right now device - tree probed devices don ' t get dma_mask set .
* Since shared usb code relies on it , set it here for now .
* Once we have dma capability bindings this can go away .
*/
retval = dma_coerce_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
if ( retval )
goto fail ;
hcd = usb_create_hcd ( driver , & pdev - > dev , dev_name ( & pdev - > dev ) ) ;
if ( ! hcd ) {
retval = - ENOMEM ;
goto fail ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
hcd - > regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( hcd - > regs ) ) {
retval = PTR_ERR ( hcd - > regs ) ;
goto err_put_hcd ;
}
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
/* registers start at offset 0x0 */
hcd_to_ehci ( hcd ) - > caps = hcd - > regs ;
retval = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
if ( retval )
goto err_put_hcd ;
device_wakeup_enable ( hcd - > self . controller ) ;
return retval ;
err_put_hcd :
usb_put_hcd ( hcd ) ;
fail :
dev_err ( & pdev - > dev , " init fail, %d \n " , retval ) ;
return retval ;
}
static int npcm7xx_ehci_hcd_drv_remove ( struct platform_device * pdev )
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
usb_remove_hcd ( hcd ) ;
usb_put_hcd ( hcd ) ;
return 0 ;
}
static const struct of_device_id npcm7xx_ehci_id_table [ ] = {
{ . compatible = " nuvoton,npcm750-ehci " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , npcm7xx_ehci_id_table ) ;
static struct platform_driver npcm7xx_ehci_hcd_driver = {
. probe = npcm7xx_ehci_hcd_drv_probe ,
. remove = npcm7xx_ehci_hcd_drv_remove ,
. shutdown = usb_hcd_platform_shutdown ,
. driver = {
. name = " npcm7xx-ehci " ,
. bus = & platform_bus_type ,
2020-09-03 13:25:37 +02:00
. pm = pm_ptr ( & ehci_npcm7xx_pm_ops ) ,
2018-06-20 09:33:04 +03:00
. of_match_table = npcm7xx_ehci_id_table ,
}
} ;
static int __init ehci_npcm7xx_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( " %s: " DRIVER_DESC " \n " , hcd_name ) ;
ehci_init_driver ( & ehci_npcm7xx_hc_driver , NULL ) ;
return platform_driver_register ( & npcm7xx_ehci_hcd_driver ) ;
}
module_init ( ehci_npcm7xx_init ) ;
static void __exit ehci_npcm7xx_cleanup ( void )
{
platform_driver_unregister ( & npcm7xx_ehci_hcd_driver ) ;
}
module_exit ( ehci_npcm7xx_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_ALIAS ( " platform:npcm7xx-ehci " ) ;
MODULE_AUTHOR ( " Avi Fishman " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;