2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-1.0+
2011-04-04 13:44:59 +09:00
/*
* Renesas USB driver
*
* Copyright ( C ) 2011 Renesas Solutions Corp .
* Kuninori Morimoto < kuninori . morimoto . gx @ renesas . com >
*/
2018-09-11 17:47:05 +09:00
# include <linux/clk.h>
2013-01-21 11:09:22 +01:00
# include <linux/err.h>
2014-07-10 09:53:59 +02:00
# include <linux/gpio.h>
2011-04-04 13:44:59 +09:00
# include <linux/io.h>
# include <linux/module.h>
2014-09-03 14:25:56 +09:00
# include <linux/of_device.h>
# include <linux/of_gpio.h>
2011-04-04 13:44:59 +09:00
# include <linux/pm_runtime.h>
2018-09-11 17:47:03 +09:00
# include <linux/reset.h>
2011-04-04 13:44:59 +09:00
# include <linux/slab.h>
# include <linux/sysfs.h>
2012-06-03 18:39:13 +02:00
# include "common.h"
2014-07-10 09:53:59 +02:00
# include "rcar2.h"
2016-01-07 18:18:13 +09:00
# include "rcar3.h"
2018-01-08 07:30:53 -05:00
# include "rza.h"
2011-04-04 13:44:59 +09:00
2011-07-07 00:23:24 -07:00
/*
* image of renesas_usbhs
*
* ex ) gadget case
* mod . c
* mod_gadget . c
* mod_host . c pipe . c fifo . c
*
* + - - - - - - - + + - - - - - - - - - - - +
* | pipe0 | - - - - - - > | fifo pio |
* + - - - - - - - - - - - - + + - - - - - - - + + - - - - - - - - - - - +
* | mod_gadget | = = = = = > | pipe1 | - - +
* + - - - - - - - - - - - - + + - - - - - - - + | + - - - - - - - - - - - +
* | pipe2 | | + - | fifo dma0 |
* + - - - - - - - - - - - - + + - - - - - - - + | | + - - - - - - - - - - - +
* | mod_host | | pipe3 | < - | - - +
* + - - - - - - - - - - - - + + - - - - - - - + | + - - - - - - - - - - - +
* | . . . . | + - - - > | fifo dma1 |
* | . . . . | + - - - - - - - - - - - +
*/
2011-04-28 16:41:20 +09:00
# define USBHSF_RUNTIME_PWCTRL (1 << 0)
/* status */
# define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0)
# define usbhsc_flags_set(p, b) ((p)->flags |= (b))
# define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b))
# define usbhsc_flags_has(p, b) ((p)->flags & (b))
2011-04-04 13:44:59 +09:00
/*
* platform call back
*
* renesas usb support platform callback function .
* Below macro call it .
* if platform doesn ' t have callback , it return 0 ( no error )
*/
# define usbhs_platform_call(priv, func, args...)\
( ! ( priv ) ? - ENODEV : \
2011-10-12 21:02:22 -07:00
! ( ( priv ) - > pfunc . func ) ? 0 : \
( priv ) - > pfunc . func ( args ) )
2011-04-04 13:44:59 +09:00
/*
* common functions
*/
u16 usbhs_read ( struct usbhs_priv * priv , u32 reg )
{
return ioread16 ( priv - > base + reg ) ;
}
void usbhs_write ( struct usbhs_priv * priv , u32 reg , u16 data )
{
iowrite16 ( data , priv - > base + reg ) ;
}
void usbhs_bset ( struct usbhs_priv * priv , u32 reg , u16 mask , u16 data )
{
u16 val = usbhs_read ( priv , reg ) ;
val & = ~ mask ;
val | = data & mask ;
usbhs_write ( priv , reg , val ) ;
}
2011-04-28 16:40:54 +09:00
struct usbhs_priv * usbhs_pdev_to_priv ( struct platform_device * pdev )
{
return dev_get_drvdata ( & pdev - > dev ) ;
}
2011-04-04 13:44:59 +09:00
/*
* syscfg functions
*/
2011-10-23 19:56:41 -07:00
static void usbhs_sys_clock_ctrl ( struct usbhs_priv * priv , int enable )
2011-04-04 13:44:59 +09:00
{
usbhs_bset ( priv , SYSCFG , SCKE , enable ? SCKE : 0 ) ;
}
void usbhs_sys_host_ctrl ( struct usbhs_priv * priv , int enable )
{
2011-10-23 19:56:30 -07:00
u16 mask = DCFM | DRPD | DPRPU | HSE | USBE ;
u16 val = DCFM | DRPD | HSE | USBE ;
2011-10-10 22:06:12 -07:00
int has_otg = usbhs_get_dparam ( priv , has_otg ) ;
if ( has_otg )
usbhs_bset ( priv , DVSTCTR , ( EXTLP | PWEN ) , ( EXTLP | PWEN ) ) ;
2011-04-04 13:44:59 +09:00
/*
* if enable
*
* - select Host mode
* - D + Line / D - Line Pull - down
*/
usbhs_bset ( priv , SYSCFG , mask , enable ? val : 0 ) ;
}
void usbhs_sys_function_ctrl ( struct usbhs_priv * priv , int enable )
{
2011-10-23 19:56:30 -07:00
u16 mask = DCFM | DRPD | DPRPU | HSE | USBE ;
2014-11-04 10:05:43 +09:00
u16 val = HSE | USBE ;
2011-04-04 13:44:59 +09:00
/*
* if enable
*
* - select Function mode
2014-11-04 10:05:43 +09:00
* - D + Line Pull - up is disabled
* When D + Line Pull - up is enabled ,
* calling usbhs_sys_function_pullup ( , 1 )
2011-04-04 13:44:59 +09:00
*/
usbhs_bset ( priv , SYSCFG , mask , enable ? val : 0 ) ;
}
2012-10-15 23:24:19 -07:00
void usbhs_sys_function_pullup ( struct usbhs_priv * priv , int enable )
{
usbhs_bset ( priv , SYSCFG , DPRPU , enable ? DPRPU : 0 ) ;
}
2011-11-24 17:28:35 -08:00
void usbhs_sys_set_test_mode ( struct usbhs_priv * priv , u16 mode )
{
usbhs_write ( priv , TESTMODE , mode ) ;
}
2011-04-04 13:44:59 +09:00
/*
* frame functions
*/
int usbhs_frame_get_num ( struct usbhs_priv * priv )
{
return usbhs_read ( priv , FRMNUM ) & FRNM_MASK ;
}
2011-10-10 22:02:33 -07:00
/*
* usb request functions
*/
void usbhs_usbreq_get_val ( struct usbhs_priv * priv , struct usb_ctrlrequest * req )
{
u16 val ;
val = usbhs_read ( priv , USBREQ ) ;
req - > bRequest = ( val > > 8 ) & 0xFF ;
req - > bRequestType = ( val > > 0 ) & 0xFF ;
req - > wValue = usbhs_read ( priv , USBVAL ) ;
req - > wIndex = usbhs_read ( priv , USBINDX ) ;
req - > wLength = usbhs_read ( priv , USBLENG ) ;
}
void usbhs_usbreq_set_val ( struct usbhs_priv * priv , struct usb_ctrlrequest * req )
{
usbhs_write ( priv , USBREQ , ( req - > bRequest < < 8 ) | req - > bRequestType ) ;
usbhs_write ( priv , USBVAL , req - > wValue ) ;
usbhs_write ( priv , USBINDX , req - > wIndex ) ;
usbhs_write ( priv , USBLENG , req - > wLength ) ;
usbhs_bset ( priv , DCPCTR , SUREQ , SUREQ ) ;
}
2011-10-10 22:01:40 -07:00
/*
* bus / vbus functions
*/
void usbhs_bus_send_sof_enable ( struct usbhs_priv * priv )
{
2011-10-10 22:06:35 -07:00
u16 status = usbhs_read ( priv , DVSTCTR ) & ( USBRST | UACT ) ;
if ( status ! = USBRST ) {
struct device * dev = usbhs_priv_to_dev ( priv ) ;
dev_err ( dev , " usbhs should be reset \n " ) ;
}
2011-10-10 22:01:40 -07:00
usbhs_bset ( priv , DVSTCTR , ( USBRST | UACT ) , UACT ) ;
}
void usbhs_bus_send_reset ( struct usbhs_priv * priv )
{
usbhs_bset ( priv , DVSTCTR , ( USBRST | UACT ) , USBRST ) ;
}
2011-10-10 22:01:51 -07:00
int usbhs_bus_get_speed ( struct usbhs_priv * priv )
{
u16 dvstctr = usbhs_read ( priv , DVSTCTR ) ;
switch ( RHST & dvstctr ) {
case RHST_LOW_SPEED :
return USB_SPEED_LOW ;
case RHST_FULL_SPEED :
return USB_SPEED_FULL ;
case RHST_HIGH_SPEED :
return USB_SPEED_HIGH ;
}
return USB_SPEED_UNKNOWN ;
}
2011-10-10 22:01:40 -07:00
int usbhs_vbus_ctrl ( struct usbhs_priv * priv , int enable )
{
struct platform_device * pdev = usbhs_priv_to_pdev ( priv ) ;
return usbhs_platform_call ( priv , set_vbus , pdev , enable ) ;
}
static void usbhsc_bus_init ( struct usbhs_priv * priv )
{
usbhs_write ( priv , DVSTCTR , 0 ) ;
usbhs_vbus_ctrl ( priv , 0 ) ;
}
2011-10-10 22:06:46 -07:00
/*
* device configuration
*/
2011-10-31 00:47:13 -07:00
int usbhs_set_device_config ( struct usbhs_priv * priv , int devnum ,
2011-10-10 22:06:46 -07:00
u16 upphub , u16 hubport , u16 speed )
{
struct device * dev = usbhs_priv_to_dev ( priv ) ;
u16 usbspd = 0 ;
u32 reg = DEVADD0 + ( 2 * devnum ) ;
if ( devnum > 10 ) {
dev_err ( dev , " cannot set speed to unknown device %d \n " , devnum ) ;
return - EIO ;
}
if ( upphub > 0xA ) {
dev_err ( dev , " unsupported hub number %d \n " , upphub ) ;
return - EIO ;
}
switch ( speed ) {
case USB_SPEED_LOW :
usbspd = USBSPD_SPEED_LOW ;
break ;
case USB_SPEED_FULL :
usbspd = USBSPD_SPEED_FULL ;
break ;
case USB_SPEED_HIGH :
usbspd = USBSPD_SPEED_HIGH ;
break ;
default :
dev_err ( dev , " unsupported speed %d \n " , speed ) ;
return - EIO ;
}
usbhs_write ( priv , reg , UPPHUB ( upphub ) |
HUBPORT ( hubport ) |
USBSPD ( usbspd ) ) ;
return 0 ;
}
2015-03-12 15:35:20 +09:00
/*
* interrupt functions
*/
void usbhs_xxxsts_clear ( struct usbhs_priv * priv , u16 sts_reg , u16 bit )
{
u16 pipe_mask = ( u16 ) GENMASK ( usbhs_get_dparam ( priv , pipe_size ) , 0 ) ;
usbhs_write ( priv , sts_reg , ~ ( 1 < < bit ) & pipe_mask ) ;
}
2011-04-04 13:44:59 +09:00
/*
* local functions
*/
2011-10-10 22:01:28 -07:00
static void usbhsc_set_buswait ( struct usbhs_priv * priv )
2011-04-04 13:44:59 +09:00
{
int wait = usbhs_get_dparam ( priv , buswait_bwait ) ;
2011-10-10 22:01:28 -07:00
/* set bus wait if platform have */
if ( wait )
usbhs_bset ( priv , BUSWAIT , 0x000F , wait ) ;
2011-04-04 13:44:59 +09:00
}
2018-09-11 17:47:05 +09:00
static bool usbhsc_is_multi_clks ( struct usbhs_priv * priv )
{
if ( priv - > dparam . type = = USBHS_TYPE_RCAR_GEN3 | |
priv - > dparam . type = = USBHS_TYPE_RCAR_GEN3_WITH_PLL )
return true ;
return false ;
}
static int usbhsc_clk_get ( struct device * dev , struct usbhs_priv * priv )
{
if ( ! usbhsc_is_multi_clks ( priv ) )
return 0 ;
/* The first clock should exist */
priv - > clks [ 0 ] = of_clk_get ( dev - > of_node , 0 ) ;
if ( IS_ERR ( priv - > clks [ 0 ] ) )
return PTR_ERR ( priv - > clks [ 0 ] ) ;
/*
* To backward compatibility with old DT , this driver checks the return
* value if it ' s - ENOENT or not .
*/
priv - > clks [ 1 ] = of_clk_get ( dev - > of_node , 1 ) ;
if ( PTR_ERR ( priv - > clks [ 1 ] ) = = - ENOENT )
priv - > clks [ 1 ] = NULL ;
else if ( IS_ERR ( priv - > clks [ 1 ] ) )
return PTR_ERR ( priv - > clks [ 1 ] ) ;
return 0 ;
}
static void usbhsc_clk_put ( struct usbhs_priv * priv )
{
int i ;
if ( ! usbhsc_is_multi_clks ( priv ) )
return ;
for ( i = 0 ; i < ARRAY_SIZE ( priv - > clks ) ; i + + )
clk_put ( priv - > clks [ i ] ) ;
}
static int usbhsc_clk_prepare_enable ( struct usbhs_priv * priv )
{
int i , ret ;
if ( ! usbhsc_is_multi_clks ( priv ) )
return 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( priv - > clks ) ; i + + ) {
ret = clk_prepare_enable ( priv - > clks [ i ] ) ;
if ( ret ) {
while ( - - i > = 0 )
clk_disable_unprepare ( priv - > clks [ i ] ) ;
return ret ;
}
}
return ret ;
}
static void usbhsc_clk_disable_unprepare ( struct usbhs_priv * priv )
{
int i ;
if ( ! usbhsc_is_multi_clks ( priv ) )
return ;
for ( i = 0 ; i < ARRAY_SIZE ( priv - > clks ) ; i + + )
clk_disable_unprepare ( priv - > clks [ i ] ) ;
}
2011-04-04 13:44:59 +09:00
/*
* platform default param
*/
2014-07-10 09:53:59 +02:00
/* commonly used on old SH-Mobile SoCs */
2015-11-18 14:34:09 +09:00
static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe [ ] = {
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_CONTROL , 64 , 0x00 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_ISOC , 1024 , 0x08 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_ISOC , 1024 , 0x18 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x28 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x38 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x48 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x04 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x05 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x06 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x07 , false ) ,
2011-04-04 13:44:59 +09:00
} ;
2014-07-10 09:53:59 +02:00
/* commonly used on newer SH-Mobile and R-Car SoCs */
2015-11-18 14:34:09 +09:00
static struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe [ ] = {
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_CONTROL , 64 , 0x00 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_ISOC , 1024 , 0x08 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_ISOC , 1024 , 0x28 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x48 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x58 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x68 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x04 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x05 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_INT , 64 , 0x06 , false ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x78 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x88 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0x98 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0xa8 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0xb8 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0xc8 , true ) ,
RENESAS_USBHS_PIPE ( USB_ENDPOINT_XFER_BULK , 512 , 0xd8 , true ) ,
2014-07-10 09:53:59 +02:00
} ;
2011-04-04 13:44:59 +09:00
/*
2011-04-28 16:41:02 +09:00
* power control
*/
static void usbhsc_power_ctrl ( struct usbhs_priv * priv , int enable )
{
2011-10-23 19:57:10 -07:00
struct platform_device * pdev = usbhs_priv_to_pdev ( priv ) ;
2011-04-28 16:41:02 +09:00
struct device * dev = usbhs_priv_to_dev ( priv ) ;
if ( enable ) {
/* enable PM */
pm_runtime_get_sync ( dev ) ;
2018-09-11 17:47:05 +09:00
/* enable clks */
if ( usbhsc_clk_prepare_enable ( priv ) )
return ;
2011-10-23 19:57:10 -07:00
/* enable platform power */
usbhs_platform_call ( priv , power_ctrl , pdev , priv - > base , enable ) ;
2011-04-28 16:41:02 +09:00
/* USB on */
usbhs_sys_clock_ctrl ( priv , enable ) ;
} else {
/* USB off */
usbhs_sys_clock_ctrl ( priv , enable ) ;
2011-10-23 19:57:10 -07:00
/* disable platform power */
usbhs_platform_call ( priv , power_ctrl , pdev , priv - > base , enable ) ;
2018-09-11 17:47:05 +09:00
/* disable clks */
usbhsc_clk_disable_unprepare ( priv ) ;
2011-04-28 16:41:02 +09:00
/* disable PM */
pm_runtime_put_sync ( dev ) ;
}
}
/*
2011-10-10 21:58:19 -07:00
* hotplug
2011-04-04 13:44:59 +09:00
*/
2011-10-10 21:58:19 -07:00
static void usbhsc_hotplug ( struct usbhs_priv * priv )
2011-04-04 13:44:59 +09:00
{
struct platform_device * pdev = usbhs_priv_to_pdev ( priv ) ;
struct usbhs_mod * mod = usbhs_mod_get_current ( priv ) ;
int id ;
int enable ;
2014-12-16 01:42:13 +03:00
int cable ;
2011-04-04 13:44:59 +09:00
int ret ;
/*
* get vbus status from platform
*/
enable = usbhs_platform_call ( priv , get_vbus , pdev ) ;
/*
* get id from platform
*/
id = usbhs_platform_call ( priv , get_id , pdev ) ;
if ( enable & & ! mod ) {
2014-12-16 01:42:13 +03:00
if ( priv - > edev ) {
2017-01-16 21:37:02 +09:00
cable = extcon_get_state ( priv - > edev , EXTCON_USB_HOST ) ;
2014-12-16 01:42:13 +03:00
if ( ( cable > 0 & & id ! = USBHS_HOST ) | |
( ! cable & & id ! = USBHS_GADGET ) ) {
dev_info ( & pdev - > dev ,
" USB cable plugged in doesn't match the selected role! \n " ) ;
return ;
}
}
2011-04-04 13:44:59 +09:00
ret = usbhs_mod_change ( priv , id ) ;
if ( ret < 0 )
return ;
dev_dbg ( & pdev - > dev , " %s enable \n " , __func__ ) ;
2011-04-28 16:41:02 +09:00
/* power on */
2011-04-28 16:41:20 +09:00
if ( usbhsc_flags_has ( priv , USBHSF_RUNTIME_PWCTRL ) )
usbhsc_power_ctrl ( priv , enable ) ;
2011-04-04 13:44:59 +09:00
2011-10-10 22:01:40 -07:00
/* bus init */
usbhsc_set_buswait ( priv ) ;
usbhsc_bus_init ( priv ) ;
2011-04-04 13:44:59 +09:00
/* module start */
usbhs_mod_call ( priv , start , priv ) ;
} else if ( ! enable & & mod ) {
dev_dbg ( & pdev - > dev , " %s disable \n " , __func__ ) ;
/* module stop */
usbhs_mod_call ( priv , stop , priv ) ;
2011-10-10 22:01:40 -07:00
/* bus init */
usbhsc_bus_init ( priv ) ;
2011-04-28 16:41:02 +09:00
/* power off */
2011-04-28 16:41:20 +09:00
if ( usbhsc_flags_has ( priv , USBHSF_RUNTIME_PWCTRL ) )
usbhsc_power_ctrl ( priv , enable ) ;
2011-04-04 13:44:59 +09:00
usbhs_mod_change ( priv , - 1 ) ;
/* reset phy for next connection */
usbhs_platform_call ( priv , phy_reset , pdev ) ;
}
}
2011-10-10 21:58:19 -07:00
/*
* notify hotplug
*/
static void usbhsc_notify_hotplug ( struct work_struct * work )
{
struct usbhs_priv * priv = container_of ( work ,
struct usbhs_priv ,
notify_hotplug_work . work ) ;
usbhsc_hotplug ( priv ) ;
}
2011-10-24 02:25:48 -07:00
static int usbhsc_drvcllbck_notify_hotplug ( struct platform_device * pdev )
2011-04-04 13:44:59 +09:00
{
2011-04-28 16:40:54 +09:00
struct usbhs_priv * priv = usbhs_pdev_to_priv ( pdev ) ;
2011-04-28 16:41:14 +09:00
int delay = usbhs_get_dparam ( priv , detection_delay ) ;
2011-04-04 13:44:59 +09:00
/*
* This functions will be called in interrupt .
* To make sure safety context ,
* use workqueue for usbhs_notify_hotplug
*/
2011-10-23 19:57:02 -07:00
schedule_delayed_work ( & priv - > notify_hotplug_work ,
msecs_to_jiffies ( delay ) ) ;
2011-04-04 13:44:59 +09:00
return 0 ;
}
/*
* platform functions
*/
2014-09-03 14:25:56 +09:00
static const struct of_device_id usbhs_of_match [ ] = {
{
. compatible = " renesas,usbhs-r8a7790 " ,
2015-05-18 20:04:14 +09:00
. data = ( void * ) USBHS_TYPE_RCAR_GEN2 ,
2014-09-03 14:25:56 +09:00
} ,
{
. compatible = " renesas,usbhs-r8a7791 " ,
2015-05-18 20:04:14 +09:00
. data = ( void * ) USBHS_TYPE_RCAR_GEN2 ,
2014-09-03 14:25:56 +09:00
} ,
2015-05-18 20:04:15 +09:00
{
. compatible = " renesas,usbhs-r8a7794 " ,
. data = ( void * ) USBHS_TYPE_RCAR_GEN2 ,
} ,
2015-09-29 18:21:19 +09:00
{
. compatible = " renesas,usbhs-r8a7795 " ,
2016-01-07 18:18:13 +09:00
. data = ( void * ) USBHS_TYPE_RCAR_GEN3 ,
2015-09-29 18:21:19 +09:00
} ,
2016-08-24 16:39:53 +09:00
{
. compatible = " renesas,usbhs-r8a7796 " ,
. data = ( void * ) USBHS_TYPE_RCAR_GEN3 ,
} ,
2018-09-21 21:26:32 +09:00
{
. compatible = " renesas,usbhs-r8a77990 " ,
. data = ( void * ) USBHS_TYPE_RCAR_GEN3_WITH_PLL ,
} ,
2017-10-03 20:09:14 +09:00
{
. compatible = " renesas,usbhs-r8a77995 " ,
. data = ( void * ) USBHS_TYPE_RCAR_GEN3_WITH_PLL ,
} ,
2015-12-16 13:51:51 +09:00
{
. compatible = " renesas,rcar-gen2-usbhs " ,
. data = ( void * ) USBHS_TYPE_RCAR_GEN2 ,
} ,
{
. compatible = " renesas,rcar-gen3-usbhs " ,
2016-01-07 18:18:13 +09:00
. data = ( void * ) USBHS_TYPE_RCAR_GEN3 ,
2015-12-16 13:51:51 +09:00
} ,
2018-01-08 07:30:53 -05:00
{
. compatible = " renesas,rza1-usbhs " ,
. data = ( void * ) USBHS_TYPE_RZA1 ,
} ,
2014-09-03 14:25:56 +09:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , usbhs_of_match ) ;
static struct renesas_usbhs_platform_info * usbhs_parse_dt ( struct device * dev )
{
struct renesas_usbhs_platform_info * info ;
struct renesas_usbhs_driver_param * dparam ;
u32 tmp ;
int gpio ;
info = devm_kzalloc ( dev , sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
return NULL ;
dparam = & info - > driver_param ;
2017-10-04 14:26:48 +02:00
dparam - > type = ( uintptr_t ) of_device_get_match_data ( dev ) ;
2014-09-03 14:25:56 +09:00
if ( ! of_property_read_u32 ( dev - > of_node , " renesas,buswait " , & tmp ) )
dparam - > buswait_bwait = tmp ;
gpio = of_get_named_gpio_flags ( dev - > of_node , " renesas,enable-gpio " , 0 ,
NULL ) ;
if ( gpio > 0 )
dparam - > enable_gpio = gpio ;
2016-08-08 21:50:51 +09:00
if ( dparam - > type = = USBHS_TYPE_RCAR_GEN2 | |
2017-10-03 20:09:14 +09:00
dparam - > type = = USBHS_TYPE_RCAR_GEN3 | |
dparam - > type = = USBHS_TYPE_RCAR_GEN3_WITH_PLL ) {
2015-03-12 15:35:20 +09:00
dparam - > has_usb_dmac = 1 ;
2017-10-03 20:09:13 +09:00
dparam - > pipe_configs = usbhsc_new_pipe ;
dparam - > pipe_size = ARRAY_SIZE ( usbhsc_new_pipe ) ;
}
2015-03-12 15:35:20 +09:00
2018-01-08 07:30:53 -05:00
if ( dparam - > type = = USBHS_TYPE_RZA1 ) {
dparam - > pipe_configs = usbhsc_new_pipe ;
dparam - > pipe_size = ARRAY_SIZE ( usbhsc_new_pipe ) ;
}
2014-09-03 14:25:56 +09:00
return info ;
}
2011-10-26 19:33:49 -07:00
static int usbhs_probe ( struct platform_device * pdev )
2011-04-04 13:44:59 +09:00
{
2017-11-07 08:26:07 +00:00
struct renesas_usbhs_platform_info * info = renesas_usbhs_get_info ( pdev ) ;
2011-04-04 13:44:59 +09:00
struct renesas_usbhs_driver_callback * dfunc ;
struct usbhs_priv * priv ;
2012-01-05 15:37:18 +09:00
struct resource * res , * irq_res ;
2011-04-04 13:44:59 +09:00
int ret ;
2014-09-03 14:25:56 +09:00
/* check device node */
if ( pdev - > dev . of_node )
info = pdev - > dev . platform_data = usbhs_parse_dt ( & pdev - > dev ) ;
2011-04-04 13:44:59 +09:00
/* check platform information */
2014-07-10 09:53:59 +02:00
if ( ! info ) {
2011-04-04 13:44:59 +09:00
dev_err ( & pdev - > dev , " no platform information \n " ) ;
return - EINVAL ;
}
/* platform data */
2012-01-05 15:37:18 +09:00
irq_res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
2014-10-29 21:30:16 +05:30
if ( ! irq_res ) {
2011-04-04 13:44:59 +09:00
dev_err ( & pdev - > dev , " Not enough Renesas USB platform resources. \n " ) ;
return - ENODEV ;
}
/* usb private data */
2012-09-10 19:01:07 -07:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2014-10-14 15:56:19 +08:00
if ( ! priv )
2011-04-04 13:44:59 +09:00
return - ENOMEM ;
2014-10-29 21:30:16 +05:30
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 11:09:22 +01:00
priv - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
2011-04-04 13:44:59 +09:00
2014-12-16 01:42:13 +03:00
if ( of_property_read_bool ( pdev - > dev . of_node , " extcon " ) ) {
priv - > edev = extcon_get_edev_by_phandle ( & pdev - > dev , 0 ) ;
if ( IS_ERR ( priv - > edev ) )
return PTR_ERR ( priv - > edev ) ;
}
2018-09-11 17:47:03 +09:00
priv - > rsts = devm_reset_control_array_get_optional_shared ( & pdev - > dev ) ;
if ( IS_ERR ( priv - > rsts ) )
return PTR_ERR ( priv - > rsts ) ;
2011-04-04 13:44:59 +09:00
/*
* care platform info
*/
2014-07-10 09:53:59 +02:00
2011-10-12 21:02:22 -07:00
memcpy ( & priv - > dparam ,
& info - > driver_param ,
sizeof ( struct renesas_usbhs_driver_param ) ) ;
2011-04-04 13:44:59 +09:00
2014-07-10 09:53:59 +02:00
switch ( priv - > dparam . type ) {
2015-05-18 20:04:14 +09:00
case USBHS_TYPE_RCAR_GEN2 :
2014-07-10 09:53:59 +02:00
priv - > pfunc = usbhs_rcar2_ops ;
2016-01-07 18:18:13 +09:00
break ;
case USBHS_TYPE_RCAR_GEN3 :
priv - > pfunc = usbhs_rcar3_ops ;
2014-07-10 09:53:59 +02:00
break ;
2017-10-03 20:09:14 +09:00
case USBHS_TYPE_RCAR_GEN3_WITH_PLL :
priv - > pfunc = usbhs_rcar3_with_pll_ops ;
break ;
2018-01-08 07:30:53 -05:00
case USBHS_TYPE_RZA1 :
priv - > pfunc = usbhs_rza1_ops ;
break ;
2014-07-10 09:53:59 +02:00
default :
if ( ! info - > platform_callback . get_id ) {
dev_err ( & pdev - > dev , " no platform callbacks " ) ;
return - EINVAL ;
}
memcpy ( & priv - > pfunc ,
& info - > platform_callback ,
sizeof ( struct renesas_usbhs_platform_callback ) ) ;
break ;
}
2011-04-04 13:44:59 +09:00
/* set driver callback functions for platform */
dfunc = & info - > driver_callback ;
dfunc - > notify_hotplug = usbhsc_drvcllbck_notify_hotplug ;
/* set default param if platform doesn't have */
2015-11-18 14:34:09 +09:00
if ( ! priv - > dparam . pipe_configs ) {
priv - > dparam . pipe_configs = usbhsc_default_pipe ;
priv - > dparam . pipe_size = ARRAY_SIZE ( usbhsc_default_pipe ) ;
2011-04-04 13:44:59 +09:00
}
2011-10-12 21:02:22 -07:00
if ( ! priv - > dparam . pio_dma_border )
priv - > dparam . pio_dma_border = 64 ; /* 64byte */
2011-04-04 13:44:59 +09:00
2011-04-28 16:41:20 +09:00
/* FIXME */
/* runtime power control ? */
2011-10-12 21:02:22 -07:00
if ( priv - > pfunc . get_vbus )
2011-04-28 16:41:20 +09:00
usbhsc_flags_set ( priv , USBHSF_RUNTIME_PWCTRL ) ;
2011-04-04 13:44:59 +09:00
/*
* priv settings
*/
2012-01-05 15:37:18 +09:00
priv - > irq = irq_res - > start ;
if ( irq_res - > flags & IORESOURCE_IRQ_SHAREABLE )
priv - > irqflags = IRQF_SHARED ;
2011-04-04 13:44:59 +09:00
priv - > pdev = pdev ;
2011-04-28 16:41:14 +09:00
INIT_DELAYED_WORK ( & priv - > notify_hotplug_work , usbhsc_notify_hotplug ) ;
2011-04-04 13:44:59 +09:00
spin_lock_init ( usbhs_priv_to_lock ( priv ) ) ;
/* call pipe and module init */
ret = usbhs_pipe_probe ( priv ) ;
if ( ret < 0 )
2012-09-10 19:01:07 -07:00
return ret ;
2011-04-04 13:44:59 +09:00
2011-06-06 14:18:44 +09:00
ret = usbhs_fifo_probe ( priv ) ;
2011-04-04 13:44:59 +09:00
if ( ret < 0 )
2011-05-11 16:00:15 +09:00
goto probe_end_pipe_exit ;
2011-04-04 13:44:59 +09:00
2011-06-06 14:18:44 +09:00
ret = usbhs_mod_probe ( priv ) ;
if ( ret < 0 )
goto probe_end_fifo_exit ;
2011-04-04 13:44:59 +09:00
/* dev_set_drvdata should be called after usbhs_mod_init */
2013-08-27 16:10:31 +08:00
platform_set_drvdata ( pdev , priv ) ;
2011-04-04 13:44:59 +09:00
2018-09-11 17:47:03 +09:00
ret = reset_control_deassert ( priv - > rsts ) ;
if ( ret )
goto probe_fail_rst ;
2018-09-11 17:47:05 +09:00
ret = usbhsc_clk_get ( & pdev - > dev , priv ) ;
if ( ret )
goto probe_fail_clks ;
2011-04-04 13:44:59 +09:00
/*
* deviece reset here because
* USB device might be used in boot loader .
*/
usbhs_sys_clock_ctrl ( priv , 0 ) ;
2014-07-10 09:53:59 +02:00
/* check GPIO determining if USB function should be enabled */
if ( priv - > dparam . enable_gpio ) {
gpio_request_one ( priv - > dparam . enable_gpio , GPIOF_IN , NULL ) ;
ret = ! gpio_get_value ( priv - > dparam . enable_gpio ) ;
gpio_free ( priv - > dparam . enable_gpio ) ;
if ( ret ) {
dev_warn ( & pdev - > dev ,
" USB function not selected (GPIO %d) \n " ,
priv - > dparam . enable_gpio ) ;
ret = - ENOTSUPP ;
goto probe_end_mod_exit ;
}
}
2011-04-04 13:44:59 +09:00
/*
* platform call
*
* USB phy setup might depend on CPU / Board .
* If platform has its callback functions ,
* call it here .
*/
ret = usbhs_platform_call ( priv , hardware_init , pdev ) ;
if ( ret < 0 ) {
2014-11-05 01:48:48 +03:00
dev_err ( & pdev - > dev , " platform init failed. \n " ) ;
2011-05-11 16:00:15 +09:00
goto probe_end_mod_exit ;
2011-04-04 13:44:59 +09:00
}
/* reset phy for connection */
usbhs_platform_call ( priv , phy_reset , pdev ) ;
2011-04-28 16:41:20 +09:00
/* power control */
pm_runtime_enable ( & pdev - > dev ) ;
if ( ! usbhsc_flags_has ( priv , USBHSF_RUNTIME_PWCTRL ) ) {
usbhsc_power_ctrl ( priv , 1 ) ;
usbhs_mod_autonomy_mode ( priv ) ;
}
2011-04-04 13:44:59 +09:00
/*
* manual call notify_hotplug for cold plug
*/
2014-12-12 22:53:18 +03:00
usbhsc_drvcllbck_notify_hotplug ( pdev ) ;
2011-04-04 13:44:59 +09:00
dev_info ( & pdev - > dev , " probed \n " ) ;
return ret ;
probe_end_mod_exit :
2018-09-11 17:47:05 +09:00
usbhsc_clk_put ( priv ) ;
probe_fail_clks :
2018-09-11 17:47:03 +09:00
reset_control_assert ( priv - > rsts ) ;
probe_fail_rst :
2011-04-04 13:44:59 +09:00
usbhs_mod_remove ( priv ) ;
2011-06-06 14:18:44 +09:00
probe_end_fifo_exit :
usbhs_fifo_remove ( priv ) ;
2011-05-11 16:00:15 +09:00
probe_end_pipe_exit :
usbhs_pipe_remove ( priv ) ;
2011-04-04 13:44:59 +09:00
2016-06-27 21:09:18 +09:00
dev_info ( & pdev - > dev , " probe failed (%d) \n " , ret ) ;
2011-04-04 13:44:59 +09:00
return ret ;
}
2012-11-19 13:26:20 -05:00
static int usbhs_remove ( struct platform_device * pdev )
2011-04-04 13:44:59 +09:00
{
2011-04-28 16:40:54 +09:00
struct usbhs_priv * priv = usbhs_pdev_to_priv ( pdev ) ;
2017-11-07 08:26:07 +00:00
struct renesas_usbhs_platform_info * info = renesas_usbhs_get_info ( pdev ) ;
2011-04-21 14:10:16 +09:00
struct renesas_usbhs_driver_callback * dfunc = & info - > driver_callback ;
2011-04-04 13:44:59 +09:00
dev_dbg ( & pdev - > dev , " usb remove \n " ) ;
2011-04-21 14:10:16 +09:00
dfunc - > notify_hotplug = NULL ;
2011-04-28 16:41:20 +09:00
/* power off */
if ( ! usbhsc_flags_has ( priv , USBHSF_RUNTIME_PWCTRL ) )
usbhsc_power_ctrl ( priv , 0 ) ;
2011-04-04 13:44:59 +09:00
2011-04-28 16:41:20 +09:00
pm_runtime_disable ( & pdev - > dev ) ;
2011-04-04 13:44:59 +09:00
usbhs_platform_call ( priv , hardware_exit , pdev ) ;
2018-09-11 17:47:05 +09:00
usbhsc_clk_put ( priv ) ;
2018-09-11 17:47:03 +09:00
reset_control_assert ( priv - > rsts ) ;
2011-04-04 13:44:59 +09:00
usbhs_mod_remove ( priv ) ;
2011-06-06 14:18:44 +09:00
usbhs_fifo_remove ( priv ) ;
2011-05-11 16:00:15 +09:00
usbhs_pipe_remove ( priv ) ;
2011-04-04 13:44:59 +09:00
return 0 ;
}
2018-12-11 11:06:25 +01:00
static __maybe_unused int usbhsc_suspend ( struct device * dev )
2011-10-10 21:58:19 -07:00
{
struct usbhs_priv * priv = dev_get_drvdata ( dev ) ;
struct usbhs_mod * mod = usbhs_mod_get_current ( priv ) ;
if ( mod ) {
usbhs_mod_call ( priv , stop , priv ) ;
usbhs_mod_change ( priv , - 1 ) ;
}
if ( mod | | ! usbhsc_flags_has ( priv , USBHSF_RUNTIME_PWCTRL ) )
usbhsc_power_ctrl ( priv , 0 ) ;
return 0 ;
}
2018-12-11 11:06:25 +01:00
static __maybe_unused int usbhsc_resume ( struct device * dev )
2011-10-10 21:58:19 -07:00
{
struct usbhs_priv * priv = dev_get_drvdata ( dev ) ;
struct platform_device * pdev = usbhs_priv_to_pdev ( priv ) ;
2017-07-19 16:16:54 +09:00
if ( ! usbhsc_flags_has ( priv , USBHSF_RUNTIME_PWCTRL ) ) {
2011-10-10 21:58:19 -07:00
usbhsc_power_ctrl ( priv , 1 ) ;
2017-07-19 16:16:54 +09:00
usbhs_mod_autonomy_mode ( priv ) ;
}
2011-10-10 21:58:19 -07:00
2012-08-05 22:44:43 -07:00
usbhs_platform_call ( priv , phy_reset , pdev ) ;
usbhsc_drvcllbck_notify_hotplug ( pdev ) ;
2011-10-10 21:58:19 -07:00
return 0 ;
}
2018-11-21 06:31:23 +00:00
static SIMPLE_DEV_PM_OPS ( usbhsc_pm_ops , usbhsc_suspend , usbhsc_resume ) ;
2011-10-10 21:58:19 -07:00
2011-04-04 13:44:59 +09:00
static struct platform_driver renesas_usbhs_driver = {
. driver = {
. name = " renesas_usbhs " ,
2011-10-10 21:58:19 -07:00
. pm = & usbhsc_pm_ops ,
2014-09-03 14:25:56 +09:00
. of_match_table = of_match_ptr ( usbhs_of_match ) ,
2011-04-04 13:44:59 +09:00
} ,
. probe = usbhs_probe ,
2012-11-19 13:21:08 -05:00
. remove = usbhs_remove ,
2011-04-04 13:44:59 +09:00
} ;
2011-11-27 20:16:27 +08:00
module_platform_driver ( renesas_usbhs_driver ) ;
2011-04-04 13:44:59 +09:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Renesas USB driver " ) ;
MODULE_AUTHOR ( " Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> " ) ;