usb: musb: twl: use mailbox API to send VBUS or ID events

The atomic notifier from twl4030/twl6030 to notifiy VBUS and ID events,
is replaced by a direct call to omap musb blue.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Kishon Vijay Abraham I 2012-06-22 17:40:52 +05:30 committed by Felipe Balbi
parent 1e5acb8d61
commit c9721438c0
4 changed files with 133 additions and 84 deletions

View file

@ -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,11 +42,13 @@
struct omap2430_glue {
struct device *dev;
struct platform_device *musb;
u8 xceiv_event;
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)
@ -225,54 +228,58 @@ 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 device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
struct omap2430_glue *glue = _glue;
struct musb *musb = glue_to_musb(glue);
glue->status = status;
if (!musb) {
dev_err(glue->dev, "musb core is not yet ready\n");
return;
}
glue->xceiv_event = event;
schedule_work(&glue->omap_musb_mailbox_work);
return NOTIFY_OK;
}
EXPORT_SYMBOL_GPL(omap_musb_mailbox);
static void omap_musb_mailbox_work(struct work_struct *data_notifier_work)
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
struct omap2430_glue *glue = container_of(data_notifier_work,
struct omap2430_glue, omap_musb_mailbox_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;
switch (glue->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");
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");
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) {
@ -282,15 +289,24 @@ static void omap_musb_mailbox_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;
@ -330,14 +346,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;
@ -350,12 +363,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;
@ -374,7 +388,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;
@ -385,7 +399,10 @@ 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);
}
@ -439,11 +456,18 @@ 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,
@ -552,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)
{

View file

@ -33,11 +33,11 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/ulpi.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/notifier.h>
#include <linux/slab.h>
/* Register defines */
@ -159,7 +159,7 @@ struct twl4030_usb {
enum twl4030_usb_mode usb_mode;
int irq;
u8 linkstat;
enum omap_musb_vbus_id_status linkstat;
bool vbus_supplied;
u8 asleep;
bool irq_enabled;
@ -246,10 +246,11 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
/*-------------------------------------------------------------------------*/
static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
static enum omap_musb_vbus_id_status
twl4030_usb_linkstat(struct twl4030_usb *twl)
{
int status;
int linkstat = USB_EVENT_NONE;
enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
struct usb_otg *otg = twl->phy.otg;
twl->vbus_supplied = false;
@ -273,24 +274,24 @@ static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
twl->vbus_supplied = true;
if (status & BIT(2))
linkstat = USB_EVENT_ID;
linkstat = OMAP_MUSB_ID_GROUND;
else
linkstat = USB_EVENT_VBUS;
} else
linkstat = USB_EVENT_NONE;
linkstat = OMAP_MUSB_VBUS_VALID;
} else {
if (twl->linkstat != OMAP_MUSB_UNKNOWN)
linkstat = OMAP_MUSB_VBUS_OFF;
}
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
status, status, linkstat);
twl->phy.last_event = linkstat;
/* REVISIT this assumes host and peripheral controllers
* are registered, and that both are active...
*/
spin_lock_irq(&twl->lock);
twl->linkstat = linkstat;
if (linkstat == USB_EVENT_ID) {
if (linkstat == OMAP_MUSB_ID_GROUND) {
otg->default_a = true;
twl->phy.state = OTG_STATE_A_IDLE;
} else {
@ -501,10 +502,10 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{
struct twl4030_usb *twl = _twl;
int status;
enum omap_musb_vbus_id_status status;
status = twl4030_usb_linkstat(twl);
if (status >= 0) {
if (status > 0) {
/* FIXME add a set_power() method so that B-devices can
* configure the charger appropriately. It's not always
* correct to consume VBUS power, and how much current to
@ -516,13 +517,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
* USB_LINK_VBUS state. musb_hdrc won't care until it
* starts to handle softconnect right.
*/
if (status == USB_EVENT_NONE)
if (status == OMAP_MUSB_VBUS_OFF ||
status == OMAP_MUSB_ID_FLOAT)
twl4030_phy_suspend(twl, 0);
else
twl4030_phy_resume(twl);
atomic_notifier_call_chain(&twl->phy.notifier, status,
twl->phy.otg->gadget);
omap_musb_mailbox(twl->linkstat);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
@ -531,11 +532,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
static void twl4030_usb_phy_init(struct twl4030_usb *twl)
{
int status;
enum omap_musb_vbus_id_status status;
status = twl4030_usb_linkstat(twl);
if (status >= 0) {
if (status == USB_EVENT_NONE) {
if (status > 0) {
if (status == OMAP_MUSB_VBUS_OFF ||
status == OMAP_MUSB_ID_FLOAT) {
__twl4030_phy_power(twl, 0);
twl->asleep = 1;
} else {
@ -543,8 +545,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl)
twl->asleep = 0;
}
atomic_notifier_call_chain(&twl->phy.notifier, status,
twl->phy.otg->gadget);
omap_musb_mailbox(twl->linkstat);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
}
@ -613,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
twl->usb_mode = pdata->usb_mode;
twl->vbus_supplied = false;
twl->asleep = 1;
twl->linkstat = OMAP_MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
twl->phy.label = "twl4030";
@ -639,8 +641,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
/* Our job is to use irqs and status from the power module
* to keep the transceiver disabled when nothing's connected.
*

View file

@ -26,10 +26,10 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/usb/otg.h>
#include <linux/usb/musb-omap.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/delay.h>
@ -100,7 +100,7 @@ struct twl6030_usb {
int irq1;
int irq2;
u8 linkstat;
enum omap_musb_vbus_id_status linkstat;
u8 asleep;
bool irq_enabled;
bool vbus_enable;
@ -147,7 +147,7 @@ static int twl6030_phy_init(struct usb_phy *x)
dev = twl->dev;
pdata = dev->platform_data;
if (twl->linkstat == USB_EVENT_ID)
if (twl->linkstat == OMAP_MUSB_ID_GROUND)
pdata->phy_power(twl->dev, 1, 1);
else
pdata->phy_power(twl->dev, 0, 1);
@ -235,13 +235,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev,
spin_lock_irqsave(&twl->lock, flags);
switch (twl->linkstat) {
case USB_EVENT_VBUS:
case OMAP_MUSB_VBUS_VALID:
ret = snprintf(buf, PAGE_SIZE, "vbus\n");
break;
case USB_EVENT_ID:
case OMAP_MUSB_ID_GROUND:
ret = snprintf(buf, PAGE_SIZE, "id\n");
break;
case USB_EVENT_NONE:
case OMAP_MUSB_VBUS_OFF:
ret = snprintf(buf, PAGE_SIZE, "none\n");
break;
default:
@ -257,7 +257,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
struct usb_otg *otg = twl->phy.otg;
int status;
enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
u8 vbus_state, hw_state;
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
@ -268,22 +268,20 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
if (vbus_state & VBUS_DET) {
regulator_enable(twl->usb3v3);
twl->asleep = 1;
status = USB_EVENT_VBUS;
status = OMAP_MUSB_VBUS_VALID;
otg->default_a = false;
twl->phy.state = OTG_STATE_B_IDLE;
twl->linkstat = status;
twl->phy.last_event = status;
atomic_notifier_call_chain(&twl->phy.notifier,
status, otg->gadget);
omap_musb_mailbox(status);
} else {
status = USB_EVENT_NONE;
twl->linkstat = status;
twl->phy.last_event = status;
atomic_notifier_call_chain(&twl->phy.notifier,
status, otg->gadget);
if (twl->asleep) {
regulator_disable(twl->usb3v3);
twl->asleep = 0;
if (twl->linkstat != OMAP_MUSB_UNKNOWN) {
status = OMAP_MUSB_VBUS_OFF;
twl->linkstat = status;
omap_musb_mailbox(status);
if (twl->asleep) {
regulator_disable(twl->usb3v3);
twl->asleep = 0;
}
}
}
}
@ -296,7 +294,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
struct usb_otg *otg = twl->phy.otg;
int status = USB_EVENT_NONE;
enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
u8 hw_state;
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
@ -308,13 +306,11 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1);
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
0x10);
status = USB_EVENT_ID;
status = OMAP_MUSB_ID_GROUND;
otg->default_a = true;
twl->phy.state = OTG_STATE_A_IDLE;
twl->linkstat = status;
twl->phy.last_event = status;
atomic_notifier_call_chain(&twl->phy.notifier, status,
otg->gadget);
omap_musb_mailbox(status);
} else {
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR,
0x10);
@ -419,6 +415,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
twl->irq1 = platform_get_irq(pdev, 0);
twl->irq2 = platform_get_irq(pdev, 1);
twl->features = pdata->features;
twl->linkstat = OMAP_MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
twl->phy.label = "twl6030";
@ -449,8 +446,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
twl->irq_enabled = true;

View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2011-2012 by Texas Instruments
*
* The Inventra Controller Driver for Linux is free software; you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License version 2 as published by the Free Software
* Foundation.
*/
#ifndef __MUSB_OMAP_H__
#define __MUSB_OMAP_H__
enum omap_musb_vbus_id_status {
OMAP_MUSB_UNKNOWN = 0,
OMAP_MUSB_ID_GROUND,
OMAP_MUSB_ID_FLOAT,
OMAP_MUSB_VBUS_VALID,
OMAP_MUSB_VBUS_OFF,
};
#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \
defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE))
void omap_musb_mailbox(enum omap_musb_vbus_id_status status);
#else
static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
{
}
#endif
#endif /* __MUSB_OMAP_H__ */