2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2020-07-02 15:46:01 +01:00
/*
2015-05-13 15:26:51 +03:00
* ulpi . c - DesignWare USB3 Controller ' s ULPI PHY interface
*
* Copyright ( C ) 2015 Intel Corporation
*
* Author : Heikki Krogerus < heikki . krogerus @ linux . intel . com >
*/
2020-12-10 11:50:07 +03:00
# include <linux/delay.h>
# include <linux/time64.h>
2015-05-13 15:26:51 +03:00
# include <linux/ulpi/regs.h>
# include "core.h"
# include "io.h"
# define DWC3_ULPI_ADDR(a) \
( ( a > = ULPI_EXT_VENDOR_SPECIFIC ) ? \
DWC3_GUSB2PHYACC_ADDR ( ULPI_ACCESS_EXTENDED ) | \
DWC3_GUSB2PHYACC_EXTEND_ADDR ( a ) : DWC3_GUSB2PHYACC_ADDR ( a ) )
2020-12-10 11:50:07 +03:00
# define DWC3_ULPI_BASE_DELAY DIV_ROUND_UP(NSEC_PER_SEC, 60000000L)
static int dwc3_ulpi_busyloop ( struct dwc3 * dwc , u8 addr , bool read )
2015-05-13 15:26:51 +03:00
{
2020-12-10 11:50:07 +03:00
unsigned long ns = 5L * DWC3_ULPI_BASE_DELAY ;
2020-12-10 11:50:08 +03:00
unsigned int count = 10000 ;
2015-05-13 15:26:51 +03:00
u32 reg ;
2020-12-10 11:50:07 +03:00
if ( addr > = ULPI_EXT_VENDOR_SPECIFIC )
ns + = DWC3_ULPI_BASE_DELAY ;
if ( read )
ns + = DWC3_ULPI_BASE_DELAY ;
2020-12-10 11:50:08 +03:00
reg = dwc3_readl ( dwc - > regs , DWC3_GUSB2PHYCFG ( 0 ) ) ;
if ( reg & DWC3_GUSB2PHYCFG_SUSPHY )
usleep_range ( 1000 , 1200 ) ;
2015-05-13 15:26:51 +03:00
while ( count - - ) {
2020-12-10 11:50:07 +03:00
ndelay ( ns ) ;
2015-05-13 15:26:51 +03:00
reg = dwc3_readl ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) ) ;
2020-12-10 11:50:06 +03:00
if ( reg & DWC3_GUSB2PHYACC_DONE )
2015-05-13 15:26:51 +03:00
return 0 ;
cpu_relax ( ) ;
}
return - ETIMEDOUT ;
}
2016-08-16 19:04:48 +03:00
static int dwc3_ulpi_read ( struct device * dev , u8 addr )
2015-05-13 15:26:51 +03:00
{
2016-08-16 19:04:48 +03:00
struct dwc3 * dwc = dev_get_drvdata ( dev ) ;
2015-05-13 15:26:51 +03:00
u32 reg ;
int ret ;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR ( addr ) ;
dwc3_writel ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) , reg ) ;
2020-12-10 11:50:07 +03:00
ret = dwc3_ulpi_busyloop ( dwc , addr , true ) ;
2015-05-13 15:26:51 +03:00
if ( ret )
return ret ;
reg = dwc3_readl ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) ) ;
return DWC3_GUSB2PHYACC_DATA ( reg ) ;
}
2016-08-16 19:04:48 +03:00
static int dwc3_ulpi_write ( struct device * dev , u8 addr , u8 val )
2015-05-13 15:26:51 +03:00
{
2016-08-16 19:04:48 +03:00
struct dwc3 * dwc = dev_get_drvdata ( dev ) ;
2015-05-13 15:26:51 +03:00
u32 reg ;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR ( addr ) ;
reg | = DWC3_GUSB2PHYACC_WRITE | val ;
dwc3_writel ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) , reg ) ;
2020-12-10 11:50:07 +03:00
return dwc3_ulpi_busyloop ( dwc , addr , false ) ;
2015-05-13 15:26:51 +03:00
}
2016-08-16 19:04:53 +03:00
static const struct ulpi_ops dwc3_ulpi_ops = {
2016-08-16 19:04:50 +03:00
. read = dwc3_ulpi_read ,
. write = dwc3_ulpi_write ,
2015-05-13 15:26:51 +03:00
} ;
int dwc3_ulpi_init ( struct dwc3 * dwc )
{
/* Register the interface */
dwc - > ulpi = ulpi_register_interface ( dwc - > dev , & dwc3_ulpi_ops ) ;
if ( IS_ERR ( dwc - > ulpi ) ) {
dev_err ( dwc - > dev , " failed to register ULPI interface " ) ;
return PTR_ERR ( dwc - > ulpi ) ;
}
return 0 ;
}
void dwc3_ulpi_exit ( struct dwc3 * dwc )
{
if ( dwc - > ulpi ) {
ulpi_unregister_interface ( dwc - > ulpi ) ;
dwc - > ulpi = NULL ;
}
}