2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2012-09-12 14:58:05 +03:00
/*
* Copyright 2012 Freescale Semiconductor , Inc .
*/
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/err.h>
# include <linux/io.h>
2013-03-30 12:54:01 +02:00
# include <linux/delay.h>
2020-01-23 14:35:58 +08:00
# include <linux/usb/otg.h>
2012-09-12 14:58:05 +03:00
2013-06-24 14:46:36 +03:00
# include "ci_hdrc_imx.h"
2012-09-12 14:58:05 +03:00
2013-03-30 12:54:01 +02:00
# define MX25_USB_PHY_CTRL_OFFSET 0x08
# define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23)
2014-04-23 15:56:36 +08:00
# define MX25_EHCI_INTERFACE_SINGLE_UNI (2 << 0)
# define MX25_EHCI_INTERFACE_DIFF_UNI (0 << 0)
# define MX25_EHCI_INTERFACE_MASK (0xf)
# define MX25_OTG_SIC_SHIFT 29
# define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
# define MX25_OTG_PM_BIT BIT(24)
# define MX25_OTG_PP_BIT BIT(11)
# define MX25_OTG_OCPOL_BIT BIT(3)
# define MX25_H1_SIC_SHIFT 21
# define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
# define MX25_H1_PP_BIT BIT(18)
# define MX25_H1_PM_BIT BIT(16)
# define MX25_H1_IPPUE_UP_BIT BIT(7)
# define MX25_H1_IPPUE_DOWN_BIT BIT(6)
# define MX25_H1_TLL_BIT BIT(5)
# define MX25_H1_USBTE_BIT BIT(4)
# define MX25_H1_OCPOL_BIT BIT(2)
2013-12-06 16:35:14 +08:00
# define MX27_H1_PM_BIT BIT(8)
# define MX27_H2_PM_BIT BIT(16)
# define MX27_OTG_PM_BIT BIT(24)
2013-03-30 12:54:00 +02:00
# define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
2014-05-04 09:24:39 +08:00
# define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c
2016-09-26 13:14:19 +02:00
# define MX53_USB_CTRL_1_OFFSET 0x10
# define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK (0x11 << 2)
# define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI BIT(2)
# define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK (0x11 << 6)
# define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI BIT(6)
2013-03-30 12:54:00 +02:00
# define MX53_USB_UH2_CTRL_OFFSET 0x14
# define MX53_USB_UH3_CTRL_OFFSET 0x18
2016-09-26 13:14:20 +02:00
# define MX53_USB_CLKONOFF_CTRL_OFFSET 0x24
# define MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF BIT(21)
# define MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF BIT(22)
2013-03-30 12:54:00 +02:00
# define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
# define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
# define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
2016-09-26 13:14:19 +02:00
# define MX53_USB_CTRL_1_UH2_ULPI_EN BIT(26)
# define MX53_USB_CTRL_1_UH3_ULPI_EN BIT(27)
# define MX53_USB_UHx_CTRL_WAKE_UP_EN BIT(7)
# define MX53_USB_UHx_CTRL_ULPI_INT_EN BIT(8)
2014-05-04 09:24:39 +08:00
# define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3
# define MX53_USB_PLL_DIV_24_MHZ 0x01
2013-03-30 12:54:00 +02:00
2014-11-05 14:58:32 +08:00
# define MX6_BM_NON_BURST_SETTING BIT(1)
2013-03-30 12:53:59 +02:00
# define MX6_BM_OVER_CUR_DIS BIT(7)
2016-07-20 16:02:42 +08:00
# define MX6_BM_OVER_CUR_POLARITY BIT(8)
2019-02-27 06:51:34 +00:00
# define MX6_BM_PWR_POLARITY BIT(9)
2015-02-11 12:44:46 +08:00
# define MX6_BM_WAKEUP_ENABLE BIT(10)
2018-10-16 09:17:02 +08:00
# define MX6_BM_UTMI_ON_CLOCK BIT(13)
2015-02-11 12:44:46 +08:00
# define MX6_BM_ID_WAKEUP BIT(16)
# define MX6_BM_VBUS_WAKEUP BIT(17)
2015-02-11 12:44:53 +08:00
# define MX6SX_BM_DPDM_WAKEUP_EN BIT(29)
2015-02-11 12:44:46 +08:00
# define MX6_BM_WAKEUP_INTR BIT(31)
2018-10-16 09:17:02 +08:00
# define MX6_USB_HSIC_CTRL_OFFSET 0x10
/* Send resume signal without 480Mhz PHY clock */
# define MX6SX_BM_HSIC_AUTO_RESUME BIT(23)
/* set before portsc.suspendM = 1 */
# define MX6_BM_HSIC_DEV_CONN BIT(21)
/* HSIC enable */
# define MX6_BM_HSIC_EN BIT(12)
/* Force HSIC module 480M clock on, even when in Host is in suspend mode */
# define MX6_BM_HSIC_CLK_ON BIT(11)
2015-02-11 12:44:53 +08:00
# define MX6_USB_OTG1_PHY_CTRL 0x18
/* For imx6dql, it is host-only controller, for later imx6, it is otg's */
# define MX6_USB_OTG2_PHY_CTRL 0x1c
# define MX6SX_USB_VBUS_WAKEUP_SOURCE(v) (v << 8)
# define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS MX6SX_USB_VBUS_WAKEUP_SOURCE(0)
# define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID MX6SX_USB_VBUS_WAKEUP_SOURCE(1)
# define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID MX6SX_USB_VBUS_WAKEUP_SOURCE(2)
# define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END MX6SX_USB_VBUS_WAKEUP_SOURCE(3)
2012-09-12 14:58:05 +03:00
2014-09-22 08:14:15 +08:00
# define VF610_OVER_CUR_DIS BIT(7)
2015-09-09 16:33:02 +08:00
# define MX7D_USBNC_USB_CTRL2 0x4
# define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK 0x3
# define MX7D_USB_VBUS_WAKEUP_SOURCE(v) (v << 0)
# define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS MX7D_USB_VBUS_WAKEUP_SOURCE(0)
# define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1)
# define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
# define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
2020-01-23 14:43:47 +08:00
# define MX7D_USBNC_AUTO_RESUME BIT(2)
2020-01-23 14:35:58 +08:00
/* The default DM/DP value is pull-down */
# define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6)
# define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1)
# define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
# define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
# define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
# define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
# define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
# define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
# define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
BIT ( 14 ) | BIT ( 15 ) )
# define MX7D_USB_OTG_PHY_CFG1 0x30
# define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
# define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
# define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
# define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
# define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16)
# define MX7D_USB_OTG_PHY_CFG2 0x34
# define MX7D_USB_OTG_PHY_STATUS 0x3c
# define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
# define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
# define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
# define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
2015-09-09 16:33:02 +08:00
2019-09-30 13:56:26 +08:00
# define MX7D_USB_OTG_PHY_CFG1 0x30
# define TXPREEMPAMPTUNE0_BIT 28
# define TXPREEMPAMPTUNE0_MASK (3 << 28)
# define TXVREFTUNE0_BIT 20
# define TXVREFTUNE0_MASK (0xf << 20)
2019-09-09 14:41:41 +08:00
# define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
MX6_BM_ID_WAKEUP )
2013-08-14 12:44:16 +03:00
struct usbmisc_ops {
/* It's called once when probe a usb device */
int ( * init ) ( struct imx_usbmisc_data * data ) ;
/* It's called once after adding a usb device */
int ( * post ) ( struct imx_usbmisc_data * data ) ;
2015-02-11 12:44:46 +08:00
/* It's called when we need to enable/disable usb wakeup */
int ( * set_wakeup ) ( struct imx_usbmisc_data * data , bool enabled ) ;
2018-10-16 09:17:02 +08:00
/* It's called before setting portsc.suspendM */
int ( * hsic_set_connect ) ( struct imx_usbmisc_data * data ) ;
/* It's called during suspend/resume */
int ( * hsic_set_clk ) ( struct imx_usbmisc_data * data , bool enabled ) ;
2020-01-23 14:35:58 +08:00
/* usb charger detection */
int ( * charger_detection ) ( struct imx_usbmisc_data * data ) ;
2022-10-13 23:14:39 +08:00
/* It's called when system resume from usb power lost */
int ( * power_lost_check ) ( struct imx_usbmisc_data * data ) ;
2013-08-14 12:44:16 +03:00
} ;
2013-03-30 12:53:56 +02:00
struct imx_usbmisc {
2012-09-12 14:58:05 +03:00
void __iomem * base ;
spinlock_t lock ;
2013-03-30 12:53:59 +02:00
const struct usbmisc_ops * ops ;
2012-09-12 14:58:05 +03:00
} ;
2017-05-15 06:48:58 -07:00
static inline bool is_imx53_usbmisc ( struct imx_usbmisc_data * data ) ;
2014-04-23 15:56:36 +08:00
static int usbmisc_imx25_init ( struct imx_usbmisc_data * data )
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2014-04-23 15:56:36 +08:00
unsigned long flags ;
u32 val = 0 ;
if ( data - > index > 1 )
return - EINVAL ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
switch ( data - > index ) {
case 0 :
val = readl ( usbmisc - > base ) ;
val & = ~ ( MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT ) ;
val | = ( MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK ) < < MX25_OTG_SIC_SHIFT ;
val | = ( MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT ) ;
2018-12-04 09:31:31 +01:00
/*
* If the polarity is not configured assume active high for
* historical reasons .
*/
if ( data - > oc_pol_configured & & data - > oc_pol_active_low )
val & = ~ MX25_OTG_OCPOL_BIT ;
2014-04-23 15:56:36 +08:00
writel ( val , usbmisc - > base ) ;
break ;
case 1 :
val = readl ( usbmisc - > base ) ;
val & = ~ ( MX25_H1_SIC_MASK | MX25_H1_PP_BIT | MX25_H1_IPPUE_UP_BIT ) ;
val | = ( MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK ) < < MX25_H1_SIC_SHIFT ;
val | = ( MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT ) ;
2018-12-04 09:31:31 +01:00
/*
* If the polarity is not configured assume active high for
* historical reasons .
*/
if ( data - > oc_pol_configured & & data - > oc_pol_active_low )
val & = ~ MX25_H1_OCPOL_BIT ;
2014-04-23 15:56:36 +08:00
writel ( val , usbmisc - > base ) ;
break ;
}
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
2013-08-14 12:44:16 +03:00
static int usbmisc_imx25_post ( struct imx_usbmisc_data * data )
2013-03-30 12:54:01 +02:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-03-30 12:54:01 +02:00
void __iomem * reg ;
unsigned long flags ;
u32 val ;
2013-08-14 12:44:16 +03:00
if ( data - > index > 2 )
return - EINVAL ;
2013-03-30 12:54:01 +02:00
2018-03-29 17:52:45 +08:00
if ( data - > index )
return 0 ;
2018-03-29 17:52:46 +08:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
reg = usbmisc - > base + MX25_USB_PHY_CTRL_OFFSET ;
val = readl ( reg ) ;
if ( data - > evdo )
val | = MX25_BM_EXTERNAL_VBUS_DIVIDER ;
else
val & = ~ MX25_BM_EXTERNAL_VBUS_DIVIDER ;
writel ( val , reg ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
usleep_range ( 5000 , 10000 ) ; /* needed to stabilize voltage */
2013-03-30 12:54:01 +02:00
return 0 ;
}
2013-12-06 16:35:14 +08:00
static int usbmisc_imx27_init ( struct imx_usbmisc_data * data )
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-12-06 16:35:14 +08:00
unsigned long flags ;
u32 val ;
switch ( data - > index ) {
case 0 :
val = MX27_OTG_PM_BIT ;
break ;
case 1 :
val = MX27_H1_PM_BIT ;
break ;
case 2 :
val = MX27_H2_PM_BIT ;
break ;
default :
return - EINVAL ;
2015-05-09 12:15:24 -03:00
}
2013-12-06 16:35:14 +08:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
if ( data - > disable_oc )
val = readl ( usbmisc - > base ) | val ;
else
val = readl ( usbmisc - > base ) & ~ val ;
writel ( val , usbmisc - > base ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
2013-08-14 12:44:16 +03:00
static int usbmisc_imx53_init ( struct imx_usbmisc_data * data )
2013-03-30 12:54:00 +02:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-03-30 12:54:00 +02:00
void __iomem * reg = NULL ;
unsigned long flags ;
u32 val = 0 ;
2013-08-14 12:44:16 +03:00
if ( data - > index > 3 )
return - EINVAL ;
2013-03-30 12:54:00 +02:00
2014-05-04 09:24:39 +08:00
/* Select a 24 MHz reference clock for the PHY */
2014-11-26 13:44:26 +08:00
val = readl ( usbmisc - > base + MX53_USB_OTG_PHY_CTRL_1_OFFSET ) ;
2014-05-04 09:24:39 +08:00
val & = ~ MX53_USB_PHYCTRL1_PLLDIV_MASK ;
val | = MX53_USB_PLL_DIV_24_MHZ ;
writel ( val , usbmisc - > base + MX53_USB_OTG_PHY_CTRL_1_OFFSET ) ;
2016-09-26 13:14:19 +02:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
switch ( data - > index ) {
case 0 :
if ( data - > disable_oc ) {
2013-03-30 12:54:00 +02:00
reg = usbmisc - > base + MX53_USB_OTG_PHY_CTRL_0_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_OTG ;
2016-09-26 13:14:19 +02:00
writel ( val , reg ) ;
}
break ;
case 1 :
if ( data - > disable_oc ) {
2013-03-30 12:54:00 +02:00
reg = usbmisc - > base + MX53_USB_OTG_PHY_CTRL_0_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_H1 ;
2016-09-26 13:14:19 +02:00
writel ( val , reg ) ;
}
break ;
case 2 :
if ( data - > ulpi ) {
/* set USBH2 into ULPI-mode. */
reg = usbmisc - > base + MX53_USB_CTRL_1_OFFSET ;
val = readl ( reg ) | MX53_USB_CTRL_1_UH2_ULPI_EN ;
/* select ULPI clock */
val & = ~ MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK ;
val | = MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI ;
writel ( val , reg ) ;
/* Set interrupt wake up enable */
reg = usbmisc - > base + MX53_USB_UH2_CTRL_OFFSET ;
val = readl ( reg ) | MX53_USB_UHx_CTRL_WAKE_UP_EN
| MX53_USB_UHx_CTRL_ULPI_INT_EN ;
writel ( val , reg ) ;
2017-05-15 06:48:58 -07:00
if ( is_imx53_usbmisc ( data ) ) {
/* Disable internal 60Mhz clock */
reg = usbmisc - > base +
MX53_USB_CLKONOFF_CTRL_OFFSET ;
val = readl ( reg ) |
MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF ;
writel ( val , reg ) ;
}
2016-09-26 13:14:19 +02:00
}
if ( data - > disable_oc ) {
2013-03-30 12:54:00 +02:00
reg = usbmisc - > base + MX53_USB_UH2_CTRL_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_UHx ;
2016-09-26 13:14:19 +02:00
writel ( val , reg ) ;
}
break ;
case 3 :
if ( data - > ulpi ) {
/* set USBH3 into ULPI-mode. */
reg = usbmisc - > base + MX53_USB_CTRL_1_OFFSET ;
val = readl ( reg ) | MX53_USB_CTRL_1_UH3_ULPI_EN ;
/* select ULPI clock */
val & = ~ MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK ;
val | = MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI ;
writel ( val , reg ) ;
/* Set interrupt wake up enable */
2013-03-30 12:54:00 +02:00
reg = usbmisc - > base + MX53_USB_UH3_CTRL_OFFSET ;
2016-09-26 13:14:19 +02:00
val = readl ( reg ) | MX53_USB_UHx_CTRL_WAKE_UP_EN
| MX53_USB_UHx_CTRL_ULPI_INT_EN ;
writel ( val , reg ) ;
2017-05-15 06:48:58 -07:00
if ( is_imx53_usbmisc ( data ) ) {
/* Disable internal 60Mhz clock */
reg = usbmisc - > base +
MX53_USB_CLKONOFF_CTRL_OFFSET ;
val = readl ( reg ) |
MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF ;
writel ( val , reg ) ;
}
2013-03-30 12:54:00 +02:00
}
2016-09-26 13:14:19 +02:00
if ( data - > disable_oc ) {
reg = usbmisc - > base + MX53_USB_UH3_CTRL_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_UHx ;
2013-03-30 12:54:00 +02:00
writel ( val , reg ) ;
2016-09-26 13:14:19 +02:00
}
break ;
2013-03-30 12:54:00 +02:00
}
2016-09-26 13:14:19 +02:00
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
2013-03-30 12:54:00 +02:00
return 0 ;
}
2019-09-09 14:41:41 +08:00
static u32 usbmisc_wakeup_setting ( struct imx_usbmisc_data * data )
{
u32 wakeup_setting = MX6_USB_OTG_WAKEUP_BITS ;
2020-07-28 14:12:11 +08:00
if ( data - > ext_id | | data - > available_role ! = USB_DR_MODE_OTG )
2019-09-09 14:41:41 +08:00
wakeup_setting & = ~ MX6_BM_ID_WAKEUP ;
2020-07-28 14:12:11 +08:00
if ( data - > ext_vbus | | data - > available_role = = USB_DR_MODE_HOST )
2019-09-09 14:41:41 +08:00
wakeup_setting & = ~ MX6_BM_VBUS_WAKEUP ;
return wakeup_setting ;
}
2015-02-11 12:44:46 +08:00
static int usbmisc_imx6q_set_wakeup
( struct imx_usbmisc_data * data , bool enabled )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
unsigned long flags ;
u32 val ;
int ret = 0 ;
if ( data - > index > 3 )
return - EINVAL ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + data - > index * 4 ) ;
if ( enabled ) {
2019-09-09 14:41:41 +08:00
val & = ~ MX6_USB_OTG_WAKEUP_BITS ;
val | = usbmisc_wakeup_setting ( data ) ;
2015-02-11 12:44:46 +08:00
} else {
if ( val & MX6_BM_WAKEUP_INTR )
pr_debug ( " wakeup int at ci_hdrc.%d \n " , data - > index ) ;
2019-09-09 14:41:41 +08:00
val & = ~ MX6_USB_OTG_WAKEUP_BITS ;
2015-02-11 12:44:46 +08:00
}
2018-03-29 17:52:47 +08:00
writel ( val , usbmisc - > base + data - > index * 4 ) ;
2015-02-11 12:44:46 +08:00
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return ret ;
}
2013-08-14 12:44:16 +03:00
static int usbmisc_imx6q_init ( struct imx_usbmisc_data * data )
2012-09-12 14:58:05 +03:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2012-09-12 14:58:05 +03:00
unsigned long flags ;
u32 reg ;
2013-08-14 12:44:16 +03:00
if ( data - > index > 3 )
return - EINVAL ;
2012-09-12 14:58:05 +03:00
2014-11-05 14:58:32 +08:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
2016-07-20 16:02:42 +08:00
reg = readl ( usbmisc - > base + data - > index * 4 ) ;
2013-08-14 12:44:16 +03:00
if ( data - > disable_oc ) {
2016-07-20 16:02:42 +08:00
reg | = MX6_BM_OVER_CUR_DIS ;
2018-08-27 15:59:56 +02:00
} else {
2018-12-04 09:31:29 +01:00
reg & = ~ MX6_BM_OVER_CUR_DIS ;
/*
* If the polarity is not configured keep it as setup by the
* bootloader .
*/
if ( data - > oc_pol_configured & & data - > oc_pol_active_low )
reg | = MX6_BM_OVER_CUR_POLARITY ;
else if ( data - > oc_pol_configured )
reg & = ~ MX6_BM_OVER_CUR_POLARITY ;
2012-09-12 14:58:05 +03:00
}
2019-02-27 06:51:34 +00:00
/* If the polarity is not set keep it as setup by the bootlader */
if ( data - > pwr_pol = = 1 )
reg | = MX6_BM_PWR_POLARITY ;
2016-07-20 16:02:42 +08:00
writel ( reg , usbmisc - > base + data - > index * 4 ) ;
2012-09-12 14:58:05 +03:00
2014-11-05 14:58:32 +08:00
/* SoC non-burst setting */
reg = readl ( usbmisc - > base + data - > index * 4 ) ;
writel ( reg | MX6_BM_NON_BURST_SETTING ,
usbmisc - > base + data - > index * 4 ) ;
2018-10-16 09:17:02 +08:00
/* For HSIC controller */
if ( data - > hsic ) {
reg = readl ( usbmisc - > base + data - > index * 4 ) ;
writel ( reg | MX6_BM_UTMI_ON_CLOCK ,
usbmisc - > base + data - > index * 4 ) ;
reg = readl ( usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET
+ ( data - > index - 2 ) * 4 ) ;
reg | = MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON ;
writel ( reg , usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET
+ ( data - > index - 2 ) * 4 ) ;
}
2014-11-05 14:58:32 +08:00
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
2015-02-11 12:44:46 +08:00
usbmisc_imx6q_set_wakeup ( data , false ) ;
2012-09-12 14:58:05 +03:00
return 0 ;
}
2018-10-16 09:17:02 +08:00
static int usbmisc_imx6_hsic_get_reg_offset ( struct imx_usbmisc_data * data )
{
int offset , ret = 0 ;
if ( data - > index = = 2 | | data - > index = = 3 ) {
offset = ( data - > index - 2 ) * 4 ;
} else if ( data - > index = = 0 ) {
/*
* For SoCs like i . MX7D and later , each USB controller has
* its own non - core register region . For SoCs before i . MX7D ,
* the first two USB controllers are non - HSIC controllers .
*/
offset = 0 ;
} else {
dev_err ( data - > dev , " index is error for usbmisc \n " ) ;
ret = - EINVAL ;
}
return ret ? ret : offset ;
}
static int usbmisc_imx6_hsic_set_connect ( struct imx_usbmisc_data * data )
{
unsigned long flags ;
u32 val ;
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
int offset ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
offset = usbmisc_imx6_hsic_get_reg_offset ( data ) ;
if ( offset < 0 ) {
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return offset ;
}
val = readl ( usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET + offset ) ;
if ( ! ( val & MX6_BM_HSIC_DEV_CONN ) )
writel ( val | MX6_BM_HSIC_DEV_CONN ,
usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET + offset ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
static int usbmisc_imx6_hsic_set_clk ( struct imx_usbmisc_data * data , bool on )
{
unsigned long flags ;
u32 val ;
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
int offset ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
offset = usbmisc_imx6_hsic_get_reg_offset ( data ) ;
if ( offset < 0 ) {
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return offset ;
}
val = readl ( usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET + offset ) ;
val | = MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON ;
if ( on )
val | = MX6_BM_HSIC_CLK_ON ;
else
val & = ~ MX6_BM_HSIC_CLK_ON ;
writel ( val , usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET + offset ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
2015-02-11 12:44:53 +08:00
static int usbmisc_imx6sx_init ( struct imx_usbmisc_data * data )
{
void __iomem * reg = NULL ;
unsigned long flags ;
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
u32 val ;
usbmisc_imx6q_init ( data ) ;
if ( data - > index = = 0 | | data - > index = = 1 ) {
reg = usbmisc - > base + MX6_USB_OTG1_PHY_CTRL + data - > index * 4 ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
/* Set vbus wakeup source as bvalid */
val = readl ( reg ) ;
writel ( val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID , reg ) ;
/*
* Disable dp / dm wakeup in device mode when vbus is
* not there .
*/
val = readl ( usbmisc - > base + data - > index * 4 ) ;
writel ( val & ~ MX6SX_BM_DPDM_WAKEUP_EN ,
usbmisc - > base + data - > index * 4 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
}
2018-10-16 09:17:02 +08:00
/* For HSIC controller */
if ( data - > hsic ) {
val = readl ( usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET ) ;
val | = MX6SX_BM_HSIC_AUTO_RESUME ;
writel ( val , usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET ) ;
}
2015-03-25 07:23:52 +01:00
return 0 ;
2015-02-11 12:44:53 +08:00
}
2014-09-22 08:14:15 +08:00
static int usbmisc_vf610_init ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
u32 reg ;
/*
* Vybrid only has one misc register set , but in two different
* areas . These is reflected in two instances of this driver .
*/
if ( data - > index > = 1 )
return - EINVAL ;
if ( data - > disable_oc ) {
reg = readl ( usbmisc - > base ) ;
writel ( reg | VF610_OVER_CUR_DIS , usbmisc - > base ) ;
}
return 0 ;
}
2015-09-09 16:33:02 +08:00
static int usbmisc_imx7d_set_wakeup
( struct imx_usbmisc_data * data , bool enabled )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base ) ;
if ( enabled ) {
2019-09-09 14:41:41 +08:00
val & = ~ MX6_USB_OTG_WAKEUP_BITS ;
val | = usbmisc_wakeup_setting ( data ) ;
writel ( val , usbmisc - > base ) ;
2015-09-09 16:33:02 +08:00
} else {
if ( val & MX6_BM_WAKEUP_INTR )
dev_dbg ( data - > dev , " wakeup int \n " ) ;
2019-09-09 14:41:41 +08:00
writel ( val & ~ MX6_USB_OTG_WAKEUP_BITS , usbmisc - > base ) ;
2015-09-09 16:33:02 +08:00
}
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
static int usbmisc_imx7d_init ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
unsigned long flags ;
u32 reg ;
if ( data - > index > = 1 )
return - EINVAL ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
2016-07-20 16:02:42 +08:00
reg = readl ( usbmisc - > base ) ;
2015-09-09 16:33:02 +08:00
if ( data - > disable_oc ) {
2016-07-20 16:02:42 +08:00
reg | = MX6_BM_OVER_CUR_DIS ;
2018-12-04 09:31:29 +01:00
} else {
reg & = ~ MX6_BM_OVER_CUR_DIS ;
/*
* If the polarity is not configured keep it as setup by the
* bootloader .
*/
if ( data - > oc_pol_configured & & data - > oc_pol_active_low )
reg | = MX6_BM_OVER_CUR_POLARITY ;
else if ( data - > oc_pol_configured )
reg & = ~ MX6_BM_OVER_CUR_POLARITY ;
2015-09-09 16:33:02 +08:00
}
2019-02-27 06:51:34 +00:00
/* If the polarity is not set keep it as setup by the bootlader */
if ( data - > pwr_pol = = 1 )
reg | = MX6_BM_PWR_POLARITY ;
2016-07-20 16:02:42 +08:00
writel ( reg , usbmisc - > base ) ;
2015-09-09 16:33:02 +08:00
2020-01-23 14:43:47 +08:00
/* SoC non-burst setting */
reg = readl ( usbmisc - > base ) ;
writel ( reg | MX6_BM_NON_BURST_SETTING , usbmisc - > base ) ;
if ( ! data - > hsic ) {
reg = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
reg & = ~ MX7D_USB_VBUS_WAKEUP_SOURCE_MASK ;
writel ( reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID
| MX7D_USBNC_AUTO_RESUME ,
usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
2019-09-30 13:56:26 +08:00
/* PHY tuning for signal quality */
reg = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG1 ) ;
if ( data - > emp_curr_control & & data - > emp_curr_control < =
( TXPREEMPAMPTUNE0_MASK > > TXPREEMPAMPTUNE0_BIT ) ) {
reg & = ~ TXPREEMPAMPTUNE0_MASK ;
reg | = ( data - > emp_curr_control < < TXPREEMPAMPTUNE0_BIT ) ;
}
if ( data - > dc_vol_level_adjust & & data - > dc_vol_level_adjust < =
( TXVREFTUNE0_MASK > > TXVREFTUNE0_BIT ) ) {
reg & = ~ TXVREFTUNE0_MASK ;
reg | = ( data - > dc_vol_level_adjust < < TXVREFTUNE0_BIT ) ;
}
writel ( reg , usbmisc - > base + MX7D_USB_OTG_PHY_CFG1 ) ;
2020-01-23 14:43:47 +08:00
}
2018-10-16 09:17:02 +08:00
2015-09-09 16:33:02 +08:00
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
usbmisc_imx7d_set_wakeup ( data , false ) ;
return 0 ;
}
2020-01-23 14:35:58 +08:00
static int imx7d_charger_secondary_detection ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
struct usb_phy * usb_phy = data - > usb_phy ;
int val ;
unsigned long flags ;
2021-06-14 13:50:13 -04:00
/* Clear VDATSRCENB0 to disable VDP_SRC and IDM_SNK required by BC 1.2 spec */
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
val & = ~ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 ;
writel ( val , usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
/* TVDMSRC_DIS */
msleep ( 20 ) ;
2020-01-23 14:35:58 +08:00
/* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
writel ( val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL ,
usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
2021-06-14 13:50:13 -04:00
/* TVDMSRC_ON */
msleep ( 40 ) ;
2020-01-23 14:35:58 +08:00
/*
* Per BC 1.2 , check voltage of D + :
* DCP : if greater than VDAT_REF ;
* CDP : if less than VDAT_REF .
*/
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_STATUS ) ;
if ( val & MX7D_USB_OTG_PHY_STATUS_CHRGDET ) {
dev_dbg ( data - > dev , " It is a dedicate charging port \n " ) ;
usb_phy - > chg_type = DCP_TYPE ;
} else {
dev_dbg ( data - > dev , " It is a charging downstream port \n " ) ;
usb_phy - > chg_type = CDP_TYPE ;
}
return 0 ;
}
static void imx7_disable_charger_detector ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
val & = ~ ( MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL ) ;
writel ( val , usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
/* Set OPMODE to be 2'b00 and disable its override */
val = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
val & = ~ MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK ;
writel ( val , usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
val = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
writel ( val & ~ MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN ,
usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
}
static int imx7d_charger_data_contact_detect ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
unsigned long flags ;
u32 val ;
int i , data_pin_contact_count = 0 ;
/* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
writel ( val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB ,
usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
for ( i = 0 ; i < 100 ; i = i + 1 ) {
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_STATUS ) ;
if ( ! ( val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 ) ) {
if ( data_pin_contact_count + + > 5 )
/* Data pin makes contact */
break ;
usleep_range ( 5000 , 10000 ) ;
} else {
data_pin_contact_count = 0 ;
usleep_range ( 5000 , 6000 ) ;
}
}
/* Disable DCD after finished data contact check */
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
writel ( val & ~ MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB ,
usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
if ( i = = 100 ) {
dev_err ( data - > dev ,
" VBUS is coming from a dedicated power supply. \n " ) ;
return - ENXIO ;
}
return 0 ;
}
static int imx7d_charger_primary_detection ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
struct usb_phy * usb_phy = data - > usb_phy ;
unsigned long flags ;
u32 val ;
/* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
val & = ~ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL ;
writel ( val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 ,
usbmisc - > base + MX7D_USB_OTG_PHY_CFG2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
2021-06-14 13:50:13 -04:00
/* TVDPSRC_ON */
msleep ( 40 ) ;
2020-01-23 14:35:58 +08:00
/* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_STATUS ) ;
if ( ! ( val & MX7D_USB_OTG_PHY_STATUS_CHRGDET ) ) {
dev_dbg ( data - > dev , " It is a standard downstream port \n " ) ;
usb_phy - > chg_type = SDP_TYPE ;
}
return 0 ;
}
2020-07-03 18:41:41 +01:00
/*
2020-01-23 14:35:58 +08:00
* Whole charger detection process :
* 1. OPMODE override to be non - driving
* 2. Data contact check
* 3. Primary detection
* 4. Secondary detection
* 5. Disable charger detection
*/
static int imx7d_charger_detection ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
struct usb_phy * usb_phy = data - > usb_phy ;
unsigned long flags ;
u32 val ;
int ret ;
/* Check if vbus is valid */
val = readl ( usbmisc - > base + MX7D_USB_OTG_PHY_STATUS ) ;
if ( ! ( val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD ) ) {
dev_err ( data - > dev , " vbus is error \n " ) ;
return - EINVAL ;
}
/*
* Keep OPMODE to be non - driving mode during the whole
* charger detection process .
*/
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
val = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
val & = ~ MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK ;
val | = MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING ;
writel ( val , usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
val = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
writel ( val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN ,
usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
ret = imx7d_charger_data_contact_detect ( data ) ;
if ( ret )
return ret ;
ret = imx7d_charger_primary_detection ( data ) ;
if ( ! ret & & usb_phy - > chg_type ! = SDP_TYPE )
ret = imx7d_charger_secondary_detection ( data ) ;
imx7_disable_charger_detector ( data ) ;
return ret ;
}
2020-01-23 14:43:47 +08:00
static int usbmisc_imx7ulp_init ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
unsigned long flags ;
u32 reg ;
if ( data - > index > = 1 )
return - EINVAL ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
reg = readl ( usbmisc - > base ) ;
if ( data - > disable_oc ) {
reg | = MX6_BM_OVER_CUR_DIS ;
} else {
reg & = ~ MX6_BM_OVER_CUR_DIS ;
/*
* If the polarity is not configured keep it as setup by the
* bootloader .
*/
if ( data - > oc_pol_configured & & data - > oc_pol_active_low )
reg | = MX6_BM_OVER_CUR_POLARITY ;
else if ( data - > oc_pol_configured )
reg & = ~ MX6_BM_OVER_CUR_POLARITY ;
}
/* If the polarity is not set keep it as setup by the bootlader */
if ( data - > pwr_pol = = 1 )
reg | = MX6_BM_PWR_POLARITY ;
writel ( reg , usbmisc - > base ) ;
/* SoC non-burst setting */
reg = readl ( usbmisc - > base ) ;
writel ( reg | MX6_BM_NON_BURST_SETTING , usbmisc - > base ) ;
if ( data - > hsic ) {
reg = readl ( usbmisc - > base ) ;
writel ( reg | MX6_BM_UTMI_ON_CLOCK , usbmisc - > base ) ;
reg = readl ( usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET ) ;
reg | = MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON ;
writel ( reg , usbmisc - > base + MX6_USB_HSIC_CTRL_OFFSET ) ;
/*
* For non - HSIC controller , the autoresume is enabled
* at MXS PHY driver ( usbphy_ctrl bit18 ) .
*/
reg = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
writel ( reg | MX7D_USBNC_AUTO_RESUME ,
usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
} else {
reg = readl ( usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
reg & = ~ MX7D_USB_VBUS_WAKEUP_SOURCE_MASK ;
writel ( reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID ,
usbmisc - > base + MX7D_USBNC_USB_CTRL2 ) ;
}
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
usbmisc_imx7d_set_wakeup ( data , false ) ;
return 0 ;
}
2013-03-30 12:54:01 +02:00
static const struct usbmisc_ops imx25_usbmisc_ops = {
2014-04-23 15:56:36 +08:00
. init = usbmisc_imx25_init ,
2013-03-30 12:54:01 +02:00
. post = usbmisc_imx25_post ,
} ;
2013-12-06 16:35:14 +08:00
static const struct usbmisc_ops imx27_usbmisc_ops = {
. init = usbmisc_imx27_init ,
} ;
2017-05-15 06:48:58 -07:00
static const struct usbmisc_ops imx51_usbmisc_ops = {
. init = usbmisc_imx53_init ,
} ;
2013-03-30 12:54:00 +02:00
static const struct usbmisc_ops imx53_usbmisc_ops = {
. init = usbmisc_imx53_init ,
} ;
2012-09-12 14:58:05 +03:00
static const struct usbmisc_ops imx6q_usbmisc_ops = {
2015-02-11 12:44:46 +08:00
. set_wakeup = usbmisc_imx6q_set_wakeup ,
2012-09-12 14:58:05 +03:00
. init = usbmisc_imx6q_init ,
2018-10-16 09:17:02 +08:00
. hsic_set_connect = usbmisc_imx6_hsic_set_connect ,
. hsic_set_clk = usbmisc_imx6_hsic_set_clk ,
2012-09-12 14:58:05 +03:00
} ;
2014-09-22 08:14:15 +08:00
static const struct usbmisc_ops vf610_usbmisc_ops = {
. init = usbmisc_vf610_init ,
} ;
2015-02-11 12:44:53 +08:00
static const struct usbmisc_ops imx6sx_usbmisc_ops = {
. set_wakeup = usbmisc_imx6q_set_wakeup ,
. init = usbmisc_imx6sx_init ,
2018-10-16 09:17:02 +08:00
. hsic_set_connect = usbmisc_imx6_hsic_set_connect ,
. hsic_set_clk = usbmisc_imx6_hsic_set_clk ,
2015-02-11 12:44:53 +08:00
} ;
2015-09-09 16:33:02 +08:00
static const struct usbmisc_ops imx7d_usbmisc_ops = {
. init = usbmisc_imx7d_init ,
. set_wakeup = usbmisc_imx7d_set_wakeup ,
2020-01-23 14:35:58 +08:00
. charger_detection = imx7d_charger_detection ,
2015-09-09 16:33:02 +08:00
} ;
2020-01-23 14:43:47 +08:00
static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
. init = usbmisc_imx7ulp_init ,
. set_wakeup = usbmisc_imx7d_set_wakeup ,
. hsic_set_connect = usbmisc_imx6_hsic_set_connect ,
. hsic_set_clk = usbmisc_imx6_hsic_set_clk ,
} ;
2017-05-15 06:48:58 -07:00
static inline bool is_imx53_usbmisc ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
return usbmisc - > ops = = & imx53_usbmisc_ops ;
}
2013-08-14 12:44:16 +03:00
int imx_usbmisc_init ( struct imx_usbmisc_data * data )
{
2015-02-11 12:44:44 +08:00
struct imx_usbmisc * usbmisc ;
if ( ! data )
return 0 ;
2014-09-22 08:14:15 +08:00
2015-02-11 12:44:44 +08:00
usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-08-14 12:44:16 +03:00
if ( ! usbmisc - > ops - > init )
return 0 ;
return usbmisc - > ops - > init ( data ) ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_init ) ;
int imx_usbmisc_init_post ( struct imx_usbmisc_data * data )
{
2015-02-11 12:44:44 +08:00
struct imx_usbmisc * usbmisc ;
2022-10-13 23:14:39 +08:00
int ret = 0 ;
2015-02-11 12:44:44 +08:00
if ( ! data )
return 0 ;
2014-09-22 08:14:15 +08:00
2015-02-11 12:44:44 +08:00
usbmisc = dev_get_drvdata ( data - > dev ) ;
2022-10-13 23:14:39 +08:00
if ( usbmisc - > ops - > post )
ret = usbmisc - > ops - > post ( data ) ;
if ( ret ) {
dev_err ( data - > dev , " post init failed, ret=%d \n " , ret ) ;
return ret ;
}
2015-02-11 12:44:46 +08:00
2022-10-13 23:14:39 +08:00
if ( usbmisc - > ops - > set_wakeup )
ret = usbmisc - > ops - > set_wakeup ( data , false ) ;
if ( ret ) {
dev_err ( data - > dev , " set_wakeup failed, ret=%d \n " , ret ) ;
return ret ;
}
2015-02-11 12:44:46 +08:00
2022-10-13 23:14:39 +08:00
return 0 ;
2015-02-11 12:44:46 +08:00
}
2022-10-13 23:14:39 +08:00
EXPORT_SYMBOL_GPL ( imx_usbmisc_init_post ) ;
2015-02-11 12:44:46 +08:00
2018-10-16 09:17:02 +08:00
int imx_usbmisc_hsic_set_connect ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc ;
if ( ! data )
return 0 ;
usbmisc = dev_get_drvdata ( data - > dev ) ;
if ( ! usbmisc - > ops - > hsic_set_connect | | ! data - > hsic )
return 0 ;
return usbmisc - > ops - > hsic_set_connect ( data ) ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_hsic_set_connect ) ;
2020-01-23 14:35:58 +08:00
int imx_usbmisc_charger_detection ( struct imx_usbmisc_data * data , bool connect )
{
struct imx_usbmisc * usbmisc ;
struct usb_phy * usb_phy ;
int ret = 0 ;
if ( ! data )
return - EINVAL ;
usbmisc = dev_get_drvdata ( data - > dev ) ;
usb_phy = data - > usb_phy ;
if ( ! usbmisc - > ops - > charger_detection )
return - ENOTSUPP ;
if ( connect ) {
ret = usbmisc - > ops - > charger_detection ( data ) ;
if ( ret ) {
dev_err ( data - > dev ,
" Error occurs during detection: %d \n " ,
ret ) ;
usb_phy - > chg_state = USB_CHARGER_ABSENT ;
} else {
usb_phy - > chg_state = USB_CHARGER_PRESENT ;
}
} else {
usb_phy - > chg_state = USB_CHARGER_ABSENT ;
usb_phy - > chg_type = UNKNOWN_TYPE ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_charger_detection ) ;
2022-10-13 23:14:39 +08:00
int imx_usbmisc_suspend ( struct imx_usbmisc_data * data , bool wakeup )
{
struct imx_usbmisc * usbmisc ;
int ret = 0 ;
if ( ! data )
return 0 ;
usbmisc = dev_get_drvdata ( data - > dev ) ;
if ( wakeup & & usbmisc - > ops - > set_wakeup )
ret = usbmisc - > ops - > set_wakeup ( data , true ) ;
if ( ret ) {
dev_err ( data - > dev , " set_wakeup failed, ret=%d \n " , ret ) ;
return ret ;
}
if ( usbmisc - > ops - > hsic_set_clk & & data - > hsic )
ret = usbmisc - > ops - > hsic_set_clk ( data , false ) ;
if ( ret ) {
dev_err ( data - > dev , " set_wakeup failed, ret=%d \n " , ret ) ;
return ret ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_suspend ) ;
int imx_usbmisc_resume ( struct imx_usbmisc_data * data , bool wakeup )
{
struct imx_usbmisc * usbmisc ;
int ret = 0 ;
if ( ! data )
return 0 ;
usbmisc = dev_get_drvdata ( data - > dev ) ;
if ( usbmisc - > ops - > power_lost_check )
ret = usbmisc - > ops - > power_lost_check ( data ) ;
if ( ret > 0 ) {
/* re-init if resume from power lost */
ret = imx_usbmisc_init ( data ) ;
if ( ret ) {
dev_err ( data - > dev , " re-init failed, ret=%d \n " , ret ) ;
return ret ;
}
}
if ( wakeup & & usbmisc - > ops - > set_wakeup )
ret = usbmisc - > ops - > set_wakeup ( data , false ) ;
if ( ret ) {
dev_err ( data - > dev , " set_wakeup failed, ret=%d \n " , ret ) ;
return ret ;
}
if ( usbmisc - > ops - > hsic_set_clk & & data - > hsic )
ret = usbmisc - > ops - > hsic_set_clk ( data , true ) ;
if ( ret ) {
dev_err ( data - > dev , " set_wakeup failed, ret=%d \n " , ret ) ;
goto hsic_set_clk_fail ;
}
return 0 ;
hsic_set_clk_fail :
if ( wakeup & & usbmisc - > ops - > set_wakeup )
usbmisc - > ops - > set_wakeup ( data , true ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_resume ) ;
2013-03-30 12:53:56 +02:00
static const struct of_device_id usbmisc_imx_dt_ids [ ] = {
2013-03-30 12:54:01 +02:00
{
. compatible = " fsl,imx25-usbmisc " ,
. data = & imx25_usbmisc_ops ,
} ,
2014-04-23 15:56:36 +08:00
{
. compatible = " fsl,imx35-usbmisc " ,
. data = & imx25_usbmisc_ops ,
} ,
2013-12-06 16:35:14 +08:00
{
. compatible = " fsl,imx27-usbmisc " ,
. data = & imx27_usbmisc_ops ,
} ,
2013-12-06 16:35:15 +08:00
{
. compatible = " fsl,imx51-usbmisc " ,
2017-05-15 06:48:58 -07:00
. data = & imx51_usbmisc_ops ,
2013-12-06 16:35:15 +08:00
} ,
2013-03-30 12:54:00 +02:00
{
. compatible = " fsl,imx53-usbmisc " ,
. data = & imx53_usbmisc_ops ,
} ,
2013-03-30 12:53:59 +02:00
{
. compatible = " fsl,imx6q-usbmisc " ,
. data = & imx6q_usbmisc_ops ,
} ,
2014-09-22 08:14:15 +08:00
{
. compatible = " fsl,vf610-usbmisc " ,
. data = & vf610_usbmisc_ops ,
} ,
2015-02-11 12:44:53 +08:00
{
. compatible = " fsl,imx6sx-usbmisc " ,
. data = & imx6sx_usbmisc_ops ,
} ,
2015-09-16 15:52:32 +08:00
{
. compatible = " fsl,imx6ul-usbmisc " ,
. data = & imx6sx_usbmisc_ops ,
} ,
2016-07-20 16:02:42 +08:00
{
. compatible = " fsl,imx7d-usbmisc " ,
. data = & imx7d_usbmisc_ops ,
} ,
2019-04-28 10:35:31 +08:00
{
. compatible = " fsl,imx7ulp-usbmisc " ,
2020-01-23 14:43:47 +08:00
. data = & imx7ulp_usbmisc_ops ,
2019-04-28 10:35:31 +08:00
} ,
2012-09-12 14:58:05 +03:00
{ /* sentinel */ }
} ;
2013-06-20 23:33:25 +02:00
MODULE_DEVICE_TABLE ( of , usbmisc_imx_dt_ids ) ;
2012-09-12 14:58:05 +03:00
2013-03-30 12:53:56 +02:00
static int usbmisc_imx_probe ( struct platform_device * pdev )
2012-09-12 14:58:05 +03:00
{
2013-03-30 12:53:56 +02:00
struct imx_usbmisc * data ;
2012-09-12 14:58:05 +03:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
spin_lock_init ( & data - > lock ) ;
2019-06-05 14:37:37 -03:00
data - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-01-21 11:09:22 +01:00
if ( IS_ERR ( data - > base ) )
return PTR_ERR ( data - > base ) ;
2012-09-12 14:58:05 +03:00
2020-11-24 13:39:11 -03:00
data - > ops = of_device_get_match_data ( & pdev - > dev ) ;
2014-09-22 08:14:15 +08:00
platform_set_drvdata ( pdev , data ) ;
2012-09-12 14:58:05 +03:00
return 0 ;
}
2013-03-30 12:53:56 +02:00
static int usbmisc_imx_remove ( struct platform_device * pdev )
2012-09-12 14:58:05 +03:00
{
return 0 ;
}
2013-03-30 12:53:56 +02:00
static struct platform_driver usbmisc_imx_driver = {
. probe = usbmisc_imx_probe ,
. remove = usbmisc_imx_remove ,
2012-09-12 14:58:05 +03:00
. driver = {
2013-03-30 12:53:56 +02:00
. name = " usbmisc_imx " ,
. of_match_table = usbmisc_imx_dt_ids ,
2012-09-12 14:58:05 +03:00
} ,
} ;
2013-06-13 17:59:59 +03:00
module_platform_driver ( usbmisc_imx_driver ) ;
2012-09-12 14:58:05 +03:00
2013-03-30 12:53:56 +02:00
MODULE_ALIAS ( " platform:usbmisc-imx " ) ;
2018-09-02 19:36:50 +02:00
MODULE_LICENSE ( " GPL " ) ;
2013-03-30 12:53:56 +02:00
MODULE_DESCRIPTION ( " driver for imx usb non-core registers " ) ;
2012-09-12 14:58:05 +03:00
MODULE_AUTHOR ( " Richard Zhao <richard.zhao@freescale.com> " ) ;