|
@@ -34,6 +34,7 @@
|
|
|
#include <linux/dma-mapping.h>
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/err.h>
|
|
|
+#include <linux/usb/musb-omap.h>
|
|
|
|
|
|
#include "musb_core.h"
|
|
|
#include "omap2430.h"
|
|
@@ -41,9 +42,13 @@
|
|
|
struct omap2430_glue {
|
|
|
struct device *dev;
|
|
|
struct platform_device *musb;
|
|
|
+ enum omap_musb_vbus_id_status status;
|
|
|
+ struct work_struct omap_musb_mailbox_work;
|
|
|
};
|
|
|
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
|
|
|
|
|
+struct omap2430_glue *_glue;
|
|
|
+
|
|
|
static struct timer_list musb_idle_timer;
|
|
|
|
|
|
static void musb_do_idle(unsigned long _musb)
|
|
@@ -223,50 +228,63 @@ static inline void omap2430_low_level_init(struct musb *musb)
|
|
|
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
|
|
}
|
|
|
|
|
|
-static int musb_otg_notifications(struct notifier_block *nb,
|
|
|
- unsigned long event, void *unused)
|
|
|
+void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
|
|
|
{
|
|
|
- struct musb *musb = container_of(nb, struct musb, nb);
|
|
|
+ struct omap2430_glue *glue = _glue;
|
|
|
+ struct musb *musb = glue_to_musb(glue);
|
|
|
|
|
|
- musb->xceiv_event = event;
|
|
|
- schedule_work(&musb->otg_notifier_work);
|
|
|
+ glue->status = status;
|
|
|
+ if (!musb) {
|
|
|
+ dev_err(glue->dev, "musb core is not yet ready\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- return NOTIFY_OK;
|
|
|
+ schedule_work(&glue->omap_musb_mailbox_work);
|
|
|
}
|
|
|
+EXPORT_SYMBOL_GPL(omap_musb_mailbox);
|
|
|
|
|
|
-static void musb_otg_notifier_work(struct work_struct *data_notifier_work)
|
|
|
+static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
|
|
{
|
|
|
- struct musb *musb = container_of(data_notifier_work, struct musb, otg_notifier_work);
|
|
|
+ struct musb *musb = glue_to_musb(glue);
|
|
|
struct device *dev = musb->controller;
|
|
|
struct musb_hdrc_platform_data *pdata = dev->platform_data;
|
|
|
struct omap_musb_board_data *data = pdata->board_data;
|
|
|
+ struct usb_otg *otg = musb->xceiv->otg;
|
|
|
|
|
|
- switch (musb->xceiv_event) {
|
|
|
- case USB_EVENT_ID:
|
|
|
- dev_dbg(musb->controller, "ID GND\n");
|
|
|
+ switch (glue->status) {
|
|
|
+ case OMAP_MUSB_ID_GROUND:
|
|
|
+ dev_dbg(dev, "ID GND\n");
|
|
|
|
|
|
+ otg->default_a = true;
|
|
|
+ musb->xceiv->state = OTG_STATE_A_IDLE;
|
|
|
+ musb->xceiv->last_event = USB_EVENT_ID;
|
|
|
if (!is_otg_enabled(musb) || musb->gadget_driver) {
|
|
|
- pm_runtime_get_sync(musb->controller);
|
|
|
+ pm_runtime_get_sync(dev);
|
|
|
usb_phy_init(musb->xceiv);
|
|
|
omap2430_musb_set_vbus(musb, 1);
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
- case USB_EVENT_VBUS:
|
|
|
- dev_dbg(musb->controller, "VBUS Connect\n");
|
|
|
+ case OMAP_MUSB_VBUS_VALID:
|
|
|
+ dev_dbg(dev, "VBUS Connect\n");
|
|
|
|
|
|
+ otg->default_a = false;
|
|
|
+ musb->xceiv->state = OTG_STATE_B_IDLE;
|
|
|
+ musb->xceiv->last_event = USB_EVENT_VBUS;
|
|
|
if (musb->gadget_driver)
|
|
|
- pm_runtime_get_sync(musb->controller);
|
|
|
+ pm_runtime_get_sync(dev);
|
|
|
usb_phy_init(musb->xceiv);
|
|
|
break;
|
|
|
|
|
|
- case USB_EVENT_NONE:
|
|
|
- dev_dbg(musb->controller, "VBUS Disconnect\n");
|
|
|
+ case OMAP_MUSB_ID_FLOAT:
|
|
|
+ case OMAP_MUSB_VBUS_OFF:
|
|
|
+ dev_dbg(dev, "VBUS Disconnect\n");
|
|
|
|
|
|
+ musb->xceiv->last_event = USB_EVENT_NONE;
|
|
|
if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
|
|
|
if (musb->gadget_driver) {
|
|
|
- pm_runtime_mark_last_busy(musb->controller);
|
|
|
- pm_runtime_put_autosuspend(musb->controller);
|
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
|
}
|
|
|
|
|
|
if (data->interface_type == MUSB_INTERFACE_UTMI) {
|
|
@@ -276,15 +294,24 @@ static void musb_otg_notifier_work(struct work_struct *data_notifier_work)
|
|
|
usb_phy_shutdown(musb->xceiv);
|
|
|
break;
|
|
|
default:
|
|
|
- dev_dbg(musb->controller, "ID float\n");
|
|
|
+ dev_dbg(dev, "ID float\n");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+static void omap_musb_mailbox_work(struct work_struct *mailbox_work)
|
|
|
+{
|
|
|
+ struct omap2430_glue *glue = container_of(mailbox_work,
|
|
|
+ struct omap2430_glue, omap_musb_mailbox_work);
|
|
|
+ omap_musb_set_mailbox(glue);
|
|
|
+}
|
|
|
+
|
|
|
static int omap2430_musb_init(struct musb *musb)
|
|
|
{
|
|
|
u32 l;
|
|
|
int status = 0;
|
|
|
struct device *dev = musb->controller;
|
|
|
+ struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
|
|
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
|
|
struct omap_musb_board_data *data = plat->board_data;
|
|
|
|
|
@@ -292,14 +319,12 @@ static int omap2430_musb_init(struct musb *musb)
|
|
|
* up through ULPI. TWL4030-family PMICs include one,
|
|
|
* which needs a driver, drivers aren't always needed.
|
|
|
*/
|
|
|
- musb->xceiv = usb_get_transceiver();
|
|
|
- if (!musb->xceiv) {
|
|
|
+ musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
|
|
+ if (IS_ERR_OR_NULL(musb->xceiv)) {
|
|
|
pr_err("HS USB OTG: no transceiver configured\n");
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
- INIT_WORK(&musb->otg_notifier_work, musb_otg_notifier_work);
|
|
|
-
|
|
|
status = pm_runtime_get_sync(dev);
|
|
|
if (status < 0) {
|
|
|
dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status);
|
|
@@ -326,14 +351,11 @@ static int omap2430_musb_init(struct musb *musb)
|
|
|
musb_readl(musb->mregs, OTG_INTERFSEL),
|
|
|
musb_readl(musb->mregs, OTG_SIMENABLE));
|
|
|
|
|
|
- musb->nb.notifier_call = musb_otg_notifications;
|
|
|
- status = usb_register_notifier(musb->xceiv, &musb->nb);
|
|
|
-
|
|
|
- if (status)
|
|
|
- dev_dbg(musb->controller, "notification register failed\n");
|
|
|
-
|
|
|
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
|
|
|
|
|
|
+ if (glue->status != OMAP_MUSB_UNKNOWN)
|
|
|
+ omap_musb_set_mailbox(glue);
|
|
|
+
|
|
|
pm_runtime_put_noidle(musb->controller);
|
|
|
return 0;
|
|
|
|
|
@@ -346,12 +368,13 @@ static void omap2430_musb_enable(struct musb *musb)
|
|
|
u8 devctl;
|
|
|
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
|
|
struct device *dev = musb->controller;
|
|
|
+ struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
|
|
struct musb_hdrc_platform_data *pdata = dev->platform_data;
|
|
|
struct omap_musb_board_data *data = pdata->board_data;
|
|
|
|
|
|
- switch (musb->xceiv->last_event) {
|
|
|
+ switch (glue->status) {
|
|
|
|
|
|
- case USB_EVENT_ID:
|
|
|
+ case OMAP_MUSB_ID_GROUND:
|
|
|
usb_phy_init(musb->xceiv);
|
|
|
if (data->interface_type != MUSB_INTERFACE_UTMI)
|
|
|
break;
|
|
@@ -370,7 +393,7 @@ static void omap2430_musb_enable(struct musb *musb)
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
- case USB_EVENT_VBUS:
|
|
|
+ case OMAP_MUSB_VBUS_VALID:
|
|
|
usb_phy_init(musb->xceiv);
|
|
|
break;
|
|
|
|
|
@@ -381,17 +404,18 @@ static void omap2430_musb_enable(struct musb *musb)
|
|
|
|
|
|
static void omap2430_musb_disable(struct musb *musb)
|
|
|
{
|
|
|
- if (musb->xceiv->last_event)
|
|
|
+ struct device *dev = musb->controller;
|
|
|
+ struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
|
|
+
|
|
|
+ if (glue->status != OMAP_MUSB_UNKNOWN)
|
|
|
usb_phy_shutdown(musb->xceiv);
|
|
|
}
|
|
|
|
|
|
static int omap2430_musb_exit(struct musb *musb)
|
|
|
{
|
|
|
del_timer_sync(&musb_idle_timer);
|
|
|
- cancel_work_sync(&musb->otg_notifier_work);
|
|
|
|
|
|
omap2430_low_level_exit(musb);
|
|
|
- usb_put_transceiver(musb->xceiv);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -418,7 +442,7 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
|
|
|
struct omap2430_glue *glue;
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
|
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
|
|
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
|
|
|
if (!glue) {
|
|
|
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
|
|
goto err0;
|
|
@@ -427,7 +451,7 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
|
|
|
musb = platform_device_alloc("musb-hdrc", -1);
|
|
|
if (!musb) {
|
|
|
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
|
|
- goto err1;
|
|
|
+ goto err0;
|
|
|
}
|
|
|
|
|
|
musb->dev.parent = &pdev->dev;
|
|
@@ -436,22 +460,31 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
|
|
|
|
|
|
glue->dev = &pdev->dev;
|
|
|
glue->musb = musb;
|
|
|
+ glue->status = OMAP_MUSB_UNKNOWN;
|
|
|
|
|
|
pdata->platform_ops = &omap2430_ops;
|
|
|
|
|
|
platform_set_drvdata(pdev, glue);
|
|
|
|
|
|
+ /*
|
|
|
+ * REVISIT if we ever have two instances of the wrapper, we will be
|
|
|
+ * in big trouble
|
|
|
+ */
|
|
|
+ _glue = glue;
|
|
|
+
|
|
|
+ INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
|
|
|
+
|
|
|
ret = platform_device_add_resources(musb, pdev->resource,
|
|
|
pdev->num_resources);
|
|
|
if (ret) {
|
|
|
dev_err(&pdev->dev, "failed to add resources\n");
|
|
|
- goto err2;
|
|
|
+ goto err1;
|
|
|
}
|
|
|
|
|
|
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
|
|
if (ret) {
|
|
|
dev_err(&pdev->dev, "failed to add platform_data\n");
|
|
|
- goto err2;
|
|
|
+ goto err1;
|
|
|
}
|
|
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
@@ -459,16 +492,13 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
|
|
|
ret = platform_device_add(musb);
|
|
|
if (ret) {
|
|
|
dev_err(&pdev->dev, "failed to register musb device\n");
|
|
|
- goto err2;
|
|
|
+ goto err1;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-err2:
|
|
|
- platform_device_put(musb);
|
|
|
-
|
|
|
err1:
|
|
|
- kfree(glue);
|
|
|
+ platform_device_put(musb);
|
|
|
|
|
|
err0:
|
|
|
return ret;
|
|
@@ -478,9 +508,9 @@ static int __devexit omap2430_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
struct omap2430_glue *glue = platform_get_drvdata(pdev);
|
|
|
|
|
|
+ cancel_work_sync(&glue->omap_musb_mailbox_work);
|
|
|
platform_device_del(glue->musb);
|
|
|
platform_device_put(glue->musb);
|
|
|
- kfree(glue);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -546,7 +576,7 @@ static int __init omap2430_init(void)
|
|
|
{
|
|
|
return platform_driver_register(&omap2430_driver);
|
|
|
}
|
|
|
-module_init(omap2430_init);
|
|
|
+subsys_initcall(omap2430_init);
|
|
|
|
|
|
static void __exit omap2430_exit(void)
|
|
|
{
|