Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9721438 authored by Kishon Vijay Abraham I's avatar Kishon Vijay Abraham I Committed by Felipe Balbi
Browse files

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: default avatarKishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 1e5acb8d
Loading
Loading
Loading
Loading
+59 −35
Original line number Diff line number Diff line
@@ -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->xceiv_event = event;
	schedule_work(&glue->omap_musb_mailbox_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 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)
{
+23 −23
Original line number Diff line number Diff line
@@ -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.
	 *
+21 −26
Original line number Diff line number Diff line
@@ -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,25 +268,23 @@ 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;
			if (twl->linkstat != OMAP_MUSB_UNKNOWN) {
				status = OMAP_MUSB_VBUS_OFF;
				twl->linkstat = status;
			twl->phy.last_event = status;
			atomic_notifier_call_chain(&twl->phy.notifier,
						status, otg->gadget);
				omap_musb_mailbox(status);
				if (twl->asleep) {
					regulator_disable(twl->usb3v3);
					twl->asleep = 0;
				}
			}
		}
	}
	sysfs_notify(&twl->dev->kobj, NULL, "vbus");

	return IRQ_HANDLED;
@@ -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;
+30 −0
Original line number Diff line number Diff line
/*
 * 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__ */