summaryrefslogtreecommitdiffstats
path: root/drivers/usb/chipidea/usbmisc_imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea/usbmisc_imx.c')
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c183
1 files changed, 176 insertions, 7 deletions
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index def80ff547e4..097ffbca0bd9 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -64,10 +64,22 @@
#define MX6_BM_OVER_CUR_DIS BIT(7)
#define MX6_BM_OVER_CUR_POLARITY BIT(8)
#define MX6_BM_WAKEUP_ENABLE BIT(10)
+#define MX6_BM_UTMI_ON_CLOCK BIT(13)
#define MX6_BM_ID_WAKEUP BIT(16)
#define MX6_BM_VBUS_WAKEUP BIT(17)
#define MX6SX_BM_DPDM_WAKEUP_EN BIT(29)
#define MX6_BM_WAKEUP_INTR BIT(31)
+
+#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
@@ -94,6 +106,10 @@ struct usbmisc_ops {
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);
};
struct imx_usbmisc {
@@ -120,6 +136,14 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
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:
@@ -129,6 +153,13 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
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;
+
writel(val, usbmisc->base);
break;
@@ -340,11 +371,17 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
reg = readl(usbmisc->base + data->index * 4);
if (data->disable_oc) {
reg |= MX6_BM_OVER_CUR_DIS;
- } else if (data->oc_polarity == 1) {
- /* High active */
- reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
} else {
- reg &= ~(MX6_BM_OVER_CUR_DIS);
+ 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;
}
writel(reg, usbmisc->base + data->index * 4);
@@ -353,6 +390,18 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
writel(reg | MX6_BM_NON_BURST_SETTING,
usbmisc->base + data->index * 4);
+ /* 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);
+ }
+
spin_unlock_irqrestore(&usbmisc->lock, flags);
usbmisc_imx6q_set_wakeup(data, false);
@@ -360,6 +409,79 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
return 0;
}
+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;
+}
+
+
static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
{
void __iomem *reg = NULL;
@@ -385,6 +507,13 @@ static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
spin_unlock_irqrestore(&usbmisc->lock, flags);
}
+ /* 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);
+ }
+
return 0;
}
@@ -444,9 +573,17 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
reg = readl(usbmisc->base);
if (data->disable_oc) {
reg |= MX6_BM_OVER_CUR_DIS;
- } else if (data->oc_polarity == 1) {
- /* High active */
- reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
+ } 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;
}
writel(reg, usbmisc->base);
@@ -454,6 +591,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
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);
@@ -481,6 +619,8 @@ static const struct usbmisc_ops imx53_usbmisc_ops = {
static const struct usbmisc_ops imx6q_usbmisc_ops = {
.set_wakeup = usbmisc_imx6q_set_wakeup,
.init = usbmisc_imx6q_init,
+ .hsic_set_connect = usbmisc_imx6_hsic_set_connect,
+ .hsic_set_clk = usbmisc_imx6_hsic_set_clk,
};
static const struct usbmisc_ops vf610_usbmisc_ops = {
@@ -490,6 +630,8 @@ static const struct usbmisc_ops vf610_usbmisc_ops = {
static const struct usbmisc_ops imx6sx_usbmisc_ops = {
.set_wakeup = usbmisc_imx6q_set_wakeup,
.init = usbmisc_imx6sx_init,
+ .hsic_set_connect = usbmisc_imx6_hsic_set_connect,
+ .hsic_set_clk = usbmisc_imx6_hsic_set_clk,
};
static const struct usbmisc_ops imx7d_usbmisc_ops = {
@@ -546,6 +688,33 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
}
EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
+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);
+
+int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return 0;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->hsic_set_clk || !data->hsic)
+ return 0;
+ return usbmisc->ops->hsic_set_clk(data, on);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
static const struct of_device_id usbmisc_imx_dt_ids[] = {
{
.compatible = "fsl,imx25-usbmisc",