2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-01-07 12:18:13 +03:00
/*
* Renesas USB driver R - Car Gen . 3 initialization and power control
*
* Copyright ( C ) 2016 Renesas Electronics Corporation
*/
2016-10-20 07:19:19 +03:00
# include <linux/delay.h>
2016-01-07 12:18:13 +03:00
# include <linux/io.h>
# include "common.h"
# include "rcar3.h"
# define LPSTS 0x102
2017-10-03 14:09:14 +03:00
# define UGCTRL 0x180 /* 32-bit register */
2016-01-07 12:18:13 +03:00
# define UGCTRL2 0x184 /* 32-bit register */
2017-10-03 14:09:14 +03:00
# define UGSTS 0x188 /* 32-bit register */
2016-01-07 12:18:13 +03:00
/* Low Power Status register (LPSTS) */
# define LPSTS_SUSPM 0x4000
2017-10-03 14:09:14 +03:00
/* R-Car D3 only: USB General control register (UGCTRL) */
# define UGCTRL_PLLRESET 0x00000001
# define UGCTRL_CONNECT 0x00000004
2017-08-02 07:21:45 +03:00
/*
* USB General control register 2 ( UGCTRL2 )
* Remarks : bit [ 31 : 11 ] and bit [ 9 : 6 ] should be 0
*/
2016-01-07 12:18:13 +03:00
# define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */
2017-12-13 09:46:59 +03:00
# define UGCTRL2_USB0SEL_EHCI 0x00000010
2017-10-03 14:09:14 +03:00
# define UGCTRL2_USB0SEL_HSUSB 0x00000020
2016-01-07 12:18:13 +03:00
# define UGCTRL2_USB0SEL_OTG 0x00000030
2017-08-02 07:21:45 +03:00
# define UGCTRL2_VBUSSEL 0x00000400
2016-01-07 12:18:13 +03:00
2017-10-03 14:09:14 +03:00
/* R-Car D3 only: USB General status register (UGSTS) */
# define UGSTS_LOCK 0x00000100
2016-06-21 20:52:54 +03:00
static void usbhs_write32 ( struct usbhs_priv * priv , u32 reg , u32 data )
2016-01-07 12:18:13 +03:00
{
iowrite32 ( data , priv - > base + reg ) ;
}
2017-10-03 14:09:14 +03:00
static u32 usbhs_read32 ( struct usbhs_priv * priv , u32 reg )
{
return ioread32 ( priv - > base + reg ) ;
}
2017-12-13 09:46:57 +03:00
static void usbhs_rcar3_set_ugctrl2 ( struct usbhs_priv * priv , u32 val )
{
usbhs_write32 ( priv , UGCTRL2 , val | UGCTRL2_RESERVED_3 ) ;
}
2017-12-13 09:46:59 +03:00
static void usbhs_rcar3_set_usbsel ( struct usbhs_priv * priv , bool ehci )
{
if ( ehci )
usbhs_rcar3_set_ugctrl2 ( priv , UGCTRL2_USB0SEL_EHCI ) ;
else
usbhs_rcar3_set_ugctrl2 ( priv , UGCTRL2_USB0SEL_HSUSB ) ;
}
2016-01-07 12:18:13 +03:00
static int usbhs_rcar3_power_ctrl ( struct platform_device * pdev ,
void __iomem * base , int enable )
{
struct usbhs_priv * priv = usbhs_pdev_to_priv ( pdev ) ;
2017-12-13 09:46:57 +03:00
usbhs_rcar3_set_ugctrl2 ( priv , UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL ) ;
2016-01-07 12:18:13 +03:00
2016-10-20 07:19:19 +03:00
if ( enable ) {
2016-01-07 12:18:13 +03:00
usbhs_bset ( priv , LPSTS , LPSTS_SUSPM , LPSTS_SUSPM ) ;
2016-10-20 07:19:19 +03:00
/* The controller on R-Car Gen3 needs to wait up to 45 usec */
udelay ( 45 ) ;
} else {
2016-01-07 12:18:13 +03:00
usbhs_bset ( priv , LPSTS , LPSTS_SUSPM , 0 ) ;
2016-10-20 07:19:19 +03:00
}
2016-01-07 12:18:13 +03:00
return 0 ;
}
2017-10-03 14:09:14 +03:00
/* R-Car D3 needs to release UGCTRL.PLLRESET */
static int usbhs_rcar3_power_and_pll_ctrl ( struct platform_device * pdev ,
void __iomem * base , int enable )
{
struct usbhs_priv * priv = usbhs_pdev_to_priv ( pdev ) ;
u32 val ;
int timeout = 1000 ;
2017-12-13 09:46:59 +03:00
bool is_host = false ;
2017-10-03 14:09:14 +03:00
if ( enable ) {
usbhs_write32 ( priv , UGCTRL , 0 ) ; /* release PLLRESET */
2017-12-13 09:46:59 +03:00
if ( priv - > edev )
is_host = extcon_get_state ( priv - > edev , EXTCON_USB_HOST ) ;
usbhs_rcar3_set_usbsel ( priv , is_host ) ;
2017-10-03 14:09:14 +03:00
usbhs_bset ( priv , LPSTS , LPSTS_SUSPM , LPSTS_SUSPM ) ;
do {
val = usbhs_read32 ( priv , UGSTS ) ;
udelay ( 1 ) ;
} while ( ! ( val & UGSTS_LOCK ) & & timeout - - ) ;
usbhs_write32 ( priv , UGCTRL , UGCTRL_CONNECT ) ;
} else {
usbhs_write32 ( priv , UGCTRL , 0 ) ;
usbhs_bset ( priv , LPSTS , LPSTS_SUSPM , 0 ) ;
usbhs_write32 ( priv , UGCTRL , UGCTRL_PLLRESET ) ;
}
return 0 ;
}
2016-01-07 12:18:13 +03:00
static int usbhs_rcar3_get_id ( struct platform_device * pdev )
{
return USBHS_GADGET ;
}
2017-12-13 09:47:00 +03:00
static int usbhs_rcar3_notifier ( struct notifier_block * nb , unsigned long event ,
void * data )
{
struct usbhs_priv * priv = container_of ( nb , struct usbhs_priv , nb ) ;
usbhs_rcar3_set_usbsel ( priv , ! ! event ) ;
return NOTIFY_DONE ;
}
2016-01-07 12:18:13 +03:00
const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
. power_ctrl = usbhs_rcar3_power_ctrl ,
. get_id = usbhs_rcar3_get_id ,
} ;
2017-10-03 14:09:14 +03:00
const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = {
. power_ctrl = usbhs_rcar3_power_and_pll_ctrl ,
. get_id = usbhs_rcar3_get_id ,
2017-12-13 09:47:00 +03:00
. notifier = usbhs_rcar3_notifier ,
2017-10-03 14:09:14 +03:00
} ;