2018-08-22 01:02:20 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-11-30 04:44:30 +03:00
/*
* Renesas R - Car Gen3 for USB2 .0 PHY driver
*
2017-10-12 09:34:45 +03:00
* Copyright ( C ) 2015 - 2017 Renesas Electronics Corporation
2015-11-30 04:44:30 +03:00
*
* This is based on the phy - rcar - gen2 driver :
* Copyright ( C ) 2014 Renesas Solutions Corp .
* Copyright ( C ) 2014 Cogent Embedded , Inc .
*/
2017-09-21 06:11:24 +03:00
# include <linux/extcon-provider.h>
2015-11-30 04:44:32 +03:00
# include <linux/interrupt.h>
2015-11-30 04:44:30 +03:00
# include <linux/io.h>
# include <linux/module.h>
2019-06-10 09:23:55 +03:00
# include <linux/mutex.h>
2015-11-30 04:44:30 +03:00
# include <linux/of.h>
# include <linux/of_address.h>
2017-10-12 09:34:47 +03:00
# include <linux/of_device.h>
2015-11-30 04:44:30 +03:00
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
2017-03-14 02:37:40 +03:00
# include <linux/pm_runtime.h>
2016-03-03 13:09:05 +03:00
# include <linux/regulator/consumer.h>
2019-10-07 10:55:10 +03:00
# include <linux/string.h>
2017-10-12 09:34:45 +03:00
# include <linux/usb/of.h>
2016-06-27 09:36:53 +03:00
# include <linux/workqueue.h>
2015-11-30 04:44:30 +03:00
/******* USB2.0 Host registers (original offset is +0x200) *******/
# define USB2_INT_ENABLE 0x000
# define USB2_USBCTR 0x00c
# define USB2_SPD_RSM_TIMSET 0x10c
# define USB2_OC_TIMSET 0x110
2015-11-30 04:44:31 +03:00
# define USB2_COMMCTRL 0x600
2015-11-30 04:44:32 +03:00
# define USB2_OBINTSTA 0x604
# define USB2_OBINTEN 0x608
2015-11-30 04:44:31 +03:00
# define USB2_VBCTRL 0x60c
# define USB2_LINECTRL1 0x610
# define USB2_ADPCTRL 0x630
2015-11-30 04:44:30 +03:00
/* INT_ENABLE */
2015-11-30 04:44:32 +03:00
# define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
2019-04-11 13:27:36 +03:00
# define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) /* For EHCI */
# define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) /* For OHCI */
2015-11-30 04:44:30 +03:00
/* USBCTR */
# define USB2_USBCTR_DIRPD BIT(2)
# define USB2_USBCTR_PLL_RST BIT(1)
/* SPD_RSM_TIMSET */
# define USB2_SPD_RSM_TIMSET_INIT 0x014e029b
/* OC_TIMSET */
# define USB2_OC_TIMSET_INIT 0x000209ab
2015-11-30 04:44:31 +03:00
/* COMMCTRL */
# define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */
2015-11-30 04:44:32 +03:00
/* OBINTSTA and OBINTEN */
# define USB2_OBINT_SESSVLDCHG BIT(12)
# define USB2_OBINT_IDDIGCHG BIT(11)
# define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \
USB2_OBINT_IDDIGCHG )
2015-11-30 04:44:31 +03:00
/* VBCTRL */
2019-08-06 11:51:19 +03:00
# define USB2_VBCTRL_OCCLREN BIT(16)
2015-11-30 04:44:31 +03:00
# define USB2_VBCTRL_DRVVBUSSEL BIT(8)
/* LINECTRL1 */
# define USB2_LINECTRL1_DPRPD_EN BIT(19)
# define USB2_LINECTRL1_DP_RPD BIT(18)
# define USB2_LINECTRL1_DMRPD_EN BIT(17)
# define USB2_LINECTRL1_DM_RPD BIT(16)
2016-11-09 05:30:25 +03:00
# define USB2_LINECTRL1_OPMODE_NODRV BIT(6)
2015-11-30 04:44:31 +03:00
/* ADPCTRL */
# define USB2_ADPCTRL_OTGSESSVLD BIT(20)
# define USB2_ADPCTRL_IDDIG BIT(19)
# define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
# define USB2_ADPCTRL_DRVVBUS BIT(4)
2019-04-11 13:27:36 +03:00
# define NUM_OF_PHYS 4
enum rcar_gen3_phy_index {
PHY_INDEX_BOTH_HC ,
PHY_INDEX_OHCI ,
PHY_INDEX_EHCI ,
PHY_INDEX_HSUSB
} ;
static const u32 rcar_gen3_int_enable [ NUM_OF_PHYS ] = {
USB2_INT_ENABLE_USBH_INTB_EN | USB2_INT_ENABLE_USBH_INTA_EN ,
USB2_INT_ENABLE_USBH_INTA_EN ,
USB2_INT_ENABLE_USBH_INTB_EN ,
0
} ;
struct rcar_gen3_phy {
struct phy * phy ;
struct rcar_gen3_chan * ch ;
u32 int_enable_bits ;
bool initialized ;
bool otg_initialized ;
bool powered ;
} ;
2015-11-30 04:44:30 +03:00
struct rcar_gen3_chan {
2016-03-03 13:09:04 +03:00
void __iomem * base ;
2019-04-11 13:27:35 +03:00
struct device * dev ; /* platform_device's device */
2016-04-29 11:52:25 +03:00
struct extcon_dev * extcon ;
2019-04-11 13:27:36 +03:00
struct rcar_gen3_phy rphys [ NUM_OF_PHYS ] ;
2016-03-03 13:09:05 +03:00
struct regulator * vbus ;
2016-06-27 09:36:53 +03:00
struct work_struct work ;
2019-06-10 09:23:55 +03:00
struct mutex lock ; /* protects rphys[...].powered */
2018-09-21 14:53:22 +03:00
enum usb_dr_mode dr_mode ;
2020-07-17 14:44:56 +03:00
int irq ;
2016-06-27 09:36:53 +03:00
bool extcon_host ;
2018-09-21 14:53:24 +03:00
bool is_otg_channel ;
2018-09-21 14:53:19 +03:00
bool uses_otg_pins ;
2015-11-30 04:44:30 +03:00
} ;
2018-09-21 14:53:24 +03:00
/*
* Combination about is_otg_channel and uses_otg_pins :
*
* Parameters | | Behaviors
* is_otg_channel | uses_otg_pins | | irqs | role sysfs
* - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - + - - - - - - - - - - - -
* true | true | | enabled | enabled
* true | false | | disabled | enabled
* false | any | | disabled | disabled
*/
2016-06-27 09:36:53 +03:00
static void rcar_gen3_phy_usb2_work ( struct work_struct * work )
{
struct rcar_gen3_chan * ch = container_of ( work , struct rcar_gen3_chan ,
work ) ;
if ( ch - > extcon_host ) {
2016-12-30 07:11:28 +03:00
extcon_set_state_sync ( ch - > extcon , EXTCON_USB_HOST , true ) ;
extcon_set_state_sync ( ch - > extcon , EXTCON_USB , false ) ;
2016-06-27 09:36:53 +03:00
} else {
2016-12-30 07:11:28 +03:00
extcon_set_state_sync ( ch - > extcon , EXTCON_USB_HOST , false ) ;
extcon_set_state_sync ( ch - > extcon , EXTCON_USB , true ) ;
2016-06-27 09:36:53 +03:00
}
}
2015-11-30 04:44:31 +03:00
static void rcar_gen3_set_host_mode ( struct rcar_gen3_chan * ch , int host )
{
2016-03-03 13:09:04 +03:00
void __iomem * usb2_base = ch - > base ;
2015-11-30 04:44:31 +03:00
u32 val = readl ( usb2_base + USB2_COMMCTRL ) ;
2019-04-11 13:27:35 +03:00
dev_vdbg ( ch - > dev , " %s: %08x, %d \n " , __func__ , val , host ) ;
2015-11-30 04:44:31 +03:00
if ( host )
val & = ~ USB2_COMMCTRL_OTG_PERI ;
else
val | = USB2_COMMCTRL_OTG_PERI ;
writel ( val , usb2_base + USB2_COMMCTRL ) ;
}
static void rcar_gen3_set_linectrl ( struct rcar_gen3_chan * ch , int dp , int dm )
{
2016-03-03 13:09:04 +03:00
void __iomem * usb2_base = ch - > base ;
2015-11-30 04:44:31 +03:00
u32 val = readl ( usb2_base + USB2_LINECTRL1 ) ;
2019-04-11 13:27:35 +03:00
dev_vdbg ( ch - > dev , " %s: %08x, %d, %d \n " , __func__ , val , dp , dm ) ;
2015-11-30 04:44:31 +03:00
val & = ~ ( USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD ) ;
if ( dp )
val | = USB2_LINECTRL1_DP_RPD ;
if ( dm )
val | = USB2_LINECTRL1_DM_RPD ;
writel ( val , usb2_base + USB2_LINECTRL1 ) ;
}
static void rcar_gen3_enable_vbus_ctrl ( struct rcar_gen3_chan * ch , int vbus )
{
2016-03-03 13:09:04 +03:00
void __iomem * usb2_base = ch - > base ;
2015-11-30 04:44:31 +03:00
u32 val = readl ( usb2_base + USB2_ADPCTRL ) ;
2019-04-11 13:27:35 +03:00
dev_vdbg ( ch - > dev , " %s: %08x, %d \n " , __func__ , val , vbus ) ;
2015-11-30 04:44:31 +03:00
if ( vbus )
val | = USB2_ADPCTRL_DRVVBUS ;
else
val & = ~ USB2_ADPCTRL_DRVVBUS ;
writel ( val , usb2_base + USB2_ADPCTRL ) ;
}
2018-09-21 14:53:21 +03:00
static void rcar_gen3_control_otg_irq ( struct rcar_gen3_chan * ch , int enable )
{
void __iomem * usb2_base = ch - > base ;
u32 val = readl ( usb2_base + USB2_OBINTEN ) ;
2018-09-21 14:53:23 +03:00
if ( ch - > uses_otg_pins & & enable )
2018-09-21 14:53:21 +03:00
val | = USB2_OBINT_BITS ;
else
val & = ~ USB2_OBINT_BITS ;
writel ( val , usb2_base + USB2_OBINTEN ) ;
}
2015-11-30 04:44:31 +03:00
static void rcar_gen3_init_for_host ( struct rcar_gen3_chan * ch )
{
rcar_gen3_set_linectrl ( ch , 1 , 1 ) ;
rcar_gen3_set_host_mode ( ch , 1 ) ;
rcar_gen3_enable_vbus_ctrl ( ch , 1 ) ;
2016-04-29 11:52:25 +03:00
2016-06-27 09:36:53 +03:00
ch - > extcon_host = true ;
schedule_work ( & ch - > work ) ;
2015-11-30 04:44:31 +03:00
}
static void rcar_gen3_init_for_peri ( struct rcar_gen3_chan * ch )
{
rcar_gen3_set_linectrl ( ch , 0 , 1 ) ;
rcar_gen3_set_host_mode ( ch , 0 ) ;
rcar_gen3_enable_vbus_ctrl ( ch , 0 ) ;
2016-04-29 11:52:25 +03:00
2016-06-27 09:36:53 +03:00
ch - > extcon_host = false ;
schedule_work ( & ch - > work ) ;
2015-11-30 04:44:31 +03:00
}
2016-11-09 05:30:25 +03:00
static void rcar_gen3_init_for_b_host ( struct rcar_gen3_chan * ch )
{
void __iomem * usb2_base = ch - > base ;
u32 val ;
val = readl ( usb2_base + USB2_LINECTRL1 ) ;
writel ( val | USB2_LINECTRL1_OPMODE_NODRV , usb2_base + USB2_LINECTRL1 ) ;
rcar_gen3_set_linectrl ( ch , 1 , 1 ) ;
rcar_gen3_set_host_mode ( ch , 1 ) ;
rcar_gen3_enable_vbus_ctrl ( ch , 0 ) ;
val = readl ( usb2_base + USB2_LINECTRL1 ) ;
writel ( val & ~ USB2_LINECTRL1_OPMODE_NODRV , usb2_base + USB2_LINECTRL1 ) ;
}
static void rcar_gen3_init_for_a_peri ( struct rcar_gen3_chan * ch )
{
rcar_gen3_set_linectrl ( ch , 0 , 1 ) ;
rcar_gen3_set_host_mode ( ch , 0 ) ;
rcar_gen3_enable_vbus_ctrl ( ch , 1 ) ;
}
static void rcar_gen3_init_from_a_peri_to_a_host ( struct rcar_gen3_chan * ch )
{
2018-09-21 14:53:21 +03:00
rcar_gen3_control_otg_irq ( ch , 0 ) ;
2016-11-09 05:30:25 +03:00
2018-09-21 14:53:18 +03:00
rcar_gen3_enable_vbus_ctrl ( ch , 1 ) ;
2016-11-09 05:30:25 +03:00
rcar_gen3_init_for_host ( ch ) ;
2018-09-21 14:53:21 +03:00
rcar_gen3_control_otg_irq ( ch , 1 ) ;
2016-11-09 05:30:25 +03:00
}
2015-11-30 04:44:31 +03:00
static bool rcar_gen3_check_id ( struct rcar_gen3_chan * ch )
{
2018-09-21 14:53:23 +03:00
if ( ! ch - > uses_otg_pins )
return ( ch - > dr_mode = = USB_DR_MODE_HOST ) ? false : true ;
2016-03-03 13:09:04 +03:00
return ! ! ( readl ( ch - > base + USB2_ADPCTRL ) & USB2_ADPCTRL_IDDIG ) ;
2015-11-30 04:44:31 +03:00
}
static void rcar_gen3_device_recognition ( struct rcar_gen3_chan * ch )
{
2016-05-31 15:47:17 +03:00
if ( ! rcar_gen3_check_id ( ch ) )
2015-11-30 04:44:31 +03:00
rcar_gen3_init_for_host ( ch ) ;
else
rcar_gen3_init_for_peri ( ch ) ;
}
2016-11-09 05:30:25 +03:00
static bool rcar_gen3_is_host ( struct rcar_gen3_chan * ch )
{
return ! ( readl ( ch - > base + USB2_COMMCTRL ) & USB2_COMMCTRL_OTG_PERI ) ;
}
2017-10-12 09:34:46 +03:00
static enum phy_mode rcar_gen3_get_phy_mode ( struct rcar_gen3_chan * ch )
{
if ( rcar_gen3_is_host ( ch ) )
return PHY_MODE_USB_HOST ;
return PHY_MODE_USB_DEVICE ;
}
2019-04-11 13:27:36 +03:00
static bool rcar_gen3_is_any_rphy_initialized ( struct rcar_gen3_chan * ch )
{
int i ;
for ( i = 0 ; i < NUM_OF_PHYS ; i + + ) {
if ( ch - > rphys [ i ] . initialized )
return true ;
}
return false ;
}
static bool rcar_gen3_needs_init_otg ( struct rcar_gen3_chan * ch )
{
int i ;
for ( i = 0 ; i < NUM_OF_PHYS ; i + + ) {
if ( ch - > rphys [ i ] . otg_initialized )
return false ;
}
return true ;
}
static bool rcar_gen3_are_all_rphys_power_off ( struct rcar_gen3_chan * ch )
{
int i ;
for ( i = 0 ; i < NUM_OF_PHYS ; i + + ) {
if ( ch - > rphys [ i ] . powered )
return false ;
}
return true ;
}
2016-11-09 05:30:25 +03:00
static ssize_t role_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rcar_gen3_chan * ch = dev_get_drvdata ( dev ) ;
2017-10-12 09:34:46 +03:00
bool is_b_device ;
enum phy_mode cur_mode , new_mode ;
2016-11-09 05:30:25 +03:00
2019-04-11 13:27:36 +03:00
if ( ! ch - > is_otg_channel | | ! rcar_gen3_is_any_rphy_initialized ( ch ) )
2016-11-09 05:30:25 +03:00
return - EIO ;
2019-10-07 10:55:10 +03:00
if ( sysfs_streq ( buf , " host " ) )
2017-10-12 09:34:46 +03:00
new_mode = PHY_MODE_USB_HOST ;
2019-10-07 10:55:10 +03:00
else if ( sysfs_streq ( buf , " peripheral " ) )
2017-10-12 09:34:46 +03:00
new_mode = PHY_MODE_USB_DEVICE ;
2016-11-09 05:30:25 +03:00
else
return - EINVAL ;
2017-10-12 09:34:46 +03:00
/* is_b_device: true is B-Device. false is A-Device. */
is_b_device = rcar_gen3_check_id ( ch ) ;
cur_mode = rcar_gen3_get_phy_mode ( ch ) ;
2016-11-09 05:30:25 +03:00
/* If current and new mode is the same, this returns the error */
2017-10-12 09:34:46 +03:00
if ( cur_mode = = new_mode )
2016-11-09 05:30:25 +03:00
return - EINVAL ;
2017-10-12 09:34:46 +03:00
if ( new_mode = = PHY_MODE_USB_HOST ) { /* And is_host must be false */
2016-11-09 05:30:25 +03:00
if ( ! is_b_device ) /* A-Peripheral */
rcar_gen3_init_from_a_peri_to_a_host ( ch ) ;
else /* B-Peripheral */
rcar_gen3_init_for_b_host ( ch ) ;
} else { /* And is_host must be true */
if ( ! is_b_device ) /* A-Host */
rcar_gen3_init_for_a_peri ( ch ) ;
else /* B-Host */
rcar_gen3_init_for_peri ( ch ) ;
}
return count ;
}
static ssize_t role_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct rcar_gen3_chan * ch = dev_get_drvdata ( dev ) ;
2019-04-11 13:27:36 +03:00
if ( ! ch - > is_otg_channel | | ! rcar_gen3_is_any_rphy_initialized ( ch ) )
2016-11-09 05:30:25 +03:00
return - EIO ;
return sprintf ( buf , " %s \n " , rcar_gen3_is_host ( ch ) ? " host " :
" peripheral " ) ;
}
static DEVICE_ATTR_RW ( role ) ;
2015-11-30 04:44:31 +03:00
static void rcar_gen3_init_otg ( struct rcar_gen3_chan * ch )
{
2016-03-03 13:09:04 +03:00
void __iomem * usb2_base = ch - > base ;
2015-11-30 04:44:31 +03:00
u32 val ;
2018-11-30 10:00:57 +03:00
/* Should not use functions of read-modify-write a register */
val = readl ( usb2_base + USB2_LINECTRL1 ) ;
val = ( val & ~ USB2_LINECTRL1_DP_RPD ) | USB2_LINECTRL1_DPRPD_EN |
USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD ;
writel ( val , usb2_base + USB2_LINECTRL1 ) ;
2015-11-30 04:44:31 +03:00
val = readl ( usb2_base + USB2_VBCTRL ) ;
2019-08-06 11:51:19 +03:00
val & = ~ USB2_VBCTRL_OCCLREN ;
2015-11-30 04:44:31 +03:00
writel ( val | USB2_VBCTRL_DRVVBUSSEL , usb2_base + USB2_VBCTRL ) ;
val = readl ( usb2_base + USB2_ADPCTRL ) ;
writel ( val | USB2_ADPCTRL_IDPULLUP , usb2_base + USB2_ADPCTRL ) ;
2018-11-30 10:00:57 +03:00
msleep ( 20 ) ;
writel ( 0xffffffff , usb2_base + USB2_OBINTSTA ) ;
writel ( USB2_OBINT_BITS , usb2_base + USB2_OBINTEN ) ;
2015-11-30 04:44:31 +03:00
rcar_gen3_device_recognition ( ch ) ;
}
2020-07-17 14:44:56 +03:00
static irqreturn_t rcar_gen3_phy_usb2_irq ( int irq , void * _ch )
{
struct rcar_gen3_chan * ch = _ch ;
void __iomem * usb2_base = ch - > base ;
u32 status = readl ( usb2_base + USB2_OBINTSTA ) ;
irqreturn_t ret = IRQ_NONE ;
if ( status & USB2_OBINT_BITS ) {
dev_vdbg ( ch - > dev , " %s: %08x \n " , __func__ , status ) ;
writel ( USB2_OBINT_BITS , usb2_base + USB2_OBINTSTA ) ;
rcar_gen3_device_recognition ( ch ) ;
ret = IRQ_HANDLED ;
}
return ret ;
}
2015-11-30 04:44:30 +03:00
static int rcar_gen3_phy_usb2_init ( struct phy * p )
{
2019-04-11 13:27:36 +03:00
struct rcar_gen3_phy * rphy = phy_get_drvdata ( p ) ;
struct rcar_gen3_chan * channel = rphy - > ch ;
2016-03-03 13:09:04 +03:00
void __iomem * usb2_base = channel - > base ;
2019-04-11 13:27:36 +03:00
u32 val ;
2020-07-17 14:44:56 +03:00
int ret ;
if ( ! rcar_gen3_is_any_rphy_initialized ( channel ) & & channel - > irq > = 0 ) {
INIT_WORK ( & channel - > work , rcar_gen3_phy_usb2_work ) ;
ret = request_irq ( channel - > irq , rcar_gen3_phy_usb2_irq ,
IRQF_SHARED , dev_name ( channel - > dev ) , channel ) ;
2020-07-17 14:44:57 +03:00
if ( ret < 0 ) {
2020-07-17 14:44:56 +03:00
dev_err ( channel - > dev , " No irq handler (%d) \n " , channel - > irq ) ;
2020-07-17 14:44:57 +03:00
return ret ;
}
2020-07-17 14:44:56 +03:00
}
2015-11-30 04:44:30 +03:00
/* Initialize USB2 part */
2019-04-11 13:27:36 +03:00
val = readl ( usb2_base + USB2_INT_ENABLE ) ;
val | = USB2_INT_ENABLE_UCOM_INTEN | rphy - > int_enable_bits ;
writel ( val , usb2_base + USB2_INT_ENABLE ) ;
2015-11-30 04:44:30 +03:00
writel ( USB2_SPD_RSM_TIMSET_INIT , usb2_base + USB2_SPD_RSM_TIMSET ) ;
writel ( USB2_OC_TIMSET_INIT , usb2_base + USB2_OC_TIMSET ) ;
2016-01-07 12:16:44 +03:00
/* Initialize otg part */
2019-04-11 13:27:36 +03:00
if ( channel - > is_otg_channel ) {
if ( rcar_gen3_needs_init_otg ( channel ) )
rcar_gen3_init_otg ( channel ) ;
rphy - > otg_initialized = true ;
}
rphy - > initialized = true ;
2015-11-30 04:44:30 +03:00
return 0 ;
}
static int rcar_gen3_phy_usb2_exit ( struct phy * p )
{
2019-04-11 13:27:36 +03:00
struct rcar_gen3_phy * rphy = phy_get_drvdata ( p ) ;
struct rcar_gen3_chan * channel = rphy - > ch ;
void __iomem * usb2_base = channel - > base ;
u32 val ;
2015-11-30 04:44:30 +03:00
2019-04-11 13:27:36 +03:00
rphy - > initialized = false ;
if ( channel - > is_otg_channel )
rphy - > otg_initialized = false ;
val = readl ( usb2_base + USB2_INT_ENABLE ) ;
val & = ~ rphy - > int_enable_bits ;
if ( ! rcar_gen3_is_any_rphy_initialized ( channel ) )
val & = ~ USB2_INT_ENABLE_UCOM_INTEN ;
writel ( val , usb2_base + USB2_INT_ENABLE ) ;
2015-11-30 04:44:30 +03:00
2020-07-17 14:44:56 +03:00
if ( channel - > irq > = 0 & & ! rcar_gen3_is_any_rphy_initialized ( channel ) )
free_irq ( channel - > irq , channel ) ;
2015-11-30 04:44:30 +03:00
return 0 ;
}
static int rcar_gen3_phy_usb2_power_on ( struct phy * p )
{
2019-04-11 13:27:36 +03:00
struct rcar_gen3_phy * rphy = phy_get_drvdata ( p ) ;
struct rcar_gen3_chan * channel = rphy - > ch ;
2016-03-03 13:09:04 +03:00
void __iomem * usb2_base = channel - > base ;
2015-11-30 04:44:30 +03:00
u32 val ;
2019-06-10 09:23:55 +03:00
int ret = 0 ;
2016-03-03 13:09:05 +03:00
2019-06-10 09:23:55 +03:00
mutex_lock ( & channel - > lock ) ;
2019-04-11 13:27:36 +03:00
if ( ! rcar_gen3_are_all_rphys_power_off ( channel ) )
2019-06-10 09:23:55 +03:00
goto out ;
2019-04-11 13:27:36 +03:00
2016-03-03 13:09:05 +03:00
if ( channel - > vbus ) {
ret = regulator_enable ( channel - > vbus ) ;
if ( ret )
2019-06-10 09:23:55 +03:00
goto out ;
2016-03-03 13:09:05 +03:00
}
2015-11-30 04:44:30 +03:00
val = readl ( usb2_base + USB2_USBCTR ) ;
val | = USB2_USBCTR_PLL_RST ;
writel ( val , usb2_base + USB2_USBCTR ) ;
val & = ~ USB2_USBCTR_PLL_RST ;
writel ( val , usb2_base + USB2_USBCTR ) ;
2019-06-10 09:23:55 +03:00
out :
/* The powered flag should be set for any other phys anyway */
2019-04-11 13:27:36 +03:00
rphy - > powered = true ;
2019-06-10 09:23:55 +03:00
mutex_unlock ( & channel - > lock ) ;
2019-04-11 13:27:36 +03:00
2015-11-30 04:44:30 +03:00
return 0 ;
}
2016-03-03 13:09:05 +03:00
static int rcar_gen3_phy_usb2_power_off ( struct phy * p )
{
2019-04-11 13:27:36 +03:00
struct rcar_gen3_phy * rphy = phy_get_drvdata ( p ) ;
struct rcar_gen3_chan * channel = rphy - > ch ;
2016-03-03 13:09:05 +03:00
int ret = 0 ;
2019-06-10 09:23:55 +03:00
mutex_lock ( & channel - > lock ) ;
2019-04-11 13:27:36 +03:00
rphy - > powered = false ;
if ( ! rcar_gen3_are_all_rphys_power_off ( channel ) )
2019-06-10 09:23:55 +03:00
goto out ;
2019-04-11 13:27:36 +03:00
2016-03-03 13:09:05 +03:00
if ( channel - > vbus )
ret = regulator_disable ( channel - > vbus ) ;
2019-06-10 09:23:55 +03:00
out :
mutex_unlock ( & channel - > lock ) ;
2016-03-03 13:09:05 +03:00
return ret ;
}
2017-01-08 13:35:56 +03:00
static const struct phy_ops rcar_gen3_phy_usb2_ops = {
2015-11-30 04:44:30 +03:00
. init = rcar_gen3_phy_usb2_init ,
. exit = rcar_gen3_phy_usb2_exit ,
. power_on = rcar_gen3_phy_usb2_power_on ,
2016-03-03 13:09:05 +03:00
. power_off = rcar_gen3_phy_usb2_power_off ,
2015-11-30 04:44:30 +03:00
. owner = THIS_MODULE ,
} ;
2019-04-10 17:48:41 +03:00
static const struct phy_ops rz_g1c_phy_usb2_ops = {
. init = rcar_gen3_phy_usb2_init ,
. exit = rcar_gen3_phy_usb2_exit ,
. owner = THIS_MODULE ,
} ;
2015-11-30 04:44:30 +03:00
static const struct of_device_id rcar_gen3_phy_usb2_match_table [ ] = {
2019-04-10 17:48:41 +03:00
{
. compatible = " renesas,usb2-phy-r8a77470 " ,
. data = & rz_g1c_phy_usb2_ops ,
} ,
{
. compatible = " renesas,usb2-phy-r8a7795 " ,
. data = & rcar_gen3_phy_usb2_ops ,
} ,
{
. compatible = " renesas,usb2-phy-r8a7796 " ,
. data = & rcar_gen3_phy_usb2_ops ,
} ,
{
. compatible = " renesas,usb2-phy-r8a77965 " ,
. data = & rcar_gen3_phy_usb2_ops ,
} ,
{
. compatible = " renesas,rcar-gen3-usb2-phy " ,
. data = & rcar_gen3_phy_usb2_ops ,
} ,
{ /* sentinel */ } ,
2015-11-30 04:44:30 +03:00
} ;
MODULE_DEVICE_TABLE ( of , rcar_gen3_phy_usb2_match_table ) ;
2016-04-29 11:52:25 +03:00
static const unsigned int rcar_gen3_phy_cable [ ] = {
EXTCON_USB ,
EXTCON_USB_HOST ,
EXTCON_NONE ,
} ;
2019-04-11 13:27:36 +03:00
static struct phy * rcar_gen3_phy_usb2_xlate ( struct device * dev ,
struct of_phandle_args * args )
{
struct rcar_gen3_chan * ch = dev_get_drvdata ( dev ) ;
if ( args - > args_count = = 0 ) /* For old version dts */
return ch - > rphys [ PHY_INDEX_BOTH_HC ] . phy ;
else if ( args - > args_count > 1 ) /* Prevent invalid args count */
return ERR_PTR ( - ENODEV ) ;
if ( args - > args [ 0 ] > = NUM_OF_PHYS )
return ERR_PTR ( - ENODEV ) ;
return ch - > rphys [ args - > args [ 0 ] ] . phy ;
}
static enum usb_dr_mode rcar_gen3_get_dr_mode ( struct device_node * np )
{
enum usb_dr_mode candidate = USB_DR_MODE_UNKNOWN ;
int i ;
/*
* If one of device nodes has other dr_mode except UNKNOWN ,
* this function returns UNKNOWN . To achieve backward compatibility ,
* this loop starts the index as 0.
*/
for ( i = 0 ; i < NUM_OF_PHYS ; i + + ) {
enum usb_dr_mode mode = of_usb_get_dr_mode_by_phy ( np , i ) ;
if ( mode ! = USB_DR_MODE_UNKNOWN ) {
if ( candidate = = USB_DR_MODE_UNKNOWN )
candidate = mode ;
else if ( candidate ! = mode )
return USB_DR_MODE_UNKNOWN ;
}
}
return candidate ;
}
2015-11-30 04:44:30 +03:00
static int rcar_gen3_phy_usb2_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct rcar_gen3_chan * channel ;
struct phy_provider * provider ;
struct resource * res ;
2019-04-10 17:48:41 +03:00
const struct phy_ops * phy_usb2_ops ;
2020-07-17 14:44:56 +03:00
int ret = 0 , i ;
2015-11-30 04:44:30 +03:00
if ( ! dev - > of_node ) {
dev_err ( dev , " This driver needs device tree \n " ) ;
return - EINVAL ;
}
channel = devm_kzalloc ( dev , sizeof ( * channel ) , GFP_KERNEL ) ;
if ( ! channel )
return - ENOMEM ;
2016-01-07 12:16:44 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2016-03-03 13:09:04 +03:00
channel - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( channel - > base ) )
return PTR_ERR ( channel - > base ) ;
2015-11-30 04:44:30 +03:00
2020-07-17 14:44:56 +03:00
/* get irq number here and request_irq for OTG in phy_init */
channel - > irq = platform_get_irq_optional ( pdev , 0 ) ;
2019-04-11 13:27:36 +03:00
channel - > dr_mode = rcar_gen3_get_dr_mode ( dev - > of_node ) ;
2018-09-21 14:53:22 +03:00
if ( channel - > dr_mode ! = USB_DR_MODE_UNKNOWN ) {
2017-10-12 09:34:45 +03:00
int ret ;
2018-09-21 14:53:24 +03:00
channel - > is_otg_channel = true ;
2018-09-21 14:53:20 +03:00
channel - > uses_otg_pins = ! of_property_read_bool ( dev - > of_node ,
" renesas,no-otg-pins " ) ;
2016-04-29 11:52:25 +03:00
channel - > extcon = devm_extcon_dev_allocate ( dev ,
rcar_gen3_phy_cable ) ;
if ( IS_ERR ( channel - > extcon ) )
return PTR_ERR ( channel - > extcon ) ;
ret = devm_extcon_dev_register ( dev , channel - > extcon ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to register extcon \n " ) ;
return ret ;
}
2015-11-30 04:44:30 +03:00
}
2017-03-14 02:37:40 +03:00
/*
* devm_phy_create ( ) will call pm_runtime_enable ( & phy - > dev ) ;
* And then , phy - core will manage runtime pm for this device .
*/
pm_runtime_enable ( dev ) ;
2019-04-10 17:48:41 +03:00
phy_usb2_ops = of_device_get_match_data ( dev ) ;
if ( ! phy_usb2_ops )
return - EINVAL ;
2019-06-10 09:23:55 +03:00
mutex_init ( & channel - > lock ) ;
2019-04-11 13:27:36 +03:00
for ( i = 0 ; i < NUM_OF_PHYS ; i + + ) {
channel - > rphys [ i ] . phy = devm_phy_create ( dev , NULL ,
phy_usb2_ops ) ;
if ( IS_ERR ( channel - > rphys [ i ] . phy ) ) {
dev_err ( dev , " Failed to create USB2 PHY \n " ) ;
ret = PTR_ERR ( channel - > rphys [ i ] . phy ) ;
goto error ;
}
channel - > rphys [ i ] . ch = channel ;
channel - > rphys [ i ] . int_enable_bits = rcar_gen3_int_enable [ i ] ;
phy_set_drvdata ( channel - > rphys [ i ] . phy , & channel - > rphys [ i ] ) ;
2015-11-30 04:44:30 +03:00
}
2016-03-03 13:09:05 +03:00
channel - > vbus = devm_regulator_get_optional ( dev , " vbus " ) ;
if ( IS_ERR ( channel - > vbus ) ) {
2017-03-14 02:37:40 +03:00
if ( PTR_ERR ( channel - > vbus ) = = - EPROBE_DEFER ) {
ret = PTR_ERR ( channel - > vbus ) ;
goto error ;
}
2016-03-03 13:09:05 +03:00
channel - > vbus = NULL ;
}
2016-11-09 05:30:25 +03:00
platform_set_drvdata ( pdev , channel ) ;
2019-04-11 13:27:35 +03:00
channel - > dev = dev ;
2015-11-30 04:44:30 +03:00
2019-04-11 13:27:36 +03:00
provider = devm_of_phy_provider_register ( dev , rcar_gen3_phy_usb2_xlate ) ;
2016-11-09 05:30:25 +03:00
if ( IS_ERR ( provider ) ) {
2015-11-30 04:44:30 +03:00
dev_err ( dev , " Failed to register PHY provider \n " ) ;
2017-03-14 02:37:40 +03:00
ret = PTR_ERR ( provider ) ;
goto error ;
2018-09-21 14:53:24 +03:00
} else if ( channel - > is_otg_channel ) {
2016-11-09 05:30:25 +03:00
int ret ;
ret = device_create_file ( dev , & dev_attr_role ) ;
if ( ret < 0 )
2017-03-14 02:37:40 +03:00
goto error ;
2016-11-09 05:30:25 +03:00
}
2015-11-30 04:44:30 +03:00
2017-03-14 02:37:40 +03:00
return 0 ;
error :
pm_runtime_disable ( dev ) ;
return ret ;
2015-11-30 04:44:30 +03:00
}
2016-11-09 05:30:25 +03:00
static int rcar_gen3_phy_usb2_remove ( struct platform_device * pdev )
{
struct rcar_gen3_chan * channel = platform_get_drvdata ( pdev ) ;
2018-09-21 14:53:24 +03:00
if ( channel - > is_otg_channel )
2016-11-09 05:30:25 +03:00
device_remove_file ( & pdev - > dev , & dev_attr_role ) ;
2017-03-14 02:37:40 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2016-11-09 05:30:25 +03:00
return 0 ;
} ;
2015-11-30 04:44:30 +03:00
static struct platform_driver rcar_gen3_phy_usb2_driver = {
. driver = {
. name = " phy_rcar_gen3_usb2 " ,
. of_match_table = rcar_gen3_phy_usb2_match_table ,
} ,
. probe = rcar_gen3_phy_usb2_probe ,
2016-11-09 05:30:25 +03:00
. remove = rcar_gen3_phy_usb2_remove ,
2015-11-30 04:44:30 +03:00
} ;
module_platform_driver ( rcar_gen3_phy_usb2_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Renesas R-Car Gen3 USB 2.0 PHY " ) ;
MODULE_AUTHOR ( " Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> " ) ;