summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea/usbmisc_imx.c
diff options
context:
space:
mode:
authorLi Jun <jun.li@freescale.com>2015-06-04 17:11:18 +0800
committerNitin Garg <nitin.garg@freescale.com>2015-09-17 09:23:30 -0500
commit743319e177235dd954a374c564aa50f72f9febb1 (patch)
treeb1e1565cbb81dd2fd93b7cb314ac1c588a6623d2 /drivers/usb/chipidea/usbmisc_imx.c
parent2a9bf053d7e60a6aa5163f35a673c03cbdd2096a (diff)
MLK-11005-4 usb: chipidea: imx: add OTG ADP for imx usb driver
Add usb otg ADP support for imx usb driver, and enable it on i.MX7D. Signed-off-by: Li Jun <jun.li@freescale.com>
Diffstat (limited to 'drivers/usb/chipidea/usbmisc_imx.c')
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 34dc099a98f5..f0f8edc078d6 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -98,6 +98,25 @@
#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 OTG_DRVVBUS0 BIT(16)
+#define OTG_ADP_PRBENB0 BIT(13)
+#define USB_OTG_PHY_CFG2 0x34
+
+#define OTG_ADP_PRB_EN BIT(23)
+#define OTG_ADP_PRB_INT_EN BIT(22)
+#define OTG_ADP_SNS_INT_EN BIT(21)
+#define USB_OTG_ADP_CFG1 0x50
+
+#define ADP_CHRG_SWCMP BIT(7)
+#define ADP_CHRG_SWTIME_MASK (0xff << 8)
+#define USB_OTG_ADP_CFG2 0x54
+
+#define OTG_ADP_PRB_INT_STS BIT(27)
+#define OTG_ADP_SNS_INT_STS BIT(26)
+#define OTG_ADP_PRB_TIMER BIT(0)
+#define USB_OTG_ADP_STS 0x58
+
+#define OTG_ADP_CHRG_DELTA 0xC
struct usbmisc_ops {
/* It's called once when probe a usb device */
int (*init)(struct imx_usbmisc_data *data);
@@ -111,6 +130,20 @@ struct usbmisc_ops {
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 OTG ADP probe enable */
+ void (*adp_probe_enable)(struct imx_usbmisc_data *data, bool on);
+ /* USB OTG ADP probe start */
+ void (*adp_probe_start)(struct imx_usbmisc_data *data);
+ /* USB OTG ADP sense enable */
+ void (*adp_sense_enable)(struct imx_usbmisc_data *data, bool on);
+ /* Check if usb OTG ADP probe irq */
+ bool (*is_probe_int)(struct imx_usbmisc_data *data);
+ /* Check if usb OTG ADP sense irq */
+ bool (*is_sense_int)(struct imx_usbmisc_data *data);
+ /* Check if usb OTG ADP sense probe is on-going by A-dev */
+ bool (*adp_sense_connection)(struct imx_usbmisc_data *data);
+ /* Check if it's a device attach or dettach event */
+ bool (*adp_attach_event)(struct imx_usbmisc_data *data);
};
struct imx_usbmisc {
@@ -460,6 +493,175 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
return 0;
}
+static void usbmisc_otg_adp_update_charge_time(struct imx_usbmisc *usbmisc)
+{
+ u32 reg;
+
+ /* ADP_CHRG_SWTIME to be 0, force to generate probe irq */
+ reg = readl(usbmisc->base + USB_OTG_ADP_CFG2);
+ reg = reg & ~ADP_CHRG_SWTIME_MASK;
+
+ /* Use ADP_CHRG_SWCMP */
+ reg = reg | ADP_CHRG_SWCMP;
+ writel(reg, usbmisc->base + USB_OTG_ADP_CFG2);
+}
+
+static void usbmisc_otg_adp_prb_int(struct imx_usbmisc *usbmisc, bool on)
+{
+ u32 reg;
+
+ reg = readl(usbmisc->base + USB_OTG_ADP_CFG1);
+ if (on)
+ writel(reg | OTG_ADP_PRB_INT_EN,
+ usbmisc->base + USB_OTG_ADP_CFG1);
+ else
+ writel(reg & ~OTG_ADP_PRB_INT_EN,
+ usbmisc->base + USB_OTG_ADP_CFG1);
+}
+
+static void usbmisc_otg_adp_probe_enable(struct imx_usbmisc_data *data, bool on)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+
+ /* Disable probe irq */
+ if (!on)
+ usbmisc_otg_adp_prb_int(usbmisc, false);
+
+ /* Disable DRVVBUS */
+ reg = readl(usbmisc->base + USB_OTG_PHY_CFG2);
+ if (on)
+ writel(reg & ~OTG_DRVVBUS0, usbmisc->base + USB_OTG_PHY_CFG2);
+ else
+ writel(reg | OTG_DRVVBUS0, usbmisc->base + USB_OTG_PHY_CFG2);
+
+ reg = readl(usbmisc->base + USB_OTG_ADP_CFG1);
+
+ if (on) {
+ usbmisc_otg_adp_update_charge_time(usbmisc);
+ writel(reg | OTG_ADP_PRB_EN, usbmisc->base + USB_OTG_ADP_CFG1);
+ } else {
+ writel(reg & ~OTG_ADP_PRB_EN, usbmisc->base + USB_OTG_ADP_CFG1);
+ }
+
+ /* Enable probe irq */
+ if (on)
+ usbmisc_otg_adp_prb_int(usbmisc, true);
+
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
+static void usbmisc_otg_adp_probe_start(struct imx_usbmisc_data *data)
+{
+ data->adp_prb_n2 = 0;
+ data->adp_prb_n1 = 0;
+ data->adp_prb_n = 0;
+ usbmisc_otg_adp_probe_enable(data, true);
+}
+
+static u32 usbmisc_otg_adp_charge_time(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+
+ return readl(usbmisc->base + USB_OTG_ADP_STS) & 0xff;
+}
+
+static bool usbmisc_otg_adp_is_probe_int(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+
+ if (readl(usbmisc->base + USB_OTG_ADP_STS) & OTG_ADP_PRB_INT_STS) {
+ usbmisc_otg_adp_probe_enable(data, false);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool usbmisc_otg_adp_is_attach_event(struct imx_usbmisc_data *data)
+{
+ u32 adp_delta;
+
+ if (data->adp_prb_n2) {
+ data->adp_prb_n2 = data->adp_prb_n1;
+ data->adp_prb_n1 = data->adp_prb_n;
+ }
+ data->adp_prb_n = usbmisc_otg_adp_charge_time(data);
+
+ /* First probe charge n2==n1==0 */
+ if (data->adp_prb_n1 == 0) {
+ data->adp_prb_n1 = data->adp_prb_n;
+ data->adp_prb_n2 = data->adp_prb_n1;
+ }
+
+ dev_dbg(data->dev, "ADP the charge time is %x:%x:%x!\n",
+ data->adp_prb_n2,
+ data->adp_prb_n1,
+ data->adp_prb_n);
+
+ if (data->adp_prb_n2 > data->adp_prb_n)
+ adp_delta = data->adp_prb_n2 > data->adp_prb_n;
+ else
+ adp_delta = data->adp_prb_n - data->adp_prb_n2;
+
+ if (adp_delta > OTG_ADP_CHRG_DELTA) {
+ dev_dbg(data->dev, "ADP probe event charge delta: %x!\n",
+ adp_delta);
+ return true;
+ }
+
+ return false;
+}
+
+static void usbmisc_otg_adp_sense_enable(struct imx_usbmisc_data *data, bool on)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = readl(usbmisc->base + USB_OTG_ADP_CFG1);
+ if (on)
+ /* Enable OTG ADP sense irq */
+ writel(reg | OTG_ADP_SNS_INT_EN,
+ usbmisc->base + USB_OTG_ADP_CFG1);
+ else
+ writel(reg & ~OTG_ADP_SNS_INT_EN,
+ usbmisc->base + USB_OTG_ADP_CFG1);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
+static bool usbmisc_otg_adp_is_sense_int(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+
+ if (readl(usbmisc->base + USB_OTG_ADP_STS) & OTG_ADP_SNS_INT_STS) {
+ usbmisc_otg_adp_sense_enable(data, false);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static bool usbmisc_otg_adp_sense_connection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+
+ /* Judge if A-dev probes by checking if sense irq is enabled */
+ if (readl(usbmisc->base + USB_OTG_ADP_CFG1) & OTG_ADP_SNS_INT_EN) {
+ /* Disable sense int */
+ usbmisc_otg_adp_sense_enable(data, false);
+ dev_info(data->dev, "OTG device detached\n");
+ return false;
+ } else {
+ usbmisc_otg_adp_sense_enable(data, true);
+ return true;
+ }
+}
+
static int usbmisc_imx7d_set_wakeup
(struct imx_usbmisc_data *data, bool enabled)
{
@@ -570,6 +772,13 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = {
.init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
.power_lost_check = usbmisc_imx7d_power_lost_check,
+ .adp_probe_enable = usbmisc_otg_adp_probe_enable,
+ .adp_probe_start = usbmisc_otg_adp_probe_start,
+ .adp_sense_enable = usbmisc_otg_adp_sense_enable,
+ .is_probe_int = usbmisc_otg_adp_is_probe_int,
+ .is_sense_int = usbmisc_otg_adp_is_sense_int,
+ .adp_sense_connection = usbmisc_otg_adp_sense_connection,
+ .adp_attach_event = usbmisc_otg_adp_is_attach_event,
};
int imx_usbmisc_init(struct imx_usbmisc_data *data)
@@ -656,6 +865,104 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
}
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
+void imx_usbmisc_adp_probe_enable(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->adp_probe_enable)
+ return;
+ usbmisc->ops->adp_probe_enable(data, true);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_probe_enable);
+
+void imx_usbmisc_adp_probe_start(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->adp_probe_start)
+ return;
+ usbmisc->ops->adp_probe_start(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_probe_start);
+
+void imx_usbmisc_adp_sense_enable(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->adp_sense_enable)
+ return;
+ usbmisc->ops->adp_sense_enable(data, true);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_sense_enable);
+
+bool imx_usbmisc_adp_is_probe_int(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return 0;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->is_probe_int)
+ return 0;
+ return usbmisc->ops->is_probe_int(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_is_probe_int);
+
+bool imx_usbmisc_adp_is_sense_int(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return 0;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->is_sense_int)
+ return 0;
+ return usbmisc->ops->is_sense_int(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_is_sense_int);
+
+bool imx_usbmisc_adp_sense_connection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return 0;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->adp_sense_connection)
+ return 0;
+ return usbmisc->ops->adp_sense_connection(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_sense_connection);
+
+bool imx_usbmisc_adp_attach_event(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return 0;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (!usbmisc->ops->adp_attach_event)
+ return 0;
+ return usbmisc->ops->adp_attach_event(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_adp_attach_event);
+
static const struct of_device_id usbmisc_imx_dt_ids[] = {
{
.compatible = "fsl,imx25-usbmisc",