2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-03-01 18:09:26 +01:00
/*
* Allwinner sun4i USB phy driver
*
2015-06-13 14:37:45 +02:00
* Copyright ( C ) 2014 - 2015 Hans de Goede < hdegoede @ redhat . com >
2014-03-01 18:09:26 +01:00
*
* Based on code from
* Allwinner Technology Co . , Ltd . < www . allwinnertech . com >
*
* Modelled after : Samsung S5P / EXYNOS SoC series MIPI CSIS / DSIM DPHY driver
* Copyright ( C ) 2013 Samsung Electronics Co . , Ltd .
* Author : Sylwester Nawrocki < s . nawrocki @ samsung . com >
*/
# include <linux/clk.h>
2015-06-13 14:37:50 +02:00
# include <linux/delay.h>
2014-05-29 12:00:49 +05:30
# include <linux/err.h>
2017-09-21 12:11:24 +09:00
# include <linux/extcon-provider.h>
2014-03-01 18:09:26 +01:00
# include <linux/io.h>
2015-06-13 14:37:45 +02:00
# include <linux/interrupt.h>
2014-03-01 18:09:26 +01:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/of_address.h>
2015-12-11 16:32:17 +01:00
# include <linux/of_device.h>
2015-06-13 14:37:45 +02:00
# include <linux/of_gpio.h>
2014-03-01 18:09:26 +01:00
# include <linux/phy/phy.h>
2015-03-29 12:50:46 +02:00
# include <linux/phy/phy-sun4i-usb.h>
2014-03-01 18:09:26 +01:00
# include <linux/platform_device.h>
2015-06-13 14:37:51 +02:00
# include <linux/power_supply.h>
2014-03-01 18:09:26 +01:00
# include <linux/regulator/consumer.h>
# include <linux/reset.h>
2016-09-09 11:58:18 +08:00
# include <linux/spinlock.h>
2016-08-08 21:55:39 +02:00
# include <linux/usb/of.h>
2015-06-13 14:37:45 +02:00
# include <linux/workqueue.h>
2014-03-01 18:09:26 +01:00
# define REG_ISCR 0x00
2015-06-13 14:37:49 +02:00
# define REG_PHYCTL_A10 0x04
2014-03-01 18:09:26 +01:00
# define REG_PHYBIST 0x08
# define REG_PHYTUNE 0x0c
2015-06-13 14:37:49 +02:00
# define REG_PHYCTL_A33 0x10
2017-03-25 22:50:11 +08:00
# define REG_PHY_OTGCTL 0x20
2015-12-11 16:32:18 +01:00
2016-08-12 11:06:21 +08:00
# define REG_PMU_UNK1 0x10
2014-03-01 18:09:26 +01:00
# define PHYCTL_DATA BIT(7)
2017-03-25 22:50:11 +08:00
# define OTGCTL_ROUTE_MUSB BIT(0)
2014-03-01 18:09:26 +01:00
# define SUNXI_AHB_ICHR8_EN BIT(10)
# define SUNXI_AHB_INCR4_BURST_EN BIT(9)
# define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
# define SUNXI_ULPI_BYPASS_EN BIT(0)
2015-06-13 14:37:45 +02:00
/* ISCR, Interface Status and Control bits */
# define ISCR_ID_PULLUP_EN (1 << 17)
# define ISCR_DPDM_PULLUP_EN (1 << 16)
/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
# define ISCR_FORCE_ID_MASK (3 << 14)
# define ISCR_FORCE_ID_LOW (2 << 14)
# define ISCR_FORCE_ID_HIGH (3 << 14)
# define ISCR_FORCE_VBUS_MASK (3 << 12)
# define ISCR_FORCE_VBUS_LOW (2 << 12)
# define ISCR_FORCE_VBUS_HIGH (3 << 12)
2014-03-01 18:09:26 +01:00
/* Common Control Bits for Both PHYs */
# define PHY_PLL_BW 0x03
# define PHY_RES45_CAL_EN 0x0c
/* Private Control Bits for Each PHY */
# define PHY_TX_AMPLITUDE_TUNE 0x20
# define PHY_TX_SLEWRATE_TUNE 0x22
# define PHY_VBUSVALID_TH_SEL 0x25
# define PHY_PULLUP_RES_SEL 0x27
# define PHY_OTG_FUNC_EN 0x28
# define PHY_VBUS_DET_EN 0x29
# define PHY_DISCON_TH_SEL 0x2a
2015-03-29 12:50:46 +02:00
# define PHY_SQUELCH_DETECT 0x3c
2014-03-01 18:09:26 +01:00
2017-08-03 16:14:07 +08:00
/* A83T specific control bits for PHY0 */
# define PHY_CTL_VBUSVLDEXT BIT(5)
# define PHY_CTL_SIDDQ BIT(3)
/* A83T specific control bits for PHY2 HSIC */
# define SUNXI_EHCI_HS_FORCE BIT(20)
# define SUNXI_HSIC_CONNECT_DET BIT(17)
# define SUNXI_HSIC_CONNECT_INT BIT(16)
# define SUNXI_HSIC BIT(1)
2015-12-11 16:32:18 +01:00
# define MAX_PHYS 4
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
/*
* Note do not raise the debounce time , we must report Vusb high within 100 ms
* otherwise we get Vbus errors
*/
# define DEBOUNCE_TIME msecs_to_jiffies(50)
# define POLL_TIME msecs_to_jiffies(250)
2015-12-11 16:32:17 +01:00
enum sun4i_usb_phy_type {
sun4i_a10_phy ,
2016-06-29 20:14:10 +02:00
sun6i_a31_phy ,
2015-12-11 16:32:17 +01:00
sun8i_a33_phy ,
2017-08-03 16:14:07 +08:00
sun8i_a83t_phy ,
2015-12-11 16:32:18 +01:00
sun8i_h3_phy ,
2018-01-03 16:49:44 +08:00
sun8i_r40_phy ,
2017-01-03 23:25:31 +08:00
sun8i_v3s_phy ,
2016-08-12 11:06:21 +08:00
sun50i_a64_phy ,
2018-10-04 20:28:48 +08:00
sun50i_h6_phy ,
2015-12-11 16:32:17 +01:00
} ;
struct sun4i_usb_phy_cfg {
int num_phys ;
2017-08-03 16:14:06 +08:00
int hsic_index ;
2015-12-11 16:32:17 +01:00
enum sun4i_usb_phy_type type ;
u32 disc_thresh ;
u8 phyctl_offset ;
bool dedicated_clocks ;
2016-08-12 11:06:21 +08:00
bool enable_pmu_unk1 ;
2017-03-25 22:50:11 +08:00
bool phy0_dual_route ;
2018-10-04 20:28:47 +08:00
int missing_phys ;
2015-12-11 16:32:17 +01:00
} ;
2014-03-01 18:09:26 +01:00
struct sun4i_usb_phy_data {
void __iomem * base ;
2015-12-11 16:32:17 +01:00
const struct sun4i_usb_phy_cfg * cfg ;
2016-08-08 21:55:39 +02:00
enum usb_dr_mode dr_mode ;
2016-09-09 11:58:18 +08:00
spinlock_t reg_lock ; /* guard access to phyctl reg */
2014-03-01 18:09:26 +01:00
struct sun4i_usb_phy {
struct phy * phy ;
void __iomem * pmu ;
struct regulator * vbus ;
struct reset_control * reset ;
2014-05-13 17:44:18 +02:00
struct clk * clk ;
2017-08-03 16:14:06 +08:00
struct clk * clk2 ;
2015-06-13 14:37:45 +02:00
bool regulator_on ;
2014-03-01 18:09:26 +01:00
int index ;
} phys [ MAX_PHYS ] ;
2015-06-13 14:37:45 +02:00
/* phy0 / otg related variables */
2015-06-13 14:37:46 +02:00
struct extcon_dev * extcon ;
2015-06-13 14:37:45 +02:00
bool phy0_init ;
struct gpio_desc * id_det_gpio ;
struct gpio_desc * vbus_det_gpio ;
2015-06-13 14:37:51 +02:00
struct power_supply * vbus_power_supply ;
struct notifier_block vbus_power_nb ;
bool vbus_power_nb_registered ;
2016-09-07 22:20:38 +02:00
bool force_session_end ;
2015-06-13 14:37:45 +02:00
int id_det_irq ;
int vbus_det_irq ;
int id_det ;
int vbus_det ;
struct delayed_work detect ;
2014-03-01 18:09:26 +01:00
} ;
# define to_sun4i_usb_phy_data(phy) \
container_of ( ( phy ) , struct sun4i_usb_phy_data , phys [ ( phy ) - > index ] )
2015-06-13 14:37:45 +02:00
static void sun4i_usb_phy0_update_iscr ( struct phy * _phy , u32 clr , u32 set )
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
struct sun4i_usb_phy_data * data = to_sun4i_usb_phy_data ( phy ) ;
u32 iscr ;
iscr = readl ( data - > base + REG_ISCR ) ;
iscr & = ~ clr ;
iscr | = set ;
writel ( iscr , data - > base + REG_ISCR ) ;
}
static void sun4i_usb_phy0_set_id_detect ( struct phy * phy , u32 val )
{
if ( val )
val = ISCR_FORCE_ID_HIGH ;
else
val = ISCR_FORCE_ID_LOW ;
sun4i_usb_phy0_update_iscr ( phy , ISCR_FORCE_ID_MASK , val ) ;
}
static void sun4i_usb_phy0_set_vbus_detect ( struct phy * phy , u32 val )
{
if ( val )
val = ISCR_FORCE_VBUS_HIGH ;
else
val = ISCR_FORCE_VBUS_LOW ;
sun4i_usb_phy0_update_iscr ( phy , ISCR_FORCE_VBUS_MASK , val ) ;
}
2014-03-01 18:09:26 +01:00
static void sun4i_usb_phy_write ( struct sun4i_usb_phy * phy , u32 addr , u32 data ,
int len )
{
struct sun4i_usb_phy_data * phy_data = to_sun4i_usb_phy_data ( phy ) ;
u32 temp , usbc_bit = BIT ( phy - > index * 2 ) ;
2016-06-07 18:14:56 +01:00
void __iomem * phyctl = phy_data - > base + phy_data - > cfg - > phyctl_offset ;
2016-09-09 11:58:18 +08:00
unsigned long flags ;
2014-03-01 18:09:26 +01:00
int i ;
2016-09-09 11:58:18 +08:00
spin_lock_irqsave ( & phy_data - > reg_lock , flags ) ;
2014-03-01 18:09:26 +01:00
2017-03-25 22:50:09 +08:00
if ( phy_data - > cfg - > phyctl_offset = = REG_PHYCTL_A33 ) {
/* SoCs newer than A33 need us to set phyctl to 0 explicitly */
2015-06-13 14:37:49 +02:00
writel ( 0 , phyctl ) ;
}
2014-03-01 18:09:26 +01:00
for ( i = 0 ; i < len ; i + + ) {
2015-06-13 14:37:49 +02:00
temp = readl ( phyctl ) ;
2014-03-01 18:09:26 +01:00
/* clear the address portion */
temp & = ~ ( 0xff < < 8 ) ;
/* set the address */
temp | = ( ( addr + i ) < < 8 ) ;
2015-06-13 14:37:49 +02:00
writel ( temp , phyctl ) ;
2014-03-01 18:09:26 +01:00
/* set the data bit and clear usbc bit*/
2015-06-13 14:37:49 +02:00
temp = readb ( phyctl ) ;
2014-03-01 18:09:26 +01:00
if ( data & 0x1 )
temp | = PHYCTL_DATA ;
else
temp & = ~ PHYCTL_DATA ;
temp & = ~ usbc_bit ;
2015-06-13 14:37:49 +02:00
writeb ( temp , phyctl ) ;
2014-03-01 18:09:26 +01:00
/* pulse usbc_bit */
2015-06-13 14:37:49 +02:00
temp = readb ( phyctl ) ;
2014-03-01 18:09:26 +01:00
temp | = usbc_bit ;
2015-06-13 14:37:49 +02:00
writeb ( temp , phyctl ) ;
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:49 +02:00
temp = readb ( phyctl ) ;
2014-03-01 18:09:26 +01:00
temp & = ~ usbc_bit ;
2015-06-13 14:37:49 +02:00
writeb ( temp , phyctl ) ;
2014-03-01 18:09:26 +01:00
data > > = 1 ;
}
2016-09-09 11:58:18 +08:00
spin_unlock_irqrestore ( & phy_data - > reg_lock , flags ) ;
2014-03-01 18:09:26 +01:00
}
static void sun4i_usb_phy_passby ( struct sun4i_usb_phy * phy , int enable )
{
2017-08-03 16:14:07 +08:00
struct sun4i_usb_phy_data * phy_data = to_sun4i_usb_phy_data ( phy ) ;
2014-03-01 18:09:26 +01:00
u32 bits , reg_value ;
if ( ! phy - > pmu )
return ;
bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN ;
2017-08-03 16:14:07 +08:00
/* A83T USB2 is HSIC */
if ( phy_data - > cfg - > type = = sun8i_a83t_phy & & phy - > index = = 2 )
bits | = SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT |
SUNXI_HSIC ;
2014-03-01 18:09:26 +01:00
reg_value = readl ( phy - > pmu ) ;
if ( enable )
reg_value | = bits ;
else
reg_value & = ~ bits ;
writel ( reg_value , phy - > pmu ) ;
}
static int sun4i_usb_phy_init ( struct phy * _phy )
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
struct sun4i_usb_phy_data * data = to_sun4i_usb_phy_data ( phy ) ;
int ret ;
2015-12-11 16:32:18 +01:00
u32 val ;
2014-03-01 18:09:26 +01:00
2014-05-13 17:44:18 +02:00
ret = clk_prepare_enable ( phy - > clk ) ;
2014-03-01 18:09:26 +01:00
if ( ret )
return ret ;
2017-08-03 16:14:06 +08:00
ret = clk_prepare_enable ( phy - > clk2 ) ;
if ( ret ) {
clk_disable_unprepare ( phy - > clk ) ;
return ret ;
}
2014-03-01 18:09:26 +01:00
ret = reset_control_deassert ( phy - > reset ) ;
if ( ret ) {
2017-08-03 16:14:06 +08:00
clk_disable_unprepare ( phy - > clk2 ) ;
2014-05-13 17:44:18 +02:00
clk_disable_unprepare ( phy - > clk ) ;
2014-03-01 18:09:26 +01:00
return ret ;
}
2018-10-04 20:28:48 +08:00
if ( data - > cfg - > type = = sun8i_a83t_phy | |
data - > cfg - > type = = sun50i_h6_phy ) {
2017-08-03 16:14:07 +08:00
if ( phy - > index = = 0 ) {
val = readl ( data - > base + data - > cfg - > phyctl_offset ) ;
val | = PHY_CTL_VBUSVLDEXT ;
val & = ~ PHY_CTL_SIDDQ ;
writel ( val , data - > base + data - > cfg - > phyctl_offset ) ;
}
} else {
if ( phy - > pmu & & data - > cfg - > enable_pmu_unk1 ) {
val = readl ( phy - > pmu + REG_PMU_UNK1 ) ;
writel ( val & ~ 2 , phy - > pmu + REG_PMU_UNK1 ) ;
}
2016-08-12 11:06:21 +08:00
2017-08-03 16:14:07 +08:00
/* Enable USB 45 Ohm resistor calibration */
if ( phy - > index = = 0 )
sun4i_usb_phy_write ( phy , PHY_RES45_CAL_EN , 0x01 , 1 ) ;
2014-11-10 19:55:06 +01:00
2017-08-03 16:14:07 +08:00
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write ( phy , PHY_TX_AMPLITUDE_TUNE , 0x14 , 5 ) ;
2014-03-01 18:09:26 +01:00
2017-08-03 16:14:07 +08:00
/* Disconnect threshold adjustment */
sun4i_usb_phy_write ( phy , PHY_DISCON_TH_SEL ,
data - > cfg - > disc_thresh , 2 ) ;
}
2014-03-01 18:09:26 +01:00
sun4i_usb_phy_passby ( phy , 1 ) ;
2015-06-13 14:37:45 +02:00
if ( phy - > index = = 0 ) {
data - > phy0_init = true ;
/* Enable pull-ups */
sun4i_usb_phy0_update_iscr ( _phy , 0 , ISCR_DPDM_PULLUP_EN ) ;
sun4i_usb_phy0_update_iscr ( _phy , 0 , ISCR_ID_PULLUP_EN ) ;
2016-08-08 21:55:39 +02:00
/* Force ISCR and cable state updates */
data - > id_det = - 1 ;
data - > vbus_det = - 1 ;
queue_delayed_work ( system_wq , & data - > detect , 0 ) ;
2015-06-13 14:37:45 +02:00
}
2014-03-01 18:09:26 +01:00
return 0 ;
}
static int sun4i_usb_phy_exit ( struct phy * _phy )
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
2015-06-13 14:37:45 +02:00
struct sun4i_usb_phy_data * data = to_sun4i_usb_phy_data ( phy ) ;
if ( phy - > index = = 0 ) {
2018-10-04 20:28:48 +08:00
if ( data - > cfg - > type = = sun8i_a83t_phy | |
data - > cfg - > type = = sun50i_h6_phy ) {
2017-08-03 16:14:07 +08:00
void __iomem * phyctl = data - > base +
data - > cfg - > phyctl_offset ;
writel ( readl ( phyctl ) | PHY_CTL_SIDDQ , phyctl ) ;
}
2015-06-13 14:37:45 +02:00
/* Disable pull-ups */
sun4i_usb_phy0_update_iscr ( _phy , ISCR_DPDM_PULLUP_EN , 0 ) ;
sun4i_usb_phy0_update_iscr ( _phy , ISCR_ID_PULLUP_EN , 0 ) ;
data - > phy0_init = false ;
}
2014-03-01 18:09:26 +01:00
sun4i_usb_phy_passby ( phy , 0 ) ;
reset_control_assert ( phy - > reset ) ;
2017-08-03 16:14:06 +08:00
clk_disable_unprepare ( phy - > clk2 ) ;
2014-05-13 17:44:18 +02:00
clk_disable_unprepare ( phy - > clk ) ;
2014-03-01 18:09:26 +01:00
return 0 ;
}
2016-08-08 21:55:39 +02:00
static int sun4i_usb_phy0_get_id_det ( struct sun4i_usb_phy_data * data )
{
switch ( data - > dr_mode ) {
case USB_DR_MODE_OTG :
2016-09-07 22:20:39 +02:00
if ( data - > id_det_gpio )
return gpiod_get_value_cansleep ( data - > id_det_gpio ) ;
else
return 1 ; /* Fallback to peripheral mode */
2016-08-08 21:55:39 +02:00
case USB_DR_MODE_HOST :
return 0 ;
case USB_DR_MODE_PERIPHERAL :
default :
return 1 ;
}
}
2015-07-31 10:01:41 +02:00
static int sun4i_usb_phy0_get_vbus_det ( struct sun4i_usb_phy_data * data )
{
if ( data - > vbus_det_gpio )
return gpiod_get_value_cansleep ( data - > vbus_det_gpio ) ;
if ( data - > vbus_power_supply ) {
union power_supply_propval val ;
int r ;
r = power_supply_get_property ( data - > vbus_power_supply ,
POWER_SUPPLY_PROP_PRESENT , & val ) ;
if ( r = = 0 )
return val . intval ;
}
/* Fallback: report vbus as high */
return 1 ;
}
static bool sun4i_usb_phy0_have_vbus_det ( struct sun4i_usb_phy_data * data )
{
return data - > vbus_det_gpio | | data - > vbus_power_supply ;
}
2016-06-29 20:14:10 +02:00
static bool sun4i_usb_phy0_poll ( struct sun4i_usb_phy_data * data )
{
if ( ( data - > id_det_gpio & & data - > id_det_irq < = 0 ) | |
( data - > vbus_det_gpio & & data - > vbus_det_irq < = 0 ) )
return true ;
/*
2018-01-19 17:25:41 +08:00
* The A31 / A23 / A33 companion pmics ( AXP221 / AXP223 ) do not
* generate vbus change interrupts when the board is driving
* vbus using the N_VBUSEN pin on the pmic , so we must poll
2016-06-29 20:14:10 +02:00
* when using the pmic for vbus - det _and_ we ' re driving vbus .
*/
2018-01-19 17:25:41 +08:00
if ( ( data - > cfg - > type = = sun6i_a31_phy | |
data - > cfg - > type = = sun8i_a33_phy ) & &
2016-06-29 20:14:10 +02:00
data - > vbus_power_supply & & data - > phys [ 0 ] . regulator_on )
return true ;
return false ;
}
2014-03-01 18:09:26 +01:00
static int sun4i_usb_phy_power_on ( struct phy * _phy )
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
2015-06-13 14:37:45 +02:00
struct sun4i_usb_phy_data * data = to_sun4i_usb_phy_data ( phy ) ;
int ret ;
if ( ! phy - > vbus | | phy - > regulator_on )
return 0 ;
/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
2015-07-31 10:01:42 +02:00
if ( phy - > index = = 0 & & sun4i_usb_phy0_have_vbus_det ( data ) & &
2016-09-07 22:20:41 +02:00
data - > vbus_det ) {
dev_warn ( & _phy - > dev , " External vbus detected, not enabling our own vbus \n " ) ;
2015-06-13 14:37:45 +02:00
return 0 ;
2016-09-07 22:20:41 +02:00
}
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
ret = regulator_enable ( phy - > vbus ) ;
if ( ret )
return ret ;
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
phy - > regulator_on = true ;
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
2016-06-29 20:14:10 +02:00
if ( phy - > index = = 0 & & sun4i_usb_phy0_poll ( data ) )
2015-06-13 14:37:45 +02:00
mod_delayed_work ( system_wq , & data - > detect , DEBOUNCE_TIME ) ;
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
return 0 ;
2014-03-01 18:09:26 +01:00
}
static int sun4i_usb_phy_power_off ( struct phy * _phy )
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
2015-06-13 14:37:45 +02:00
struct sun4i_usb_phy_data * data = to_sun4i_usb_phy_data ( phy ) ;
if ( ! phy - > vbus | | ! phy - > regulator_on )
return 0 ;
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
regulator_disable ( phy - > vbus ) ;
phy - > regulator_on = false ;
2014-03-01 18:09:26 +01:00
2015-06-13 14:37:45 +02:00
/*
* phy0 vbus typically slowly discharges , sometimes this causes the
* Vbus gpio to not trigger an edge irq on Vbus off , so force a rescan .
*/
2016-06-29 20:14:10 +02:00
if ( phy - > index = = 0 & & ! sun4i_usb_phy0_poll ( data ) )
2015-06-13 14:37:45 +02:00
mod_delayed_work ( system_wq , & data - > detect , POLL_TIME ) ;
2014-03-01 18:09:26 +01:00
return 0 ;
}
2018-11-19 19:24:20 -06:00
static int sun4i_usb_phy_set_mode ( struct phy * _phy ,
enum phy_mode mode , int submode )
2016-09-07 22:20:40 +02:00
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
struct sun4i_usb_phy_data * data = to_sun4i_usb_phy_data ( phy ) ;
2016-09-23 16:40:56 +03:00
int new_mode ;
2016-09-07 22:20:40 +02:00
2019-03-22 16:51:07 +08:00
if ( phy - > index ! = 0 ) {
if ( mode = = PHY_MODE_USB_HOST )
return 0 ;
2016-09-07 22:20:40 +02:00
return - EINVAL ;
2019-03-22 16:51:07 +08:00
}
2016-09-07 22:20:40 +02:00
switch ( mode ) {
case PHY_MODE_USB_HOST :
2016-09-23 16:40:56 +03:00
new_mode = USB_DR_MODE_HOST ;
2016-09-07 22:20:40 +02:00
break ;
case PHY_MODE_USB_DEVICE :
2016-09-23 16:40:56 +03:00
new_mode = USB_DR_MODE_PERIPHERAL ;
2016-09-07 22:20:40 +02:00
break ;
case PHY_MODE_USB_OTG :
2016-09-23 16:40:56 +03:00
new_mode = USB_DR_MODE_OTG ;
2016-09-07 22:20:40 +02:00
break ;
default :
return - EINVAL ;
}
2016-09-23 16:40:56 +03:00
if ( new_mode ! = data - > dr_mode ) {
dev_info ( & _phy - > dev , " Changing dr_mode to %d \n " , new_mode ) ;
data - > dr_mode = new_mode ;
}
data - > id_det = - 1 ; /* Force reprocessing of id */
2016-09-07 22:20:40 +02:00
data - > force_session_end = true ;
queue_delayed_work ( system_wq , & data - > detect , 0 ) ;
return 0 ;
}
2015-03-29 12:50:46 +02:00
void sun4i_usb_phy_set_squelch_detect ( struct phy * _phy , bool enabled )
{
struct sun4i_usb_phy * phy = phy_get_drvdata ( _phy ) ;
sun4i_usb_phy_write ( phy , PHY_SQUELCH_DETECT , enabled ? 0 : 2 , 2 ) ;
}
2015-07-31 10:01:40 +02:00
EXPORT_SYMBOL_GPL ( sun4i_usb_phy_set_squelch_detect ) ;
2015-03-29 12:50:46 +02:00
2015-07-15 15:33:51 +08:00
static const struct phy_ops sun4i_usb_phy_ops = {
2014-03-01 18:09:26 +01:00
. init = sun4i_usb_phy_init ,
. exit = sun4i_usb_phy_exit ,
. power_on = sun4i_usb_phy_power_on ,
. power_off = sun4i_usb_phy_power_off ,
2016-09-07 22:20:40 +02:00
. set_mode = sun4i_usb_phy_set_mode ,
2014-03-01 18:09:26 +01:00
. owner = THIS_MODULE ,
} ;
2017-03-25 22:50:11 +08:00
static void sun4i_usb_phy0_reroute ( struct sun4i_usb_phy_data * data , int id_det )
{
u32 regval ;
regval = readl ( data - > base + REG_PHY_OTGCTL ) ;
if ( id_det = = 0 ) {
/* Host mode. Route phy0 to EHCI/OHCI */
regval & = ~ OTGCTL_ROUTE_MUSB ;
} else {
/* Peripheral mode. Route phy0 to MUSB */
regval | = OTGCTL_ROUTE_MUSB ;
}
writel ( regval , data - > base + REG_PHY_OTGCTL ) ;
}
2015-06-13 14:37:45 +02:00
static void sun4i_usb_phy0_id_vbus_det_scan ( struct work_struct * work )
{
struct sun4i_usb_phy_data * data =
container_of ( work , struct sun4i_usb_phy_data , detect . work ) ;
struct phy * phy0 = data - > phys [ 0 ] . phy ;
2019-03-14 14:05:18 +01:00
struct sun4i_usb_phy * phy = phy_get_drvdata ( phy0 ) ;
2016-09-07 22:20:38 +02:00
bool force_session_end , id_notify = false , vbus_notify = false ;
2016-09-07 22:20:37 +02:00
int id_det , vbus_det ;
2015-06-13 14:37:45 +02:00
2016-08-08 21:55:39 +02:00
if ( phy0 = = NULL )
return ;
id_det = sun4i_usb_phy0_get_id_det ( data ) ;
2015-06-13 14:37:51 +02:00
vbus_det = sun4i_usb_phy0_get_vbus_det ( data ) ;
2015-06-13 14:37:45 +02:00
mutex_lock ( & phy0 - > mutex ) ;
if ( ! data - > phy0_init ) {
mutex_unlock ( & phy0 - > mutex ) ;
return ;
}
2016-09-07 22:20:38 +02:00
force_session_end = data - > force_session_end ;
data - > force_session_end = false ;
2015-06-13 14:37:45 +02:00
if ( id_det ! = data - > id_det ) {
2016-09-07 22:20:38 +02:00
/* id-change, force session end if we've no vbus detection */
2016-08-08 21:55:39 +02:00
if ( data - > dr_mode = = USB_DR_MODE_OTG & &
2016-09-07 22:20:38 +02:00
! sun4i_usb_phy0_have_vbus_det ( data ) )
force_session_end = true ;
/* When entering host mode (id = 0) force end the session now */
if ( force_session_end & & id_det = = 0 ) {
2015-06-13 14:37:50 +02:00
sun4i_usb_phy0_set_vbus_detect ( phy0 , 0 ) ;
msleep ( 200 ) ;
sun4i_usb_phy0_set_vbus_detect ( phy0 , 1 ) ;
}
2015-06-13 14:37:45 +02:00
sun4i_usb_phy0_set_id_detect ( phy0 , id_det ) ;
data - > id_det = id_det ;
2016-09-07 22:20:37 +02:00
id_notify = true ;
2015-06-13 14:37:45 +02:00
}
if ( vbus_det ! = data - > vbus_det ) {
sun4i_usb_phy0_set_vbus_detect ( phy0 , vbus_det ) ;
data - > vbus_det = vbus_det ;
2016-09-07 22:20:37 +02:00
vbus_notify = true ;
2015-06-13 14:37:45 +02:00
}
mutex_unlock ( & phy0 - > mutex ) ;
2015-06-13 14:37:50 +02:00
if ( id_notify ) {
2016-12-30 13:11:29 +09:00
extcon_set_state_sync ( data - > extcon , EXTCON_USB_HOST ,
2015-06-13 14:37:46 +02:00
! id_det ) ;
2016-09-07 22:20:38 +02:00
/* When leaving host mode force end the session here */
if ( force_session_end & & id_det = = 1 ) {
2015-06-13 14:37:50 +02:00
mutex_lock ( & phy0 - > mutex ) ;
sun4i_usb_phy0_set_vbus_detect ( phy0 , 0 ) ;
msleep ( 1000 ) ;
sun4i_usb_phy0_set_vbus_detect ( phy0 , 1 ) ;
mutex_unlock ( & phy0 - > mutex ) ;
}
2017-03-25 22:50:11 +08:00
2019-03-14 14:05:18 +01:00
/* Enable PHY0 passby for host mode only. */
sun4i_usb_phy_passby ( phy , ! id_det ) ;
2017-03-25 22:50:11 +08:00
/* Re-route PHY0 if necessary */
if ( data - > cfg - > phy0_dual_route )
sun4i_usb_phy0_reroute ( data , id_det ) ;
2015-06-13 14:37:50 +02:00
}
2015-06-13 14:37:46 +02:00
if ( vbus_notify )
2016-12-30 13:11:29 +09:00
extcon_set_state_sync ( data - > extcon , EXTCON_USB , vbus_det ) ;
2015-06-13 14:37:46 +02:00
2016-06-29 20:14:10 +02:00
if ( sun4i_usb_phy0_poll ( data ) )
2015-06-13 14:37:45 +02:00
queue_delayed_work ( system_wq , & data - > detect , POLL_TIME ) ;
}
static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq ( int irq , void * dev_id )
{
struct sun4i_usb_phy_data * data = dev_id ;
/* vbus or id changed, let the pins settle and then scan them */
mod_delayed_work ( system_wq , & data - > detect , DEBOUNCE_TIME ) ;
return IRQ_HANDLED ;
}
2015-06-13 14:37:51 +02:00
static int sun4i_usb_phy0_vbus_notify ( struct notifier_block * nb ,
unsigned long val , void * v )
{
struct sun4i_usb_phy_data * data =
container_of ( nb , struct sun4i_usb_phy_data , vbus_power_nb ) ;
struct power_supply * psy = v ;
/* Properties on the vbus_power_supply changed, scan vbus_det */
if ( val = = PSY_EVENT_PROP_CHANGED & & psy = = data - > vbus_power_supply )
mod_delayed_work ( system_wq , & data - > detect , DEBOUNCE_TIME ) ;
return NOTIFY_OK ;
}
2014-03-01 18:09:26 +01:00
static struct phy * sun4i_usb_phy_xlate ( struct device * dev ,
struct of_phandle_args * args )
{
struct sun4i_usb_phy_data * data = dev_get_drvdata ( dev ) ;
2016-09-07 22:20:39 +02:00
if ( args - > args [ 0 ] > = data - > cfg - > num_phys )
2014-03-01 18:09:26 +01:00
return ERR_PTR ( - ENODEV ) ;
2018-10-04 20:28:47 +08:00
if ( data - > cfg - > missing_phys & BIT ( args - > args [ 0 ] ) )
return ERR_PTR ( - ENODEV ) ;
2014-03-01 18:09:26 +01:00
return data - > phys [ args - > args [ 0 ] ] . phy ;
}
2015-06-13 14:37:45 +02:00
static int sun4i_usb_phy_remove ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct sun4i_usb_phy_data * data = dev_get_drvdata ( dev ) ;
2015-06-13 14:37:51 +02:00
if ( data - > vbus_power_nb_registered )
power_supply_unreg_notifier ( & data - > vbus_power_nb ) ;
2016-06-18 11:31:33 +02:00
if ( data - > id_det_irq > 0 )
2015-06-13 14:37:45 +02:00
devm_free_irq ( dev , data - > id_det_irq , data ) ;
2016-06-18 11:31:33 +02:00
if ( data - > vbus_det_irq > 0 )
2015-06-13 14:37:45 +02:00
devm_free_irq ( dev , data - > vbus_det_irq , data ) ;
cancel_delayed_work_sync ( & data - > detect ) ;
return 0 ;
}
2015-06-13 14:37:46 +02:00
static const unsigned int sun4i_usb_phy0_cable [ ] = {
EXTCON_USB ,
EXTCON_USB_HOST ,
EXTCON_NONE ,
} ;
2014-03-01 18:09:26 +01:00
static int sun4i_usb_phy_probe ( struct platform_device * pdev )
{
struct sun4i_usb_phy_data * data ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct phy_provider * phy_provider ;
struct resource * res ;
2015-06-13 14:37:45 +02:00
int i , ret ;
2014-03-01 18:09:26 +01:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2016-09-09 11:58:18 +08:00
spin_lock_init ( & data - > reg_lock ) ;
2015-06-13 14:37:45 +02:00
INIT_DELAYED_WORK ( & data - > detect , sun4i_usb_phy0_id_vbus_det_scan ) ;
dev_set_drvdata ( dev , data ) ;
2015-12-11 16:32:17 +01:00
data - > cfg = of_device_get_match_data ( dev ) ;
if ( ! data - > cfg )
return - EINVAL ;
2015-06-13 14:37:49 +02:00
2014-03-01 18:09:26 +01:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " phy_ctrl " ) ;
data - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( data - > base ) )
return PTR_ERR ( data - > base ) ;
2015-09-25 08:47:29 +08:00
data - > id_det_gpio = devm_gpiod_get_optional ( dev , " usb0_id_det " ,
GPIOD_IN ) ;
2017-07-04 14:37:03 +02:00
if ( IS_ERR ( data - > id_det_gpio ) ) {
dev_err ( dev , " Couldn't request ID GPIO \n " ) ;
2015-09-25 08:47:29 +08:00
return PTR_ERR ( data - > id_det_gpio ) ;
2017-07-04 14:37:03 +02:00
}
2015-09-25 08:47:29 +08:00
data - > vbus_det_gpio = devm_gpiod_get_optional ( dev , " usb0_vbus_det " ,
GPIOD_IN ) ;
2017-07-04 14:37:03 +02:00
if ( IS_ERR ( data - > vbus_det_gpio ) ) {
dev_err ( dev , " Couldn't request VBUS detect GPIO \n " ) ;
2015-09-25 08:47:29 +08:00
return PTR_ERR ( data - > vbus_det_gpio ) ;
2017-07-04 14:37:03 +02:00
}
2015-06-13 14:37:45 +02:00
2015-06-13 14:37:51 +02:00
if ( of_find_property ( np , " usb0_vbus_power-supply " , NULL ) ) {
data - > vbus_power_supply = devm_power_supply_get_by_phandle ( dev ,
" usb0_vbus_power-supply " ) ;
2017-07-04 14:37:03 +02:00
if ( IS_ERR ( data - > vbus_power_supply ) ) {
dev_err ( dev , " Couldn't get the VBUS power supply \n " ) ;
2015-06-13 14:37:51 +02:00
return PTR_ERR ( data - > vbus_power_supply ) ;
2017-07-04 14:37:03 +02:00
}
2015-06-13 14:37:51 +02:00
if ( ! data - > vbus_power_supply )
return - EPROBE_DEFER ;
}
2016-08-08 21:55:39 +02:00
data - > dr_mode = of_usb_get_dr_mode_by_phy ( np , 0 ) ;
2016-09-07 22:20:39 +02:00
data - > extcon = devm_extcon_dev_allocate ( dev , sun4i_usb_phy0_cable ) ;
2017-07-04 14:37:03 +02:00
if ( IS_ERR ( data - > extcon ) ) {
dev_err ( dev , " Couldn't allocate our extcon device \n " ) ;
2016-09-07 22:20:39 +02:00
return PTR_ERR ( data - > extcon ) ;
2017-07-04 14:37:03 +02:00
}
2016-09-07 22:20:39 +02:00
ret = devm_extcon_dev_register ( dev , data - > extcon ) ;
if ( ret ) {
dev_err ( dev , " failed to register extcon: %d \n " , ret ) ;
return ret ;
2015-06-13 14:37:46 +02:00
}
2016-09-07 22:20:39 +02:00
for ( i = 0 ; i < data - > cfg - > num_phys ; i + + ) {
2014-05-13 17:44:17 +02:00
struct sun4i_usb_phy * phy = data - > phys + i ;
char name [ 16 ] ;
2018-10-04 20:28:47 +08:00
if ( data - > cfg - > missing_phys & BIT ( i ) )
continue ;
2014-03-01 18:09:26 +01:00
snprintf ( name , sizeof ( name ) , " usb%d_vbus " , i ) ;
2014-05-13 17:44:17 +02:00
phy - > vbus = devm_regulator_get_optional ( dev , name ) ;
if ( IS_ERR ( phy - > vbus ) ) {
2017-07-04 14:37:03 +02:00
if ( PTR_ERR ( phy - > vbus ) = = - EPROBE_DEFER ) {
dev_err ( dev ,
" Couldn't get regulator %s... Deferring probe \n " ,
name ) ;
2014-03-01 18:09:26 +01:00
return - EPROBE_DEFER ;
2017-07-04 14:37:03 +02:00
}
2014-05-13 17:44:17 +02:00
phy - > vbus = NULL ;
2014-03-01 18:09:26 +01:00
}
2015-12-11 16:32:17 +01:00
if ( data - > cfg - > dedicated_clocks )
2014-05-13 17:44:18 +02:00
snprintf ( name , sizeof ( name ) , " usb%d_phy " , i ) ;
else
strlcpy ( name , " usb_phy " , sizeof ( name ) ) ;
phy - > clk = devm_clk_get ( dev , name ) ;
if ( IS_ERR ( phy - > clk ) ) {
dev_err ( dev , " failed to get clock %s \n " , name ) ;
return PTR_ERR ( phy - > clk ) ;
}
2017-08-03 16:14:06 +08:00
/* The first PHY is always tied to OTG, and never HSIC */
if ( data - > cfg - > hsic_index & & i = = data - > cfg - > hsic_index ) {
/* HSIC needs secondary clock */
snprintf ( name , sizeof ( name ) , " usb%d_hsic_12M " , i ) ;
phy - > clk2 = devm_clk_get ( dev , name ) ;
if ( IS_ERR ( phy - > clk2 ) ) {
dev_err ( dev , " failed to get clock %s \n " , name ) ;
return PTR_ERR ( phy - > clk2 ) ;
}
}
2014-03-01 18:09:26 +01:00
snprintf ( name , sizeof ( name ) , " usb%d_reset " , i ) ;
2014-05-13 17:44:17 +02:00
phy - > reset = devm_reset_control_get ( dev , name ) ;
if ( IS_ERR ( phy - > reset ) ) {
2014-03-01 18:09:26 +01:00
dev_err ( dev , " failed to get reset %s \n " , name ) ;
2014-05-13 17:44:17 +02:00
return PTR_ERR ( phy - > reset ) ;
2014-03-01 18:09:26 +01:00
}
2017-03-25 22:50:11 +08:00
if ( i | | data - > cfg - > phy0_dual_route ) { /* No pmu for musb */
2014-03-01 18:09:26 +01:00
snprintf ( name , sizeof ( name ) , " pmu%d " , i ) ;
res = platform_get_resource_byname ( pdev ,
IORESOURCE_MEM , name ) ;
2014-05-13 17:44:17 +02:00
phy - > pmu = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( phy - > pmu ) )
return PTR_ERR ( phy - > pmu ) ;
2014-03-01 18:09:26 +01:00
}
2014-11-19 17:28:21 +02:00
phy - > phy = devm_phy_create ( dev , NULL , & sun4i_usb_phy_ops ) ;
2014-05-13 17:44:17 +02:00
if ( IS_ERR ( phy - > phy ) ) {
2014-03-01 18:09:26 +01:00
dev_err ( dev , " failed to create PHY %d \n " , i ) ;
2014-05-13 17:44:17 +02:00
return PTR_ERR ( phy - > phy ) ;
2014-03-01 18:09:26 +01:00
}
2014-05-13 17:44:17 +02:00
phy - > index = i ;
phy_set_drvdata ( phy - > phy , & data - > phys [ i ] ) ;
2014-03-01 18:09:26 +01:00
}
2015-06-13 14:37:45 +02:00
data - > id_det_irq = gpiod_to_irq ( data - > id_det_gpio ) ;
2016-06-13 13:45:48 +02:00
if ( data - > id_det_irq > 0 ) {
2015-06-13 14:37:45 +02:00
ret = devm_request_irq ( dev , data - > id_det_irq ,
sun4i_usb_phy0_id_vbus_det_irq ,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
" usb0-id-det " , data ) ;
if ( ret ) {
dev_err ( dev , " Err requesting id-det-irq: %d \n " , ret ) ;
return ret ;
}
}
2016-06-29 20:14:10 +02:00
data - > vbus_det_irq = gpiod_to_irq ( data - > vbus_det_gpio ) ;
2016-06-13 13:45:48 +02:00
if ( data - > vbus_det_irq > 0 ) {
2015-06-13 14:37:45 +02:00
ret = devm_request_irq ( dev , data - > vbus_det_irq ,
sun4i_usb_phy0_id_vbus_det_irq ,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
" usb0-vbus-det " , data ) ;
if ( ret ) {
dev_err ( dev , " Err requesting vbus-det-irq: %d \n " , ret ) ;
data - > vbus_det_irq = - 1 ;
sun4i_usb_phy_remove ( pdev ) ; /* Stop detect work */
return ret ;
}
}
2015-06-13 14:37:51 +02:00
if ( data - > vbus_power_supply ) {
data - > vbus_power_nb . notifier_call = sun4i_usb_phy0_vbus_notify ;
data - > vbus_power_nb . priority = 0 ;
ret = power_supply_reg_notifier ( & data - > vbus_power_nb ) ;
if ( ret ) {
sun4i_usb_phy_remove ( pdev ) ; /* Stop detect work */
return ret ;
}
data - > vbus_power_nb_registered = true ;
}
2014-03-01 18:09:26 +01:00
phy_provider = devm_of_phy_provider_register ( dev , sun4i_usb_phy_xlate ) ;
2015-06-13 14:37:45 +02:00
if ( IS_ERR ( phy_provider ) ) {
sun4i_usb_phy_remove ( pdev ) ; /* Stop detect work */
return PTR_ERR ( phy_provider ) ;
}
2014-03-01 18:09:26 +01:00
2017-07-04 14:37:03 +02:00
dev_dbg ( dev , " successfully loaded \n " ) ;
2015-06-13 14:37:45 +02:00
return 0 ;
2014-03-01 18:09:26 +01:00
}
2015-12-11 16:32:17 +01:00
static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
. num_phys = 3 ,
. type = sun4i_a10_phy ,
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A10 ,
. dedicated_clocks = false ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = false ,
2015-12-11 16:32:17 +01:00
} ;
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
. num_phys = 2 ,
. type = sun4i_a10_phy ,
. disc_thresh = 2 ,
. phyctl_offset = REG_PHYCTL_A10 ,
. dedicated_clocks = false ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = false ,
2015-12-11 16:32:17 +01:00
} ;
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
. num_phys = 3 ,
2016-06-29 20:14:10 +02:00
. type = sun6i_a31_phy ,
2015-12-11 16:32:17 +01:00
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A10 ,
. dedicated_clocks = true ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = false ,
2015-12-11 16:32:17 +01:00
} ;
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
. num_phys = 3 ,
. type = sun4i_a10_phy ,
. disc_thresh = 2 ,
. phyctl_offset = REG_PHYCTL_A10 ,
. dedicated_clocks = false ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = false ,
2015-12-11 16:32:17 +01:00
} ;
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
. num_phys = 2 ,
2018-01-19 17:25:41 +08:00
. type = sun6i_a31_phy ,
2015-12-11 16:32:17 +01:00
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A10 ,
. dedicated_clocks = true ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = false ,
2015-12-11 16:32:17 +01:00
} ;
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
. num_phys = 2 ,
. type = sun8i_a33_phy ,
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A33 ,
. dedicated_clocks = true ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = false ,
2015-12-11 16:32:17 +01:00
} ;
2017-08-03 16:14:07 +08:00
static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
. num_phys = 3 ,
. hsic_index = 2 ,
. type = sun8i_a83t_phy ,
. phyctl_offset = REG_PHYCTL_A33 ,
. dedicated_clocks = true ,
} ;
2015-12-11 16:32:18 +01:00
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
. num_phys = 4 ,
. type = sun8i_h3_phy ,
. disc_thresh = 3 ,
2017-03-25 22:50:10 +08:00
. phyctl_offset = REG_PHYCTL_A33 ,
2015-12-11 16:32:18 +01:00
. dedicated_clocks = true ,
2016-08-12 11:06:21 +08:00
. enable_pmu_unk1 = true ,
2017-03-25 22:50:11 +08:00
. phy0_dual_route = true ,
2016-08-12 11:06:21 +08:00
} ;
2018-01-03 16:49:44 +08:00
static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
. num_phys = 3 ,
. type = sun8i_r40_phy ,
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A33 ,
. dedicated_clocks = true ,
. enable_pmu_unk1 = true ,
. phy0_dual_route = true ,
} ;
2017-01-03 23:25:31 +08:00
static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
. num_phys = 1 ,
. type = sun8i_v3s_phy ,
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A33 ,
. dedicated_clocks = true ,
. enable_pmu_unk1 = true ,
2017-09-28 17:33:49 +08:00
. phy0_dual_route = true ,
2017-01-03 23:25:31 +08:00
} ;
2016-08-12 11:06:21 +08:00
static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
. num_phys = 2 ,
. type = sun50i_a64_phy ,
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A33 ,
. dedicated_clocks = true ,
. enable_pmu_unk1 = true ,
2017-04-05 02:45:16 +08:00
. phy0_dual_route = true ,
2015-12-11 16:32:18 +01:00
} ;
2018-10-04 20:28:48 +08:00
static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
. num_phys = 4 ,
. type = sun50i_h6_phy ,
. disc_thresh = 3 ,
. phyctl_offset = REG_PHYCTL_A33 ,
. dedicated_clocks = true ,
. enable_pmu_unk1 = true ,
. phy0_dual_route = true ,
. missing_phys = BIT ( 1 ) | BIT ( 2 ) ,
} ;
2014-03-01 18:09:26 +01:00
static const struct of_device_id sun4i_usb_phy_of_match [ ] = {
2015-12-11 16:32:17 +01:00
{ . compatible = " allwinner,sun4i-a10-usb-phy " , . data = & sun4i_a10_cfg } ,
{ . compatible = " allwinner,sun5i-a13-usb-phy " , . data = & sun5i_a13_cfg } ,
{ . compatible = " allwinner,sun6i-a31-usb-phy " , . data = & sun6i_a31_cfg } ,
{ . compatible = " allwinner,sun7i-a20-usb-phy " , . data = & sun7i_a20_cfg } ,
{ . compatible = " allwinner,sun8i-a23-usb-phy " , . data = & sun8i_a23_cfg } ,
{ . compatible = " allwinner,sun8i-a33-usb-phy " , . data = & sun8i_a33_cfg } ,
2017-08-03 16:14:07 +08:00
{ . compatible = " allwinner,sun8i-a83t-usb-phy " , . data = & sun8i_a83t_cfg } ,
2015-12-11 16:32:18 +01:00
{ . compatible = " allwinner,sun8i-h3-usb-phy " , . data = & sun8i_h3_cfg } ,
2018-01-03 16:49:44 +08:00
{ . compatible = " allwinner,sun8i-r40-usb-phy " , . data = & sun8i_r40_cfg } ,
2017-01-03 23:25:31 +08:00
{ . compatible = " allwinner,sun8i-v3s-usb-phy " , . data = & sun8i_v3s_cfg } ,
2016-08-12 11:06:21 +08:00
{ . compatible = " allwinner,sun50i-a64-usb-phy " ,
. data = & sun50i_a64_cfg } ,
2018-10-04 20:28:48 +08:00
{ . compatible = " allwinner,sun50i-h6-usb-phy " , . data = & sun50i_h6_cfg } ,
2014-03-01 18:09:26 +01:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sun4i_usb_phy_of_match ) ;
static struct platform_driver sun4i_usb_phy_driver = {
. probe = sun4i_usb_phy_probe ,
2015-06-13 14:37:45 +02:00
. remove = sun4i_usb_phy_remove ,
2014-03-01 18:09:26 +01:00
. driver = {
. of_match_table = sun4i_usb_phy_of_match ,
. name = " sun4i-usb-phy " ,
}
} ;
module_platform_driver ( sun4i_usb_phy_driver ) ;
MODULE_DESCRIPTION ( " Allwinner sun4i USB phy driver " ) ;
MODULE_AUTHOR ( " Hans de Goede <hdegoede@redhat.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;