Commit 6d2d91b3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'usb-4.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB fixes from Greg KH:
 "Here are a number of USB fixes and new device ids for 4.4-rc2.  All
  have been in linux-next and the details are in the shortlog"

* tag 'usb-4.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (28 commits)
  usblp: do not set TASK_INTERRUPTIBLE before lock
  USB: MAINTAINERS: cxacru
  usb: kconfig: fix warning of select USB_OTG
  USB: option: add XS Stick W100-2 from 4G Systems
  xhci: Fix a race in usb2 LPM resume, blocking U3 for usb2 devices
  usb: xhci: fix checking ep busy for CFC
  xhci: Workaround to get Intel xHCI reset working more reliably
  usb: chipidea: imx: fix a possible NULL dereference
  usb: chipidea: usbmisc_imx: fix a possible NULL dereference
  usb: chipidea: otg: gadget module load and unload support
  usb: chipidea: debug: disable usb irq while role switch
  ARM: dts: imx27.dtsi: change the clock information for usb
  usb: chipidea: imx: refine clock operations to adapt for all platforms
  usb: gadget: atmel_usba_udc: Expose correct device speed
  usb: musb: enable usb_dma parameter
  usb: phy: phy-mxs-usb: fix a possible NULL dereference
  usb: dwc3: gadget: let us set lower max_speed
  usb: musb: fix tx fifo flush handling
  usb: gadget: f_loopback: fix the warning during the enumeration
  usb: dwc2: host: Fix remote wakeup when not in DWC2_L2
  ...
parents 0ec7dc8d 19cd80a2
...@@ -2931,10 +2931,9 @@ S: Maintained ...@@ -2931,10 +2931,9 @@ S: Maintained
F: drivers/platform/x86/compal-laptop.c F: drivers/platform/x86/compal-laptop.c
CONEXANT ACCESSRUNNER USB DRIVER CONEXANT ACCESSRUNNER USB DRIVER
M: Simon Arlott <cxacru@fire.lp0.eu>
L: accessrunner-general@lists.sourceforge.net L: accessrunner-general@lists.sourceforge.net
W: http://accessrunner.sourceforge.net/ W: http://accessrunner.sourceforge.net/
S: Maintained S: Orphan
F: drivers/usb/atm/cxacru.c F: drivers/usb/atm/cxacru.c
CONFIGFS CONFIGFS
......
...@@ -486,7 +486,10 @@ usbotg: usb@10024000 { ...@@ -486,7 +486,10 @@ usbotg: usb@10024000 {
compatible = "fsl,imx27-usb"; compatible = "fsl,imx27-usb";
reg = <0x10024000 0x200>; reg = <0x10024000 0x200>;
interrupts = <56>; interrupts = <56>;
clocks = <&clks IMX27_CLK_USB_IPG_GATE>; clocks = <&clks IMX27_CLK_USB_IPG_GATE>,
<&clks IMX27_CLK_USB_AHB_GATE>,
<&clks IMX27_CLK_USB_DIV>;
clock-names = "ipg", "ahb", "per";
fsl,usbmisc = <&usbmisc 0>; fsl,usbmisc = <&usbmisc 0>;
status = "disabled"; status = "disabled";
}; };
...@@ -495,7 +498,10 @@ usbh1: usb@10024200 { ...@@ -495,7 +498,10 @@ usbh1: usb@10024200 {
compatible = "fsl,imx27-usb"; compatible = "fsl,imx27-usb";
reg = <0x10024200 0x200>; reg = <0x10024200 0x200>;
interrupts = <54>; interrupts = <54>;
clocks = <&clks IMX27_CLK_USB_IPG_GATE>; clocks = <&clks IMX27_CLK_USB_IPG_GATE>,
<&clks IMX27_CLK_USB_AHB_GATE>,
<&clks IMX27_CLK_USB_DIV>;
clock-names = "ipg", "ahb", "per";
fsl,usbmisc = <&usbmisc 1>; fsl,usbmisc = <&usbmisc 1>;
dr_mode = "host"; dr_mode = "host";
status = "disabled"; status = "disabled";
...@@ -505,7 +511,10 @@ usbh2: usb@10024400 { ...@@ -505,7 +511,10 @@ usbh2: usb@10024400 {
compatible = "fsl,imx27-usb"; compatible = "fsl,imx27-usb";
reg = <0x10024400 0x200>; reg = <0x10024400 0x200>;
interrupts = <55>; interrupts = <55>;
clocks = <&clks IMX27_CLK_USB_IPG_GATE>; clocks = <&clks IMX27_CLK_USB_IPG_GATE>,
<&clks IMX27_CLK_USB_AHB_GATE>,
<&clks IMX27_CLK_USB_DIV>;
clock-names = "ipg", "ahb", "per";
fsl,usbmisc = <&usbmisc 2>; fsl,usbmisc = <&usbmisc 2>;
dr_mode = "host"; dr_mode = "host";
status = "disabled"; status = "disabled";
...@@ -515,7 +524,6 @@ usbmisc: usbmisc@10024600 { ...@@ -515,7 +524,6 @@ usbmisc: usbmisc@10024600 {
#index-cells = <1>; #index-cells = <1>;
compatible = "fsl,imx27-usbmisc"; compatible = "fsl,imx27-usbmisc";
reg = <0x10024600 0x200>; reg = <0x10024600 0x200>;
clocks = <&clks IMX27_CLK_USB_AHB_GATE>;
}; };
sahara2: sahara@10025000 { sahara2: sahara@10025000 {
......
...@@ -84,6 +84,12 @@ struct ci_hdrc_imx_data { ...@@ -84,6 +84,12 @@ struct ci_hdrc_imx_data {
struct imx_usbmisc_data *usbmisc_data; struct imx_usbmisc_data *usbmisc_data;
bool supports_runtime_pm; bool supports_runtime_pm;
bool in_lpm; bool in_lpm;
/* SoC before i.mx6 (except imx23/imx28) needs three clks */
bool need_three_clks;
struct clk *clk_ipg;
struct clk *clk_ahb;
struct clk *clk_per;
/* --------------------------------- */
}; };
/* Common functions shared by usbmisc drivers */ /* Common functions shared by usbmisc drivers */
...@@ -135,6 +141,102 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) ...@@ -135,6 +141,102 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
} }
/* End of common functions shared by usbmisc drivers*/ /* End of common functions shared by usbmisc drivers*/
static int imx_get_clks(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
data->clk_ipg = devm_clk_get(dev, "ipg");
if (IS_ERR(data->clk_ipg)) {
/* If the platform only needs one clocks */
data->clk = devm_clk_get(dev, NULL);
if (IS_ERR(data->clk)) {
ret = PTR_ERR(data->clk);
dev_err(dev,
"Failed to get clks, err=%ld,%ld\n",
PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
return ret;
}
return ret;
}
data->clk_ahb = devm_clk_get(dev, "ahb");
if (IS_ERR(data->clk_ahb)) {
ret = PTR_ERR(data->clk_ahb);
dev_err(dev,
"Failed to get ahb clock, err=%d\n", ret);
return ret;
}
data->clk_per = devm_clk_get(dev, "per");
if (IS_ERR(data->clk_per)) {
ret = PTR_ERR(data->clk_per);
dev_err(dev,
"Failed to get per clock, err=%d\n", ret);
return ret;
}
data->need_three_clks = true;
return ret;
}
static int imx_prepare_enable_clks(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
if (data->need_three_clks) {
ret = clk_prepare_enable(data->clk_ipg);
if (ret) {
dev_err(dev,
"Failed to prepare/enable ipg clk, err=%d\n",
ret);
return ret;
}
ret = clk_prepare_enable(data->clk_ahb);
if (ret) {
dev_err(dev,
"Failed to prepare/enable ahb clk, err=%d\n",
ret);
clk_disable_unprepare(data->clk_ipg);
return ret;
}
ret = clk_prepare_enable(data->clk_per);
if (ret) {
dev_err(dev,
"Failed to prepare/enable per clk, err=%d\n",
ret);
clk_disable_unprepare(data->clk_ahb);
clk_disable_unprepare(data->clk_ipg);
return ret;
}
} else {
ret = clk_prepare_enable(data->clk);
if (ret) {
dev_err(dev,
"Failed to prepare/enable clk, err=%d\n",
ret);
return ret;
}
}
return ret;
}
static void imx_disable_unprepare_clks(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
if (data->need_three_clks) {
clk_disable_unprepare(data->clk_per);
clk_disable_unprepare(data->clk_ahb);
clk_disable_unprepare(data->clk_ipg);
} else {
clk_disable_unprepare(data->clk);
}
}
static int ci_hdrc_imx_probe(struct platform_device *pdev) static int ci_hdrc_imx_probe(struct platform_device *pdev)
{ {
...@@ -145,31 +247,31 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -145,31 +247,31 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
.flags = CI_HDRC_SET_NON_ZERO_TTHA, .flags = CI_HDRC_SET_NON_ZERO_TTHA,
}; };
int ret; int ret;
const struct of_device_id *of_id = const struct of_device_id *of_id;
of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
if (!of_id)
return -ENODEV;
imx_platform_flag = of_id->data;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, data);
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
if (IS_ERR(data->usbmisc_data)) if (IS_ERR(data->usbmisc_data))
return PTR_ERR(data->usbmisc_data); return PTR_ERR(data->usbmisc_data);
data->clk = devm_clk_get(&pdev->dev, NULL); ret = imx_get_clks(&pdev->dev);
if (IS_ERR(data->clk)) { if (ret)
dev_err(&pdev->dev, return ret;
"Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
return PTR_ERR(data->clk);
}
ret = clk_prepare_enable(data->clk); ret = imx_prepare_enable_clks(&pdev->dev);
if (ret) { if (ret)
dev_err(&pdev->dev,
"Failed to prepare or enable clock, err=%d\n", ret);
return ret; return ret;
}
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
if (IS_ERR(data->phy)) { if (IS_ERR(data->phy)) {
...@@ -212,8 +314,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -212,8 +314,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto disable_device; goto disable_device;
} }
platform_set_drvdata(pdev, data);
if (data->supports_runtime_pm) { if (data->supports_runtime_pm) {
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
...@@ -226,7 +326,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -226,7 +326,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device: disable_device:
ci_hdrc_remove_device(data->ci_pdev); ci_hdrc_remove_device(data->ci_pdev);
err_clk: err_clk:
clk_disable_unprepare(data->clk); imx_disable_unprepare_clks(&pdev->dev);
return ret; return ret;
} }
...@@ -240,7 +340,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) ...@@ -240,7 +340,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);
} }
ci_hdrc_remove_device(data->ci_pdev); ci_hdrc_remove_device(data->ci_pdev);
clk_disable_unprepare(data->clk); imx_disable_unprepare_clks(&pdev->dev);
return 0; return 0;
} }
...@@ -252,7 +352,7 @@ static int imx_controller_suspend(struct device *dev) ...@@ -252,7 +352,7 @@ static int imx_controller_suspend(struct device *dev)
dev_dbg(dev, "at %s\n", __func__); dev_dbg(dev, "at %s\n", __func__);
clk_disable_unprepare(data->clk); imx_disable_unprepare_clks(dev);
data->in_lpm = true; data->in_lpm = true;
return 0; return 0;
...@@ -270,7 +370,7 @@ static int imx_controller_resume(struct device *dev) ...@@ -270,7 +370,7 @@ static int imx_controller_resume(struct device *dev)
return 0; return 0;
} }
ret = clk_prepare_enable(data->clk); ret = imx_prepare_enable_clks(dev);
if (ret) if (ret)
return ret; return ret;
...@@ -285,7 +385,7 @@ static int imx_controller_resume(struct device *dev) ...@@ -285,7 +385,7 @@ static int imx_controller_resume(struct device *dev)
return 0; return 0;
clk_disable: clk_disable:
clk_disable_unprepare(data->clk); imx_disable_unprepare_clks(dev);
return ret; return ret;
} }
......
...@@ -322,8 +322,10 @@ static ssize_t ci_role_write(struct file *file, const char __user *ubuf, ...@@ -322,8 +322,10 @@ static ssize_t ci_role_write(struct file *file, const char __user *ubuf,
return -EINVAL; return -EINVAL;
pm_runtime_get_sync(ci->dev); pm_runtime_get_sync(ci->dev);
disable_irq(ci->irq);
ci_role_stop(ci); ci_role_stop(ci);
ret = ci_role_start(ci, role); ret = ci_role_start(ci, role);
enable_irq(ci->irq);
pm_runtime_put_sync(ci->dev); pm_runtime_put_sync(ci->dev);
return ret ? ret : count; return ret ? ret : count;
......
...@@ -1751,6 +1751,22 @@ static int ci_udc_start(struct usb_gadget *gadget, ...@@ -1751,6 +1751,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
return retval; return retval;
} }
static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
{
if (!ci_otg_is_fsm_mode(ci))
return;
mutex_lock(&ci->fsm.lock);
if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
ci->fsm.a_bidl_adis_tmout = 1;
ci_hdrc_otg_fsm_start(ci);
} else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
ci->fsm.protocol = PROTO_UNDEF;
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
}
mutex_unlock(&ci->fsm.lock);
}
/** /**
* ci_udc_stop: unregister a gadget driver * ci_udc_stop: unregister a gadget driver
*/ */
...@@ -1775,6 +1791,7 @@ static int ci_udc_stop(struct usb_gadget *gadget) ...@@ -1775,6 +1791,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
ci->driver = NULL; ci->driver = NULL;
spin_unlock_irqrestore(&ci->lock, flags); spin_unlock_irqrestore(&ci->lock, flags);
ci_udc_stop_for_otg_fsm(ci);
return 0; return 0;
} }
......
...@@ -500,7 +500,11 @@ static int usbmisc_imx_probe(struct platform_device *pdev) ...@@ -500,7 +500,11 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
struct imx_usbmisc *data; struct imx_usbmisc *data;
struct of_device_id *tmp_dev; const struct of_device_id *of_id;
of_id = of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
if (!of_id)
return -ENODEV;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
...@@ -513,9 +517,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev) ...@@ -513,9 +517,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
if (IS_ERR(data->base)) if (IS_ERR(data->base))
return PTR_ERR(data->base); return PTR_ERR(data->base);
tmp_dev = (struct of_device_id *) data->ops = (const struct usbmisc_ops *)of_id->data;
of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
data->ops = (const struct usbmisc_ops *)tmp_dev->data;
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
return 0; return 0;
......
...@@ -884,11 +884,11 @@ static int usblp_wwait(struct usblp *usblp, int nonblock) ...@@ -884,11 +884,11 @@ static int usblp_wwait(struct usblp *usblp, int nonblock)
add_wait_queue(&usblp->wwait, &waita); add_wait_queue(&usblp->wwait, &waita);
for (;;) { for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (mutex_lock_interruptible(&usblp->mut)) { if (mutex_lock_interruptible(&usblp->mut)) {
rc = -EINTR; rc = -EINTR;
break; break;
} }
set_current_state(TASK_INTERRUPTIBLE);
rc = usblp_wtest(usblp, nonblock); rc = usblp_wtest(usblp, nonblock);
mutex_unlock(&usblp->mut); mutex_unlock(&usblp->mut);
if (rc <= 0) if (rc <= 0)
......
...@@ -77,8 +77,7 @@ config USB_OTG_BLACKLIST_HUB ...@@ -77,8 +77,7 @@ config USB_OTG_BLACKLIST_HUB
config USB_OTG_FSM config USB_OTG_FSM
tristate "USB 2.0 OTG FSM implementation" tristate "USB 2.0 OTG FSM implementation"
depends on USB depends on USB && USB_OTG
select USB_OTG
select USB_PHY select USB_PHY
help help
Implements OTG Finite State Machine as specified in On-The-Go Implements OTG Finite State Machine as specified in On-The-Go
......
...@@ -324,12 +324,13 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) ...@@ -324,12 +324,13 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
*/ */
static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg) static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
{ {
if (hsotg->lx_state == DWC2_L2) { if (hsotg->bus_suspended) {
hsotg->flags.b.port_suspend_change = 1; hsotg->flags.b.port_suspend_change = 1;
usb_hcd_resume_root_hub(hsotg->priv); usb_hcd_resume_root_hub(hsotg->priv);
} else {
hsotg->flags.b.port_l1_change = 1;
} }
if (hsotg->lx_state == DWC2_L1)
hsotg->flags.b.port_l1_change = 1;
} }
/** /**
...@@ -1428,8 +1429,8 @@ static void dwc2_wakeup_detected(unsigned long data) ...@@ -1428,8 +1429,8 @@ static void dwc2_wakeup_detected(unsigned long data)
dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n", dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n",
dwc2_readl(hsotg->regs + HPRT0)); dwc2_readl(hsotg->regs + HPRT0));
hsotg->bus_suspended = 0;
dwc2_hcd_rem_wakeup(hsotg); dwc2_hcd_rem_wakeup(hsotg);
hsotg->bus_suspended = 0;
/* Change to L0 state */ /* Change to L0 state */
hsotg->lx_state = DWC2_L0; hsotg->lx_state = DWC2_L0;
......
...@@ -108,7 +108,8 @@ static const struct dwc2_core_params params_rk3066 = { ...@@ -108,7 +108,8 @@ static const struct dwc2_core_params params_rk3066 = {
.host_ls_low_power_phy_clk = -1, .host_ls_low_power_phy_clk = -1,
.ts_dline = -1, .ts_dline = -1,
.reload_ctl = -1, .reload_ctl = -1,
.ahbcfg = 0x7, /* INCR16 */ .ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT,
.uframe_sched = -1, .uframe_sched = -1,
.external_id_pin_ctl = -1, .external_id_pin_ctl = -1,
.hibernation = -1, .hibernation = -1,
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#define PCI_DEVICE_ID_INTEL_BSW 0x22b7 #define PCI_DEVICE_ID_INTEL_BSW 0x22b7
#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 #define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
#define PCI_DEVICE_ID_INTEL_SPTH 0xa130 #define PCI_DEVICE_ID_INTEL_SPTH 0xa130
#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
...@@ -210,6 +212,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = { ...@@ -210,6 +212,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */ { } /* Terminating Entry */
}; };
......
...@@ -2744,11 +2744,33 @@ int dwc3_gadget_init(struct dwc3 *dwc) ...@@ -2744,11 +2744,33 @@ int dwc3_gadget_init(struct dwc3 *dwc)
} }
dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.max_speed = USB_SPEED_SUPER;
dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true; dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget"; dwc->gadget.name = "dwc3-gadget";
/*
* FIXME We might be setting max_speed to <SUPER, however versions
* <2.20a of dwc3 have an issue with metastability (documented
* elsewhere in this driver) which tells us we can't set max speed to
* anything lower than SUPER.
*
* Because gadget.max_speed is only used by composite.c and function
* drivers (i.e. it won't go into dwc3's registers) we are allowing this
* to happen so we avoid sending SuperSpeed Capability descriptor
* together with our BOS descriptor as that could confuse host into
* thinking we can handle super speed.
*
* Note that, in fact, we won't even support GetBOS requests when speed
* is less than super speed because we don't have means, yet, to tell
* composite.c that we are USB 2.0 + LPM ECN.
*/
if (dwc->revision < DWC3_REVISION_220A)
dwc3_trace(trace_dwc3_gadget,
"Changing max_speed on rev %08x\n",
dwc->revision);
dwc->gadget.max_speed = dwc->maximum_speed;
/* /*
* Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
* on ep out. * on ep out.
......
...@@ -329,7 +329,7 @@ static int alloc_requests(struct usb_composite_dev *cdev, ...@@ -329,7 +329,7 @@ static int alloc_requests(struct usb_composite_dev *cdev,
for (i = 0; i < loop->qlen && result == 0; i++) { for (i = 0; i < loop->qlen && result == 0; i++) {
result = -ENOMEM; result = -ENOMEM;
in_req = usb_ep_alloc_request(loop->in_ep, GFP_KERNEL); in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC);
if (!in_req) if (!in_req)
goto fail; goto fail;
......
...@@ -1633,7 +1633,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) ...@@ -1633,7 +1633,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
spin_lock(&udc->lock); spin_lock(&udc->lock);
int_enb = usba_int_enb_get(udc); int_enb = usba_int_enb_get(udc);
status = usba_readl(udc, INT_STA) & int_enb; status = usba_readl(udc, INT_STA) & (int_enb | USBA_HIGH_SPEED);
DBG(DBG_INT, "irq, status=%#08x\n", status); DBG(DBG_INT, "irq, status=%#08x\n", status);
if (status & USBA_DET_SUSPEND) { if (status & USBA_DET_SUSPEND) {
......
...@@ -782,12 +782,15 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, ...@@ -782,12 +782,15 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
status |= USB_PORT_STAT_SUSPEND; status |= USB_PORT_STAT_SUSPEND;
} }
} }
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 &&
&& (raw_port_status & PORT_POWER) (raw_port_status & PORT_POWER)) {
&& (bus_state->suspended_ports & (1 << wIndex))) { if (bus_state->suspended_ports & (1 << wIndex)) {
bus_state->suspended_ports &= ~(1 << wIndex); bus_state->suspended_ports &= ~(1 << wIndex);
if (hcd->speed < HCD_USB3) if (hcd->speed < HCD_USB3)
bus_state->port_c_suspend |= 1 << wIndex; bus_state->port_c_suspend |= 1 << wIndex;
}
bus_state->resume_done[wIndex] = 0;
clear_bit(wIndex, &bus_state->resuming_ports);
} }
if (raw_port_status & PORT_CONNECT) { if (raw_port_status & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION; status |= USB_PORT_STAT_CONNECTION;
......
...@@ -3896,28 +3896,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -3896,28 +3896,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return ret; return ret;
} }
static int ep_ring_is_processing(struct xhci_hcd *xhci,
int slot_id, unsigned int ep_index)
{
struct xhci_virt_device *xdev;
struct xhci_ring *ep_ring;
struct xhci_ep_ctx *ep_ctx;
struct xhci_virt_ep *xep;
dma_addr_t hw_deq;
xdev = xhci->devs[slot_id];
xep = &xhci->devs[slot_id]->eps[ep_index];
ep_ring = xep->ring;
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) != EP_STATE_RUNNING)
return 0;
hw_deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
return (hw_deq !=
xhci_trb_virt_to_dma(ep_ring->enq_seg, ep_ring->enqueue));
}
/* /*
* Check transfer ring to guarantee there is enough room for the urb. * Check transfer ring to guarantee there is enough room for the urb.
* Update ISO URB start_frame and interval. * Update ISO URB start_frame and interval.
...@@ -3983,10 +3961,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -3983,10 +3961,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
} }
/* Calculate the start frame and put it in urb->start_frame. */ /* Calculate the start frame and put it in urb->start_frame. */
if (HCC_CFC(xhci->hcc_params) && if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
ep_ring_is_processing(xhci, slot_id, ep_index)) { if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
urb->start_frame = xep->next_frame_id; EP_STATE_RUNNING) {
goto skip_start_over; urb->start_frame = xep->next_frame_id;
goto skip_start_over;
}
} }
start_frame = readl(&xhci->run_regs->microframe_index); start_frame = readl(&xhci->run_regs->microframe_index);
......
...@@ -175,6 +175,16 @@ int xhci_reset(struct xhci_hcd *xhci) ...@@ -175,6 +175,16 @@ int xhci_reset(struct xhci_hcd *xhci)
command |= CMD_RESET; command |= CMD_RESET;
writel(command, &xhci->op_regs->command); writel(command, &xhci->op_regs->command);
/* Existing Intel xHCI controllers require a delay of 1 mS,
* after setting the CMD_RESET bit, and before accessing any
* HC registers. This allows the HC to complete the
* reset operation and be ready for HC register access.
* Without this delay, the subsequent HC register access,
* may result in a system hang very rarely.
*/
if (xhci->quirks & XHCI_INTEL_HOST)
udelay(1000);
ret = xhci_handshake(&xhci->op_regs->command, ret = xhci_handshake(&xhci->op_regs->command,
CMD_RESET, 0, 10 * 1000 * 1000); CMD_RESET, 0, 10 * 1000 * 1000);
if (ret) if (ret)
......
...@@ -132,7 +132,7 @@ static inline struct musb *dev_to_musb(struct device *dev) ...@@ -132,7 +132,7 @@ static inline struct musb *dev_to_musb(struct device *dev)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#ifndef CONFIG_BLACKFIN #ifndef CONFIG_BLACKFIN
static int musb_ulpi_read(struct usb_phy *phy, u32 offset) static int musb_ulpi_read(struct usb_phy *phy, u32 reg)
{ {
void __iomem *addr = phy->io_priv; void __iomem *addr = phy->io_priv;
int i = 0; int i = 0;
...@@ -151,7 +151,7 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset) ...@@ -151,7 +151,7 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset)
* ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM. * ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM.
*/ */
musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)reg);
musb_writeb(addr, MUSB_ULPI_REG_CONTROL, musb_writeb(addr, MUSB_ULPI_REG_CONTROL,
MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR); MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR);
...@@ -176,7 +176,7 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset) ...@@ -176,7 +176,7 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset)
return ret; return ret;
} }
static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) static int musb_ulpi_write(struct usb_phy *phy, u32 val, u32 reg)
{ {
void __iomem *addr = phy->io_priv; void __iomem *addr = phy->io_priv;
int i = 0; int i = 0;
...@@ -191,8 +191,8 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) ...@@ -191,8 +191,8 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data)
power &= ~MUSB_POWER_SUSPENDM; power &= ~MUSB_POWER_SUSPENDM;
musb_writeb(addr, MUSB_POWER, power); musb_writeb(addr, MUSB_POWER, power);
musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)reg);
musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)data); musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)val);
musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ); musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ);
while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL)
...@@ -1668,7 +1668,7 @@ EXPORT_SYMBOL_GPL(musb_interrupt); ...@@ -1668,7 +1668,7 @@ EXPORT_SYMBOL_GPL(musb_interrupt);
static bool use_dma = 1; static bool use_dma = 1;
/* "modprobe ... use_dma=0" etc */ /* "modprobe ... use_dma=0" etc */
module_param(use_dma, bool, 0); module_param(use_dma, bool, 0644);
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
......
...@@ -112,22 +112,32 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) ...@@ -112,22 +112,32 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
struct musb *musb = ep->musb; struct musb *musb = ep->musb;
void __iomem *epio = ep->regs; void __iomem *epio = ep->regs;
u16 csr; u16 csr;
u16 lastcsr = 0;
int retries = 1000; int retries = 1000;
csr = musb_readw(epio, MUSB_TXCSR); csr = musb_readw(epio, MUSB_TXCSR);
while (csr & MUSB_TXCSR_FIFONOTEMPTY) { while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
if (csr != lastcsr)
dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
lastcsr = csr;
csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY; csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY;
musb_writew(epio, MUSB_TXCSR, csr); musb_writew(epio, MUSB_TXCSR, csr);
csr = musb_readw(epio, MUSB_TXCSR); csr = musb_readw(epio, MUSB_TXCSR);
if (WARN(retries-- < 1,
/*
* FIXME: sometimes the tx fifo flush failed, it has been
* observed during device disconnect on AM335x.
*
* To reproduce the issue, ensure tx urb(s) are queued when
* unplug the usb device which is connected to AM335x usb
* host port.
*
* I found using a usb-ethernet device and running iperf
* (client on AM335x) has very high chance to trigger it.
*
* Better to turn on dev_dbg() in musb_cleanup_urb() with
* CPPI enabled to see the issue when aborting the tx channel.
*/
if (dev_WARN_ONCE(musb->controller, retries-- < 1,
"Could not flush host TX%d fifo: csr: %04x\n", "Could not flush host TX%d fifo: csr: %04x\n",
ep->epnum, csr)) ep->epnum, csr))
return; return;
mdelay(1);
} }
} }
......
...@@ -21,7 +21,6 @@ config AB8500_USB ...@@ -21,7 +21,6 @@ config AB8500_USB
config FSL_USB2_OTG config FSL_USB2_OTG
bool "Freescale USB OTG Transceiver Driver" bool "Freescale USB OTG Transceiver Driver"
depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
select USB_OTG
select USB_PHY select USB_PHY
help help
Enable this to support Freescale USB OTG transceiver. Enable this to support Freescale USB OTG transceiver.
...@@ -168,8 +167,7 @@ config USB_QCOM_8X16_PHY ...@@ -168,8 +167,7 @@ config USB_QCOM_8X16_PHY
config USB_MV_OTG config USB_MV_OTG
tristate "Marvell USB OTG support" tristate "Marvell USB OTG support"
depends on USB_EHCI_MV && USB_MV_UDC && PM depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG
select USB_OTG
select USB_PHY select USB_PHY
help help
Say Y here if you want to build Marvell USB OTG transciever Say Y here if you want to build Marvell USB OTG transciever
......
...@@ -452,10 +452,13 @@ static int mxs_phy_probe(struct platform_device *pdev) ...@@ -452,10 +452,13 @@ static int mxs_phy_probe(struct platform_device *pdev)
struct clk *clk; struct clk *clk;
struct mxs_phy *mxs_phy; struct mxs_phy *mxs_phy;
int ret; int ret;
const struct of_device_id *of_id = const struct of_device_id *of_id;
of_match_device(mxs_phy_dt_ids, &pdev->dev);
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev);
if (!of_id)
return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res); base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) if (IS_ERR(base))
......
...@@ -105,7 +105,6 @@ static int omap_otg_probe(struct platform_device *pdev) ...@@ -105,7 +105,6 @@ static int omap_otg_probe(struct platform_device *pdev)
extcon = extcon_get_extcon_dev(config->extcon); extcon = extcon_get_extcon_dev(config->extcon);
if (!extcon) if (!extcon)
return -EPROBE_DEFER; return -EPROBE_DEFER;
otg_dev->extcon = extcon;
otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL); otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
if (!otg_dev) if (!otg_dev)
...@@ -115,6 +114,7 @@ static int omap_otg_probe(struct platform_device *pdev) ...@@ -115,6 +114,7 @@ static int omap_otg_probe(struct platform_device *pdev)
if (IS_ERR(otg_dev->base)) if (IS_ERR(otg_dev->base))
return PTR_ERR(otg_dev->base); return PTR_ERR(otg_dev->base);
otg_dev->extcon = extcon;
otg_dev->id_nb.notifier_call = omap_otg_id_notifier; otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier; otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
......
...@@ -161,6 +161,7 @@ static void option_instat_callback(struct urb *urb); ...@@ -161,6 +161,7 @@ static void option_instat_callback(struct urb *urb);
#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED 0x9001 #define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED 0x9001
#define NOVATELWIRELESS_PRODUCT_E362 0x9010 #define NOVATELWIRELESS_PRODUCT_E362 0x9010
#define NOVATELWIRELESS_PRODUCT_E371 0x9011 #define NOVATELWIRELESS_PRODUCT_E371 0x9011
#define NOVATELWIRELESS_PRODUCT_U620L 0x9022
#define NOVATELWIRELESS_PRODUCT_G2 0xA010 #define NOVATELWIRELESS_PRODUCT_G2 0xA010
#define NOVATELWIRELESS_PRODUCT_MC551 0xB001 #define NOVATELWIRELESS_PRODUCT_MC551 0xB001
...@@ -354,6 +355,7 @@ static void option_instat_callback(struct urb *urb); ...@@ -354,6 +355,7 @@ static void option_instat_callback(struct urb *urb);
/* This is the 4G XS Stick W14 a.k.a. Mobilcom Debitel Surf-Stick * /* This is the 4G XS Stick W14 a.k.a. Mobilcom Debitel Surf-Stick *
* It seems to contain a Qualcomm QSC6240/6290 chipset */ * It seems to contain a Qualcomm QSC6240/6290 chipset */
#define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603 #define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603
#define FOUR_G_SYSTEMS_PRODUCT_W100 0x9b01
/* iBall 3.5G connect wireless modem */ /* iBall 3.5G connect wireless modem */
#define IBALL_3_5G_CONNECT 0x9605 #define IBALL_3_5G_CONNECT 0x9605
...@@ -519,6 +521,11 @@ static const struct option_blacklist_info four_g_w14_blacklist = { ...@@ -519,6 +521,11 @@ static const struct option_blacklist_info four_g_w14_blacklist = {
.sendsetup = BIT(0) | BIT(1), .sendsetup = BIT(0) | BIT(1),
}; };
static const struct option_blacklist_info four_g_w100_blacklist = {
.sendsetup = BIT(1) | BIT(2),
.reserved = BIT(3),
};
static const struct option_blacklist_info alcatel_x200_blacklist = { static const struct option_blacklist_info alcatel_x200_blacklist = {
.sendsetup = BIT(0) | BIT(1), .sendsetup = BIT(0) | BIT(1),
.reserved = BIT(4), .reserved = BIT(4),
...@@ -1052,6 +1059,7 @@ static const struct usb_device_id option_ids[] = { ...@@ -1052,6 +1059,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC551, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC551, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E362, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E362, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E371, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E371, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U620L, 0xff, 0x00, 0x00) },
{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) },
{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) },
...@@ -1641,6 +1649,9 @@ static const struct usb_device_id option_ids[] = { ...@@ -1641,6 +1649,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14), { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14),
.driver_info = (kernel_ulong_t)&four_g_w14_blacklist .driver_info = (kernel_ulong_t)&four_g_w14_blacklist
}, },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100),
.driver_info = (kernel_ulong_t)&four_g_w100_blacklist
},
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) },
{ USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) },
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#define DRIVER_AUTHOR "Qualcomm Inc" #define DRIVER_AUTHOR "Qualcomm Inc"
#define DRIVER_DESC "Qualcomm USB Serial driver" #define DRIVER_DESC "Qualcomm USB Serial driver"
#define QUECTEL_EC20_PID 0x9215
/* standard device layouts supported by this driver */ /* standard device layouts supported by this driver */
enum qcserial_layouts { enum qcserial_layouts {
QCSERIAL_G2K = 0, /* Gobi 2000 */ QCSERIAL_G2K = 0, /* Gobi 2000 */
...@@ -171,6 +173,38 @@ static const struct usb_device_id id_table[] = { ...@@ -171,6 +173,38 @@ static const struct usb_device_id id_table[] = {
}; };
MODULE_DEVICE_TABLE(usb, id_table); MODULE_DEVICE_TABLE(usb, id_table);
static int handle_quectel_ec20(struct device *dev, int ifnum)
{
int altsetting = 0;
/*
* Quectel EC20 Mini PCIe LTE module layout:
* 0: DM/DIAG (use libqcdm from ModemManager for communication)
* 1: NMEA
* 2: AT-capable modem port
* 3: Modem interface
* 4: NDIS
*/
switch (ifnum) {
case 0:
dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n");
break;
case 1:
dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n");
break;
case 2:
case 3:
dev_dbg(dev, "Quectel EC20 Modem port found\n");
break;
case 4:
/* Don't claim the QMI/net interface */
altsetting = -1;
break;
}
return altsetting;
}
static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
{ {
struct usb_host_interface *intf = serial->interface->cur_altsetting; struct usb_host_interface *intf = serial->interface->cur_altsetting;
...@@ -181,6 +215,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) ...@@ -181,6 +215,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
int altsetting = -1; int altsetting = -1;
bool sendsetup = false; bool sendsetup = false;
/* we only support vendor specific functions */
if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
goto done;
nintf = serial->dev->actconfig->desc.bNumInterfaces; nintf = serial->dev->actconfig->desc.bNumInterfaces;
dev_dbg(dev, "Num Interfaces = %d\n", nintf); dev_dbg(dev, "Num Interfaces = %d\n", nintf);
ifnum = intf->desc.bInterfaceNumber; ifnum = intf->desc.bInterfaceNumber;
...@@ -240,6 +278,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) ...@@ -240,6 +278,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
altsetting = -1; altsetting = -1;
break; break;
case QCSERIAL_G2K: case QCSERIAL_G2K:
/* handle non-standard layouts */
if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) {
altsetting = handle_quectel_ec20(dev, ifnum);
goto done;
}
/* /*
* Gobi 2K+ USB layout: * Gobi 2K+ USB layout:
* 0: QMI/net * 0: QMI/net
...@@ -301,29 +345,39 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) ...@@ -301,29 +345,39 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
break; break;
case QCSERIAL_HWI: case QCSERIAL_HWI:
/* /*
* Huawei layout: * Huawei devices map functions by subclass + protocol
* 0: AT-capable modem port * instead of interface numbers. The protocol identify
* 1: DM/DIAG * a specific function, while the subclass indicate a
* 2: AT-capable modem port * specific firmware source
* 3: CCID-compatible PCSC interface *
* 4: QMI/net * This is a blacklist of functions known to be
* 5: NMEA * non-serial. The rest are assumed to be serial and
* will be handled by this driver
*/ */
switch (ifnum) { switch (intf->desc.bInterfaceProtocol) {
case 0: /* QMI combined (qmi_wwan) */
case 2: case 0x07:
dev_dbg(dev, "Modem port found\n"); case 0x37:
break; case 0x67:
case 1: /* QMI data (qmi_wwan) */
dev_dbg(dev, "DM/DIAG interface found\n"); case 0x08:
break; case 0x38:
case 5: case 0x68:
dev_dbg(dev, "NMEA GPS interface found\n"); /* QMI control (qmi_wwan) */
break; case 0x09:
default: case 0x39:
/* don't claim any unsupported interface */ case 0x69:
/* NCM like (huawei_cdc_ncm) */
case 0x16:
case 0x46:
case 0x76:
altsetting = -1; altsetting = -1;
break; break;
default:
dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n",
intf->desc.bInterfaceClass,
intf->desc.bInterfaceSubClass,
intf->desc.bInterfaceProtocol);
} }
break; break;
default: default:
......
...@@ -159,6 +159,7 @@ static const struct usb_device_id ti_id_table_3410[] = { ...@@ -159,6 +159,7 @@ static const struct usb_device_id ti_id_table_3410[] = {
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STEREO_PLUG_ID) }, { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STEREO_PLUG_ID) },
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) }, { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
{ } /* terminator */ { } /* terminator */
}; };
...@@ -191,6 +192,7 @@ static const struct usb_device_id ti_id_table_combined[] = { ...@@ -191,6 +192,7 @@ static const struct usb_device_id ti_id_table_combined[] = {
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) }, { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) }, { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
{ } /* terminator */ { } /* terminator */
}; };
......
...@@ -56,6 +56,10 @@ ...@@ -56,6 +56,10 @@
#define ABBOTT_PRODUCT_ID ABBOTT_STEREO_PLUG_ID #define ABBOTT_PRODUCT_ID ABBOTT_STEREO_PLUG_ID
#define ABBOTT_STRIP_PORT_ID 0x3420 #define ABBOTT_STRIP_PORT_ID 0x3420
/* Honeywell vendor and product IDs */
#define HONEYWELL_VENDOR_ID 0x10ac
#define HONEYWELL_HGI80_PRODUCT_ID 0x0102 /* Honeywell HGI80 */
/* Commands */ /* Commands */
#define TI_GET_VERSION 0x01 #define TI_GET_VERSION 0x01
#define TI_GET_PORT_STATUS 0x02 #define TI_GET_PORT_STATUS 0x02
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment