#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)
#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)
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); /* It's called when we need to enable/disable usb wakeup */ int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled); /* 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); /* usb charger detection */ int (*charger_detection)(struct imx_usbmisc_data *data); /* It's called when system resume from usb power lost */ int (*power_lost_check)(struct imx_usbmisc_data *data); /* It's called when device controller changed pullup status */ void (*pullup)(struct imx_usbmisc_data *data, bool on); /* It's called during suspend/resume to save power */ void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on);
};
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);
/* * 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;
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);
/* * 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;
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;
}
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);
/* Select a 24 MHz reference clock for the PHY */
val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
val |= MX53_USB_PLL_DIV_24_MHZ;
writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
spin_lock_irqsave(&usbmisc->lock, flags);
switch (data->index) { case 0: if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
writel(val, reg);
} break; case 1: if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
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); 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);
}
} if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
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 */
reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
| MX53_USB_UHx_CTRL_ULPI_INT_EN;
writel(val, reg);
/* * 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; elseif (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
} /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base + data->index * 4);
staticint 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;
} elseif (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;
}
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;
/* * 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 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; elseif (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
} /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
/* 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);
/* 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);
/* TVDMSRC_ON */
msleep(40);
/* * 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;
}
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);
staticint imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data)
{ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsignedlong 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;
}
/* 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);
/* TVDPSRC_ON */
msleep(40);
/* 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;
}
/* 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);
spin_lock_irqsave(&usbmisc->lock, flags); /* * Disable VBUS valid comparator when in suspend mode, * when OTG is disabled and DRVVBUS0 is asserted case * the Bandgap circuitry and VBUS Valid comparator are * still powered, even in Suspend or Sleep mode.
*/
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); if (on)
val |= MX7D_USB_OTG_PHY_CFG2_DRVVBUS0; else
val &= ~MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
/* * 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; elseif (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
} /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base);
spin_unlock_irqrestore(&usbmisc->lock, flags); /* * Here use a power on reset value to judge * if the controller experienced a power lost
*/ if (val == 0x30001000) return 1; else return 0;
}
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + data->index * 4);
spin_unlock_irqrestore(&usbmisc->lock, flags); /* * Here use a power on reset value to judge * if the controller experienced a power lost
*/ if (val == 0x30001000) return 1; else return 0;
}
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, "hsic_set_clk failed, ret=%d\n", ret); goto hsic_set_clk_fail;
}
if (usbmisc->ops->vbus_comparator_on)
usbmisc->ops->vbus_comparator_on(data, true);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.