* [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea
@ 2013-02-05 8:00 Peter Chen
2013-02-05 8:00 ` [PATCH v8 1/8] Revert "USB: chipidea: add vbus detect for udc" Peter Chen
` (8 more replies)
0 siblings, 9 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
Changes for v7:
- Add ci_supports_gadget helper to know if the controller
supports gadget, if the controller supports gadget, it
needs to read otgsc to know the vbus value, basically,
if the controller supports gadget, it will support host
as well [3/8]
- At ci_hdrc_probe, it needs to add free memory at error path
[3/8]
- Cosolidate ci->driver = NULL at ci13xxx_stop
[8/8]
Changes for v6:
For Patch 8/8, we only need to set ci->driver to NULL when usb cable
is not connected, for other changes, it will case some runtime pm
unmatch and un-align with udc-core & composite driver problems.
Changes for v5:
- Add Alex comments for init/destroy function [3/8] [4/8]
- Remove memset(&ci->gadget, 0, sizeof(ci->gadget)) at destory function [4/8]
- Add Kishon's comment: Change the format of struct usb_otg otg at drivers/usb/chipidea/ci.h
[1/8]
- Add comments for CI_VBUS_STABLE_TIMEOUT [3/8]
- Change the otg_set_peripheral return value check as the fully
chipidea driver users don't need it. [4/8]
- Fix one bug that the oops when re-plug in usb cable after
rmmod gadget [8/8]
Anyone who has tested this patchset, please give a tested-by, thanks.
This patchset adds tested otg id switch function and
vbus connect/disconnect detection for chipidea driver.
The mainly design of id/vbus handling follows msm otg driver.
I hope the msm usb maintainer can have a look of this patchset,
and give some comments, and move the whole msm usb driver to
chipidea framework if possible in the future.
This patch is fully tested at imx6 sabrelite platform.
My chipidea repo: https://github.com/hzpeterchen/linux-usb.git
which is rebased of Sascha's git
(git://git.pengutronix.de/git/imx/linux-2.6.git tags/usb-chipidea-otg-for-next)
At sascha's tag, it finishes some dr_mode and phy_type patches,
in that way, we can choose host-only and gadget-only function at
dts file as well as we can choose different phy setting.
Besides sascha's work, there are some Michael Grzeschik/Marc Kleine-Budde
usbmisc patch, imx chipidea suspend/resume patch, and one pin control
patch for imx6
Peter Chen (8):
Revert "USB: chipidea: add vbus detect for udc"
usb: chipidea: add otg file
usb: chipidea: add otg id switch and vbus connect/disconnect detect
usb: chipidea: consolidate kinds of APIs for both roles
usb: chipidea: udc: add pullup/pulldown dp at hw_device_state
usb: chipidea: udc: retire the flag CI13_PULLUP_ON_VBUS
usb: chipidea: imx: add internal vbus regulator control
usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod
gadget
drivers/usb/chipidea/Makefile | 2 +-
drivers/usb/chipidea/bits.h | 10 ++
drivers/usb/chipidea/ci.h | 12 ++-
drivers/usb/chipidea/ci13xxx_imx.c | 80 ++++++++++++----
drivers/usb/chipidea/ci13xxx_msm.c | 1 -
drivers/usb/chipidea/core.c | 189 ++++++++++++++++++++++++++++++++----
drivers/usb/chipidea/host.c | 6 +
drivers/usb/chipidea/host.h | 4 +-
drivers/usb/chipidea/otg.c | 68 +++++++++++++
drivers/usb/chipidea/otg.h | 9 ++
drivers/usb/chipidea/udc.c | 112 ++++++++++------------
drivers/usb/chipidea/udc.h | 4 +-
include/linux/usb/chipidea.h | 1 -
13 files changed, 390 insertions(+), 108 deletions(-)
create mode 100644 drivers/usb/chipidea/otg.c
create mode 100644 drivers/usb/chipidea/otg.h
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 1/8] Revert "USB: chipidea: add vbus detect for udc"
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 2/8] usb: chipidea: add otg file Peter Chen
` (7 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
vbus register is at otgsc, and vbus detect does not belong
to device function. Revert this patch, and will move
vbus detect function to drivers/usb/chipidea/udc.c
This reverts commit 8c4fc031954b4eb72daf13d3c907a985a3eee208.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index e25d126..d738603 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -139,7 +139,6 @@ struct ci13xxx {
enum ci_role role;
bool is_otg;
struct work_struct work;
- struct work_struct vbus_work;
struct workqueue_struct *wq;
struct dma_pool *qh_pool;
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 2f45bba..d214448 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -305,18 +305,6 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci)
return reg;
}
-static void hw_enable_vbus_intr(struct ci13xxx *ci)
-{
- hw_write(ci, OP_OTGSC, OTGSC_AVVIS, OTGSC_AVVIS);
- hw_write(ci, OP_OTGSC, OTGSC_AVVIE, OTGSC_AVVIE);
- queue_work(ci->wq, &ci->vbus_work);
-}
-
-static void hw_disable_vbus_intr(struct ci13xxx *ci)
-{
- hw_write(ci, OP_OTGSC, OTGSC_AVVIE, 0);
-}
-
/**
* hw_test_and_clear_setup_guard: test & clear setup guard (execute without
* interruption)
@@ -383,16 +371,6 @@ static int hw_usb_reset(struct ci13xxx *ci)
return 0;
}
-static void vbus_work(struct work_struct *work)
-{
- struct ci13xxx *ci = container_of(work, struct ci13xxx, vbus_work);
-
- if (hw_read(ci, OP_OTGSC, OTGSC_AVV))
- usb_gadget_vbus_connect(&ci->gadget);
- else
- usb_gadget_vbus_disconnect(&ci->gadget);
-}
-
/******************************************************************************
* UTIL block
*****************************************************************************/
@@ -1392,7 +1370,6 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
if (is_active) {
pm_runtime_get_sync(&_gadget->dev);
hw_device_reset(ci, USBMODE_CM_DC);
- hw_enable_vbus_intr(ci);
hw_device_state(ci, ci->ep0out->qh.dma);
} else {
hw_device_state(ci, 0);
@@ -1567,10 +1544,8 @@ static int ci13xxx_start(struct usb_gadget *gadget,
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) {
if (ci->vbus_active) {
- if (ci->platdata->flags & CI13XXX_REGS_SHARED) {
+ if (ci->platdata->flags & CI13XXX_REGS_SHARED)
hw_device_reset(ci, USBMODE_CM_DC);
- hw_enable_vbus_intr(ci);
- }
} else {
pm_runtime_put_sync(&ci->gadget.dev);
goto done;
@@ -1676,13 +1651,6 @@ static irqreturn_t udc_irq(struct ci13xxx *ci)
} else {
retval = IRQ_NONE;
}
-
- intr = hw_read(ci, OP_OTGSC, ~0);
- hw_write(ci, OP_OTGSC, ~0, intr);
-
- if (intr & (OTGSC_AVVIE & OTGSC_AVVIS))
- queue_work(ci->wq, &ci->vbus_work);
-
spin_unlock(&ci->lock);
return retval;
@@ -1758,7 +1726,6 @@ static int udc_start(struct ci13xxx *ci)
retval = hw_device_reset(ci, USBMODE_CM_DC);
if (retval)
goto put_transceiver;
- hw_enable_vbus_intr(ci);
}
retval = device_register(&ci->gadget.dev);
@@ -1821,9 +1788,6 @@ static void udc_stop(struct ci13xxx *ci)
if (ci == NULL)
return;
- hw_disable_vbus_intr(ci);
- cancel_work_sync(&ci->vbus_work);
-
usb_del_gadget_udc(&ci->gadget);
destroy_eps(ci);
@@ -1864,7 +1828,6 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci)
rdrv->irq = udc_irq;
rdrv->name = "gadget";
ci->roles[CI_ROLE_GADGET] = rdrv;
- INIT_WORK(&ci->vbus_work, vbus_work);
return 0;
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 2/8] usb: chipidea: add otg file
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
2013-02-05 8:00 ` [PATCH v8 1/8] Revert "USB: chipidea: add vbus detect for udc" Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect Peter Chen
` (6 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
Implement struct usb_otg, In that way, calling otg_set_peripheral
will not be failed at udc.c.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index d92ca32..11f513c 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
-ci_hdrc-y := core.o
+ci_hdrc-y := core.o otg.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index d738603..697e369 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -129,6 +129,7 @@ struct hw_bank {
* @vbus_active: is VBUS active
* @transceiver: pointer to USB PHY, if any
* @hcd: pointer to usb_hcd for ehci host driver
+ * @otg: for otg support
*/
struct ci13xxx {
struct device *dev;
@@ -164,6 +165,7 @@ struct ci13xxx {
bool global_phy;
struct usb_phy *transceiver;
struct usb_hcd *hcd;
+ struct usb_otg otg;
};
static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
new file mode 100644
index 0000000..7dea3b3
--- /dev/null
+++ b/drivers/usb/chipidea/otg.c
@@ -0,0 +1,60 @@
+/*
+ * otg.c - ChipIdea USB IP core OTG driver
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ * Author: Peter Chen
+ *
+ * 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+#include "udc.h"
+#include "bits.h"
+#include "host.h"
+#include "debug.h"
+
+static int ci_otg_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *periph)
+{
+ otg->gadget = periph;
+
+ return 0;
+}
+
+static int ci_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ otg->host = host;
+
+ return 0;
+}
+
+/**
+ * ci_hdrc_otg_init - initialize device related bits
+ * ci: the controller
+ *
+ * This function create otg struct, if the device can switch between
+ * device and host.
+ */
+int ci_hdrc_otg_init(struct ci13xxx *ci)
+{
+ /* Useless at current */
+ ci->otg.set_peripheral = ci_otg_set_peripheral;
+ ci->otg.set_host = ci_otg_set_host;
+ if (!IS_ERR_OR_NULL(ci->transceiver))
+ ci->transceiver->otg = &ci->otg;
+
+ return 0;
+}
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
new file mode 100644
index 0000000..b4c6b3e
--- /dev/null
+++ b/drivers/usb/chipidea/otg.h
@@ -0,0 +1,6 @@
+#ifndef __DRIVERS_USB_CHIPIDEA_OTG_H
+#define __DRIVERS_USB_CHIPIDEA_OTG_H
+
+int ci_hdrc_otg_init(struct ci13xxx *ci);
+
+#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
2013-02-05 8:00 ` [PATCH v8 1/8] Revert "USB: chipidea: add vbus detect for udc" Peter Chen
2013-02-05 8:00 ` [PATCH v8 2/8] usb: chipidea: add otg file Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-14 12:37 ` Alexander Shishkin
2013-02-05 8:00 ` [PATCH v8 4/8] usb: chipidea: consolidate kinds of APIs for both roles Peter Chen
` (5 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
The main design flow is the same with msm otg driver, that is the id and
vbus interrupt are handled at core driver, others are handled by
individual drivers.
- At former design, when switch usb role from device->host, it will call
udc_stop, it will remove the gadget driver, so when switch role
from host->device, it can't add gadget driver any more.
At new design, when role switch occurs, the gadget just calls
usb_gadget_vbus_disconnect/usb_gadget_vbus_connect as well as
reset controller, it will not free any device/gadget structure
- Add vbus connect and disconnect to core interrupt handler, it can
notify udc driver by calling usb_gadget_vbus_disconnect
/usb_gadget_vbus_connect.
- vbus interrupt needs to be handled when gadget function is enabled
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
index d8ffc2f..58ef56c 100644
--- a/drivers/usb/chipidea/bits.h
+++ b/drivers/usb/chipidea/bits.h
@@ -77,11 +77,21 @@
#define OTGSC_ASVIS BIT(18)
#define OTGSC_BSVIS BIT(19)
#define OTGSC_BSEIS BIT(20)
+#define OTGSC_1MSIS BIT(21)
+#define OTGSC_DPIS BIT(22)
#define OTGSC_IDIE BIT(24)
#define OTGSC_AVVIE BIT(25)
#define OTGSC_ASVIE BIT(26)
#define OTGSC_BSVIE BIT(27)
#define OTGSC_BSEIE BIT(28)
+#define OTGSC_1MSIE BIT(29)
+#define OTGSC_DPIE BIT(30)
+#define OTGSC_INT_EN_BITS (OTGSC_IDIE | OTGSC_AVVIE | OTGSC_ASVIE \
+ | OTGSC_BSVIE | OTGSC_BSEIE | OTGSC_1MSIE \
+ | OTGSC_DPIE)
+#define OTGSC_INT_STATUS_BITS (OTGSC_IDIS | OTGSC_AVVIS | OTGSC_ASVIS \
+ | OTGSC_BSVIS | OTGSC_BSEIS | OTGSC_1MSIS \
+ | OTGSC_DPIS)
/* USBMODE */
#define USBMODE_CM (0x03UL << 0)
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 697e369..325d790 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -130,6 +130,7 @@ struct hw_bank {
* @transceiver: pointer to USB PHY, if any
* @hcd: pointer to usb_hcd for ehci host driver
* @otg: for otg support
+ * @events: events for otg, and handled@ci_role_work
*/
struct ci13xxx {
struct device *dev;
@@ -140,6 +141,7 @@ struct ci13xxx {
enum ci_role role;
bool is_otg;
struct work_struct work;
+ struct delayed_work dwork;
struct workqueue_struct *wq;
struct dma_pool *qh_pool;
@@ -166,6 +168,8 @@ struct ci13xxx {
struct usb_phy *transceiver;
struct usb_hcd *hcd;
struct usb_otg otg;
+ bool id_event;
+ bool b_sess_valid_event;
};
static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
@@ -314,4 +318,6 @@ int hw_port_test_set(struct ci13xxx *ci, u8 mode);
u8 hw_port_test_get(struct ci13xxx *ci);
+void ci_handle_vbus_change(struct ci13xxx *ci);
+
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
index 136869b..793fdba 100644
--- a/drivers/usb/chipidea/ci13xxx_imx.c
+++ b/drivers/usb/chipidea/ci13xxx_imx.c
@@ -110,7 +110,8 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
pdata->capoffset = DEF_CAPOFFSET;
pdata->flags = CI13XXX_REQUIRE_TRANSCEIVER |
CI13XXX_PULLUP_ON_VBUS |
- CI13XXX_DISABLE_STREAMING;
+ CI13XXX_DISABLE_STREAMING |
+ CI13XXX_REGS_SHARED;
pdata->phy_mode = of_usb_get_phy_mode(pdev->dev.of_node);
pdata->dr_mode = of_usb_get_dr_mode(pdev->dev.of_node);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index c89f2aa..fbb6984 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -75,6 +75,7 @@
#include "bits.h"
#include "host.h"
#include "debug.h"
+#include "otg.h"
/* Controller register map */
static uintptr_t ci_regs_nolpm[] = {
@@ -201,6 +202,14 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
if (ci->hw_ep_max > ENDPT_MAX)
return -ENODEV;
+ /* Disable all interrupts bits */
+ hw_write(ci, OP_USBINTR, 0xffffffff, 0);
+ ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS);
+
+ /* Clear all interrupts status bits*/
+ hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
+ ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS);
+
dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n",
ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
@@ -302,24 +311,132 @@ static enum ci_role ci_otg_role(struct ci13xxx *ci)
}
/**
- * ci_role_work - perform role changing based on ID pin
- * @work: work struct
+ * hw_wait_reg: wait the register value
+ *
+ * Sometimes, it needs to wait register value before going on.
+ * Eg, when switch to device mode, the vbus value should be lower
+ * than OTGSC_BSV before connects to host.
+ *
+ * @ci: the controller
+ * @reg: register index
+ * @mask: mast bit
+ * @value: the bit value to wait
+ * @timeout: timeout to indicate an error
+ *
+ * This function returns an error code if timeout
*/
-static void ci_role_work(struct work_struct *work)
+static int hw_wait_reg(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask,
+ u32 value, unsigned long timeout)
+{
+ unsigned long elapse = jiffies + timeout;
+
+ while (hw_read(ci, reg, mask) != value) {
+ if (time_after(jiffies, elapse)) {
+ dev_err(ci->dev, "timeout waiting for %08x in %d\n",
+ mask, reg);
+ return -ETIMEDOUT;
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
+/*
+ * Since there are some capacitances at vbus, the vbus may not
+ * change very quickly when we stop/start internal 5v.
+ * Below is a vbus stable timeout value, the routine will quit
+ * if the vbus gets the required value, we can think there are some
+ * problems for hardware if the vbus can't get the required value
+ * within 5 seconds.
+ */
+#define CI_VBUS_STABLE_TIMEOUT 500
+static void ci_handle_id_switch(struct ci13xxx *ci)
{
- struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
enum ci_role role = ci_otg_role(ci);
if (role != ci->role) {
dev_dbg(ci->dev, "switching from %s to %s\n",
ci_role(ci)->name, ci->roles[role]->name);
- ci_role_stop(ci);
- ci_role_start(ci, role);
- enable_irq(ci->irq);
+ /* 1. Finish the current role */
+ if (ci->role == CI_ROLE_GADGET) {
+ usb_gadget_vbus_disconnect(&ci->gadget);
+ /* host doesn't care B_SESSION_VALID event */
+ ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
+ ci_disable_otg_interrupt(ci, OTGSC_BSVIE);
+ ci->role = CI_ROLE_END;
+ /* reset controller */
+ hw_device_reset(ci, USBMODE_CM_IDLE);
+ } else if (ci->role == CI_ROLE_HOST) {
+ ci_role_stop(ci);
+ /* reset controller */
+ hw_device_reset(ci, USBMODE_CM_IDLE);
+ }
+
+ /* 2. Turn on/off vbus according to coming role */
+ if (ci_otg_role(ci) == CI_ROLE_GADGET) {
+ otg_set_vbus(&ci->otg, false);
+ /* wait vbus lower than OTGSC_BSV */
+ hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
+ CI_VBUS_STABLE_TIMEOUT);
+ } else if (ci_otg_role(ci) == CI_ROLE_HOST) {
+ otg_set_vbus(&ci->otg, true);
+ /* wait vbus higher than OTGSC_AVV */
+ hw_wait_reg(ci, OP_OTGSC, OTGSC_AVV, OTGSC_AVV,
+ CI_VBUS_STABLE_TIMEOUT);
+ }
+
+ /* 3. Begin the new role */
+ if (ci_otg_role(ci) == CI_ROLE_GADGET) {
+ ci->role = role;
+ ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
+ ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
+ } else if (ci_otg_role(ci) == CI_ROLE_HOST) {
+ ci_role_start(ci, role);
+ }
}
}
+void ci_handle_vbus_change(struct ci13xxx *ci)
+{
+ u32 otgsc = hw_read(ci, OP_OTGSC, ~0);
+
+ if (otgsc & OTGSC_BSV)
+ usb_gadget_vbus_connect(&ci->gadget);
+ else
+ usb_gadget_vbus_disconnect(&ci->gadget);
+}
+
+/**
+ * ci_otg_work - perform otg (vbus/id) event handle
+ * @work: work struct
+ */
+static void ci_otg_work(struct work_struct *work)
+{
+ struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
+
+ if (ci->id_event) {
+ ci->id_event = false;
+ ci_handle_id_switch(ci);
+ } else if (ci->b_sess_valid_event) {
+ ci->b_sess_valid_event = false;
+ ci_handle_vbus_change(ci);
+ } else
+ dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
+
+ enable_irq(ci->irq);
+}
+
+static void ci_delayed_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct ci13xxx *ci = container_of(dwork, struct ci13xxx, dwork);
+
+ otg_set_vbus(&ci->otg, true);
+
+}
+
static ssize_t show_role(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -352,25 +469,49 @@ static ssize_t store_role(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role);
+static bool ci_supports_gadget(struct ci13xxx *ci)
+{
+ return (ci->roles[CI_ROLE_GADGET]) ? true : false;
+}
+
static irqreturn_t ci_irq(int irq, void *data)
{
struct ci13xxx *ci = data;
irqreturn_t ret = IRQ_NONE;
u32 otgsc = 0;
- if (ci->is_otg)
+ if (ci_supports_gadget(ci))
otgsc = hw_read(ci, OP_OTGSC, ~0);
- if (ci->role != CI_ROLE_END)
- ret = ci_role(ci)->irq(ci);
+ /*
+ * Handle id change interrupt, it indicates device/host function
+ * switch.
+ */
+ if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+ ci->id_event = true;
+ ci_clear_otg_interrupt(ci, OTGSC_IDIS);
+ disable_irq_nosync(ci->irq);
+ queue_work(ci->wq, &ci->work);
+ return IRQ_HANDLED;
+ }
- if (ci->is_otg && (otgsc & OTGSC_IDIS)) {
- hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
+ /*
+ * Handle vbus change interrupt, it indicates device connection
+ * and disconnection events.
+ */
+ if (ci_supports_gadget(ci) && (otgsc & OTGSC_BSVIE)
+ && (otgsc & OTGSC_BSVIS)) {
+ ci->b_sess_valid_event = true;
+ ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
disable_irq_nosync(ci->irq);
queue_work(ci->wq, &ci->work);
- ret = IRQ_HANDLED;
+ return IRQ_HANDLED;
}
+ /* Handle device/host interrupt */
+ if (ci->role != CI_ROLE_END)
+ ret = ci_role(ci)->irq(ci);
+
return ret;
}
@@ -436,6 +577,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
enum usb_dr_mode dr_mode;
+ u32 otgsc;
if (!dev->platform_data) {
dev_err(dev, "platform data missing\n");
@@ -481,7 +623,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
- INIT_WORK(&ci->work, ci_role_work);
+ INIT_WORK(&ci->work, ci_otg_work);
+ INIT_DELAYED_WORK(&ci->dwork, ci_delayed_work);
ci->wq = create_singlethread_workqueue("ci_otg");
if (!ci->wq) {
dev_err(dev, "can't create workqueue\n");
@@ -512,7 +655,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
+ dev_dbg(dev, "support otg\n");
ci->is_otg = true;
+ /* if otg is supported, create struct usb_otg */
+ ci_hdrc_otg_init(ci);
/* ID pin needs 1ms debouce time, we delay 2ms for safe */
mdelay(2);
ci->role = ci_otg_role(ci);
@@ -531,6 +677,17 @@ static int ci_hdrc_probe(struct platform_device *pdev)
goto rm_wq;
}
+ otgsc = hw_read(ci, OP_OTGSC, ~0);
+ /*
+ * if it is device mode:
+ * - Enable vbus detect
+ * - If it has already connected to host, notify udc
+ */
+ if (ci->role == CI_ROLE_GADGET) {
+ ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
+ ci_handle_vbus_change(ci);
+ }
+
platform_set_drvdata(pdev, ci);
ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
ci);
@@ -541,8 +698,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (ret)
goto rm_attr;
- if (ci->is_otg)
- hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE);
+ /* Handle the situation that usb device at the MicroB to A cable */
+ if (ci->is_otg && !(otgsc & OTGSC_ID))
+ queue_delayed_work(ci->wq, &ci->dwork, msecs_to_jiffies(500));
return ret;
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 7dea3b3..2986d91 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -10,21 +10,28 @@
* published by the Free Software Foundation.
*/
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
#include "ci.h"
-#include "udc.h"
#include "bits.h"
-#include "host.h"
-#include "debug.h"
+
+void ci_clear_otg_interrupt(struct ci13xxx *ci, u32 bits)
+{
+ /* Only clear request bits */
+ hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS, bits);
+}
+
+void ci_enable_otg_interrupt(struct ci13xxx *ci, u32 bits)
+{
+ hw_write(ci, OP_OTGSC, bits, bits);
+}
+
+void ci_disable_otg_interrupt(struct ci13xxx *ci, u32 bits)
+{
+ hw_write(ci, OP_OTGSC, bits, 0);
+}
static int ci_otg_set_peripheral(struct usb_otg *otg,
struct usb_gadget *periph)
@@ -55,6 +62,7 @@ int ci_hdrc_otg_init(struct ci13xxx *ci)
ci->otg.set_host = ci_otg_set_host;
if (!IS_ERR_OR_NULL(ci->transceiver))
ci->transceiver->otg = &ci->otg;
+ ci_enable_otg_interrupt(ci, OTGSC_IDIE);
return 0;
}
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
index b4c6b3e..fa30428 100644
--- a/drivers/usb/chipidea/otg.h
+++ b/drivers/usb/chipidea/otg.h
@@ -2,5 +2,8 @@
#define __DRIVERS_USB_CHIPIDEA_OTG_H
int ci_hdrc_otg_init(struct ci13xxx *ci);
+void ci_clear_otg_interrupt(struct ci13xxx *ci, u32 bits);
+void ci_enable_otg_interrupt(struct ci13xxx *ci, u32 bits);
+void ci_disable_otg_interrupt(struct ci13xxx *ci, u32 bits);
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index d214448..83e54bb 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1371,6 +1371,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
pm_runtime_get_sync(&_gadget->dev);
hw_device_reset(ci, USBMODE_CM_DC);
hw_device_state(ci, ci->ep0out->qh.dma);
+ dev_dbg(ci->dev, "Connected to host\n");
} else {
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
@@ -1378,6 +1379,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
CI13XXX_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(&ci->gadget);
pm_runtime_put_sync(&_gadget->dev);
+ dev_dbg(ci->dev, "Disconnected from host\n");
}
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 4/8] usb: chipidea: consolidate kinds of APIs for both roles
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
` (2 preceding siblings ...)
2013-02-05 8:00 ` [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 5/8] usb: chipidea: udc: add pullup/pulldown dp at hw_device_state Peter Chen
` (4 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
- Create/destroy the gadget at udc's init and destory function
- start/stop API are used at otg id switch and probe routine
- Defer some gadget operations at ci's delayed work queue
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 325d790..3ebe87a 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -205,7 +205,6 @@ static inline void ci_role_stop(struct ci13xxx *ci)
ci->roles[role]->stop(ci);
}
-
/******************************************************************************
* REGISTERS
*****************************************************************************/
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index fbb6984..eacaf9f 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -360,27 +360,16 @@ static void ci_handle_id_switch(struct ci13xxx *ci)
ci_role(ci)->name, ci->roles[role]->name);
/* 1. Finish the current role */
- if (ci->role == CI_ROLE_GADGET) {
- usb_gadget_vbus_disconnect(&ci->gadget);
- /* host doesn't care B_SESSION_VALID event */
- ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
- ci_disable_otg_interrupt(ci, OTGSC_BSVIE);
- ci->role = CI_ROLE_END;
- /* reset controller */
- hw_device_reset(ci, USBMODE_CM_IDLE);
- } else if (ci->role == CI_ROLE_HOST) {
- ci_role_stop(ci);
- /* reset controller */
- hw_device_reset(ci, USBMODE_CM_IDLE);
- }
+ ci_role_stop(ci);
+ hw_device_reset(ci, USBMODE_CM_IDLE);
/* 2. Turn on/off vbus according to coming role */
- if (ci_otg_role(ci) == CI_ROLE_GADGET) {
+ if (role == CI_ROLE_GADGET) {
otg_set_vbus(&ci->otg, false);
/* wait vbus lower than OTGSC_BSV */
hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
CI_VBUS_STABLE_TIMEOUT);
- } else if (ci_otg_role(ci) == CI_ROLE_HOST) {
+ } else if (role == CI_ROLE_HOST) {
otg_set_vbus(&ci->otg, true);
/* wait vbus higher than OTGSC_AVV */
hw_wait_reg(ci, OP_OTGSC, OTGSC_AVV, OTGSC_AVV,
@@ -388,13 +377,7 @@ static void ci_handle_id_switch(struct ci13xxx *ci)
}
/* 3. Begin the new role */
- if (ci_otg_role(ci) == CI_ROLE_GADGET) {
- ci->role = role;
- ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
- ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
- } else if (ci_otg_role(ci) == CI_ROLE_HOST) {
- ci_role_start(ci, role);
- }
+ ci_role_start(ci, role);
}
}
@@ -433,8 +416,24 @@ static void ci_delayed_work(struct work_struct *work)
struct delayed_work *dwork = to_delayed_work(work);
struct ci13xxx *ci = container_of(dwork, struct ci13xxx, dwork);
- otg_set_vbus(&ci->otg, true);
+ if (ci->role == CI_ROLE_GADGET) {
+ /*
+ * if it is device mode:
+ * - Enable vbus detect
+ * - If it has already connected to host, notify udc
+ */
+ ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
+ ci_handle_vbus_change(ci);
+ } else if (ci->is_otg && (ci->role == CI_ROLE_HOST)) {
+ /* USB Device at the MicroB to A cable */
+ otg_set_vbus(&ci->otg, true);
+ }
+}
+static inline void ci_role_destroy(struct ci13xxx *ci)
+{
+ ci_hdrc_gadget_destroy(ci);
+ ci_hdrc_host_destroy(ci);
}
static ssize_t show_role(struct device *dev, struct device_attribute *attr,
@@ -577,7 +576,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
enum usb_dr_mode dr_mode;
- u32 otgsc;
if (!dev->platform_data) {
dev_err(dev, "platform data missing\n");
@@ -674,18 +672,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "can't start %s role\n", ci_role(ci)->name);
ret = -ENODEV;
- goto rm_wq;
- }
-
- otgsc = hw_read(ci, OP_OTGSC, ~0);
- /*
- * if it is device mode:
- * - Enable vbus detect
- * - If it has already connected to host, notify udc
- */
- if (ci->role == CI_ROLE_GADGET) {
- ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
- ci_handle_vbus_change(ci);
+ goto free_memory;
}
platform_set_drvdata(pdev, ci);
@@ -696,18 +683,22 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ret = device_create_file(dev, &dev_attr_role);
if (ret)
- goto rm_attr;
+ goto free_irq;
- /* Handle the situation that usb device at the MicroB to A cable */
- if (ci->is_otg && !(otgsc & OTGSC_ID))
- queue_delayed_work(ci->wq, &ci->dwork, msecs_to_jiffies(500));
+ /* Defer some operations */
+ queue_delayed_work(ci->wq, &ci->dwork, msecs_to_jiffies(200));
return ret;
-rm_attr:
- device_remove_file(dev, &dev_attr_role);
+free_irq:
+ free_irq(ci->irq, ci);
stop:
- ci_role_stop(ci);
+ ci_role_destroy(ci);
+free_memory:
+ if (ci->roles[CI_ROLE_HOST])
+ devm_kfree(dev, ci->roles[CI_ROLE_HOST]);
+ if (ci->roles[CI_ROLE_GADGET])
+ devm_kfree(dev, ci->roles[CI_ROLE_GADGET]);
rm_wq:
flush_workqueue(ci->wq);
destroy_workqueue(ci->wq);
@@ -723,7 +714,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
destroy_workqueue(ci->wq);
device_remove_file(ci->dev, &dev_attr_role);
free_irq(ci->irq, ci);
- ci_role_stop(ci);
+ ci_role_destroy(ci);
return 0;
}
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 8e9d312..ead3158 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -105,3 +105,9 @@ int ci_hdrc_host_init(struct ci13xxx *ci)
return 0;
}
+
+void ci_hdrc_host_destroy(struct ci13xxx *ci)
+{
+ if (ci->role == CI_ROLE_HOST)
+ host_stop(ci);
+}
diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h
index 761fb1f..5fd03a4 100644
--- a/drivers/usb/chipidea/host.h
+++ b/drivers/usb/chipidea/host.h
@@ -4,13 +4,15 @@
#ifdef CONFIG_USB_CHIPIDEA_HOST
int ci_hdrc_host_init(struct ci13xxx *ci);
-
+void ci_hdrc_host_destroy(struct ci13xxx *ci);
#else
static inline int ci_hdrc_host_init(struct ci13xxx *ci)
{
return -ENXIO;
}
+static void ci_hdrc_host_destroy(struct ci13xxx *ci)
+{}
#endif
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 83e54bb..e82dae4 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -31,6 +31,7 @@
#include "ci.h"
#include "udc.h"
+#include "otg.h"
#include "bits.h"
#include "debug.h"
@@ -1743,7 +1744,12 @@ static int udc_start(struct ci13xxx *ci)
if (!IS_ERR_OR_NULL(ci->transceiver)) {
retval = otg_set_peripheral(ci->transceiver->otg,
&ci->gadget);
- if (retval)
+ /*
+ * If we implement all USB functions using chipidea drivers,
+ * it doesn't need to call above API, meanwhile, if we only
+ * use gadget function, calling above API is useless.
+ */
+ if (retval && retval != -ENOTSUPP)
goto remove_dbg;
}
@@ -1780,12 +1786,27 @@ free_qh_pool:
return retval;
}
+static int udc_id_switch_for_device(struct ci13xxx *ci)
+{
+ ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
+ ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
+
+ return 0;
+}
+
+static void udc_id_switch_for_host(struct ci13xxx *ci)
+{
+ /* host doesn't care B_SESSION_VALID event */
+ ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
+ ci_disable_otg_interrupt(ci, OTGSC_BSVIE);
+}
+
/**
- * udc_remove: parent remove must call this to remove UDC
+ * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC
*
* No interrupts active, the IRQ has been released
*/
-static void udc_stop(struct ci13xxx *ci)
+void ci_hdrc_gadget_destroy(struct ci13xxx *ci)
{
if (ci == NULL)
return;
@@ -1804,15 +1825,13 @@ static void udc_stop(struct ci13xxx *ci)
}
dbg_remove_files(&ci->gadget.dev);
device_unregister(&ci->gadget.dev);
- /* my kobject is dynamic, I swear! */
- memset(&ci->gadget, 0, sizeof(ci->gadget));
}
/**
* ci_hdrc_gadget_init - initialize device related bits
* ci: the controller
*
- * This function enables the gadget role, if the device is "device capable".
+ * This function initializes gadget, if the device is "device capable".
*/
int ci_hdrc_gadget_init(struct ci13xxx *ci)
{
@@ -1825,11 +1844,11 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci)
if (!rdrv)
return -ENOMEM;
- rdrv->start = udc_start;
- rdrv->stop = udc_stop;
+ rdrv->start = udc_id_switch_for_device;
+ rdrv->stop = udc_id_switch_for_host;
rdrv->irq = udc_irq;
rdrv->name = "gadget";
ci->roles[CI_ROLE_GADGET] = rdrv;
- return 0;
+ return udc_start(ci);
}
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index 4ff2384..6c42d49 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -80,6 +80,7 @@ struct ci13xxx_req {
#ifdef CONFIG_USB_CHIPIDEA_UDC
int ci_hdrc_gadget_init(struct ci13xxx *ci);
+void ci_hdrc_gadget_destroy(struct ci13xxx *ci);
#else
@@ -87,7 +88,8 @@ static inline int ci_hdrc_gadget_init(struct ci13xxx *ci)
{
return -ENXIO;
}
-
+static void ci_hdrc_gadget_destroy(struct ci13xxx *ci)
+{}
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 5/8] usb: chipidea: udc: add pullup/pulldown dp at hw_device_state
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
` (3 preceding siblings ...)
2013-02-05 8:00 ` [PATCH v8 4/8] usb: chipidea: consolidate kinds of APIs for both roles Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 6/8] usb: chipidea: udc: retire the flag CI13_PULLUP_ON_VBUS Peter Chen
` (3 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
- During the connect/disconnect host, we need to pullup
and pulldown dp
- Make sure the dp is not pullup until the vbus is on when
flag CI13XXX_PULLUP_ON_VBUS is set
- Using hw_device_state when set run/stop bit
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index e82dae4..597ae64 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -91,8 +91,10 @@ static int hw_device_state(struct ci13xxx *ci, u32 dma)
/* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0,
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
+ hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
} else {
hw_write(ci, OP_USBINTR, ~0, 0);
+ hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
}
return 0;
}
@@ -1424,10 +1426,14 @@ static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_on)
{
struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
+ if ((ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) &&
+ !ci->vbus_active)
+ return -ENOTSUPP;
+
if (is_on)
- hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
+ hw_device_state(ci, ci->ep0out->qh.dma);
else
- hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+ hw_device_state(ci, 0);
return 0;
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 6/8] usb: chipidea: udc: retire the flag CI13_PULLUP_ON_VBUS
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
` (4 preceding siblings ...)
2013-02-05 8:00 ` [PATCH v8 5/8] usb: chipidea: udc: add pullup/pulldown dp at hw_device_state Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 7/8] usb: chipidea: imx: add internal vbus regulator control Peter Chen
` (2 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
(change CI13XXX to CI13 to avoid junk email check)
Now, we have handled vbus session in core driver when the
vbus interrupt occurs, so our pullup operations are all
according to vbus.
Of cource, the software can still call .pullup when device connects
to host if it wants to connect/disconnect with host.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
index 793fdba..3ed119e 100644
--- a/drivers/usb/chipidea/ci13xxx_imx.c
+++ b/drivers/usb/chipidea/ci13xxx_imx.c
@@ -109,7 +109,6 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
pdata->name = "ci13xxx_imx";
pdata->capoffset = DEF_CAPOFFSET;
pdata->flags = CI13XXX_REQUIRE_TRANSCEIVER |
- CI13XXX_PULLUP_ON_VBUS |
CI13XXX_DISABLE_STREAMING |
CI13XXX_REGS_SHARED;
diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c
index 7d16681..5755ee8 100644
--- a/drivers/usb/chipidea/ci13xxx_msm.c
+++ b/drivers/usb/chipidea/ci13xxx_msm.c
@@ -49,7 +49,6 @@ static struct ci13xxx_platform_data ci13xxx_msm_platdata = {
.name = "ci13xxx_msm",
.flags = CI13XXX_REGS_SHARED |
CI13XXX_REQUIRE_TRANSCEIVER |
- CI13XXX_PULLUP_ON_VBUS |
CI13XXX_DISABLE_STREAMING,
.notify_event = ci13xxx_msm_notify_event,
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 597ae64..b57b735 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1360,9 +1360,6 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
unsigned long flags;
int gadget_ready = 0;
- if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS))
- return -EOPNOTSUPP;
-
spin_lock_irqsave(&ci->lock, flags);
ci->vbus_active = is_active;
if (ci->driver)
@@ -1426,8 +1423,7 @@ static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_on)
{
struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
- if ((ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) &&
- !ci->vbus_active)
+ if (!ci->vbus_active)
return -ENOTSUPP;
if (is_on)
@@ -1551,14 +1547,12 @@ static int ci13xxx_start(struct usb_gadget *gadget,
ci->driver = driver;
pm_runtime_get_sync(&ci->gadget.dev);
- if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) {
- if (ci->vbus_active) {
- if (ci->platdata->flags & CI13XXX_REGS_SHARED)
- hw_device_reset(ci, USBMODE_CM_DC);
- } else {
- pm_runtime_put_sync(&ci->gadget.dev);
- goto done;
- }
+ if (ci->vbus_active) {
+ if (ci->platdata->flags & CI13XXX_REGS_SHARED)
+ hw_device_reset(ci, USBMODE_CM_DC);
+ } else {
+ pm_runtime_put_sync(&ci->gadget.dev);
+ goto done;
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
@@ -1581,8 +1575,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
spin_lock_irqsave(&ci->lock, flags);
- if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) ||
- ci->vbus_active) {
+ if (ci->vbus_active) {
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index b314647..d543e4a 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -18,7 +18,6 @@ struct ci13xxx_platform_data {
unsigned long flags;
#define CI13XXX_REGS_SHARED BIT(0)
#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
-#define CI13XXX_PULLUP_ON_VBUS BIT(2)
#define CI13XXX_DISABLE_STREAMING BIT(3)
enum usb_dr_mode dr_mode;
#define CI13XXX_CONTROLLER_RESET_EVENT 0
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 7/8] usb: chipidea: imx: add internal vbus regulator control
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
` (5 preceding siblings ...)
2013-02-05 8:00 ` [PATCH v8 6/8] usb: chipidea: udc: retire the flag CI13_PULLUP_ON_VBUS Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget Peter Chen
2013-02-14 12:15 ` [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Alexander Shishkin
8 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
- For host, the vbus should always be on.
- For otg, the vbus is off defaultly, the vbus needs to be
turned on/off when usb role switches.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 3ebe87a..bd78078 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -131,6 +131,7 @@ struct hw_bank {
* @hcd: pointer to usb_hcd for ehci host driver
* @otg: for otg support
* @events: events for otg, and handled at ci_role_work
+ * @reg_vbus: used to control internal vbus regulator
*/
struct ci13xxx {
struct device *dev;
@@ -170,6 +171,7 @@ struct ci13xxx {
struct usb_otg otg;
bool id_event;
bool b_sess_valid_event;
+ struct regulator *reg_vbus;
};
static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
index 3ed119e..5499cf8 100644
--- a/drivers/usb/chipidea/ci13xxx_imx.c
+++ b/drivers/usb/chipidea/ci13xxx_imx.c
@@ -85,11 +85,43 @@ EXPORT_SYMBOL_GPL(usbmisc_get_init_data);
/* End of common functions shared by usbmisc drivers*/
+static int ci13xxx_otg_set_vbus(struct usb_otg *otg, bool enabled)
+{
+ struct ci13xxx *ci = container_of(otg, struct ci13xxx, otg);
+ struct regulator *reg_vbus = ci->reg_vbus;
+ int ret;
+
+ WARN_ON(!reg_vbus);
+
+ if (reg_vbus) {
+ if (enabled) {
+ ret = regulator_enable(reg_vbus);
+ if (ret) {
+ dev_err(ci->dev,
+ "Failed to enable vbus regulator, ret=%d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ ret = regulator_disable(reg_vbus);
+ if (ret) {
+ dev_err(ci->dev,
+ "Failed to disable vbus regulator, ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int ci13xxx_imx_probe(struct platform_device *pdev)
{
struct ci13xxx_imx_data *data;
struct ci13xxx_platform_data *pdata;
struct platform_device *plat_ci;
+ struct ci13xxx *ci;
struct resource *res;
struct regulator *reg_vbus;
struct pinctrl *pinctrl;
@@ -163,20 +195,11 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
data->phy = phy;
}
- /* we only support host now, so enable vbus here */
reg_vbus = devm_regulator_get(&pdev->dev, "vbus");
- if (!IS_ERR(reg_vbus)) {
- ret = regulator_enable(reg_vbus);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to enable vbus regulator, err=%d\n",
- ret);
- goto err_clk;
- }
+ if (!IS_ERR(reg_vbus))
data->reg_vbus = reg_vbus;
- } else {
+ else
reg_vbus = NULL;
- }
pdata->phy = data->phy;
@@ -186,7 +209,7 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
if (!pdev->dev.dma_mask) {
ret = -ENOMEM;
dev_err(&pdev->dev, "Failed to alloc dma_mask!\n");
- goto err;
+ goto err_clk;
}
*pdev->dev.dma_mask = DMA_BIT_MASK(32);
dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask);
@@ -197,7 +220,7 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"usbmisc init failed, ret=%d\n", ret);
- goto err;
+ goto err_clk;
}
}
@@ -209,20 +232,39 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Can't register ci_hdrc platform device, err=%d\n",
ret);
- goto err;
+ goto err_clk;
}
data->ci_pdev = plat_ci;
platform_set_drvdata(pdev, data);
+ ci = platform_get_drvdata(plat_ci);
+ /*
+ * Internal vbus on/off policy
+ * - Always on for host only function
+ * - Always off for gadget only function
+ * - call otg.set_vbus to control on/off according usb role
+ */
+
+ if (ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]
+ && reg_vbus) {
+ ret = regulator_enable(reg_vbus);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to enable vbus regulator, ret=%d\n",
+ ret);
+ goto err_clk;
+ }
+ } else if (ci->is_otg) {
+ ci->otg.set_vbus = ci13xxx_otg_set_vbus;
+ ci->reg_vbus = data->reg_vbus;
+ }
+
pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
-err:
- if (reg_vbus)
- regulator_disable(reg_vbus);
err_clk:
clk_disable_unprepare(data->clk);
return ret;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
` (6 preceding siblings ...)
2013-02-05 8:00 ` [PATCH v8 7/8] usb: chipidea: imx: add internal vbus regulator control Peter Chen
@ 2013-02-05 8:00 ` Peter Chen
2013-02-13 8:41 ` Felipe Balbi
2013-02-14 12:15 ` [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Alexander Shishkin
8 siblings, 1 reply; 18+ messages in thread
From: Peter Chen @ 2013-02-05 8:00 UTC (permalink / raw)
To: linux-arm-kernel
When we rmmod gadget, the ci->driver needs to be cleared.
Otherwise, we plug in usb cable again, the driver will
consider gadget is there, in fact, it is removed.
Below is the oops this patch fixes.
root at freescale ~$ ci_hdrc ci_hdrc.0: Connected to host
Unable to handle kernel paging request at virtual address 7f01171c
pgd = 80004000
[7f01171c] *pgd=4fa1e811, *pte=00000000, *ppte=00000000
Internal error: Oops: 7 [#1] SMP ARM
Modules linked in: f_acm libcomposite u_serial [last unloaded: g_serial]
CPU: 0 Not tainted (3.8.0-rc5+ #221)
PC is at _gadget_stop_activity+0xbc/0x128
LR is at ep_fifo_flush+0x68/0xa0
pc : [<803634cc>] lr : [<80363938>] psr: 20000193
sp : 806c7dc8 ip : 00000000 fp : 806c7de4
r10: 00000000 r9 : 80710ea4 r8 : 00000000
r7 : bf834094 r6 : bf834098 r5 : bf834010 r4 : bf8340a0
r3 : 7f011708 r2 : 01940194 r1 : a0000193 r0 : bf834014
Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 10c53c7d Table: 4f06404a DAC: 00000017
Process swapper/0 (pid: 0, stack limit = 0x806c6238)
Stack: (0x806c7dc8 to 0x806c8000)
7dc0: bf834010 bf809950 bf834010 0000004b 806c7e44 806c7de8
7de0: 80365400 8036341c 0000ec1c 00000000 0000ec1c 00000040 fff5ede0 bf834014
7e00: 000a1220 00000000 0000002e ffffd958 806c7e3c 806c7e20 8004e870 bf834010
7e20: bf809950 0000004b 0000004b 00000000 80710ea4 00000000 806c7e5c 806c7e48
7e40: 80362888 80365154 bfb35180 bf809950 806c7e9c 806c7e60 8008111c 803627f4
7e60: 00989680 00000000 bf809900 bf809964 812df8c0 bf809900 bf809950 806c3f2c
7e80: 0000004b 00000000 412fc09a 00000000 806c7eb4 806c7ea0 80081368 800810b8
7ea0: bf809900 bf809950 806c7ecc 806c7eb8 800843c8 8008130c 0000004b 806cf748
7ec0: 806c7ee4 806c7ed0 80081088 8008432c 806c6000 806cf748 806c7f0c 806c7ee8
7ee0: 8000fa44 8008105c f400010c 806ce974 806c7f30 f4000110 806d29e8 412fc09a
7f00: 806c7f2c 806c7f10 80008584 8000f9f4 8000fbd0 60000013 ffffffff 806c7f64
7f20: 806c7f84 806c7f30 8000eac0 80008554 00000000 00000000 0000000f 8001aea0
7f40: 806c6000 80712a48 804f0040 00000000 806d29e8 412fc09a 00000000 806c7f84
7f60: 806c7f88 806c7f78 8000fbcc 8000fbd0 60000013 ffffffff 806c7fac 806c7f88
7f80: 8001015c 8000fb9c 806cf5b0 80712980 806a4528 8fffffff 1000406a 412fc09a
7fa0: 806c7fbc 806c7fb0 804e5334 800100ac 806c7ff4 806c7fc0 80668940 804e52d4
7fc0: ffffffff ffffffff 806684bc 00000000 00000000 806a4528 10c53c7d 806ce94c
7fe0: 806a4524 806d29dc 00000000 806c7ff8 10008078 806686b0 00000000 00000000
Backtrace:
[<80363410>] (_gadget_stop_activity+0x0/0x128) from [<80365400>] (udc_irq+0x2b8/0xf38)
r7:0000004b r6:bf834010 r5:bf809950 r4:bf834010
[<80365148>] (udc_irq+0x0/0xf38) from [<80362888>] (ci_irq+0xa0/0xf4)
[<803627e8>] (ci_irq+0x0/0xf4) from [<8008111c>] (handle_irq_event_percpu+0x70/0x254)
r5:bf809950 r4:bfb35180
[<800810ac>] (handle_irq_event_percpu+0x0/0x254) from [<80081368>] (handle_irq_event+0x68/0x88)
[<80081300>] (handle_irq_event+0x0/0x88) from [<800843c8>] (handle_fasteoi_irq+0xa8/0x178)
r5:bf809950 r4:bf809900
[<80084320>] (handle_fasteoi_irq+0x0/0x178) from [<80081088>] (generic_handle_irq+0x38/0x40)
r5:806cf748 r4:0000004b
[<80081050>] (generic_handle_irq+0x0/0x40) from [<8000fa44>] (handle_IRQ+0x5c/0xbc)
r5:806cf748 r4:806c6000
[<8000f9e8>] (handle_IRQ+0x0/0xbc) from [<80008584>] (gic_handle_irq+0x3c/0x70)
r9:412fc09a r8:806d29e8 r7:f4000110 r6:806c7f30 r5:806ce974
r4:f400010c
[<80008548>] (gic_handle_irq+0x0/0x70) from [<8000eac0>] (__irq_svc+0x40/0x54)
Exception stack(0x806c7f30 to 0x806c7f78)
7f20: 00000000 00000000 0000000f 8001aea0
7f40: 806c6000 80712a48 804f0040 00000000 806d29e8 412fc09a 00000000 806c7f84
7f60: 806c7f88 806c7f78 8000fbcc 8000fbd0 60000013 ffffffff
r7:806c7f64 r6:ffffffff r5:60000013 r4:8000fbd0
[<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
[<800100a0>] (cpu_idle+0x0/0xf8) from [<804e5334>] (rest_init+0x6c/0x84)
r9:412fc09a r8:1000406a r7:8fffffff r6:806a4528 r5:80712980
r4:806cf5b0
[<804e52c8>] (rest_init+0x0/0x84) from [<80668940>] (start_kernel+0x29c/0x2ec)
[<806686a4>] (start_kernel+0x0/0x2ec) from [<10008078>] (0x10008078)
Code: e12fff33 e5953200 e3530000 0a000002 (e5933014)
---[ end trace aade28ad434062bd ]---
Kernel panic - not syncing: 0xbf8bdfa8)
df60: 00000000 00000000 0000000f 8001aea0 bf8bc000 80712a48 804f0040 00000000
df80: 806d29e8 412fc09a 00000000 bf8bdfb4 bf8bdfb8 bf8bdfa8 8000fbcc 8000fbd0
dfa0: 60000013 ffffffff
r7:bf8bdf94 r6:ffffffff r5:60000013 r4:8000fbd0
[<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
[<800100a0>] (cpu_idle+0x0/0xf8) from [<804e7930>] (secondary_start_kernel+0x120/0x148)
r9:412fc09a r8:1000406a r7:80712d20 r6:10c03c7d r5:00000002
r4:806e2008
[<804e7810>] (secondary_start_kernel+0x0/0x148) from [<104e7148>] (0x104e7148)
r5:0000001f r4:4f8a806a
CPU3: stopping
Backtrace:
[<800132e8>] (dump_backtrace+0x0/0x114) from [<804ea684>] (dump_stack+0x20/0x24)
r7:00000000 r6:806c3f2c r5:806cf748 r4:80712d18
[<804ea664>] (dump_stack+0x0/0x24) from [<80014adc>] (handle_IPI+0x140/0x18c)
[<8001499c>] (handle_IPI+0x0/0x18c) from [<800085b0>] (gic_handle_irq+0x68/0x70)
r9:412fc09a r8:806d29e8 r7:f4000110 r6:bf8bff60 r5:806ce974
r4:f400010c
[<80008548>] (gic_handle_irq+0x0/0x70) from [<8000eac0>] (__irq_svc+0x40/0x54)
Exception stack(0xbf8bff60 to 0xbf8bffa8)
ff60: 00000000 00000000 0000000f 8001aea0 bf8be000 80712a48 804f0040 00000000
ff80: 806d29e8 412fc09a 00000000 bf8bffb4 bf8bffb8 bf8bffa8 8000fbcc 8000fbd0
ffa0: 60000013 ffffffff
r7:bf8bff94 r6:ffffffff r5:60000013 r4:8000fbd0
[<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
[<800100a0>] (cpu_idle+0x0/0xf8) from [<804e7930>] (secondary_start_kernel+0x120/0x148)
r9:412fc09a r8:1000406a r7:80712d20 r6:10c03c7d r5:00000003
r4:806e2008
[<804e7810>] (secondary_start_kernel+0x0/0x148) from [<104e7148>] (0x104e7148)
r5:0000001f r4:4f8a806a
CPU1: stopping
Backtrace:
[<800132e8>] (dump_backtrace+0x0/0x114) from [<804ea684>] (dump_stack+0x20/0x24)
r7:00000000 r6:806c3f2c r5:806cf748 r4:80712d18
[<804ea664>] (dump_stack+0x0/0x24) from [<80014adc>] (handle_IPI+0x140/0x18c)
[<8001499c>] (handle_IPI+0x0/0x18c) from [<800085b0>] (gic_handle_irq+0x68/0x70)
r9:412fc09a r8:806d29e8 r7:f4000110 r6:bf8bbf60 r5:806ce974
r4:f400010c
[<80008548>] (gic_handle_irq+0x0/0x70) from [<8000eac0>] (__irq_svc+0x40/0x54)
Exception stack(0xbf8bbf60 to 0xbf8bbfa8)
bf60: 00000000 00000000 0000000f 8001aea0 bf8ba000 80712a48 804f0040 00000000
bf80: 806d29e8 412fc09a 00000000 bf8bbfb4 bf8bbfb8 bf8bbfa8 8000fbcc 8000fbd0
bfa0: 60000013 ffffffff
r7:bf8bbf94 r6:ffffffff r5:60000013 r4:8000fbd0
[<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
[<800100a0>] (cpu_idle+0x0/0xf8) from [<804e7930>] (secondary_start_kernel+0x120/0x148)
r9:412fc09a r8:1000406a r7:80712d20 r6:10c03c7d r5:00000001
r4:806e2008
Signed-off-by: Peter Chen <peter.chen@freescale.com>
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index b57b735..8319d7e 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1575,12 +1575,17 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
spin_lock_irqsave(&ci->lock, flags);
+ /*
+ * Put it at the beginning due to avoid calling gadget
+ * disconnect at _gadget_stop_activity
+ */
+ ci->driver = NULL;
+
if (ci->vbus_active) {
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI13XXX_CONTROLLER_STOPPED_EVENT);
- ci->driver = NULL;
spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
--
1.7.0.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-05 8:00 ` [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget Peter Chen
@ 2013-02-13 8:41 ` Felipe Balbi
2013-02-17 3:09 ` Peter Chen
0 siblings, 1 reply; 18+ messages in thread
From: Felipe Balbi @ 2013-02-13 8:41 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Tue, Feb 05, 2013 at 04:00:15PM +0800, Peter Chen wrote:
> When we rmmod gadget, the ci->driver needs to be cleared.
> Otherwise, we plug in usb cable again, the driver will
> consider gadget is there, in fact, it is removed.
> Below is the oops this patch fixes.
>
> root at freescale ~$ ci_hdrc ci_hdrc.0: Connected to host
> Unable to handle kernel paging request at virtual address 7f01171c
> pgd = 80004000
> [7f01171c] *pgd=4fa1e811, *pte=00000000, *ppte=00000000
> Internal error: Oops: 7 [#1] SMP ARM
> Modules linked in: f_acm libcomposite u_serial [last unloaded: g_serial]
> CPU: 0 Not tainted (3.8.0-rc5+ #221)
> PC is at _gadget_stop_activity+0xbc/0x128
> LR is at ep_fifo_flush+0x68/0xa0
> pc : [<803634cc>] lr : [<80363938>] psr: 20000193
> sp : 806c7dc8 ip : 00000000 fp : 806c7de4
> r10: 00000000 r9 : 80710ea4 r8 : 00000000
> r7 : bf834094 r6 : bf834098 r5 : bf834010 r4 : bf8340a0
> r3 : 7f011708 r2 : 01940194 r1 : a0000193 r0 : bf834014
> Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
> Control: 10c53c7d Table: 4f06404a DAC: 00000017
> Process swapper/0 (pid: 0, stack limit = 0x806c6238)
> Stack: (0x806c7dc8 to 0x806c8000)
> 7dc0: bf834010 bf809950 bf834010 0000004b 806c7e44 806c7de8
> 7de0: 80365400 8036341c 0000ec1c 00000000 0000ec1c 00000040 fff5ede0 bf834014
> 7e00: 000a1220 00000000 0000002e ffffd958 806c7e3c 806c7e20 8004e870 bf834010
> 7e20: bf809950 0000004b 0000004b 00000000 80710ea4 00000000 806c7e5c 806c7e48
> 7e40: 80362888 80365154 bfb35180 bf809950 806c7e9c 806c7e60 8008111c 803627f4
> 7e60: 00989680 00000000 bf809900 bf809964 812df8c0 bf809900 bf809950 806c3f2c
> 7e80: 0000004b 00000000 412fc09a 00000000 806c7eb4 806c7ea0 80081368 800810b8
> 7ea0: bf809900 bf809950 806c7ecc 806c7eb8 800843c8 8008130c 0000004b 806cf748
> 7ec0: 806c7ee4 806c7ed0 80081088 8008432c 806c6000 806cf748 806c7f0c 806c7ee8
> 7ee0: 8000fa44 8008105c f400010c 806ce974 806c7f30 f4000110 806d29e8 412fc09a
> 7f00: 806c7f2c 806c7f10 80008584 8000f9f4 8000fbd0 60000013 ffffffff 806c7f64
> 7f20: 806c7f84 806c7f30 8000eac0 80008554 00000000 00000000 0000000f 8001aea0
> 7f40: 806c6000 80712a48 804f0040 00000000 806d29e8 412fc09a 00000000 806c7f84
> 7f60: 806c7f88 806c7f78 8000fbcc 8000fbd0 60000013 ffffffff 806c7fac 806c7f88
> 7f80: 8001015c 8000fb9c 806cf5b0 80712980 806a4528 8fffffff 1000406a 412fc09a
> 7fa0: 806c7fbc 806c7fb0 804e5334 800100ac 806c7ff4 806c7fc0 80668940 804e52d4
> 7fc0: ffffffff ffffffff 806684bc 00000000 00000000 806a4528 10c53c7d 806ce94c
> 7fe0: 806a4524 806d29dc 00000000 806c7ff8 10008078 806686b0 00000000 00000000
> Backtrace:
> [<80363410>] (_gadget_stop_activity+0x0/0x128) from [<80365400>] (udc_irq+0x2b8/0xf38)
> r7:0000004b r6:bf834010 r5:bf809950 r4:bf834010
> [<80365148>] (udc_irq+0x0/0xf38) from [<80362888>] (ci_irq+0xa0/0xf4)
> [<803627e8>] (ci_irq+0x0/0xf4) from [<8008111c>] (handle_irq_event_percpu+0x70/0x254)
> r5:bf809950 r4:bfb35180
> [<800810ac>] (handle_irq_event_percpu+0x0/0x254) from [<80081368>] (handle_irq_event+0x68/0x88)
> [<80081300>] (handle_irq_event+0x0/0x88) from [<800843c8>] (handle_fasteoi_irq+0xa8/0x178)
> r5:bf809950 r4:bf809900
> [<80084320>] (handle_fasteoi_irq+0x0/0x178) from [<80081088>] (generic_handle_irq+0x38/0x40)
> r5:806cf748 r4:0000004b
> [<80081050>] (generic_handle_irq+0x0/0x40) from [<8000fa44>] (handle_IRQ+0x5c/0xbc)
> r5:806cf748 r4:806c6000
> [<8000f9e8>] (handle_IRQ+0x0/0xbc) from [<80008584>] (gic_handle_irq+0x3c/0x70)
> r9:412fc09a r8:806d29e8 r7:f4000110 r6:806c7f30 r5:806ce974
> r4:f400010c
> [<80008548>] (gic_handle_irq+0x0/0x70) from [<8000eac0>] (__irq_svc+0x40/0x54)
> Exception stack(0x806c7f30 to 0x806c7f78)
> 7f20: 00000000 00000000 0000000f 8001aea0
> 7f40: 806c6000 80712a48 804f0040 00000000 806d29e8 412fc09a 00000000 806c7f84
> 7f60: 806c7f88 806c7f78 8000fbcc 8000fbd0 60000013 ffffffff
> r7:806c7f64 r6:ffffffff r5:60000013 r4:8000fbd0
> [<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
> [<800100a0>] (cpu_idle+0x0/0xf8) from [<804e5334>] (rest_init+0x6c/0x84)
> r9:412fc09a r8:1000406a r7:8fffffff r6:806a4528 r5:80712980
> r4:806cf5b0
> [<804e52c8>] (rest_init+0x0/0x84) from [<80668940>] (start_kernel+0x29c/0x2ec)
> [<806686a4>] (start_kernel+0x0/0x2ec) from [<10008078>] (0x10008078)
> Code: e12fff33 e5953200 e3530000 0a000002 (e5933014)
> ---[ end trace aade28ad434062bd ]---
> Kernel panic - not syncing: 0xbf8bdfa8)
> df60: 00000000 00000000 0000000f 8001aea0 bf8bc000 80712a48 804f0040 00000000
> df80: 806d29e8 412fc09a 00000000 bf8bdfb4 bf8bdfb8 bf8bdfa8 8000fbcc 8000fbd0
> dfa0: 60000013 ffffffff
> r7:bf8bdf94 r6:ffffffff r5:60000013 r4:8000fbd0
> [<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
> [<800100a0>] (cpu_idle+0x0/0xf8) from [<804e7930>] (secondary_start_kernel+0x120/0x148)
> r9:412fc09a r8:1000406a r7:80712d20 r6:10c03c7d r5:00000002
> r4:806e2008
> [<804e7810>] (secondary_start_kernel+0x0/0x148) from [<104e7148>] (0x104e7148)
> r5:0000001f r4:4f8a806a
> CPU3: stopping
> Backtrace:
> [<800132e8>] (dump_backtrace+0x0/0x114) from [<804ea684>] (dump_stack+0x20/0x24)
> r7:00000000 r6:806c3f2c r5:806cf748 r4:80712d18
> [<804ea664>] (dump_stack+0x0/0x24) from [<80014adc>] (handle_IPI+0x140/0x18c)
> [<8001499c>] (handle_IPI+0x0/0x18c) from [<800085b0>] (gic_handle_irq+0x68/0x70)
> r9:412fc09a r8:806d29e8 r7:f4000110 r6:bf8bff60 r5:806ce974
> r4:f400010c
> [<80008548>] (gic_handle_irq+0x0/0x70) from [<8000eac0>] (__irq_svc+0x40/0x54)
> Exception stack(0xbf8bff60 to 0xbf8bffa8)
> ff60: 00000000 00000000 0000000f 8001aea0 bf8be000 80712a48 804f0040 00000000
> ff80: 806d29e8 412fc09a 00000000 bf8bffb4 bf8bffb8 bf8bffa8 8000fbcc 8000fbd0
> ffa0: 60000013 ffffffff
> r7:bf8bff94 r6:ffffffff r5:60000013 r4:8000fbd0
> [<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
> [<800100a0>] (cpu_idle+0x0/0xf8) from [<804e7930>] (secondary_start_kernel+0x120/0x148)
> r9:412fc09a r8:1000406a r7:80712d20 r6:10c03c7d r5:00000003
> r4:806e2008
> [<804e7810>] (secondary_start_kernel+0x0/0x148) from [<104e7148>] (0x104e7148)
> r5:0000001f r4:4f8a806a
> CPU1: stopping
> Backtrace:
> [<800132e8>] (dump_backtrace+0x0/0x114) from [<804ea684>] (dump_stack+0x20/0x24)
> r7:00000000 r6:806c3f2c r5:806cf748 r4:80712d18
> [<804ea664>] (dump_stack+0x0/0x24) from [<80014adc>] (handle_IPI+0x140/0x18c)
> [<8001499c>] (handle_IPI+0x0/0x18c) from [<800085b0>] (gic_handle_irq+0x68/0x70)
> r9:412fc09a r8:806d29e8 r7:f4000110 r6:bf8bbf60 r5:806ce974
> r4:f400010c
> [<80008548>] (gic_handle_irq+0x0/0x70) from [<8000eac0>] (__irq_svc+0x40/0x54)
> Exception stack(0xbf8bbf60 to 0xbf8bbfa8)
> bf60: 00000000 00000000 0000000f 8001aea0 bf8ba000 80712a48 804f0040 00000000
> bf80: 806d29e8 412fc09a 00000000 bf8bbfb4 bf8bbfb8 bf8bbfa8 8000fbcc 8000fbd0
> bfa0: 60000013 ffffffff
> r7:bf8bbf94 r6:ffffffff r5:60000013 r4:8000fbd0
> [<8000fb90>] (default_idle+0x0/0x4c) from [<8001015c>] (cpu_idle+0xbc/0xf8)
> [<800100a0>] (cpu_idle+0x0/0xf8) from [<804e7930>] (secondary_start_kernel+0x120/0x148)
> r9:412fc09a r8:1000406a r7:80712d20 r6:10c03c7d r5:00000001
> r4:806e2008
>
> Signed-off-by: Peter Chen <peter.chen@freescale.com>
>
> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> index b57b735..8319d7e 100644
> --- a/drivers/usb/chipidea/udc.c
> +++ b/drivers/usb/chipidea/udc.c
> @@ -1575,12 +1575,17 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
>
> spin_lock_irqsave(&ci->lock, flags);
>
> + /*
> + * Put it at the beginning due to avoid calling gadget
> + * disconnect at _gadget_stop_activity
> + */
> + ci->driver = NULL;
> +
> if (ci->vbus_active) {
> hw_device_state(ci, 0);
> if (ci->platdata->notify_event)
> ci->platdata->notify_event(ci,
> CI13XXX_CONTROLLER_STOPPED_EVENT);
> - ci->driver = NULL;
> spin_unlock_irqrestore(&ci->lock, flags);
> _gadget_stop_activity(&ci->gadget);
> spin_lock_irqsave(&ci->lock, flags);
NACK, this isn't the real problem. The real problem is that chipidea
shouldn't be calling ->disconnect() by itself when we unload a gadget
driver.
See if this helps:
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 1b65ac8..4d6274f 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -598,9 +598,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
usb_ep_fifo_flush(&ci->ep0out->ep);
usb_ep_fifo_flush(&ci->ep0in->ep);
- if (ci->driver)
- ci->driver->disconnect(gadget);
-
/* make sure to disable all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_disable(ep);
@@ -631,6 +628,11 @@ __acquires(ci->lock)
dbg_event(0xFF, "BUS RST", 0);
+ if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (ci->driver)
+ ci->driver->disconnect(gadget);
+ }
+
spin_unlock(&ci->lock);
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
--
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130213/fdf7fedc/attachment.sig>
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
` (7 preceding siblings ...)
2013-02-05 8:00 ` [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget Peter Chen
@ 2013-02-14 12:15 ` Alexander Shishkin
8 siblings, 0 replies; 18+ messages in thread
From: Alexander Shishkin @ 2013-02-14 12:15 UTC (permalink / raw)
To: linux-arm-kernel
Peter Chen <peter.chen@freescale.com> writes:
> Changes for v7:
> - Add ci_supports_gadget helper to know if the controller
> supports gadget, if the controller supports gadget, it
> needs to read otgsc to know the vbus value, basically,
> if the controller supports gadget, it will support host
> as well [3/8]
> - At ci_hdrc_probe, it needs to add free memory at error path
> [3/8]
> - Cosolidate ci->driver = NULL at ci13xxx_stop
> [8/8]
Michael, Marc, could you give your Tested-by's and/or Reviewed-by's for
this one?
Thanks,
--
Alex
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect
2013-02-05 8:00 ` [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect Peter Chen
@ 2013-02-14 12:37 ` Alexander Shishkin
2013-02-17 3:27 ` Peter Chen
0 siblings, 1 reply; 18+ messages in thread
From: Alexander Shishkin @ 2013-02-14 12:37 UTC (permalink / raw)
To: linux-arm-kernel
Peter Chen <peter.chen@freescale.com> writes:
> The main design flow is the same with msm otg driver, that is the id and
> vbus interrupt are handled at core driver, others are handled by
> individual drivers.
>
> - At former design, when switch usb role from device->host, it will call
> udc_stop, it will remove the gadget driver, so when switch role
> from host->device, it can't add gadget driver any more.
> At new design, when role switch occurs, the gadget just calls
> usb_gadget_vbus_disconnect/usb_gadget_vbus_connect as well as
> reset controller, it will not free any device/gadget structure
>
> - Add vbus connect and disconnect to core interrupt handler, it can
> notify udc driver by calling usb_gadget_vbus_disconnect
> /usb_gadget_vbus_connect.
>
> - vbus interrupt needs to be handled when gadget function is enabled
Please find some comments below.
>
> Signed-off-by: Peter Chen <peter.chen@freescale.com>
>
> diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
> index d8ffc2f..58ef56c 100644
> --- a/drivers/usb/chipidea/bits.h
> +++ b/drivers/usb/chipidea/bits.h
> @@ -77,11 +77,21 @@
> #define OTGSC_ASVIS BIT(18)
> #define OTGSC_BSVIS BIT(19)
> #define OTGSC_BSEIS BIT(20)
> +#define OTGSC_1MSIS BIT(21)
> +#define OTGSC_DPIS BIT(22)
> #define OTGSC_IDIE BIT(24)
> #define OTGSC_AVVIE BIT(25)
> #define OTGSC_ASVIE BIT(26)
> #define OTGSC_BSVIE BIT(27)
> #define OTGSC_BSEIE BIT(28)
> +#define OTGSC_1MSIE BIT(29)
> +#define OTGSC_DPIE BIT(30)
> +#define OTGSC_INT_EN_BITS (OTGSC_IDIE | OTGSC_AVVIE | OTGSC_ASVIE \
> + | OTGSC_BSVIE | OTGSC_BSEIE | OTGSC_1MSIE \
> + | OTGSC_DPIE)
> +#define OTGSC_INT_STATUS_BITS (OTGSC_IDIS | OTGSC_AVVIS | OTGSC_ASVIS \
> + | OTGSC_BSVIS | OTGSC_BSEIS | OTGSC_1MSIS \
> + | OTGSC_DPIS)
>
> /* USBMODE */
> #define USBMODE_CM (0x03UL << 0)
> diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
> index 697e369..325d790 100644
> --- a/drivers/usb/chipidea/ci.h
> +++ b/drivers/usb/chipidea/ci.h
> @@ -130,6 +130,7 @@ struct hw_bank {
> * @transceiver: pointer to USB PHY, if any
> * @hcd: pointer to usb_hcd for ehci host driver
> * @otg: for otg support
> + * @events: events for otg, and handled at ci_role_work
Should be id_event and b_sess_valid_event.
> */
> struct ci13xxx {
> struct device *dev;
> @@ -140,6 +141,7 @@ struct ci13xxx {
> enum ci_role role;
> bool is_otg;
> struct work_struct work;
> + struct delayed_work dwork;
> struct workqueue_struct *wq;
>
> struct dma_pool *qh_pool;
> @@ -166,6 +168,8 @@ struct ci13xxx {
> struct usb_phy *transceiver;
> struct usb_hcd *hcd;
> struct usb_otg otg;
> + bool id_event;
> + bool b_sess_valid_event;
> };
>
> static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
> @@ -314,4 +318,6 @@ int hw_port_test_set(struct ci13xxx *ci, u8 mode);
>
> u8 hw_port_test_get(struct ci13xxx *ci);
>
> +void ci_handle_vbus_change(struct ci13xxx *ci);
> +
> #endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
> diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
> index 136869b..793fdba 100644
> --- a/drivers/usb/chipidea/ci13xxx_imx.c
> +++ b/drivers/usb/chipidea/ci13xxx_imx.c
> @@ -110,7 +110,8 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
> pdata->capoffset = DEF_CAPOFFSET;
> pdata->flags = CI13XXX_REQUIRE_TRANSCEIVER |
> CI13XXX_PULLUP_ON_VBUS |
> - CI13XXX_DISABLE_STREAMING;
> + CI13XXX_DISABLE_STREAMING |
> + CI13XXX_REGS_SHARED;
>
> pdata->phy_mode = of_usb_get_phy_mode(pdev->dev.of_node);
> pdata->dr_mode = of_usb_get_dr_mode(pdev->dev.of_node);
> diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> index c89f2aa..fbb6984 100644
> --- a/drivers/usb/chipidea/core.c
> +++ b/drivers/usb/chipidea/core.c
> @@ -75,6 +75,7 @@
> #include "bits.h"
> #include "host.h"
> #include "debug.h"
> +#include "otg.h"
>
> /* Controller register map */
> static uintptr_t ci_regs_nolpm[] = {
> @@ -201,6 +202,14 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
> if (ci->hw_ep_max > ENDPT_MAX)
> return -ENODEV;
>
> + /* Disable all interrupts bits */
> + hw_write(ci, OP_USBINTR, 0xffffffff, 0);
> + ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS);
> +
> + /* Clear all interrupts status bits*/
> + hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
> + ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS);
> +
> dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n",
> ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
>
> @@ -302,24 +311,132 @@ static enum ci_role ci_otg_role(struct ci13xxx *ci)
> }
>
> /**
> - * ci_role_work - perform role changing based on ID pin
> - * @work: work struct
> + * hw_wait_reg: wait the register value
> + *
> + * Sometimes, it needs to wait register value before going on.
> + * Eg, when switch to device mode, the vbus value should be lower
> + * than OTGSC_BSV before connects to host.
> + *
> + * @ci: the controller
> + * @reg: register index
> + * @mask: mast bit
> + * @value: the bit value to wait
> + * @timeout: timeout to indicate an error
> + *
> + * This function returns an error code if timeout
> */
> -static void ci_role_work(struct work_struct *work)
> +static int hw_wait_reg(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask,
> + u32 value, unsigned long timeout)
> +{
> + unsigned long elapse = jiffies + timeout;
> +
> + while (hw_read(ci, reg, mask) != value) {
> + if (time_after(jiffies, elapse)) {
> + dev_err(ci->dev, "timeout waiting for %08x in %d\n",
> + mask, reg);
> + return -ETIMEDOUT;
> + }
> + msleep(20);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Since there are some capacitances at vbus, the vbus may not
> + * change very quickly when we stop/start internal 5v.
> + * Below is a vbus stable timeout value, the routine will quit
> + * if the vbus gets the required value, we can think there are some
> + * problems for hardware if the vbus can't get the required value
> + * within 5 seconds.
> + */
> +#define CI_VBUS_STABLE_TIMEOUT 500
> +static void ci_handle_id_switch(struct ci13xxx *ci)
> {
> - struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
> enum ci_role role = ci_otg_role(ci);
>
> if (role != ci->role) {
> dev_dbg(ci->dev, "switching from %s to %s\n",
> ci_role(ci)->name, ci->roles[role]->name);
>
> - ci_role_stop(ci);
> - ci_role_start(ci, role);
> - enable_irq(ci->irq);
> + /* 1. Finish the current role */
> + if (ci->role == CI_ROLE_GADGET) {
> + usb_gadget_vbus_disconnect(&ci->gadget);
> + /* host doesn't care B_SESSION_VALID event */
> + ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
> + ci_disable_otg_interrupt(ci, OTGSC_BSVIE);
> + ci->role = CI_ROLE_END;
> + /* reset controller */
> + hw_device_reset(ci, USBMODE_CM_IDLE);
> + } else if (ci->role == CI_ROLE_HOST) {
> + ci_role_stop(ci);
> + /* reset controller */
> + hw_device_reset(ci, USBMODE_CM_IDLE);
> + }
> +
> + /* 2. Turn on/off vbus according to coming role */
> + if (ci_otg_role(ci) == CI_ROLE_GADGET) {
> + otg_set_vbus(&ci->otg, false);
> + /* wait vbus lower than OTGSC_BSV */
> + hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
> + CI_VBUS_STABLE_TIMEOUT);
> + } else if (ci_otg_role(ci) == CI_ROLE_HOST) {
> + otg_set_vbus(&ci->otg, true);
> + /* wait vbus higher than OTGSC_AVV */
> + hw_wait_reg(ci, OP_OTGSC, OTGSC_AVV, OTGSC_AVV,
> + CI_VBUS_STABLE_TIMEOUT);
> + }
> +
> + /* 3. Begin the new role */
> + if (ci_otg_role(ci) == CI_ROLE_GADGET) {
> + ci->role = role;
> + ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
> + ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
> + } else if (ci_otg_role(ci) == CI_ROLE_HOST) {
> + ci_role_start(ci, role);
> + }
> }
> }
If you reordered the patchset so that the role api patch comes before
this one, this function would have been much shorter from the start. Now
it looks like you're adding lots of code in one patch and then replace
and reshuffle it in subsequent patches, which makes it really difficult
to review.
>
> +void ci_handle_vbus_change(struct ci13xxx *ci)
> +{
> + u32 otgsc = hw_read(ci, OP_OTGSC, ~0);
> +
> + if (otgsc & OTGSC_BSV)
> + usb_gadget_vbus_connect(&ci->gadget);
> + else
> + usb_gadget_vbus_disconnect(&ci->gadget);
> +}
> +
> +/**
> + * ci_otg_work - perform otg (vbus/id) event handle
> + * @work: work struct
> + */
> +static void ci_otg_work(struct work_struct *work)
> +{
> + struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
> +
> + if (ci->id_event) {
> + ci->id_event = false;
> + ci_handle_id_switch(ci);
> + } else if (ci->b_sess_valid_event) {
> + ci->b_sess_valid_event = false;
> + ci_handle_vbus_change(ci);
> + } else
> + dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
> +
> + enable_irq(ci->irq);
> +}
> +
> +static void ci_delayed_work(struct work_struct *work)
> +{
> + struct delayed_work *dwork = to_delayed_work(work);
> + struct ci13xxx *ci = container_of(dwork, struct ci13xxx, dwork);
> +
> + otg_set_vbus(&ci->otg, true);
> +
> +}
That's one newline too many.
> +
> static ssize_t show_role(struct device *dev, struct device_attribute *attr,
> char *buf)
> {
> @@ -352,25 +469,49 @@ static ssize_t store_role(struct device *dev, struct device_attribute *attr,
>
> static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role);
>
> +static bool ci_supports_gadget(struct ci13xxx *ci)
> +{
> + return (ci->roles[CI_ROLE_GADGET]) ? true : false;
return !!ci->roles[CI_ROLE_GADGET];
should do the job, but some people question its readability.
> +}
> +
> static irqreturn_t ci_irq(int irq, void *data)
> {
> struct ci13xxx *ci = data;
> irqreturn_t ret = IRQ_NONE;
> u32 otgsc = 0;
>
> - if (ci->is_otg)
> + if (ci_supports_gadget(ci))
We can't do this since there are indeed devices out there that support
gadget and don't support otg to such an extend that OTGSC accesses are
discouraged. We should really make sure that we're only touching it on
otg capable devices.
> otgsc = hw_read(ci, OP_OTGSC, ~0);
>
> - if (ci->role != CI_ROLE_END)
> - ret = ci_role(ci)->irq(ci);
> + /*
> + * Handle id change interrupt, it indicates device/host function
> + * switch.
> + */
> + if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
> + ci->id_event = true;
> + ci_clear_otg_interrupt(ci, OTGSC_IDIS);
> + disable_irq_nosync(ci->irq);
> + queue_work(ci->wq, &ci->work);
> + return IRQ_HANDLED;
> + }
>
> - if (ci->is_otg && (otgsc & OTGSC_IDIS)) {
> - hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
> + /*
> + * Handle vbus change interrupt, it indicates device connection
> + * and disconnection events.
> + */
> + if (ci_supports_gadget(ci) && (otgsc & OTGSC_BSVIE)
> + && (otgsc & OTGSC_BSVIS)) {
> + ci->b_sess_valid_event = true;
> + ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
> disable_irq_nosync(ci->irq);
> queue_work(ci->wq, &ci->work);
> - ret = IRQ_HANDLED;
> + return IRQ_HANDLED;
> }
>
> + /* Handle device/host interrupt */
> + if (ci->role != CI_ROLE_END)
> + ret = ci_role(ci)->irq(ci);
> +
> return ret;
> }
>
> @@ -436,6 +577,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> void __iomem *base;
> int ret;
> enum usb_dr_mode dr_mode;
> + u32 otgsc;
>
> if (!dev->platform_data) {
> dev_err(dev, "platform data missing\n");
> @@ -481,7 +623,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> return -ENODEV;
> }
>
> - INIT_WORK(&ci->work, ci_role_work);
> + INIT_WORK(&ci->work, ci_otg_work);
> + INIT_DELAYED_WORK(&ci->dwork, ci_delayed_work);
> ci->wq = create_singlethread_workqueue("ci_otg");
> if (!ci->wq) {
> dev_err(dev, "can't create workqueue\n");
> @@ -512,7 +655,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> }
>
> if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
> + dev_dbg(dev, "support otg\n");
> ci->is_otg = true;
> + /* if otg is supported, create struct usb_otg */
> + ci_hdrc_otg_init(ci);
> /* ID pin needs 1ms debouce time, we delay 2ms for safe */
> mdelay(2);
> ci->role = ci_otg_role(ci);
> @@ -531,6 +677,17 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> goto rm_wq;
> }
>
> + otgsc = hw_read(ci, OP_OTGSC, ~0);
> + /*
> + * if it is device mode:
> + * - Enable vbus detect
> + * - If it has already connected to host, notify udc
> + */
> + if (ci->role == CI_ROLE_GADGET) {
> + ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
> + ci_handle_vbus_change(ci);
> + }
Same here: this code is moved away in one of the subsequent patches, and
I stumble on it every time I read this part.
> +
> platform_set_drvdata(pdev, ci);
> ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
> ci);
> @@ -541,8 +698,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> if (ret)
> goto rm_attr;
>
> - if (ci->is_otg)
> - hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE);
> + /* Handle the situation that usb device at the MicroB to A cable */
> + if (ci->is_otg && !(otgsc & OTGSC_ID))
> + queue_delayed_work(ci->wq, &ci->dwork, msecs_to_jiffies(500));
>
> return ret;
>
> diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
> index 7dea3b3..2986d91 100644
> --- a/drivers/usb/chipidea/otg.c
> +++ b/drivers/usb/chipidea/otg.c
> @@ -10,21 +10,28 @@
> * published by the Free Software Foundation.
> */
>
> -#include <linux/platform_device.h>
> -#include <linux/module.h>
> -#include <linux/io.h>
> -#include <linux/irq.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/usb/gadget.h>
> #include <linux/usb/otg.h>
> +#include <linux/usb/gadget.h>
> #include <linux/usb/chipidea.h>
>
> #include "ci.h"
> -#include "udc.h"
> #include "bits.h"
> -#include "host.h"
> -#include "debug.h"
> +
> +void ci_clear_otg_interrupt(struct ci13xxx *ci, u32 bits)
> +{
> + /* Only clear request bits */
> + hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS, bits);
> +}
> +
> +void ci_enable_otg_interrupt(struct ci13xxx *ci, u32 bits)
> +{
> + hw_write(ci, OP_OTGSC, bits, bits);
> +}
> +
> +void ci_disable_otg_interrupt(struct ci13xxx *ci, u32 bits)
> +{
> + hw_write(ci, OP_OTGSC, bits, 0);
> +}
>
> static int ci_otg_set_peripheral(struct usb_otg *otg,
> struct usb_gadget *periph)
> @@ -55,6 +62,7 @@ int ci_hdrc_otg_init(struct ci13xxx *ci)
> ci->otg.set_host = ci_otg_set_host;
> if (!IS_ERR_OR_NULL(ci->transceiver))
> ci->transceiver->otg = &ci->otg;
> + ci_enable_otg_interrupt(ci, OTGSC_IDIE);
>
> return 0;
> }
> diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
> index b4c6b3e..fa30428 100644
> --- a/drivers/usb/chipidea/otg.h
> +++ b/drivers/usb/chipidea/otg.h
> @@ -2,5 +2,8 @@
> #define __DRIVERS_USB_CHIPIDEA_OTG_H
>
> int ci_hdrc_otg_init(struct ci13xxx *ci);
> +void ci_clear_otg_interrupt(struct ci13xxx *ci, u32 bits);
> +void ci_enable_otg_interrupt(struct ci13xxx *ci, u32 bits);
> +void ci_disable_otg_interrupt(struct ci13xxx *ci, u32 bits);
>
> #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> index d214448..83e54bb 100644
> --- a/drivers/usb/chipidea/udc.c
> +++ b/drivers/usb/chipidea/udc.c
> @@ -1371,6 +1371,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
> pm_runtime_get_sync(&_gadget->dev);
> hw_device_reset(ci, USBMODE_CM_DC);
> hw_device_state(ci, ci->ep0out->qh.dma);
> + dev_dbg(ci->dev, "Connected to host\n");
> } else {
> hw_device_state(ci, 0);
> if (ci->platdata->notify_event)
> @@ -1378,6 +1379,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
> CI13XXX_CONTROLLER_STOPPED_EVENT);
> _gadget_stop_activity(&ci->gadget);
> pm_runtime_put_sync(&_gadget->dev);
> + dev_dbg(ci->dev, "Disconnected from host\n");
> }
> }
>
> --
> 1.7.0.4
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-13 8:41 ` Felipe Balbi
@ 2013-02-17 3:09 ` Peter Chen
2013-02-26 18:21 ` Felipe Balbi
0 siblings, 1 reply; 18+ messages in thread
From: Peter Chen @ 2013-02-17 3:09 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Feb 13, 2013 at 10:41:28AM +0200, Felipe Balbi wrote:
> Hi,
>
> >
> > Signed-off-by: Peter Chen <peter.chen@freescale.com>
> >
> > diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> > index b57b735..8319d7e 100644
> > --- a/drivers/usb/chipidea/udc.c
> > +++ b/drivers/usb/chipidea/udc.c
> > @@ -1575,12 +1575,17 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
> >
> > spin_lock_irqsave(&ci->lock, flags);
> >
> > + /*
> > + * Put it at the beginning due to avoid calling gadget
> > + * disconnect at _gadget_stop_activity
> > + */
> > + ci->driver = NULL;
> > +
> > if (ci->vbus_active) {
> > hw_device_state(ci, 0);
> > if (ci->platdata->notify_event)
> > ci->platdata->notify_event(ci,
> > CI13XXX_CONTROLLER_STOPPED_EVENT);
> > - ci->driver = NULL;
> > spin_unlock_irqrestore(&ci->lock, flags);
> > _gadget_stop_activity(&ci->gadget);
> > spin_lock_irqsave(&ci->lock, flags);
>
> NACK, this isn't the real problem. The real problem is that chipidea
> shouldn't be calling ->disconnect() by itself when we unload a gadget
> driver.
Yes, you point the root cause, besides, we also need to notify gadget
when the disconnect occurs even the gadget is still there. Below patch
is tested:
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index b57b735..e355914 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -579,9 +579,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
usb_ep_fifo_flush(&ci->ep0out->ep);
usb_ep_fifo_flush(&ci->ep0in->ep);
- if (ci->driver)
- ci->driver->disconnect(gadget);
-
/* make sure to disable all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_disable(ep);
@@ -612,6 +609,11 @@ __acquires(ci->lock)
dbg_event(0xFF, "BUS RST", 0);
+ if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (ci->driver)
+ ci->driver->disconnect(&ci->gadget);
+ }
+
spin_unlock(&ci->lock);
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
@@ -1373,6 +1375,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
hw_device_state(ci, ci->ep0out->qh.dma);
dev_dbg(ci->dev, "Connected to host\n");
} else {
+ if (ci->driver)
+ ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
@@ -1580,13 +1584,14 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI13XXX_CONTROLLER_STOPPED_EVENT);
- ci->driver = NULL;
spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
pm_runtime_put(&ci->gadget.dev);
}
+ ci->driver = NULL;
+
spin_unlock_irqrestore(&ci->lock, flags);
return 0;
--
Best Regards,
Peter Chen
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect
2013-02-14 12:37 ` Alexander Shishkin
@ 2013-02-17 3:27 ` Peter Chen
0 siblings, 0 replies; 18+ messages in thread
From: Peter Chen @ 2013-02-17 3:27 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Feb 14, 2013 at 02:37:42PM +0200, Alexander Shishkin wrote:
> Peter Chen <peter.chen@freescale.com> writes:
>
> > @@ -130,6 +130,7 @@ struct hw_bank {
> > * @transceiver: pointer to USB PHY, if any
> > * @hcd: pointer to usb_hcd for ehci host driver
> > * @otg: for otg support
> > + * @events: events for otg, and handled at ci_role_work
>
> Should be id_event and b_sess_valid_event.
Will change
>
> If you reordered the patchset so that the role api patch comes before
> this one, this function would have been much shorter from the start. Now
> it looks like you're adding lots of code in one patch and then replace
> and reshuffle it in subsequent patches, which makes it really difficult
> to review.
I will merge the 3/8 and 4/8 to one patch.
> > +static void ci_delayed_work(struct work_struct *work)
> > +{
> > + struct delayed_work *dwork = to_delayed_work(work);
> > + struct ci13xxx *ci = container_of(dwork, struct ci13xxx, dwork);
> > +
> > + otg_set_vbus(&ci->otg, true);
> > +
> > +}
>
> That's one newline too many.
Will change
>
> > +
> > static ssize_t show_role(struct device *dev, struct device_attribute *attr,
> > char *buf)
> > {
> > @@ -352,25 +469,49 @@ static ssize_t store_role(struct device *dev, struct device_attribute *attr,
> >
> > static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role);
> >
> > +static bool ci_supports_gadget(struct ci13xxx *ci)
> > +{
> > + return (ci->roles[CI_ROLE_GADGET]) ? true : false;
>
I prefer to keep it
> > +}
> > +
> > static irqreturn_t ci_irq(int irq, void *data)
> > {
> > struct ci13xxx *ci = data;
> > irqreturn_t ret = IRQ_NONE;
> > u32 otgsc = 0;
> >
> > - if (ci->is_otg)
> > + if (ci_supports_gadget(ci))
>
> We can't do this since there are indeed devices out there that support
> gadget and don't support otg to such an extend that OTGSC accesses are
> discouraged. We should really make sure that we're only touching it on
> otg capable devices.
>
OK, for such devices they need external gpio to judge connection
and disconnection.
I will replace ci_supports_gadget(ci) to ci_supports_otg(ci), it
indicates the controller supports both host and devices. Currently,
the ci->is_otg indicates the driver has both host and device funcitons.
--
Best Regards,
Peter Chen
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-17 3:09 ` Peter Chen
@ 2013-02-26 18:21 ` Felipe Balbi
2013-02-27 3:10 ` Peter Chen
0 siblings, 1 reply; 18+ messages in thread
From: Felipe Balbi @ 2013-02-26 18:21 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
On Sun, Feb 17, 2013 at 11:09:53AM +0800, Peter Chen wrote:
> @@ -1373,6 +1375,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
> hw_device_state(ci, ci->ep0out->qh.dma);
> dev_dbg(ci->dev, "Connected to host\n");
> } else {
> + if (ci->driver)
> + ci->driver->disconnect(&ci->gadget);
This looks wrong. Why do you need to call ->disconnect() here ?
--
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130226/e9160490/attachment.sig>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-26 18:21 ` Felipe Balbi
@ 2013-02-27 3:10 ` Peter Chen
2013-02-27 8:42 ` Felipe Balbi
0 siblings, 1 reply; 18+ messages in thread
From: Peter Chen @ 2013-02-27 3:10 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Feb 26, 2013 at 08:21:41PM +0200, Felipe Balbi wrote:
> Hi,
>
> On Sun, Feb 17, 2013 at 11:09:53AM +0800, Peter Chen wrote:
> > @@ -1373,6 +1375,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
> > hw_device_state(ci, ci->ep0out->qh.dma);
> > dev_dbg(ci->dev, "Connected to host\n");
> > } else {
> > + if (ci->driver)
> > + ci->driver->disconnect(&ci->gadget);
>
> This looks wrong. Why do you need to call ->disconnect() here ?
When we disconnect usb cable from the host, we need to notify gadget module
disconnection occurs, the gadget module may not be unloaded in future.
If the disconnection is not notified, the gadget module may still call
struct usb_ep_ops's API to visit hardware at its thread but the controller
may already enter low power mode.
In fact, a common problem is how can we make sure the gadget will not
visit register if the controller enters low power mode, currently,
there is no prefect solution.
>
> --
> balbi
--
Best Regards,
Peter Chen
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-27 3:10 ` Peter Chen
@ 2013-02-27 8:42 ` Felipe Balbi
2013-02-27 8:57 ` Chen Peter-B29397
0 siblings, 1 reply; 18+ messages in thread
From: Felipe Balbi @ 2013-02-27 8:42 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Feb 27, 2013 at 11:10:03AM +0800, Peter Chen wrote:
> On Tue, Feb 26, 2013 at 08:21:41PM +0200, Felipe Balbi wrote:
> > Hi,
> >
> > On Sun, Feb 17, 2013 at 11:09:53AM +0800, Peter Chen wrote:
> > > @@ -1373,6 +1375,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
> > > hw_device_state(ci, ci->ep0out->qh.dma);
> > > dev_dbg(ci->dev, "Connected to host\n");
> > > } else {
> > > + if (ci->driver)
> > > + ci->driver->disconnect(&ci->gadget);
> >
> > This looks wrong. Why do you need to call ->disconnect() here ?
>
> When we disconnect usb cable from the host, we need to notify gadget module
> disconnection occurs, the gadget module may not be unloaded in future.
ok, I get it now, after reading the code I see that this gets called
from your VBUS IRQ handler (actually you queue an unnecessary workqueue
for that).
> If the disconnection is not notified, the gadget module may still call
> struct usb_ep_ops's API to visit hardware at its thread but the controller
> may already enter low power mode.
>
> In fact, a common problem is how can we make sure the gadget will not
> visit register if the controller enters low power mode, currently,
> there is no prefect solution.
yeah, I have an idea for that, but won't happen for v3.10 I'm afraid...
--
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130227/085bf7dd/attachment.sig>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget
2013-02-27 8:42 ` Felipe Balbi
@ 2013-02-27 8:57 ` Chen Peter-B29397
0 siblings, 0 replies; 18+ messages in thread
From: Chen Peter-B29397 @ 2013-02-27 8:57 UTC (permalink / raw)
To: linux-arm-kernel
> > > > } else {
> > > > + if (ci->driver)
> > > > + ci->driver->disconnect(&ci->gadget);
> > >
> > > This looks wrong. Why do you need to call ->disconnect() here ?
> >
> > When we disconnect usb cable from the host, we need to notify gadget
> module
> > disconnection occurs, the gadget module may not be unloaded in future.
>
Sorry, can you point that?
Peter,
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2013-02-27 8:57 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-05 8:00 [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Peter Chen
2013-02-05 8:00 ` [PATCH v8 1/8] Revert "USB: chipidea: add vbus detect for udc" Peter Chen
2013-02-05 8:00 ` [PATCH v8 2/8] usb: chipidea: add otg file Peter Chen
2013-02-05 8:00 ` [PATCH v8 3/8] usb: chipidea: add otg id switch and vbus connect/disconnect detect Peter Chen
2013-02-14 12:37 ` Alexander Shishkin
2013-02-17 3:27 ` Peter Chen
2013-02-05 8:00 ` [PATCH v8 4/8] usb: chipidea: consolidate kinds of APIs for both roles Peter Chen
2013-02-05 8:00 ` [PATCH v8 5/8] usb: chipidea: udc: add pullup/pulldown dp at hw_device_state Peter Chen
2013-02-05 8:00 ` [PATCH v8 6/8] usb: chipidea: udc: retire the flag CI13_PULLUP_ON_VBUS Peter Chen
2013-02-05 8:00 ` [PATCH v8 7/8] usb: chipidea: imx: add internal vbus regulator control Peter Chen
2013-02-05 8:00 ` [PATCH v8 8/8] usb: chipidea: udc: fix the oops when plugs in usb cable after rmmod gadget Peter Chen
2013-02-13 8:41 ` Felipe Balbi
2013-02-17 3:09 ` Peter Chen
2013-02-26 18:21 ` Felipe Balbi
2013-02-27 3:10 ` Peter Chen
2013-02-27 8:42 ` Felipe Balbi
2013-02-27 8:57 ` Chen Peter-B29397
2013-02-14 12:15 ` [PATCH v8 0/8] Add tested id switch and vbus connect detect support for Chipidea Alexander Shishkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).