All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add MSM USB Host Controller support
@ 2010-11-09 11:19 Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 1/4] USB: Add MSM OTG Controller driver Pavankumar Kondeti
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Pavankumar Kondeti @ 2010-11-09 11:19 UTC (permalink / raw)
  To: greg; +Cc: linux-usb, linux-arm-msm, Pavankumar Kondeti

This patch series adds support for EHCI compliant High speed USB host
controller found in MSM family of SOCs. HCD depends on OTG driver
for PHY initialization, clock management and powering up VBUS. So OTG
driver is added in the patch series. These patches depends on
"Add MSM USB Device Controller" series for header files. These patches
are tested against v2.6.37-rc1

Pavankumar Kondeti (4):
  USB: Add MSM OTG Controller driver
  USB: EHCI: Add MSM Host Controller driver
  USB: EHCI: msm: Add support for power management
  USB: OTG: msm: Add spport for power management

 drivers/usb/Kconfig              |    1 +
 drivers/usb/host/Kconfig         |   12 +
 drivers/usb/host/ehci-hcd.c      |    5 +
 drivers/usb/host/ehci-msm.c      |  373 ++++++++++++++
 drivers/usb/otg/Kconfig          |   11 +
 drivers/usb/otg/Makefile         |    1 +
 drivers/usb/otg/msm72k_otg.c     |  995 ++++++++++++++++++++++++++++++++++++++
 include/linux/usb/msm_hsusb.h    |   89 ++++
 include/linux/usb/msm_hsusb_hw.h |   12 +
 9 files changed, 1499 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ehci-msm.c
 create mode 100644 drivers/usb/otg/msm72k_otg.c


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-09 11:19 [PATCH 0/4] Add MSM USB Host Controller support Pavankumar Kondeti
@ 2010-11-09 11:19 ` Pavankumar Kondeti
  2010-11-16 21:43   ` Greg KH
  2010-11-09 11:19 ` [PATCH 2/4] USB: EHCI: Add MSM Host " Pavankumar Kondeti
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Pavankumar Kondeti @ 2010-11-09 11:19 UTC (permalink / raw)
  To: greg; +Cc: linux-usb, linux-arm-msm, Pavankumar Kondeti

This driver implements PHY initialization, clock management, memory mapping
register address space, ULPI IO ops and simple OTG state machine to kick
host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
reference voltage on some boards.  Hence provide a sysfs interface to
select host/peripheral mode.

Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
---
 drivers/usb/otg/Kconfig          |   11 +
 drivers/usb/otg/Makefile         |    1 +
 drivers/usb/otg/msm72k_otg.c     |  724 ++++++++++++++++++++++++++++++++++++++
 include/linux/usb/msm_hsusb.h    |   85 +++++
 include/linux/usb/msm_hsusb_hw.h |    9 +
 5 files changed, 830 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/otg/msm72k_otg.c

diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 5ce0752..2eaceb3 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -81,4 +81,15 @@ config USB_LANGWELL_OTG
 	  To compile this driver as a module, choose M here: the
 	  module will be called langwell_otg.
 
+config USB_MSM_OTG_72K
+	tristate "OTG support for Qualcomm on-chip USB controller"
+	depends on ARCH_MSM
+	select USB_OTG_UTILS
+	help
+	  Enable this to support the USB OTG transceiver on MSM chips. It
+	  handles PHY initialization, clock management, memory mapping of
+	  register address space and workarounds required after resetting
+	  the hardware. This driver is required for even peripheral only
+	  or host only mode configuration.
+
 endif # USB || OTG
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 66f1b83..3b1b096 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_TWL4030_USB)	+= twl4030-usb.o
 obj-$(CONFIG_USB_LANGWELL_OTG)	+= langwell_otg.o
 obj-$(CONFIG_NOP_USB_XCEIV)	+= nop-usb-xceiv.o
 obj-$(CONFIG_USB_ULPI)		+= ulpi.o
+obj-$(CONFIG_USB_MSM_OTG_72K)	+= msm72k_otg.o
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
new file mode 100644
index 0000000..06eea65
--- /dev/null
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -0,0 +1,724 @@
+/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/usb/msm_hsusb_hw.h>
+
+#define MSM_USB_BASE	(otg->io_priv)
+#define DRIVER_NAME	"msm_otg"
+
+#define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
+static int ulpi_read(struct otg_transceiver *otg, u32 reg)
+{
+	int cnt = 0;
+
+	/* initiate read operation */
+	writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(otg->dev, "ulpi_read: timeout %08x\n",
+			readl(USB_ULPI_VIEWPORT));
+		return -ETIMEDOUT;
+	}
+	return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
+}
+
+static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
+{
+	int cnt = 0;
+
+	/* initiate write operation */
+	writel(ULPI_RUN | ULPI_WRITE |
+	       ULPI_ADDR(reg) | ULPI_DATA(val),
+	       USB_ULPI_VIEWPORT);
+
+	/* wait for completion */
+	while (cnt < ULPI_IO_TIMEOUT_USEC) {
+		if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+		dev_err(otg->dev, "ulpi_write: timeout\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static struct otg_io_access_ops msm_otg_io_ops = {
+	.read = ulpi_read,
+	.write = ulpi_write,
+};
+
+static void ulpi_init(struct msm_otg *motg)
+{
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int *seq = pdata->phy_init_seq;
+
+	if (!seq)
+		return;
+
+	while (seq[0] >= 0) {
+		dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n",
+				seq[0], seq[1]);
+		ulpi_write(&motg->otg, seq[0], seq[1]);
+		seq += 2;
+	}
+}
+
+static int msm_otg_phy_reset(struct msm_otg *motg)
+{
+	u32 val;
+	int ret;
+	int retries;
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	struct otg_transceiver *otg = &motg->otg;
+
+	if (!pdata->phy_reset)
+		return 0;
+
+	if (pdata->hw_reset)
+		pdata->hw_reset(1);
+
+	pdata->phy_reset();
+
+	if (pdata->hw_reset)
+		pdata->hw_reset(0);
+
+	val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK;
+	writel(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+	/* FIXME: only necessary for pre-45nm internal PHYs. */
+	for (retries = 3; retries > 0; retries--) {
+		ret = ulpi_write(&motg->otg, ULPI_FUNC_SUSPENDM,
+				ULPI_FUNC_CTRL_CLR);
+		if (!ret)
+			break;
+		pdata->phy_reset();
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	/* This reset calibrates the phy, if the above write succeeded */
+	pdata->phy_reset();
+
+	/* FIXME: pre-45nm internal phys have a known issue which can cause them
+	 * to lockup on reset. If ULPI accesses fail, try resetting the phy
+	 * again.
+	 */
+	for (retries = 3; retries > 0; retries--) {
+		ret = ulpi_read(&motg->otg, ULPI_DEBUG_REG);
+		if (ret != -ETIMEDOUT)
+			break;
+		pdata->phy_reset();
+	}
+	if (!retries)
+		return -ETIMEDOUT;
+
+	dev_info(motg->otg.dev, "phy_reset: success\n");
+	return 0;
+}
+
+#define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
+static int msm_otg_reset(struct otg_transceiver *otg)
+{
+	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	int cnt = 0;
+	int ret;
+	u32 val = 0;
+
+	ret = msm_otg_phy_reset(motg);
+	if (ret) {
+		dev_err(otg->dev, "phy_reset failed\n");
+		return ret;
+	}
+
+	ulpi_init(motg);
+
+	writel(USBCMD_RESET, USB_USBCMD);
+	while (cnt < LINK_RESET_TIMEOUT_USEC) {
+		if (!(readl(USB_USBCMD) & USBCMD_RESET))
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= LINK_RESET_TIMEOUT_USEC)
+		return -ETIMEDOUT;
+
+	/* select ULPI phy */
+	writel(0x80000000, USB_PORTSC);
+
+	msleep(100);
+
+	writel(0x0, USB_AHBBURST);
+	writel(0x00, USB_AHBMODE);
+
+	if (pdata->otg_control == OTG_PHY_CONTROL) {
+		val = readl(USB_OTGSC);
+		val |= OTGSC_IDIE | OTGSC_BSVIE;
+		writel(val, USB_OTGSC);
+	}
+
+	return 0;
+}
+
+static void msm_otg_start_host(struct otg_transceiver *otg, int on)
+{
+	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	struct usb_hcd *hcd;
+
+	if (!otg->host)
+		return;
+
+	hcd = bus_to_hcd(otg->host);
+
+	if (on) {
+		dev_dbg(otg->dev, "host on\n");
+
+		if (pdata->vbus_power)
+			pdata->vbus_power(1);
+		/*
+		 * Some boards have a switch cotrolled by gpio
+		 * to enable/disable internal HUB. Enable internal
+		 * HUB before kicking the host.
+		 */
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_A_HOST);
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	} else {
+		dev_dbg(otg->dev, "host off\n");
+
+		usb_remove_hcd(hcd);
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_UNDEFINED);
+		if (pdata->vbus_power)
+			pdata->vbus_power(0);
+	}
+}
+
+static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+	struct usb_hcd *hcd;
+
+	/*
+	 * Fail host registration if this board can support
+	 * only peripheral configuration.
+	 */
+	if (motg->pdata->mode == USB_PERIPHERAL) {
+		dev_info(otg->dev, "Host mode is not supported\n");
+		return -ENODEV;
+	}
+
+	if (!host) {
+		if (otg->state == OTG_STATE_A_HOST) {
+			msm_otg_start_host(otg, 0);
+			otg->host = NULL;
+			otg->state = OTG_STATE_UNDEFINED;
+			schedule_work(&motg->sm_work);
+		} else {
+			otg->host = NULL;
+		}
+
+		return 0;
+	}
+
+	hcd = bus_to_hcd(host);
+	hcd->power_budget = motg->pdata->power_budget;
+
+	otg->host = host;
+	dev_dbg(otg->dev, "host driver registered w/ tranceiver\n");
+
+	/*
+	 * Kick the state machine work, if peripheral is not supported
+	 * or peripheral is already registered with us.
+	 */
+	if (motg->pdata->mode == USB_HOST || otg->gadget)
+		schedule_work(&motg->sm_work);
+
+	return 0;
+}
+
+static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on)
+{
+	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+	struct msm_otg_platform_data *pdata = motg->pdata;
+
+	if (!otg->gadget)
+		return;
+
+	if (on) {
+		dev_dbg(otg->dev, "gadget on\n");
+		/*
+		 * Some boards have a switch cotrolled by gpio
+		 * to enable/disable internal HUB. Disable internal
+		 * HUB before kicking the gadget.
+		 */
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_B_PERIPHERAL);
+		usb_gadget_vbus_connect(otg->gadget);
+	} else {
+		dev_dbg(otg->dev, "gadget off\n");
+		usb_gadget_vbus_disconnect(otg->gadget);
+		if (pdata->setup_gpio)
+			pdata->setup_gpio(OTG_STATE_UNDEFINED);
+	}
+
+}
+
+static int msm_otg_set_peripheral(struct otg_transceiver *otg,
+			struct usb_gadget *gadget)
+{
+	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+
+	/*
+	 * Fail peripheral registration if this board can support
+	 * only host configuration.
+	 */
+	if (motg->pdata->mode == USB_HOST) {
+		dev_info(otg->dev, "Peripheral mode is not supported\n");
+		return -ENODEV;
+	}
+
+	if (!gadget) {
+		if (otg->state == OTG_STATE_B_PERIPHERAL) {
+			msm_otg_start_peripheral(otg, 0);
+			otg->gadget = NULL;
+			otg->state = OTG_STATE_UNDEFINED;
+			schedule_work(&motg->sm_work);
+		} else {
+			otg->gadget = NULL;
+		}
+
+		return 0;
+	}
+	otg->gadget = gadget;
+	dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n");
+
+	/*
+	 * Kick the state machine work, if host is not supported
+	 * or host is already registered with us.
+	 */
+	if (motg->pdata->mode == USB_PERIPHERAL || otg->host)
+		schedule_work(&motg->sm_work);
+
+	return 0;
+}
+
+/*
+ * We support OTG, Peripheral only and Host only configurations. In case
+ * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen
+ * via Id pin status or user request (sysfs). Id/BSV interrupts are not
+ * enabled when switch is controlled by user and default mode is supplied
+ * by board file, which can be changed by userspace later.
+ */
+static void msm_otg_init_sm(struct msm_otg *motg)
+{
+	struct otg_transceiver *otg = &motg->otg;
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	u32 otgsc;
+
+	switch (pdata->mode) {
+	case USB_OTG:
+		if (pdata->otg_control == OTG_PHY_CONTROL) {
+			otgsc = readl(USB_OTGSC);
+			if (otgsc & OTGSC_ID)
+				set_bit(ID, &motg->inputs);
+			else
+				clear_bit(ID, &motg->inputs);
+
+			if (otgsc & OTGSC_BSV)
+				set_bit(B_SESS_VLD, &motg->inputs);
+			else
+				clear_bit(B_SESS_VLD, &motg->inputs);
+		} else if (pdata->otg_control == OTG_USER_CONTROL) {
+			if (pdata->default_mode == USB_HOST) {
+				clear_bit(ID, &motg->inputs);
+			} else if (pdata->default_mode == USB_PERIPHERAL) {
+				set_bit(ID, &motg->inputs);
+				set_bit(B_SESS_VLD, &motg->inputs);
+			} else {
+				set_bit(ID, &motg->inputs);
+				clear_bit(B_SESS_VLD, &motg->inputs);
+			}
+		}
+		break;
+	case USB_HOST:
+		clear_bit(ID, &motg->inputs);
+		break;
+	case USB_PERIPHERAL:
+		set_bit(ID, &motg->inputs);
+		break;
+	default:
+		break;
+	}
+}
+
+static void msm_otg_sm_work(struct work_struct *w)
+{
+	struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
+	struct otg_transceiver *otg = &motg->otg;
+
+	switch (otg->state) {
+	case OTG_STATE_UNDEFINED:
+		dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n");
+		msm_otg_reset(otg);
+		msm_otg_init_sm(motg);
+		otg->state = OTG_STATE_B_IDLE;
+		/* FALL THROUGH */
+	case OTG_STATE_B_IDLE:
+		dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n");
+		if (!test_bit(ID, &motg->inputs) && otg->host) {
+			/* disable BSV bit */
+			writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
+			msm_otg_start_host(otg, 1);
+			otg->state = OTG_STATE_A_HOST;
+		} else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) {
+			msm_otg_start_peripheral(otg, 1);
+			otg->state = OTG_STATE_B_PERIPHERAL;
+		}
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n");
+		if (!test_bit(B_SESS_VLD, &motg->inputs) ||
+				!test_bit(ID, &motg->inputs)) {
+			msm_otg_start_peripheral(otg, 0);
+			otg->state = OTG_STATE_B_IDLE;
+			msm_otg_reset(otg);
+			schedule_work(w);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n");
+		if (test_bit(ID, &motg->inputs)) {
+			msm_otg_start_host(otg, 0);
+			otg->state = OTG_STATE_B_IDLE;
+			msm_otg_reset(otg);
+			schedule_work(w);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static irqreturn_t msm_otg_irq(int irq, void *data)
+{
+	struct msm_otg *motg = data;
+	struct otg_transceiver *otg = &motg->otg;
+	u32 otgsc = 0;
+
+	otgsc = readl(USB_OTGSC);
+	if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS)))
+		return IRQ_NONE;
+
+	if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) {
+		if (otgsc & OTGSC_ID)
+			set_bit(ID, &motg->inputs);
+		else
+			clear_bit(ID, &motg->inputs);
+		dev_dbg(otg->dev, "ID set/clear\n");
+	} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
+		if (otgsc & OTGSC_BSV)
+			set_bit(B_SESS_VLD, &motg->inputs);
+		else
+			clear_bit(B_SESS_VLD, &motg->inputs);
+		dev_dbg(otg->dev, "BSV set/clear\n");
+	}
+
+	writel(otgsc, USB_OTGSC);
+	schedule_work(&motg->sm_work);
+	return IRQ_HANDLED;
+}
+
+static ssize_t
+msm_otg_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+	struct otg_transceiver *otg = &motg->otg;
+	int ret;
+
+	switch (otg->state) {
+	case OTG_STATE_A_HOST:
+		ret = sprintf(buf, "%s\n", "host\n");
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		ret = sprintf(buf, "%s\n", "peripheral\n");
+		break;
+	default:
+		ret = sprintf(buf, "%s\n", "none\n");
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t
+msm_otg_mode_store(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+	struct otg_transceiver *otg = &motg->otg;
+	int status = n;
+	enum usb_mode_type req_mode;
+
+	if (sysfs_streq(buf, "host")) {
+		req_mode = USB_HOST;
+	} else if (sysfs_streq(buf, "peripheral")) {
+		req_mode = USB_PERIPHERAL;
+	} else if (sysfs_streq(buf, "none")) {
+		req_mode = USB_NONE;
+	} else {
+		status = -EINVAL;
+		goto out;
+	}
+
+	switch (req_mode) {
+	case USB_NONE:
+		switch (otg->state) {
+		case OTG_STATE_A_HOST:
+		case OTG_STATE_B_PERIPHERAL:
+			set_bit(ID, &motg->inputs);
+			clear_bit(B_SESS_VLD, &motg->inputs);
+			break;
+		default:
+			goto out;
+		}
+		break;
+	case USB_PERIPHERAL:
+		switch (otg->state) {
+		case OTG_STATE_B_IDLE:
+		case OTG_STATE_A_HOST:
+			set_bit(ID, &motg->inputs);
+			set_bit(B_SESS_VLD, &motg->inputs);
+			break;
+		default:
+			goto out;
+		}
+		break;
+	case USB_HOST:
+		switch (otg->state) {
+		case OTG_STATE_B_IDLE:
+		case OTG_STATE_B_PERIPHERAL:
+			clear_bit(ID, &motg->inputs);
+			break;
+		default:
+			goto out;
+		}
+		break;
+	default:
+		goto out;
+	}
+
+	schedule_work(&motg->sm_work);
+out:
+	return status;
+
+}
+static DEVICE_ATTR(mode, S_IWUSR, msm_otg_mode_show, msm_otg_mode_store);
+
+static int __init msm_otg_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct resource *res;
+	struct msm_otg *motg;
+	struct otg_transceiver *otg;
+
+	dev_info(&pdev->dev, "msm_otg probe\n");
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
+		return -ENODEV;
+	}
+
+	motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+	if (!motg) {
+		dev_err(&pdev->dev, "unable to allocate msm_otg\n");
+		return -ENOMEM;
+	}
+
+	motg->pdata = pdev->dev.platform_data;
+	otg = &motg->otg;
+	otg->dev = &pdev->dev;
+
+	motg->clk = clk_get(&pdev->dev, "usb_hs_clk");
+	if (IS_ERR(motg->clk)) {
+		dev_err(&pdev->dev, "failed to get usb_hs_clk\n");
+		ret = PTR_ERR(motg->clk);
+		goto free_dev;
+	}
+
+	motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
+	if (IS_ERR(motg->pclk)) {
+		dev_err(&pdev->dev, "failed to get usb_hs_pclk\n");
+		ret = PTR_ERR(motg->pclk);
+		goto put_clk;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get platform resource mem\n");
+		ret = -ENODEV;
+		goto put_pclk;
+	}
+
+	otg->io_priv = ioremap(res->start, resource_size(res));
+	if (!otg->io_priv) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto put_pclk;
+	}
+	dev_info(&pdev->dev, "OTG regs = %p\n", otg->io_priv);
+
+	motg->irq = platform_get_irq(pdev, 0);
+	if (!motg->irq) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		ret = -ENODEV;
+		goto free_regs;
+	}
+
+	clk_enable(motg->clk);
+	clk_enable(motg->pclk);
+
+	writel(0, USB_USBINTR);
+	writel(0, USB_OTGSC);
+
+	INIT_WORK(&motg->sm_work, msm_otg_sm_work);
+	ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
+					"msm_otg", motg);
+	if (ret) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		goto disable_clks;
+	}
+
+	otg->init = msm_otg_reset;
+	otg->set_host = msm_otg_set_host;
+	otg->set_peripheral = msm_otg_set_peripheral;
+
+	otg->io_ops = &msm_otg_io_ops;
+
+	ret = otg_set_transceiver(&motg->otg);
+	if (ret) {
+		dev_err(&pdev->dev, "otg_set_transceiver failed\n");
+		goto free_irq;
+	}
+
+	platform_set_drvdata(pdev, motg);
+	device_init_wakeup(&pdev->dev, 1);
+
+	if (motg->pdata->mode == USB_OTG &&
+			motg->pdata->otg_control == OTG_USER_CONTROL) {
+		ret = device_create_file(&pdev->dev, &dev_attr_mode);
+		if (ret != 0)
+			dev_info(&pdev->dev, "failed to create sysfs entry:"
+				"(mode) error: (%d)\n", ret);
+	}
+
+	return 0;
+
+free_irq:
+	free_irq(motg->irq, motg);
+disable_clks:
+	clk_disable(motg->pclk);
+	clk_disable(motg->clk);
+free_regs:
+	iounmap(otg->io_priv);
+put_pclk:
+	clk_put(motg->pclk);
+put_clk:
+	clk_put(motg->clk);
+free_dev:
+	kfree(motg);
+	return ret;
+}
+
+static int __devexit msm_otg_remove(struct platform_device *pdev)
+{
+	struct msm_otg *motg = platform_get_drvdata(pdev);
+	struct otg_transceiver *otg = &motg->otg;
+
+	device_remove_file(&pdev->dev, &dev_attr_mode);
+	cancel_work_sync(&motg->sm_work);
+	device_init_wakeup(&pdev->dev, 0);
+	otg_set_transceiver(NULL);
+
+	free_irq(motg->irq, motg);
+
+	clk_disable(motg->pclk);
+	clk_disable(motg->clk);
+
+	iounmap(otg->io_priv);
+
+	clk_put(motg->pclk);
+	clk_put(motg->clk);
+
+	kfree(motg);
+
+	return 0;
+}
+
+static struct platform_driver msm_otg_driver = {
+	.remove = __devexit_p(msm_otg_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_otg_init(void)
+{
+	return platform_driver_probe(&msm_otg_driver, msm_otg_probe);
+}
+
+static void __exit msm_otg_exit(void)
+{
+	platform_driver_unregister(&msm_otg_driver);
+}
+
+module_init(msm_otg_init);
+module_exit(msm_otg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM USB transceiver driver");
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index f95b62c..11760d5 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -2,6 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Author: Brian Swetland <swetland@google.com>
+ * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -18,6 +19,7 @@
 #define __ASM_ARCH_MSM_HSUSB_H
 
 #include <linux/types.h>
+#include <linux/usb/otg.h>
 
 /**
  * USB charger types.
@@ -51,4 +53,87 @@ struct msm_hsusb_platform_data {
 	int *phy_init_seq;
 };
 
+/**
+ * Supported USB modes
+ *
+ * USB_PERIPHERAL       Only peripheral mode is supported.
+ * USB_HOST             Only host mode is supported.
+ * USB_OTG              OTG mode is supported.
+ *
+ */
+enum usb_mode_type {
+	USB_NONE = 0,
+	USB_PERIPHERAL,
+	USB_HOST,
+	USB_OTG,
+};
+
+/**
+ * OTG control
+ *
+ * OTG_NO_CONTROL	Id/VBUS notifications not required. Useful in host
+ *                      only configuration.
+ * OTG_PHY_CONTROL	Id/VBUS notifications comes form USB PHY.
+ * OTG_PMIC_CONTROL	Id/VBUS notifications comes from PMIC hardware.
+ * OTG_USER_CONTROL	Id/VBUS notifcations comes from User via sysfs.
+ *
+ */
+enum otg_control_type {
+	OTG_NO_CONTROL = 0,
+	OTG_PHY_CONTROL,
+	OTG_PMIC_CONTROL,
+	OTG_USER_CONTROL,
+};
+
+/**
+ * struct msm_otg_platform_data - platform device data
+ *              for msm72k_otg driver.
+ * @phy_reset:	assert followed by de-assert of PHY.
+ * @hw_reset:	if enable is true, assert link otherwise
+ *              de-assert link.
+ * @phy_init_seq: PHY configuration sequence. val, reg pairs
+ *              terminated by -1.
+ * @vbus_power: VBUS power on/off routine.
+ * @power_budget: VBUS power budget in mA (0 will be treated as 500mA).
+ * @mode: Supported mode (OTG/peripheral/host).
+ * @otg_control: OTG switch controlled by user/Id pin
+ * @default_mode: Default operational mode. Applicable only if
+ *              OTG switch is controller by user.
+ *
+ */
+struct msm_otg_platform_data {
+	void (*phy_reset)(void);
+	void (*hw_reset)(bool enable);
+	int *phy_init_seq;
+	void (*vbus_power)(bool on);
+	unsigned power_budget;
+	enum usb_mode_type mode;
+	enum otg_control_type otg_control;
+	enum usb_mode_type default_mode;
+	void (*setup_gpio)(enum usb_otg_state state);
+};
+
+/**
+ * struct msm_otg: OTG driver data. Shared by HCD and DCD.
+ * @otg: USB OTG Transceiver structure.
+ * @pdata: otg device platform data.
+ * @irq: IRQ number assigned for HSUSB controller.
+ * @clk: clock struct of usb_hs_clk.
+ * @pclk: clock struct of usb_hs_pclk.
+ * @inputs: OTG state machine inputs(Id, SessValid etc).
+ * @sm_work: OTG state machine work.
+ *
+ */
+struct msm_otg {
+	struct otg_transceiver otg;
+	struct msm_otg_platform_data *pdata;
+	int irq;
+	struct clk *clk;
+	struct clk *pclk;
+#define ID		0
+#define B_SESS_VLD	1
+	unsigned long inputs;
+	struct work_struct sm_work;
+};
+
 #endif
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 743e6b9..ee58f74 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -195,4 +195,13 @@ struct ept_queue_item {
 #define PORTSC_LS              (3 << 10) /* Read - Port's Line status */
 #define PORTSC_PHCD            (1 << 23) /* phy suspend mode */
 
+/* OTG definitions */
+#define OTGSC_INTSTS_MASK	(0x7f << 16)
+#define OTGSC_ID		(1 << 8)
+#define OTGSC_BSV		(1 << 11)
+#define OTGSC_IDIS		(1 << 16)
+#define OTGSC_BSVIS		(1 << 19)
+#define OTGSC_IDIE		(1 << 24)
+#define OTGSC_BSVIE		(1 << 27)
+
 #endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */
-- 
1.7.1

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/4] USB: EHCI: Add MSM Host Controller driver
  2010-11-09 11:19 [PATCH 0/4] Add MSM USB Host Controller support Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 1/4] USB: Add MSM OTG Controller driver Pavankumar Kondeti
@ 2010-11-09 11:19 ` Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 3/4] USB: EHCI: msm: Add support for power management Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 4/4] USB: OTG: msm: Add spport " Pavankumar Kondeti
  3 siblings, 0 replies; 11+ messages in thread
From: Pavankumar Kondeti @ 2010-11-09 11:19 UTC (permalink / raw)
  To: greg; +Cc: linux-usb, linux-arm-msm, Pavankumar Kondeti

This patch adds support for EHCI compliant HSUSB Host controller found
on MSM chips.  The root hub has a single port and TT is built into it.
This driver depends on OTG driver for PHY initialization, clock
management and powering up VBUS.

Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
---
 drivers/usb/Kconfig         |    1 +
 drivers/usb/host/Kconfig    |   12 ++
 drivers/usb/host/ehci-hcd.c |    5 +
 drivers/usb/host/ehci-msm.c |  294 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 312 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ehci-msm.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 67eb377..4c2f754 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -66,6 +66,7 @@ config USB_ARCH_HAS_EHCI
 	default y if ARCH_AT91SAM9G45
 	default y if ARCH_MXC
 	default y if ARCH_OMAP3
+	default y if ARCH_MSM
 	default PCI
 
 # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 2391c39..c816475 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -133,6 +133,18 @@ config USB_EHCI_MXC
 	---help---
 	  Variation of ARC USB block used in some Freescale chips.
 
+config USB_EHCI_MSM
+	bool "Support for MSM on-chip EHCI USB controller"
+	depends on USB_EHCI_HCD && ARCH_MSM
+	select USB_EHCI_ROOT_HUB_TT
+	select USB_MSM_OTG_72K
+	---help---
+	  Enables support for the USB Host controller present on the
+	  Qualcomm chipsets. Root Hub has inbuilt TT.
+	  This driver depends on OTG driver for PHY initialization,
+	  clock management, powering up VBUS and memory mapping of
+	  register address space.
+
 config USB_EHCI_HCD_PPC_OF
 	bool "EHCI support for PPC USB controller on OF platform bus"
 	depends on USB_EHCI_HCD && PPC_OF
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 502a7e6..d3e6b1b 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1216,6 +1216,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		ehci_octeon_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_MSM
+#include "ehci-msm.c"
+#define PLATFORM_DRIVER		ehci_msm_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
new file mode 100644
index 0000000..7a2172a
--- /dev/null
+++ b/drivers/usb/host/ehci-msm.c
@@ -0,0 +1,294 @@
+/* ehci-msm.c - HSUSB Host Controller Driver Implementation
+ *
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * Partly derived from ehci-fsl.c and ehci-hcd.c
+ * Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/usb/otg.h>
+#include <linux/usb/msm_hsusb_hw.h>
+
+#define MSM_USB_BASE (hcd->regs)
+
+static struct otg_transceiver *otg;
+
+/*
+ * ehci_run defined in drivers/usb/host/ehci-hcd.c reset the controller and
+ * the configuration settings in ehci_msm_reset vanish after controller is
+ * reset. Resetting the controler in ehci_run seems to be un-necessary
+ * provided HCD reset the controller before calling ehci_run. Most of the HCD
+ * do but some are not. So this function is same as ehci_run but we don't
+ * reset the controller here.
+ */
+static int ehci_msm_run(struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
+	u32			temp;
+	u32			hcc_params;
+
+	hcd->uses_new_polling = 1;
+
+	ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+	ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+	/*
+	 * hcc_params controls whether ehci->regs->segment must (!!!)
+	 * be used; it constrains QH/ITD/SITD and QTD locations.
+	 * pci_pool consistent memory always uses segment zero.
+	 * streaming mappings for I/O buffers, like pci_map_single(),
+	 * can return segments above 4GB, if the device allows.
+	 *
+	 * NOTE:  the dma mask is visible through dma_supported(), so
+	 * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+	 * Scsi_Host.highmem_io, and so forth.  It's readonly to all
+	 * host side drivers though.
+	 */
+	hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
+	if (HCC_64BIT_ADDR(hcc_params))
+		ehci_writel(ehci, 0, &ehci->regs->segment);
+
+	/*
+	 * Philips, Intel, and maybe others need CMD_RUN before the
+	 * root hub will detect new devices (why?); NEC doesn't
+	 */
+	ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+	ehci->command |= CMD_RUN;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+	dbg_cmd(ehci, "init", ehci->command);
+
+	/*
+	 * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+	 * are explicitly handed to companion controller(s), so no TT is
+	 * involved with the root hub.  (Except where one is integrated,
+	 * and there's no companion controller unless maybe for USB OTG.)
+	 *
+	 * Turning on the CF flag will transfer ownership of all ports
+	 * from the companions to the EHCI controller.  If any of the
+	 * companions are in the middle of a port reset at the time, it
+	 * could cause trouble.  Write-locking ehci_cf_port_reset_rwsem
+	 * guarantees that no resets are in progress.  After we set CF,
+	 * a short delay lets the hardware catch up; new resets shouldn't
+	 * be started before the port switching actions could complete.
+	 */
+	down_write(&ehci_cf_port_reset_rwsem);
+	hcd->state = HC_STATE_RUNNING;
+	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
+	usleep_range(5000, 5500);
+	up_write(&ehci_cf_port_reset_rwsem);
+	ehci->last_periodic_enable = ktime_get_real();
+
+	temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
+	ehci_info(ehci,
+		"USB %x.%x started, EHCI %x.%02x%s\n",
+		((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
+		temp >> 8, temp & 0xff,
+		ignore_oc ? ", overcurrent ignored" : "");
+
+	ehci_writel(ehci, INTR_MASK,
+		    &ehci->regs->intr_enable); /* Turn On Interrupts */
+
+	/* GRR this is run-once init(), being done every time the HC starts.
+	 * So long as they're part of class devices, we can't do it init()
+	 * since the class device isn't created that early.
+	 */
+	create_debug_files(ehci);
+	create_companion_file(ehci);
+
+	return 0;
+}
+
+static int ehci_msm_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = USB_CAPLENGTH;
+	ehci->regs = USB_CAPLENGTH +
+		HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+	/* cache the data to minimize the chip reads*/
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	hcd->has_tt = 1;
+	ehci->sbrn = HCD_USB2;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	retval = ehci_reset(ehci);
+	if (retval)
+		return retval;
+
+	/* bursts of unspecified length. */
+	writel(0, USB_AHBBURST);
+	/* Use the AHB transactor */
+	writel(0, USB_AHBMODE);
+	/* Disable streaming mode and select host mode */
+	writel(0x13, USB_USBMODE);
+
+	return 0;
+}
+
+static struct hc_driver msm_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Qualcomm On-Chip EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq			= ehci_irq,
+	.flags			= HCD_USB2 | HCD_MEMORY,
+
+	.reset			= ehci_msm_reset,
+	.start			= ehci_msm_run,
+
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	/*
+	 * PM support
+	 */
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+};
+
+static int ehci_msm_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	int ret;
+
+	dev_dbg(&pdev->dev, "ehci_msm proble\n");
+
+	hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		return  -ENOMEM;
+	}
+
+	hcd->irq = platform_get_irq(pdev, 0);
+	if (hcd->irq < 0) {
+		dev_err(&pdev->dev, "Unable to get IRQ resource\n");
+		ret = hcd->irq;
+		goto put_hcd;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Unable to get memory resource\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+
+	/*
+	 * OTG driver takes care of PHY initialization, clock management,
+	 * powering up VBUS and mapping of registers address space.
+	 */
+	otg = otg_get_transceiver();
+	if (!otg) {
+		dev_err(&pdev->dev, "unable to find transceiver\n");
+		ret = -ENODEV;
+		goto unmap;
+	}
+
+	ret = try_module_get(otg->dev->driver->owner);
+	if (!ret) {
+		dev_err(&pdev->dev, "unable to get otg module\n");
+		ret = -ENODEV;
+		goto put_transceiver;
+	}
+
+	hcd->regs = otg->io_priv;
+
+	ret = otg_set_host(otg, &hcd->self);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register with transceiver\n");
+		goto put_module;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	return 0;
+put_module:
+	module_put(otg->dev->driver->owner);
+put_transceiver:
+	otg_put_transceiver(otg);
+unmap:
+	iounmap(hcd->regs);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int __devexit ehci_msm_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	otg_set_host(otg, NULL);
+	otg_put_transceiver(otg);
+	module_put(otg->dev->driver->owner);
+
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static struct platform_driver ehci_msm_driver = {
+	.probe	= ehci_msm_probe,
+	.remove	= __devexit_p(ehci_msm_remove),
+	.driver = {
+		   .name = "msm_hsusb_host",
+	},
+};
-- 
1.7.1

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/4] USB: EHCI: msm: Add support for power management
  2010-11-09 11:19 [PATCH 0/4] Add MSM USB Host Controller support Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 1/4] USB: Add MSM OTG Controller driver Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 2/4] USB: EHCI: Add MSM Host " Pavankumar Kondeti
@ 2010-11-09 11:19 ` Pavankumar Kondeti
  2010-11-09 11:19 ` [PATCH 4/4] USB: OTG: msm: Add spport " Pavankumar Kondeti
  3 siblings, 0 replies; 11+ messages in thread
From: Pavankumar Kondeti @ 2010-11-09 11:19 UTC (permalink / raw)
  To: greg; +Cc: linux-usb, linux-arm-msm, Pavankumar Kondeti

Host device is made a child of OTG device.  Implement nop functions for
runtime PM callbacks and enable runtime PM. OTG device is notfied
about Host device suspend and takes care of putting hardware into low
power mode.  Adjust port power wakeup flags during system suspend and
resume.

Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
---
 drivers/usb/host/Kconfig    |    4 +-
 drivers/usb/host/ehci-msm.c |   81 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index c816475..de67da1 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -142,8 +142,8 @@ config USB_EHCI_MSM
 	  Enables support for the USB Host controller present on the
 	  Qualcomm chipsets. Root Hub has inbuilt TT.
 	  This driver depends on OTG driver for PHY initialization,
-	  clock management, powering up VBUS and memory mapping of
-	  register address space.
+	  clock management, powering up VBUS, memory mapping of
+	  register address space and power management.
 
 config USB_EHCI_HCD_PPC_OF
 	bool "EHCI support for PPC USB controller on OF platform bus"
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 7a2172a..9e46102 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/usb/otg.h>
 #include <linux/usb/msm_hsusb_hw.h>
@@ -232,7 +233,8 @@ static int ehci_msm_probe(struct platform_device *pdev)
 
 	/*
 	 * OTG driver takes care of PHY initialization, clock management,
-	 * powering up VBUS and mapping of registers address space.
+	 * powering up VBUS, mapping of registers address space and power
+	 * management.
 	 */
 	otg = otg_get_transceiver();
 	if (!otg) {
@@ -257,7 +259,11 @@ static int ehci_msm_probe(struct platform_device *pdev)
 	}
 
 	device_init_wakeup(&pdev->dev, 1);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	return 0;
+
 put_module:
 	module_put(otg->dev->driver->owner);
 put_transceiver:
@@ -275,6 +281,8 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
 	device_init_wakeup(&pdev->dev, 0);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
 
 	otg_set_host(otg, NULL);
 	otg_put_transceiver(otg);
@@ -285,10 +293,81 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "ehci-msm runtime idle\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "ehci-msm runtime suspend\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "ehci-msm runtime resume\n");
+	return 0;
+}
+#else
+#define ehci_msm_runtime_idle NULL
+#define ehci_msm_runtime_suspend NULL
+#define ehci_msm_runtime_resume NULL
+#endif
+
+#ifdef CONFIG_PM
+static int ehci_msm_pm_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	bool wakeup = device_may_wakeup(dev);
+
+	dev_dbg(dev, "ehci-msm PM suspend\n");
+
+	/*
+	 * EHCI helper function has also the same check before manipulating
+	 * port wakeup flags.  We do check here the same condition before
+	 * calling the same helper function to avoid bringing hardware
+	 * from Low power mode when there is no need for adjusting port
+	 * wakeup flags.
+	 */
+	if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {
+		pm_runtime_resume(dev);
+		ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
+				wakeup);
+	}
+
+	return 0;
+}
+
+static int ehci_msm_pm_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "ehci-msm PM resume\n");
+	ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
+
+	return 0;
+}
+#else
+#define ehci_msm_pm_suspend	NULL
+#define ehci_msm_pm_resume	NULL
+#endif
+
+static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
+	.runtime_suspend = ehci_msm_runtime_suspend,
+	.runtime_resume  = ehci_msm_runtime_resume,
+	.runtime_idle    = ehci_msm_runtime_idle,
+	.suspend         = ehci_msm_pm_suspend,
+	.resume          = ehci_msm_pm_resume,
+};
+
 static struct platform_driver ehci_msm_driver = {
 	.probe	= ehci_msm_probe,
 	.remove	= __devexit_p(ehci_msm_remove),
 	.driver = {
 		   .name = "msm_hsusb_host",
+		   .pm = &ehci_msm_dev_pm_ops,
 	},
 };
-- 
1.7.1

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/4] USB: OTG: msm: Add spport for power management
  2010-11-09 11:19 [PATCH 0/4] Add MSM USB Host Controller support Pavankumar Kondeti
                   ` (2 preceding siblings ...)
  2010-11-09 11:19 ` [PATCH 3/4] USB: EHCI: msm: Add support for power management Pavankumar Kondeti
@ 2010-11-09 11:19 ` Pavankumar Kondeti
  3 siblings, 0 replies; 11+ messages in thread
From: Pavankumar Kondeti @ 2010-11-09 11:19 UTC (permalink / raw)
  To: greg; +Cc: linux-usb, linux-arm-msm, Pavankumar Kondeti

Implement runtime and system pm ops to put hardware into low power
mode (LPM). As part of LPM, USB clocks are turned off, PHY is put
into suspend state and PHY comparators are turned off if VBUS/Id
notifications are not required from PHY.

Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
---
 drivers/usb/otg/Kconfig          |    6 +-
 drivers/usb/otg/msm72k_otg.c     |  279 +++++++++++++++++++++++++++++++++++++-
 include/linux/usb/msm_hsusb.h    |    4 +
 include/linux/usb/msm_hsusb_hw.h |    3 +
 4 files changed, 285 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 2eaceb3..cc19f80 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -88,8 +88,8 @@ config USB_MSM_OTG_72K
 	help
 	  Enable this to support the USB OTG transceiver on MSM chips. It
 	  handles PHY initialization, clock management, memory mapping of
-	  register address space and workarounds required after resetting
-	  the hardware. This driver is required for even peripheral only
-	  or host only mode configuration.
+	  register address space, workarounds required after resetting
+	  the hardware and power management. This driver is required for
+	  even peripheral only or host only mode configurations.
 
 endif # USB || OTG
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
index 06eea65..ace7b13 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/usb.h>
 #include <linux/usb/otg.h>
@@ -204,6 +205,147 @@ static int msm_otg_reset(struct otg_transceiver *otg)
 	return 0;
 }
 
+#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
+static int msm_otg_suspend(struct msm_otg *dev)
+{
+	struct otg_transceiver *otg = &dev->otg;
+	struct usb_bus *bus = otg->host;
+	struct msm_otg_platform_data *pdata = dev->pdata;
+	int cnt = 0;
+
+	if (atomic_read(&dev->in_lpm))
+		return 0;
+
+	/*
+	 * Interrupt Latch Register auto-clear feature is not present
+	 * in all PHY versions. Latch register is clear on read type.
+	 * Clear latch register to avoid spurious wakeup from
+	 * low power mode (LPM).
+	 */
+	ulpi_read(otg, 0x14);
+
+	/*
+	 * PHY comparators are disabled when PHY enters into low power
+	 * mode (LPM). Keep PHY comparators ON in LPM only when we expect
+	 * VBUS/Id notifications from USB PHY. Otherwise turn off USB
+	 * PHY comparators. This save significant amount of power.
+	 */
+	if (pdata->otg_control == OTG_PHY_CONTROL)
+		ulpi_write(otg, 0x01, 0x30);
+
+	/*
+	 * PLL is not turned off when PHY enters into low power mode (LPM).
+	 * Disable PLL for maximum power savings.
+	 */
+	ulpi_write(otg, 0x08, 0x09);
+
+	/*
+	 * PHY may take some time or even fail to enter into low power
+	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
+	 * in failure case.
+	 */
+	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
+		dev_err(otg->dev, "Unable to suspend PHY\n");
+		msm_otg_reset(otg);
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * PHY has capability to generate interrupt asynchronously in low
+	 * power mode (LPM). This interrupt is level triggered. So USB IRQ
+	 * line must be disabled till async interrupt enable bit is cleared
+	 * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+	 * block data communication from PHY.
+	 */
+	writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+
+	clk_disable(dev->pclk);
+	clk_disable(dev->clk);
+
+	if (device_may_wakeup(otg->dev))
+		enable_irq_wake(dev->irq);
+	if (bus)
+		clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+
+	atomic_set(&dev->in_lpm, 1);
+
+	dev_info(otg->dev, "USB in low power mode\n");
+
+	return 0;
+}
+
+#define PHY_RESUME_TIMEOUT_USEC	(100 * 1000)
+static int msm_otg_resume(struct msm_otg *dev)
+{
+	struct otg_transceiver *otg = &dev->otg;
+	struct usb_bus *bus = otg->host;
+	int cnt = 0;
+	unsigned temp;
+
+	if (!atomic_read(&dev->in_lpm))
+		return 0;
+
+	clk_enable(dev->pclk);
+	clk_enable(dev->clk);
+
+	temp = readl(USB_USBCMD);
+	temp &= ~ASYNC_INTR_CTRL;
+	temp &= ~ULPI_STP_CTRL;
+	writel(temp, USB_USBCMD);
+
+	/*
+	 * PHY comes out of low power mode (LPM) in case of wakeup
+	 * from asynchronous interrupt.
+	 */
+	if (!(readl(USB_PORTSC) & PORTSC_PHCD))
+		goto skip_phy_resume;
+
+	writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC);
+	while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+		if (!(readl(USB_PORTSC) & PORTSC_PHCD))
+			break;
+		udelay(1);
+		cnt++;
+	}
+
+	if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+		/*
+		 * This is a fatal error. Reset the link and
+		 * PHY. USB state can not be restored. Re-insertion
+		 * of USB cable is the only way to get USB working.
+		 */
+		dev_err(otg->dev, "Unable to resume USB."
+				"Re-plugin the cable\n");
+		msm_otg_reset(otg);
+	}
+
+skip_phy_resume:
+	if (device_may_wakeup(otg->dev))
+		disable_irq_wake(dev->irq);
+	if (bus)
+		set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+
+	if (dev->async_int) {
+		dev->async_int = 0;
+		pm_runtime_put(otg->dev);
+		enable_irq(dev->irq);
+	}
+
+	atomic_set(&dev->in_lpm, 0);
+
+	dev_info(otg->dev, "USB exited from low power mode\n");
+
+	return 0;
+}
+
 static void msm_otg_start_host(struct otg_transceiver *otg, int on)
 {
 	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
@@ -255,6 +397,7 @@ static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
 
 	if (!host) {
 		if (otg->state == OTG_STATE_A_HOST) {
+			pm_runtime_get_sync(otg->dev);
 			msm_otg_start_host(otg, 0);
 			otg->host = NULL;
 			otg->state = OTG_STATE_UNDEFINED;
@@ -276,8 +419,10 @@ static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
 	 * Kick the state machine work, if peripheral is not supported
 	 * or peripheral is already registered with us.
 	 */
-	if (motg->pdata->mode == USB_HOST || otg->gadget)
+	if (motg->pdata->mode == USB_HOST || otg->gadget) {
+		pm_runtime_get_sync(otg->dev);
 		schedule_work(&motg->sm_work);
+	}
 
 	return 0;
 }
@@ -325,6 +470,7 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg,
 
 	if (!gadget) {
 		if (otg->state == OTG_STATE_B_PERIPHERAL) {
+			pm_runtime_get_sync(otg->dev);
 			msm_otg_start_peripheral(otg, 0);
 			otg->gadget = NULL;
 			otg->state = OTG_STATE_UNDEFINED;
@@ -342,8 +488,10 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg,
 	 * Kick the state machine work, if host is not supported
 	 * or host is already registered with us.
 	 */
-	if (motg->pdata->mode == USB_PERIPHERAL || otg->host)
+	if (motg->pdata->mode == USB_PERIPHERAL || otg->host) {
+		pm_runtime_get_sync(otg->dev);
 		schedule_work(&motg->sm_work);
+	}
 
 	return 0;
 }
@@ -420,6 +568,7 @@ static void msm_otg_sm_work(struct work_struct *w)
 			msm_otg_start_peripheral(otg, 1);
 			otg->state = OTG_STATE_B_PERIPHERAL;
 		}
+		pm_runtime_put_sync(otg->dev);
 		break;
 	case OTG_STATE_B_PERIPHERAL:
 		dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n");
@@ -451,6 +600,13 @@ static irqreturn_t msm_otg_irq(int irq, void *data)
 	struct otg_transceiver *otg = &motg->otg;
 	u32 otgsc = 0;
 
+	if (atomic_read(&motg->in_lpm)) {
+		disable_irq_nosync(irq);
+		motg->async_int = 1;
+		pm_runtime_get(otg->dev);
+		return IRQ_HANDLED;
+	}
+
 	otgsc = readl(USB_OTGSC);
 	if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS)))
 		return IRQ_NONE;
@@ -461,12 +617,14 @@ static irqreturn_t msm_otg_irq(int irq, void *data)
 		else
 			clear_bit(ID, &motg->inputs);
 		dev_dbg(otg->dev, "ID set/clear\n");
+		pm_runtime_get_noresume(otg->dev);
 	} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
 		if (otgsc & OTGSC_BSV)
 			set_bit(B_SESS_VLD, &motg->inputs);
 		else
 			clear_bit(B_SESS_VLD, &motg->inputs);
 		dev_dbg(otg->dev, "BSV set/clear\n");
+		pm_runtime_get_noresume(otg->dev);
 	}
 
 	writel(otgsc, USB_OTGSC);
@@ -553,6 +711,7 @@ msm_otg_mode_store(struct device *dev, struct device_attribute *attr,
 		goto out;
 	}
 
+	pm_runtime_get_sync(dev);
 	schedule_work(&motg->sm_work);
 out:
 	return status;
@@ -656,8 +815,10 @@ static int __init msm_otg_probe(struct platform_device *pdev)
 				"(mode) error: (%d)\n", ret);
 	}
 
-	return 0;
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
 
+	return 0;
 free_irq:
 	free_irq(motg->irq, motg);
 disable_clks:
@@ -678,17 +839,42 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
 {
 	struct msm_otg *motg = platform_get_drvdata(pdev);
 	struct otg_transceiver *otg = &motg->otg;
+	int cnt = 0;
 
-	device_remove_file(&pdev->dev, &dev_attr_mode);
 	cancel_work_sync(&motg->sm_work);
+
+	msm_otg_resume(motg);
+
 	device_init_wakeup(&pdev->dev, 0);
+
+	pm_runtime_disable(&pdev->dev);
+
+	device_remove_file(&pdev->dev, &dev_attr_mode);
 	otg_set_transceiver(NULL);
 
 	free_irq(motg->irq, motg);
 
+	/*
+	 * Put PHY in low power mode.
+	 */
+	ulpi_read(otg, 0x14);
+	ulpi_write(otg, 0x08, 0x09);
+
+	writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+	while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+		if (readl(USB_PORTSC) & PORTSC_PHCD)
+			break;
+		udelay(1);
+		cnt++;
+	}
+	if (cnt >= PHY_SUSPEND_TIMEOUT_USEC)
+		dev_err(otg->dev, "Unable to suspend PHY\n");
+
 	clk_disable(motg->pclk);
 	clk_disable(motg->clk);
 
+	pm_runtime_set_suspended(&pdev->dev);
+
 	iounmap(otg->io_priv);
 
 	clk_put(motg->pclk);
@@ -699,11 +885,96 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int msm_otg_runtime_idle(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+	struct otg_transceiver *otg = &motg->otg;
+
+	dev_dbg(dev, "OTG runtime idle\n");
+
+	/*
+	 * It is observed some times that a spurious interrupt
+	 * comes when PHY is put into LPM immediately after PHY reset.
+	 * This 1 sec delay also prevents entering into LPM immediately
+	 * after asynchronous interrupt.
+	 */
+	if (otg->state != OTG_STATE_UNDEFINED)
+		pm_schedule_suspend(dev, 1000);
+
+	return -EAGAIN;
+}
+
+static int msm_otg_runtime_suspend(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "OTG runtime suspend\n");
+	return msm_otg_suspend(motg);
+}
+
+static int msm_otg_runtime_resume(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "OTG runtime resume\n");
+	return msm_otg_resume(motg);
+}
+#else
+#define msm_otg_runtime_idle	NULL
+#define msm_otg_runtime_suspend	NULL
+#define msm_otg_runtime_resume	NULL
+#endif
+
+#ifdef CONFIG_PM
+static int msm_otg_pm_suspend(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "OTG PM suspend\n");
+	return msm_otg_suspend(motg);
+}
+
+static int msm_otg_pm_resume(struct device *dev)
+{
+	struct msm_otg *motg = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(dev, "OTG PM resume\n");
+
+	ret = msm_otg_resume(motg);
+	if (ret)
+		return ret;
+
+	/*
+	 * Runtime PM Documentation recommends bringing the
+	 * device to full powered state upon resume.
+	 */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#else
+#define msm_otg_pm_suspend	NULL
+#define msm_otg_pm_resume	NULL
+#endif
+
+static const struct dev_pm_ops msm_otg_dev_pm_ops = {
+	.runtime_suspend = msm_otg_runtime_suspend,
+	.runtime_resume  = msm_otg_runtime_resume,
+	.runtime_idle    = msm_otg_runtime_idle,
+	.suspend         = msm_otg_pm_suspend,
+	.resume          = msm_otg_pm_resume,
+};
+
 static struct platform_driver msm_otg_driver = {
 	.remove = __devexit_p(msm_otg_remove),
 	.driver = {
 		.name = DRIVER_NAME,
 		.owner = THIS_MODULE,
+		.pm = &msm_otg_dev_pm_ops,
 	},
 };
 
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 11760d5..83431be 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -122,6 +122,8 @@ struct msm_otg_platform_data {
  * @pclk: clock struct of usb_hs_pclk.
  * @inputs: OTG state machine inputs(Id, SessValid etc).
  * @sm_work: OTG state machine work.
+ * @in_lpm: indicates low power mode (LPM) state.
+ * @async_int: Async interrupt arrived.
  *
  */
 struct msm_otg {
@@ -134,6 +136,8 @@ struct msm_otg {
 #define B_SESS_VLD	1
 	unsigned long inputs;
 	struct work_struct sm_work;
+	atomic_t in_lpm;
+	int async_int;
 };
 
 #endif
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index ee58f74..60ec151 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -195,6 +195,9 @@ struct ept_queue_item {
 #define PORTSC_LS              (3 << 10) /* Read - Port's Line status */
 #define PORTSC_PHCD            (1 << 23) /* phy suspend mode */
 
+#define ASYNC_INTR_CTRL         (1 << 29) /* Enable async interrupt */
+#define ULPI_STP_CTRL           (1 << 30) /* Block communication with PHY */
+
 /* OTG definitions */
 #define OTGSC_INTSTS_MASK	(0x7f << 16)
 #define OTGSC_ID		(1 << 8)
-- 
1.7.1

--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-09 11:19 ` [PATCH 1/4] USB: Add MSM OTG Controller driver Pavankumar Kondeti
@ 2010-11-16 21:43   ` Greg KH
  2010-11-17 10:23     ` Pavan Kondeti
  0 siblings, 1 reply; 11+ messages in thread
From: Greg KH @ 2010-11-16 21:43 UTC (permalink / raw)
  To: Pavankumar Kondeti; +Cc: linux-usb, linux-arm-msm

On Tue, Nov 09, 2010 at 04:49:48PM +0530, Pavankumar Kondeti wrote:
> This driver implements PHY initialization, clock management, memory mapping
> register address space, ULPI IO ops and simple OTG state machine to kick
> host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
> reference voltage on some boards.  Hence provide a sysfs interface to
> select host/peripheral mode.

As you are creating a new user/kernel abi, it MUST be documented in the
Documentation/ABI/ directory.  I can't take this patch set until that
happens.

Care to redo this one, and your device patch set based on the review
comments there, and resend them?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-16 21:43   ` Greg KH
@ 2010-11-17 10:23     ` Pavan Kondeti
  2010-11-17 16:16       ` Greg KH
  0 siblings, 1 reply; 11+ messages in thread
From: Pavan Kondeti @ 2010-11-17 10:23 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linux-arm-msm

Hi Greg,

On Tue, Nov 16, 2010 at 01:43:46PM -0800, Greg KH wrote:
> On Tue, Nov 09, 2010 at 04:49:48PM +0530, Pavankumar Kondeti wrote:
> > This driver implements PHY initialization, clock management, memory mapping
> > register address space, ULPI IO ops and simple OTG state machine to kick
> > host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
> > reference voltage on some boards.  Hence provide a sysfs interface to
> > select host/peripheral mode.
> 
> As you are creating a new user/kernel abi, it MUST be documented in the
> Documentation/ABI/ directory.  I can't take this patch set until that
> happens.
> 
Thanks for letting me know this. I will add the documentation for the sysfs file.

> Care to redo this one, and your device patch set based on the review
> comments there, and resend them?
> 
I have been advised to reuse the ci13xxx_udc.c gadget controller driver. The
driver currently supports PCI but not platform bus. I am working on making it
to support platform bus. I will post RFC patch soon. Meanwhile I am thinking
of resending host driver patches (as of now host/otg uses header files of device
controller driver). Is this fine?
-- 
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-17 10:23     ` Pavan Kondeti
@ 2010-11-17 16:16       ` Greg KH
  2010-11-18  5:00         ` Pavan Kondeti
  0 siblings, 1 reply; 11+ messages in thread
From: Greg KH @ 2010-11-17 16:16 UTC (permalink / raw)
  To: Pavan Kondeti; +Cc: linux-usb, linux-arm-msm

On Wed, Nov 17, 2010 at 03:53:52PM +0530, Pavan Kondeti wrote:
> Hi Greg,
> 
> On Tue, Nov 16, 2010 at 01:43:46PM -0800, Greg KH wrote:
> > On Tue, Nov 09, 2010 at 04:49:48PM +0530, Pavankumar Kondeti wrote:
> > > This driver implements PHY initialization, clock management, memory mapping
> > > register address space, ULPI IO ops and simple OTG state machine to kick
> > > host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
> > > reference voltage on some boards.  Hence provide a sysfs interface to
> > > select host/peripheral mode.
> > 
> > As you are creating a new user/kernel abi, it MUST be documented in the
> > Documentation/ABI/ directory.  I can't take this patch set until that
> > happens.
> > 
> Thanks for letting me know this. I will add the documentation for the sysfs file.

Also note that if you are adding a new ABI like this one, it needs to
work the same for the other existing OTG drivers as well.  So please
also work to fix them to do the same thing, or change your code to work
like the existing drivers do (hint, do the latter one...)

> > Care to redo this one, and your device patch set based on the review
> > comments there, and resend them?
> > 
> I have been advised to reuse the ci13xxx_udc.c gadget controller driver. The
> driver currently supports PCI but not platform bus. I am working on making it
> to support platform bus. I will post RFC patch soon. Meanwhile I am thinking
> of resending host driver patches (as of now host/otg uses header files of device
> controller driver). Is this fine?

Would it make much sense to have the host code in the tree at this point
in time due to the other changes you are going to have to do for the
gadget controller?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-17 16:16       ` Greg KH
@ 2010-11-18  5:00         ` Pavan Kondeti
  2010-11-18  5:20           ` Greg KH
  0 siblings, 1 reply; 11+ messages in thread
From: Pavan Kondeti @ 2010-11-18  5:00 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linux-arm-msm

Hi Greg,

On Wed, Nov 17, 2010 at 08:16:37AM -0800, Greg KH wrote:
> On Wed, Nov 17, 2010 at 03:53:52PM +0530, Pavan Kondeti wrote:
> > Hi Greg,
> > 
> > On Tue, Nov 16, 2010 at 01:43:46PM -0800, Greg KH wrote:
> > > On Tue, Nov 09, 2010 at 04:49:48PM +0530, Pavankumar Kondeti wrote:
> > > > This driver implements PHY initialization, clock management, memory mapping
> > > > register address space, ULPI IO ops and simple OTG state machine to kick
> > > > host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
> > > > reference voltage on some boards.  Hence provide a sysfs interface to
> > > > select host/peripheral mode.
> > > 
> > > As you are creating a new user/kernel abi, it MUST be documented in the
> > > Documentation/ABI/ directory.  I can't take this patch set until that
> > > happens.
> > > 
> > Thanks for letting me know this. I will add the documentation for the sysfs file.
> 
> Also note that if you are adding a new ABI like this one, it needs to
> work the same for the other existing OTG drivers as well.  So please
> also work to fix them to do the same thing, or change your code to work
> like the existing drivers do (hint, do the latter one...)
> 
I am not sure if other OTG driver require this sysfs file. USB mode i.e
Host/Peripheral is changed based on Id/VBUS status. But on some of the MSM
boards, Id/VBUS is connected to reference voltage and need an additional sysfs
file for user to change the operation mode.

Where should a driver specific sysfs file should go in Doc/ABI/ ?

> > > Care to redo this one, and your device patch set based on the review
> > > comments there, and resend them?
> > > 
> > I have been advised to reuse the ci13xxx_udc.c gadget controller driver. The
> > driver currently supports PCI but not platform bus. I am working on making it
> > to support platform bus. I will post RFC patch soon. Meanwhile I am thinking
> > of resending host driver patches (as of now host/otg uses header files of device
> > controller driver). Is this fine?
> 
> Would it make much sense to have the host code in the tree at this point
> in time due to the other changes you are going to have to do for the
> gadget controller?
> 
May be, I should wait till gadget controller driver patches are ready to avoid
any additional changes in otg driver for supporting gadget.

-- 
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-18  5:00         ` Pavan Kondeti
@ 2010-11-18  5:20           ` Greg KH
  2010-11-18  7:54             ` Pavan Kondeti
  0 siblings, 1 reply; 11+ messages in thread
From: Greg KH @ 2010-11-18  5:20 UTC (permalink / raw)
  To: Pavan Kondeti; +Cc: linux-usb, linux-arm-msm

On Thu, Nov 18, 2010 at 10:30:16AM +0530, Pavan Kondeti wrote:
> Hi Greg,
> 
> On Wed, Nov 17, 2010 at 08:16:37AM -0800, Greg KH wrote:
> > On Wed, Nov 17, 2010 at 03:53:52PM +0530, Pavan Kondeti wrote:
> > > Hi Greg,
> > > 
> > > On Tue, Nov 16, 2010 at 01:43:46PM -0800, Greg KH wrote:
> > > > On Tue, Nov 09, 2010 at 04:49:48PM +0530, Pavankumar Kondeti wrote:
> > > > > This driver implements PHY initialization, clock management, memory mapping
> > > > > register address space, ULPI IO ops and simple OTG state machine to kick
> > > > > host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
> > > > > reference voltage on some boards.  Hence provide a sysfs interface to
> > > > > select host/peripheral mode.
> > > > 
> > > > As you are creating a new user/kernel abi, it MUST be documented in the
> > > > Documentation/ABI/ directory.  I can't take this patch set until that
> > > > happens.
> > > > 
> > > Thanks for letting me know this. I will add the documentation for the sysfs file.
> > 
> > Also note that if you are adding a new ABI like this one, it needs to
> > work the same for the other existing OTG drivers as well.  So please
> > also work to fix them to do the same thing, or change your code to work
> > like the existing drivers do (hint, do the latter one...)
> > 
> I am not sure if other OTG driver require this sysfs file.

That's the point, why does this driver require something that no other
driver does?

> USB mode i.e Host/Peripheral is changed based on Id/VBUS status. But
> on some of the MSM boards, Id/VBUS is connected to reference voltage
> and need an additional sysfs file for user to change the operation
> mode.

Are you sure this should be user selectable?  Who is going to do that
selection and how is it going to happen "automatically" like OTG is
supposed to handle?

> Where should a driver specific sysfs file should go in Doc/ABI/ ?

Where all others are, there are lots of examples in that directory,
including a README, right?  Did you read that and it not explain things
sufficiently?  If so, please let me know what can be expanded on in that
file to make it easier for others in the future.


My main complaint here is that you are creating a brand new
kernel/userspace ABI, and you bury it in a driver patch without giving
really any warning or description of it at all.  This is something that
we need to make sure we get correct as you (yes you) will be maintaining
it for the next 12+ years.  This is not something to do lightly at all,
as I'm sure you can imagine.


> > > > Care to redo this one, and your device patch set based on the review
> > > > comments there, and resend them?
> > > > 
> > > I have been advised to reuse the ci13xxx_udc.c gadget controller driver. The
> > > driver currently supports PCI but not platform bus. I am working on making it
> > > to support platform bus. I will post RFC patch soon. Meanwhile I am thinking
> > > of resending host driver patches (as of now host/otg uses header files of device
> > > controller driver). Is this fine?
> > 
> > Would it make much sense to have the host code in the tree at this point
> > in time due to the other changes you are going to have to do for the
> > gadget controller?
> > 
> May be, I should wait till gadget controller driver patches are ready to avoid
> any additional changes in otg driver for supporting gadget.

Yes, that sounds like a good idea.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] USB: Add MSM OTG Controller driver
  2010-11-18  5:20           ` Greg KH
@ 2010-11-18  7:54             ` Pavan Kondeti
  0 siblings, 0 replies; 11+ messages in thread
From: Pavan Kondeti @ 2010-11-18  7:54 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linux-arm-msm

On Wed, Nov 17, 2010 at 09:20:16PM -0800, Greg KH wrote:
> On Thu, Nov 18, 2010 at 10:30:16AM +0530, Pavan Kondeti wrote:
> > Hi Greg,
> > 
> > On Wed, Nov 17, 2010 at 08:16:37AM -0800, Greg KH wrote:
> > > On Wed, Nov 17, 2010 at 03:53:52PM +0530, Pavan Kondeti wrote:
> > > > Hi Greg,
> > > > 
> > > > On Tue, Nov 16, 2010 at 01:43:46PM -0800, Greg KH wrote:
> > > > > On Tue, Nov 09, 2010 at 04:49:48PM +0530, Pavankumar Kondeti wrote:
> > > > > > This driver implements PHY initialization, clock management, memory mapping
> > > > > > register address space, ULPI IO ops and simple OTG state machine to kick
> > > > > > host/peripheral based on Id/VBUS line status.  VBUS/Id lines are tied to a
> > > > > > reference voltage on some boards.  Hence provide a sysfs interface to
> > > > > > select host/peripheral mode.
> > > > > 
> > > > > As you are creating a new user/kernel abi, it MUST be documented in the
> > > > > Documentation/ABI/ directory.  I can't take this patch set until that
> > > > > happens.
> > > > > 
> > > > Thanks for letting me know this. I will add the documentation for the sysfs file.
> > > 
> > > Also note that if you are adding a new ABI like this one, it needs to
> > > work the same for the other existing OTG drivers as well.  So please
> > > also work to fix them to do the same thing, or change your code to work
> > > like the existing drivers do (hint, do the latter one...)
> > > 
> > I am not sure if other OTG driver require this sysfs file.
> 
> That's the point, why does this driver require something that no other
> driver does?
> 
> > USB mode i.e Host/Peripheral is changed based on Id/VBUS status. But
> > on some of the MSM boards, Id/VBUS is connected to reference voltage
> > and need an additional sysfs file for user to change the operation
> > mode.
> 
> Are you sure this should be user selectable?  Who is going to do that
> selection and how is it going to happen "automatically" like OTG is
> supposed to handle?
> 
The board I am using is a reference board where Id is grounded always.  But we
use it in peripheral mode for ADB (Android debugging Bridge). So user is going
to write into the sysfs file ("host"/"peripheral"/"none"). But this is mainly
for debugging/testing purpose.
> > Where should a driver specific sysfs file should go in Doc/ABI/ ?
> 
> Where all others are, there are lots of examples in that directory,
> including a README, right?  Did you read that and it not explain things
> sufficiently?  If so, please let me know what can be expanded on in that
> file to make it easier for others in the future.
> 
I have read the README file and it only talks about different levels of
stability. I have not looked at examples. After looking at examples I
understand that testing/sysfs-driver-usb-msm_otg is the correct file.
> 
> My main complaint here is that you are creating a brand new
> kernel/userspace ABI, and you bury it in a driver patch without giving
> really any warning or description of it at all.  This is something that
> we need to make sure we get correct as you (yes you) will be maintaining
> it for the next 12+ years.  This is not something to do lightly at all,
> as I'm sure you can imagine.
> 
Okay. I understand now. Thanks for the explanation. I should not have used a
sysfs file, which creates a new kernel/userspace ABI. I will use debugfs as it
is mainly intended for debugging purpose. The only reason I have gone
for sysfs is that to have this option available without CONFIG_DEBUGFS.
> 
-- 
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2010-11-18  7:54 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-09 11:19 [PATCH 0/4] Add MSM USB Host Controller support Pavankumar Kondeti
2010-11-09 11:19 ` [PATCH 1/4] USB: Add MSM OTG Controller driver Pavankumar Kondeti
2010-11-16 21:43   ` Greg KH
2010-11-17 10:23     ` Pavan Kondeti
2010-11-17 16:16       ` Greg KH
2010-11-18  5:00         ` Pavan Kondeti
2010-11-18  5:20           ` Greg KH
2010-11-18  7:54             ` Pavan Kondeti
2010-11-09 11:19 ` [PATCH 2/4] USB: EHCI: Add MSM Host " Pavankumar Kondeti
2010-11-09 11:19 ` [PATCH 3/4] USB: EHCI: msm: Add support for power management Pavankumar Kondeti
2010-11-09 11:19 ` [PATCH 4/4] USB: OTG: msm: Add spport " Pavankumar Kondeti

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.