Merge "arm64: defconfig: Enable Command DB" into msm-4.8
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index f4d1090..1c870ac 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -39,7 +39,7 @@
- clocks: a list of phandles to the controller clocks. Use as per
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
- property. Optional clocks are "bus_aggr_clk" and "cfg_ahb_clk".
+ property. Optional clocks are "bus_aggr_clk", "noc_aggr_clk" and "cfg_ahb_clk".
- qcom,charging-disabled: If present then battery charging using USB
is disabled.
- vbus_dwc3-supply: phandle to the 5V VBUS supply regulator used for host mode.
@@ -95,12 +95,13 @@
clocks = <&clock_gcc clk_gcc_usb30_master_clk>,
<&clock_gcc clk_gcc_cfg_noc_usb3_axi_clk>,
<&clock_gcc clk_gcc_aggre1_usb3_axi_clk>,
+ <&clock_rpmcc RPM_AGGR2_NOC_CLK>,
<&clock_gcc clk_gcc_usb30_mock_utmi_clk>,
<&clock_gcc clk_gcc_usb30_sleep_clk>,
<&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
<&clock_gcc clk_cxo_dwc3_clk>;
- clock-names = "core_clk", "iface_clk", "bus_aggr_clk",
+ clock-names = "core_clk", "iface_clk", "bus_aggr_clk", "noc_aggr_clk",
"utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo";
resets = <&clock_gcc GCC_USB_30_BCR>;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 2575878..5759847 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -624,16 +624,16 @@
IPADBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
- if (!ipa3_is_ready()) {
- IPAERR("IPA not ready, waiting for init completion\n");
- wait_for_completion(&ipa3_ctx->init_completion_obj);
- }
-
if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
return -ENOTTY;
+ if (!ipa3_is_ready()) {
+ IPAERR("IPA not ready, waiting for init completion\n");
+ wait_for_completion(&ipa3_ctx->init_completion_obj);
+ }
+
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
switch (cmd) {
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 65fe33b..f4060d9 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -191,6 +191,9 @@
return ret;
}
+ if (dwc->maximum_speed == USB_SPEED_HIGH)
+ goto generic_phy_init;
+
ret = usb_phy_init(dwc->usb3_phy);
if (ret == -EBUSY) {
/*
@@ -203,6 +206,8 @@
__func__, ret);
return ret;
}
+
+generic_phy_init:
ret = phy_init(dwc->usb2_generic_phy);
if (ret < 0)
return ret;
@@ -223,10 +228,13 @@
static int dwc3_core_reset(struct dwc3 *dwc)
{
int ret;
+ u32 reg;
/* Reset PHYs */
usb_phy_reset(dwc->usb2_phy);
- usb_phy_reset(dwc->usb3_phy);
+
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ usb_phy_reset(dwc->usb3_phy);
/* Initialize PHYs */
ret = dwc3_init_usb_phys(dwc);
@@ -236,6 +244,10 @@
return ret;
}
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg &= ~DWC3_GUSB3PIPECTL_DELAYP1TRANS;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT);
dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 968237d..246552e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1061,7 +1061,7 @@
/* IRQ timing statistics */
int irq;
- struct tasklet_struct bh;
+ unsigned long ep_cmd_timeout_cnt;
unsigned long irq_cnt;
unsigned int bh_completion_time[MAX_INTR_STATS];
unsigned int bh_handled_evt_cnt[MAX_INTR_STATS];
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index ea36f41..57db6d9 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -44,6 +44,7 @@
#include <linux/irq.h>
#include <linux/extcon.h>
#include <linux/reset.h>
+#include <linux/clk/qcom.h>
#include "power.h"
#include "core.h"
@@ -157,6 +158,7 @@
unsigned int utmi_clk_rate;
struct clk *utmi_clk_src;
struct clk *bus_aggr_clk;
+ struct clk *noc_aggr_clk;
struct clk *cfg_ahb_clk;
struct reset_control *core_reset;
struct regulator *dwc3_gdsc;
@@ -1244,6 +1246,7 @@
struct usb_gsi_request *request;
struct gsi_channel_info *ch_info;
bool block_db, f_suspend;
+ unsigned long flags;
switch (op) {
case GSI_EP_OP_PREPARE_TRBS:
@@ -1258,11 +1261,15 @@
case GSI_EP_OP_CONFIG:
request = (struct usb_gsi_request *)op_data;
dev_dbg(mdwc->dev, "EP_OP_CONFIG for %s\n", ep->name);
+ spin_lock_irqsave(&dwc->lock, flags);
gsi_configure_ep(ep, request);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_STARTXFER:
dev_dbg(mdwc->dev, "EP_OP_STARTXFER for %s\n", ep->name);
+ spin_lock_irqsave(&dwc->lock, flags);
ret = gsi_startxfer_for_ep(ep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_GET_XFER_IDX:
dev_dbg(mdwc->dev, "EP_OP_GET_XFER_IDX for %s\n", ep->name);
@@ -1288,12 +1295,16 @@
case GSI_EP_OP_UPDATEXFER:
request = (struct usb_gsi_request *)op_data;
dev_dbg(mdwc->dev, "EP_OP_UPDATEXFER\n");
+ spin_lock_irqsave(&dwc->lock, flags);
ret = gsi_updatexfer_for_ep(ep, request);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_ENDXFER:
request = (struct usb_gsi_request *)op_data;
dev_dbg(mdwc->dev, "EP_OP_ENDXFER for %s\n", ep->name);
+ spin_lock_irqsave(&dwc->lock, flags);
gsi_endxfer_for_ep(ep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_SET_CLR_BLOCK_DBL:
block_db = *((bool *)op_data);
@@ -1444,11 +1455,10 @@
pm_runtime_suspend(mdwc->dev);
}
+ mdwc->in_restart = false;
/* Force reconnect only if cable is still connected */
- if (mdwc->vbus_active) {
- mdwc->in_restart = false;
+ if (mdwc->vbus_active)
dwc3_resume_work(&mdwc->resume_work);
- }
dwc->err_evt_seen = false;
flush_delayed_work(&mdwc->sm_work);
@@ -1684,7 +1694,7 @@
break;
case DWC3_CONTROLLER_RESTART_USB_SESSION:
dev_dbg(mdwc->dev, "DWC3_CONTROLLER_RESTART_USB_SESSION received\n");
- dwc3_restart_usb_work(&mdwc->restart_usb_work);
+ schedule_work(&mdwc->restart_usb_work);
break;
default:
dev_dbg(mdwc->dev, "unknown dwc3 event\n");
@@ -1757,7 +1767,7 @@
u32 reg = 0;
if ((mdwc->in_host_mode || mdwc->vbus_active)
- && dwc3_msm_is_superspeed(mdwc)) {
+ && dwc3_msm_is_superspeed(mdwc) && !mdwc->in_restart) {
if (!atomic_read(&mdwc->in_p3)) {
dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n");
return -EBUSY;
@@ -1903,7 +1913,7 @@
usb_phy_set_suspend(mdwc->hs_phy, 1);
/* Suspend SS PHY */
- if (can_suspend_ssphy) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER && can_suspend_ssphy) {
/* indicate phy about SS mode */
if (dwc3_msm_is_superspeed(mdwc))
mdwc->ss_phy->flags |= DEVICE_IN_SS_MODE;
@@ -1919,8 +1929,16 @@
clk_disable_unprepare(mdwc->bus_aggr_clk);
clk_disable_unprepare(mdwc->utmi_clk);
+ /* Memory core: OFF, Memory periphery: OFF */
+ if (!mdwc->in_host_mode && !mdwc->vbus_active) {
+ clk_set_flags(mdwc->core_clk, CLKFLAG_NORETAIN_MEM);
+ clk_set_flags(mdwc->core_clk, CLKFLAG_NORETAIN_PERIPH);
+ }
+
clk_set_rate(mdwc->core_clk, 19200000);
clk_disable_unprepare(mdwc->core_clk);
+ if (mdwc->noc_aggr_clk)
+ clk_disable_unprepare(mdwc->noc_aggr_clk);
/*
* Disable iface_clk only after core_clk as core_clk has FSM
* depedency on iface_clk. Hence iface_clk should be turned off
@@ -2035,14 +2053,22 @@
* Turned ON iface_clk before core_clk due to FSM depedency.
*/
clk_prepare_enable(mdwc->iface_clk);
+ if (mdwc->noc_aggr_clk)
+ clk_prepare_enable(mdwc->noc_aggr_clk);
clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
clk_prepare_enable(mdwc->core_clk);
+
+ /* set Memory core: ON, Memory periphery: ON */
+ clk_set_flags(mdwc->core_clk, CLKFLAG_RETAIN_MEM);
+ clk_set_flags(mdwc->core_clk, CLKFLAG_RETAIN_PERIPH);
+
clk_prepare_enable(mdwc->utmi_clk);
if (mdwc->bus_aggr_clk)
clk_prepare_enable(mdwc->bus_aggr_clk);
/* Resume SS PHY */
- if (mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER &&
+ mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
mdwc->ss_phy->flags &= ~(PHY_LANE_A | PHY_LANE_B);
if (mdwc->typec_orientation == ORIENTATION_CC1)
mdwc->ss_phy->flags |= PHY_LANE_A;
@@ -2376,6 +2402,10 @@
if (IS_ERR(mdwc->bus_aggr_clk))
mdwc->bus_aggr_clk = NULL;
+ mdwc->noc_aggr_clk = devm_clk_get(mdwc->dev, "noc_aggr_clk");
+ if (IS_ERR(mdwc->noc_aggr_clk))
+ mdwc->noc_aggr_clk = NULL;
+
if (of_property_match_string(mdwc->dev->of_node,
"clock-names", "cfg_ahb_clk") >= 0) {
mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk");
@@ -2397,9 +2427,11 @@
unsigned long event, void *ptr)
{
struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, id_nb);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
struct extcon_dev *edev = ptr;
enum dwc3_id_state id;
int cc_state;
+ int speed;
if (!edev) {
dev_err(mdwc->dev, "%s: edev null\n", __func__);
@@ -2417,6 +2449,11 @@
mdwc->typec_orientation =
cc_state ? ORIENTATION_CC2 : ORIENTATION_CC1;
+ dev_dbg(mdwc->dev, "cc_state:%d", mdwc->typec_orientation);
+
+ speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
+ dwc->maximum_speed = (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
+
if (mdwc->id_state != id) {
mdwc->id_state = id;
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
@@ -2433,6 +2470,7 @@
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
struct extcon_dev *edev = ptr;
int cc_state;
+ int speed;
if (!edev) {
dev_err(mdwc->dev, "%s: edev null\n", __func__);
@@ -2451,6 +2489,11 @@
mdwc->typec_orientation =
cc_state ? ORIENTATION_CC2 : ORIENTATION_CC1;
+ dev_dbg(mdwc->dev, "cc_state:%d", mdwc->typec_orientation);
+
+ speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
+ dwc->maximum_speed = (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
+
mdwc->vbus_active = event;
if (dwc->is_drd && !mdwc->in_restart)
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
@@ -2908,6 +2951,8 @@
if (ret_pm < 0) {
dev_err(mdwc->dev,
"pm_runtime_get_sync failed with %d\n", ret_pm);
+ if (mdwc->noc_aggr_clk)
+ clk_prepare_enable(mdwc->noc_aggr_clk);
clk_prepare_enable(mdwc->utmi_clk);
clk_prepare_enable(mdwc->core_clk);
clk_prepare_enable(mdwc->iface_clk);
@@ -2993,9 +3038,11 @@
if (on) {
dev_dbg(mdwc->dev, "%s: turn on host\n", __func__);
- pm_runtime_get_sync(mdwc->dev);
mdwc->hs_phy->flags |= PHY_HOST_MODE;
- mdwc->ss_phy->flags |= PHY_HOST_MODE;
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ mdwc->ss_phy->flags |= PHY_HOST_MODE;
+
+ pm_runtime_get_sync(mdwc->dev);
usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
if (!IS_ERR(mdwc->vbus_reg))
ret = regulator_enable(mdwc->vbus_reg);
@@ -3147,7 +3194,8 @@
static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA)
{
- union power_supply_propval pval = {0,};
+ union power_supply_propval pval = {0};
+ int ret;
if (mdwc->charging_disabled)
return 0;
@@ -3163,44 +3211,23 @@
}
}
- dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
+ power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPE, &pval);
+ if (pval.intval != POWER_SUPPLY_TYPE_USB)
+ return 0;
- if (mdwc->max_power <= 2 && mA > 2) {
- /* Enable Charging */
- pval.intval = true;
- if (power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval))
- goto psy_error;
- pval.intval = 1000 * mA;
- if (power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval))
- goto psy_error;
- } else if (mdwc->max_power > 0 && (mA == 0 || mA == 2)) {
- /* Disable charging */
- pval.intval = false;
- if (power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval))
- goto psy_error;
- } else {
- /* Enable charging */
- pval.intval = true;
- if (power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval))
- goto psy_error;
- }
+ dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
/* Set max current limit in uA */
pval.intval = 1000 * mA;
- if (power_supply_set_property(mdwc->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval))
- goto psy_error;
+ ret = power_supply_set_property(mdwc->usb_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+ if (ret) {
+ dev_dbg(mdwc->dev, "power supply error when setting property\n");
+ return ret;
+ }
mdwc->max_power = mA;
return 0;
-
-psy_error:
- dev_dbg(mdwc->dev, "power supply error when setting property\n");
- return -ENXIO;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a9789cc..b8fc012 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -238,7 +238,7 @@
struct dwc3_gadget_ep_cmd_params *params)
{
struct dwc3 *dwc = dep->dwc;
- u32 timeout = 1500;
+ u32 timeout = 3000;
u32 reg;
int cmd_status = 0;
@@ -320,6 +320,11 @@
dwc3_trace(trace_dwc3_gadget, "Command Timed Out");
dev_err(dwc->dev, "%s command timeout for %s\n",
dwc3_gadget_ep_cmd_string(cmd), dep->name);
+ if (!(cmd & DWC3_DEPCMD_ENDTRANSFER)) {
+ dwc->ep_cmd_timeout_cnt++;
+ dwc3_notify_event(dwc,
+ DWC3_CONTROLLER_RESTART_USB_SESSION);
+ }
cmd_status = -ETIMEDOUT;
}
@@ -386,7 +391,16 @@
if (dep->endpoint.ep_type == EP_TYPE_GSI)
return;
- if (dep->trb_pool && dep->trb_pool_dma) {
+ /*
+ * Clean up ep ring to avoid getting xferInProgress due to stale trbs
+ * with HWO bit set from previous composition when update transfer cmd
+ * is issued.
+ */
+ if (dep->number > 1 && dep->trb_pool && dep->trb_pool_dma) {
+ memset(&dep->trb_pool[0], 0,
+ sizeof(struct dwc3_trb) * dep->num_trbs);
+ dev_dbg(dwc->dev, "Clr_TRB ring of %s\n", dep->name);
+
dma_free_coherent(dwc->dev,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
dep->trb_pool, dep->trb_pool_dma);
@@ -671,15 +685,6 @@
(dep->number & 1) ? "in" : "out");
}
- /*
- * Clean up ep ring of non-control endpoint to avoid getting
- * xferInProgress due to stale trbs with HWO bit set from previous
- * composition when update transfer cmd is issued.
- */
- if (dep->number > 1 && dep->trb_pool)
- memset(&dep->trb_pool[0], 0,
- sizeof(struct dwc3_trb) * dep->num_trbs);
-
return 0;
}
@@ -2078,17 +2083,10 @@
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
- pm_runtime_get_sync(dwc->dev);
- tasklet_kill(&dwc->bh);
-
spin_lock_irqsave(&dwc->lock, flags);
- __dwc3_gadget_stop(dwc);
dwc->gadget_driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
- pm_runtime_mark_last_busy(dwc->dev);
- pm_runtime_put_autosuspend(dwc->dev);
-
return 0;
}
@@ -2752,7 +2750,7 @@
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT);
dwc3_usb3_phy_suspend(dwc, false);
- usb_gadget_vbus_draw(&dwc->gadget, 0);
+ usb_gadget_vbus_draw(&dwc->gadget, 100);
dwc3_reset_gadget(dwc);
@@ -3276,15 +3274,6 @@
return ret;
}
-static void dwc3_interrupt_bh(unsigned long param)
-{
- struct dwc3 *dwc = (struct dwc3 *) param;
-
- pm_runtime_get(dwc->dev);
- dwc3_thread_interrupt(dwc->irq, dwc);
- enable_irq(dwc->irq);
-}
-
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
{
struct dwc3 *dwc = _dwc;
@@ -3306,7 +3295,6 @@
dwc->bh_completion_time[dwc->bh_dbg_index] = temp_time;
dwc->bh_dbg_index = (dwc->bh_dbg_index + 1) % 10;
- pm_runtime_put(dwc->dev);
return ret;
}
@@ -3366,10 +3354,8 @@
dwc->irq_event_count[dwc->irq_dbg_index] = temp_cnt / 4;
dwc->irq_dbg_index = (dwc->irq_dbg_index + 1) % MAX_INTR_STATS;
- if (ret == IRQ_WAKE_THREAD) {
- disable_irq_nosync(irq);
- tasklet_schedule(&dwc->bh);
- }
+ if (ret == IRQ_WAKE_THREAD)
+ dwc3_thread_interrupt(dwc->irq, dwc);
return IRQ_HANDLED;
}
@@ -3449,9 +3435,6 @@
goto err4;
}
- dwc->bh.func = dwc3_interrupt_bh;
- dwc->bh.data = (unsigned long)dwc;
-
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 1d369eb..a354f76 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -418,6 +418,114 @@
}
EXPORT_SYMBOL_GPL(usb_interface_id);
+/**
+ * usb_get_func_interface_id() - Find the interface ID of a function
+ * @function: the function for which want to find the interface ID
+ * Context: single threaded
+ *
+ * Returns the interface ID of the function or -ENODEV if this function
+ * is not part of this configuration
+ */
+int usb_get_func_interface_id(struct usb_function *func)
+{
+ int id;
+ struct usb_configuration *config;
+
+ if (!func)
+ return -EINVAL;
+
+ config = func->config;
+
+ for (id = 0; id < MAX_CONFIG_INTERFACES; id++) {
+ if (config->interface[id] == func)
+ return id;
+ }
+ return -ENODEV;
+}
+
+int usb_func_wakeup(struct usb_function *func)
+{
+ int ret;
+ unsigned int interface_id;
+ struct usb_gadget *gadget;
+
+ pr_debug("%s function wakeup\n",
+ func->name ? func->name : "");
+
+ if (!func || !func->config || !func->config->cdev ||
+ !func->config->cdev->gadget)
+ return -EINVAL;
+
+ gadget = func->config->cdev->gadget;
+ if ((gadget->speed != USB_SPEED_SUPER) || !func->func_wakeup_allowed) {
+ DBG(func->config->cdev,
+ "Function Wakeup is not possible. speed=%u, func_wakeup_allowed=%u\n",
+ gadget->speed,
+ func->func_wakeup_allowed);
+
+ return -ENOTSUPP;
+ }
+
+ ret = usb_get_func_interface_id(func);
+ if (ret < 0) {
+ ERROR(func->config->cdev,
+ "Function %s - Unknown interface id. Canceling USB request. ret=%d\n",
+ func->name ? func->name : "", ret);
+ return ret;
+ }
+
+ interface_id = ret;
+ ret = usb_gadget_func_wakeup(gadget, interface_id);
+ if (ret) {
+ if (ret == -EAGAIN) {
+ DBG(func->config->cdev,
+ "Function wakeup for %s could not be complete. Retry is needed.\n",
+ func->name ? func->name : "");
+ } else {
+ ERROR(func->config->cdev,
+ "Failed to wake function %s from suspend state. interface id: %d, ret=%d. Canceling USB request.\n",
+ func->name ? func->name : "",
+ interface_id, ret);
+ }
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_func_wakeup);
+
+int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ int ret;
+ struct usb_gadget *gadget;
+
+ if (!func || !ep || !req) {
+ pr_err("Invalid argument. func=%p, ep=%p, req=%p\n",
+ func, ep, req);
+ return -EINVAL;
+ }
+
+ pr_debug("Function %s queueing new data into ep %u\n",
+ func->name ? func->name : "", ep->address);
+
+ gadget = func->config->cdev->gadget;
+ if ((gadget->speed == USB_SPEED_SUPER) && func->func_is_suspended) {
+ ret = usb_func_wakeup(func);
+ if (ret) {
+ if (ret != -EAGAIN)
+ pr_err("Failed to send function wake up notification. func name:%s, ep:%u\n",
+ func->name ? func->name : "",
+ ep->address);
+ return ret;
+ }
+ }
+
+ ret = usb_ep_queue(ep, req, gfp_flags);
+ return ret;
+}
+EXPORT_SYMBOL(usb_func_ep_queue);
+
static u8 encode_bMaxPower(enum usb_device_speed speed,
struct usb_configuration *c)
{
@@ -730,6 +838,10 @@
if (f->disable)
f->disable(f);
+ /* USB 3.0 addition */
+ f->func_is_suspended = false;
+ f->func_wakeup_allowed = false;
+
bitmap_zero(f->endpoints, 32);
}
cdev->config = NULL;
@@ -1780,8 +1892,13 @@
if (!f)
break;
value = 0;
- if (f->func_suspend)
- value = f->func_suspend(f, w_index >> 8);
+ if (f->func_suspend) {
+ const u8 suspend_opt = w_index >> 8;
+
+ value = f->func_suspend(f, suspend_opt);
+ DBG(cdev, "%s function: FUNCTION_SUSPEND(%u)",
+ f->name ? f->name : "", suspend_opt);
+ }
if (value < 0) {
ERROR(cdev,
"func_suspend() returned error %d\n",
@@ -1997,6 +2114,10 @@
reset_config(cdev);
if (cdev->driver->disconnect)
cdev->driver->disconnect(cdev);
+ if (cdev->delayed_status != 0) {
+ INFO(cdev, "delayed status mismatch..resetting\n");
+ cdev->delayed_status = 0;
+ }
spin_unlock_irqrestore(&cdev->lock, flags);
}
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 30fdab0..d942be9 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -209,3 +209,41 @@
gadget->out_epnum = 0;
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
+
+/**
+ * usb_ep_autoconfig_by_name - Used to pick the endpoint by name. eg ep1in-gsi
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized.
+ * @ep_name: EP name that is to be searched.
+ *
+ */
+struct usb_ep *usb_ep_autoconfig_by_name(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ const char *ep_name
+)
+{
+ struct usb_ep *ep;
+ bool ep_found = false;
+
+ list_for_each_entry(ep, &gadget->ep_list, ep_list)
+ if (strcmp(ep->name, ep_name) == 0 && !ep->driver_data) {
+ ep_found = true;
+ break;
+ }
+
+ if (ep_found) {
+ desc->bEndpointAddress &= USB_DIR_IN;
+ desc->bEndpointAddress |= ep->ep_num;
+ ep->address = desc->bEndpointAddress;
+ pr_debug("Allocating ep address:%x\n", ep->address);
+ ep->desc = NULL;
+ ep->comp_desc = NULL;
+ return ep;
+ }
+
+ pr_err("%s:error finding ep %s\n", __func__, ep_name);
+ return NULL;
+}
+EXPORT_SYMBOL(usb_ep_autoconfig_by_name);
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index b899ced..41e1b47 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -1020,7 +1020,7 @@
rndis->port.func.disable = rndis_disable;
rndis->port.func.free_func = rndis_free;
- params = rndis_register(rndis_response_available, rndis);
+ params = rndis_register(rndis_response_available, rndis, NULL);
if (IS_ERR(params)) {
kfree(rndis);
return ERR_CAST(params);
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 418a2fd..ed93f9d 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -595,7 +595,7 @@
+ sizeof(struct ethhdr)
+ sizeof(struct rndis_packet_msg_type)
+ 22));
- resp->PacketAlignmentFactor = cpu_to_le32(0);
+ resp->PacketAlignmentFactor = cpu_to_le32(params->pkt_alignment_factor);
resp->AFListOffset = cpu_to_le32(0);
resp->AFListSize = cpu_to_le32(0);
@@ -830,10 +830,16 @@
case RNDIS_MSG_HALT:
pr_debug("%s: RNDIS_MSG_HALT\n",
__func__);
- params->state = RNDIS_UNINITIALIZED;
- if (params->dev) {
- netif_carrier_off(params->dev);
- netif_stop_queue(params->dev);
+ if (params->state == RNDIS_DATA_INITIALIZED) {
+ if (params->flow_ctrl_enable) {
+ params->flow_ctrl_enable(true, params);
+ } else {
+ if (params->dev) {
+ netif_carrier_off(params->dev);
+ netif_stop_queue(params->dev);
+ }
+ }
+ params->state = RNDIS_UNINITIALIZED;
}
return 0;
@@ -885,7 +891,8 @@
ida_simple_remove(&rndis_ida, nr);
}
-struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
+struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v,
+ void (*flow_ctrl_enable)(bool enable, struct rndis_params *params))
{
struct rndis_params *params;
int i;
@@ -929,6 +936,7 @@
params->state = RNDIS_UNINITIALIZED;
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
params->resp_avail = resp_avail;
+ params->flow_ctrl_enable = flow_ctrl_enable;
params->v = v;
INIT_LIST_HEAD(¶ms->resp_queue);
pr_debug("%s: configNr = %d\n", __func__, i);
@@ -1014,6 +1022,49 @@
params->max_pkt_per_xfer = max_pkt_per_xfer;
}
+/**
+ * rndis_flow_control: enable/disable flow control with USB RNDIS interface
+ * params - RNDIS network parameter
+ * enable_flow_control - true: perform flow control, false: disable flow control
+ *
+ * In hw accelerated mode, this function triggers functionality to start/stop
+ * endless transfers, otherwise it enables/disables RNDIS network interface.
+ */
+void rndis_flow_control(struct rndis_params *params, bool enable_flow_control)
+{
+ if (!params) {
+ pr_err("%s: failed, params NULL\n", __func__);
+ return;
+ }
+
+ pr_debug("%s(): params->state:%x\n", __func__, params->state);
+
+ if (enable_flow_control) {
+ if (params->state == RNDIS_DATA_INITIALIZED) {
+ if (params->flow_ctrl_enable) {
+ params->flow_ctrl_enable(enable_flow_control,
+ params);
+ } else {
+ netif_carrier_off(params->dev);
+ netif_stop_queue(params->dev);
+ }
+ }
+ params->state = RNDIS_INITIALIZED;
+ } else {
+ if (params->state != RNDIS_DATA_INITIALIZED) {
+ if (params->flow_ctrl_enable) {
+ params->flow_ctrl_enable(enable_flow_control,
+ params);
+ } else {
+ netif_carrier_on(params->dev);
+ if (netif_running(params->dev))
+ netif_wake_queue(params->dev);
+ }
+ }
+ params->state = RNDIS_DATA_INITIALIZED;
+ }
+}
+
void rndis_add_hdr(struct sk_buff *skb)
{
struct rndis_packet_msg_type *header;
@@ -1153,6 +1204,19 @@
}
EXPORT_SYMBOL_GPL(rndis_rm_hdr);
+void rndis_set_pkt_alignment_factor(struct rndis_params *params,
+ u8 pkt_alignment_factor)
+{
+ pr_debug("%s:\n", __func__);
+
+ if (!params) {
+ pr_err("%s: failed, params NULL\n", __func__);
+ return;
+ }
+
+ params->pkt_alignment_factor = pkt_alignment_factor;
+}
+
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static int rndis_proc_show(struct seq_file *m, void *v)
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index 310cac3..939c3be 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -192,14 +192,19 @@
u32 vendorID;
u8 max_pkt_per_xfer;
const char *vendorDescr;
+ u8 pkt_alignment_factor;
void (*resp_avail)(void *v);
+ void (*flow_ctrl_enable)(bool enable,
+ struct rndis_params *params);
+
void *v;
struct list_head resp_queue;
} rndis_params;
/* RNDIS Message parser and other useless functions */
int rndis_msg_parser(struct rndis_params *params, u8 *buf);
-struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v);
+struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v,
+ void (*flow_ctrl_enable)(bool enable, struct rndis_params *params));
void rndis_deregister(struct rndis_params *params);
int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
u16 *cdc_filter);
@@ -219,5 +224,8 @@
int rndis_signal_disconnect(struct rndis_params *params);
int rndis_state(struct rndis_params *params);
extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr);
+void rndis_flow_control(struct rndis_params *params, bool enable_flow_control);
+void rndis_set_pkt_alignment_factor(struct rndis_params *params,
+ u8 pkt_alignment_factor);
#endif /* _LINUX_RNDIS_H */
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 93f0253..2f713f5 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -41,6 +41,10 @@
#include <linux/log2.h>
#include <linux/configfs.h>
+/* FUNCTION_SUSPEND: suspend options from usb 3.0 spec Table 9-7 */
+#define FUNC_SUSPEND_OPT_SUSP_MASK BIT(0)
+#define FUNC_SUSPEND_OPT_RW_EN_MASK BIT(1)
+
/*
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
* wish to delay the data/status stages of the control transfer till they
@@ -158,7 +162,11 @@
* @get_status: Returns function status as a reply to
* GetStatus() request when the recipient is Interface.
* @func_suspend: callback to be called when
- * SetFeature(FUNCTION_SUSPEND) is reseived
+ * SetFeature(FUNCTION_SUSPEND) is received
+ * @func_is_suspended: Tells whether the function is currently in
+ * Function Suspend state (used in Super Speed mode only).
+ * @func_wakeup_allowed: Tells whether Function Remote Wakeup has been allowed
+ * by the USB host (used in Super Speed mode only).
*
* A single USB function uses one or more interfaces, and should in most
* cases support operation at both full and high speeds. Each function is
@@ -229,6 +237,8 @@
int (*get_status)(struct usb_function *);
int (*func_suspend)(struct usb_function *,
u8 suspend_opt);
+ unsigned func_is_suspended:1;
+ unsigned func_wakeup_allowed:1;
/* private: */
/* internals */
struct list_head list;
@@ -244,6 +254,9 @@
int usb_function_activate(struct usb_function *);
int usb_interface_id(struct usb_configuration *, struct usb_function *);
+int usb_func_wakeup(struct usb_function *func);
+
+int usb_get_func_interface_id(struct usb_function *func);
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
struct usb_ep *_ep);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index a625a7b..9cd86ff 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -24,6 +24,7 @@
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
+#include <linux/pm_runtime.h>
#define UDC_TRACE_STR_MAX 512
@@ -297,6 +298,7 @@
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
* descriptor that is used to configure the endpoint
* @ep_type: Used to specify type of EP eg. normal vs h/w accelerated.
+ * @ep_num: Used EP number
* @ep_intr_num: Interrupter number for EP.
*
* the bus controller driver lists all the general purpose endpoints in
@@ -322,6 +324,7 @@
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
enum ep_type ep_type;
+ u8 ep_num;
u8 ep_intr_num;
};
@@ -402,6 +405,7 @@
struct usb_gadget_ops {
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
+ int (*func_wakeup)(struct usb_gadget *, int interface_id);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
@@ -415,7 +419,6 @@
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *,
struct usb_ss_ep_comp_descriptor *);
- int (*func_wakeup)(struct usb_gadget *, int interface_id);
int (*restart)(struct usb_gadget *);
};
@@ -777,8 +780,6 @@
unsigned match_existing_only:1;
};
-
-
/*-------------------------------------------------------------------------*/
/* driver modules register and unregister, as usual.
@@ -899,6 +900,24 @@
struct usb_descriptor_header *otg_desc);
/*-------------------------------------------------------------------------*/
+/**
+ * usb_func_ep_queue - queues (submits) an I/O request to a function endpoint.
+ * This function is similar to the usb_ep_queue function, but in addition it
+ * also checks whether the function is in Super Speed USB Function Suspend
+ * state, and if so a Function Wake notification is sent to the host
+ * (USB 3.0 spec, section 9.2.5.2).
+ * @func: the function which issues the USB I/O request.
+ * @ep:the endpoint associated with the request
+ * @req:the request being submitted
+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
+ * pre-allocate all necessary memory with the request.
+ *
+ */
+int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags);
+
+/*-------------------------------------------------------------------------*/
+
/* utility to simplify map/unmap of usb_requests to/from DMA */
extern int usb_gadget_map_request_by_dev(struct device *dev,
@@ -951,6 +970,129 @@
/* utility to update vbus status for udc core, it may be scheduled */
extern void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status);
+/**
+ * usb_gadget_autopm_get - increment PM-usage counter of usb gadget's parent
+ * device.
+ * @gadget: usb gadget whose parent device counter is incremented
+ *
+ * This routine should be called by function driver when it wants to use
+ * gadget's parent device and needs to guarantee that it is not suspended. In
+ * addition, the routine prevents subsequent autosuspends of gadget's parent
+ * device. However if the autoresume fails then the counter is re-decremented.
+ *
+ * This routine can run only in process context.
+ */
+static inline int usb_gadget_autopm_get(struct usb_gadget *gadget)
+{
+ int status = -ENODEV;
+
+ if (!gadget || !gadget->dev.parent)
+ return status;
+
+ status = pm_runtime_get_sync(gadget->dev.parent);
+ if (status < 0)
+ pm_runtime_put_sync(gadget->dev.parent);
+
+ if (status > 0)
+ status = 0;
+ return status;
+}
+
+/**
+ * usb_gadget_autopm_get_async - increment PM-usage counter of usb gadget's
+ * parent device.
+ * @gadget: usb gadget whose parent device counter is incremented
+ *
+ * This routine increments @gadget parent device PM usage counter and queue an
+ * autoresume request if the device is suspended. It does not autoresume device
+ * directly (it only queues a request). After a successful call, the device may
+ * not yet be resumed.
+ *
+ * This routine can run in atomic context.
+ */
+static inline int usb_gadget_autopm_get_async(struct usb_gadget *gadget)
+{
+ int status = -ENODEV;
+
+ if (!gadget || !gadget->dev.parent)
+ return status;
+
+ status = pm_runtime_get(gadget->dev.parent);
+ if (status < 0 && status != -EINPROGRESS)
+ pm_runtime_put_noidle(gadget->dev.parent);
+
+ if (status > 0 || status == -EINPROGRESS)
+ status = 0;
+ return status;
+}
+
+/**
+ * usb_gadget_autopm_get_noresume - increment PM-usage counter of usb gadget's
+ * parent device.
+ * @gadget: usb gadget whose parent device counter is incremented
+ *
+ * This routine increments PM-usage count of @gadget parent device but does not
+ * carry out an autoresume.
+ *
+ * This routine can run in atomic context.
+ */
+static inline void usb_gadget_autopm_get_noresume(struct usb_gadget *gadget)
+{
+ if (gadget && gadget->dev.parent)
+ pm_runtime_get_noresume(gadget->dev.parent);
+}
+
+/**
+ * usb_gadget_autopm_put - decrement PM-usage counter of usb gadget's parent
+ * device.
+ * @gadget: usb gadget whose parent device counter is decremented.
+ *
+ * This routine should be called by function driver when it is finished using
+ * @gadget parent device and wants to allow it to autosuspend. It decrements
+ * PM-usage counter of @gadget parent device, when the counter reaches 0, a
+ * delayed autosuspend request is attempted.
+ *
+ * This routine can run only in process context.
+ */
+static inline void usb_gadget_autopm_put(struct usb_gadget *gadget)
+{
+ if (gadget && gadget->dev.parent)
+ pm_runtime_put_sync(gadget->dev.parent);
+}
+
+/**
+ * usb_gadget_autopm_put_async - decrement PM-usage counter of usb gadget's
+ * parent device.
+ * @gadget: usb gadget whose parent device counter is decremented.
+ *
+ * This routine decrements PM-usage counter of @gadget parent device and
+ * schedules a delayed autosuspend request if the counter is <= 0.
+ *
+ * This routine can run in atomic context.
+ */
+static inline void usb_gadget_autopm_put_async(struct usb_gadget *gadget)
+{
+ if (gadget && gadget->dev.parent)
+ pm_runtime_put(gadget->dev.parent);
+}
+
+/**
+ * usb_gadget_autopm_put_no_suspend - decrement PM-usage counter of usb gadget
+'s
+ * parent device.
+ * @gadget: usb gadget whose parent device counter is decremented.
+ *
+ * This routine decrements PM-usage counter of @gadget parent device but does
+ * not carry out an autosuspend.
+ *
+ * This routine can run in atomic context.
+ */
+static inline void usb_gadget_autopm_put_no_suspend(struct usb_gadget *gadget)
+{
+ if (gadget && gadget->dev.parent)
+ pm_runtime_put_noidle(gadget->dev.parent);
+}
+
/*-------------------------------------------------------------------------*/
/* utility wrapping a simple endpoint selection policy */
@@ -966,6 +1108,9 @@
extern void usb_ep_autoconfig_release(struct usb_ep *);
extern void usb_ep_autoconfig_reset(struct usb_gadget *);
+extern struct usb_ep *usb_ep_autoconfig_by_name(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ const char *ep_name);
#ifdef CONFIG_USB_DWC3_MSM
int msm_ep_config(struct usb_ep *ep);