MLK-19850-5 usb: chipidea: imx: add imx7d USB charger detection
Since USB generic PHY doesn't have USB charger interface, we implement imx7d (using USB generic PHY) USB charger detection at usbmisc. Signed-off-by: Peter Chen <peter.chen@nxp.com>5.4-rM2-2.2.x-imx-squashed
parent
b90bd6962c
commit
b4b090d280
|
@ -277,6 +277,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
|
|||
struct device *dev = ci->dev->parent;
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
struct imx_usbmisc_data *mdata = data->usbmisc_data;
|
||||
|
||||
switch (event) {
|
||||
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
|
||||
|
@ -292,6 +293,12 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
|
|||
dev_err(dev,
|
||||
"hsic_set_connect failed, err=%d\n", ret);
|
||||
break;
|
||||
case CI_HDRC_CONTROLLER_VBUS_EVENT:
|
||||
if (ci->vbus_active)
|
||||
ret = imx_usbmisc_charger_detection(mdata, true);
|
||||
else
|
||||
ret = imx_usbmisc_charger_detection(mdata, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -411,6 +418,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
pdata.usb_phy = data->phy;
|
||||
if (data->usbmisc_data)
|
||||
data->usbmisc_data->usb_phy = data->phy;
|
||||
|
||||
if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
|
||||
of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
|
||||
|
|
|
@ -27,6 +27,7 @@ struct imx_usbmisc_data {
|
|||
unsigned int ext_vbus:1; /* Vbus from exteranl event */
|
||||
int emp_curr_control;
|
||||
int dc_vol_level_adjust;
|
||||
struct usb_phy *usb_phy;
|
||||
};
|
||||
|
||||
int imx_usbmisc_init(struct imx_usbmisc_data *data);
|
||||
|
@ -35,5 +36,6 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
|
|||
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
|
||||
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
|
||||
int imx_usbmisc_power_lost_check(struct imx_usbmisc_data *);
|
||||
int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
|
||||
|
||||
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
|
||||
|
|
|
@ -95,19 +95,41 @@
|
|||
#define VF610_OVER_CUR_DIS BIT(7)
|
||||
|
||||
#define MX7D_USBNC_USB_CTRL2 0x4
|
||||
/* The default DM/DP value is pull-down */
|
||||
#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
|
||||
#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
|
||||
#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
|
||||
#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
|
||||
#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
|
||||
BIT(14) | BIT(15))
|
||||
#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
|
||||
#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
|
||||
#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_AUTO_RESUME BIT(2)
|
||||
#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)
|
||||
#define MX7D_USBNC_AUTO_RESUME BIT(2)
|
||||
|
||||
#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)
|
||||
#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
|
||||
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
|
||||
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
|
||||
#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
|
||||
#define MX7D_USB_OTG_PHY_CFG2 0x34
|
||||
|
||||
#define MX7D_USB_OTG_PHY_STATUS 0x3c
|
||||
#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
|
||||
#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
|
||||
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
|
||||
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
|
||||
|
||||
#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
|
||||
MX6_BM_ID_WAKEUP)
|
||||
|
@ -125,6 +147,8 @@ struct usbmisc_ops {
|
|||
int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
|
||||
/* It's called when system resume from usb power lost */
|
||||
int (*power_lost_check)(struct imx_usbmisc_data *data);
|
||||
/* usb charger detection */
|
||||
int (*charger_detection)(struct imx_usbmisc_data *data);
|
||||
};
|
||||
|
||||
struct imx_usbmisc {
|
||||
|
@ -682,6 +706,188 @@ static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
|
@ -803,6 +1009,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = {
|
|||
.init = usbmisc_imx7ulp_init,
|
||||
.set_wakeup = usbmisc_imx7d_set_wakeup,
|
||||
.power_lost_check = usbmisc_imx7d_power_lost_check,
|
||||
.charger_detection = imx7d_charger_detection,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
|
||||
|
@ -904,6 +1111,38 @@ int imx_usbmisc_power_lost_check(struct imx_usbmisc_data *data)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(imx_usbmisc_power_lost_check);
|
||||
|
||||
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);
|
||||
|
||||
static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx25-usbmisc",
|
||||
|
|
Loading…
Reference in New Issue