2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2020-07-02 17:46:01 +03: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 >
*/
# 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 ) )
static int dwc3_ulpi_busyloop ( struct dwc3 * dwc )
{
2020-08-13 08:30:38 +03:00
unsigned int count = 1000 ;
2015-05-13 15:26:51 +03:00
u32 reg ;
while ( count - - ) {
reg = dwc3_readl ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) ) ;
if ( ! ( reg & DWC3_GUSB2PHYACC_BUSY ) )
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 ;
2017-06-05 17:01:22 +03:00
reg = dwc3_readl ( dwc - > regs , DWC3_GUSB2PHYCFG ( 0 ) ) ;
if ( reg & DWC3_GUSB2PHYCFG_SUSPHY ) {
reg & = ~ DWC3_GUSB2PHYCFG_SUSPHY ;
dwc3_writel ( dwc - > regs , DWC3_GUSB2PHYCFG ( 0 ) , reg ) ;
}
2015-05-13 15:26:51 +03:00
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR ( addr ) ;
dwc3_writel ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) , reg ) ;
ret = dwc3_ulpi_busyloop ( dwc ) ;
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 ;
2017-06-05 17:01:22 +03:00
reg = dwc3_readl ( dwc - > regs , DWC3_GUSB2PHYCFG ( 0 ) ) ;
if ( reg & DWC3_GUSB2PHYCFG_SUSPHY ) {
reg & = ~ DWC3_GUSB2PHYCFG_SUSPHY ;
dwc3_writel ( dwc - > regs , DWC3_GUSB2PHYCFG ( 0 ) , reg ) ;
}
2015-05-13 15:26:51 +03:00
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR ( addr ) ;
reg | = DWC3_GUSB2PHYACC_WRITE | val ;
dwc3_writel ( dwc - > regs , DWC3_GUSB2PHYACC ( 0 ) , reg ) ;
return dwc3_ulpi_busyloop ( dwc ) ;
}
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 ;
}
}