On Mon, Apr 18, 2016 at 04:17:48PM +0800, Li Jun wrote: > Adds imx7d usb charger detection implementation, which needs pull up > DP line before do secondary detection and pull down DP afterwards. > You can merge patch 3 and patch 4 as one patch. Acked-by: Peter Chen <peter.chen@xxxxxxx> > Signed-off-by: Li Jun <jun.li@xxxxxxx> > --- > drivers/usb/chipidea/ci_hdrc_imx.c | 3 +- > drivers/usb/chipidea/usbmisc_imx.c | 157 +++++++++++++++++++++++++++++++++++++ > 2 files changed, 159 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c > index 83a59d6..5a6b712 100644 > --- a/drivers/usb/chipidea/ci_hdrc_imx.c > +++ b/drivers/usb/chipidea/ci_hdrc_imx.c > @@ -67,7 +67,8 @@ static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { > }; > > static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { > - .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, > + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | > + CI_HDRC_PULL_DP_FOR_CHARGER, > }; > > static const struct of_device_id ci_hdrc_imx_dt_ids[] = { > diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c > index daa7c02..4f4ae56 100644 > --- a/drivers/usb/chipidea/usbmisc_imx.c > +++ b/drivers/usb/chipidea/usbmisc_imx.c > @@ -74,6 +74,11 @@ > #define VF610_OVER_CUR_DIS BIT(7) > > #define MX7D_USBNC_USB_CTRL2 0x4 > +#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_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) > @@ -81,6 +86,18 @@ > #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_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) > + > struct usbmisc_ops { > /* It's called once when probe a usb device */ > int (*init)(struct imx_usbmisc_data *data); > @@ -385,6 +402,144 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) > return 0; > } > > +static void imx7d_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); > + int i, data_pin_contact_count = 0; > + unsigned long flags; > + u32 val; > + > + spin_lock_irqsave(&usbmisc->lock, flags); > + > + /* 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"); > + spin_unlock_irqrestore(&usbmisc->lock, flags); > + return -EINVAL; > + } > + > + /* > + * - Do not check whether a charger is connected to the USB port > + * - Check whether the USB plug has been in contact with each other > + */ > + 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); > + > + /* Check if plug is connected */ > + 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; > + else > + usleep_range(5000, 10000); > + } else { > + data_pin_contact_count = 0; > + usleep_range(5000, 6000); > + } > + } > + > + if (i == 100) { > + dev_err(data->dev, > + "VBUS is coming from a dedicated power supply.\n"); > + imx7d_disable_charger_detector(data); > + return -ENXIO; > + } > + > + return 0; > +} > + > +static enum usb_charger_type usbmisc_imx7d_charger_det( > + struct imx_usbmisc_data *data) > +{ > + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); > + enum usb_charger_type ret = UNKNOWN_TYPE; > + unsigned long flags; > + u32 val; > + > + if (imx7d_charger_data_contact_detect(data)) > + return ret; > + > + spin_lock_irqsave(&usbmisc->lock, flags); > + /* Set OPMODE to be non-driving mode */ > + 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); > + > + /* Do not check data contact */ > + 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, > + usbmisc->base + MX7D_USB_OTG_PHY_CFG2); > + spin_unlock_irqrestore(&usbmisc->lock, flags); > + > + usleep_range(1000, 2000); > + > + /* Check if it is a charger */ > + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); > + if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) { > + dev_dbg(data->dev, "It is a stardard downstream port\n"); > + ret = SDP_TYPE; > + } > + > + imx7d_disable_charger_detector(data); > + > + return ret; > +} > + > +static enum usb_charger_type usbmisc_imx7d_charger_sec_det( > + struct imx_usbmisc_data *data) > +{ > + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); > + enum usb_charger_type ret = UNKNOWN_TYPE; > + int val; > + > + msleep(80); > + > + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS); > + if (val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE1) > + ret = DCP_TYPE; > + else > + ret = CDP_TYPE; > + > + return ret; > +} > + > static const struct usbmisc_ops imx25_usbmisc_ops = { > .init = usbmisc_imx25_init, > .post = usbmisc_imx25_post, > @@ -415,6 +570,8 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { > static const struct usbmisc_ops imx7d_usbmisc_ops = { > .init = usbmisc_imx7d_init, > .set_wakeup = usbmisc_imx7d_set_wakeup, > + .charger_det = usbmisc_imx7d_charger_det, > + .charger_secondary_det = usbmisc_imx7d_charger_sec_det, > }; > > int imx_usbmisc_init(struct imx_usbmisc_data *data) > -- > 1.9.1 > -- Best Regards, Peter Chen -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html