2019-08-26 14:19:30 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
2020-12-07 13:32:21 +03:00
* Cadence USBSS and USBSSP DRD Driver - host side
2019-08-26 14:19:30 +03:00
*
* Copyright ( C ) 2018 - 2019 Cadence Design Systems .
* Copyright ( C ) 2017 - 2018 NXP
*
* Authors : Peter Chen < peter . chen @ nxp . com >
* Pawel Laszczak < pawell @ cadence . com >
*/
# include <linux/platform_device.h>
2021-12-02 23:34:00 +03:00
# include <linux/slab.h>
2019-08-26 14:19:30 +03:00
# include "core.h"
# include "drd.h"
2019-10-17 15:44:27 +03:00
# include "host-export.h"
2020-09-02 12:57:32 +03:00
# include <linux/usb/hcd.h>
2020-05-22 12:56:30 +03:00
# include "../host/xhci.h"
# include "../host/xhci-plat.h"
# define XECP_PORT_CAP_REG 0x8000
# define XECP_AUX_CTRL_REG1 0x8120
# define CFG_RXDET_P3_EN BIT(15)
# define LPM_2_STB_SWITCH_EN BIT(25)
2020-12-11 08:09:54 +03:00
static int xhci_cdns3_suspend_quirk ( struct usb_hcd * hcd ) ;
2020-05-22 12:56:30 +03:00
static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
2020-07-27 12:53:37 +03:00
. quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI ,
2020-05-22 12:56:30 +03:00
. suspend_quirk = xhci_cdns3_suspend_quirk ,
} ;
2019-08-26 14:19:30 +03:00
2020-12-07 13:32:21 +03:00
static int __cdns_host_init ( struct cdns * cdns )
2019-08-26 14:19:30 +03:00
{
struct platform_device * xhci ;
int ret ;
2020-09-02 12:57:32 +03:00
struct usb_hcd * hcd ;
2019-08-26 14:19:30 +03:00
2020-12-07 13:32:21 +03:00
cdns_drd_host_on ( cdns ) ;
2019-08-26 14:19:30 +03:00
xhci = platform_device_alloc ( " xhci-hcd " , PLATFORM_DEVID_AUTO ) ;
if ( ! xhci ) {
dev_err ( cdns - > dev , " couldn't allocate xHCI device \n " ) ;
return - ENOMEM ;
}
xhci - > dev . parent = cdns - > dev ;
cdns - > host_dev = xhci ;
ret = platform_device_add_resources ( xhci , cdns - > xhci_res ,
2020-12-07 13:32:21 +03:00
CDNS_XHCI_RESOURCES_NUM ) ;
2019-08-26 14:19:30 +03:00
if ( ret ) {
dev_err ( cdns - > dev , " couldn't add resources to xHCI device \n " ) ;
goto err1 ;
}
2020-09-28 10:20:03 +03:00
cdns - > xhci_plat_data = kmemdup ( & xhci_plat_cdns3_xhci ,
sizeof ( struct xhci_plat_priv ) , GFP_KERNEL ) ;
if ( ! cdns - > xhci_plat_data ) {
ret = - ENOMEM ;
goto err1 ;
}
2020-11-23 13:49:31 +03:00
if ( cdns - > pdata & & ( cdns - > pdata - > quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW ) )
2020-09-28 10:20:03 +03:00
cdns - > xhci_plat_data - > quirks | = XHCI_DEFAULT_PM_RUNTIME_ALLOW ;
ret = platform_device_add_data ( xhci , cdns - > xhci_plat_data ,
2020-05-22 12:56:30 +03:00
sizeof ( struct xhci_plat_priv ) ) ;
if ( ret )
2020-09-28 10:20:03 +03:00
goto free_memory ;
2020-05-22 12:56:30 +03:00
2019-08-26 14:19:30 +03:00
ret = platform_device_add ( xhci ) ;
if ( ret ) {
dev_err ( cdns - > dev , " failed to register xHCI device \n " ) ;
2020-09-28 10:20:03 +03:00
goto free_memory ;
2019-08-26 14:19:30 +03:00
}
2020-09-02 12:57:32 +03:00
/* Glue needs to access xHCI region register for Power management */
hcd = platform_get_drvdata ( xhci ) ;
if ( hcd )
cdns - > xhci_regs = hcd - > regs ;
2019-08-26 14:19:30 +03:00
return 0 ;
2020-09-28 10:20:03 +03:00
free_memory :
kfree ( cdns - > xhci_plat_data ) ;
2019-08-26 14:19:30 +03:00
err1 :
platform_device_put ( xhci ) ;
return ret ;
}
2020-12-11 08:09:54 +03:00
static int xhci_cdns3_suspend_quirk ( struct usb_hcd * hcd )
2020-05-22 12:56:30 +03:00
{
struct xhci_hcd * xhci = hcd_to_xhci ( hcd ) ;
u32 value ;
if ( pm_runtime_status_suspended ( hcd - > self . controller ) )
return 0 ;
/* set usbcmd.EU3S */
value = readl ( & xhci - > op_regs - > command ) ;
value | = CMD_PM_INDEX ;
writel ( value , & xhci - > op_regs - > command ) ;
if ( hcd - > regs ) {
value = readl ( hcd - > regs + XECP_AUX_CTRL_REG1 ) ;
value | = CFG_RXDET_P3_EN ;
writel ( value , hcd - > regs + XECP_AUX_CTRL_REG1 ) ;
value = readl ( hcd - > regs + XECP_PORT_CAP_REG ) ;
value | = LPM_2_STB_SWITCH_EN ;
writel ( value , hcd - > regs + XECP_PORT_CAP_REG ) ;
}
return 0 ;
}
2020-12-07 13:32:21 +03:00
static void cdns_host_exit ( struct cdns * cdns )
2019-08-26 14:19:30 +03:00
{
2020-09-28 10:20:03 +03:00
kfree ( cdns - > xhci_plat_data ) ;
2019-08-26 14:19:30 +03:00
platform_device_unregister ( cdns - > host_dev ) ;
cdns - > host_dev = NULL ;
2020-12-07 13:32:21 +03:00
cdns_drd_host_off ( cdns ) ;
2019-08-26 14:19:30 +03:00
}
2020-12-07 13:32:21 +03:00
int cdns_host_init ( struct cdns * cdns )
2019-08-26 14:19:30 +03:00
{
2020-12-07 13:32:21 +03:00
struct cdns_role_driver * rdrv ;
2019-08-26 14:19:30 +03:00
rdrv = devm_kzalloc ( cdns - > dev , sizeof ( * rdrv ) , GFP_KERNEL ) ;
if ( ! rdrv )
return - ENOMEM ;
2020-12-07 13:32:21 +03:00
rdrv - > start = __cdns_host_init ;
rdrv - > stop = cdns_host_exit ;
rdrv - > state = CDNS_ROLE_STATE_INACTIVE ;
2019-08-26 14:19:30 +03:00
rdrv - > name = " host " ;
cdns - > roles [ USB_ROLE_HOST ] = rdrv ;
return 0 ;
}