forked from Minki/linux
USB update for 4.8-rc1
Here's the big USB driver update for 4.8-rc1. Lots of the normal stuff in here, musb, gadget, xhci, and other updates and fixes. All of the details are in the shortlog. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iFYEABECABYFAleVPioPHGdyZWdAa3JvYWguY29tAAoJEDFH1A3bLfspB5AAnj7a VJ2t2kcWzFUNQ6dyJrJCGGRAAKDZmb5CnOGeqJmdVpDzN1CGLYjfiw== =47iA -----END PGP SIGNATURE----- Merge tag 'usb-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here's the big USB driver update for 4.8-rc1. Lots of the normal stuff in here, musb, gadget, xhci, and other updates and fixes. All of the details are in the shortlog. All of these have been in linux-next for a while with no reported issues" * tag 'usb-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (169 commits) cdc-acm: beautify probe() cdc-wdm: use the common CDC parser cdc-acm: cleanup error handling cdc-acm: use the common parser usbnet: move the CDC parser into USB core usb: musb: sunxi: Simplify dr_mode handling usb: musb: sunxi: make unexported symbols static usb: musb: cppi41: add dma channel tracepoints usb: musb: cppi41: move struct cppi41_dma_channel to header usb: musb: cleanup cppi_dma header usb: musb: gadget: add usb-request tracepoints usb: musb: host: add urb tracepoints usb: musb: add tracepoints to dump interrupt events usb: musb: add tracepoints for register access usb: musb: dsps: use musb register read/write wrappers instead usb: musb: switch dev_dbg to tracepoints usb: musb: add tracepoints support for debugging usb: quirks: Add no-lpm quirk for Elan phy: rcar-gen3-usb2: fix mutex_lock calling in interrupt phy: rockhip-usb: use devm_add_action_or_reset() ...
This commit is contained in:
commit
b7545b79a1
40
Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt
Normal file
40
Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt
Normal file
@ -0,0 +1,40 @@
|
||||
TI DA8xx/OMAP-L1xx/AM18xx USB PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "ti,da830-usb-phy".
|
||||
- #phy-cells: must be 1.
|
||||
|
||||
This device controls the PHY for both the USB 1.1 OHCI and USB 2.0 OTG
|
||||
controllers on DA8xx SoCs. Consumers of this device should use index 0 for
|
||||
the USB 2.0 phy device and index 1 for the USB 1.1 phy device.
|
||||
|
||||
It also requires a "syscon" node with compatible = "ti,da830-cfgchip", "syscon"
|
||||
to access the CFGCHIP2 register.
|
||||
|
||||
Example:
|
||||
|
||||
cfgchip: cfgchip@1417c {
|
||||
compatible = "ti,da830-cfgchip", "syscon";
|
||||
reg = <0x1417c 0x14>;
|
||||
};
|
||||
|
||||
usb_phy: usb-phy {
|
||||
compatible = "ti,da830-usb-phy";
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
usb20: usb@200000 {
|
||||
compatible = "ti,da830-musb";
|
||||
reg = <0x200000 0x1000>;
|
||||
interrupts = <58>;
|
||||
phys = <&usb_phy 0>;
|
||||
phy-names = "usb-phy";
|
||||
};
|
||||
|
||||
usb11: usb@225000 {
|
||||
compatible = "ti,da830-ohci";
|
||||
reg = <0x225000 0x1000>;
|
||||
interrupts = <59>;
|
||||
phys = <&usb_phy 1>;
|
||||
phy-names = "usb-phy";
|
||||
};
|
@ -5,11 +5,13 @@ Required properties:
|
||||
"rockchip,rk3066a-usb-phy"
|
||||
"rockchip,rk3188-usb-phy"
|
||||
"rockchip,rk3288-usb-phy"
|
||||
- rockchip,grf : phandle to the syscon managing the "general
|
||||
register files"
|
||||
- #address-cells: should be 1
|
||||
- #size-cells: should be 0
|
||||
|
||||
Deprecated properties:
|
||||
- rockchip,grf : phandle to the syscon managing the "general
|
||||
register files" - phy should be a child of the GRF instead
|
||||
|
||||
Sub-nodes:
|
||||
Each PHY should be represented as a sub-node.
|
||||
|
||||
@ -28,14 +30,19 @@ Optional Properties:
|
||||
|
||||
Example:
|
||||
|
||||
usbphy: phy {
|
||||
compatible = "rockchip,rk3288-usb-phy";
|
||||
rockchip,grf = <&grf>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
grf: syscon@ff770000 {
|
||||
compatible = "rockchip,rk3288-grf", "syscon", "simple-mfd";
|
||||
|
||||
usbphy0: usb-phy0 {
|
||||
#phy-cells = <0>;
|
||||
reg = <0x320>;
|
||||
...
|
||||
|
||||
usbphy: phy {
|
||||
compatible = "rockchip,rk3288-usb-phy";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usbphy0: usb-phy0 {
|
||||
#phy-cells = <0>;
|
||||
reg = <0x320>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -93,7 +93,7 @@ Example:
|
||||
phys = <&usb_phy0>;
|
||||
phy-names = "usb-phy";
|
||||
vbus-supply = <®_usb0_vbus>;
|
||||
gadget-itc-setting = <0x4>; /* 4 micro-frames */
|
||||
itc-setting = <0x4>; /* 4 micro-frames */
|
||||
/* Incremental burst of unspecified length */
|
||||
ahb-burst-config = <0x0>;
|
||||
tx-burst-size-dword = <0x10>; /* 64 bytes */
|
||||
|
@ -14,7 +14,7 @@ Optional properties:
|
||||
- clocks : a list of phandle + clock specifier pairs
|
||||
- phys : phandle + phy specifier pair
|
||||
- phy-names : "usb"
|
||||
- resets : phandle + reset specifier pair
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -232,7 +232,7 @@
|
||||
};
|
||||
|
||||
usb1: ohci@00400000 {
|
||||
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
|
||||
compatible = "atmel,sama5d2-ohci", "usb-ohci";
|
||||
reg = <0x00400000 0x100000>;
|
||||
interrupts = <41 IRQ_TYPE_LEVEL_HIGH 2>;
|
||||
clocks = <&uhphs_clk>, <&uhphs_clk>, <&uhpck>;
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/usbnet.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -1972,143 +1971,6 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
|
||||
struct usb_interface *intf,
|
||||
u8 *buffer,
|
||||
int buflen)
|
||||
{
|
||||
/* duplicates are ignored */
|
||||
struct usb_cdc_union_desc *union_header = NULL;
|
||||
|
||||
/* duplicates are not tolerated */
|
||||
struct usb_cdc_header_desc *header = NULL;
|
||||
struct usb_cdc_ether_desc *ether = NULL;
|
||||
struct usb_cdc_mdlm_detail_desc *detail = NULL;
|
||||
struct usb_cdc_mdlm_desc *desc = NULL;
|
||||
|
||||
unsigned int elength;
|
||||
int cnt = 0;
|
||||
|
||||
memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
|
||||
hdr->phonet_magic_present = false;
|
||||
while (buflen > 0) {
|
||||
elength = buffer[0];
|
||||
if (!elength) {
|
||||
dev_err(&intf->dev, "skipping garbage byte\n");
|
||||
elength = 1;
|
||||
goto next_desc;
|
||||
}
|
||||
if (buffer[1] != USB_DT_CS_INTERFACE) {
|
||||
dev_err(&intf->dev, "skipping garbage\n");
|
||||
goto next_desc;
|
||||
}
|
||||
|
||||
switch (buffer[2]) {
|
||||
case USB_CDC_UNION_TYPE: /* we've found it */
|
||||
if (elength < sizeof(struct usb_cdc_union_desc))
|
||||
goto next_desc;
|
||||
if (union_header) {
|
||||
dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
|
||||
goto next_desc;
|
||||
}
|
||||
union_header = (struct usb_cdc_union_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_COUNTRY_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_country_functional_desc))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_country_functional_desc =
|
||||
(struct usb_cdc_country_functional_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (elength != sizeof(struct usb_cdc_header_desc))
|
||||
goto next_desc;
|
||||
if (header)
|
||||
return -EINVAL;
|
||||
header = (struct usb_cdc_header_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_ACM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_acm_descriptor))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_acm_descriptor =
|
||||
(struct usb_cdc_acm_descriptor *)buffer;
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (elength != sizeof(struct usb_cdc_ether_desc))
|
||||
goto next_desc;
|
||||
if (ether)
|
||||
return -EINVAL;
|
||||
ether = (struct usb_cdc_ether_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_CALL_MANAGEMENT_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_call_mgmt_descriptor =
|
||||
(struct usb_cdc_call_mgmt_descriptor *)buffer;
|
||||
break;
|
||||
case USB_CDC_DMM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_dmm_desc))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_dmm_desc =
|
||||
(struct usb_cdc_dmm_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MDLM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mdlm_desc *))
|
||||
goto next_desc;
|
||||
if (desc)
|
||||
return -EINVAL;
|
||||
desc = (struct usb_cdc_mdlm_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MDLM_DETAIL_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
|
||||
goto next_desc;
|
||||
if (detail)
|
||||
return -EINVAL;
|
||||
detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_NCM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_ncm_desc))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MBIM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mbim_desc))
|
||||
goto next_desc;
|
||||
|
||||
hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MBIM_EXTENDED_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
|
||||
break;
|
||||
hdr->usb_cdc_mbim_extended_desc =
|
||||
(struct usb_cdc_mbim_extended_desc *)buffer;
|
||||
break;
|
||||
case CDC_PHONET_MAGIC_NUMBER:
|
||||
hdr->phonet_magic_present = true;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* there are LOTS more CDC descriptors that
|
||||
* could legitimately be found here.
|
||||
*/
|
||||
dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
|
||||
buffer[2], elength);
|
||||
goto next_desc;
|
||||
}
|
||||
cnt++;
|
||||
next_desc:
|
||||
buflen -= elength;
|
||||
buffer += elength;
|
||||
}
|
||||
hdr->usb_cdc_union_desc = union_header;
|
||||
hdr->usb_cdc_header_desc = header;
|
||||
hdr->usb_cdc_mdlm_detail_desc = detail;
|
||||
hdr->usb_cdc_mdlm_desc = desc;
|
||||
hdr->usb_cdc_ether_desc = ether;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(cdc_parse_cdc_header);
|
||||
|
||||
/*
|
||||
* The function can't be called inside suspend/resume callback,
|
||||
* otherwise deadlock will be caused.
|
||||
|
@ -44,6 +44,16 @@ config ARMADA375_USBCLUSTER_PHY
|
||||
depends on OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_DA8XX_USB
|
||||
tristate "TI DA8xx USB PHY Driver"
|
||||
depends on ARCH_DAVINCI_DA8XX
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enable this to support the USB PHY on DA8xx SoCs.
|
||||
|
||||
This driver controls both the USB 1.1 PHY and the USB 2.0 PHY.
|
||||
|
||||
config PHY_DM816X_USB
|
||||
tristate "TI dm816x USB PHY driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
@ -176,6 +186,7 @@ config TWL4030_USB
|
||||
tristate "TWL4030 USB Transceiver Driver"
|
||||
depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
|
||||
depends on USB_SUPPORT
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y'
|
||||
select GENERIC_PHY
|
||||
select USB_PHY
|
||||
help
|
||||
|
@ -6,6 +6,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
|
||||
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
|
||||
obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
|
||||
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
|
||||
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
|
||||
|
@ -342,6 +342,21 @@ int phy_power_off(struct phy *phy)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_power_off);
|
||||
|
||||
int phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!phy || !phy->ops->set_mode)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->set_mode(phy, mode);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_set_mode);
|
||||
|
||||
/**
|
||||
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
|
||||
* @np: device_node for which to get the phy
|
||||
|
245
drivers/phy/phy-da8xx-usb.c
Normal file
245
drivers/phy/phy-da8xx-usb.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver
|
||||
*
|
||||
* Copyright (C) 2016 David Lechner <david@lechnology.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/da8xx-cfgchip.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct da8xx_usb_phy {
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy *usb11_phy;
|
||||
struct phy *usb20_phy;
|
||||
struct clk *usb11_clk;
|
||||
struct clk *usb20_clk;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int da8xx_usb11_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(d_phy->usb11_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,
|
||||
CFGCHIP2_USB1SUSPENDM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_usb11_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
|
||||
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);
|
||||
|
||||
clk_disable_unprepare(d_phy->usb11_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops da8xx_usb11_phy_ops = {
|
||||
.power_on = da8xx_usb11_phy_power_on,
|
||||
.power_off = da8xx_usb11_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int da8xx_usb20_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(d_phy->usb20_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_usb20_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
|
||||
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN,
|
||||
CFGCHIP2_OTGPWRDN);
|
||||
|
||||
clk_disable_unprepare(d_phy->usb20_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
u32 val;
|
||||
|
||||
switch (mode) {
|
||||
case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */
|
||||
val = CFGCHIP2_OTGMODE_FORCE_HOST;
|
||||
break;
|
||||
case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */
|
||||
val = CFGCHIP2_OTGMODE_FORCE_DEVICE;
|
||||
break;
|
||||
case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */
|
||||
val = CFGCHIP2_OTGMODE_NO_OVERRIDE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK,
|
||||
val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops da8xx_usb20_phy_ops = {
|
||||
.power_on = da8xx_usb20_phy_power_on,
|
||||
.power_off = da8xx_usb20_phy_power_off,
|
||||
.set_mode = da8xx_usb20_phy_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (!d_phy)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
switch (args->args[0]) {
|
||||
case 0:
|
||||
return d_phy->usb20_phy;
|
||||
case 1:
|
||||
return d_phy->usb11_phy;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int da8xx_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct da8xx_usb_phy *d_phy;
|
||||
|
||||
d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL);
|
||||
if (!d_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
if (node)
|
||||
d_phy->regmap = syscon_regmap_lookup_by_compatible(
|
||||
"ti,da830-cfgchip");
|
||||
else
|
||||
d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon.0");
|
||||
if (IS_ERR(d_phy->regmap)) {
|
||||
dev_err(dev, "Failed to get syscon\n");
|
||||
return PTR_ERR(d_phy->regmap);
|
||||
}
|
||||
|
||||
d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy");
|
||||
if (IS_ERR(d_phy->usb11_clk)) {
|
||||
dev_err(dev, "Failed to get usb11_phy clock\n");
|
||||
return PTR_ERR(d_phy->usb11_clk);
|
||||
}
|
||||
|
||||
d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy");
|
||||
if (IS_ERR(d_phy->usb20_clk)) {
|
||||
dev_err(dev, "Failed to get usb20_phy clock\n");
|
||||
return PTR_ERR(d_phy->usb20_clk);
|
||||
}
|
||||
|
||||
d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops);
|
||||
if (IS_ERR(d_phy->usb11_phy)) {
|
||||
dev_err(dev, "Failed to create usb11 phy\n");
|
||||
return PTR_ERR(d_phy->usb11_phy);
|
||||
}
|
||||
|
||||
d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops);
|
||||
if (IS_ERR(d_phy->usb20_phy)) {
|
||||
dev_err(dev, "Failed to create usb20 phy\n");
|
||||
return PTR_ERR(d_phy->usb20_phy);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, d_phy);
|
||||
phy_set_drvdata(d_phy->usb11_phy, d_phy);
|
||||
phy_set_drvdata(d_phy->usb20_phy, d_phy);
|
||||
|
||||
if (node) {
|
||||
d_phy->phy_provider = devm_of_phy_provider_register(dev,
|
||||
da8xx_usb_phy_of_xlate);
|
||||
if (IS_ERR(d_phy->phy_provider)) {
|
||||
dev_err(dev, "Failed to create phy provider\n");
|
||||
return PTR_ERR(d_phy->phy_provider);
|
||||
}
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0");
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to create usb11 phy lookup\n");
|
||||
ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy",
|
||||
"musb-da8xx");
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to create usb20 phy lookup\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_usb_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev);
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx");
|
||||
phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id da8xx_usb_phy_ids[] = {
|
||||
{ .compatible = "ti,da830-usb-phy" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids);
|
||||
|
||||
static struct platform_driver da8xx_usb_phy_driver = {
|
||||
.probe = da8xx_usb_phy_probe,
|
||||
.remove = da8xx_usb_phy_remove,
|
||||
.driver = {
|
||||
.name = "da8xx-usb-phy",
|
||||
.of_match_table = da8xx_usb_phy_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(da8xx_usb_phy_driver);
|
||||
|
||||
MODULE_ALIAS("platform:da8xx-usb-phy");
|
||||
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
|
||||
MODULE_DESCRIPTION("TI DA8xx USB PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -140,7 +140,6 @@ static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev)
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
dev_err(dev, "%s: failed to allocate phy\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
@ -196,7 +196,6 @@ static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev)
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
dev_err(dev, "%s: failed to allocate phy\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/******* USB2.0 Host registers (original offset is +0x200) *******/
|
||||
#define USB2_INT_ENABLE 0x000
|
||||
@ -81,9 +82,25 @@ struct rcar_gen3_chan {
|
||||
struct extcon_dev *extcon;
|
||||
struct phy *phy;
|
||||
struct regulator *vbus;
|
||||
struct work_struct work;
|
||||
bool extcon_host;
|
||||
bool has_otg;
|
||||
};
|
||||
|
||||
static void rcar_gen3_phy_usb2_work(struct work_struct *work)
|
||||
{
|
||||
struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan,
|
||||
work);
|
||||
|
||||
if (ch->extcon_host) {
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true);
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB, false);
|
||||
} else {
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false);
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
|
||||
{
|
||||
void __iomem *usb2_base = ch->base;
|
||||
@ -130,8 +147,8 @@ static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
|
||||
rcar_gen3_set_host_mode(ch, 1);
|
||||
rcar_gen3_enable_vbus_ctrl(ch, 1);
|
||||
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true);
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB, false);
|
||||
ch->extcon_host = true;
|
||||
schedule_work(&ch->work);
|
||||
}
|
||||
|
||||
static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
|
||||
@ -140,8 +157,8 @@ static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
|
||||
rcar_gen3_set_host_mode(ch, 0);
|
||||
rcar_gen3_enable_vbus_ctrl(ch, 0);
|
||||
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false);
|
||||
extcon_set_cable_state_(ch->extcon, EXTCON_USB, true);
|
||||
ch->extcon_host = false;
|
||||
schedule_work(&ch->work);
|
||||
}
|
||||
|
||||
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
|
||||
@ -301,6 +318,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
if (irq >= 0) {
|
||||
int ret;
|
||||
|
||||
INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
|
||||
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
|
||||
IRQF_SHARED, dev_name(dev), channel);
|
||||
if (irq < 0)
|
||||
|
@ -236,9 +236,10 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
|
||||
goto err_clk_prov;
|
||||
}
|
||||
|
||||
err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
|
||||
err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
|
||||
rk_phy);
|
||||
if (err)
|
||||
goto err_devm_action;
|
||||
return err;
|
||||
|
||||
rk_phy->phy = devm_phy_create(base->dev, child, &ops);
|
||||
if (IS_ERR(rk_phy->phy)) {
|
||||
@ -256,9 +257,6 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
|
||||
else
|
||||
return rockchip_usb_phy_power(rk_phy, 1);
|
||||
|
||||
err_devm_action:
|
||||
if (!rk_phy->uart_enabled)
|
||||
of_clk_del_provider(child);
|
||||
err_clk_prov:
|
||||
if (!rk_phy->uart_enabled)
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
@ -397,8 +395,13 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
|
||||
phy_base->pdata = match->data;
|
||||
|
||||
phy_base->dev = dev;
|
||||
phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,grf");
|
||||
phy_base->reg_base = ERR_PTR(-ENODEV);
|
||||
if (dev->parent && dev->parent->of_node)
|
||||
phy_base->reg_base = syscon_node_to_regmap(
|
||||
dev->parent->of_node);
|
||||
if (IS_ERR(phy_base->reg_base))
|
||||
phy_base->reg_base = syscon_regmap_lookup_by_phandle(
|
||||
dev->of_node, "rockchip,grf");
|
||||
if (IS_ERR(phy_base->reg_base)) {
|
||||
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
|
||||
return PTR_ERR(phy_base->reg_base);
|
||||
@ -463,7 +466,11 @@ static int __init rockchip_init_usb_uart(void)
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
grf = ERR_PTR(-ENODEV);
|
||||
if (np->parent)
|
||||
grf = syscon_node_to_regmap(np->parent);
|
||||
if (IS_ERR(grf))
|
||||
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
pr_err("%s: Missing rockchip,grf property, %lu\n",
|
||||
__func__, PTR_ERR(grf));
|
||||
|
@ -94,6 +94,7 @@
|
||||
|
||||
enum sun4i_usb_phy_type {
|
||||
sun4i_a10_phy,
|
||||
sun6i_a31_phy,
|
||||
sun8i_a33_phy,
|
||||
sun8i_h3_phy,
|
||||
};
|
||||
@ -122,7 +123,6 @@ struct sun4i_usb_phy_data {
|
||||
/* phy0 / otg related variables */
|
||||
struct extcon_dev *extcon;
|
||||
bool phy0_init;
|
||||
bool phy0_poll;
|
||||
struct gpio_desc *id_det_gpio;
|
||||
struct gpio_desc *vbus_det_gpio;
|
||||
struct power_supply *vbus_power_supply;
|
||||
@ -343,6 +343,24 @@ static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data)
|
||||
return data->vbus_det_gpio || data->vbus_power_supply;
|
||||
}
|
||||
|
||||
static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data)
|
||||
{
|
||||
if ((data->id_det_gpio && data->id_det_irq <= 0) ||
|
||||
(data->vbus_det_gpio && data->vbus_det_irq <= 0))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* The A31 companion pmic (axp221) does not generate vbus change
|
||||
* interrupts when the board is driving vbus, so we must poll
|
||||
* when using the pmic for vbus-det _and_ we're driving vbus.
|
||||
*/
|
||||
if (data->cfg->type == sun6i_a31_phy &&
|
||||
data->vbus_power_supply && data->phys[0].regulator_on)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_power_on(struct phy *_phy)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
@ -364,7 +382,7 @@ static int sun4i_usb_phy_power_on(struct phy *_phy)
|
||||
phy->regulator_on = true;
|
||||
|
||||
/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
|
||||
if (phy->index == 0 && data->vbus_det_gpio && data->phy0_poll)
|
||||
if (phy->index == 0 && sun4i_usb_phy0_poll(data))
|
||||
mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
|
||||
|
||||
return 0;
|
||||
@ -385,7 +403,7 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
|
||||
* phy0 vbus typically slowly discharges, sometimes this causes the
|
||||
* Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
|
||||
*/
|
||||
if (phy->index == 0 && data->vbus_det_gpio && !data->phy0_poll)
|
||||
if (phy->index == 0 && !sun4i_usb_phy0_poll(data))
|
||||
mod_delayed_work(system_wq, &data->detect, POLL_TIME);
|
||||
|
||||
return 0;
|
||||
@ -468,7 +486,7 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
|
||||
if (vbus_notify)
|
||||
extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det);
|
||||
|
||||
if (data->phy0_poll)
|
||||
if (sun4i_usb_phy0_poll(data))
|
||||
queue_delayed_work(system_wq, &data->detect, POLL_TIME);
|
||||
}
|
||||
|
||||
@ -644,11 +662,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
|
||||
data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
|
||||
if ((data->id_det_gpio && data->id_det_irq <= 0) ||
|
||||
(data->vbus_det_gpio && data->vbus_det_irq <= 0))
|
||||
data->phy0_poll = true;
|
||||
|
||||
if (data->id_det_irq > 0) {
|
||||
ret = devm_request_irq(dev, data->id_det_irq,
|
||||
sun4i_usb_phy0_id_vbus_det_irq,
|
||||
@ -660,6 +673,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
|
||||
if (data->vbus_det_irq > 0) {
|
||||
ret = devm_request_irq(dev, data->vbus_det_irq,
|
||||
sun4i_usb_phy0_id_vbus_det_irq,
|
||||
@ -711,7 +725,7 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
|
||||
.num_phys = 3,
|
||||
.type = sun4i_a10_phy,
|
||||
.type = sun6i_a31_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A10,
|
||||
.dedicated_clocks = true,
|
||||
|
@ -518,7 +518,7 @@ enum clk_type_t {
|
||||
CLK_INT_SING = 2, /* Internal single ended */
|
||||
};
|
||||
|
||||
enum phy_mode {
|
||||
enum xgene_phy_mode {
|
||||
MODE_SATA = 0, /* List them for simple reference */
|
||||
MODE_SGMII = 1,
|
||||
MODE_PCIE = 2,
|
||||
@ -542,7 +542,7 @@ struct xgene_sata_override_param {
|
||||
struct xgene_phy_ctx {
|
||||
struct device *dev;
|
||||
struct phy *phy;
|
||||
enum phy_mode mode; /* Mode of operation */
|
||||
enum xgene_phy_mode mode; /* Mode of operation */
|
||||
enum clk_type_t clk_type; /* Input clock selection */
|
||||
void __iomem *sds_base; /* PHY CSR base addr */
|
||||
struct clk *clk; /* Optional clock */
|
||||
|
@ -309,6 +309,7 @@ config BATTERY_RX51
|
||||
config CHARGER_ISP1704
|
||||
tristate "ISP1704 USB Charger Detection"
|
||||
depends on USB_PHY
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
help
|
||||
Say Y to enable support for USB Charger Detection with
|
||||
ISP1707/ISP1704 USB transceivers.
|
||||
|
@ -4,8 +4,9 @@ config USB_CHIPIDEA
|
||||
select EXTCON
|
||||
help
|
||||
Say Y here if your system has a dual role high speed USB
|
||||
controller based on ChipIdea silicon IP. Currently, only the
|
||||
peripheral mode is supported.
|
||||
controller based on ChipIdea silicon IP. It supports:
|
||||
Dual-role switch (ID, OTG FSM, sysfs), Host-only, and
|
||||
Peripheral-only.
|
||||
|
||||
When compiled dynamically, the module will be called ci-hdrc.ko.
|
||||
|
||||
|
@ -946,7 +946,7 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct async_icount old, new;
|
||||
|
||||
if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
|
||||
if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))
|
||||
return -EINVAL;
|
||||
do {
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
@ -1146,7 +1146,7 @@ static int acm_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_cdc_union_desc *union_header = NULL;
|
||||
struct usb_cdc_country_functional_desc *cfd = NULL;
|
||||
struct usb_cdc_call_mgmt_descriptor *cmgmd = NULL;
|
||||
unsigned char *buffer = intf->altsetting->extra;
|
||||
int buflen = intf->altsetting->extralen;
|
||||
struct usb_interface *control_interface;
|
||||
@ -1155,18 +1155,16 @@ static int acm_probe(struct usb_interface *intf,
|
||||
struct usb_endpoint_descriptor *epread = NULL;
|
||||
struct usb_endpoint_descriptor *epwrite = NULL;
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct usb_cdc_parsed_header h;
|
||||
struct acm *acm;
|
||||
int minor;
|
||||
int ctrlsize, readsize;
|
||||
u8 *buf;
|
||||
u8 ac_management_function = 0;
|
||||
u8 call_management_function = 0;
|
||||
int call_interface_num = -1;
|
||||
int data_interface_num = -1;
|
||||
int call_intf_num = -1;
|
||||
int data_intf_num = -1;
|
||||
unsigned long quirks;
|
||||
int num_rx_buf;
|
||||
int i;
|
||||
unsigned int elength = 0;
|
||||
int combined_interfaces = 0;
|
||||
struct device *tty_dev;
|
||||
int rv = -ENOMEM;
|
||||
@ -1210,70 +1208,22 @@ static int acm_probe(struct usb_interface *intf,
|
||||
}
|
||||
}
|
||||
|
||||
while (buflen > 0) {
|
||||
elength = buffer[0];
|
||||
if (!elength) {
|
||||
dev_err(&intf->dev, "skipping garbage byte\n");
|
||||
elength = 1;
|
||||
goto next_desc;
|
||||
}
|
||||
if (buffer[1] != USB_DT_CS_INTERFACE) {
|
||||
dev_err(&intf->dev, "skipping garbage\n");
|
||||
goto next_desc;
|
||||
}
|
||||
|
||||
switch (buffer[2]) {
|
||||
case USB_CDC_UNION_TYPE: /* we've found it */
|
||||
if (elength < sizeof(struct usb_cdc_union_desc))
|
||||
goto next_desc;
|
||||
if (union_header) {
|
||||
dev_err(&intf->dev, "More than one "
|
||||
"union descriptor, skipping ...\n");
|
||||
goto next_desc;
|
||||
}
|
||||
union_header = (struct usb_cdc_union_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
|
||||
if (elength < sizeof(struct usb_cdc_country_functional_desc))
|
||||
goto next_desc;
|
||||
cfd = (struct usb_cdc_country_functional_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_HEADER_TYPE: /* maybe check version */
|
||||
break; /* for now we ignore it */
|
||||
case USB_CDC_ACM_TYPE:
|
||||
if (elength < 4)
|
||||
goto next_desc;
|
||||
ac_management_function = buffer[3];
|
||||
break;
|
||||
case USB_CDC_CALL_MANAGEMENT_TYPE:
|
||||
if (elength < 5)
|
||||
goto next_desc;
|
||||
call_management_function = buffer[3];
|
||||
call_interface_num = buffer[4];
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* there are LOTS more CDC descriptors that
|
||||
* could legitimately be found here.
|
||||
*/
|
||||
dev_dbg(&intf->dev, "Ignoring descriptor: "
|
||||
"type %02x, length %ud\n",
|
||||
buffer[2], elength);
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
buflen -= elength;
|
||||
buffer += elength;
|
||||
}
|
||||
cdc_parse_cdc_header(&h, intf, buffer, buflen);
|
||||
union_header = h.usb_cdc_union_desc;
|
||||
cmgmd = h.usb_cdc_call_mgmt_descriptor;
|
||||
if (cmgmd)
|
||||
call_intf_num = cmgmd->bDataInterface;
|
||||
|
||||
if (!union_header) {
|
||||
if (call_interface_num > 0) {
|
||||
if (call_intf_num > 0) {
|
||||
dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
|
||||
/* quirks for Droids MuIn LCD */
|
||||
if (quirks & NO_DATA_INTERFACE)
|
||||
if (quirks & NO_DATA_INTERFACE) {
|
||||
data_interface = usb_ifnum_to_if(usb_dev, 0);
|
||||
else
|
||||
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
|
||||
} else {
|
||||
data_intf_num = call_intf_num;
|
||||
data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
|
||||
}
|
||||
control_interface = intf;
|
||||
} else {
|
||||
if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
|
||||
@ -1287,8 +1237,9 @@ next_desc:
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data_intf_num = union_header->bSlaveInterface0;
|
||||
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
|
||||
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
|
||||
data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
|
||||
}
|
||||
|
||||
if (!control_interface || !data_interface) {
|
||||
@ -1296,7 +1247,7 @@ next_desc:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (data_interface_num != call_interface_num)
|
||||
if (data_intf_num != call_intf_num)
|
||||
dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
|
||||
|
||||
if (control_interface == data_interface) {
|
||||
@ -1379,11 +1330,8 @@ made_compressed_probe:
|
||||
goto alloc_fail;
|
||||
|
||||
minor = acm_alloc_minor(acm);
|
||||
if (minor < 0) {
|
||||
dev_err(&intf->dev, "no more free acm devices\n");
|
||||
kfree(acm);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (minor < 0)
|
||||
goto alloc_fail1;
|
||||
|
||||
ctrlsize = usb_endpoint_maxp(epctrl);
|
||||
readsize = usb_endpoint_maxp(epread) *
|
||||
@ -1394,7 +1342,8 @@ made_compressed_probe:
|
||||
acm->data = data_interface;
|
||||
acm->minor = minor;
|
||||
acm->dev = usb_dev;
|
||||
acm->ctrl_caps = ac_management_function;
|
||||
if (h.usb_cdc_acm_descriptor)
|
||||
acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
|
||||
if (quirks & NO_CAP_LINE)
|
||||
acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
|
||||
acm->ctrlsize = ctrlsize;
|
||||
@ -1488,7 +1437,10 @@ made_compressed_probe:
|
||||
if (i < 0)
|
||||
goto alloc_fail7;
|
||||
|
||||
if (cfd) { /* export the country data */
|
||||
if (h.usb_cdc_country_functional_desc) { /* export the country data */
|
||||
struct usb_cdc_country_functional_desc * cfd =
|
||||
h.usb_cdc_country_functional_desc;
|
||||
|
||||
acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
|
||||
if (!acm->country_codes)
|
||||
goto skip_countries;
|
||||
@ -1572,6 +1524,7 @@ alloc_fail4:
|
||||
usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
|
||||
alloc_fail2:
|
||||
acm_release_minor(acm);
|
||||
alloc_fail1:
|
||||
kfree(acm);
|
||||
alloc_fail:
|
||||
return rv;
|
||||
|
@ -875,38 +875,18 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
int rv = -EINVAL;
|
||||
struct usb_host_interface *iface;
|
||||
struct usb_endpoint_descriptor *ep;
|
||||
struct usb_cdc_dmm_desc *dmhd;
|
||||
struct usb_cdc_parsed_header hdr;
|
||||
u8 *buffer = intf->altsetting->extra;
|
||||
int buflen = intf->altsetting->extralen;
|
||||
u16 maxcom = WDM_DEFAULT_BUFSIZE;
|
||||
|
||||
if (!buffer)
|
||||
goto err;
|
||||
while (buflen > 2) {
|
||||
if (buffer[1] != USB_DT_CS_INTERFACE) {
|
||||
dev_err(&intf->dev, "skipping garbage\n");
|
||||
goto next_desc;
|
||||
}
|
||||
|
||||
switch (buffer[2]) {
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
break;
|
||||
case USB_CDC_DMM_TYPE:
|
||||
dmhd = (struct usb_cdc_dmm_desc *)buffer;
|
||||
maxcom = le16_to_cpu(dmhd->wMaxCommand);
|
||||
dev_dbg(&intf->dev,
|
||||
"Finding maximum buffer length: %d", maxcom);
|
||||
break;
|
||||
default:
|
||||
dev_err(&intf->dev,
|
||||
"Ignoring extra header, type %d, length %d\n",
|
||||
buffer[2], buffer[0]);
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
buflen -= buffer[0];
|
||||
buffer += buffer[0];
|
||||
}
|
||||
cdc_parse_cdc_header(&hdr, intf, buffer, buflen);
|
||||
|
||||
if (hdr.usb_cdc_dmm_desc)
|
||||
maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand);
|
||||
|
||||
iface = intf->cur_altsetting;
|
||||
if (iface->desc.bNumEndpoints != 1)
|
||||
|
@ -131,15 +131,17 @@ EXPORT_SYMBOL_GPL(usb_get_dr_mode);
|
||||
* of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device
|
||||
* which is associated with the given phy device_node
|
||||
* @np: Pointer to the given phy device_node
|
||||
* @arg0: phandle args[0] for phy's with #phy-cells >= 1, or -1 for
|
||||
* phys which do not have phy-cells
|
||||
*
|
||||
* In dts a usb controller associates with phy devices. The function gets
|
||||
* the string from property 'dr_mode' of the controller associated with the
|
||||
* given phy device node, and returns the correspondig enum usb_dr_mode.
|
||||
*/
|
||||
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
|
||||
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
|
||||
{
|
||||
struct device_node *controller = NULL;
|
||||
struct device_node *phy;
|
||||
struct of_phandle_args args;
|
||||
const char *dr_mode;
|
||||
int index;
|
||||
int err;
|
||||
@ -148,12 +150,24 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
|
||||
controller = of_find_node_with_property(controller, "phys");
|
||||
index = 0;
|
||||
do {
|
||||
phy = of_parse_phandle(controller, "phys", index);
|
||||
of_node_put(phy);
|
||||
if (phy == phy_np)
|
||||
if (arg0 == -1) {
|
||||
args.np = of_parse_phandle(controller, "phys",
|
||||
index);
|
||||
args.args_count = 0;
|
||||
} else {
|
||||
err = of_parse_phandle_with_args(controller,
|
||||
"phys", "#phy-cells",
|
||||
index, &args);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_put(args.np);
|
||||
if (args.np == np && (args.args_count == 0 ||
|
||||
args.args[0] == arg0))
|
||||
goto finish;
|
||||
index++;
|
||||
} while (phy);
|
||||
} while (args.np);
|
||||
} while (controller);
|
||||
|
||||
finish:
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/nls.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h> /* for usbcore internals */
|
||||
#include <asm/byteorder.h>
|
||||
@ -2023,3 +2024,155 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
|
||||
|
||||
/**
|
||||
* cdc_parse_cdc_header - parse the extra headers present in CDC devices
|
||||
* @hdr: the place to put the results of the parsing
|
||||
* @intf: the interface for which parsing is requested
|
||||
* @buffer: pointer to the extra headers to be parsed
|
||||
* @buflen: length of the extra headers
|
||||
*
|
||||
* This evaluates the extra headers present in CDC devices which
|
||||
* bind the interfaces for data and control and provide details
|
||||
* about the capabilities of the device.
|
||||
*
|
||||
* Return: number of descriptors parsed or -EINVAL
|
||||
* if the header is contradictory beyond salvage
|
||||
*/
|
||||
|
||||
int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
|
||||
struct usb_interface *intf,
|
||||
u8 *buffer,
|
||||
int buflen)
|
||||
{
|
||||
/* duplicates are ignored */
|
||||
struct usb_cdc_union_desc *union_header = NULL;
|
||||
|
||||
/* duplicates are not tolerated */
|
||||
struct usb_cdc_header_desc *header = NULL;
|
||||
struct usb_cdc_ether_desc *ether = NULL;
|
||||
struct usb_cdc_mdlm_detail_desc *detail = NULL;
|
||||
struct usb_cdc_mdlm_desc *desc = NULL;
|
||||
|
||||
unsigned int elength;
|
||||
int cnt = 0;
|
||||
|
||||
memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
|
||||
hdr->phonet_magic_present = false;
|
||||
while (buflen > 0) {
|
||||
elength = buffer[0];
|
||||
if (!elength) {
|
||||
dev_err(&intf->dev, "skipping garbage byte\n");
|
||||
elength = 1;
|
||||
goto next_desc;
|
||||
}
|
||||
if (buffer[1] != USB_DT_CS_INTERFACE) {
|
||||
dev_err(&intf->dev, "skipping garbage\n");
|
||||
goto next_desc;
|
||||
}
|
||||
|
||||
switch (buffer[2]) {
|
||||
case USB_CDC_UNION_TYPE: /* we've found it */
|
||||
if (elength < sizeof(struct usb_cdc_union_desc))
|
||||
goto next_desc;
|
||||
if (union_header) {
|
||||
dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
|
||||
goto next_desc;
|
||||
}
|
||||
union_header = (struct usb_cdc_union_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_COUNTRY_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_country_functional_desc))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_country_functional_desc =
|
||||
(struct usb_cdc_country_functional_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (elength != sizeof(struct usb_cdc_header_desc))
|
||||
goto next_desc;
|
||||
if (header)
|
||||
return -EINVAL;
|
||||
header = (struct usb_cdc_header_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_ACM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_acm_descriptor))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_acm_descriptor =
|
||||
(struct usb_cdc_acm_descriptor *)buffer;
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (elength != sizeof(struct usb_cdc_ether_desc))
|
||||
goto next_desc;
|
||||
if (ether)
|
||||
return -EINVAL;
|
||||
ether = (struct usb_cdc_ether_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_CALL_MANAGEMENT_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_call_mgmt_descriptor =
|
||||
(struct usb_cdc_call_mgmt_descriptor *)buffer;
|
||||
break;
|
||||
case USB_CDC_DMM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_dmm_desc))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_dmm_desc =
|
||||
(struct usb_cdc_dmm_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MDLM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mdlm_desc *))
|
||||
goto next_desc;
|
||||
if (desc)
|
||||
return -EINVAL;
|
||||
desc = (struct usb_cdc_mdlm_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MDLM_DETAIL_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
|
||||
goto next_desc;
|
||||
if (detail)
|
||||
return -EINVAL;
|
||||
detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_NCM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_ncm_desc))
|
||||
goto next_desc;
|
||||
hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MBIM_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mbim_desc))
|
||||
goto next_desc;
|
||||
|
||||
hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
|
||||
break;
|
||||
case USB_CDC_MBIM_EXTENDED_TYPE:
|
||||
if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
|
||||
break;
|
||||
hdr->usb_cdc_mbim_extended_desc =
|
||||
(struct usb_cdc_mbim_extended_desc *)buffer;
|
||||
break;
|
||||
case CDC_PHONET_MAGIC_NUMBER:
|
||||
hdr->phonet_magic_present = true;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* there are LOTS more CDC descriptors that
|
||||
* could legitimately be found here.
|
||||
*/
|
||||
dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
|
||||
buffer[2], elength);
|
||||
goto next_desc;
|
||||
}
|
||||
cnt++;
|
||||
next_desc:
|
||||
buflen -= elength;
|
||||
buffer += elength;
|
||||
}
|
||||
hdr->usb_cdc_union_desc = union_header;
|
||||
hdr->usb_cdc_header_desc = header;
|
||||
hdr->usb_cdc_mdlm_detail_desc = detail;
|
||||
hdr->usb_cdc_mdlm_desc = desc;
|
||||
hdr->usb_cdc_ether_desc = ether;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(cdc_parse_cdc_header);
|
||||
|
@ -128,6 +128,9 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
{ USB_DEVICE(0x04f3, 0x016f), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
{ USB_DEVICE(0x04f3, 0x0381), .driver_info =
|
||||
USB_QUIRK_NO_LPM },
|
||||
|
||||
{ USB_DEVICE(0x04f3, 0x21b8), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
|
@ -55,6 +55,7 @@ endchoice
|
||||
config USB_DWC2_PCI
|
||||
tristate "DWC2 PCI"
|
||||
depends on PCI
|
||||
depends on USB_GADGET || !USB_GADGET
|
||||
default n
|
||||
select NOP_USB_XCEIV
|
||||
help
|
||||
|
@ -166,7 +166,7 @@ struct dwc2_hsotg_req;
|
||||
* means that it is sending data to the Host.
|
||||
* @index: The index for the endpoint registers.
|
||||
* @mc: Multi Count - number of transactions per microframe
|
||||
* @interval - Interval for periodic endpoints
|
||||
* @interval - Interval for periodic endpoints, in frames or microframes.
|
||||
* @name: The name array passed to the USB core.
|
||||
* @halted: Set if the endpoint has been halted.
|
||||
* @periodic: Set if this is a periodic ep, such as Interrupt
|
||||
@ -177,6 +177,8 @@ struct dwc2_hsotg_req;
|
||||
* @fifo_load: The amount of data loaded into the FIFO (periodic IN)
|
||||
* @last_load: The offset of data for the last start of request.
|
||||
* @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
|
||||
* @target_frame: Targeted frame num to setup next ISOC transfer
|
||||
* @frame_overrun: Indicates SOF number overrun in DSTS
|
||||
*
|
||||
* This is the driver's state for each registered enpoint, allowing it
|
||||
* to keep track of transactions that need doing. Each endpoint has a
|
||||
@ -213,7 +215,9 @@ struct dwc2_hsotg_ep {
|
||||
unsigned int periodic:1;
|
||||
unsigned int isochronous:1;
|
||||
unsigned int send_zlp:1;
|
||||
unsigned int has_correct_parity:1;
|
||||
unsigned int target_frame;
|
||||
#define TARGET_FRAME_INITIAL 0xFFFFFFFF
|
||||
bool frame_overrun;
|
||||
|
||||
char name[10];
|
||||
};
|
||||
|
@ -96,6 +96,25 @@ static inline bool using_dma(struct dwc2_hsotg *hsotg)
|
||||
return hsotg->g_using_dma;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_incr_frame_num - Increments the targeted frame number.
|
||||
* @hs_ep: The endpoint
|
||||
* @increment: The value to increment by
|
||||
*
|
||||
* This function will also check if the frame number overruns DSTS_SOFFN_LIMIT.
|
||||
* If an overrun occurs it will wrap the value and set the frame_overrun flag.
|
||||
*/
|
||||
static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
hs_ep->target_frame += hs_ep->interval;
|
||||
if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) {
|
||||
hs_ep->frame_overrun = 1;
|
||||
hs_ep->target_frame &= DSTS_SOFFN_LIMIT;
|
||||
} else {
|
||||
hs_ep->frame_overrun = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_en_gsint - enable one or more of the general interrupt
|
||||
* @hsotg: The device state
|
||||
@ -503,6 +522,23 @@ static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
|
||||
return maxsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_read_frameno - read current frame number
|
||||
* @hsotg: The device instance
|
||||
*
|
||||
* Return the current frame number
|
||||
*/
|
||||
static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts;
|
||||
|
||||
dsts = dwc2_readl(hsotg->regs + DSTS);
|
||||
dsts &= DSTS_SOFFN_MASK;
|
||||
dsts >>= DSTS_SOFFN_SHIFT;
|
||||
|
||||
return dsts;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_start_req - start a USB request from an endpoint's queue
|
||||
* @hsotg: The controller state.
|
||||
@ -631,8 +667,17 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
|
||||
__func__, &ureq->dma, dma_reg);
|
||||
}
|
||||
|
||||
if (hs_ep->isochronous && hs_ep->interval == 1) {
|
||||
hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
|
||||
if (hs_ep->target_frame & 0x1)
|
||||
ctrl |= DXEPCTL_SETODDFR;
|
||||
else
|
||||
ctrl |= DXEPCTL_SETEVENFR;
|
||||
}
|
||||
|
||||
ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
|
||||
ctrl |= DXEPCTL_USBACTEP;
|
||||
|
||||
dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state);
|
||||
|
||||
@ -658,14 +703,6 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
|
||||
dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req);
|
||||
}
|
||||
|
||||
/*
|
||||
* clear the INTknTXFEmpMsk when we start request, more as a aide
|
||||
* to debugging to see what is going on.
|
||||
*/
|
||||
if (dir_in)
|
||||
dwc2_writel(DIEPMSK_INTKNTXFEMPMSK,
|
||||
hsotg->regs + DIEPINT(index));
|
||||
|
||||
/*
|
||||
* Note, trying to clear the NAK here causes problems with transmit
|
||||
* on the S3C6400 ending up with the TXFIFO becoming full.
|
||||
@ -773,6 +810,30 @@ static void dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
|
||||
hs_req->saved_req_buf = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_target_frame_elapsed - Checks target frame
|
||||
* @hs_ep: The driver endpoint to check
|
||||
*
|
||||
* Returns 1 if targeted frame elapsed. If returned 1 then we need to drop
|
||||
* corresponding transfer.
|
||||
*/
|
||||
static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
u32 target_frame = hs_ep->target_frame;
|
||||
u32 current_frame = dwc2_hsotg_read_frameno(hsotg);
|
||||
bool frame_overrun = hs_ep->frame_overrun;
|
||||
|
||||
if (!frame_overrun && current_frame >= target_frame)
|
||||
return true;
|
||||
|
||||
if (frame_overrun && current_frame >= target_frame &&
|
||||
((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
@ -812,9 +873,18 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
first = list_empty(&hs_ep->queue);
|
||||
list_add_tail(&hs_req->queue, &hs_ep->queue);
|
||||
|
||||
if (first)
|
||||
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
|
||||
if (first) {
|
||||
if (!hs_ep->isochronous) {
|
||||
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (dwc2_gadget_target_frame_elapsed(hs_ep))
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
|
||||
if (hs_ep->target_frame != TARGET_FRAME_INITIAL)
|
||||
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1034,6 +1104,42 @@ static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep)
|
||||
return list_first_entry(&hs_ep->queue, struct dwc2_hsotg_req, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_start_next_request - Starts next request from ep queue
|
||||
* @hs_ep: Endpoint structure
|
||||
*
|
||||
* If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked
|
||||
* in its handler. Hence we need to unmask it here to be able to do
|
||||
* resynchronization.
|
||||
*/
|
||||
static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
u32 mask;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
struct dwc2_hsotg_req *hs_req;
|
||||
u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK;
|
||||
|
||||
if (!list_empty(&hs_ep->queue)) {
|
||||
hs_req = get_ep_head(hs_ep);
|
||||
dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false);
|
||||
return;
|
||||
}
|
||||
if (!hs_ep->isochronous)
|
||||
return;
|
||||
|
||||
if (dir_in) {
|
||||
dev_dbg(hsotg->dev, "%s: No more ISOC-IN requests\n",
|
||||
__func__);
|
||||
} else {
|
||||
dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n",
|
||||
__func__);
|
||||
mask = dwc2_readl(hsotg->regs + epmsk_reg);
|
||||
mask |= DOEPMSK_OUTTKNEPDISMSK;
|
||||
dwc2_writel(mask, hsotg->regs + epmsk_reg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE
|
||||
* @hsotg: The device state
|
||||
@ -1044,7 +1150,6 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
|
||||
{
|
||||
struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
|
||||
struct dwc2_hsotg_req *hs_req;
|
||||
bool restart;
|
||||
bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
|
||||
struct dwc2_hsotg_ep *ep;
|
||||
int ret;
|
||||
@ -1127,12 +1232,7 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
|
||||
|
||||
/* If we have pending request, then start it */
|
||||
if (!ep->req) {
|
||||
restart = !list_empty(&ep->queue);
|
||||
if (restart) {
|
||||
hs_req = get_ep_head(ep);
|
||||
dwc2_hsotg_start_req(hsotg, ep,
|
||||
hs_req, false);
|
||||
}
|
||||
dwc2_gadget_start_next_request(ep);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1373,7 +1473,6 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_hsotg_req *hs_req,
|
||||
int result)
|
||||
{
|
||||
bool restart;
|
||||
|
||||
if (!hs_req) {
|
||||
dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__);
|
||||
@ -1417,11 +1516,7 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
|
||||
*/
|
||||
|
||||
if (!hs_ep->req && result >= 0) {
|
||||
restart = !list_empty(&hs_ep->queue);
|
||||
if (restart) {
|
||||
hs_req = get_ep_head(hs_ep);
|
||||
dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false);
|
||||
}
|
||||
dwc2_gadget_start_next_request(hs_ep);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1597,31 +1692,15 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
|
||||
* adjust the ISOC parity here.
|
||||
*/
|
||||
if (!using_dma(hsotg)) {
|
||||
hs_ep->has_correct_parity = 1;
|
||||
if (hs_ep->isochronous && hs_ep->interval == 1)
|
||||
dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum));
|
||||
else if (hs_ep->isochronous && hs_ep->interval > 1)
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
}
|
||||
|
||||
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_read_frameno - read current frame number
|
||||
* @hsotg: The device instance
|
||||
*
|
||||
* Return the current frame number
|
||||
*/
|
||||
static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts;
|
||||
|
||||
dsts = dwc2_readl(hsotg->regs + DSTS);
|
||||
dsts &= DSTS_SOFFN_MASK;
|
||||
dsts >>= DSTS_SOFFN_SHIFT;
|
||||
|
||||
return dsts;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_handle_rx - RX FIFO has data
|
||||
* @hsotg: The device instance
|
||||
@ -1936,6 +2015,190 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
|
||||
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_read_ep_interrupts - reads interrupts for given ep
|
||||
* @hsotg: The device state.
|
||||
* @idx: Index of ep.
|
||||
* @dir_in: Endpoint direction 1-in 0-out.
|
||||
*
|
||||
* Reads for endpoint with given index and direction, by masking
|
||||
* epint_reg with coresponding mask.
|
||||
*/
|
||||
static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg,
|
||||
unsigned int idx, int dir_in)
|
||||
{
|
||||
u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK;
|
||||
u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
|
||||
u32 ints;
|
||||
u32 mask;
|
||||
u32 diepempmsk;
|
||||
|
||||
mask = dwc2_readl(hsotg->regs + epmsk_reg);
|
||||
diepempmsk = dwc2_readl(hsotg->regs + DIEPEMPMSK);
|
||||
mask |= ((diepempmsk >> idx) & 0x1) ? DIEPMSK_TXFIFOEMPTY : 0;
|
||||
mask |= DXEPINT_SETUP_RCVD;
|
||||
|
||||
ints = dwc2_readl(hsotg->regs + epint_reg);
|
||||
ints &= mask;
|
||||
return ints;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_handle_ep_disabled - handle DXEPINT_EPDISBLD
|
||||
* @hs_ep: The endpoint on which interrupt is asserted.
|
||||
*
|
||||
* This interrupt indicates that the endpoint has been disabled per the
|
||||
* application's request.
|
||||
*
|
||||
* For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK,
|
||||
* in case of ISOC completes current request.
|
||||
*
|
||||
* For ISOC-OUT endpoints completes expired requests. If there is remaining
|
||||
* request starts it.
|
||||
*/
|
||||
static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg_req *hs_req;
|
||||
unsigned char idx = hs_ep->index;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
|
||||
int dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
|
||||
|
||||
if (dir_in) {
|
||||
int epctl = dwc2_readl(hsotg->regs + epctl_reg);
|
||||
|
||||
dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
|
||||
|
||||
if (hs_ep->isochronous) {
|
||||
dwc2_hsotg_complete_in(hsotg, hs_ep);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {
|
||||
int dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
|
||||
dctl |= DCTL_CGNPINNAK;
|
||||
dwc2_writel(dctl, hsotg->regs + DCTL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dctl & DCTL_GOUTNAKSTS) {
|
||||
dctl |= DCTL_CGOUTNAK;
|
||||
dwc2_writel(dctl, hsotg->regs + DCTL);
|
||||
}
|
||||
|
||||
if (!hs_ep->isochronous)
|
||||
return;
|
||||
|
||||
if (list_empty(&hs_ep->queue)) {
|
||||
dev_dbg(hsotg->dev, "%s: complete_ep 0x%p, ep->queue empty!\n",
|
||||
__func__, hs_ep);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
hs_req = get_ep_head(hs_ep);
|
||||
if (hs_req)
|
||||
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req,
|
||||
-ENODATA);
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
} while (dwc2_gadget_target_frame_elapsed(hs_ep));
|
||||
|
||||
dwc2_gadget_start_next_request(hs_ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS
|
||||
* @hs_ep: The endpoint on which interrupt is asserted.
|
||||
*
|
||||
* This is starting point for ISOC-OUT transfer, synchronization done with
|
||||
* first out token received from host while corresponding EP is disabled.
|
||||
*
|
||||
* Device does not know initial frame in which out token will come. For this
|
||||
* HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon
|
||||
* getting this interrupt SW starts calculation for next transfer frame.
|
||||
*/
|
||||
static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = ep->parent;
|
||||
int dir_in = ep->dir_in;
|
||||
u32 doepmsk;
|
||||
|
||||
if (dir_in || !ep->isochronous)
|
||||
return;
|
||||
|
||||
dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), -ENODATA);
|
||||
|
||||
if (ep->interval > 1 &&
|
||||
ep->target_frame == TARGET_FRAME_INITIAL) {
|
||||
u32 dsts;
|
||||
u32 ctrl;
|
||||
|
||||
dsts = dwc2_readl(hsotg->regs + DSTS);
|
||||
ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
|
||||
dwc2_gadget_incr_frame_num(ep);
|
||||
|
||||
ctrl = dwc2_readl(hsotg->regs + DOEPCTL(ep->index));
|
||||
if (ep->target_frame & 0x1)
|
||||
ctrl |= DXEPCTL_SETODDFR;
|
||||
else
|
||||
ctrl |= DXEPCTL_SETEVENFR;
|
||||
|
||||
dwc2_writel(ctrl, hsotg->regs + DOEPCTL(ep->index));
|
||||
}
|
||||
|
||||
dwc2_gadget_start_next_request(ep);
|
||||
doepmsk = dwc2_readl(hsotg->regs + DOEPMSK);
|
||||
doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK;
|
||||
dwc2_writel(doepmsk, hsotg->regs + DOEPMSK);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_handle_nak - handle NAK interrupt
|
||||
* @hs_ep: The endpoint on which interrupt is asserted.
|
||||
*
|
||||
* This is starting point for ISOC-IN transfer, synchronization done with
|
||||
* first IN token received from host while corresponding EP is disabled.
|
||||
*
|
||||
* Device does not know when first one token will arrive from host. On first
|
||||
* token arrival HW generates 2 interrupts: 'in token received while FIFO empty'
|
||||
* and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was
|
||||
* sent in response to that as there was no data in FIFO. SW is basing on this
|
||||
* interrupt to obtain frame in which token has come and then based on the
|
||||
* interval calculates next frame for transfer.
|
||||
*/
|
||||
static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
|
||||
if (!dir_in || !hs_ep->isochronous)
|
||||
return;
|
||||
|
||||
if (hs_ep->target_frame == TARGET_FRAME_INITIAL) {
|
||||
hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
|
||||
if (hs_ep->interval > 1) {
|
||||
u32 ctrl = dwc2_readl(hsotg->regs +
|
||||
DIEPCTL(hs_ep->index));
|
||||
if (hs_ep->target_frame & 0x1)
|
||||
ctrl |= DXEPCTL_SETODDFR;
|
||||
else
|
||||
ctrl |= DXEPCTL_SETEVENFR;
|
||||
|
||||
dwc2_writel(ctrl, hsotg->regs + DIEPCTL(hs_ep->index));
|
||||
}
|
||||
|
||||
dwc2_hsotg_complete_request(hsotg, hs_ep,
|
||||
get_ep_head(hs_ep), 0);
|
||||
}
|
||||
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_epint - handle an in/out endpoint interrupt
|
||||
* @hsotg: The driver state
|
||||
@ -1954,7 +2217,7 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
u32 ints;
|
||||
u32 ctrl;
|
||||
|
||||
ints = dwc2_readl(hsotg->regs + epint_reg);
|
||||
ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in);
|
||||
ctrl = dwc2_readl(hsotg->regs + epctl_reg);
|
||||
|
||||
/* Clear endpoint interrupts */
|
||||
@ -1973,11 +2236,10 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD)))
|
||||
ints &= ~DXEPINT_XFERCOMPL;
|
||||
|
||||
if (ints & DXEPINT_XFERCOMPL) {
|
||||
hs_ep->has_correct_parity = 1;
|
||||
if (hs_ep->isochronous && hs_ep->interval == 1)
|
||||
dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg);
|
||||
if (ints & DXEPINT_STSPHSERCVD)
|
||||
dev_dbg(hsotg->dev, "%s: StsPhseRcvd asserted\n", __func__);
|
||||
|
||||
if (ints & DXEPINT_XFERCOMPL) {
|
||||
dev_dbg(hsotg->dev,
|
||||
"%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n",
|
||||
__func__, dwc2_readl(hsotg->regs + epctl_reg),
|
||||
@ -1988,7 +2250,12 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
* at completing IN requests here
|
||||
*/
|
||||
if (dir_in) {
|
||||
if (hs_ep->isochronous && hs_ep->interval > 1)
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
|
||||
dwc2_hsotg_complete_in(hsotg, hs_ep);
|
||||
if (ints & DXEPINT_NAKINTRPT)
|
||||
ints &= ~DXEPINT_NAKINTRPT;
|
||||
|
||||
if (idx == 0 && !hs_ep->req)
|
||||
dwc2_hsotg_enqueue_setup(hsotg);
|
||||
@ -1997,28 +2264,21 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
* We're using DMA, we need to fire an OutDone here
|
||||
* as we ignore the RXFIFO.
|
||||
*/
|
||||
if (hs_ep->isochronous && hs_ep->interval > 1)
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
|
||||
dwc2_hsotg_handle_outdone(hsotg, idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ints & DXEPINT_EPDISBLD) {
|
||||
dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
|
||||
if (ints & DXEPINT_EPDISBLD)
|
||||
dwc2_gadget_handle_ep_disabled(hs_ep);
|
||||
|
||||
if (dir_in) {
|
||||
int epctl = dwc2_readl(hsotg->regs + epctl_reg);
|
||||
if (ints & DXEPINT_OUTTKNEPDIS)
|
||||
dwc2_gadget_handle_out_token_ep_disabled(hs_ep);
|
||||
|
||||
dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
|
||||
|
||||
if ((epctl & DXEPCTL_STALL) &&
|
||||
(epctl & DXEPCTL_EPTYPE_BULK)) {
|
||||
int dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
|
||||
dctl |= DCTL_CGNPINNAK;
|
||||
dwc2_writel(dctl, hsotg->regs + DCTL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ints & DXEPINT_NAKINTRPT)
|
||||
dwc2_gadget_handle_nak(hs_ep);
|
||||
|
||||
if (ints & DXEPINT_AHBERR)
|
||||
dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__);
|
||||
@ -2046,20 +2306,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
|
||||
if (dir_in && !hs_ep->isochronous) {
|
||||
/* not sure if this is important, but we'll clear it anyway */
|
||||
if (ints & DIEPMSK_INTKNTXFEMPMSK) {
|
||||
if (ints & DXEPINT_INTKNTXFEMP) {
|
||||
dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
|
||||
__func__, idx);
|
||||
}
|
||||
|
||||
/* this probably means something bad is happening */
|
||||
if (ints & DIEPMSK_INTKNEPMISMSK) {
|
||||
if (ints & DXEPINT_INTKNEPMIS) {
|
||||
dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n",
|
||||
__func__, idx);
|
||||
}
|
||||
|
||||
/* FIFO has space or is empty (see GAHBCFG) */
|
||||
if (hsotg->dedicated_fifos &&
|
||||
ints & DIEPMSK_TXFIFOEMPTY) {
|
||||
ints & DXEPINT_TXFEMP) {
|
||||
dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
|
||||
__func__, idx);
|
||||
if (!using_dma(hsotg))
|
||||
@ -2322,18 +2582,16 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
dwc2_writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
|
||||
DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
|
||||
DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
|
||||
DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
|
||||
DIEPMSK_INTKNEPMISMSK,
|
||||
DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK,
|
||||
hsotg->regs + DIEPMSK);
|
||||
|
||||
/*
|
||||
* don't need XferCompl, we get that from RXFIFO in slave mode. In
|
||||
* DMA mode we may need this.
|
||||
*/
|
||||
dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK |
|
||||
DIEPMSK_TIMEOUTMSK) : 0) |
|
||||
dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK) : 0) |
|
||||
DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK |
|
||||
DOEPMSK_SETUPMSK,
|
||||
DOEPMSK_SETUPMSK | DOEPMSK_STSPHSERCVDMSK,
|
||||
hsotg->regs + DOEPMSK);
|
||||
|
||||
dwc2_writel(0, hsotg->regs + DAINTMSK);
|
||||
@ -2413,6 +2671,85 @@ void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_handle_incomplete_isoc_in - handle incomplete ISO IN Interrupt.
|
||||
* @hsotg: The device state:
|
||||
*
|
||||
* This interrupt indicates one of the following conditions occurred while
|
||||
* transmitting an ISOC transaction.
|
||||
* - Corrupted IN Token for ISOC EP.
|
||||
* - Packet not complete in FIFO.
|
||||
*
|
||||
* The following actions will be taken:
|
||||
* - Determine the EP
|
||||
* - Disable EP; when 'Endpoint Disabled' interrupt is received Flush FIFO
|
||||
*/
|
||||
static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hsotg_ep *hs_ep;
|
||||
u32 epctrl;
|
||||
u32 idx;
|
||||
|
||||
dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
|
||||
|
||||
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_in[idx];
|
||||
epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx));
|
||||
if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
|
||||
dwc2_gadget_target_frame_elapsed(hs_ep)) {
|
||||
epctrl |= DXEPCTL_SNAK;
|
||||
epctrl |= DXEPCTL_EPDIS;
|
||||
dwc2_writel(epctrl, hsotg->regs + DIEPCTL(idx));
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_handle_incomplete_isoc_out - handle incomplete ISO OUT Interrupt
|
||||
* @hsotg: The device state:
|
||||
*
|
||||
* This interrupt indicates one of the following conditions occurred while
|
||||
* transmitting an ISOC transaction.
|
||||
* - Corrupted OUT Token for ISOC EP.
|
||||
* - Packet not complete in FIFO.
|
||||
*
|
||||
* The following actions will be taken:
|
||||
* - Determine the EP
|
||||
* - Set DCTL_SGOUTNAK and unmask GOUTNAKEFF if target frame elapsed.
|
||||
*/
|
||||
static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gintsts;
|
||||
u32 gintmsk;
|
||||
u32 epctrl;
|
||||
struct dwc2_hsotg_ep *hs_ep;
|
||||
int idx;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
|
||||
|
||||
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_out[idx];
|
||||
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
|
||||
if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
|
||||
dwc2_gadget_target_frame_elapsed(hs_ep)) {
|
||||
/* Unmask GOUTNAKEFF interrupt */
|
||||
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
gintmsk |= GINTSTS_GOUTNAKEFF;
|
||||
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
|
||||
|
||||
gintsts = dwc2_readl(hsotg->regs + GINTSTS);
|
||||
if (!(gintsts & GINTSTS_GOUTNAKEFF))
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_irq - handle device interrupt
|
||||
* @irq: The IRQ number triggered
|
||||
@ -2545,11 +2882,29 @@ irq_retry:
|
||||
*/
|
||||
|
||||
if (gintsts & GINTSTS_GOUTNAKEFF) {
|
||||
dev_info(hsotg->dev, "GOUTNakEff triggered\n");
|
||||
u8 idx;
|
||||
u32 epctrl;
|
||||
u32 gintmsk;
|
||||
struct dwc2_hsotg_ep *hs_ep;
|
||||
|
||||
__orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
|
||||
/* Mask this interrupt */
|
||||
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
gintmsk &= ~GINTSTS_GOUTNAKEFF;
|
||||
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
|
||||
|
||||
dwc2_hsotg_dump(hsotg);
|
||||
dev_dbg(hsotg->dev, "GOUTNakEff triggered\n");
|
||||
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_out[idx];
|
||||
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
|
||||
|
||||
if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) {
|
||||
epctrl |= DXEPCTL_SNAK;
|
||||
epctrl |= DXEPCTL_EPDIS;
|
||||
dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx));
|
||||
}
|
||||
}
|
||||
|
||||
/* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_GINNAKEFF) {
|
||||
@ -2560,39 +2915,11 @@ irq_retry:
|
||||
dwc2_hsotg_dump(hsotg);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_INCOMPL_SOIN) {
|
||||
u32 idx, epctl_reg;
|
||||
struct dwc2_hsotg_ep *hs_ep;
|
||||
if (gintsts & GINTSTS_INCOMPL_SOIN)
|
||||
dwc2_gadget_handle_incomplete_isoc_in(hsotg);
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOIN\n", __func__);
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_in[idx];
|
||||
|
||||
if (!hs_ep->isochronous || hs_ep->has_correct_parity)
|
||||
continue;
|
||||
|
||||
epctl_reg = DIEPCTL(idx);
|
||||
dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg);
|
||||
}
|
||||
dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_INCOMPL_SOOUT) {
|
||||
u32 idx, epctl_reg;
|
||||
struct dwc2_hsotg_ep *hs_ep;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
hs_ep = hsotg->eps_out[idx];
|
||||
|
||||
if (!hs_ep->isochronous || hs_ep->has_correct_parity)
|
||||
continue;
|
||||
|
||||
epctl_reg = DOEPCTL(idx);
|
||||
dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg);
|
||||
}
|
||||
dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
if (gintsts & GINTSTS_INCOMPL_SOOUT)
|
||||
dwc2_gadget_handle_incomplete_isoc_out(hsotg);
|
||||
|
||||
/*
|
||||
* if we've had fifo events, we should try and go around the
|
||||
@ -2624,6 +2951,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
u32 epctrl_reg;
|
||||
u32 epctrl;
|
||||
u32 mps;
|
||||
u32 mask;
|
||||
unsigned int dir_in;
|
||||
unsigned int i, val, size;
|
||||
int ret = 0;
|
||||
@ -2666,15 +2994,6 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
*/
|
||||
epctrl |= DXEPCTL_USBACTEP;
|
||||
|
||||
/*
|
||||
* set the NAK status on the endpoint, otherwise we might try and
|
||||
* do something with data that we've yet got a request to process
|
||||
* since the RXFIFO will take data for an endpoint even if the
|
||||
* size register hasn't been set.
|
||||
*/
|
||||
|
||||
epctrl |= DXEPCTL_SNAK;
|
||||
|
||||
/* update the endpoint state */
|
||||
dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in);
|
||||
|
||||
@ -2683,18 +3002,24 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
hs_ep->periodic = 0;
|
||||
hs_ep->halted = 0;
|
||||
hs_ep->interval = desc->bInterval;
|
||||
hs_ep->has_correct_parity = 0;
|
||||
|
||||
if (hs_ep->interval > 1 && hs_ep->mc > 1)
|
||||
dev_err(hsotg->dev, "MC > 1 when interval is not 1\n");
|
||||
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
epctrl |= DXEPCTL_EPTYPE_ISO;
|
||||
epctrl |= DXEPCTL_SETEVENFR;
|
||||
hs_ep->isochronous = 1;
|
||||
if (dir_in)
|
||||
hs_ep->interval = 1 << (desc->bInterval - 1);
|
||||
hs_ep->target_frame = TARGET_FRAME_INITIAL;
|
||||
if (dir_in) {
|
||||
hs_ep->periodic = 1;
|
||||
mask = dwc2_readl(hsotg->regs + DIEPMSK);
|
||||
mask |= DIEPMSK_NAKMSK;
|
||||
dwc2_writel(mask, hsotg->regs + DIEPMSK);
|
||||
} else {
|
||||
mask = dwc2_readl(hsotg->regs + DOEPMSK);
|
||||
mask |= DOEPMSK_OUTTKNEPDISMSK;
|
||||
dwc2_writel(mask, hsotg->regs + DOEPMSK);
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
@ -2705,6 +3030,9 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
if (dir_in)
|
||||
hs_ep->periodic = 1;
|
||||
|
||||
if (hsotg->gadget.speed == USB_SPEED_HIGH)
|
||||
hs_ep->interval = 1 << (desc->bInterval - 1);
|
||||
|
||||
epctrl |= DXEPCTL_EPTYPE_INTERRUPT;
|
||||
break;
|
||||
|
||||
@ -2758,7 +3086,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
}
|
||||
|
||||
/* for non control endpoints, set PID to D0 */
|
||||
if (index)
|
||||
if (index && !hs_ep->isochronous)
|
||||
epctrl |= DXEPCTL_SETD0PID;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n",
|
||||
@ -2875,10 +3203,8 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
|
||||
dev_warn(hsotg->dev,
|
||||
"%s: timeout DIEPINT.NAKEFF\n", __func__);
|
||||
} else {
|
||||
/* Clear any pending nak effect interrupt */
|
||||
dwc2_writel(GINTSTS_GOUTNAKEFF, hsotg->regs + GINTSTS);
|
||||
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF))
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
|
||||
/* Wait for global nak to take effect */
|
||||
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
|
||||
|
@ -367,7 +367,8 @@ static void pmap_unschedule(unsigned long *map, int bits_per_period,
|
||||
* @fmt: The format for printf.
|
||||
* @...: The args for printf.
|
||||
*/
|
||||
static void cat_printf(char **buf, size_t *size, const char *fmt, ...)
|
||||
static __printf(3, 4)
|
||||
void cat_printf(char **buf, size_t *size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
|
@ -459,6 +459,9 @@
|
||||
#define DSTS_SUSPSTS (1 << 0)
|
||||
|
||||
#define DIEPMSK HSOTG_REG(0x810)
|
||||
#define DIEPMSK_NAKMSK (1 << 13)
|
||||
#define DIEPMSK_BNAININTRMSK (1 << 9)
|
||||
#define DIEPMSK_TXFIFOUNDRNMSK (1 << 8)
|
||||
#define DIEPMSK_TXFIFOEMPTY (1 << 7)
|
||||
#define DIEPMSK_INEPNAKEFFMSK (1 << 6)
|
||||
#define DIEPMSK_INTKNEPMISMSK (1 << 5)
|
||||
@ -470,6 +473,7 @@
|
||||
|
||||
#define DOEPMSK HSOTG_REG(0x814)
|
||||
#define DOEPMSK_BACK2BACKSETUP (1 << 6)
|
||||
#define DOEPMSK_STSPHSERCVDMSK (1 << 5)
|
||||
#define DOEPMSK_OUTTKNEPDISMSK (1 << 4)
|
||||
#define DOEPMSK_SETUPMSK (1 << 3)
|
||||
#define DOEPMSK_AHBERRMSK (1 << 2)
|
||||
@ -486,6 +490,7 @@
|
||||
#define DTKNQR2 HSOTG_REG(0x824)
|
||||
#define DTKNQR3 HSOTG_REG(0x830)
|
||||
#define DTKNQR4 HSOTG_REG(0x834)
|
||||
#define DIEPEMPMSK HSOTG_REG(0x834)
|
||||
|
||||
#define DVBUSDIS HSOTG_REG(0x828)
|
||||
#define DVBUSPULSE HSOTG_REG(0x82C)
|
||||
@ -544,9 +549,18 @@
|
||||
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
|
||||
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
|
||||
#define DXEPINT_SETUP_RCVD (1 << 15)
|
||||
#define DXEPINT_NYETINTRPT (1 << 14)
|
||||
#define DXEPINT_NAKINTRPT (1 << 13)
|
||||
#define DXEPINT_BBLEERRINTRPT (1 << 12)
|
||||
#define DXEPINT_PKTDRPSTS (1 << 11)
|
||||
#define DXEPINT_BNAINTR (1 << 9)
|
||||
#define DXEPINT_TXFIFOUNDRN (1 << 8)
|
||||
#define DXEPINT_OUTPKTERR (1 << 8)
|
||||
#define DXEPINT_TXFEMP (1 << 7)
|
||||
#define DXEPINT_INEPNAKEFF (1 << 6)
|
||||
#define DXEPINT_BACK2BACKSETUP (1 << 6)
|
||||
#define DXEPINT_INTKNEPMIS (1 << 5)
|
||||
#define DXEPINT_STSPHSERCVD (1 << 5)
|
||||
#define DXEPINT_INTKNTXFEMP (1 << 4)
|
||||
#define DXEPINT_OUTTKNEPDIS (1 << 4)
|
||||
#define DXEPINT_TIMEOUT (1 << 3)
|
||||
|
@ -41,14 +41,13 @@
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include "platform_data.h"
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
|
||||
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
{
|
||||
@ -149,9 +148,8 @@ static int dwc3_soft_reset(struct dwc3 *dwc)
|
||||
/*
|
||||
* dwc3_frame_length_adjustment - Adjusts frame length if required
|
||||
* @dwc3: Pointer to our controller context structure
|
||||
* @fladj: Value of GFLADJ_30MHZ to adjust frame length
|
||||
*/
|
||||
static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
|
||||
static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
u32 dft;
|
||||
@ -159,15 +157,15 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
|
||||
if (dwc->revision < DWC3_REVISION_250A)
|
||||
return;
|
||||
|
||||
if (fladj == 0)
|
||||
if (dwc->fladj == 0)
|
||||
return;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
|
||||
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
|
||||
if (!dev_WARN_ONCE(dwc->dev, dft == fladj,
|
||||
if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
|
||||
"request value same as default, ignoring\n")) {
|
||||
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
|
||||
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj;
|
||||
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
|
||||
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
|
||||
}
|
||||
}
|
||||
@ -507,6 +505,21 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_core_init - Low-level initialization of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
@ -556,6 +569,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
|
||||
@ -622,22 +639,45 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
dwc3_core_num_eps(dwc);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
dwc3_core_num_eps(dwc);
|
||||
|
||||
ret = dwc3_setup_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
/* Adjust Frame Length */
|
||||
dwc3_frame_length_adjustment(dwc);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err3:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err2:
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
err1:
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
@ -649,15 +689,6 @@ err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
}
|
||||
|
||||
static int dwc3_core_get_phy(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
@ -735,7 +766,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
@ -743,7 +775,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
@ -751,13 +784,15 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
@ -793,13 +828,11 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
u8 lpm_nyet_threshold;
|
||||
u8 tx_de_emphasis;
|
||||
u8 hird_threshold;
|
||||
u32 fladj = 0;
|
||||
|
||||
int ret;
|
||||
|
||||
@ -814,16 +847,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dwc->mem = mem;
|
||||
dwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[1].start = res->start;
|
||||
dwc->xhci_resources[1].end = res->end;
|
||||
dwc->xhci_resources[1].flags = res->flags;
|
||||
dwc->xhci_resources[1].name = res->name;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
@ -909,40 +932,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
device_property_read_string(dev, "snps,hsphy_interface",
|
||||
&dwc->hsphy_interface);
|
||||
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
|
||||
&fladj);
|
||||
|
||||
if (pdata) {
|
||||
dwc->maximum_speed = pdata->maximum_speed;
|
||||
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
|
||||
if (pdata->lpm_nyet_threshold)
|
||||
lpm_nyet_threshold = pdata->lpm_nyet_threshold;
|
||||
dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
|
||||
if (pdata->hird_threshold)
|
||||
hird_threshold = pdata->hird_threshold;
|
||||
|
||||
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
|
||||
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
|
||||
dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
|
||||
dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
|
||||
dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
|
||||
dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
|
||||
dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
|
||||
dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
|
||||
dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
|
||||
dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
|
||||
dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
|
||||
dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk;
|
||||
dwc->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_quirk;
|
||||
|
||||
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
|
||||
if (pdata->tx_de_emphasis)
|
||||
tx_de_emphasis = pdata->tx_de_emphasis;
|
||||
|
||||
dwc->hsphy_interface = pdata->hsphy_interface;
|
||||
fladj = pdata->fladj_value;
|
||||
}
|
||||
&dwc->fladj);
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
@ -953,10 +943,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
@ -969,29 +955,43 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST) &&
|
||||
(dwc->dr_mode == USB_DR_MODE_OTG ||
|
||||
dwc->dr_mode == USB_DR_MODE_UNKNOWN))
|
||||
dwc->dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
|
||||
(dwc->dr_mode == USB_DR_MODE_OTG ||
|
||||
dwc->dr_mode == USB_DR_MODE_UNKNOWN))
|
||||
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
dwc->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
goto err1;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
/* Check the maximum_speed parameter */
|
||||
@ -1021,31 +1021,12 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Adjust Frame Length */
|
||||
dwc3_frame_length_adjustment(dwc, fladj);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err5;
|
||||
|
||||
dwc3_debugfs_init(dwc);
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1053,20 +1034,19 @@ err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err4:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
err3:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err2:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
err1:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
err2:
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
err1:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
err0:
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
@ -1083,6 +1063,7 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
* probe is deferred, we don't end up getting error in request the
|
||||
@ -1092,54 +1073,161 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
|
||||
dwc3_debugfs_exit(dwc);
|
||||
dwc3_core_exit_mode(dwc);
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_suspend_common(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_suspend(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_resume_common(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_resume(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_runtime_checks(struct dwc3 *dwc)
|
||||
{
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
if (dwc->connected)
|
||||
return -EBUSY;
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (dwc3_runtime_checks(dwc))
|
||||
return -EBUSY;
|
||||
|
||||
ret = dwc3_suspend_common(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
device_init_wakeup(dev, false);
|
||||
|
||||
ret = dwc3_resume_common(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_process_pending_events(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_runtime_idle(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
if (dwc3_runtime_checks(dwc))
|
||||
return -EBUSY;
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_suspend(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
break;
|
||||
}
|
||||
|
||||
dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0);
|
||||
WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0);
|
||||
ret = dwc3_suspend_common(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
@ -1149,76 +1237,28 @@ static int dwc3_suspend(struct device *dev)
|
||||
static int dwc3_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
ret = dwc3_resume_common(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_power;
|
||||
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb3phy_power;
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_init;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_resume(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_usb2phy_init:
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
|
||||
err_usb3phy_power:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err_usb2phy_power:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
|
||||
dwc3_runtime_idle)
|
||||
};
|
||||
|
||||
#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
|
||||
#else
|
||||
#define DWC3_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{
|
||||
@ -1250,7 +1290,7 @@ static struct platform_driver dwc3_driver = {
|
||||
.name = "dwc3",
|
||||
.of_match_table = of_match_ptr(of_dwc3_match),
|
||||
.acpi_match_table = ACPI_PTR(dwc3_acpi_match),
|
||||
.pm = DWC3_PM_OPS,
|
||||
.pm = &dwc3_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -86,6 +86,7 @@
|
||||
#define DWC3_GCTL 0xc110
|
||||
#define DWC3_GEVTEN 0xc114
|
||||
#define DWC3_GSTS 0xc118
|
||||
#define DWC3_GUCTL1 0xc11c
|
||||
#define DWC3_GSNPSID 0xc120
|
||||
#define DWC3_GGPIO 0xc124
|
||||
#define DWC3_GUID 0xc128
|
||||
@ -138,10 +139,12 @@
|
||||
#define DWC3_DGCMDPAR 0xc710
|
||||
#define DWC3_DGCMD 0xc714
|
||||
#define DWC3_DALEPENA 0xc720
|
||||
#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10))
|
||||
#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10))
|
||||
#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
|
||||
#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
|
||||
|
||||
#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10))
|
||||
#define DWC3_DEPCMDPAR2 0x00
|
||||
#define DWC3_DEPCMDPAR1 0x04
|
||||
#define DWC3_DEPCMDPAR0 0x08
|
||||
#define DWC3_DEPCMD 0x0c
|
||||
|
||||
/* OTG Registers */
|
||||
#define DWC3_OCFG 0xcc00
|
||||
@ -231,6 +234,14 @@
|
||||
#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
|
||||
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
|
||||
|
||||
/* Global HWPARAMS0 Register */
|
||||
#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3)
|
||||
#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
|
||||
#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
|
||||
#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
|
||||
#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff)
|
||||
#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff)
|
||||
|
||||
/* Global HWPARAMS1 Register */
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
|
||||
@ -260,6 +271,10 @@
|
||||
/* Global HWPARAMS6 Register */
|
||||
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
|
||||
|
||||
/* Global HWPARAMS7 Register */
|
||||
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
|
||||
|
||||
/* Global Frame Length Adjustment Register */
|
||||
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
|
||||
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
|
||||
@ -468,6 +483,8 @@ struct dwc3_event_buffer {
|
||||
* @endpoint: usb endpoint
|
||||
* @pending_list: list of pending requests for this endpoint
|
||||
* @started_list: list of started requests on this endpoint
|
||||
* @lock: spinlock for endpoint request queue traversal
|
||||
* @regs: pointer to first endpoint register
|
||||
* @trb_pool: array of transaction buffers
|
||||
* @trb_pool_dma: dma address of @trb_pool
|
||||
* @trb_enqueue: enqueue 'pointer' into TRB array
|
||||
@ -480,6 +497,8 @@ struct dwc3_event_buffer {
|
||||
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
|
||||
* @resource_index: Resource transfer index
|
||||
* @interval: the interval on which the ISOC transfer is started
|
||||
* @allocated_requests: number of requests allocated
|
||||
* @queued_requests: number of requests queued for transfer
|
||||
* @name: a human readable name e.g. ep1out-bulk
|
||||
* @direction: true for TX, false for RX
|
||||
* @stream_capable: true when streams are enabled
|
||||
@ -489,6 +508,9 @@ struct dwc3_ep {
|
||||
struct list_head pending_list;
|
||||
struct list_head started_list;
|
||||
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
|
||||
struct dwc3_trb *trb_pool;
|
||||
dma_addr_t trb_pool_dma;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
@ -521,6 +543,8 @@ struct dwc3_ep {
|
||||
u8 number;
|
||||
u8 type;
|
||||
u8 resource_index;
|
||||
u32 allocated_requests;
|
||||
u32 queued_requests;
|
||||
u32 interval;
|
||||
|
||||
char name[20];
|
||||
@ -712,6 +736,8 @@ struct dwc3_scratchpad_array {
|
||||
* @gadget_driver: pointer to the gadget driver
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @fladj: frame length adjustment
|
||||
* @irq_gadget: peripheral controller's IRQ number
|
||||
* @nr_scratch: number of scratch buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
@ -744,6 +770,7 @@ struct dwc3_scratchpad_array {
|
||||
* @lpm_nyet_threshold: LPM NYET response threshold
|
||||
* @hird_threshold: HIRD threshold
|
||||
* @hsphy_interface: "utmi" or "ulpi"
|
||||
* @connected: true when we're connected to a host, false otherwise
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
@ -754,6 +781,7 @@ struct dwc3_scratchpad_array {
|
||||
* 0 - utmi_sleep_n
|
||||
* 1 - utmi_l1_suspend_n
|
||||
* @is_fpga: true when we are using the FPGA board
|
||||
* @pending_events: true when we have pending IRQs to be handled
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
@ -818,10 +846,8 @@ struct dwc3 {
|
||||
|
||||
enum usb_dr_mode dr_mode;
|
||||
|
||||
/* used for suspend/resume */
|
||||
u32 dcfg;
|
||||
u32 gctl;
|
||||
|
||||
u32 fladj;
|
||||
u32 irq_gadget;
|
||||
u32 nr_scratch;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
@ -860,7 +886,7 @@ struct dwc3 {
|
||||
* just so dwc31 revisions are always larger than dwc3.
|
||||
*/
|
||||
#define DWC3_REVISION_IS_DWC31 0x80000000
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31)
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
@ -890,6 +916,7 @@ struct dwc3 {
|
||||
|
||||
const char *hsphy_interface;
|
||||
|
||||
unsigned connected:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
@ -897,6 +924,7 @@ struct dwc3 {
|
||||
unsigned has_lpm_erratum:1;
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
unsigned is_fpga:1;
|
||||
unsigned pending_events:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned three_stage_setup:1;
|
||||
@ -1094,8 +1122,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
@ -1110,8 +1138,8 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
|
||||
enum dwc3_link_state state)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params)
|
||||
{ return 0; }
|
||||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
int cmd, u32 param)
|
||||
@ -1122,6 +1150,7 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc);
|
||||
int dwc3_gadget_resume(struct dwc3 *dwc);
|
||||
void dwc3_gadget_process_pending_events(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
@ -1132,6 +1161,10 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
|
||||
{
|
||||
}
|
||||
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
|
||||
|
@ -128,56 +128,112 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
||||
* dwc3_gadget_event_string - returns event name
|
||||
* @event: the event code
|
||||
*/
|
||||
static inline const char *dwc3_gadget_event_string(u8 event)
|
||||
static inline const char *
|
||||
dwc3_gadget_event_string(const struct dwc3_event_devt *event)
|
||||
{
|
||||
switch (event) {
|
||||
static char str[256];
|
||||
enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
|
||||
|
||||
switch (event->type) {
|
||||
case DWC3_DEVICE_EVENT_DISCONNECT:
|
||||
return "Disconnect";
|
||||
sprintf(str, "Disconnect: [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_RESET:
|
||||
return "Reset";
|
||||
sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_CONNECT_DONE:
|
||||
return "Connection Done";
|
||||
sprintf(str, "Connection Done [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
return "Link Status Change";
|
||||
sprintf(str, "Link Change [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
return "WakeUp";
|
||||
sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
return "End-Of-Frame";
|
||||
sprintf(str, "End-Of-Frame [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
return "Start-Of-Frame";
|
||||
sprintf(str, "Start-Of-Frame [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
return "Erratic Error";
|
||||
sprintf(str, "Erratic Error [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
return "Command Complete";
|
||||
sprintf(str, "Command Complete [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_OVERFLOW:
|
||||
return "Overflow";
|
||||
sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state));
|
||||
break;
|
||||
default:
|
||||
sprintf(str, "UNKNOWN");
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_ep_event_string - returns event name
|
||||
* @event: then event code
|
||||
*/
|
||||
static inline const char *dwc3_ep_event_string(u8 event)
|
||||
static inline const char *
|
||||
dwc3_ep_event_string(const struct dwc3_event_depevt *event)
|
||||
{
|
||||
switch (event) {
|
||||
u8 epnum = event->endpoint_number;
|
||||
static char str[256];
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
ret = sprintf(str, "ep%d%s: ", epnum >> 1,
|
||||
(epnum & 1) ? "in" : "in");
|
||||
if (ret < 0)
|
||||
return "UNKNOWN";
|
||||
|
||||
switch (event->endpoint_event) {
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
return "Transfer Complete";
|
||||
strcat(str, "Transfer Complete");
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
return "Transfer In-Progress";
|
||||
strcat(str, "Transfer In-Progress");
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
return "Transfer Not Ready";
|
||||
strcat(str, "Transfer Not Ready");
|
||||
status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
|
||||
strcat(str, status ? " (Active)" : " (Not Active)");
|
||||
break;
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
return "FIFO";
|
||||
strcat(str, "FIFO");
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
return "Stream";
|
||||
status = event->status;
|
||||
|
||||
switch (status) {
|
||||
case DEPEVT_STREAMEVT_FOUND:
|
||||
sprintf(str + ret, " Stream %d Found",
|
||||
event->parameters);
|
||||
break;
|
||||
case DEPEVT_STREAMEVT_NOTFOUND:
|
||||
default:
|
||||
strcat(str, " Stream Not Found");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
return "Endpoint Command Complete";
|
||||
strcat(str, "Endpoint Command Complete");
|
||||
break;
|
||||
default:
|
||||
sprintf(str, "UNKNOWN");
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,6 +270,46 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *dwc3_decode_event(u32 event)
|
||||
{
|
||||
const union dwc3_event evt = (union dwc3_event) event;
|
||||
|
||||
if (evt.type.is_devspec)
|
||||
return dwc3_gadget_event_string(&evt.devt);
|
||||
else
|
||||
return dwc3_ep_event_string(&evt.depevt);
|
||||
}
|
||||
|
||||
static inline const char *dwc3_ep_cmd_status_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case -ETIMEDOUT:
|
||||
return "Timed Out";
|
||||
case 0:
|
||||
return "Successful";
|
||||
case DEPEVT_TRANSFER_NO_RESOURCE:
|
||||
return "No Resource";
|
||||
case DEPEVT_TRANSFER_BUS_EXPIRY:
|
||||
return "Bus Expiry";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case -ETIMEDOUT:
|
||||
return "Timed Out";
|
||||
case 0:
|
||||
return "Successful";
|
||||
case 1:
|
||||
return "Error";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -36,9 +36,32 @@
|
||||
#define dump_register(nm) \
|
||||
{ \
|
||||
.name = __stringify(nm), \
|
||||
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
|
||||
.offset = DWC3_ ##nm, \
|
||||
}
|
||||
|
||||
#define dump_ep_register_set(n) \
|
||||
{ \
|
||||
.name = "DEPCMDPAR2("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR2, \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMDPAR1("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR1, \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMDPAR0("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR0, \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMD("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMD, \
|
||||
}
|
||||
|
||||
|
||||
static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GSBUSCFG0),
|
||||
dump_register(GSBUSCFG1),
|
||||
@ -47,6 +70,7 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GCTL),
|
||||
dump_register(GEVTEN),
|
||||
dump_register(GSTS),
|
||||
dump_register(GUCTL1),
|
||||
dump_register(GSNPSID),
|
||||
dump_register(GGPIO),
|
||||
dump_register(GUID),
|
||||
@ -218,137 +242,38 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(DGCMD),
|
||||
dump_register(DALEPENA),
|
||||
|
||||
dump_register(DEPCMDPAR2(0)),
|
||||
dump_register(DEPCMDPAR2(1)),
|
||||
dump_register(DEPCMDPAR2(2)),
|
||||
dump_register(DEPCMDPAR2(3)),
|
||||
dump_register(DEPCMDPAR2(4)),
|
||||
dump_register(DEPCMDPAR2(5)),
|
||||
dump_register(DEPCMDPAR2(6)),
|
||||
dump_register(DEPCMDPAR2(7)),
|
||||
dump_register(DEPCMDPAR2(8)),
|
||||
dump_register(DEPCMDPAR2(9)),
|
||||
dump_register(DEPCMDPAR2(10)),
|
||||
dump_register(DEPCMDPAR2(11)),
|
||||
dump_register(DEPCMDPAR2(12)),
|
||||
dump_register(DEPCMDPAR2(13)),
|
||||
dump_register(DEPCMDPAR2(14)),
|
||||
dump_register(DEPCMDPAR2(15)),
|
||||
dump_register(DEPCMDPAR2(16)),
|
||||
dump_register(DEPCMDPAR2(17)),
|
||||
dump_register(DEPCMDPAR2(18)),
|
||||
dump_register(DEPCMDPAR2(19)),
|
||||
dump_register(DEPCMDPAR2(20)),
|
||||
dump_register(DEPCMDPAR2(21)),
|
||||
dump_register(DEPCMDPAR2(22)),
|
||||
dump_register(DEPCMDPAR2(23)),
|
||||
dump_register(DEPCMDPAR2(24)),
|
||||
dump_register(DEPCMDPAR2(25)),
|
||||
dump_register(DEPCMDPAR2(26)),
|
||||
dump_register(DEPCMDPAR2(27)),
|
||||
dump_register(DEPCMDPAR2(28)),
|
||||
dump_register(DEPCMDPAR2(29)),
|
||||
dump_register(DEPCMDPAR2(30)),
|
||||
dump_register(DEPCMDPAR2(31)),
|
||||
|
||||
dump_register(DEPCMDPAR1(0)),
|
||||
dump_register(DEPCMDPAR1(1)),
|
||||
dump_register(DEPCMDPAR1(2)),
|
||||
dump_register(DEPCMDPAR1(3)),
|
||||
dump_register(DEPCMDPAR1(4)),
|
||||
dump_register(DEPCMDPAR1(5)),
|
||||
dump_register(DEPCMDPAR1(6)),
|
||||
dump_register(DEPCMDPAR1(7)),
|
||||
dump_register(DEPCMDPAR1(8)),
|
||||
dump_register(DEPCMDPAR1(9)),
|
||||
dump_register(DEPCMDPAR1(10)),
|
||||
dump_register(DEPCMDPAR1(11)),
|
||||
dump_register(DEPCMDPAR1(12)),
|
||||
dump_register(DEPCMDPAR1(13)),
|
||||
dump_register(DEPCMDPAR1(14)),
|
||||
dump_register(DEPCMDPAR1(15)),
|
||||
dump_register(DEPCMDPAR1(16)),
|
||||
dump_register(DEPCMDPAR1(17)),
|
||||
dump_register(DEPCMDPAR1(18)),
|
||||
dump_register(DEPCMDPAR1(19)),
|
||||
dump_register(DEPCMDPAR1(20)),
|
||||
dump_register(DEPCMDPAR1(21)),
|
||||
dump_register(DEPCMDPAR1(22)),
|
||||
dump_register(DEPCMDPAR1(23)),
|
||||
dump_register(DEPCMDPAR1(24)),
|
||||
dump_register(DEPCMDPAR1(25)),
|
||||
dump_register(DEPCMDPAR1(26)),
|
||||
dump_register(DEPCMDPAR1(27)),
|
||||
dump_register(DEPCMDPAR1(28)),
|
||||
dump_register(DEPCMDPAR1(29)),
|
||||
dump_register(DEPCMDPAR1(30)),
|
||||
dump_register(DEPCMDPAR1(31)),
|
||||
|
||||
dump_register(DEPCMDPAR0(0)),
|
||||
dump_register(DEPCMDPAR0(1)),
|
||||
dump_register(DEPCMDPAR0(2)),
|
||||
dump_register(DEPCMDPAR0(3)),
|
||||
dump_register(DEPCMDPAR0(4)),
|
||||
dump_register(DEPCMDPAR0(5)),
|
||||
dump_register(DEPCMDPAR0(6)),
|
||||
dump_register(DEPCMDPAR0(7)),
|
||||
dump_register(DEPCMDPAR0(8)),
|
||||
dump_register(DEPCMDPAR0(9)),
|
||||
dump_register(DEPCMDPAR0(10)),
|
||||
dump_register(DEPCMDPAR0(11)),
|
||||
dump_register(DEPCMDPAR0(12)),
|
||||
dump_register(DEPCMDPAR0(13)),
|
||||
dump_register(DEPCMDPAR0(14)),
|
||||
dump_register(DEPCMDPAR0(15)),
|
||||
dump_register(DEPCMDPAR0(16)),
|
||||
dump_register(DEPCMDPAR0(17)),
|
||||
dump_register(DEPCMDPAR0(18)),
|
||||
dump_register(DEPCMDPAR0(19)),
|
||||
dump_register(DEPCMDPAR0(20)),
|
||||
dump_register(DEPCMDPAR0(21)),
|
||||
dump_register(DEPCMDPAR0(22)),
|
||||
dump_register(DEPCMDPAR0(23)),
|
||||
dump_register(DEPCMDPAR0(24)),
|
||||
dump_register(DEPCMDPAR0(25)),
|
||||
dump_register(DEPCMDPAR0(26)),
|
||||
dump_register(DEPCMDPAR0(27)),
|
||||
dump_register(DEPCMDPAR0(28)),
|
||||
dump_register(DEPCMDPAR0(29)),
|
||||
dump_register(DEPCMDPAR0(30)),
|
||||
dump_register(DEPCMDPAR0(31)),
|
||||
|
||||
dump_register(DEPCMD(0)),
|
||||
dump_register(DEPCMD(1)),
|
||||
dump_register(DEPCMD(2)),
|
||||
dump_register(DEPCMD(3)),
|
||||
dump_register(DEPCMD(4)),
|
||||
dump_register(DEPCMD(5)),
|
||||
dump_register(DEPCMD(6)),
|
||||
dump_register(DEPCMD(7)),
|
||||
dump_register(DEPCMD(8)),
|
||||
dump_register(DEPCMD(9)),
|
||||
dump_register(DEPCMD(10)),
|
||||
dump_register(DEPCMD(11)),
|
||||
dump_register(DEPCMD(12)),
|
||||
dump_register(DEPCMD(13)),
|
||||
dump_register(DEPCMD(14)),
|
||||
dump_register(DEPCMD(15)),
|
||||
dump_register(DEPCMD(16)),
|
||||
dump_register(DEPCMD(17)),
|
||||
dump_register(DEPCMD(18)),
|
||||
dump_register(DEPCMD(19)),
|
||||
dump_register(DEPCMD(20)),
|
||||
dump_register(DEPCMD(21)),
|
||||
dump_register(DEPCMD(22)),
|
||||
dump_register(DEPCMD(23)),
|
||||
dump_register(DEPCMD(24)),
|
||||
dump_register(DEPCMD(25)),
|
||||
dump_register(DEPCMD(26)),
|
||||
dump_register(DEPCMD(27)),
|
||||
dump_register(DEPCMD(28)),
|
||||
dump_register(DEPCMD(29)),
|
||||
dump_register(DEPCMD(30)),
|
||||
dump_register(DEPCMD(31)),
|
||||
dump_ep_register_set(0),
|
||||
dump_ep_register_set(1),
|
||||
dump_ep_register_set(2),
|
||||
dump_ep_register_set(3),
|
||||
dump_ep_register_set(4),
|
||||
dump_ep_register_set(5),
|
||||
dump_ep_register_set(6),
|
||||
dump_ep_register_set(7),
|
||||
dump_ep_register_set(8),
|
||||
dump_ep_register_set(9),
|
||||
dump_ep_register_set(10),
|
||||
dump_ep_register_set(11),
|
||||
dump_ep_register_set(12),
|
||||
dump_ep_register_set(13),
|
||||
dump_ep_register_set(14),
|
||||
dump_ep_register_set(15),
|
||||
dump_ep_register_set(16),
|
||||
dump_ep_register_set(17),
|
||||
dump_ep_register_set(18),
|
||||
dump_ep_register_set(19),
|
||||
dump_ep_register_set(20),
|
||||
dump_ep_register_set(21),
|
||||
dump_ep_register_set(22),
|
||||
dump_ep_register_set(23),
|
||||
dump_ep_register_set(24),
|
||||
dump_ep_register_set(25),
|
||||
dump_ep_register_set(26),
|
||||
dump_ep_register_set(27),
|
||||
dump_ep_register_set(28),
|
||||
dump_ep_register_set(29),
|
||||
dump_ep_register_set(30),
|
||||
dump_ep_register_set(31),
|
||||
|
||||
dump_register(OCFG),
|
||||
dump_register(OCTL),
|
||||
@ -939,7 +864,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
|
||||
dwc->regset->regs = dwc3_regs;
|
||||
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
|
||||
dwc->regset->base = dwc->regs;
|
||||
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
if (!file)
|
||||
|
@ -165,7 +165,7 @@ static void dwc3_omap_write_utmi_ctrl(struct dwc3_omap *omap, u32 value)
|
||||
|
||||
static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 -
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_0 -
|
||||
omap->irq0_offset);
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value)
|
||||
|
||||
static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC +
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_MISC +
|
||||
omap->irqmisc_offset);
|
||||
}
|
||||
|
||||
@ -231,35 +231,30 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
|
||||
}
|
||||
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_CTRL_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_SESSEND);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
|
||||
val &= ~USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_VBUS_VALID:
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val &= ~USBOTGSS_UTMI_OTG_CTRL_SESSEND;
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_SESSVALID;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_ID_FLOAT:
|
||||
if (omap->vbus_reg)
|
||||
regulator_disable(omap->vbus_reg);
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
|
||||
case OMAP_DWC3_VBUS_OFF:
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_CTRL_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND
|
||||
| USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
break;
|
||||
|
||||
@ -268,19 +263,38 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap);
|
||||
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap);
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
|
||||
if (dwc3_omap_read_irqmisc_status(omap) ||
|
||||
dwc3_omap_read_irq0_status(omap)) {
|
||||
/* mask irqs */
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt_thread(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
u32 reg;
|
||||
|
||||
/* clear irq status flags */
|
||||
reg = dwc3_omap_read_irqmisc_status(omap);
|
||||
|
||||
dwc3_omap_write_irqmisc_status(omap, reg);
|
||||
|
||||
reg = dwc3_omap_read_irq0_status(omap);
|
||||
|
||||
dwc3_omap_write_irq0_status(omap, reg);
|
||||
|
||||
/* unmask irqs */
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -497,8 +511,9 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
|
||||
dwc3_omap_interrupt_thread, IRQF_SHARED,
|
||||
"dwc3-omap", omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
|
@ -20,11 +20,11 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "platform_data.h"
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce
|
||||
@ -51,62 +51,70 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
|
||||
{
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
|
||||
struct dwc3_platform_data pdata;
|
||||
struct property_entry properties[] = {
|
||||
PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
|
||||
PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf),
|
||||
PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"),
|
||||
PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1),
|
||||
/*
|
||||
* FIXME these quirks should be removed when AMD NL
|
||||
* tapes out
|
||||
*/
|
||||
PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
|
||||
{ },
|
||||
};
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
|
||||
pdata.has_lpm_erratum = true;
|
||||
pdata.lpm_nyet_threshold = 0xf;
|
||||
|
||||
pdata.u2exit_lfps_quirk = true;
|
||||
pdata.u2ss_inp3_quirk = true;
|
||||
pdata.req_p1p2p3_quirk = true;
|
||||
pdata.del_p1p2p3_quirk = true;
|
||||
pdata.del_phy_power_chg_quirk = true;
|
||||
pdata.lfps_filter_quirk = true;
|
||||
pdata.rx_detect_poll_quirk = true;
|
||||
|
||||
pdata.tx_de_emphasis_quirk = true;
|
||||
pdata.tx_de_emphasis = 1;
|
||||
|
||||
/*
|
||||
* FIXME these quirks should be removed when AMD NL
|
||||
* taps out
|
||||
*/
|
||||
pdata.disable_scramble_quirk = true;
|
||||
pdata.dis_u3_susphy_quirk = true;
|
||||
pdata.dis_u2_susphy_quirk = true;
|
||||
|
||||
return platform_device_add_data(dwc3, &pdata, sizeof(pdata));
|
||||
return platform_device_add_properties(dwc3, properties);
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
|
||||
struct gpio_desc *gpio;
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
int ret;
|
||||
|
||||
acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
acpi_dwc3_byt_gpios);
|
||||
struct property_entry properties[] = {
|
||||
PROPERTY_ENTRY_STRING("dr-mode", "peripheral"),
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* These GPIOs will turn on the USB2 PHY. Note that we have to
|
||||
* put the gpio descriptors again here because the phy driver
|
||||
* might want to grab them, too.
|
||||
*/
|
||||
gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
ret = platform_device_add_properties(dwc3, properties);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
acpi_dwc3_byt_gpios);
|
||||
|
||||
/*
|
||||
* These GPIOs will turn on the USB2 PHY. Note that we have to
|
||||
* put the gpio descriptors again here because the phy driver
|
||||
* might want to grab them, too.
|
||||
*/
|
||||
gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,15 +122,14 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
|
||||
(pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 ||
|
||||
pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI ||
|
||||
pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) {
|
||||
struct property_entry properties[] = {
|
||||
PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"),
|
||||
PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"),
|
||||
{ },
|
||||
};
|
||||
|
||||
struct dwc3_platform_data pdata;
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
pdata.usb3_lpm_capable = true;
|
||||
pdata.has_lpm_erratum = true;
|
||||
pdata.dis_enblslpm_quirk = true;
|
||||
|
||||
return platform_device_add_data(dwc3, &pdata, sizeof(pdata));
|
||||
return platform_device_add_properties(dwc3, properties);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -180,7 +187,11 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
||||
goto err;
|
||||
}
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
device_set_run_wake(dev, true);
|
||||
pci_set_drvdata(pci, dwc3);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
platform_device_put(dwc3);
|
||||
@ -189,6 +200,8 @@ err:
|
||||
|
||||
static void dwc3_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
device_init_wakeup(&pci->dev, false);
|
||||
pm_runtime_get(&pci->dev);
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev));
|
||||
platform_device_unregister(pci_get_drvdata(pci));
|
||||
}
|
||||
@ -219,11 +232,43 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_pci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
if (device_run_wake(dev))
|
||||
return 0;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int dwc3_pci_pm_dummy(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* There's nothing to do here. No, seriously. Everything is either taken
|
||||
* care either by PCI subsystem or dwc3/core.c, so we have nothing
|
||||
* missing here.
|
||||
*
|
||||
* So you'd think we didn't need this at all, but PCI subsystem will
|
||||
* bail out if we don't have a valid callback :-s
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct dev_pm_ops dwc3_pci_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_pm_dummy, dwc3_pci_pm_dummy)
|
||||
SET_RUNTIME_PM_OPS(dwc3_pci_runtime_suspend, dwc3_pci_pm_dummy,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct pci_driver dwc3_pci_driver = {
|
||||
.name = "dwc3-pci",
|
||||
.id_table = dwc3_pci_id_table,
|
||||
.probe = dwc3_pci_probe,
|
||||
.remove = dwc3_pci_remove,
|
||||
.driver = {
|
||||
.pm = &dwc3_pci_dev_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
|
@ -98,8 +98,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
|
||||
trace_dwc3_prepare_trb(dep, trb);
|
||||
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_STARTTRANSFER, ¶ms);
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms);
|
||||
if (ret < 0) {
|
||||
dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
|
||||
dep->name);
|
||||
@ -107,9 +106,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
}
|
||||
|
||||
dep->flags |= DWC3_EP_BUSY;
|
||||
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
|
||||
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
|
||||
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
|
||||
|
||||
return 0;
|
||||
@ -499,7 +496,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
case USB_RECIP_ENDPOINT:
|
||||
switch (wValue) {
|
||||
case USB_ENDPOINT_HALT:
|
||||
dep = dwc3_wIndex_to_dep(dwc, wIndex);
|
||||
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
|
||||
if (!dep)
|
||||
return -EINVAL;
|
||||
if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
|
||||
@ -622,8 +619,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
||||
struct timing {
|
||||
u8 u1sel;
|
||||
u8 u1pel;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
__le16 u2sel;
|
||||
__le16 u2pel;
|
||||
} __packed timing;
|
||||
|
||||
int ret;
|
||||
@ -980,7 +977,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1008,7 +1005,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1058,7 +1055,7 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
cmd |= DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
}
|
||||
@ -1112,11 +1109,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
u8 epnum = event->endpoint_number;
|
||||
|
||||
dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
|
||||
dwc3_ep_event_string(event->endpoint_event),
|
||||
epnum >> 1, (epnum & 1) ? "in" : "out",
|
||||
dwc3_trace(trace_dwc3_ep0, "%s: state '%s'",
|
||||
dwc3_ep_event_string(event),
|
||||
dwc3_ep0_state_string(dwc->ep0state));
|
||||
|
||||
switch (event->endpoint_event) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -95,11 +95,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
*
|
||||
* Caller should take care of locking
|
||||
*/
|
||||
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
|
||||
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
|
||||
{
|
||||
u32 res_id;
|
||||
|
||||
res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number));
|
||||
res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
|
||||
|
||||
return DWC3_DEPCMD_GET_RSC_IDX(res_id);
|
||||
}
|
||||
|
@ -16,15 +16,55 @@
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/xhci_pdriver.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct property_entry props[2];
|
||||
struct platform_device *xhci;
|
||||
struct usb_xhci_pdata pdata;
|
||||
int ret;
|
||||
int ret, irq;
|
||||
struct resource *res;
|
||||
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
|
||||
|
||||
irq = platform_get_irq_byname(dwc3_pdev, "host");
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return irq;
|
||||
|
||||
if (irq <= 0) {
|
||||
irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return irq;
|
||||
|
||||
if (irq <= 0) {
|
||||
irq = platform_get_irq(dwc3_pdev, 0);
|
||||
if (irq <= 0) {
|
||||
if (irq != -EPROBE_DEFER) {
|
||||
dev_err(dwc->dev,
|
||||
"missing host IRQ\n");
|
||||
}
|
||||
if (!irq)
|
||||
irq = -EINVAL;
|
||||
return irq;
|
||||
} else {
|
||||
res = platform_get_resource(dwc3_pdev,
|
||||
IORESOURCE_IRQ, 0);
|
||||
}
|
||||
} else {
|
||||
res = platform_get_resource_byname(dwc3_pdev,
|
||||
IORESOURCE_IRQ,
|
||||
"dwc_usb3");
|
||||
}
|
||||
|
||||
} else {
|
||||
res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
|
||||
"host");
|
||||
}
|
||||
|
||||
dwc->xhci_resources[1].start = irq;
|
||||
dwc->xhci_resources[1].end = irq;
|
||||
dwc->xhci_resources[1].flags = res->flags;
|
||||
dwc->xhci_resources[1].name = res->name;
|
||||
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
if (!xhci) {
|
||||
@ -47,14 +87,15 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
|
||||
|
||||
pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
|
||||
|
||||
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "couldn't add platform data to xHCI device\n");
|
||||
goto err1;
|
||||
if (dwc->usb3_lpm_capable) {
|
||||
props[0].name = "usb3-lpm-capable";
|
||||
ret = platform_device_add_properties(xhci, props);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to add properties to xHCI\n");
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
|
||||
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
u32 offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
u32 value;
|
||||
|
||||
/*
|
||||
@ -34,7 +33,7 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
value = readl(base + offs);
|
||||
value = readl(base + offset - DWC3_GLOBALS_REGS_START);
|
||||
|
||||
/*
|
||||
* When tracing we want to make it easy to find the correct address on
|
||||
@ -49,14 +48,12 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
u32 offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
writel(value, base + offs);
|
||||
writel(value, base + offset - DWC3_GLOBALS_REGS_START);
|
||||
|
||||
/*
|
||||
* When tracing we want to make it easy to find the correct address on
|
||||
|
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* platform_data.h - USB DWC3 Platform Data Support
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
struct dwc3_platform_data {
|
||||
enum usb_device_speed maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
bool usb3_lpm_capable;
|
||||
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
u8 hird_threshold;
|
||||
|
||||
u8 lpm_nyet_threshold;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned has_lpm_erratum:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
unsigned u2ss_inp3_quirk:1;
|
||||
unsigned req_p1p2p3_quirk:1;
|
||||
unsigned del_p1p2p3_quirk:1;
|
||||
unsigned del_phy_power_chg_quirk:1;
|
||||
unsigned lfps_filter_quirk:1;
|
||||
unsigned rx_detect_poll_quirk:1;
|
||||
unsigned dis_u3_susphy_quirk:1;
|
||||
unsigned dis_u2_susphy_quirk:1;
|
||||
unsigned dis_enblslpm_quirk:1;
|
||||
unsigned dis_rxdet_inp3_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
u32 fladj_value;
|
||||
|
||||
const char *hsphy_interface;
|
||||
};
|
@ -71,7 +71,8 @@ DECLARE_EVENT_CLASS(dwc3_log_event,
|
||||
TP_fast_assign(
|
||||
__entry->event = event;
|
||||
),
|
||||
TP_printk("event %08x", __entry->event)
|
||||
TP_printk("event (%08x): %s", __entry->event,
|
||||
dwc3_decode_event(__entry->event))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_event, dwc3_event,
|
||||
@ -85,21 +86,21 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
||||
TP_STRUCT__entry(
|
||||
__field(__u8, bRequestType)
|
||||
__field(__u8, bRequest)
|
||||
__field(__le16, wValue)
|
||||
__field(__le16, wIndex)
|
||||
__field(__le16, wLength)
|
||||
__field(__u16, wValue)
|
||||
__field(__u16, wIndex)
|
||||
__field(__u16, wLength)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->bRequestType = ctrl->bRequestType;
|
||||
__entry->bRequest = ctrl->bRequest;
|
||||
__entry->wValue = ctrl->wValue;
|
||||
__entry->wIndex = ctrl->wIndex;
|
||||
__entry->wLength = ctrl->wLength;
|
||||
__entry->wValue = le16_to_cpu(ctrl->wValue);
|
||||
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
__entry->wLength = le16_to_cpu(ctrl->wLength);
|
||||
),
|
||||
TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d",
|
||||
__entry->bRequestType, __entry->bRequest,
|
||||
le16_to_cpu(__entry->wValue), le16_to_cpu(__entry->wIndex),
|
||||
le16_to_cpu(__entry->wLength)
|
||||
__entry->wValue, __entry->wIndex,
|
||||
__entry->wLength
|
||||
)
|
||||
);
|
||||
|
||||
@ -166,37 +167,41 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param),
|
||||
TP_ARGS(cmd, param),
|
||||
TP_PROTO(unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(cmd, param, status),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param)
|
||||
__field(int, status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->cmd = cmd;
|
||||
__entry->param = param;
|
||||
__entry->status = status;
|
||||
),
|
||||
TP_printk("cmd '%s' [%d] param %08x",
|
||||
TP_printk("cmd '%s' [%d] param %08x --> status: %s",
|
||||
dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param
|
||||
__entry->cmd, __entry->param,
|
||||
dwc3_gadget_generic_cmd_status_string(__entry->status)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param),
|
||||
TP_ARGS(cmd, param)
|
||||
TP_PROTO(unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(cmd, param, status)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params),
|
||||
TP_ARGS(dep, cmd, params),
|
||||
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
|
||||
TP_ARGS(dep, cmd, params, cmd_status),
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, DWC3_MSG_MAX)
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param0)
|
||||
__field(u32, param1)
|
||||
__field(u32, param2)
|
||||
__field(int, cmd_status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
|
||||
@ -204,18 +209,20 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
__entry->param0 = params->param0;
|
||||
__entry->param1 = params->param1;
|
||||
__entry->param2 = params->param2;
|
||||
__entry->cmd_status = cmd_status;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x",
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x --> status: %s",
|
||||
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param0,
|
||||
__entry->param1, __entry->param2
|
||||
__entry->param1, __entry->param2,
|
||||
dwc3_ep_cmd_status_string(__entry->cmd_status)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd,
|
||||
TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params),
|
||||
TP_ARGS(dep, cmd, params)
|
||||
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
|
||||
TP_ARGS(dep, cmd, params, cmd_status)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
@ -224,6 +231,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, DWC3_MSG_MAX)
|
||||
__field(struct dwc3_trb *, trb)
|
||||
__field(u32, allocated)
|
||||
__field(u32, queued)
|
||||
__field(u32, bpl)
|
||||
__field(u32, bph)
|
||||
__field(u32, size)
|
||||
@ -232,14 +241,53 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
|
||||
__entry->trb = trb;
|
||||
__entry->allocated = dep->allocated_requests;
|
||||
__entry->queued = dep->queued_requests;
|
||||
__entry->bpl = trb->bpl;
|
||||
__entry->bph = trb->bph;
|
||||
__entry->size = trb->size;
|
||||
__entry->ctrl = trb->ctrl;
|
||||
),
|
||||
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x",
|
||||
__get_str(name), __entry->trb, __entry->bph, __entry->bpl,
|
||||
__entry->size, __entry->ctrl
|
||||
TP_printk("%s: %d/%d trb %p buf %08x%08x size %d ctrl %08x (%c%c%c%c:%c%c:%s)",
|
||||
__get_str(name), __entry->queued, __entry->allocated,
|
||||
__entry->trb, __entry->bph, __entry->bpl,
|
||||
__entry->size, __entry->ctrl,
|
||||
__entry->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_CSP ? 'S' : 's',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_ISP_IMI ? 'S' : 's',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_IOC ? 'C' : 'c',
|
||||
({char *s;
|
||||
switch (__entry->ctrl & 0x3f0) {
|
||||
case DWC3_TRBCTL_NORMAL:
|
||||
s = "normal";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_SETUP:
|
||||
s = "setup";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_STATUS2:
|
||||
s = "status2";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_STATUS3:
|
||||
s = "status3";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_DATA:
|
||||
s = "data";
|
||||
break;
|
||||
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
|
||||
s = "isoc-first";
|
||||
break;
|
||||
case DWC3_TRBCTL_ISOCHRONOUS:
|
||||
s = "isoc";
|
||||
break;
|
||||
case DWC3_TRBCTL_LINK_TRB:
|
||||
s = "link";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
break;
|
||||
} s; })
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
@ -1093,5 +1093,5 @@ static int __init kgdbdbgp_start_thread(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(kgdbdbgp_start_thread);
|
||||
device_initcall(kgdbdbgp_start_thread);
|
||||
#endif /* CONFIG_KGDB */
|
||||
|
@ -114,7 +114,7 @@ config USB_GADGET_VBUS_DRAW
|
||||
|
||||
config USB_GADGET_STORAGE_NUM_BUFFERS
|
||||
int "Number of storage pipeline buffers"
|
||||
range 2 32
|
||||
range 2 256
|
||||
default 2
|
||||
help
|
||||
Usually 2 buffers are enough to establish a good buffering
|
||||
|
@ -93,7 +93,7 @@ int usb_gadget_config_buf(
|
||||
*cp = *config;
|
||||
|
||||
/* then interface/endpoint/class/vendor/... */
|
||||
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
|
||||
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
|
||||
length - USB_DT_CONFIG_SIZE, desc);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
@ -130,6 +130,12 @@ struct ffs_epfile {
|
||||
|
||||
struct dentry *dentry;
|
||||
|
||||
/*
|
||||
* Buffer for holding data from partial reads which may happen since
|
||||
* we’re rounding user read requests to a multiple of a max packet size.
|
||||
*/
|
||||
struct ffs_buffer *read_buffer; /* P: epfile->mutex */
|
||||
|
||||
char name[5];
|
||||
|
||||
unsigned char in; /* P: ffs->eps_lock */
|
||||
@ -138,6 +144,12 @@ struct ffs_epfile {
|
||||
unsigned char _pad;
|
||||
};
|
||||
|
||||
struct ffs_buffer {
|
||||
size_t length;
|
||||
char *data;
|
||||
char storage[];
|
||||
};
|
||||
|
||||
/* ffs_io_data structure ***************************************************/
|
||||
|
||||
struct ffs_io_data {
|
||||
@ -640,6 +652,49 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
|
||||
{
|
||||
ssize_t ret = copy_to_iter(data, data_len, iter);
|
||||
if (likely(ret == data_len))
|
||||
return ret;
|
||||
|
||||
if (unlikely(iov_iter_count(iter)))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Dear user space developer!
|
||||
*
|
||||
* TL;DR: To stop getting below error message in your kernel log, change
|
||||
* user space code using functionfs to align read buffers to a max
|
||||
* packet size.
|
||||
*
|
||||
* Some UDCs (e.g. dwc3) require request sizes to be a multiple of a max
|
||||
* packet size. When unaligned buffer is passed to functionfs, it
|
||||
* internally uses a larger, aligned buffer so that such UDCs are happy.
|
||||
*
|
||||
* Unfortunately, this means that host may send more data than was
|
||||
* requested in read(2) system call. f_fs doesn’t know what to do with
|
||||
* that excess data so it simply drops it.
|
||||
*
|
||||
* Was the buffer aligned in the first place, no such problem would
|
||||
* happen.
|
||||
*
|
||||
* Data may be dropped only in AIO reads. Synchronous reads are handled
|
||||
* by splitting a request into multiple parts. This splitting may still
|
||||
* be a problem though so it’s likely best to align the buffer
|
||||
* regardless of it being AIO or not..
|
||||
*
|
||||
* This only affects OUT endpoints, i.e. reading data with a read(2),
|
||||
* aio_read(2) etc. system calls. Writing data to an IN endpoint is not
|
||||
* affected.
|
||||
*/
|
||||
pr_err("functionfs read size %d > requested size %zd, dropping excess data. "
|
||||
"Align read buffer size to max packet size to avoid the problem.\n",
|
||||
data_len, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ffs_user_copy_worker(struct work_struct *work)
|
||||
{
|
||||
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
|
||||
@ -650,9 +705,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
|
||||
|
||||
if (io_data->read && ret > 0) {
|
||||
use_mm(io_data->mm);
|
||||
ret = copy_to_iter(io_data->buf, ret, &io_data->data);
|
||||
if (ret != io_data->req->actual && iov_iter_count(&io_data->data))
|
||||
ret = -EFAULT;
|
||||
ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
|
||||
unuse_mm(io_data->mm);
|
||||
}
|
||||
|
||||
@ -680,6 +733,58 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
||||
schedule_work(&io_data->work);
|
||||
}
|
||||
|
||||
/* Assumes epfile->mutex is held. */
|
||||
static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
|
||||
struct iov_iter *iter)
|
||||
{
|
||||
struct ffs_buffer *buf = epfile->read_buffer;
|
||||
ssize_t ret;
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
ret = copy_to_iter(buf->data, buf->length, iter);
|
||||
if (buf->length == ret) {
|
||||
kfree(buf);
|
||||
epfile->read_buffer = NULL;
|
||||
} else if (unlikely(iov_iter_count(iter))) {
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
buf->length -= ret;
|
||||
buf->data += ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Assumes epfile->mutex is held. */
|
||||
static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
|
||||
void *data, int data_len,
|
||||
struct iov_iter *iter)
|
||||
{
|
||||
struct ffs_buffer *buf;
|
||||
|
||||
ssize_t ret = copy_to_iter(data, data_len, iter);
|
||||
if (likely(data_len == ret))
|
||||
return ret;
|
||||
|
||||
if (unlikely(iov_iter_count(iter)))
|
||||
return -EFAULT;
|
||||
|
||||
/* See ffs_copy_to_iter for more context. */
|
||||
pr_warn("functionfs read size %d > requested size %zd, splitting request into multiple reads.",
|
||||
data_len, ret);
|
||||
|
||||
data_len -= ret;
|
||||
buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
buf->length = data_len;
|
||||
buf->data = buf->storage;
|
||||
memcpy(buf->storage, data + ret, data_len);
|
||||
epfile->read_buffer = buf;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
{
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
@ -709,21 +814,40 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
if (halt && epfile->isoc)
|
||||
return -EINVAL;
|
||||
|
||||
/* We will be using request and read_buffer */
|
||||
ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
/* Allocate & copy */
|
||||
if (!halt) {
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
/*
|
||||
* Do we have buffered data from previous partial read? Check
|
||||
* that for synchronous case only because we do not have
|
||||
* facility to ‘wake up’ a pending asynchronous read and push
|
||||
* buffered data to it which we would need to make things behave
|
||||
* consistently.
|
||||
*/
|
||||
if (!io_data->aio && io_data->read) {
|
||||
ret = __ffs_epfile_read_buffered(epfile, &io_data->data);
|
||||
if (ret)
|
||||
goto error_mutex;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we _do_ wait above, the epfile->ffs->gadget might be NULL
|
||||
* before the waiting completes, so do not assign to 'gadget'
|
||||
* earlier
|
||||
*/
|
||||
struct usb_gadget *gadget = epfile->ffs->gadget;
|
||||
size_t copied;
|
||||
gadget = epfile->ffs->gadget;
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
/* In the meantime, endpoint got disabled or changed. */
|
||||
if (epfile->ep != ep) {
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
return -ESHUTDOWN;
|
||||
ret = -ESHUTDOWN;
|
||||
goto error_lock;
|
||||
}
|
||||
data_len = iov_iter_count(&io_data->data);
|
||||
/*
|
||||
@ -735,22 +859,17 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
data = kmalloc(data_len, GFP_KERNEL);
|
||||
if (unlikely(!data))
|
||||
return -ENOMEM;
|
||||
if (!io_data->read) {
|
||||
copied = copy_from_iter(data, data_len, &io_data->data);
|
||||
if (copied != data_len) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
if (unlikely(!data)) {
|
||||
ret = -ENOMEM;
|
||||
goto error_mutex;
|
||||
}
|
||||
if (!io_data->read &&
|
||||
copy_from_iter(data, data_len, &io_data->data) != data_len) {
|
||||
ret = -EFAULT;
|
||||
goto error_mutex;
|
||||
}
|
||||
}
|
||||
|
||||
/* We will be using request */
|
||||
ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
|
||||
if (unlikely(ret))
|
||||
goto error;
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (epfile->ep != ep) {
|
||||
@ -803,18 +922,13 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
interrupted = ep->status < 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX We may end up silently droping data here. Since data_len
|
||||
* (i.e. req->length) may be bigger than len (after being
|
||||
* rounded up to maxpacketsize), we may end up with more data
|
||||
* then user space has space for.
|
||||
*/
|
||||
ret = interrupted ? -EINTR : ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = copy_to_iter(data, ret, &io_data->data);
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
if (interrupted)
|
||||
ret = -EINTR;
|
||||
else if (io_data->read && ep->status > 0)
|
||||
ret = __ffs_epfile_read_data(epfile, data, ep->status,
|
||||
&io_data->data);
|
||||
else
|
||||
ret = ep->status;
|
||||
goto error_mutex;
|
||||
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) {
|
||||
ret = -ENOMEM;
|
||||
@ -980,6 +1094,8 @@ ffs_epfile_release(struct inode *inode, struct file *file)
|
||||
|
||||
ENTER();
|
||||
|
||||
kfree(epfile->read_buffer);
|
||||
epfile->read_buffer = NULL;
|
||||
ffs_data_closed(epfile->ffs);
|
||||
|
||||
return 0;
|
||||
@ -1605,19 +1721,24 @@ static void ffs_func_eps_disable(struct ffs_function *func)
|
||||
unsigned count = func->ffs->eps_count;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||
do {
|
||||
if (epfile)
|
||||
mutex_lock(&epfile->mutex);
|
||||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||
/* pending requests get nuked */
|
||||
if (likely(ep->ep))
|
||||
usb_ep_disable(ep->ep);
|
||||
++ep;
|
||||
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
||||
|
||||
if (epfile) {
|
||||
epfile->ep = NULL;
|
||||
kfree(epfile->read_buffer);
|
||||
epfile->read_buffer = NULL;
|
||||
mutex_unlock(&epfile->mutex);
|
||||
++epfile;
|
||||
}
|
||||
} while (--count);
|
||||
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
||||
}
|
||||
|
||||
static int ffs_func_eps_enable(struct ffs_function *func)
|
||||
@ -2227,8 +2348,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
|
||||
{
|
||||
u32 str_count, needed_count, lang_count;
|
||||
struct usb_gadget_strings **stringtabs, *t;
|
||||
struct usb_string *strings, *s;
|
||||
const char *data = _data;
|
||||
struct usb_string *s;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -2286,7 +2407,6 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
|
||||
stringtabs = vla_ptr(vlabuf, d, stringtabs);
|
||||
t = vla_ptr(vlabuf, d, stringtab);
|
||||
s = vla_ptr(vlabuf, d, strings);
|
||||
strings = s;
|
||||
}
|
||||
|
||||
/* For each language */
|
||||
|
@ -2655,18 +2655,6 @@ void fsg_common_put(struct fsg_common *common)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsg_common_put);
|
||||
|
||||
/* check if fsg_num_buffers is within a valid range */
|
||||
static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers)
|
||||
{
|
||||
#define FSG_MAX_NUM_BUFFERS 32
|
||||
|
||||
if (fsg_num_buffers >= 2 && fsg_num_buffers <= FSG_MAX_NUM_BUFFERS)
|
||||
return 0;
|
||||
pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
|
||||
fsg_num_buffers, 2, FSG_MAX_NUM_BUFFERS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct fsg_common *fsg_common_setup(struct fsg_common *common)
|
||||
{
|
||||
if (!common) {
|
||||
@ -2709,11 +2697,7 @@ static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n)
|
||||
int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
|
||||
{
|
||||
struct fsg_buffhd *bh, *buffhds;
|
||||
int i, rc;
|
||||
|
||||
rc = fsg_num_buffers_validate(n);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
int i;
|
||||
|
||||
buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
|
||||
if (!buffhds)
|
||||
@ -3401,10 +3385,6 @@ static ssize_t fsg_opts_num_buffers_store(struct config_item *item,
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
ret = fsg_num_buffers_validate(num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
fsg_common_set_num_buffers(opts->common, num);
|
||||
ret = len;
|
||||
|
||||
|
@ -907,7 +907,6 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
|
||||
port->port_num, tty, count);
|
||||
@ -917,7 +916,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
count = gs_buf_put(&port->port_write_buf, buf, count);
|
||||
/* treat count == 0 as flush_chars() */
|
||||
if (port->port_usb)
|
||||
status = gs_start_tx(port);
|
||||
gs_start_tx(port);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return count;
|
||||
|
@ -265,7 +265,7 @@ static void *functionfs_acquire_dev(struct ffs_dev *dev)
|
||||
{
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -275,7 +275,7 @@ static void functionfs_release_dev(struct ffs_dev *dev)
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller of this function takes ffs_lock
|
||||
* The caller of this function takes ffs_lock
|
||||
*/
|
||||
static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
{
|
||||
@ -294,12 +294,12 @@ static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
++missing_funcs;
|
||||
gfs_registered = false;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller of this function takes ffs_lock
|
||||
* The caller of this function takes ffs_lock
|
||||
*/
|
||||
static void functionfs_closed_callback(struct ffs_data *ffs)
|
||||
{
|
||||
@ -347,17 +347,14 @@ static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
{
|
||||
struct f_rndis_opts *rndis_opts;
|
||||
|
||||
fi_rndis = usb_get_function_instance("rndis");
|
||||
if (IS_ERR(fi_rndis)) {
|
||||
ret = PTR_ERR(fi_rndis);
|
||||
goto error;
|
||||
}
|
||||
rndis_opts = container_of(fi_rndis, struct f_rndis_opts,
|
||||
func_inst);
|
||||
#ifndef CONFIG_USB_FUNCTIONFS_ETH
|
||||
net = rndis_opts->net;
|
||||
net = container_of(fi_rndis, struct f_rndis_opts,
|
||||
func_inst)->net;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -312,7 +312,7 @@ config USB_NET2272_DMA
|
||||
If unsure, say "N" here. The driver works fine in PIO mode.
|
||||
|
||||
config USB_NET2280
|
||||
tristate "NetChip 228x / PLX USB338x"
|
||||
tristate "NetChip NET228x / PLX USB3x8x"
|
||||
depends on PCI
|
||||
help
|
||||
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
|
||||
@ -322,6 +322,8 @@ config USB_NET2280
|
||||
(for control transfers) and several endpoints with dedicated
|
||||
functions.
|
||||
|
||||
PLX 2380 is a PCIe version of the PLX 2380.
|
||||
|
||||
PLX 3380 / 3382 is a PCIe based USB peripheral controller which
|
||||
supports full, high speed USB 2.0 and super speed USB 3.0
|
||||
data transfers.
|
||||
|
@ -1,3 +1,8 @@
|
||||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
udc-core-y := core.o trace.o
|
||||
|
||||
#
|
||||
# USB peripheral controller drivers
|
||||
#
|
||||
|
@ -2340,7 +2340,6 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
struct udc_ep *ep;
|
||||
struct udc_request *req;
|
||||
struct udc_data_dma *td;
|
||||
unsigned dma_done;
|
||||
unsigned len;
|
||||
|
||||
ep = &dev->ep[ep_ix];
|
||||
@ -2385,13 +2384,8 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
*/
|
||||
if (use_dma_ppb_du) {
|
||||
td = udc_get_last_dma_desc(req);
|
||||
if (td) {
|
||||
dma_done =
|
||||
AMD_GETBITS(td->status,
|
||||
UDC_DMA_IN_STS_BS);
|
||||
/* don't care DMA done */
|
||||
if (td)
|
||||
req->req.actual = req->req.length;
|
||||
}
|
||||
} else {
|
||||
/* assume all bytes transferred */
|
||||
req->req.actual = req->req.length;
|
||||
@ -3417,4 +3411,3 @@ module_pci_driver(udc_pci_driver);
|
||||
MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
|
||||
MODULE_AUTHOR("Thomas Dahlmann");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -1920,6 +1920,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
|
||||
udc->errata = match->data;
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
|
||||
if (IS_ERR(udc->pmc))
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc");
|
||||
if (udc->errata && IS_ERR(udc->pmc))
|
||||
return ERR_CAST(udc->pmc);
|
||||
|
||||
|
@ -57,7 +57,6 @@ static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
|
||||
u32 param0, u32 param1, u32 param2)
|
||||
{
|
||||
u32 temp, cmd_status;
|
||||
int reset_bdc = 0;
|
||||
int ret;
|
||||
|
||||
temp = bdc_readl(bdc->regs, BDC_CMDSC);
|
||||
@ -94,7 +93,6 @@ static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
|
||||
|
||||
case BDC_CMDS_INTL:
|
||||
dev_err(bdc->dev, "BDC Internal error\n");
|
||||
reset_bdc = 1;
|
||||
ret = -ECONNRESET;
|
||||
break;
|
||||
|
||||
@ -102,7 +100,6 @@ static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
|
||||
dev_err(bdc->dev,
|
||||
"command timedout waited for %dusec\n",
|
||||
BDC_CMD_TIMEOUT);
|
||||
reset_bdc = 1;
|
||||
ret = -ECONNRESET;
|
||||
break;
|
||||
default:
|
||||
|
@ -81,7 +81,7 @@ static void ep_bd_list_free(struct bdc_ep *ep, u32 num_tabs)
|
||||
continue;
|
||||
}
|
||||
if (!bd_table->start_bd) {
|
||||
dev_dbg(bdc->dev, "bd dma pool not allocted\n");
|
||||
dev_dbg(bdc->dev, "bd dma pool not allocated\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -702,11 +702,9 @@ static int ep0_queue(struct bdc_ep *ep, struct bdc_req *req)
|
||||
/* Queue data stage */
|
||||
static int ep0_queue_data_stage(struct bdc *bdc)
|
||||
{
|
||||
struct usb_request *ep0_usb_req;
|
||||
struct bdc_ep *ep;
|
||||
|
||||
dev_dbg(bdc->dev, "%s\n", __func__);
|
||||
ep0_usb_req = &bdc->ep0_req.usb_req;
|
||||
ep = bdc->bdc_ep_array[1];
|
||||
bdc->ep0_req.ep = ep;
|
||||
bdc->ep0_req.usb_req.complete = NULL;
|
||||
@ -1393,10 +1391,8 @@ static int ep0_set_sel(struct bdc *bdc,
|
||||
{
|
||||
struct bdc_ep *ep;
|
||||
u16 wLength;
|
||||
u16 wValue;
|
||||
|
||||
dev_dbg(bdc->dev, "%s\n", __func__);
|
||||
wValue = le16_to_cpu(setup_pkt->wValue);
|
||||
wLength = le16_to_cpu(setup_pkt->wLength);
|
||||
if (unlikely(wLength != 6)) {
|
||||
dev_err(bdc->dev, "%s Wrong wLength:%d\n", __func__, wLength);
|
||||
|
1523
drivers/usb/gadget/udc/core.c
Normal file
1523
drivers/usb/gadget/udc/core.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -647,12 +647,10 @@ static int dummy_disable(struct usb_ep *_ep)
|
||||
static struct usb_request *dummy_alloc_request(struct usb_ep *_ep,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct dummy_ep *ep;
|
||||
struct dummy_request *req;
|
||||
|
||||
if (!_ep)
|
||||
return NULL;
|
||||
ep = usb_ep_to_dummy_ep(_ep);
|
||||
|
||||
req = kzalloc(sizeof(*req), mem_flags);
|
||||
if (!req)
|
||||
@ -2444,9 +2442,6 @@ static int dummy_start(struct usb_hcd *hcd)
|
||||
|
||||
static void dummy_stop(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dummy *dum;
|
||||
|
||||
dum = hcd_to_dummy_hcd(hcd)->dum;
|
||||
device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
|
||||
dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
|
||||
}
|
||||
|
@ -1199,8 +1199,6 @@ static irqreturn_t m66592_irq(int irq, void *_m66592)
|
||||
struct m66592 *m66592 = _m66592;
|
||||
u16 intsts0;
|
||||
u16 intenb0;
|
||||
u16 brdysts, nrdysts, bempsts;
|
||||
u16 brdyenb, nrdyenb, bempenb;
|
||||
u16 savepipe;
|
||||
u16 mask0;
|
||||
|
||||
@ -1224,12 +1222,10 @@ static irqreturn_t m66592_irq(int irq, void *_m66592)
|
||||
|
||||
mask0 = intsts0 & intenb0;
|
||||
if (mask0) {
|
||||
brdysts = m66592_read(m66592, M66592_BRDYSTS);
|
||||
nrdysts = m66592_read(m66592, M66592_NRDYSTS);
|
||||
bempsts = m66592_read(m66592, M66592_BEMPSTS);
|
||||
brdyenb = m66592_read(m66592, M66592_BRDYENB);
|
||||
nrdyenb = m66592_read(m66592, M66592_NRDYENB);
|
||||
bempenb = m66592_read(m66592, M66592_BEMPENB);
|
||||
u16 brdysts = m66592_read(m66592, M66592_BRDYSTS);
|
||||
u16 bempsts = m66592_read(m66592, M66592_BEMPSTS);
|
||||
u16 brdyenb = m66592_read(m66592, M66592_BRDYENB);
|
||||
u16 bempenb = m66592_read(m66592, M66592_BEMPENB);
|
||||
|
||||
if (mask0 & M66592_VBINT) {
|
||||
m66592_write(m66592, 0xffff & ~M66592_VBINT,
|
||||
@ -1408,28 +1404,20 @@ static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
|
||||
static int m66592_set_halt(struct usb_ep *_ep, int value)
|
||||
{
|
||||
struct m66592_ep *ep;
|
||||
struct m66592_request *req;
|
||||
struct m66592_ep *ep = container_of(_ep, struct m66592_ep, ep);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
ep = container_of(_ep, struct m66592_ep, ep);
|
||||
req = list_entry(ep->queue.next, struct m66592_request, queue);
|
||||
|
||||
spin_lock_irqsave(&ep->m66592->lock, flags);
|
||||
if (!list_empty(&ep->queue)) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
if (value) {
|
||||
} else if (value) {
|
||||
ep->busy = 1;
|
||||
pipe_stall(ep->m66592, ep->pipenum);
|
||||
} else {
|
||||
ep->busy = 0;
|
||||
pipe_stop(ep->m66592, ep->pipenum);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&ep->m66592->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
@ -119,18 +119,14 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index,
|
||||
struct mv_u3d_req *curr_req)
|
||||
{
|
||||
struct mv_u3d_trb *curr_trb;
|
||||
dma_addr_t cur_deq_lo;
|
||||
struct mv_u3d_ep_context *curr_ep_context;
|
||||
int trb_complete, actual, remaining_length = 0;
|
||||
int actual, remaining_length = 0;
|
||||
int direction, ep_num;
|
||||
int retval = 0;
|
||||
u32 tmp, status, length;
|
||||
|
||||
curr_ep_context = &u3d->ep_context[index];
|
||||
direction = index % 2;
|
||||
ep_num = index / 2;
|
||||
|
||||
trb_complete = 0;
|
||||
actual = curr_req->req.length;
|
||||
|
||||
while (!list_empty(&curr_req->trb_list)) {
|
||||
@ -143,15 +139,10 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index,
|
||||
}
|
||||
|
||||
curr_trb->trb_hw->ctrl.own = 0;
|
||||
if (direction == MV_U3D_EP_DIR_OUT) {
|
||||
if (direction == MV_U3D_EP_DIR_OUT)
|
||||
tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo);
|
||||
cur_deq_lo =
|
||||
ioread32(&u3d->vuc_regs->rxst[ep_num].curdeqlo);
|
||||
} else {
|
||||
else
|
||||
tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo);
|
||||
cur_deq_lo =
|
||||
ioread32(&u3d->vuc_regs->txst[ep_num].curdeqlo);
|
||||
}
|
||||
|
||||
status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT;
|
||||
length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK;
|
||||
@ -527,7 +518,6 @@ static int mv_u3d_ep_enable(struct usb_ep *_ep,
|
||||
{
|
||||
struct mv_u3d *u3d;
|
||||
struct mv_u3d_ep *ep;
|
||||
struct mv_u3d_ep_context *ep_context;
|
||||
u16 max = 0;
|
||||
unsigned maxburst = 0;
|
||||
u32 epxcr, direction;
|
||||
@ -548,9 +538,6 @@ static int mv_u3d_ep_enable(struct usb_ep *_ep,
|
||||
_ep->maxburst = 1;
|
||||
maxburst = _ep->maxburst;
|
||||
|
||||
/* Get the endpoint context address */
|
||||
ep_context = (struct mv_u3d_ep_context *)ep->ep_context;
|
||||
|
||||
/* Set the max burst size */
|
||||
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
@ -633,7 +620,6 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep)
|
||||
{
|
||||
struct mv_u3d *u3d;
|
||||
struct mv_u3d_ep *ep;
|
||||
struct mv_u3d_ep_context *ep_context;
|
||||
u32 epxcr, direction;
|
||||
unsigned long flags;
|
||||
|
||||
@ -646,9 +632,6 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep)
|
||||
|
||||
u3d = ep->u3d;
|
||||
|
||||
/* Get the endpoint context address */
|
||||
ep_context = ep->ep_context;
|
||||
|
||||
direction = mv_u3d_ep_dir(ep);
|
||||
|
||||
/* nuke all pending requests (does flush) */
|
||||
|
@ -129,7 +129,7 @@ static int process_ep_req(struct mv_udc *udc, int index,
|
||||
{
|
||||
struct mv_dtd *curr_dtd;
|
||||
struct mv_dqh *curr_dqh;
|
||||
int td_complete, actual, remaining_length;
|
||||
int actual, remaining_length;
|
||||
int i, direction;
|
||||
int retval = 0;
|
||||
u32 errors;
|
||||
@ -139,7 +139,6 @@ static int process_ep_req(struct mv_udc *udc, int index,
|
||||
direction = index % 2;
|
||||
|
||||
curr_dtd = curr_req->head;
|
||||
td_complete = 0;
|
||||
actual = curr_req->req.length;
|
||||
|
||||
for (i = 0; i < curr_req->dtd_count; i++) {
|
||||
@ -412,11 +411,8 @@ static int req_to_dtd(struct mv_req *req)
|
||||
unsigned count;
|
||||
int is_last, is_first = 1;
|
||||
struct mv_dtd *dtd, *last_dtd = NULL;
|
||||
struct mv_udc *udc;
|
||||
dma_addr_t dma;
|
||||
|
||||
udc = req->ep->udc;
|
||||
|
||||
do {
|
||||
dtd = build_dtd(req, &count, &dma, &is_last);
|
||||
if (dtd == NULL)
|
||||
@ -567,7 +563,7 @@ static int mv_ep_disable(struct usb_ep *_ep)
|
||||
struct mv_udc *udc;
|
||||
struct mv_ep *ep;
|
||||
struct mv_dqh *dqh;
|
||||
u32 bit_pos, epctrlx, direction;
|
||||
u32 epctrlx, direction;
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct mv_ep, ep);
|
||||
@ -582,7 +578,6 @@ static int mv_ep_disable(struct usb_ep *_ep)
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
direction = ep_dir(ep);
|
||||
bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
|
||||
|
||||
/* Reset the max packet length and the interrupt on Setup */
|
||||
dqh->max_packet_length = 0;
|
||||
|
@ -329,12 +329,10 @@ static int net2272_disable(struct usb_ep *_ep)
|
||||
static struct usb_request *
|
||||
net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
|
||||
{
|
||||
struct net2272_ep *ep;
|
||||
struct net2272_request *req;
|
||||
|
||||
if (!_ep)
|
||||
return NULL;
|
||||
ep = container_of(_ep, struct net2272_ep, ep);
|
||||
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
if (!req)
|
||||
@ -348,10 +346,8 @@ net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
|
||||
static void
|
||||
net2272_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
struct net2272_ep *ep;
|
||||
struct net2272_request *req;
|
||||
|
||||
ep = container_of(_ep, struct net2272_ep, ep);
|
||||
if (!_ep || !_req)
|
||||
return;
|
||||
|
||||
|
@ -211,7 +211,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED) {
|
||||
if (dev->quirks & PLX_PCIE) {
|
||||
if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
@ -245,7 +245,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
/* set type, direction, address; reset fifo counters */
|
||||
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
|
||||
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) {
|
||||
if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) {
|
||||
tmp = readl(&ep->cfg->ep_cfg);
|
||||
/* If USB ep number doesn't match hardware ep number */
|
||||
if ((tmp & 0xf) != usb_endpoint_num(desc)) {
|
||||
@ -316,7 +316,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
|
||||
}
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED)
|
||||
if (dev->quirks & PLX_PCIE)
|
||||
ep_clear_seqnum(ep);
|
||||
writel(tmp, &ep->cfg->ep_cfg);
|
||||
|
||||
@ -527,7 +527,7 @@ static int net2280_disable(struct usb_ep *_ep)
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
nuke(ep);
|
||||
|
||||
if (ep->dev->quirks & PLX_SUPERSPEED)
|
||||
if (ep->dev->quirks & PLX_PCIE)
|
||||
ep_reset_338x(ep->dev->regs, ep);
|
||||
else
|
||||
ep_reset_228x(ep->dev->regs, ep);
|
||||
@ -862,7 +862,7 @@ static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma)
|
||||
writel(readl(&dma->dmastat), &dma->dmastat);
|
||||
|
||||
writel(td_dma, &dma->dmadesc);
|
||||
if (ep->dev->quirks & PLX_SUPERSPEED)
|
||||
if (ep->dev->quirks & PLX_PCIE)
|
||||
dmactl |= BIT(DMA_REQUEST_OUTSTANDING);
|
||||
writel(dmactl, &dma->dmactl);
|
||||
|
||||
@ -1046,7 +1046,7 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
|
||||
/* kickstart this i/o queue? */
|
||||
if (list_empty(&ep->queue) && !ep->stopped &&
|
||||
!((dev->quirks & PLX_SUPERSPEED) && ep->dma &&
|
||||
!((dev->quirks & PLX_PCIE) && ep->dma &&
|
||||
(readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) {
|
||||
|
||||
/* use DMA if the endpoint supports it, else pio */
|
||||
@ -1169,7 +1169,7 @@ static void scan_dma_completions(struct net2280_ep *ep)
|
||||
break;
|
||||
} else if (!ep->is_in &&
|
||||
(req->req.length % ep->ep.maxpacket) &&
|
||||
!(ep->dev->quirks & PLX_SUPERSPEED)) {
|
||||
!(ep->dev->quirks & PLX_PCIE)) {
|
||||
|
||||
tmp = readl(&ep->regs->ep_stat);
|
||||
/* AVOID TROUBLE HERE by not issuing short reads from
|
||||
@ -1367,7 +1367,7 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
|
||||
ep->wedged = 1;
|
||||
} else {
|
||||
clear_halt(ep);
|
||||
if (ep->dev->quirks & PLX_SUPERSPEED &&
|
||||
if (ep->dev->quirks & PLX_PCIE &&
|
||||
!list_empty(&ep->queue) && ep->td_dma)
|
||||
restart_dma(ep);
|
||||
ep->wedged = 0;
|
||||
@ -2394,7 +2394,7 @@ static int net2280_start(struct usb_gadget *_gadget,
|
||||
*/
|
||||
net2280_led_active(dev, 1);
|
||||
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
|
||||
if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched)
|
||||
defect7374_enable_data_eps_zero(dev);
|
||||
|
||||
ep0_start(dev);
|
||||
@ -3063,7 +3063,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
|
||||
}
|
||||
ep->stopped = 0;
|
||||
dev->protocol_stall = 0;
|
||||
if (!(dev->quirks & PLX_SUPERSPEED)) {
|
||||
if (!(dev->quirks & PLX_PCIE)) {
|
||||
if (ep->dev->quirks & PLX_2280)
|
||||
tmp = BIT(FIFO_OVERFLOW) |
|
||||
BIT(FIFO_UNDERFLOW);
|
||||
@ -3090,7 +3090,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
|
||||
cpu_to_le32s(&u.raw[0]);
|
||||
cpu_to_le32s(&u.raw[1]);
|
||||
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
|
||||
if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched)
|
||||
defect7374_workaround(dev, u.r);
|
||||
|
||||
tmp = 0;
|
||||
@ -3173,7 +3173,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
|
||||
} else {
|
||||
ep_vdbg(dev, "%s clear halt\n", e->ep.name);
|
||||
clear_halt(e);
|
||||
if ((ep->dev->quirks & PLX_SUPERSPEED) &&
|
||||
if ((ep->dev->quirks & PLX_PCIE) &&
|
||||
!list_empty(&e->queue) && e->td_dma)
|
||||
restart_dma(e);
|
||||
}
|
||||
@ -3195,7 +3195,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
|
||||
if (e->ep.name == ep0name)
|
||||
goto do_stall;
|
||||
set_halt(e);
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && e->dma)
|
||||
if ((dev->quirks & PLX_PCIE) && e->dma)
|
||||
abort_dma(e);
|
||||
allow_status(ep);
|
||||
ep_vdbg(dev, "%s set halt\n", ep->ep.name);
|
||||
@ -3234,7 +3234,7 @@ do_stall:
|
||||
#undef w_length
|
||||
|
||||
next_endpoints:
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) {
|
||||
if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) {
|
||||
u32 mask = (BIT(ENDPOINT_0_INTERRUPT) |
|
||||
USB3380_IRQSTAT0_EP_INTR_MASK_IN |
|
||||
USB3380_IRQSTAT0_EP_INTR_MASK_OUT);
|
||||
@ -3399,7 +3399,7 @@ __acquires(dev->lock)
|
||||
writel(tmp, &dma->dmastat);
|
||||
|
||||
/* dma sync*/
|
||||
if (dev->quirks & PLX_SUPERSPEED) {
|
||||
if (dev->quirks & PLX_PCIE) {
|
||||
u32 r_dmacount = readl(&dma->dmacount);
|
||||
if (!ep->is_in && (r_dmacount & 0x00FFFFFF) &&
|
||||
(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT)))
|
||||
@ -3468,7 +3468,7 @@ static irqreturn_t net2280_irq(int irq, void *_dev)
|
||||
/* control requests and PIO */
|
||||
handle_stat0_irqs(dev, readl(&dev->regs->irqstat0));
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED) {
|
||||
if (dev->quirks & PLX_PCIE) {
|
||||
/* re-enable interrupt to trigger any possible new interrupt */
|
||||
u32 pciirqenb1 = readl(&dev->regs->pciirqenb1);
|
||||
writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1);
|
||||
@ -3513,7 +3513,7 @@ static void net2280_remove(struct pci_dev *pdev)
|
||||
}
|
||||
if (dev->got_irq)
|
||||
free_irq(pdev->irq, dev);
|
||||
if (dev->quirks & PLX_SUPERSPEED)
|
||||
if (dev->quirks & PLX_PCIE)
|
||||
pci_disable_msi(pdev);
|
||||
if (dev->regs)
|
||||
iounmap(dev->regs);
|
||||
@ -3593,7 +3593,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
|
||||
dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED) {
|
||||
if (dev->quirks & PLX_PCIE) {
|
||||
u32 fsmvalue;
|
||||
u32 usbstat;
|
||||
dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
|
||||
@ -3637,7 +3637,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED)
|
||||
if (dev->quirks & PLX_PCIE)
|
||||
if (pci_enable_msi(pdev))
|
||||
ep_err(dev, "Failed to enable MSI mode\n");
|
||||
|
||||
@ -3755,10 +3755,19 @@ static const struct pci_device_id pci_ids[] = { {
|
||||
.class = PCI_CLASS_SERIAL_USB_DEVICE,
|
||||
.class_mask = ~0,
|
||||
.vendor = PCI_VENDOR_ID_PLX,
|
||||
.device = 0x2380,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = PLX_PCIE,
|
||||
},
|
||||
{
|
||||
.class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
|
||||
.class_mask = ~0,
|
||||
.vendor = PCI_VENDOR_ID_PLX,
|
||||
.device = 0x3380,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = PLX_SUPERSPEED,
|
||||
.driver_data = PLX_PCIE | PLX_SUPERSPEED,
|
||||
},
|
||||
{
|
||||
.class = PCI_CLASS_SERIAL_USB_DEVICE,
|
||||
@ -3767,7 +3776,7 @@ static const struct pci_device_id pci_ids[] = { {
|
||||
.device = 0x3382,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = PLX_SUPERSPEED,
|
||||
.driver_data = PLX_PCIE | PLX_SUPERSPEED,
|
||||
},
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
|
@ -47,6 +47,7 @@ set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value)
|
||||
#define PLX_LEGACY BIT(0)
|
||||
#define PLX_2280 BIT(1)
|
||||
#define PLX_SUPERSPEED BIT(2)
|
||||
#define PLX_PCIE BIT(3)
|
||||
|
||||
#define REG_DIAG 0x0
|
||||
#define RETRY_COUNTER 16
|
||||
|
@ -1477,11 +1477,11 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
|
||||
req->dma_mapped = 0;
|
||||
}
|
||||
ep->halted = 1;
|
||||
spin_lock(&dev->lock);
|
||||
spin_unlock(&dev->lock);
|
||||
if (!ep->in)
|
||||
pch_udc_ep_clear_rrdy(ep);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_unlock(&dev->lock);
|
||||
spin_lock(&dev->lock);
|
||||
ep->halted = halted;
|
||||
}
|
||||
|
||||
@ -1984,9 +1984,8 @@ static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
|
||||
if (ep->num == PCH_UDC_EP0)
|
||||
ep->dev->stall = 1;
|
||||
pch_udc_ep_set_stall(ep);
|
||||
pch_udc_enable_ep_interrupts(ep->dev,
|
||||
PCH_UDC_EPINT(ep->in,
|
||||
ep->num));
|
||||
pch_udc_enable_ep_interrupts(
|
||||
ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
|
||||
} else {
|
||||
pch_udc_ep_clear_stall(ep);
|
||||
}
|
||||
@ -2451,16 +2450,11 @@ static void pch_udc_svc_control_out(struct pch_udc_dev *dev)
|
||||
*/
|
||||
static void pch_udc_postsvc_epinters(struct pch_udc_dev *dev, int ep_num)
|
||||
{
|
||||
struct pch_udc_ep *ep;
|
||||
struct pch_udc_request *req;
|
||||
|
||||
ep = &dev->ep[UDC_EPIN_IDX(ep_num)];
|
||||
if (!list_empty(&ep->queue)) {
|
||||
req = list_entry(ep->queue.next, struct pch_udc_request, queue);
|
||||
pch_udc_enable_ep_interrupts(ep->dev,
|
||||
PCH_UDC_EPINT(ep->in, ep->num));
|
||||
pch_udc_ep_clear_nak(ep);
|
||||
}
|
||||
struct pch_udc_ep *ep = &dev->ep[UDC_EPIN_IDX(ep_num)];
|
||||
if (list_empty(&ep->queue))
|
||||
return;
|
||||
pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
|
||||
pch_udc_ep_clear_nak(ep);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2573,9 +2567,9 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
|
||||
empty_req_queue(ep);
|
||||
}
|
||||
if (dev->driver) {
|
||||
spin_lock(&dev->lock);
|
||||
usb_gadget_udc_reset(&dev->gadget, dev->driver);
|
||||
spin_unlock(&dev->lock);
|
||||
usb_gadget_udc_reset(&dev->gadget, dev->driver);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2654,9 +2648,9 @@ static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
|
||||
dev->ep[i].halted = 0;
|
||||
}
|
||||
dev->stall = 0;
|
||||
spin_lock(&dev->lock);
|
||||
dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2691,9 +2685,9 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
|
||||
dev->stall = 0;
|
||||
|
||||
/* call gadget zero with setup data received */
|
||||
spin_lock(&dev->lock);
|
||||
dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->setup(&dev->gadget, &dev->setup_data);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1825,13 +1825,10 @@ fail:
|
||||
* Disables all udc endpoints (even control endpoint), report disconnect to
|
||||
* the gadget user.
|
||||
*/
|
||||
static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
|
||||
static void stop_activity(struct pxa_udc *udc)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* don't disconnect drivers more than once */
|
||||
if (udc->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
driver = NULL;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
for (i = 0; i < NR_USB_ENDPOINTS; i++)
|
||||
@ -1848,7 +1845,7 @@ static int pxa27x_udc_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct pxa_udc *udc = to_pxa(g);
|
||||
|
||||
stop_activity(udc, NULL);
|
||||
stop_activity(udc);
|
||||
udc_disable(udc);
|
||||
|
||||
udc->driver = NULL;
|
||||
@ -2296,7 +2293,7 @@ static void irq_udc_reset(struct pxa_udc *udc)
|
||||
|
||||
if ((udccr & UDCCR_UDA) == 0) {
|
||||
dev_dbg(udc->dev, "USB reset start\n");
|
||||
stop_activity(udc, udc->driver);
|
||||
stop_activity(udc);
|
||||
}
|
||||
udc->gadget.speed = USB_SPEED_FULL;
|
||||
memset(&udc->stats, 0, sizeof udc->stats);
|
||||
|
@ -1464,8 +1464,6 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
|
||||
struct r8a66597 *r8a66597 = _r8a66597;
|
||||
u16 intsts0;
|
||||
u16 intenb0;
|
||||
u16 brdysts, nrdysts, bempsts;
|
||||
u16 brdyenb, nrdyenb, bempenb;
|
||||
u16 savepipe;
|
||||
u16 mask0;
|
||||
|
||||
@ -1481,12 +1479,10 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
|
||||
|
||||
mask0 = intsts0 & intenb0;
|
||||
if (mask0) {
|
||||
brdysts = r8a66597_read(r8a66597, BRDYSTS);
|
||||
nrdysts = r8a66597_read(r8a66597, NRDYSTS);
|
||||
bempsts = r8a66597_read(r8a66597, BEMPSTS);
|
||||
brdyenb = r8a66597_read(r8a66597, BRDYENB);
|
||||
nrdyenb = r8a66597_read(r8a66597, NRDYENB);
|
||||
bempenb = r8a66597_read(r8a66597, BEMPENB);
|
||||
u16 brdysts = r8a66597_read(r8a66597, BRDYSTS);
|
||||
u16 bempsts = r8a66597_read(r8a66597, BEMPSTS);
|
||||
u16 brdyenb = r8a66597_read(r8a66597, BRDYENB);
|
||||
u16 bempenb = r8a66597_read(r8a66597, BEMPENB);
|
||||
|
||||
if (mask0 & VBINT) {
|
||||
r8a66597_write(r8a66597, 0xffff & ~VBINT,
|
||||
@ -1658,20 +1654,14 @@ static int r8a66597_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
|
||||
static int r8a66597_set_halt(struct usb_ep *_ep, int value)
|
||||
{
|
||||
struct r8a66597_ep *ep;
|
||||
struct r8a66597_request *req;
|
||||
struct r8a66597_ep *ep = container_of(_ep, struct r8a66597_ep, ep);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
ep = container_of(_ep, struct r8a66597_ep, ep);
|
||||
req = get_request_from_ep(ep);
|
||||
|
||||
spin_lock_irqsave(&ep->r8a66597->lock, flags);
|
||||
if (!list_empty(&ep->queue)) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
if (value) {
|
||||
} else if (value) {
|
||||
ep->busy = 1;
|
||||
pipe_stall(ep->r8a66597, ep->pipenum);
|
||||
} else {
|
||||
@ -1679,8 +1669,6 @@ static int r8a66597_set_halt(struct usb_ep *_ep, int value)
|
||||
ep->wedge = 0;
|
||||
pipe_stop(ep->r8a66597, ep->pipenum);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
18
drivers/usb/gadget/udc/trace.c
Normal file
18
drivers/usb/gadget/udc/trace.c
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* trace.c - USB Gadget Framework Trace Support
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
* Author: Felipe Balbi <felipe.balbi@linux.intel.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
298
drivers/usb/gadget/udc/trace.h
Normal file
298
drivers/usb/gadget/udc/trace.h
Normal file
@ -0,0 +1,298 @@
|
||||
/**
|
||||
* udc.c - Core UDC Framework
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
* Author: Felipe Balbi <felipe.balbi@linux.intel.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM gadget
|
||||
|
||||
#if !defined(__UDC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __UDC_TRACE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
DECLARE_EVENT_CLASS(udc_log_gadget,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret),
|
||||
TP_STRUCT__entry(
|
||||
__field(enum usb_device_speed, speed)
|
||||
__field(enum usb_device_speed, max_speed)
|
||||
__field(enum usb_device_state, state)
|
||||
__field(unsigned, mA)
|
||||
__field(unsigned, sg_supported)
|
||||
__field(unsigned, is_otg)
|
||||
__field(unsigned, is_a_peripheral)
|
||||
__field(unsigned, b_hnp_enable)
|
||||
__field(unsigned, a_hnp_support)
|
||||
__field(unsigned, hnp_polling_support)
|
||||
__field(unsigned, host_request_flag)
|
||||
__field(unsigned, quirk_ep_out_aligned_size)
|
||||
__field(unsigned, quirk_altset_not_supp)
|
||||
__field(unsigned, quirk_stall_not_supp)
|
||||
__field(unsigned, quirk_zlp_not_supp)
|
||||
__field(unsigned, is_selfpowered)
|
||||
__field(unsigned, deactivated)
|
||||
__field(unsigned, connected)
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->speed = g->speed;
|
||||
__entry->max_speed = g->max_speed;
|
||||
__entry->state = g->state;
|
||||
__entry->mA = g->mA;
|
||||
__entry->sg_supported = g->sg_supported;
|
||||
__entry->is_otg = g->is_otg;
|
||||
__entry->is_a_peripheral = g->is_a_peripheral;
|
||||
__entry->b_hnp_enable = g->b_hnp_enable;
|
||||
__entry->a_hnp_support = g->a_hnp_support;
|
||||
__entry->hnp_polling_support = g->hnp_polling_support;
|
||||
__entry->host_request_flag = g->host_request_flag;
|
||||
__entry->quirk_ep_out_aligned_size = g->quirk_ep_out_aligned_size;
|
||||
__entry->quirk_altset_not_supp = g->quirk_altset_not_supp;
|
||||
__entry->quirk_stall_not_supp = g->quirk_stall_not_supp;
|
||||
__entry->quirk_zlp_not_supp = g->quirk_zlp_not_supp;
|
||||
__entry->is_selfpowered = g->is_selfpowered;
|
||||
__entry->deactivated = g->deactivated;
|
||||
__entry->connected = g->connected;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("speed %d/%d state %d %dmA [%s%s%s%s%s%s%s%s%s%s%s%s%s%s] --> %d",
|
||||
__entry->speed, __entry->max_speed, __entry->state, __entry->mA,
|
||||
__entry->sg_supported ? "sg:" : "",
|
||||
__entry->is_otg ? "OTG:" : "",
|
||||
__entry->is_a_peripheral ? "a_peripheral:" : "",
|
||||
__entry->b_hnp_enable ? "b_hnp:" : "",
|
||||
__entry->a_hnp_support ? "a_hnp:" : "",
|
||||
__entry->hnp_polling_support ? "hnp_poll:" : "",
|
||||
__entry->host_request_flag ? "hostreq:" : "",
|
||||
__entry->quirk_ep_out_aligned_size ? "out_aligned:" : "",
|
||||
__entry->quirk_altset_not_supp ? "no_altset:" : "",
|
||||
__entry->quirk_stall_not_supp ? "no_stall:" : "",
|
||||
__entry->quirk_zlp_not_supp ? "no_zlp" : "",
|
||||
__entry->is_selfpowered ? "self-powered:" : "bus-powered:",
|
||||
__entry->deactivated ? "deactivated:" : "activated:",
|
||||
__entry->connected ? "connected" : "disconnected",
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_frame_number,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_wakeup,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_set_selfpowered,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_clear_selfpowered,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_connect,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_draw,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_disconnect,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_connect,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_disconnect,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_deactivate,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_gadget, usb_gadget_activate,
|
||||
TP_PROTO(struct usb_gadget *g, int ret),
|
||||
TP_ARGS(g, ret)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(udc_log_ep,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret),
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, UDC_TRACE_STR_MAX)
|
||||
__field(unsigned, maxpacket)
|
||||
__field(unsigned, maxpacket_limit)
|
||||
__field(unsigned, max_streams)
|
||||
__field(unsigned, mult)
|
||||
__field(unsigned, maxburst)
|
||||
__field(u8, address)
|
||||
__field(bool, claimed)
|
||||
__field(bool, enabled)
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name);
|
||||
__entry->maxpacket = ep->maxpacket;
|
||||
__entry->maxpacket_limit = ep->maxpacket_limit;
|
||||
__entry->max_streams = ep->max_streams;
|
||||
__entry->mult = ep->mult;
|
||||
__entry->maxburst = ep->maxburst;
|
||||
__entry->address = ep->address,
|
||||
__entry->claimed = ep->claimed;
|
||||
__entry->enabled = ep->enabled;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("%s: mps %d/%d streams %d mult %d burst %d addr %02x %s%s --> %d",
|
||||
__get_str(name), __entry->maxpacket, __entry->maxpacket_limit,
|
||||
__entry->max_streams, __entry->mult, __entry->maxburst,
|
||||
__entry->address, __entry->claimed ? "claimed:" : "released:",
|
||||
__entry->enabled ? "enabled" : "disabled", ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_set_maxpacket_limit,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_enable,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_disable,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_set_halt,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_clear_halt,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_set_wedge,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_fifo_status,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_ep, usb_ep_fifo_flush,
|
||||
TP_PROTO(struct usb_ep *ep, int ret),
|
||||
TP_ARGS(ep, ret)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(udc_log_req,
|
||||
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
|
||||
TP_ARGS(ep, req, ret),
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, UDC_TRACE_STR_MAX)
|
||||
__field(unsigned, length)
|
||||
__field(unsigned, actual)
|
||||
__field(unsigned, num_sgs)
|
||||
__field(unsigned, num_mapped_sgs)
|
||||
__field(unsigned, stream_id)
|
||||
__field(unsigned, no_interrupt)
|
||||
__field(unsigned, zero)
|
||||
__field(unsigned, short_not_ok)
|
||||
__field(int, status)
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name);
|
||||
__entry->length = req->length;
|
||||
__entry->actual = req->actual;
|
||||
__entry->num_sgs = req->num_sgs;
|
||||
__entry->num_mapped_sgs = req->num_mapped_sgs;
|
||||
__entry->stream_id = req->stream_id;
|
||||
__entry->no_interrupt = req->no_interrupt;
|
||||
__entry->zero = req->zero;
|
||||
__entry->short_not_ok = req->short_not_ok;
|
||||
__entry->status = req->status;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("%s: length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d",
|
||||
__get_str(name), __entry->actual, __entry->length,
|
||||
__entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "i" : "I",
|
||||
__entry->status, __entry->ret
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_req, usb_ep_alloc_request,
|
||||
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
|
||||
TP_ARGS(ep, req, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_req, usb_ep_free_request,
|
||||
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
|
||||
TP_ARGS(ep, req, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_req, usb_ep_queue,
|
||||
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
|
||||
TP_ARGS(ep, req, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_req, usb_ep_dequeue,
|
||||
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
|
||||
TP_ARGS(ep, req, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(udc_log_req, usb_gadget_giveback_request,
|
||||
TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret),
|
||||
TP_ARGS(ep, req, ret)
|
||||
);
|
||||
|
||||
#endif /* __UDC_TRACE_H */
|
||||
|
||||
/* this part has to be here */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
#include <trace/define_trace.h>
|
@ -1,800 +0,0 @@
|
||||
/**
|
||||
* udc.c - Core UDC Framework
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
/**
|
||||
* struct usb_udc - describes one usb device controller
|
||||
* @driver - the gadget driver pointer. For use by the class code
|
||||
* @dev - the child device to the actual controller
|
||||
* @gadget - the gadget. For use by the class code
|
||||
* @list - for use by the udc class driver
|
||||
* @vbus - for udcs who care about vbus status, this value is real vbus status;
|
||||
* for udcs who do not care about vbus status, this value is always true
|
||||
*
|
||||
* This represents the internal data structure which is used by the UDC-class
|
||||
* to hold information about udc driver and gadget together.
|
||||
*/
|
||||
struct usb_udc {
|
||||
struct usb_gadget_driver *driver;
|
||||
struct usb_gadget *gadget;
|
||||
struct device dev;
|
||||
struct list_head list;
|
||||
bool vbus;
|
||||
};
|
||||
|
||||
static struct class *udc_class;
|
||||
static LIST_HEAD(udc_list);
|
||||
static LIST_HEAD(gadget_driver_pending_list);
|
||||
static DEFINE_MUTEX(udc_lock);
|
||||
|
||||
static int udc_bind_to_driver(struct usb_udc *udc,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
|
||||
int usb_gadget_map_request_by_dev(struct device *dev,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
if (req->length == 0)
|
||||
return 0;
|
||||
|
||||
if (req->num_sgs) {
|
||||
int mapped;
|
||||
|
||||
mapped = dma_map_sg(dev, req->sg, req->num_sgs,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
if (mapped == 0) {
|
||||
dev_err(dev, "failed to map SGs\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
req->num_mapped_sgs = mapped;
|
||||
} else {
|
||||
req->dma = dma_map_single(dev, req->buf, req->length,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (dma_mapping_error(dev, req->dma)) {
|
||||
dev_err(dev, "failed to map buffer\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev);
|
||||
|
||||
int usb_gadget_map_request(struct usb_gadget *gadget,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
|
||||
|
||||
void usb_gadget_unmap_request_by_dev(struct device *dev,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
if (req->length == 0)
|
||||
return;
|
||||
|
||||
if (req->num_mapped_sgs) {
|
||||
dma_unmap_sg(dev, req->sg, req->num_mapped_sgs,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
req->num_mapped_sgs = 0;
|
||||
} else {
|
||||
dma_unmap_single(dev, req->dma, req->length,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
|
||||
|
||||
void usb_gadget_unmap_request(struct usb_gadget *gadget,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
|
||||
|
||||
#endif /* CONFIG_HAS_DMA */
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* usb_gadget_giveback_request - give the request back to the gadget layer
|
||||
* Context: in_interrupt()
|
||||
*
|
||||
* This is called by device controller drivers in order to return the
|
||||
* completed request back to the gadget layer.
|
||||
*/
|
||||
void usb_gadget_giveback_request(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
if (likely(req->status == 0))
|
||||
usb_led_activity(USB_LED_EVENT_GADGET);
|
||||
|
||||
req->complete(ep, req);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* gadget_find_ep_by_name - returns ep whose name is the same as sting passed
|
||||
* in second parameter or NULL if searched endpoint not found
|
||||
* @g: controller to check for quirk
|
||||
* @name: name of searched endpoint
|
||||
*/
|
||||
struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
|
||||
gadget_for_each_ep(ep, g) {
|
||||
if (!strcmp(ep->name, name))
|
||||
return ep;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gadget_find_ep_by_name);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
|
||||
struct usb_ep *ep, struct usb_endpoint_descriptor *desc,
|
||||
struct usb_ss_ep_comp_descriptor *ep_comp)
|
||||
{
|
||||
u8 type;
|
||||
u16 max;
|
||||
int num_req_streams = 0;
|
||||
|
||||
/* endpoint already claimed? */
|
||||
if (ep->claimed)
|
||||
return 0;
|
||||
|
||||
type = usb_endpoint_type(desc);
|
||||
max = 0x7ff & usb_endpoint_maxp(desc);
|
||||
|
||||
if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
|
||||
return 0;
|
||||
if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
|
||||
return 0;
|
||||
|
||||
if (max > ep->maxpacket_limit)
|
||||
return 0;
|
||||
|
||||
/* "high bandwidth" works only at high speed */
|
||||
if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp(desc) & (3<<11))
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
/* only support ep0 for portable CONTROL traffic */
|
||||
return 0;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (!ep->caps.type_iso)
|
||||
return 0;
|
||||
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
|
||||
if (!gadget_is_dualspeed(gadget) && max > 1023)
|
||||
return 0;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (!ep->caps.type_bulk)
|
||||
return 0;
|
||||
if (ep_comp && gadget_is_superspeed(gadget)) {
|
||||
/* Get the number of required streams from the
|
||||
* EP companion descriptor and see if the EP
|
||||
* matches it
|
||||
*/
|
||||
num_req_streams = ep_comp->bmAttributes & 0x1f;
|
||||
if (num_req_streams > ep->max_streams)
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* Bulk endpoints handle interrupt transfers,
|
||||
* except the toggle-quirky iso-synch kind
|
||||
*/
|
||||
if (!ep->caps.type_int && !ep->caps.type_bulk)
|
||||
return 0;
|
||||
/* INT: limit 64 bytes full speed, 1024 high/super speed */
|
||||
if (!gadget_is_dualspeed(gadget) && max > 64)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void usb_gadget_state_work(struct work_struct *work)
|
||||
{
|
||||
struct usb_gadget *gadget = work_to_gadget(work);
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
if (udc)
|
||||
sysfs_notify(&udc->dev.kobj, NULL, "state");
|
||||
}
|
||||
|
||||
void usb_gadget_set_state(struct usb_gadget *gadget,
|
||||
enum usb_device_state state)
|
||||
{
|
||||
gadget->state = state;
|
||||
schedule_work(&gadget->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void usb_udc_connect_control(struct usb_udc *udc)
|
||||
{
|
||||
if (udc->vbus)
|
||||
usb_gadget_connect(udc->gadget);
|
||||
else
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_udc_vbus_handler - updates the udc core vbus status, and try to
|
||||
* connect or disconnect gadget
|
||||
* @gadget: The gadget which vbus change occurs
|
||||
* @status: The vbus status
|
||||
*
|
||||
* The udc driver calls it when it wants to connect or disconnect gadget
|
||||
* according to vbus status.
|
||||
*/
|
||||
void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
|
||||
{
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
if (udc) {
|
||||
udc->vbus = status;
|
||||
usb_udc_connect_control(udc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
|
||||
* @gadget: The gadget which bus reset occurs
|
||||
* @driver: The gadget driver we want to notify
|
||||
*
|
||||
* If the udc driver has bus reset handler, it needs to call this when the bus
|
||||
* reset occurs, it notifies the gadget driver that the bus reset occurs as
|
||||
* well as updates gadget state.
|
||||
*/
|
||||
void usb_gadget_udc_reset(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
driver->reset(gadget);
|
||||
usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_start - tells usb device controller to start up
|
||||
* @udc: The UDC to be started
|
||||
*
|
||||
* This call is issued by the UDC Class driver when it's about
|
||||
* to register a gadget driver to the device controller, before
|
||||
* calling gadget driver's bind() method.
|
||||
*
|
||||
* It allows the controller to be powered off until strictly
|
||||
* necessary to have it powered on.
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
static inline int usb_gadget_udc_start(struct usb_udc *udc)
|
||||
{
|
||||
return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
|
||||
* @gadget: The device we want to stop activity
|
||||
* @driver: The driver to unbind from @gadget
|
||||
*
|
||||
* This call is issued by the UDC Class driver after calling
|
||||
* gadget driver's unbind() method.
|
||||
*
|
||||
* The details are implementation specific, but it can go as
|
||||
* far as powering off UDC completely and disable its data
|
||||
* line pullups.
|
||||
*/
|
||||
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
|
||||
{
|
||||
udc->gadget->ops->udc_stop(udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_udc_release - release the usb_udc struct
|
||||
* @dev: the dev member within usb_udc
|
||||
*
|
||||
* This is called by driver's core in order to free memory once the last
|
||||
* reference is released.
|
||||
*/
|
||||
static void usb_udc_release(struct device *dev)
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
|
||||
udc = container_of(dev, struct usb_udc, dev);
|
||||
dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
|
||||
kfree(udc);
|
||||
}
|
||||
|
||||
static const struct attribute_group *usb_udc_attr_groups[];
|
||||
|
||||
static void usb_udc_nop_release(struct device *dev)
|
||||
{
|
||||
dev_vdbg(dev, "%s\n", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
|
||||
* @parent: the parent device to this udc. Usually the controller driver's
|
||||
* device.
|
||||
* @gadget: the gadget to be added to the list.
|
||||
* @release: a gadget release function.
|
||||
*
|
||||
* Returns zero on success, negative errno otherwise.
|
||||
*/
|
||||
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
void (*release)(struct device *dev))
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
struct usb_gadget_driver *driver;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
|
||||
if (!udc)
|
||||
goto err1;
|
||||
|
||||
dev_set_name(&gadget->dev, "gadget");
|
||||
INIT_WORK(&gadget->work, usb_gadget_state_work);
|
||||
gadget->dev.parent = parent;
|
||||
|
||||
if (release)
|
||||
gadget->dev.release = release;
|
||||
else
|
||||
gadget->dev.release = usb_udc_nop_release;
|
||||
|
||||
ret = device_register(&gadget->dev);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
device_initialize(&udc->dev);
|
||||
udc->dev.release = usb_udc_release;
|
||||
udc->dev.class = udc_class;
|
||||
udc->dev.groups = usb_udc_attr_groups;
|
||||
udc->dev.parent = parent;
|
||||
ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
udc->gadget = gadget;
|
||||
gadget->udc = udc;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_add_tail(&udc->list, &udc_list);
|
||||
|
||||
ret = device_add(&udc->dev);
|
||||
if (ret)
|
||||
goto err4;
|
||||
|
||||
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
|
||||
udc->vbus = true;
|
||||
|
||||
/* pick up one of pending gadget drivers */
|
||||
list_for_each_entry(driver, &gadget_driver_pending_list, pending) {
|
||||
if (!driver->udc_name || strcmp(driver->udc_name,
|
||||
dev_name(&udc->dev)) == 0) {
|
||||
ret = udc_bind_to_driver(udc, driver);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
list_del(&driver->pending);
|
||||
if (ret)
|
||||
goto err4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
list_del(&udc->list);
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
err3:
|
||||
put_device(&udc->dev);
|
||||
device_del(&gadget->dev);
|
||||
|
||||
err2:
|
||||
put_device(&gadget->dev);
|
||||
kfree(udc);
|
||||
|
||||
err1:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
|
||||
|
||||
/**
|
||||
* usb_get_gadget_udc_name - get the name of the first UDC controller
|
||||
* This functions returns the name of the first UDC controller in the system.
|
||||
* Please note that this interface is usefull only for legacy drivers which
|
||||
* assume that there is only one UDC controller in the system and they need to
|
||||
* get its name before initialization. There is no guarantee that the UDC
|
||||
* of the returned name will be still available, when gadget driver registers
|
||||
* itself.
|
||||
*
|
||||
* Returns pointer to string with UDC controller name on success, NULL
|
||||
* otherwise. Caller should kfree() returned string.
|
||||
*/
|
||||
char *usb_get_gadget_udc_name(void)
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
char *name = NULL;
|
||||
|
||||
/* For now we take the first available UDC */
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
if (!udc->driver) {
|
||||
name = kstrdup(udc->gadget->name, GFP_KERNEL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
return name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name);
|
||||
|
||||
/**
|
||||
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
|
||||
* @parent: the parent device to this udc. Usually the controller
|
||||
* driver's device.
|
||||
* @gadget: the gadget to be added to the list
|
||||
*
|
||||
* Returns zero on success, negative errno otherwise.
|
||||
*/
|
||||
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
|
||||
{
|
||||
return usb_add_gadget_udc_release(parent, gadget, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
|
||||
|
||||
static void usb_gadget_remove_driver(struct usb_udc *udc)
|
||||
{
|
||||
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
|
||||
udc->driver->function);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
udc->driver->unbind(udc->gadget);
|
||||
usb_gadget_udc_stop(udc);
|
||||
|
||||
udc->driver = NULL;
|
||||
udc->dev.driver = NULL;
|
||||
udc->gadget->dev.driver = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_del_gadget_udc - deletes @udc from udc_list
|
||||
* @gadget: the gadget to be removed.
|
||||
*
|
||||
* This, will call usb_gadget_unregister_driver() if
|
||||
* the @udc is still busy.
|
||||
*/
|
||||
void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
if (!udc)
|
||||
return;
|
||||
|
||||
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_del(&udc->list);
|
||||
|
||||
if (udc->driver) {
|
||||
struct usb_gadget_driver *driver = udc->driver;
|
||||
|
||||
usb_gadget_remove_driver(udc);
|
||||
list_add(&driver->pending, &gadget_driver_pending_list);
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
|
||||
flush_work(&gadget->work);
|
||||
device_unregister(&udc->dev);
|
||||
device_unregister(&gadget->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
|
||||
driver->function);
|
||||
|
||||
udc->driver = driver;
|
||||
udc->dev.driver = &driver->driver;
|
||||
udc->gadget->dev.driver = &driver->driver;
|
||||
|
||||
ret = driver->bind(udc->gadget, driver);
|
||||
if (ret)
|
||||
goto err1;
|
||||
ret = usb_gadget_udc_start(udc);
|
||||
if (ret) {
|
||||
driver->unbind(udc->gadget);
|
||||
goto err1;
|
||||
}
|
||||
usb_udc_connect_control(udc);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
return 0;
|
||||
err1:
|
||||
if (ret != -EISNAM)
|
||||
dev_err(&udc->dev, "failed to start %s: %d\n",
|
||||
udc->driver->function, ret);
|
||||
udc->driver = NULL;
|
||||
udc->dev.driver = NULL;
|
||||
udc->gadget->dev.driver = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!driver || !driver->bind || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
if (driver->udc_name) {
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
ret = strcmp(driver->udc_name, dev_name(&udc->dev));
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
if (!ret && !udc->driver)
|
||||
goto found;
|
||||
} else {
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
/* For now we take the first one */
|
||||
if (!udc->driver)
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
if (!driver->match_existing_only) {
|
||||
list_add_tail(&driver->pending, &gadget_driver_pending_list);
|
||||
pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",
|
||||
driver->function);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&udc_lock);
|
||||
return ret;
|
||||
found:
|
||||
ret = udc_bind_to_driver(udc, driver);
|
||||
mutex_unlock(&udc_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
|
||||
|
||||
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!driver || !driver->unbind)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list)
|
||||
if (udc->driver == driver) {
|
||||
usb_gadget_remove_driver(udc);
|
||||
usb_gadget_set_state(udc->gadget,
|
||||
USB_STATE_NOTATTACHED);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
list_del(&driver->pending);
|
||||
ret = 0;
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static ssize_t usb_udc_srp_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
|
||||
if (sysfs_streq(buf, "1"))
|
||||
usb_gadget_wakeup(udc->gadget);
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store);
|
||||
|
||||
static ssize_t usb_udc_softconn_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
|
||||
if (!udc->driver) {
|
||||
dev_err(dev, "soft-connect without a gadget driver\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (sysfs_streq(buf, "connect")) {
|
||||
usb_gadget_udc_start(udc);
|
||||
usb_gadget_connect(udc->gadget);
|
||||
} else if (sysfs_streq(buf, "disconnect")) {
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
usb_gadget_udc_stop(udc);
|
||||
} else {
|
||||
dev_err(dev, "unsupported command '%s'\n", buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store);
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
struct usb_gadget *gadget = udc->gadget;
|
||||
|
||||
return sprintf(buf, "%s\n", usb_state_string(gadget->state));
|
||||
}
|
||||
static DEVICE_ATTR_RO(state);
|
||||
|
||||
#define USB_UDC_SPEED_ATTR(name, param) \
|
||||
ssize_t name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", \
|
||||
usb_speed_string(udc->gadget->param)); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
static USB_UDC_SPEED_ATTR(current_speed, speed);
|
||||
static USB_UDC_SPEED_ATTR(maximum_speed, max_speed);
|
||||
|
||||
#define USB_UDC_ATTR(name) \
|
||||
ssize_t name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
|
||||
struct usb_gadget *gadget = udc->gadget; \
|
||||
\
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
static USB_UDC_ATTR(is_otg);
|
||||
static USB_UDC_ATTR(is_a_peripheral);
|
||||
static USB_UDC_ATTR(b_hnp_enable);
|
||||
static USB_UDC_ATTR(a_hnp_support);
|
||||
static USB_UDC_ATTR(a_alt_hnp_support);
|
||||
static USB_UDC_ATTR(is_selfpowered);
|
||||
|
||||
static struct attribute *usb_udc_attrs[] = {
|
||||
&dev_attr_srp.attr,
|
||||
&dev_attr_soft_connect.attr,
|
||||
&dev_attr_state.attr,
|
||||
&dev_attr_current_speed.attr,
|
||||
&dev_attr_maximum_speed.attr,
|
||||
|
||||
&dev_attr_is_otg.attr,
|
||||
&dev_attr_is_a_peripheral.attr,
|
||||
&dev_attr_b_hnp_enable.attr,
|
||||
&dev_attr_a_hnp_support.attr,
|
||||
&dev_attr_a_alt_hnp_support.attr,
|
||||
&dev_attr_is_selfpowered.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group usb_udc_attr_group = {
|
||||
.attrs = usb_udc_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *usb_udc_attr_groups[] = {
|
||||
&usb_udc_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
int ret;
|
||||
|
||||
ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add uevent USB_UDC_NAME\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (udc->driver) {
|
||||
ret = add_uevent_var(env, "USB_UDC_DRIVER=%s",
|
||||
udc->driver->function);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init usb_udc_init(void)
|
||||
{
|
||||
udc_class = class_create(THIS_MODULE, "udc");
|
||||
if (IS_ERR(udc_class)) {
|
||||
pr_err("failed to create udc class --> %ld\n",
|
||||
PTR_ERR(udc_class));
|
||||
return PTR_ERR(udc_class);
|
||||
}
|
||||
|
||||
udc_class->dev_uevent = usb_udc_uevent;
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(usb_udc_init);
|
||||
|
||||
static void __exit usb_udc_exit(void)
|
||||
{
|
||||
class_destroy(udc_class);
|
||||
}
|
||||
module_exit(usb_udc_exit);
|
||||
|
||||
MODULE_DESCRIPTION("UDC Framework");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -2055,7 +2055,6 @@ static int xudc_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
struct xusb_udc *udc;
|
||||
struct xusb_ep *ep0;
|
||||
int irq;
|
||||
int ret;
|
||||
u32 ier;
|
||||
@ -2119,8 +2118,6 @@ static int xudc_probe(struct platform_device *pdev)
|
||||
|
||||
xudc_eps_init(udc);
|
||||
|
||||
ep0 = &udc->ep[0];
|
||||
|
||||
/* Set device address to 0.*/
|
||||
udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0);
|
||||
|
||||
|
@ -180,7 +180,7 @@ config USB_EHCI_MXC
|
||||
config USB_EHCI_HCD_OMAP
|
||||
tristate "EHCI support for OMAP3 and later chips"
|
||||
depends on ARCH_OMAP
|
||||
select NOP_USB_XCEIV
|
||||
depends on NOP_USB_XCEIV
|
||||
default y
|
||||
---help---
|
||||
Enables support for the on-chip EHCI controller on
|
||||
|
@ -39,11 +39,12 @@
|
||||
|
||||
#define DRIVER_DESC "EHCI generic platform driver"
|
||||
#define EHCI_MAX_CLKS 3
|
||||
#define EHCI_MAX_RSTS 3
|
||||
#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
|
||||
|
||||
struct ehci_platform_priv {
|
||||
struct clk *clks[EHCI_MAX_CLKS];
|
||||
struct reset_control *rst;
|
||||
struct reset_control *rsts[EHCI_MAX_RSTS];
|
||||
struct phy **phys;
|
||||
int num_phys;
|
||||
bool reset_on_resume;
|
||||
@ -149,7 +150,7 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ehci_platform_priv *priv;
|
||||
struct ehci_hcd *ehci;
|
||||
int err, irq, phy_num, clk = 0;
|
||||
int err, irq, phy_num, clk = 0, rst;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
@ -234,16 +235,20 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_optional(&dev->dev, NULL);
|
||||
if (IS_ERR(priv->rst)) {
|
||||
err = PTR_ERR(priv->rst);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_clks;
|
||||
priv->rst = NULL;
|
||||
} else {
|
||||
err = reset_control_deassert(priv->rst);
|
||||
for (rst = 0; rst < EHCI_MAX_RSTS; rst++) {
|
||||
priv->rsts[rst] = devm_reset_control_get_shared_by_index(
|
||||
&dev->dev, rst);
|
||||
if (IS_ERR(priv->rsts[rst])) {
|
||||
err = PTR_ERR(priv->rsts[rst]);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_reset;
|
||||
priv->rsts[rst] = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
err = reset_control_deassert(priv->rsts[rst]);
|
||||
if (err)
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
@ -300,8 +305,8 @@ err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_reset:
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
while (--rst >= 0)
|
||||
reset_control_assert(priv->rsts[rst]);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
@ -319,15 +324,15 @@ static int ehci_platform_remove(struct platform_device *dev)
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
int clk;
|
||||
int clk, rst;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
for (rst = 0; rst < EHCI_MAX_RSTS && priv->rsts[rst]; rst++)
|
||||
reset_control_assert(priv->rsts[rst]);
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
@ -33,11 +33,12 @@
|
||||
|
||||
#define DRIVER_DESC "OHCI generic platform driver"
|
||||
#define OHCI_MAX_CLKS 3
|
||||
#define OHCI_MAX_RESETS 2
|
||||
#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv)
|
||||
|
||||
struct ohci_platform_priv {
|
||||
struct clk *clks[OHCI_MAX_CLKS];
|
||||
struct reset_control *rst;
|
||||
struct reset_control *resets[OHCI_MAX_RESETS];
|
||||
struct phy **phys;
|
||||
int num_phys;
|
||||
};
|
||||
@ -117,7 +118,7 @@ static int ohci_platform_probe(struct platform_device *dev)
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ohci_platform_priv *priv;
|
||||
struct ohci_hcd *ohci;
|
||||
int err, irq, phy_num, clk = 0;
|
||||
int err, irq, phy_num, clk = 0, rst = 0;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
@ -195,19 +196,21 @@ static int ohci_platform_probe(struct platform_device *dev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_optional(&dev->dev, NULL);
|
||||
if (IS_ERR(priv->rst)) {
|
||||
err = PTR_ERR(priv->rst);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_clks;
|
||||
priv->rst = NULL;
|
||||
} else {
|
||||
err = reset_control_deassert(priv->rst);
|
||||
if (err)
|
||||
goto err_put_clks;
|
||||
for (rst = 0; rst < OHCI_MAX_RESETS; rst++) {
|
||||
priv->resets[rst] =
|
||||
devm_reset_control_get_shared_by_index(
|
||||
&dev->dev, rst);
|
||||
if (IS_ERR(priv->resets[rst])) {
|
||||
err = PTR_ERR(priv->resets[rst]);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_reset;
|
||||
priv->resets[rst] = NULL;
|
||||
break;
|
||||
}
|
||||
err = reset_control_deassert(priv->resets[rst]);
|
||||
if (err)
|
||||
goto err_reset;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
@ -265,8 +268,8 @@ err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_reset:
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
while (--rst >= 0)
|
||||
reset_control_assert(priv->resets[rst]);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
@ -284,15 +287,15 @@ static int ohci_platform_remove(struct platform_device *dev)
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
|
||||
int clk;
|
||||
int clk, rst;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
for (rst = 0; rst < OHCI_MAX_RESETS && priv->resets[rst]; rst++)
|
||||
reset_control_assert(priv->resets[rst]);
|
||||
|
||||
for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
@ -37,7 +37,9 @@
|
||||
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
|
||||
*/
|
||||
static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
|
||||
unsigned int cycle_state, gfp_t flags)
|
||||
unsigned int cycle_state,
|
||||
unsigned int max_packet,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct xhci_segment *seg;
|
||||
dma_addr_t dma;
|
||||
@ -53,6 +55,14 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (max_packet) {
|
||||
seg->bounce_buf = kzalloc(max_packet, flags | GFP_DMA);
|
||||
if (!seg->bounce_buf) {
|
||||
dma_pool_free(xhci->segment_pool, seg->trbs, dma);
|
||||
kfree(seg);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
|
||||
if (cycle_state == 0) {
|
||||
for (i = 0; i < TRBS_PER_SEGMENT; i++)
|
||||
@ -70,6 +80,7 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
|
||||
dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
|
||||
seg->trbs = NULL;
|
||||
}
|
||||
kfree(seg->bounce_buf);
|
||||
kfree(seg);
|
||||
}
|
||||
|
||||
@ -317,11 +328,11 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring,
|
||||
static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
|
||||
struct xhci_segment **first, struct xhci_segment **last,
|
||||
unsigned int num_segs, unsigned int cycle_state,
|
||||
enum xhci_ring_type type, gfp_t flags)
|
||||
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags)
|
||||
{
|
||||
struct xhci_segment *prev;
|
||||
|
||||
prev = xhci_segment_alloc(xhci, cycle_state, flags);
|
||||
prev = xhci_segment_alloc(xhci, cycle_state, max_packet, flags);
|
||||
if (!prev)
|
||||
return -ENOMEM;
|
||||
num_segs--;
|
||||
@ -330,7 +341,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
|
||||
while (num_segs > 0) {
|
||||
struct xhci_segment *next;
|
||||
|
||||
next = xhci_segment_alloc(xhci, cycle_state, flags);
|
||||
next = xhci_segment_alloc(xhci, cycle_state, max_packet, flags);
|
||||
if (!next) {
|
||||
prev = *first;
|
||||
while (prev) {
|
||||
@ -360,7 +371,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
|
||||
*/
|
||||
static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
||||
unsigned int num_segs, unsigned int cycle_state,
|
||||
enum xhci_ring_type type, gfp_t flags)
|
||||
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags)
|
||||
{
|
||||
struct xhci_ring *ring;
|
||||
int ret;
|
||||
@ -370,13 +381,15 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
|
||||
return NULL;
|
||||
|
||||
ring->num_segs = num_segs;
|
||||
ring->bounce_buf_len = max_packet;
|
||||
INIT_LIST_HEAD(&ring->td_list);
|
||||
ring->type = type;
|
||||
if (num_segs == 0)
|
||||
return ring;
|
||||
|
||||
ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg,
|
||||
&ring->last_seg, num_segs, cycle_state, type, flags);
|
||||
&ring->last_seg, num_segs, cycle_state, type,
|
||||
max_packet, flags);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
@ -470,7 +483,8 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
ring->num_segs : num_segs_needed;
|
||||
|
||||
ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
|
||||
num_segs, ring->cycle_state, ring->type, flags);
|
||||
num_segs, ring->cycle_state, ring->type,
|
||||
ring->bounce_buf_len, flags);
|
||||
if (ret)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -652,7 +666,8 @@ struct xhci_ring *xhci_stream_id_to_ring(
|
||||
*/
|
||||
struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs,
|
||||
unsigned int num_streams, gfp_t mem_flags)
|
||||
unsigned int num_streams,
|
||||
unsigned int max_packet, gfp_t mem_flags)
|
||||
{
|
||||
struct xhci_stream_info *stream_info;
|
||||
u32 cur_stream;
|
||||
@ -704,9 +719,11 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
||||
* and add their segment DMA addresses to the radix tree.
|
||||
* Stream 0 is reserved.
|
||||
*/
|
||||
|
||||
for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
|
||||
stream_info->stream_rings[cur_stream] =
|
||||
xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, mem_flags);
|
||||
xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, max_packet,
|
||||
mem_flags);
|
||||
cur_ring = stream_info->stream_rings[cur_stream];
|
||||
if (!cur_ring)
|
||||
goto cleanup_rings;
|
||||
@ -1003,7 +1020,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
|
||||
}
|
||||
|
||||
/* Allocate endpoint 0 ring */
|
||||
dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, flags);
|
||||
dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, 0, flags);
|
||||
if (!dev->eps[0].ring)
|
||||
goto fail;
|
||||
|
||||
@ -1434,22 +1451,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||||
return -EINVAL;
|
||||
|
||||
ring_type = usb_endpoint_type(&ep->desc);
|
||||
/* Set up the endpoint ring */
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
xhci_ring_alloc(xhci, 2, 1, ring_type, mem_flags);
|
||||
if (!virt_dev->eps[ep_index].new_ring) {
|
||||
/* Attempt to use the ring cache */
|
||||
if (virt_dev->num_rings_cached == 0)
|
||||
return -ENOMEM;
|
||||
virt_dev->num_rings_cached--;
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
virt_dev->ring_cache[virt_dev->num_rings_cached];
|
||||
virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL;
|
||||
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring,
|
||||
1, ring_type);
|
||||
}
|
||||
virt_dev->eps[ep_index].skip = false;
|
||||
ep_ring = virt_dev->eps[ep_index].new_ring;
|
||||
|
||||
/*
|
||||
* Get values to fill the endpoint context, mostly from ep descriptor.
|
||||
@ -1479,6 +1480,23 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||||
if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2))
|
||||
mult = 0;
|
||||
|
||||
/* Set up the endpoint ring */
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
|
||||
if (!virt_dev->eps[ep_index].new_ring) {
|
||||
/* Attempt to use the ring cache */
|
||||
if (virt_dev->num_rings_cached == 0)
|
||||
return -ENOMEM;
|
||||
virt_dev->num_rings_cached--;
|
||||
virt_dev->eps[ep_index].new_ring =
|
||||
virt_dev->ring_cache[virt_dev->num_rings_cached];
|
||||
virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL;
|
||||
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring,
|
||||
1, ring_type);
|
||||
}
|
||||
virt_dev->eps[ep_index].skip = false;
|
||||
ep_ring = virt_dev->eps[ep_index].new_ring;
|
||||
|
||||
/* Fill the endpoint context */
|
||||
ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
|
||||
EP_INTERVAL(interval) |
|
||||
@ -2409,7 +2427,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
goto fail;
|
||||
|
||||
/* Set up the command ring to have one segments for now. */
|
||||
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags);
|
||||
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, 0, flags);
|
||||
if (!xhci->cmd_ring)
|
||||
goto fail;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
@ -2454,7 +2472,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
*/
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring");
|
||||
xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
|
||||
flags);
|
||||
0, flags);
|
||||
if (!xhci->event_ring)
|
||||
goto fail;
|
||||
if (xhci_check_trb_in_td_math(xhci) < 0)
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/xhci_pdriver.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "xhci.h"
|
||||
@ -138,8 +137,6 @@ MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
|
||||
|
||||
static int xhci_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
const struct of_device_id *match;
|
||||
const struct hc_driver *driver;
|
||||
struct xhci_hcd *xhci;
|
||||
@ -202,7 +199,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
match = of_match_node(usb_xhci_of_match, node);
|
||||
match = of_match_node(usb_xhci_of_match, pdev->dev.of_node);
|
||||
if (match) {
|
||||
const struct xhci_plat_priv *priv_match = match->data;
|
||||
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
||||
@ -223,8 +220,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
|
||||
(pdata && pdata->usb3_lpm_capable))
|
||||
if (device_property_read_bool(&pdev->dev, "usb3-lpm-capable"))
|
||||
xhci->quirks |= XHCI_LPM_SUPPORT;
|
||||
|
||||
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
||||
|
@ -66,6 +66,7 @@
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include "xhci.h"
|
||||
#include "xhci-trace.h"
|
||||
#include "xhci-mtk.h"
|
||||
@ -88,36 +89,25 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
|
||||
return seg->dma + (segment_offset * sizeof(*trb));
|
||||
}
|
||||
|
||||
/* Does this link TRB point to the first segment in a ring,
|
||||
* or was the previous TRB the last TRB on the last segment in the ERST?
|
||||
*/
|
||||
static bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
struct xhci_segment *seg, union xhci_trb *trb)
|
||||
static bool trb_is_link(union xhci_trb *trb)
|
||||
{
|
||||
if (ring == xhci->event_ring)
|
||||
return (trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
|
||||
(seg->next == xhci->event_ring->first_seg);
|
||||
else
|
||||
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
|
||||
return TRB_TYPE_LINK_LE32(trb->link.control);
|
||||
}
|
||||
|
||||
/* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
|
||||
* segment? I.e. would the updated event TRB pointer step off the end of the
|
||||
* event seg?
|
||||
*/
|
||||
static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
struct xhci_segment *seg, union xhci_trb *trb)
|
||||
static bool last_trb_on_seg(struct xhci_segment *seg, union xhci_trb *trb)
|
||||
{
|
||||
if (ring == xhci->event_ring)
|
||||
return trb == &seg->trbs[TRBS_PER_SEGMENT];
|
||||
else
|
||||
return TRB_TYPE_LINK_LE32(trb->link.control);
|
||||
return trb == &seg->trbs[TRBS_PER_SEGMENT - 1];
|
||||
}
|
||||
|
||||
static int enqueue_is_link_trb(struct xhci_ring *ring)
|
||||
static bool last_trb_on_ring(struct xhci_ring *ring,
|
||||
struct xhci_segment *seg, union xhci_trb *trb)
|
||||
{
|
||||
struct xhci_link_trb *link = &ring->enqueue->link;
|
||||
return TRB_TYPE_LINK_LE32(link->control);
|
||||
return last_trb_on_seg(seg, trb) && (seg->next == ring->first_seg);
|
||||
}
|
||||
|
||||
static bool link_trb_toggles_cycle(union xhci_trb *trb)
|
||||
{
|
||||
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
|
||||
}
|
||||
|
||||
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
|
||||
@ -129,7 +119,7 @@ static void next_trb(struct xhci_hcd *xhci,
|
||||
struct xhci_segment **seg,
|
||||
union xhci_trb **trb)
|
||||
{
|
||||
if (last_trb(xhci, ring, *seg, *trb)) {
|
||||
if (trb_is_link(*trb)) {
|
||||
*seg = (*seg)->next;
|
||||
*trb = ((*seg)->trbs);
|
||||
} else {
|
||||
@ -145,32 +135,29 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
{
|
||||
ring->deq_updates++;
|
||||
|
||||
/*
|
||||
* If this is not event ring, and the dequeue pointer
|
||||
* is not on a link TRB, there is one more usable TRB
|
||||
*/
|
||||
if (ring->type != TYPE_EVENT &&
|
||||
!last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
|
||||
ring->num_trbs_free++;
|
||||
|
||||
do {
|
||||
/*
|
||||
* Update the dequeue pointer further if that was a link TRB or
|
||||
* we're at the end of an event ring segment (which doesn't have
|
||||
* link TRBS)
|
||||
*/
|
||||
if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) {
|
||||
if (ring->type == TYPE_EVENT &&
|
||||
last_trb_on_last_seg(xhci, ring,
|
||||
ring->deq_seg, ring->dequeue)) {
|
||||
ring->cycle_state ^= 1;
|
||||
}
|
||||
ring->deq_seg = ring->deq_seg->next;
|
||||
ring->dequeue = ring->deq_seg->trbs;
|
||||
} else {
|
||||
/* event ring doesn't have link trbs, check for last trb */
|
||||
if (ring->type == TYPE_EVENT) {
|
||||
if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
|
||||
ring->dequeue++;
|
||||
return;
|
||||
}
|
||||
} while (last_trb(xhci, ring, ring->deq_seg, ring->dequeue));
|
||||
if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue))
|
||||
ring->cycle_state ^= 1;
|
||||
ring->deq_seg = ring->deq_seg->next;
|
||||
ring->dequeue = ring->deq_seg->trbs;
|
||||
return;
|
||||
}
|
||||
|
||||
/* All other rings have link trbs */
|
||||
if (!trb_is_link(ring->dequeue)) {
|
||||
ring->dequeue++;
|
||||
ring->num_trbs_free++;
|
||||
}
|
||||
while (trb_is_link(ring->dequeue)) {
|
||||
ring->deq_seg = ring->deq_seg->next;
|
||||
ring->dequeue = ring->deq_seg->trbs;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -198,50 +185,42 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
|
||||
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
|
||||
/* If this is not event ring, there is one less usable TRB */
|
||||
if (ring->type != TYPE_EVENT &&
|
||||
!last_trb(xhci, ring, ring->enq_seg, ring->enqueue))
|
||||
if (!trb_is_link(ring->enqueue))
|
||||
ring->num_trbs_free--;
|
||||
next = ++(ring->enqueue);
|
||||
|
||||
ring->enq_updates++;
|
||||
/* Update the dequeue pointer further if that was a link TRB or we're at
|
||||
* the end of an event ring segment (which doesn't have link TRBS)
|
||||
*/
|
||||
while (last_trb(xhci, ring, ring->enq_seg, next)) {
|
||||
if (ring->type != TYPE_EVENT) {
|
||||
/*
|
||||
* If the caller doesn't plan on enqueueing more
|
||||
* TDs before ringing the doorbell, then we
|
||||
* don't want to give the link TRB to the
|
||||
* hardware just yet. We'll give the link TRB
|
||||
* back in prepare_ring() just before we enqueue
|
||||
* the TD at the top of the ring.
|
||||
*/
|
||||
if (!chain && !more_trbs_coming)
|
||||
break;
|
||||
/* Update the dequeue pointer further if that was a link TRB */
|
||||
while (trb_is_link(next)) {
|
||||
|
||||
/* If we're not dealing with 0.95 hardware or
|
||||
* isoc rings on AMD 0.96 host,
|
||||
* carry over the chain bit of the previous TRB
|
||||
* (which may mean the chain bit is cleared).
|
||||
*/
|
||||
if (!(ring->type == TYPE_ISOC &&
|
||||
(xhci->quirks & XHCI_AMD_0x96_HOST))
|
||||
&& !xhci_link_trb_quirk(xhci)) {
|
||||
next->link.control &=
|
||||
cpu_to_le32(~TRB_CHAIN);
|
||||
next->link.control |=
|
||||
cpu_to_le32(chain);
|
||||
}
|
||||
/* Give this link TRB to the hardware */
|
||||
wmb();
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
/*
|
||||
* If the caller doesn't plan on enqueueing more TDs before
|
||||
* ringing the doorbell, then we don't want to give the link TRB
|
||||
* to the hardware just yet. We'll give the link TRB back in
|
||||
* prepare_ring() just before we enqueue the TD at the top of
|
||||
* the ring.
|
||||
*/
|
||||
if (!chain && !more_trbs_coming)
|
||||
break;
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
|
||||
ring->cycle_state ^= 1;
|
||||
}
|
||||
/* If we're not dealing with 0.95 hardware or isoc rings on
|
||||
* AMD 0.96 host, carry over the chain bit of the previous TRB
|
||||
* (which may mean the chain bit is cleared).
|
||||
*/
|
||||
if (!(ring->type == TYPE_ISOC &&
|
||||
(xhci->quirks & XHCI_AMD_0x96_HOST)) &&
|
||||
!xhci_link_trb_quirk(xhci)) {
|
||||
next->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
next->link.control |= cpu_to_le32(chain);
|
||||
}
|
||||
/* Give this link TRB to the hardware */
|
||||
wmb();
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (link_trb_toggles_cycle(next))
|
||||
ring->cycle_state ^= 1;
|
||||
|
||||
ring->enq_seg = ring->enq_seg->next;
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
next = ring->enqueue;
|
||||
@ -626,6 +605,31 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
||||
struct xhci_td *td)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
struct xhci_segment *seg = td->bounce_seg;
|
||||
struct urb *urb = td->urb;
|
||||
|
||||
if (!seg || !urb)
|
||||
return;
|
||||
|
||||
if (usb_urb_dir_out(urb)) {
|
||||
dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
|
||||
DMA_TO_DEVICE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* for in tranfers we need to copy the data from bounce to sg */
|
||||
sg_pcopy_from_buffer(urb->sg, urb->num_mapped_sgs, seg->bounce_buf,
|
||||
seg->bounce_len, seg->bounce_offs);
|
||||
dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
|
||||
DMA_FROM_DEVICE);
|
||||
seg->bounce_len = 0;
|
||||
seg->bounce_offs = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get a command completion for a Stop Endpoint Command, we need to
|
||||
* unlink any cancelled TDs from the ring. There are two ways to do that:
|
||||
@ -745,6 +749,9 @@ remove_finished_td:
|
||||
/* Doesn't matter what we pass for status, since the core will
|
||||
* just overwrite it (because the URB has been unlinked).
|
||||
*/
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
|
||||
if (ep_ring && cur_td->bounce_seg)
|
||||
xhci_unmap_td_bounce_buffer(xhci, ep_ring, cur_td);
|
||||
xhci_giveback_urb_in_irq(xhci, cur_td, 0);
|
||||
|
||||
/* Stop processing the cancelled list if the watchdog timer is
|
||||
@ -767,6 +774,9 @@ static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||||
list_del_init(&cur_td->td_list);
|
||||
if (!list_empty(&cur_td->cancelled_td_list))
|
||||
list_del_init(&cur_td->cancelled_td_list);
|
||||
|
||||
if (cur_td->bounce_seg)
|
||||
xhci_unmap_td_bounce_buffer(xhci, ring, cur_td);
|
||||
xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
@ -917,7 +927,7 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
|
||||
* the dequeue pointer one segment further, or we'll jump off
|
||||
* the segment into la-la-land.
|
||||
*/
|
||||
if (last_trb(xhci, ep_ring, ep_ring->deq_seg, ep_ring->dequeue)) {
|
||||
if (trb_is_link(ep_ring->dequeue)) {
|
||||
ep_ring->deq_seg = ep_ring->deq_seg->next;
|
||||
ep_ring->dequeue = ep_ring->deq_seg->trbs;
|
||||
}
|
||||
@ -926,8 +936,7 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
|
||||
/* We have more usable TRBs */
|
||||
ep_ring->num_trbs_free++;
|
||||
ep_ring->dequeue++;
|
||||
if (last_trb(xhci, ep_ring, ep_ring->deq_seg,
|
||||
ep_ring->dequeue)) {
|
||||
if (trb_is_link(ep_ring->dequeue)) {
|
||||
if (ep_ring->dequeue ==
|
||||
dev->eps[ep_index].queued_deq_ptr)
|
||||
break;
|
||||
@ -1865,6 +1874,10 @@ td_cleanup:
|
||||
urb = td->urb;
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
/* if a bounce buffer was used to align this td then unmap it */
|
||||
if (td->bounce_seg)
|
||||
xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);
|
||||
|
||||
/* Do one last check of the actual transfer length.
|
||||
* If the host controller said we transferred more data than the buffer
|
||||
* length, urb->actual_length will be a very big number (since it's
|
||||
@ -2865,36 +2878,29 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
||||
}
|
||||
}
|
||||
|
||||
if (enqueue_is_link_trb(ep_ring)) {
|
||||
struct xhci_ring *ring = ep_ring;
|
||||
union xhci_trb *next;
|
||||
while (trb_is_link(ep_ring->enqueue)) {
|
||||
/* If we're not dealing with 0.95 hardware or isoc rings
|
||||
* on AMD 0.96 host, clear the chain bit.
|
||||
*/
|
||||
if (!xhci_link_trb_quirk(xhci) &&
|
||||
!(ep_ring->type == TYPE_ISOC &&
|
||||
(xhci->quirks & XHCI_AMD_0x96_HOST)))
|
||||
ep_ring->enqueue->link.control &=
|
||||
cpu_to_le32(~TRB_CHAIN);
|
||||
else
|
||||
ep_ring->enqueue->link.control |=
|
||||
cpu_to_le32(TRB_CHAIN);
|
||||
|
||||
next = ring->enqueue;
|
||||
wmb();
|
||||
ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
while (last_trb(xhci, ring, ring->enq_seg, next)) {
|
||||
/* If we're not dealing with 0.95 hardware or isoc rings
|
||||
* on AMD 0.96 host, clear the chain bit.
|
||||
*/
|
||||
if (!xhci_link_trb_quirk(xhci) &&
|
||||
!(ring->type == TYPE_ISOC &&
|
||||
(xhci->quirks & XHCI_AMD_0x96_HOST)))
|
||||
next->link.control &= cpu_to_le32(~TRB_CHAIN);
|
||||
else
|
||||
next->link.control |= cpu_to_le32(TRB_CHAIN);
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (link_trb_toggles_cycle(ep_ring->enqueue))
|
||||
ep_ring->cycle_state ^= 1;
|
||||
|
||||
wmb();
|
||||
next->link.control ^= cpu_to_le32(TRB_CYCLE);
|
||||
|
||||
/* Toggle the cycle bit after the last ring segment. */
|
||||
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
|
||||
ring->cycle_state ^= 1;
|
||||
}
|
||||
ring->enq_seg = ring->enq_seg->next;
|
||||
ring->enqueue = ring->enq_seg->trbs;
|
||||
next = ring->enqueue;
|
||||
}
|
||||
ep_ring->enq_seg = ep_ring->enq_seg->next;
|
||||
ep_ring->enqueue = ep_ring->enq_seg->trbs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3092,7 +3098,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
*/
|
||||
static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
|
||||
int trb_buff_len, unsigned int td_total_len,
|
||||
struct urb *urb, unsigned int num_trbs_left)
|
||||
struct urb *urb, bool more_trbs_coming)
|
||||
{
|
||||
u32 maxp, total_packet_count;
|
||||
|
||||
@ -3101,7 +3107,7 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
|
||||
return ((td_total_len - transferred) >> 10);
|
||||
|
||||
/* One TRB with a zero-length data packet. */
|
||||
if (num_trbs_left == 0 || (transferred == 0 && trb_buff_len == 0) ||
|
||||
if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) ||
|
||||
trb_buff_len == td_total_len)
|
||||
return 0;
|
||||
|
||||
@ -3116,37 +3122,103 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
|
||||
return (total_packet_count - ((transferred + trb_buff_len) / maxp));
|
||||
}
|
||||
|
||||
|
||||
static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len,
|
||||
u32 *trb_buff_len, struct xhci_segment *seg)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
unsigned int unalign;
|
||||
unsigned int max_pkt;
|
||||
u32 new_buff_len;
|
||||
|
||||
max_pkt = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
|
||||
unalign = (enqd_len + *trb_buff_len) % max_pkt;
|
||||
|
||||
/* we got lucky, last normal TRB data on segment is packet aligned */
|
||||
if (unalign == 0)
|
||||
return 0;
|
||||
|
||||
xhci_dbg(xhci, "Unaligned %d bytes, buff len %d\n",
|
||||
unalign, *trb_buff_len);
|
||||
|
||||
/* is the last nornal TRB alignable by splitting it */
|
||||
if (*trb_buff_len > unalign) {
|
||||
*trb_buff_len -= unalign;
|
||||
xhci_dbg(xhci, "split align, new buff len %d\n", *trb_buff_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want enqd_len + trb_buff_len to sum up to a number aligned to
|
||||
* number which is divisible by the endpoint's wMaxPacketSize. IOW:
|
||||
* (size of currently enqueued TRBs + remainder) % wMaxPacketSize == 0.
|
||||
*/
|
||||
new_buff_len = max_pkt - (enqd_len % max_pkt);
|
||||
|
||||
if (new_buff_len > (urb->transfer_buffer_length - enqd_len))
|
||||
new_buff_len = (urb->transfer_buffer_length - enqd_len);
|
||||
|
||||
/* create a max max_pkt sized bounce buffer pointed to by last trb */
|
||||
if (usb_urb_dir_out(urb)) {
|
||||
sg_pcopy_to_buffer(urb->sg, urb->num_mapped_sgs,
|
||||
seg->bounce_buf, new_buff_len, enqd_len);
|
||||
seg->bounce_dma = dma_map_single(dev, seg->bounce_buf,
|
||||
max_pkt, DMA_TO_DEVICE);
|
||||
} else {
|
||||
seg->bounce_dma = dma_map_single(dev, seg->bounce_buf,
|
||||
max_pkt, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
if (dma_mapping_error(dev, seg->bounce_dma)) {
|
||||
/* try without aligning. Some host controllers survive */
|
||||
xhci_warn(xhci, "Failed mapping bounce buffer, not aligning\n");
|
||||
return 0;
|
||||
}
|
||||
*trb_buff_len = new_buff_len;
|
||||
seg->bounce_len = new_buff_len;
|
||||
seg->bounce_offs = enqd_len;
|
||||
|
||||
xhci_dbg(xhci, "Bounce align, new buff len %d\n", *trb_buff_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This is very similar to what ehci-q.c qtd_fill() does */
|
||||
int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||
{
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_ring *ring;
|
||||
struct urb_priv *urb_priv;
|
||||
struct xhci_td *td;
|
||||
struct xhci_generic_trb *start_trb;
|
||||
struct scatterlist *sg = NULL;
|
||||
bool more_trbs_coming;
|
||||
bool zero_length_needed;
|
||||
unsigned int num_trbs, last_trb_num, i;
|
||||
bool more_trbs_coming = true;
|
||||
bool need_zero_pkt = false;
|
||||
bool first_trb = true;
|
||||
unsigned int num_trbs;
|
||||
unsigned int start_cycle, num_sgs = 0;
|
||||
unsigned int running_total, block_len, trb_buff_len;
|
||||
unsigned int full_len;
|
||||
int ret;
|
||||
unsigned int enqd_len, block_len, trb_buff_len, full_len;
|
||||
int sent_len, ret;
|
||||
u32 field, length_field, remainder;
|
||||
u64 addr;
|
||||
u64 addr, send_addr;
|
||||
|
||||
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ep_ring)
|
||||
ring = xhci_urb_to_transfer_ring(xhci, urb);
|
||||
if (!ring)
|
||||
return -EINVAL;
|
||||
|
||||
full_len = urb->transfer_buffer_length;
|
||||
/* If we have scatter/gather list, we use it. */
|
||||
if (urb->num_sgs) {
|
||||
num_sgs = urb->num_mapped_sgs;
|
||||
sg = urb->sg;
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
block_len = sg_dma_len(sg);
|
||||
num_trbs = count_sg_trbs_needed(urb);
|
||||
} else
|
||||
} else {
|
||||
num_trbs = count_trbs_needed(urb);
|
||||
|
||||
addr = (u64) urb->transfer_dma;
|
||||
block_len = full_len;
|
||||
}
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
num_trbs, urb, 0, mem_flags);
|
||||
@ -3155,20 +3227,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
|
||||
urb_priv = urb->hcpriv;
|
||||
|
||||
last_trb_num = num_trbs - 1;
|
||||
|
||||
/* Deal with URB_ZERO_PACKET - need one more td/trb */
|
||||
zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET &&
|
||||
urb_priv->length == 2;
|
||||
if (zero_length_needed) {
|
||||
num_trbs++;
|
||||
xhci_dbg(xhci, "Creating zero length td.\n");
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
1, urb, 1, mem_flags);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
if (urb->transfer_flags & URB_ZERO_PACKET && urb_priv->length > 1)
|
||||
need_zero_pkt = true;
|
||||
|
||||
td = urb_priv->td[0];
|
||||
|
||||
@ -3177,102 +3238,97 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
* until we've finished creating all the other TRBs. The ring's cycle
|
||||
* state may change as we enqueue the other TRBs, so save it too.
|
||||
*/
|
||||
start_trb = &ep_ring->enqueue->generic;
|
||||
start_cycle = ep_ring->cycle_state;
|
||||
|
||||
full_len = urb->transfer_buffer_length;
|
||||
running_total = 0;
|
||||
block_len = 0;
|
||||
start_trb = &ring->enqueue->generic;
|
||||
start_cycle = ring->cycle_state;
|
||||
send_addr = addr;
|
||||
|
||||
/* Queue the TRBs, even if they are zero-length */
|
||||
for (i = 0; i < num_trbs; i++) {
|
||||
for (enqd_len = 0; enqd_len < full_len; enqd_len += trb_buff_len) {
|
||||
field = TRB_TYPE(TRB_NORMAL);
|
||||
|
||||
if (block_len == 0) {
|
||||
/* A new contiguous block. */
|
||||
if (sg) {
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
block_len = sg_dma_len(sg);
|
||||
} else {
|
||||
addr = (u64) urb->transfer_dma;
|
||||
block_len = full_len;
|
||||
}
|
||||
/* TRB buffer should not cross 64KB boundaries */
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
trb_buff_len = min_t(unsigned int,
|
||||
trb_buff_len,
|
||||
block_len);
|
||||
} else {
|
||||
/* Further through the contiguous block. */
|
||||
trb_buff_len = block_len;
|
||||
if (trb_buff_len > TRB_MAX_BUFF_SIZE)
|
||||
trb_buff_len = TRB_MAX_BUFF_SIZE;
|
||||
}
|
||||
/* TRB buffer should not cross 64KB boundaries */
|
||||
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(addr);
|
||||
trb_buff_len = min_t(unsigned int, trb_buff_len, block_len);
|
||||
|
||||
if (running_total + trb_buff_len > full_len)
|
||||
trb_buff_len = full_len - running_total;
|
||||
if (enqd_len + trb_buff_len > full_len)
|
||||
trb_buff_len = full_len - enqd_len;
|
||||
|
||||
/* Don't change the cycle bit of the first TRB until later */
|
||||
if (i == 0) {
|
||||
if (first_trb) {
|
||||
first_trb = false;
|
||||
if (start_cycle == 0)
|
||||
field |= TRB_CYCLE;
|
||||
} else
|
||||
field |= ep_ring->cycle_state;
|
||||
field |= ring->cycle_state;
|
||||
|
||||
/* Chain all the TRBs together; clear the chain bit in the last
|
||||
* TRB to indicate it's the last TRB in the chain.
|
||||
*/
|
||||
if (i < last_trb_num) {
|
||||
if (enqd_len + trb_buff_len < full_len) {
|
||||
field |= TRB_CHAIN;
|
||||
} else {
|
||||
field |= TRB_IOC;
|
||||
if (i == last_trb_num)
|
||||
td->last_trb = ep_ring->enqueue;
|
||||
else if (zero_length_needed) {
|
||||
trb_buff_len = 0;
|
||||
urb_priv->td[1]->last_trb = ep_ring->enqueue;
|
||||
if (trb_is_link(ring->enqueue + 1)) {
|
||||
if (xhci_align_td(xhci, urb, enqd_len,
|
||||
&trb_buff_len,
|
||||
ring->enq_seg)) {
|
||||
send_addr = ring->enq_seg->bounce_dma;
|
||||
/* assuming TD won't span 2 segs */
|
||||
td->bounce_seg = ring->enq_seg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enqd_len + trb_buff_len >= full_len) {
|
||||
field &= ~TRB_CHAIN;
|
||||
field |= TRB_IOC;
|
||||
more_trbs_coming = false;
|
||||
td->last_trb = ring->enqueue;
|
||||
}
|
||||
|
||||
/* Only set interrupt on short packet for IN endpoints */
|
||||
if (usb_urb_dir_in(urb))
|
||||
field |= TRB_ISP;
|
||||
|
||||
/* Set the TRB length, TD size, and interrupter fields. */
|
||||
remainder = xhci_td_remainder(xhci, running_total,
|
||||
trb_buff_len, full_len,
|
||||
urb, num_trbs - i - 1);
|
||||
remainder = xhci_td_remainder(xhci, enqd_len, trb_buff_len,
|
||||
full_len, urb, more_trbs_coming);
|
||||
|
||||
length_field = TRB_LEN(trb_buff_len) |
|
||||
TRB_TD_SIZE(remainder) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
||||
if (i < num_trbs - 1)
|
||||
more_trbs_coming = true;
|
||||
else
|
||||
more_trbs_coming = false;
|
||||
queue_trb(xhci, ep_ring, more_trbs_coming,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt,
|
||||
lower_32_bits(send_addr),
|
||||
upper_32_bits(send_addr),
|
||||
length_field,
|
||||
field);
|
||||
|
||||
running_total += trb_buff_len;
|
||||
addr += trb_buff_len;
|
||||
block_len -= trb_buff_len;
|
||||
sent_len = trb_buff_len;
|
||||
|
||||
if (sg) {
|
||||
if (block_len == 0) {
|
||||
/* New sg entry */
|
||||
--num_sgs;
|
||||
if (num_sgs == 0)
|
||||
break;
|
||||
while (sg && sent_len >= block_len) {
|
||||
/* New sg entry */
|
||||
--num_sgs;
|
||||
sent_len -= block_len;
|
||||
if (num_sgs != 0) {
|
||||
sg = sg_next(sg);
|
||||
block_len = sg_dma_len(sg);
|
||||
addr = (u64) sg_dma_address(sg);
|
||||
addr += sent_len;
|
||||
}
|
||||
}
|
||||
block_len -= sent_len;
|
||||
send_addr = addr;
|
||||
}
|
||||
|
||||
check_trb_math(urb, running_total);
|
||||
if (need_zero_pkt) {
|
||||
ret = prepare_transfer(xhci, xhci->devs[slot_id],
|
||||
ep_index, urb->stream_id,
|
||||
1, urb, 1, mem_flags);
|
||||
urb_priv->td[1]->last_trb = ring->enqueue;
|
||||
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
|
||||
queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
|
||||
}
|
||||
|
||||
check_trb_math(urb, enqd_len);
|
||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||
start_cycle, start_trb);
|
||||
return 0;
|
||||
@ -3666,7 +3722,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||
/* Set the TRB length, TD size, & interrupter fields. */
|
||||
remainder = xhci_td_remainder(xhci, running_total,
|
||||
trb_buff_len, td_len,
|
||||
urb, trbs_per_td - j - 1);
|
||||
urb, more_trbs_coming);
|
||||
|
||||
length_field = TRB_LEN(trb_buff_len) |
|
||||
TRB_INTR_TARGET(0);
|
||||
|
@ -3139,6 +3139,7 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
struct xhci_input_control_ctx *ctrl_ctx;
|
||||
unsigned int ep_index;
|
||||
unsigned int num_stream_ctxs;
|
||||
unsigned int max_packet;
|
||||
unsigned long flags;
|
||||
u32 changed_ep_bitmask = 0;
|
||||
|
||||
@ -3212,9 +3213,11 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
|
||||
for (i = 0; i < num_eps; i++) {
|
||||
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
|
||||
max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&eps[i]->desc));
|
||||
vdev->eps[ep_index].stream_info = xhci_alloc_stream_info(xhci,
|
||||
num_stream_ctxs,
|
||||
num_streams, mem_flags);
|
||||
num_streams,
|
||||
max_packet, mem_flags);
|
||||
if (!vdev->eps[ep_index].stream_info)
|
||||
goto cleanup;
|
||||
/* Set maxPstreams in endpoint context and update deq ptr to
|
||||
|
@ -1347,6 +1347,11 @@ struct xhci_segment {
|
||||
/* private to HCD */
|
||||
struct xhci_segment *next;
|
||||
dma_addr_t dma;
|
||||
/* Max packet sized bounce buffer for td-fragmant alignment */
|
||||
dma_addr_t bounce_dma;
|
||||
void *bounce_buf;
|
||||
unsigned int bounce_offs;
|
||||
unsigned int bounce_len;
|
||||
};
|
||||
|
||||
struct xhci_td {
|
||||
@ -1356,6 +1361,7 @@ struct xhci_td {
|
||||
struct xhci_segment *start_seg;
|
||||
union xhci_trb *first_trb;
|
||||
union xhci_trb *last_trb;
|
||||
struct xhci_segment *bounce_seg;
|
||||
/* actual_length of the URB has already been set */
|
||||
bool urb_length_set;
|
||||
};
|
||||
@ -1405,6 +1411,7 @@ struct xhci_ring {
|
||||
unsigned int num_segs;
|
||||
unsigned int num_trbs_free;
|
||||
unsigned int num_trbs_free_temp;
|
||||
unsigned int bounce_buf_len;
|
||||
enum xhci_ring_type type;
|
||||
bool last_td_was_short;
|
||||
struct radix_tree_root *trb_address_map;
|
||||
@ -1807,7 +1814,8 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
|
||||
unsigned int ep_index);
|
||||
struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
|
||||
unsigned int num_stream_ctxs,
|
||||
unsigned int num_streams, gfp_t flags);
|
||||
unsigned int num_streams,
|
||||
unsigned int max_packet, gfp_t flags);
|
||||
void xhci_free_stream_info(struct xhci_hcd *xhci,
|
||||
struct xhci_stream_info *stream_info);
|
||||
void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
|
||||
|
@ -13,11 +13,11 @@ typedef void (*mts_scsi_cmnd_callback)(struct scsi_cmnd *);
|
||||
|
||||
struct mts_transfer_context
|
||||
{
|
||||
struct mts_desc* instance;
|
||||
struct mts_desc *instance;
|
||||
mts_scsi_cmnd_callback final_callback;
|
||||
struct scsi_cmnd *srb;
|
||||
|
||||
void* data;
|
||||
void *data;
|
||||
unsigned data_length;
|
||||
int data_pipe;
|
||||
int fragment;
|
||||
@ -38,7 +38,7 @@ struct mts_desc {
|
||||
u8 ep_response;
|
||||
u8 ep_image;
|
||||
|
||||
struct Scsi_Host * host;
|
||||
struct Scsi_Host *host;
|
||||
|
||||
struct urb *urb;
|
||||
struct mts_transfer_context context;
|
||||
|
@ -330,6 +330,17 @@ static int usb3503_i2c_probe(struct i2c_client *i2c,
|
||||
return usb3503_probe(hub);
|
||||
}
|
||||
|
||||
static int usb3503_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct usb3503 *hub;
|
||||
|
||||
hub = i2c_get_clientdata(i2c);
|
||||
if (hub->clk)
|
||||
clk_disable_unprepare(hub->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb3503_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb3503 *hub;
|
||||
@ -338,10 +349,22 @@ static int usb3503_platform_probe(struct platform_device *pdev)
|
||||
if (!hub)
|
||||
return -ENOMEM;
|
||||
hub->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, hub);
|
||||
|
||||
return usb3503_probe(hub);
|
||||
}
|
||||
|
||||
static int usb3503_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb3503 *hub;
|
||||
|
||||
hub = platform_get_drvdata(pdev);
|
||||
if (hub->clk)
|
||||
clk_disable_unprepare(hub->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int usb3503_i2c_suspend(struct device *dev)
|
||||
{
|
||||
@ -395,6 +418,7 @@ static struct i2c_driver usb3503_i2c_driver = {
|
||||
.of_match_table = of_match_ptr(usb3503_of_match),
|
||||
},
|
||||
.probe = usb3503_i2c_probe,
|
||||
.remove = usb3503_i2c_remove,
|
||||
.id_table = usb3503_id,
|
||||
};
|
||||
|
||||
@ -404,6 +428,7 @@ static struct platform_driver usb3503_platform_driver = {
|
||||
.of_match_table = of_match_ptr(usb3503_of_match),
|
||||
},
|
||||
.probe = usb3503_platform_probe,
|
||||
.remove = usb3503_platform_remove,
|
||||
};
|
||||
|
||||
static int __init usb3503_init(void)
|
||||
|
@ -2,9 +2,12 @@
|
||||
# for USB OTG silicon based on Mentor Graphics INVENTRA designs
|
||||
#
|
||||
|
||||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_musb_trace.o := -I$(src)
|
||||
|
||||
obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
|
||||
|
||||
musb_hdrc-y := musb_core.o
|
||||
musb_hdrc-y := musb_core.o musb_trace.o
|
||||
|
||||
musb_hdrc-$(CONFIG_USB_MUSB_HOST)$(CONFIG_USB_MUSB_DUAL_ROLE) += musb_virthub.o musb_host.o
|
||||
musb_hdrc-$(CONFIG_USB_MUSB_GADGET)$(CONFIG_USB_MUSB_DUAL_ROLE) += musb_gadget_ep0.o musb_gadget.o
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "musb_core.h"
|
||||
#include "musb_debug.h"
|
||||
#include "cppi_dma.h"
|
||||
#include "davinci.h"
|
||||
|
||||
|
||||
/* CPPI DMA status 7-mar-2006:
|
||||
@ -232,7 +233,7 @@ static void cppi_controller_stop(struct cppi *controller)
|
||||
musb_writel(tibase, DAVINCI_RXCPPI_INTCLR_REG,
|
||||
DAVINCI_DMA_ALL_CHANNELS_ENABLE);
|
||||
|
||||
dev_dbg(musb->controller, "Tearing down RX and TX Channels\n");
|
||||
musb_dbg(musb, "Tearing down RX and TX Channels");
|
||||
for (i = 0; i < ARRAY_SIZE(controller->tx); i++) {
|
||||
/* FIXME restructure of txdma to use bds like rxdma */
|
||||
controller->tx[i].last_processed = NULL;
|
||||
@ -297,13 +298,13 @@ cppi_channel_allocate(struct dma_controller *c,
|
||||
*/
|
||||
if (transmit) {
|
||||
if (index >= ARRAY_SIZE(controller->tx)) {
|
||||
dev_dbg(musb->controller, "no %cX%d CPPI channel\n", 'T', index);
|
||||
musb_dbg(musb, "no %cX%d CPPI channel", 'T', index);
|
||||
return NULL;
|
||||
}
|
||||
cppi_ch = controller->tx + index;
|
||||
} else {
|
||||
if (index >= ARRAY_SIZE(controller->rx)) {
|
||||
dev_dbg(musb->controller, "no %cX%d CPPI channel\n", 'R', index);
|
||||
musb_dbg(musb, "no %cX%d CPPI channel", 'R', index);
|
||||
return NULL;
|
||||
}
|
||||
cppi_ch = controller->rx + index;
|
||||
@ -314,13 +315,13 @@ cppi_channel_allocate(struct dma_controller *c,
|
||||
* with the other DMA engine too
|
||||
*/
|
||||
if (cppi_ch->hw_ep)
|
||||
dev_dbg(musb->controller, "re-allocating DMA%d %cX channel %p\n",
|
||||
musb_dbg(musb, "re-allocating DMA%d %cX channel %p",
|
||||
index, transmit ? 'T' : 'R', cppi_ch);
|
||||
cppi_ch->hw_ep = ep;
|
||||
cppi_ch->channel.status = MUSB_DMA_STATUS_FREE;
|
||||
cppi_ch->channel.max_len = 0x7fffffff;
|
||||
|
||||
dev_dbg(musb->controller, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R');
|
||||
musb_dbg(musb, "Allocate CPPI%d %cX", index, transmit ? 'T' : 'R');
|
||||
return &cppi_ch->channel;
|
||||
}
|
||||
|
||||
@ -335,8 +336,8 @@ static void cppi_channel_release(struct dma_channel *channel)
|
||||
c = container_of(channel, struct cppi_channel, channel);
|
||||
tibase = c->controller->tibase;
|
||||
if (!c->hw_ep)
|
||||
dev_dbg(c->controller->musb->controller,
|
||||
"releasing idle DMA channel %p\n", c);
|
||||
musb_dbg(c->controller->musb,
|
||||
"releasing idle DMA channel %p", c);
|
||||
else if (!c->transmit)
|
||||
core_rxirq_enable(tibase, c->index + 1);
|
||||
|
||||
@ -354,11 +355,10 @@ cppi_dump_rx(int level, struct cppi_channel *c, const char *tag)
|
||||
|
||||
musb_ep_select(base, c->index + 1);
|
||||
|
||||
dev_dbg(c->controller->musb->controller,
|
||||
musb_dbg(c->controller->musb,
|
||||
"RX DMA%d%s: %d left, csr %04x, "
|
||||
"%08x H%08x S%08x C%08x, "
|
||||
"B%08x L%08x %08x .. %08x"
|
||||
"\n",
|
||||
"B%08x L%08x %08x .. %08x",
|
||||
c->index, tag,
|
||||
musb_readl(c->controller->tibase,
|
||||
DAVINCI_RXCPPI_BUFCNT0_REG + 4 * c->index),
|
||||
@ -385,11 +385,10 @@ cppi_dump_tx(int level, struct cppi_channel *c, const char *tag)
|
||||
|
||||
musb_ep_select(base, c->index + 1);
|
||||
|
||||
dev_dbg(c->controller->musb->controller,
|
||||
musb_dbg(c->controller->musb,
|
||||
"TX DMA%d%s: csr %04x, "
|
||||
"H%08x S%08x C%08x %08x, "
|
||||
"F%08x L%08x .. %08x"
|
||||
"\n",
|
||||
"F%08x L%08x .. %08x",
|
||||
c->index, tag,
|
||||
musb_readw(c->hw_ep->regs, MUSB_TXCSR),
|
||||
|
||||
@ -590,7 +589,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx)
|
||||
length = min(n_bds * maxpacket, length);
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n",
|
||||
musb_dbg(musb, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u",
|
||||
tx->index,
|
||||
maxpacket,
|
||||
rndis ? "rndis" : "transparent",
|
||||
@ -647,7 +646,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx)
|
||||
bd->hw_options |= CPPI_ZERO_SET;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "TXBD %p: nxt %08x buf %08x len %04x opt %08x\n",
|
||||
musb_dbg(musb, "TXBD %p: nxt %08x buf %08x len %04x opt %08x",
|
||||
bd, bd->hw_next, bd->hw_bufp,
|
||||
bd->hw_off_len, bd->hw_options);
|
||||
|
||||
@ -813,8 +812,8 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket)
|
||||
|
||||
length = min(n_bds * maxpacket, length);
|
||||
|
||||
dev_dbg(musb->controller, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) "
|
||||
"dma 0x%llx len %u %u/%u\n",
|
||||
musb_dbg(musb, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) "
|
||||
"dma 0x%llx len %u %u/%u",
|
||||
rx->index, maxpacket,
|
||||
onepacket
|
||||
? (is_rndis ? "rndis" : "onepacket")
|
||||
@ -924,7 +923,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket)
|
||||
DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4))
|
||||
& 0xffff;
|
||||
if (i < (2 + n_bds)) {
|
||||
dev_dbg(musb->controller, "bufcnt%d underrun - %d (for %d)\n",
|
||||
musb_dbg(musb, "bufcnt%d underrun - %d (for %d)",
|
||||
rx->index, i, n_bds);
|
||||
musb_writel(tibase,
|
||||
DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4),
|
||||
@ -973,7 +972,7 @@ static int cppi_channel_program(struct dma_channel *ch,
|
||||
/* WARN_ON(1); */
|
||||
break;
|
||||
case MUSB_DMA_STATUS_UNKNOWN:
|
||||
dev_dbg(musb->controller, "%cX DMA%d not allocated!\n",
|
||||
musb_dbg(musb, "%cX DMA%d not allocated!",
|
||||
cppi_ch->transmit ? 'T' : 'R',
|
||||
cppi_ch->index);
|
||||
/* FALLTHROUGH */
|
||||
@ -1029,8 +1028,8 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
|
||||
if (!completed && (bd->hw_options & CPPI_OWN_SET))
|
||||
break;
|
||||
|
||||
dev_dbg(musb->controller, "C/RXBD %llx: nxt %08x buf %08x "
|
||||
"off.len %08x opt.len %08x (%d)\n",
|
||||
musb_dbg(musb, "C/RXBD %llx: nxt %08x buf %08x "
|
||||
"off.len %08x opt.len %08x (%d)",
|
||||
(unsigned long long)bd->dma, bd->hw_next, bd->hw_bufp,
|
||||
bd->hw_off_len, bd->hw_options,
|
||||
rx->channel.actual_len);
|
||||
@ -1051,7 +1050,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
|
||||
* CPPI ignores those BDs even though OWN is still set.
|
||||
*/
|
||||
completed = true;
|
||||
dev_dbg(musb->controller, "rx short %d/%d (%d)\n",
|
||||
musb_dbg(musb, "rx short %d/%d (%d)",
|
||||
len, bd->buflen,
|
||||
rx->channel.actual_len);
|
||||
}
|
||||
@ -1101,7 +1100,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
|
||||
musb_ep_select(cppi->mregs, rx->index + 1);
|
||||
csr = musb_readw(regs, MUSB_RXCSR);
|
||||
if (csr & MUSB_RXCSR_DMAENAB) {
|
||||
dev_dbg(musb->controller, "list%d %p/%p, last %llx%s, csr %04x\n",
|
||||
musb_dbg(musb, "list%d %p/%p, last %llx%s, csr %04x",
|
||||
rx->index,
|
||||
rx->head, rx->tail,
|
||||
rx->last_processed
|
||||
@ -1164,7 +1163,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "CPPI IRQ Tx%x Rx%x\n", tx, rx);
|
||||
musb_dbg(musb, "CPPI IRQ Tx%x Rx%x", tx, rx);
|
||||
|
||||
/* process TX channels */
|
||||
for (index = 0; tx; tx = tx >> 1, index++) {
|
||||
@ -1192,7 +1191,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
|
||||
* that needs to be acknowledged.
|
||||
*/
|
||||
if (NULL == bd) {
|
||||
dev_dbg(musb->controller, "null BD\n");
|
||||
musb_dbg(musb, "null BD");
|
||||
musb_writel(&tx_ram->tx_complete, 0, 0);
|
||||
continue;
|
||||
}
|
||||
@ -1207,7 +1206,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
|
||||
if (bd->hw_options & CPPI_OWN_SET)
|
||||
break;
|
||||
|
||||
dev_dbg(musb->controller, "C/TXBD %p n %x b %x off %x opt %x\n",
|
||||
musb_dbg(musb, "C/TXBD %p n %x b %x off %x opt %x",
|
||||
bd, bd->hw_next, bd->hw_bufp,
|
||||
bd->hw_off_len, bd->hw_options);
|
||||
|
||||
|
@ -7,17 +7,10 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#include "musb_dma.h"
|
||||
#include "musb_core.h"
|
||||
|
||||
|
||||
/* FIXME fully isolate CPPI from DaVinci ... the "CPPI generic" registers
|
||||
* would seem to be shared with the TUSB6020 (over VLYNQ).
|
||||
*/
|
||||
|
||||
#include "davinci.h"
|
||||
|
||||
#include "musb_dma.h"
|
||||
|
||||
/* CPPI RX/TX state RAM */
|
||||
|
||||
@ -131,4 +124,24 @@ struct cppi {
|
||||
/* CPPI IRQ handler */
|
||||
extern irqreturn_t cppi_interrupt(int, void *);
|
||||
|
||||
struct cppi41_dma_channel {
|
||||
struct dma_channel channel;
|
||||
struct cppi41_dma_controller *controller;
|
||||
struct musb_hw_ep *hw_ep;
|
||||
struct dma_chan *dc;
|
||||
dma_cookie_t cookie;
|
||||
u8 port_num;
|
||||
u8 is_tx;
|
||||
u8 is_allocated;
|
||||
u8 usb_toggle;
|
||||
|
||||
dma_addr_t buf_addr;
|
||||
u32 total_len;
|
||||
u32 prog_len;
|
||||
u32 transferred;
|
||||
u32 packet_sz;
|
||||
struct list_head tx_check;
|
||||
int tx_zlp;
|
||||
};
|
||||
|
||||
#endif /* end of ifndef _CPPI_DMA_H_ */
|
||||
|
@ -102,6 +102,7 @@
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "musb_trace.h"
|
||||
|
||||
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
|
||||
|
||||
@ -258,31 +259,43 @@ static u32 musb_default_busctl_offset(u8 epnum, u16 offset)
|
||||
|
||||
static u8 musb_default_readb(const void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readb(addr + offset);
|
||||
u8 data = __raw_readb(addr + offset);
|
||||
|
||||
trace_musb_readb(__builtin_return_address(0), addr, offset, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data)
|
||||
{
|
||||
trace_musb_writeb(__builtin_return_address(0), addr, offset, data);
|
||||
__raw_writeb(data, addr + offset);
|
||||
}
|
||||
|
||||
static u16 musb_default_readw(const void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readw(addr + offset);
|
||||
u16 data = __raw_readw(addr + offset);
|
||||
|
||||
trace_musb_readw(__builtin_return_address(0), addr, offset, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data)
|
||||
{
|
||||
trace_musb_writew(__builtin_return_address(0), addr, offset, data);
|
||||
__raw_writew(data, addr + offset);
|
||||
}
|
||||
|
||||
static u32 musb_default_readl(const void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readl(addr + offset);
|
||||
u32 data = __raw_readl(addr + offset);
|
||||
|
||||
trace_musb_readl(__builtin_return_address(0), addr, offset, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void musb_default_writel(void __iomem *addr, unsigned offset, u32 data)
|
||||
{
|
||||
trace_musb_writel(__builtin_return_address(0), addr, offset, data);
|
||||
__raw_writel(data, addr + offset);
|
||||
}
|
||||
|
||||
@ -461,20 +474,21 @@ static void musb_otg_timer_func(unsigned long data)
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
dev_dbg(musb->controller, "HNP: b_wait_acon timeout; back to b_peripheral\n");
|
||||
musb_dbg(musb,
|
||||
"HNP: b_wait_acon timeout; back to b_peripheral");
|
||||
musb_g_disconnect(musb);
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb->is_active = 0;
|
||||
break;
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
dev_dbg(musb->controller, "HNP: %s timeout\n",
|
||||
musb_dbg(musb, "HNP: %s timeout",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
musb_platform_set_vbus(musb, 0);
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "HNP: Unhandled mode %s\n",
|
||||
musb_dbg(musb, "HNP: Unhandled mode %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
@ -489,17 +503,17 @@ void musb_hnp_stop(struct musb *musb)
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u8 reg;
|
||||
|
||||
dev_dbg(musb->controller, "HNP: stop from %s\n",
|
||||
musb_dbg(musb, "HNP: stop from %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb_g_disconnect(musb);
|
||||
dev_dbg(musb->controller, "HNP: back to %s\n",
|
||||
musb_dbg(musb, "HNP: back to %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
dev_dbg(musb->controller, "HNP: Disabling HR\n");
|
||||
musb_dbg(musb, "HNP: Disabling HR");
|
||||
if (hcd)
|
||||
hcd->self.is_b_host = 0;
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
@ -510,7 +524,7 @@ void musb_hnp_stop(struct musb *musb)
|
||||
/* REVISIT: Start SESSION_REQUEST here? */
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n",
|
||||
musb_dbg(musb, "HNP: Stopping in unknown state %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
|
||||
@ -541,8 +555,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
{
|
||||
irqreturn_t handled = IRQ_NONE;
|
||||
|
||||
dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl,
|
||||
int_usb);
|
||||
musb_dbg(musb, "<== DevCtl=%02x, int_usb=0x%x", devctl, int_usb);
|
||||
|
||||
/* in host mode, the peripheral may issue remote wakeup.
|
||||
* in peripheral mode, the host may resume the link.
|
||||
@ -550,7 +563,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
*/
|
||||
if (int_usb & MUSB_INTR_RESUME) {
|
||||
handled = IRQ_HANDLED;
|
||||
dev_dbg(musb->controller, "RESUME (%s)\n",
|
||||
musb_dbg(musb, "RESUME (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
if (devctl & MUSB_DEVCTL_HM) {
|
||||
@ -619,11 +632,11 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
|
||||
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
|
||||
&& (devctl & MUSB_DEVCTL_BDEVICE)) {
|
||||
dev_dbg(musb->controller, "SessReq while on B state\n");
|
||||
musb_dbg(musb, "SessReq while on B state");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n",
|
||||
musb_dbg(musb, "SESSION_REQUEST (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
/* IRQ arrives from ID pin sense or (later, if VBUS power
|
||||
@ -714,7 +727,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x\n",
|
||||
musb_dbg(musb, "SUSPEND (%s) devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state), devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
|
||||
@ -743,7 +756,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
musb->is_active = musb->g.b_hnp_enable;
|
||||
if (musb->is_active) {
|
||||
musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
|
||||
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
|
||||
musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
+ msecs_to_jiffies(
|
||||
OTG_TIME_B_ASE0_BRST));
|
||||
@ -760,7 +773,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
||||
dev_dbg(musb->controller, "REVISIT: SUSPEND as B_HOST\n");
|
||||
musb_dbg(musb, "REVISIT: SUSPEND as B_HOST");
|
||||
break;
|
||||
default:
|
||||
/* "should not happen" */
|
||||
@ -797,14 +810,14 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (int_usb & MUSB_INTR_SUSPEND) {
|
||||
dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n");
|
||||
musb_dbg(musb, "HNP: SUSPEND+CONNECT, now b_host");
|
||||
int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
goto b_host;
|
||||
} else
|
||||
dev_dbg(musb->controller, "CONNECT as b_peripheral???\n");
|
||||
musb_dbg(musb, "CONNECT as b_peripheral???");
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n");
|
||||
musb_dbg(musb, "HNP: CONNECT, now b_host");
|
||||
b_host:
|
||||
musb->xceiv->otg->state = OTG_STATE_B_HOST;
|
||||
if (musb->hcd)
|
||||
@ -823,12 +836,12 @@ b_host:
|
||||
|
||||
musb_host_poke_root_hub(musb);
|
||||
|
||||
dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n",
|
||||
musb_dbg(musb, "CONNECT (%s) devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state), devctl);
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_DISCONNECT) {
|
||||
dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n",
|
||||
musb_dbg(musb, "DISCONNECT (%s) as %s, devctl %02x",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
MUSB_MODE(musb), devctl);
|
||||
handled = IRQ_HANDLED;
|
||||
@ -891,7 +904,7 @@ b_host:
|
||||
if (is_host_active(musb))
|
||||
musb_recover_from_babble(musb);
|
||||
} else {
|
||||
dev_dbg(musb->controller, "BUS RESET as %s\n",
|
||||
musb_dbg(musb, "BUS RESET as %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
@ -899,7 +912,7 @@ b_host:
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
|
||||
/* never use invalid T(a_wait_bcon) */
|
||||
dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n",
|
||||
musb_dbg(musb, "HNP: in %s, %d msec timeout",
|
||||
usb_otg_state_string(musb->xceiv->otg->state),
|
||||
TA_WAIT_BCON(musb));
|
||||
mod_timer(&musb->otg_timer, jiffies
|
||||
@ -910,7 +923,7 @@ b_host:
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n",
|
||||
musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
|
||||
musb_g_reset(musb);
|
||||
@ -922,7 +935,7 @@ b_host:
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n",
|
||||
musb_dbg(musb, "Unhandled BUS RESET as %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
@ -1030,7 +1043,7 @@ void musb_start(struct musb *musb)
|
||||
u8 devctl = musb_readb(regs, MUSB_DEVCTL);
|
||||
u8 power;
|
||||
|
||||
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
|
||||
musb_dbg(musb, "<== devctl %02x", devctl);
|
||||
|
||||
musb_enable_interrupts(musb);
|
||||
musb_writeb(regs, MUSB_TESTMODE, 0);
|
||||
@ -1078,7 +1091,7 @@ void musb_stop(struct musb *musb)
|
||||
/* stop IRQs, timers, ... */
|
||||
musb_platform_disable(musb);
|
||||
musb_generic_disable(musb);
|
||||
dev_dbg(musb->controller, "HDRC disabled\n");
|
||||
musb_dbg(musb, "HDRC disabled");
|
||||
|
||||
/* FIXME
|
||||
* - mark host and/or peripheral drivers unusable/inactive
|
||||
@ -1391,7 +1404,7 @@ static int ep_config_from_hw(struct musb *musb)
|
||||
void __iomem *mbase = musb->mregs;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(musb->controller, "<== static silicon ep config\n");
|
||||
musb_dbg(musb, "<== static silicon ep config");
|
||||
|
||||
/* FIXME pick up ep0 maxpacket size */
|
||||
|
||||
@ -1532,8 +1545,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
hw_ep->tx_reinit = 1;
|
||||
|
||||
if (hw_ep->max_packet_sz_tx) {
|
||||
dev_dbg(musb->controller,
|
||||
"%s: hw_ep %d%s, %smax %d\n",
|
||||
musb_dbg(musb, "%s: hw_ep %d%s, %smax %d",
|
||||
musb_driver_name, i,
|
||||
hw_ep->is_shared_fifo ? "shared" : "tx",
|
||||
hw_ep->tx_double_buffered
|
||||
@ -1541,8 +1553,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
hw_ep->max_packet_sz_tx);
|
||||
}
|
||||
if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) {
|
||||
dev_dbg(musb->controller,
|
||||
"%s: hw_ep %d%s, %smax %d\n",
|
||||
musb_dbg(musb, "%s: hw_ep %d%s, %smax %d",
|
||||
musb_driver_name, i,
|
||||
"rx",
|
||||
hw_ep->rx_double_buffered
|
||||
@ -1550,7 +1561,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
hw_ep->max_packet_sz_rx);
|
||||
}
|
||||
if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx))
|
||||
dev_dbg(musb->controller, "hw_ep %d not configured\n", i);
|
||||
musb_dbg(musb, "hw_ep %d not configured", i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1577,9 +1588,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n",
|
||||
is_host_active(musb) ? "host" : "peripheral",
|
||||
musb->int_usb, musb->int_tx, musb->int_rx);
|
||||
trace_musb_isr(musb);
|
||||
|
||||
/**
|
||||
* According to Mentor Graphics' documentation, flowchart on page 98,
|
||||
@ -1976,7 +1985,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
* Fail when the board needs a feature that's not enabled.
|
||||
*/
|
||||
if (!plat) {
|
||||
dev_dbg(dev, "no platform_data?\n");
|
||||
dev_err(dev, "no platform_data?\n");
|
||||
status = -ENODEV;
|
||||
goto fail0;
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "cppi_dma.h"
|
||||
#include "musb_core.h"
|
||||
#include "musb_trace.h"
|
||||
|
||||
#define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
|
||||
|
||||
@ -22,26 +24,6 @@
|
||||
#define USB_CTRL_AUTOREQ 0xd0
|
||||
#define USB_TDOWN 0xd8
|
||||
|
||||
struct cppi41_dma_channel {
|
||||
struct dma_channel channel;
|
||||
struct cppi41_dma_controller *controller;
|
||||
struct musb_hw_ep *hw_ep;
|
||||
struct dma_chan *dc;
|
||||
dma_cookie_t cookie;
|
||||
u8 port_num;
|
||||
u8 is_tx;
|
||||
u8 is_allocated;
|
||||
u8 usb_toggle;
|
||||
|
||||
dma_addr_t buf_addr;
|
||||
u32 total_len;
|
||||
u32 prog_len;
|
||||
u32 transferred;
|
||||
u32 packet_sz;
|
||||
struct list_head tx_check;
|
||||
int tx_zlp;
|
||||
};
|
||||
|
||||
#define MUSB_DMA_NUM_CHANNELS 15
|
||||
|
||||
struct cppi41_dma_controller {
|
||||
@ -96,8 +78,8 @@ static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
|
||||
if (!toggle && toggle == cppi41_channel->usb_toggle) {
|
||||
csr |= MUSB_RXCSR_H_DATATOGGLE | MUSB_RXCSR_H_WR_DATATOGGLE;
|
||||
musb_writew(cppi41_channel->hw_ep->regs, MUSB_RXCSR, csr);
|
||||
dev_dbg(cppi41_channel->controller->musb->controller,
|
||||
"Restoring DATA1 toggle.\n");
|
||||
musb_dbg(cppi41_channel->controller->musb,
|
||||
"Restoring DATA1 toggle.");
|
||||
}
|
||||
|
||||
cppi41_channel->usb_toggle = toggle;
|
||||
@ -145,6 +127,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
|
||||
csr = MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY;
|
||||
musb_writew(epio, MUSB_TXCSR, csr);
|
||||
}
|
||||
|
||||
trace_musb_cppi41_done(cppi41_channel);
|
||||
musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx);
|
||||
} else {
|
||||
/* next iteration, reload */
|
||||
@ -173,6 +157,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
|
||||
dma_desc->callback = cppi41_dma_callback;
|
||||
dma_desc->callback_param = &cppi41_channel->channel;
|
||||
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
|
||||
trace_musb_cppi41_cont(cppi41_channel);
|
||||
dma_async_issue_pending(dc);
|
||||
|
||||
if (!cppi41_channel->is_tx) {
|
||||
@ -240,10 +225,7 @@ static void cppi41_dma_callback(void *private_data)
|
||||
transferred = cppi41_channel->prog_len - txstate.residue;
|
||||
cppi41_channel->transferred += transferred;
|
||||
|
||||
dev_dbg(musb->controller, "DMA transfer done on hw_ep=%d bytes=%d/%d\n",
|
||||
hw_ep->epnum, cppi41_channel->transferred,
|
||||
cppi41_channel->total_len);
|
||||
|
||||
trace_musb_cppi41_gb(cppi41_channel);
|
||||
update_rx_toggle(cppi41_channel);
|
||||
|
||||
if (cppi41_channel->transferred == cppi41_channel->total_len ||
|
||||
@ -374,12 +356,6 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
|
||||
struct musb *musb = cppi41_channel->controller->musb;
|
||||
unsigned use_gen_rndis = 0;
|
||||
|
||||
dev_dbg(musb->controller,
|
||||
"configure ep%d/%x packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n",
|
||||
cppi41_channel->port_num, RNDIS_REG(cppi41_channel->port_num),
|
||||
packet_sz, mode, (unsigned long long) dma_addr,
|
||||
len, cppi41_channel->is_tx);
|
||||
|
||||
cppi41_channel->buf_addr = dma_addr;
|
||||
cppi41_channel->total_len = len;
|
||||
cppi41_channel->transferred = 0;
|
||||
@ -431,6 +407,8 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
|
||||
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
|
||||
cppi41_channel->channel.rx_packet_done = false;
|
||||
|
||||
trace_musb_cppi41_config(cppi41_channel);
|
||||
|
||||
save_rx_toggle(cppi41_channel);
|
||||
dma_async_issue_pending(dc);
|
||||
return true;
|
||||
@ -461,6 +439,7 @@ static struct dma_channel *cppi41_dma_channel_allocate(struct dma_controller *c,
|
||||
cppi41_channel->hw_ep = hw_ep;
|
||||
cppi41_channel->is_allocated = 1;
|
||||
|
||||
trace_musb_cppi41_alloc(cppi41_channel);
|
||||
return &cppi41_channel->channel;
|
||||
}
|
||||
|
||||
@ -468,6 +447,7 @@ static void cppi41_dma_channel_release(struct dma_channel *channel)
|
||||
{
|
||||
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
|
||||
|
||||
trace_musb_cppi41_free(cppi41_channel);
|
||||
if (cppi41_channel->is_allocated) {
|
||||
cppi41_channel->is_allocated = 0;
|
||||
channel->status = MUSB_DMA_STATUS_FREE;
|
||||
@ -537,8 +517,7 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
|
||||
u16 csr;
|
||||
|
||||
is_tx = cppi41_channel->is_tx;
|
||||
dev_dbg(musb->controller, "abort channel=%d, is_tx=%d\n",
|
||||
cppi41_channel->port_num, is_tx);
|
||||
trace_musb_cppi41_abort(cppi41_channel);
|
||||
|
||||
if (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)
|
||||
return 0;
|
||||
|
@ -42,6 +42,8 @@
|
||||
#define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args)
|
||||
#define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args)
|
||||
|
||||
void musb_dbg(struct musb *musb, const char *fmt, ...);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int musb_init_debugfs(struct musb *musb);
|
||||
void musb_exit_debugfs(struct musb *musb);
|
||||
|
@ -51,30 +51,6 @@
|
||||
|
||||
static const struct of_device_id musb_dsps_of_match[];
|
||||
|
||||
/**
|
||||
* avoid using musb_readx()/musb_writex() as glue layer should not be
|
||||
* dependent on musb core layer symbols.
|
||||
*/
|
||||
static inline u8 dsps_readb(const void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readb(addr + offset);
|
||||
}
|
||||
|
||||
static inline u32 dsps_readl(const void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readl(addr + offset);
|
||||
}
|
||||
|
||||
static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
|
||||
{
|
||||
__raw_writeb(data, addr + offset);
|
||||
}
|
||||
|
||||
static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
|
||||
{
|
||||
__raw_writel(data, addr + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* DSPS musb wrapper register offset.
|
||||
* FIXME: This should be expanded to have all the wrapper registers from TI DSPS
|
||||
@ -223,8 +199,8 @@ static void dsps_musb_enable(struct musb *musb)
|
||||
((musb->epmask & wrp->rxep_mask) << wrp->rxep_shift);
|
||||
coremask = (wrp->usb_bitmap & ~MUSB_INTR_SOF);
|
||||
|
||||
dsps_writel(reg_base, wrp->epintr_set, epmask);
|
||||
dsps_writel(reg_base, wrp->coreintr_set, coremask);
|
||||
musb_writel(reg_base, wrp->epintr_set, epmask);
|
||||
musb_writel(reg_base, wrp->coreintr_set, coremask);
|
||||
/* start polling for ID change in dual-role idle mode */
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
|
||||
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
@ -244,10 +220,10 @@ static void dsps_musb_disable(struct musb *musb)
|
||||
const struct dsps_musb_wrapper *wrp = glue->wrp;
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
|
||||
dsps_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
|
||||
dsps_writel(reg_base, wrp->epintr_clear,
|
||||
musb_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
|
||||
musb_writel(reg_base, wrp->epintr_clear,
|
||||
wrp->txep_bitmap | wrp->rxep_bitmap);
|
||||
dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
}
|
||||
|
||||
static void otg_timer(unsigned long _musb)
|
||||
@ -265,14 +241,14 @@ static void otg_timer(unsigned long _musb)
|
||||
* We poll because DSPS IP's won't expose several OTG-critical
|
||||
* status change events (from the transceiver) otherwise.
|
||||
*/
|
||||
devctl = dsps_readb(mregs, MUSB_DEVCTL);
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
skip_session = 1;
|
||||
/* fall */
|
||||
|
||||
@ -286,13 +262,13 @@ static void otg_timer(unsigned long _musb)
|
||||
MUSB_HST_MODE(musb);
|
||||
}
|
||||
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
|
||||
dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
|
||||
musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
msecs_to_jiffies(wrp->poll_timeout));
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
|
||||
dsps_writel(musb->ctrl_base, wrp->coreintr_set,
|
||||
musb_writel(musb->ctrl_base, wrp->coreintr_set,
|
||||
MUSB_INTR_VBUSERROR << wrp->usb_shift);
|
||||
break;
|
||||
default:
|
||||
@ -315,29 +291,29 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
/* Get endpoint interrupts */
|
||||
epintr = dsps_readl(reg_base, wrp->epintr_status);
|
||||
epintr = musb_readl(reg_base, wrp->epintr_status);
|
||||
musb->int_rx = (epintr & wrp->rxep_bitmap) >> wrp->rxep_shift;
|
||||
musb->int_tx = (epintr & wrp->txep_bitmap) >> wrp->txep_shift;
|
||||
|
||||
if (epintr)
|
||||
dsps_writel(reg_base, wrp->epintr_status, epintr);
|
||||
musb_writel(reg_base, wrp->epintr_status, epintr);
|
||||
|
||||
/* Get usb core interrupts */
|
||||
usbintr = dsps_readl(reg_base, wrp->coreintr_status);
|
||||
usbintr = musb_readl(reg_base, wrp->coreintr_status);
|
||||
if (!usbintr && !epintr)
|
||||
goto out;
|
||||
|
||||
musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift;
|
||||
if (usbintr)
|
||||
dsps_writel(reg_base, wrp->coreintr_status, usbintr);
|
||||
musb_writel(reg_base, wrp->coreintr_status, usbintr);
|
||||
|
||||
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
|
||||
usbintr, epintr);
|
||||
|
||||
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
|
||||
int drvvbus = dsps_readl(reg_base, wrp->status);
|
||||
int drvvbus = musb_readl(reg_base, wrp->status);
|
||||
void __iomem *mregs = musb->mregs;
|
||||
u8 devctl = dsps_readb(mregs, MUSB_DEVCTL);
|
||||
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
int err;
|
||||
|
||||
err = musb->int_usb & MUSB_INTR_VBUSERROR;
|
||||
@ -442,7 +418,7 @@ static int dsps_musb_init(struct musb *musb)
|
||||
musb->phy = devm_phy_get(dev->parent, "usb2-phy");
|
||||
|
||||
/* Returns zero if e.g. not clocked */
|
||||
rev = dsps_readl(reg_base, wrp->revision);
|
||||
rev = musb_readl(reg_base, wrp->revision);
|
||||
if (!rev)
|
||||
return -ENODEV;
|
||||
|
||||
@ -463,14 +439,14 @@ static int dsps_musb_init(struct musb *musb)
|
||||
setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
|
||||
|
||||
/* Reset the musb */
|
||||
dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
|
||||
musb_writel(reg_base, wrp->control, (1 << wrp->reset));
|
||||
|
||||
musb->isr = dsps_interrupt;
|
||||
|
||||
/* reset the otgdisable bit, needed for host mode to work */
|
||||
val = dsps_readl(reg_base, wrp->phy_utmi);
|
||||
val = musb_readl(reg_base, wrp->phy_utmi);
|
||||
val &= ~(1 << wrp->otg_disable);
|
||||
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
|
||||
musb_writel(musb->ctrl_base, wrp->phy_utmi, val);
|
||||
|
||||
/*
|
||||
* Check whether the dsps version has babble control enabled.
|
||||
@ -478,11 +454,11 @@ static int dsps_musb_init(struct musb *musb)
|
||||
* If MUSB_BABBLE_CTL returns 0x4 then we have the babble control
|
||||
* logic enabled.
|
||||
*/
|
||||
val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
val = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
if (val & MUSB_BABBLE_RCV_DISABLE) {
|
||||
glue->sw_babble_enabled = true;
|
||||
val |= MUSB_BABBLE_SW_SESSION_CTRL;
|
||||
dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
|
||||
musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
|
||||
}
|
||||
|
||||
return dsps_musb_dbg_init(musb, glue);
|
||||
@ -510,7 +486,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
|
||||
void __iomem *ctrl_base = musb->ctrl_base;
|
||||
u32 reg;
|
||||
|
||||
reg = dsps_readl(ctrl_base, wrp->mode);
|
||||
reg = musb_readl(ctrl_base, wrp->mode);
|
||||
|
||||
switch (mode) {
|
||||
case MUSB_HOST:
|
||||
@ -523,8 +499,8 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
|
||||
*/
|
||||
reg |= (1 << wrp->iddig_mux);
|
||||
|
||||
dsps_writel(ctrl_base, wrp->mode, reg);
|
||||
dsps_writel(ctrl_base, wrp->phy_utmi, 0x02);
|
||||
musb_writel(ctrl_base, wrp->mode, reg);
|
||||
musb_writel(ctrl_base, wrp->phy_utmi, 0x02);
|
||||
break;
|
||||
case MUSB_PERIPHERAL:
|
||||
reg |= (1 << wrp->iddig);
|
||||
@ -536,10 +512,10 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
|
||||
*/
|
||||
reg |= (1 << wrp->iddig_mux);
|
||||
|
||||
dsps_writel(ctrl_base, wrp->mode, reg);
|
||||
musb_writel(ctrl_base, wrp->mode, reg);
|
||||
break;
|
||||
case MUSB_OTG:
|
||||
dsps_writel(ctrl_base, wrp->phy_utmi, 0x02);
|
||||
musb_writel(ctrl_base, wrp->phy_utmi, 0x02);
|
||||
break;
|
||||
default:
|
||||
dev_err(glue->dev, "unsupported mode %d\n", mode);
|
||||
@ -554,7 +530,7 @@ static bool dsps_sw_babble_control(struct musb *musb)
|
||||
u8 babble_ctl;
|
||||
bool session_restart = false;
|
||||
|
||||
babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
babble_ctl = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n",
|
||||
babble_ctl);
|
||||
/*
|
||||
@ -571,14 +547,14 @@ static bool dsps_sw_babble_control(struct musb *musb)
|
||||
* babble is due to noise, then set transmit idle (d7 bit)
|
||||
* to resume normal operation
|
||||
*/
|
||||
babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
babble_ctl = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE;
|
||||
dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl);
|
||||
musb_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl);
|
||||
|
||||
/* wait till line monitor flag cleared */
|
||||
dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n");
|
||||
do {
|
||||
babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
babble_ctl = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
udelay(1);
|
||||
} while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--);
|
||||
|
||||
@ -896,13 +872,13 @@ static int dsps_suspend(struct device *dev)
|
||||
return 0;
|
||||
|
||||
mbase = musb->ctrl_base;
|
||||
glue->context.control = dsps_readl(mbase, wrp->control);
|
||||
glue->context.epintr = dsps_readl(mbase, wrp->epintr_set);
|
||||
glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set);
|
||||
glue->context.phy_utmi = dsps_readl(mbase, wrp->phy_utmi);
|
||||
glue->context.mode = dsps_readl(mbase, wrp->mode);
|
||||
glue->context.tx_mode = dsps_readl(mbase, wrp->tx_mode);
|
||||
glue->context.rx_mode = dsps_readl(mbase, wrp->rx_mode);
|
||||
glue->context.control = musb_readl(mbase, wrp->control);
|
||||
glue->context.epintr = musb_readl(mbase, wrp->epintr_set);
|
||||
glue->context.coreintr = musb_readl(mbase, wrp->coreintr_set);
|
||||
glue->context.phy_utmi = musb_readl(mbase, wrp->phy_utmi);
|
||||
glue->context.mode = musb_readl(mbase, wrp->mode);
|
||||
glue->context.tx_mode = musb_readl(mbase, wrp->tx_mode);
|
||||
glue->context.rx_mode = musb_readl(mbase, wrp->rx_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -918,13 +894,13 @@ static int dsps_resume(struct device *dev)
|
||||
return 0;
|
||||
|
||||
mbase = musb->ctrl_base;
|
||||
dsps_writel(mbase, wrp->control, glue->context.control);
|
||||
dsps_writel(mbase, wrp->epintr_set, glue->context.epintr);
|
||||
dsps_writel(mbase, wrp->coreintr_set, glue->context.coreintr);
|
||||
dsps_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi);
|
||||
dsps_writel(mbase, wrp->mode, glue->context.mode);
|
||||
dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
|
||||
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
|
||||
musb_writel(mbase, wrp->control, glue->context.control);
|
||||
musb_writel(mbase, wrp->epintr_set, glue->context.epintr);
|
||||
musb_writel(mbase, wrp->coreintr_set, glue->context.coreintr);
|
||||
musb_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi);
|
||||
musb_writel(mbase, wrp->mode, glue->context.mode);
|
||||
musb_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
|
||||
musb_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
|
||||
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "musb_trace.h"
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
@ -167,15 +168,7 @@ __acquires(ep->musb->lock)
|
||||
if (!dma_mapping_error(&musb->g.dev, request->dma))
|
||||
unmap_dma_buffer(req, musb);
|
||||
|
||||
if (request->status == 0)
|
||||
dev_dbg(musb->controller, "%s done request %p, %d/%d\n",
|
||||
ep->end_point.name, request,
|
||||
req->request.actual, req->request.length);
|
||||
else
|
||||
dev_dbg(musb->controller, "%s request %p, %d/%d fault %d\n",
|
||||
ep->end_point.name, request,
|
||||
req->request.actual, req->request.length,
|
||||
request->status);
|
||||
trace_musb_req_gb(req);
|
||||
usb_gadget_giveback_request(&req->ep->end_point, &req->request);
|
||||
spin_lock(&musb->lock);
|
||||
ep->busy = busy;
|
||||
@ -217,8 +210,7 @@ static void nuke(struct musb_ep *ep, const int status)
|
||||
}
|
||||
|
||||
value = c->channel_abort(ep->dma);
|
||||
dev_dbg(musb->controller, "%s: abort DMA --> %d\n",
|
||||
ep->name, value);
|
||||
musb_dbg(musb, "%s: abort DMA --> %d", ep->name, value);
|
||||
c->channel_release(ep->dma);
|
||||
ep->dma = NULL;
|
||||
}
|
||||
@ -266,14 +258,14 @@ static void txstate(struct musb *musb, struct musb_request *req)
|
||||
|
||||
/* Check if EP is disabled */
|
||||
if (!musb_ep->desc) {
|
||||
dev_dbg(musb->controller, "ep:%s disabled - ignore request\n",
|
||||
musb_dbg(musb, "ep:%s disabled - ignore request",
|
||||
musb_ep->end_point.name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we shouldn't get here while DMA is active ... but we do ... */
|
||||
if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
|
||||
dev_dbg(musb->controller, "dma pending...\n");
|
||||
musb_dbg(musb, "dma pending...");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -285,18 +277,18 @@ static void txstate(struct musb *musb, struct musb_request *req)
|
||||
(int)(request->length - request->actual));
|
||||
|
||||
if (csr & MUSB_TXCSR_TXPKTRDY) {
|
||||
dev_dbg(musb->controller, "%s old packet still ready , txcsr %03x\n",
|
||||
musb_dbg(musb, "%s old packet still ready , txcsr %03x",
|
||||
musb_ep->end_point.name, csr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (csr & MUSB_TXCSR_P_SENDSTALL) {
|
||||
dev_dbg(musb->controller, "%s stalling, txcsr %03x\n",
|
||||
musb_dbg(musb, "%s stalling, txcsr %03x",
|
||||
musb_ep->end_point.name, csr);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n",
|
||||
musb_dbg(musb, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x",
|
||||
epnum, musb_ep->packet_sz, fifo_count,
|
||||
csr);
|
||||
|
||||
@ -424,7 +416,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
|
||||
}
|
||||
|
||||
/* host may already have the data when this message shows... */
|
||||
dev_dbg(musb->controller, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n",
|
||||
musb_dbg(musb, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d",
|
||||
musb_ep->end_point.name, use_dma ? "dma" : "pio",
|
||||
request->actual, request->length,
|
||||
musb_readw(epio, MUSB_TXCSR),
|
||||
@ -450,8 +442,9 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
||||
req = next_request(musb_ep);
|
||||
request = &req->request;
|
||||
|
||||
trace_musb_req_tx(req);
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
dev_dbg(musb->controller, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr);
|
||||
musb_dbg(musb, "<== %s, txcsr %04x", musb_ep->end_point.name, csr);
|
||||
|
||||
dma = is_dma_capable() ? musb_ep->dma : NULL;
|
||||
|
||||
@ -480,7 +473,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
||||
* SHOULD NOT HAPPEN... has with CPPI though, after
|
||||
* changing SENDSTALL (and other cases); harmless?
|
||||
*/
|
||||
dev_dbg(musb->controller, "%s dma still busy?\n", musb_ep->end_point.name);
|
||||
musb_dbg(musb, "%s dma still busy?", musb_ep->end_point.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -497,7 +490,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
||||
/* Ensure writebuffer is empty. */
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
request->actual += musb_ep->dma->actual_len;
|
||||
dev_dbg(musb->controller, "TXCSR%d %04x, DMA off, len %zu, req %p\n",
|
||||
musb_dbg(musb, "TXCSR%d %04x, DMA off, len %zu, req %p",
|
||||
epnum, csr, musb_ep->dma->actual_len, request);
|
||||
}
|
||||
|
||||
@ -524,7 +517,6 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
||||
if (csr & MUSB_TXCSR_TXPKTRDY)
|
||||
return;
|
||||
|
||||
dev_dbg(musb->controller, "sending zero pkt\n");
|
||||
musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE
|
||||
| MUSB_TXCSR_TXPKTRDY);
|
||||
request->zero = 0;
|
||||
@ -543,7 +535,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
|
||||
musb_ep_select(mbase, epnum);
|
||||
req = musb_ep->desc ? next_request(musb_ep) : NULL;
|
||||
if (!req) {
|
||||
dev_dbg(musb->controller, "%s idle now\n",
|
||||
musb_dbg(musb, "%s idle now",
|
||||
musb_ep->end_point.name);
|
||||
return;
|
||||
}
|
||||
@ -579,19 +571,19 @@ static void rxstate(struct musb *musb, struct musb_request *req)
|
||||
|
||||
/* Check if EP is disabled */
|
||||
if (!musb_ep->desc) {
|
||||
dev_dbg(musb->controller, "ep:%s disabled - ignore request\n",
|
||||
musb_dbg(musb, "ep:%s disabled - ignore request",
|
||||
musb_ep->end_point.name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We shouldn't get here while DMA is active, but we do... */
|
||||
if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) {
|
||||
dev_dbg(musb->controller, "DMA pending...\n");
|
||||
musb_dbg(musb, "DMA pending...");
|
||||
return;
|
||||
}
|
||||
|
||||
if (csr & MUSB_RXCSR_P_SENDSTALL) {
|
||||
dev_dbg(musb->controller, "%s stalling, RXCSR %04x\n",
|
||||
musb_dbg(musb, "%s stalling, RXCSR %04x",
|
||||
musb_ep->end_point.name, csr);
|
||||
return;
|
||||
}
|
||||
@ -766,7 +758,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
|
||||
}
|
||||
|
||||
len = request->length - request->actual;
|
||||
dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n",
|
||||
musb_dbg(musb, "%s OUT/RX pio fifo %d/%d, maxpacket %d",
|
||||
musb_ep->end_point.name,
|
||||
fifo_count, len,
|
||||
musb_ep->packet_sz);
|
||||
@ -849,12 +841,13 @@ void musb_g_rx(struct musb *musb, u8 epnum)
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
trace_musb_req_rx(req);
|
||||
request = &req->request;
|
||||
|
||||
csr = musb_readw(epio, MUSB_RXCSR);
|
||||
dma = is_dma_capable() ? musb_ep->dma : NULL;
|
||||
|
||||
dev_dbg(musb->controller, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name,
|
||||
musb_dbg(musb, "<== %s, rxcsr %04x%s %p", musb_ep->end_point.name,
|
||||
csr, dma ? " (dma)" : "", request);
|
||||
|
||||
if (csr & MUSB_RXCSR_P_SENTSTALL) {
|
||||
@ -869,18 +862,18 @@ void musb_g_rx(struct musb *musb, u8 epnum)
|
||||
csr &= ~MUSB_RXCSR_P_OVERRUN;
|
||||
musb_writew(epio, MUSB_RXCSR, csr);
|
||||
|
||||
dev_dbg(musb->controller, "%s iso overrun on %p\n", musb_ep->name, request);
|
||||
musb_dbg(musb, "%s iso overrun on %p", musb_ep->name, request);
|
||||
if (request->status == -EINPROGRESS)
|
||||
request->status = -EOVERFLOW;
|
||||
}
|
||||
if (csr & MUSB_RXCSR_INCOMPRX) {
|
||||
/* REVISIT not necessarily an error */
|
||||
dev_dbg(musb->controller, "%s, incomprx\n", musb_ep->end_point.name);
|
||||
musb_dbg(musb, "%s, incomprx", musb_ep->end_point.name);
|
||||
}
|
||||
|
||||
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
|
||||
/* "should not happen"; likely RXPKTRDY pending for DMA */
|
||||
dev_dbg(musb->controller, "%s busy, csr %04x\n",
|
||||
musb_dbg(musb, "%s busy, csr %04x",
|
||||
musb_ep->end_point.name, csr);
|
||||
return;
|
||||
}
|
||||
@ -894,11 +887,6 @@ void musb_g_rx(struct musb *musb, u8 epnum)
|
||||
|
||||
request->actual += musb_ep->dma->actual_len;
|
||||
|
||||
dev_dbg(musb->controller, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n",
|
||||
epnum, csr,
|
||||
musb_readw(epio, MUSB_RXCSR),
|
||||
musb_ep->dma->actual_len, request);
|
||||
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \
|
||||
defined(CONFIG_USB_UX500_DMA)
|
||||
/* Autoclear doesn't clear RxPktRdy for short packets */
|
||||
@ -996,7 +984,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
|
||||
ok = musb->hb_iso_rx;
|
||||
|
||||
if (!ok) {
|
||||
dev_dbg(musb->controller, "no support for high bandwidth ISO\n");
|
||||
musb_dbg(musb, "no support for high bandwidth ISO");
|
||||
goto fail;
|
||||
}
|
||||
musb_ep->hb_mult = (tmp >> 11) & 3;
|
||||
@ -1019,7 +1007,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
|
||||
goto fail;
|
||||
|
||||
if (tmp > hw_ep->max_packet_sz_tx) {
|
||||
dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n");
|
||||
musb_dbg(musb, "packet size beyond hardware FIFO size");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -1062,7 +1050,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
|
||||
goto fail;
|
||||
|
||||
if (tmp > hw_ep->max_packet_sz_rx) {
|
||||
dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n");
|
||||
musb_dbg(musb, "packet size beyond hardware FIFO size");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -1174,7 +1162,7 @@ static int musb_gadget_disable(struct usb_ep *ep)
|
||||
|
||||
spin_unlock_irqrestore(&(musb->lock), flags);
|
||||
|
||||
dev_dbg(musb->controller, "%s\n", musb_ep->end_point.name);
|
||||
musb_dbg(musb, "%s", musb_ep->end_point.name);
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -1186,19 +1174,17 @@ static int musb_gadget_disable(struct usb_ep *ep)
|
||||
struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
{
|
||||
struct musb_ep *musb_ep = to_musb_ep(ep);
|
||||
struct musb *musb = musb_ep->musb;
|
||||
struct musb_request *request = NULL;
|
||||
|
||||
request = kzalloc(sizeof *request, gfp_flags);
|
||||
if (!request) {
|
||||
dev_dbg(musb->controller, "not enough memory\n");
|
||||
if (!request)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
request->request.dma = DMA_ADDR_INVALID;
|
||||
request->epnum = musb_ep->current_epnum;
|
||||
request->ep = musb_ep;
|
||||
|
||||
trace_musb_req_alloc(request);
|
||||
return &request->request;
|
||||
}
|
||||
|
||||
@ -1208,7 +1194,10 @@ struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
*/
|
||||
void musb_free_request(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(to_musb_request(req));
|
||||
struct musb_request *request = to_musb_request(req);
|
||||
|
||||
trace_musb_req_free(request);
|
||||
kfree(request);
|
||||
}
|
||||
|
||||
static LIST_HEAD(buffers);
|
||||
@ -1225,10 +1214,7 @@ struct free_record {
|
||||
*/
|
||||
void musb_ep_restart(struct musb *musb, struct musb_request *req)
|
||||
{
|
||||
dev_dbg(musb->controller, "<== %s request %p len %u on hw_ep%d\n",
|
||||
req->tx ? "TX/IN" : "RX/OUT",
|
||||
&req->request, req->request.length, req->epnum);
|
||||
|
||||
trace_musb_req_start(req);
|
||||
musb_ep_select(musb->mregs, req->epnum);
|
||||
if (req->tx)
|
||||
txstate(musb, req);
|
||||
@ -1259,7 +1245,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
if (request->ep != musb_ep)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(musb->controller, "<== to %s request=%p\n", ep->name, req);
|
||||
trace_musb_req_enq(request);
|
||||
|
||||
/* request is mine now... */
|
||||
request->request.actual = 0;
|
||||
@ -1273,7 +1259,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
|
||||
/* don't queue if the ep is down */
|
||||
if (!musb_ep->desc) {
|
||||
dev_dbg(musb->controller, "req %p queued to %s while ep %s\n",
|
||||
musb_dbg(musb, "req %p queued to %s while ep %s",
|
||||
req, ep->name, "disabled");
|
||||
status = -ESHUTDOWN;
|
||||
unmap_dma_buffer(request, musb);
|
||||
@ -1301,9 +1287,11 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)
|
||||
int status = 0;
|
||||
struct musb *musb = musb_ep->musb;
|
||||
|
||||
if (!ep || !request || to_musb_request(request)->ep != musb_ep)
|
||||
if (!ep || !request || req->ep != musb_ep)
|
||||
return -EINVAL;
|
||||
|
||||
trace_musb_req_deq(req);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &musb_ep->req_list, list) {
|
||||
@ -1311,7 +1299,8 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)
|
||||
break;
|
||||
}
|
||||
if (r != req) {
|
||||
dev_dbg(musb->controller, "request %p not queued to %s\n", request, ep->name);
|
||||
dev_err(musb->controller, "request %p not queued to %s\n",
|
||||
request, ep->name);
|
||||
status = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
@ -1377,7 +1366,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value)
|
||||
request = next_request(musb_ep);
|
||||
if (value) {
|
||||
if (request) {
|
||||
dev_dbg(musb->controller, "request in progress, cannot halt %s\n",
|
||||
musb_dbg(musb, "request in progress, cannot halt %s",
|
||||
ep->name);
|
||||
status = -EAGAIN;
|
||||
goto done;
|
||||
@ -1386,7 +1375,8 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value)
|
||||
if (musb_ep->is_in) {
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
if (csr & MUSB_TXCSR_FIFONOTEMPTY) {
|
||||
dev_dbg(musb->controller, "FIFO busy, cannot halt %s\n", ep->name);
|
||||
musb_dbg(musb, "FIFO busy, cannot halt %s",
|
||||
ep->name);
|
||||
status = -EAGAIN;
|
||||
goto done;
|
||||
}
|
||||
@ -1395,7 +1385,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value)
|
||||
musb_ep->wedged = 0;
|
||||
|
||||
/* set/clear the stall and toggle bits */
|
||||
dev_dbg(musb->controller, "%s: %s stall\n", ep->name, value ? "set" : "clear");
|
||||
musb_dbg(musb, "%s: %s stall", ep->name, value ? "set" : "clear");
|
||||
if (musb_ep->is_in) {
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
csr |= MUSB_TXCSR_P_WZC_BITS
|
||||
@ -1422,7 +1412,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value)
|
||||
|
||||
/* maybe start the first request in the queue */
|
||||
if (!musb_ep->busy && !value && request) {
|
||||
dev_dbg(musb->controller, "restarting the request\n");
|
||||
musb_dbg(musb, "restarting the request");
|
||||
musb_ep_restart(musb, request);
|
||||
}
|
||||
|
||||
@ -1558,7 +1548,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
|
||||
case OTG_STATE_B_IDLE:
|
||||
/* Start SRP ... OTG not required. */
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
dev_dbg(musb->controller, "Sending SRP: devctl: %02x\n", devctl);
|
||||
musb_dbg(musb, "Sending SRP: devctl: %02x", devctl);
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(mregs, MUSB_DEVCTL, devctl);
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
@ -1586,7 +1576,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
|
||||
status = 0;
|
||||
goto done;
|
||||
default:
|
||||
dev_dbg(musb->controller, "Unhandled wake: %s\n",
|
||||
musb_dbg(musb, "Unhandled wake: %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
goto done;
|
||||
}
|
||||
@ -1596,7 +1586,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
|
||||
power = musb_readb(mregs, MUSB_POWER);
|
||||
power |= MUSB_POWER_RESUME;
|
||||
musb_writeb(mregs, MUSB_POWER, power);
|
||||
dev_dbg(musb->controller, "issue wakeup\n");
|
||||
musb_dbg(musb, "issue wakeup");
|
||||
|
||||
/* FIXME do this next chunk in a timer callback, no udelay */
|
||||
mdelay(2);
|
||||
@ -1628,7 +1618,7 @@ static void musb_pullup(struct musb *musb, int is_on)
|
||||
|
||||
/* FIXME if on, HdrcStart; if off, HdrcStop */
|
||||
|
||||
dev_dbg(musb->controller, "gadget D+ pullup %s\n",
|
||||
musb_dbg(musb, "gadget D+ pullup %s",
|
||||
is_on ? "on" : "off");
|
||||
musb_writeb(musb->mregs, MUSB_POWER, power);
|
||||
}
|
||||
@ -1636,7 +1626,7 @@ static void musb_pullup(struct musb *musb, int is_on)
|
||||
#if 0
|
||||
static int musb_gadget_vbus_session(struct usb_gadget *gadget, int is_active)
|
||||
{
|
||||
dev_dbg(musb->controller, "<= %s =>\n", __func__);
|
||||
musb_dbg(musb, "<= %s =>\n", __func__);
|
||||
|
||||
/*
|
||||
* FIXME iff driver's softconnect flag is set (as it is during probe,
|
||||
@ -2011,7 +2001,7 @@ void musb_g_suspend(struct musb *musb)
|
||||
u8 devctl;
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
dev_dbg(musb->controller, "devctl %02x\n", devctl);
|
||||
musb_dbg(musb, "musb_g_suspend: devctl %02x", devctl);
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_B_IDLE:
|
||||
@ -2030,7 +2020,7 @@ void musb_g_suspend(struct musb *musb)
|
||||
/* REVISIT if B_HOST, clear DEVCTL.HOSTREQ;
|
||||
* A_PERIPHERAL may need care too
|
||||
*/
|
||||
WARNING("unhandled SUSPEND transition (%s)\n",
|
||||
WARNING("unhandled SUSPEND transition (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
@ -2047,7 +2037,7 @@ void musb_g_disconnect(struct musb *musb)
|
||||
void __iomem *mregs = musb->mregs;
|
||||
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
|
||||
dev_dbg(musb->controller, "devctl %02x\n", devctl);
|
||||
musb_dbg(musb, "musb_g_disconnect: devctl %02x", devctl);
|
||||
|
||||
/* clear HR */
|
||||
musb_writeb(mregs, MUSB_DEVCTL, devctl & MUSB_DEVCTL_SESSION);
|
||||
@ -2064,7 +2054,7 @@ void musb_g_disconnect(struct musb *musb)
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
default:
|
||||
dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n",
|
||||
musb_dbg(musb, "Unhandled disconnect %s, setting a_idle",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
@ -2094,7 +2084,7 @@ __acquires(musb->lock)
|
||||
u8 devctl = musb_readb(mbase, MUSB_DEVCTL);
|
||||
u8 power;
|
||||
|
||||
dev_dbg(musb->controller, "<== %s driver '%s'\n",
|
||||
musb_dbg(musb, "<== %s driver '%s'",
|
||||
(devctl & MUSB_DEVCTL_BDEVICE)
|
||||
? "B-Device" : "A-Device",
|
||||
musb->gadget_driver
|
||||
|
@ -206,7 +206,7 @@ static inline void musb_try_b_hnp_enable(struct musb *musb)
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u8 devctl;
|
||||
|
||||
dev_dbg(musb->controller, "HNP: Setting HR\n");
|
||||
musb_dbg(musb, "HNP: Setting HR");
|
||||
devctl = musb_readb(mbase, MUSB_DEVCTL);
|
||||
musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR);
|
||||
}
|
||||
@ -303,7 +303,7 @@ __acquires(musb->lock)
|
||||
/* Maybe start the first request in the queue */
|
||||
request = next_request(musb_ep);
|
||||
if (!musb_ep->busy && request) {
|
||||
dev_dbg(musb->controller, "restarting the request\n");
|
||||
musb_dbg(musb, "restarting the request");
|
||||
musb_ep_restart(musb, request);
|
||||
}
|
||||
|
||||
@ -550,7 +550,7 @@ static void ep0_txstate(struct musb *musb)
|
||||
|
||||
if (!req) {
|
||||
/* WARN_ON(1); */
|
||||
dev_dbg(musb->controller, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0));
|
||||
musb_dbg(musb, "odd; csr0 %04x", musb_readw(regs, MUSB_CSR0));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -607,7 +607,7 @@ musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req)
|
||||
/* NOTE: earlier 2.6 versions changed setup packets to host
|
||||
* order, but now USB packets always stay in USB byte order.
|
||||
*/
|
||||
dev_dbg(musb->controller, "SETUP req%02x.%02x v%04x i%04x l%d\n",
|
||||
musb_dbg(musb, "SETUP req%02x.%02x v%04x i%04x l%d",
|
||||
req->bRequestType,
|
||||
req->bRequest,
|
||||
le16_to_cpu(req->wValue),
|
||||
@ -675,7 +675,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
len = musb_readb(regs, MUSB_COUNT0);
|
||||
|
||||
dev_dbg(musb->controller, "csr %04x, count %d, ep0stage %s\n",
|
||||
musb_dbg(musb, "csr %04x, count %d, ep0stage %s",
|
||||
csr, len, decode_ep0stage(musb->ep0_state));
|
||||
|
||||
if (csr & MUSB_CSR0_P_DATAEND) {
|
||||
@ -752,7 +752,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
|
||||
/* enter test mode if needed (exit by reset) */
|
||||
else if (musb->test_mode) {
|
||||
dev_dbg(musb->controller, "entering TESTMODE\n");
|
||||
musb_dbg(musb, "entering TESTMODE");
|
||||
|
||||
if (MUSB_TEST_PACKET == musb->test_mode_nr)
|
||||
musb_load_testpacket(musb);
|
||||
@ -864,7 +864,7 @@ setup:
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "handled %d, csr %04x, ep0stage %s\n",
|
||||
musb_dbg(musb, "handled %d, csr %04x, ep0stage %s",
|
||||
handled, csr,
|
||||
decode_ep0stage(musb->ep0_state));
|
||||
|
||||
@ -881,7 +881,7 @@ setup:
|
||||
if (handled < 0) {
|
||||
musb_ep_select(mbase, 0);
|
||||
stall:
|
||||
dev_dbg(musb->controller, "stall (%d)\n", handled);
|
||||
musb_dbg(musb, "stall (%d)", handled);
|
||||
musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_IDLE;
|
||||
finish:
|
||||
@ -961,7 +961,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
|
||||
status = 0;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "ep0 request queued in state %d\n",
|
||||
musb_dbg(musb, "ep0 request queued in state %d",
|
||||
musb->ep0_state);
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
@ -970,7 +970,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
|
||||
/* add request to the list */
|
||||
list_add_tail(&req->list, &ep->req_list);
|
||||
|
||||
dev_dbg(musb->controller, "queue to %s (%s), length=%d\n",
|
||||
musb_dbg(musb, "queue to %s (%s), length=%d",
|
||||
ep->name, ep->is_in ? "IN/TX" : "OUT/RX",
|
||||
req->request.length);
|
||||
|
||||
@ -1063,7 +1063,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value)
|
||||
musb->ackpend = 0;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "ep0 can't halt in state %d\n", musb->ep0_state);
|
||||
musb_dbg(musb, "ep0 can't halt in state %d", musb->ep0_state);
|
||||
status = -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "musb_host.h"
|
||||
#include "musb_trace.h"
|
||||
|
||||
/* MUSB HOST status 22-mar-2006
|
||||
*
|
||||
@ -131,7 +132,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
|
||||
* 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
|
||||
* Better to turn on musb_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,
|
||||
@ -225,8 +226,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
void *buf = urb->transfer_buffer;
|
||||
u32 offset = 0;
|
||||
struct musb_hw_ep *hw_ep = qh->hw_ep;
|
||||
unsigned pipe = urb->pipe;
|
||||
u8 address = usb_pipedevice(pipe);
|
||||
int epnum = hw_ep->epnum;
|
||||
|
||||
/* initialize software qh state */
|
||||
@ -254,16 +253,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
len = urb->transfer_buffer_length - urb->actual_length;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n",
|
||||
qh, urb, address, qh->epnum,
|
||||
is_in ? "in" : "out",
|
||||
({char *s; switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL: s = ""; break;
|
||||
case USB_ENDPOINT_XFER_BULK: s = "-bulk"; break;
|
||||
case USB_ENDPOINT_XFER_ISOC: s = "-iso"; break;
|
||||
default: s = "-intr"; break;
|
||||
} s; }),
|
||||
epnum, buf + offset, len);
|
||||
trace_musb_urb_start(musb, urb);
|
||||
|
||||
/* Configure endpoint */
|
||||
musb_ep_set_qh(hw_ep, is_in, qh);
|
||||
@ -277,7 +267,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
dev_dbg(musb->controller, "check whether there's still time for periodic Tx\n");
|
||||
musb_dbg(musb, "check whether there's still time for periodic Tx");
|
||||
frame = musb_readw(mbase, MUSB_FRAME);
|
||||
/* FIXME this doesn't implement that scheduling policy ...
|
||||
* or handle framecounter wrapping
|
||||
@ -291,7 +281,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
} else {
|
||||
qh->frame = urb->start_frame;
|
||||
/* enable SOF interrupt so we can count down */
|
||||
dev_dbg(musb->controller, "SOF for %d\n", epnum);
|
||||
musb_dbg(musb, "SOF for %d", epnum);
|
||||
#if 1 /* ifndef CONFIG_ARCH_DAVINCI */
|
||||
musb_writeb(mbase, MUSB_INTRUSBE, 0xff);
|
||||
#endif
|
||||
@ -299,7 +289,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
|
||||
break;
|
||||
default:
|
||||
start:
|
||||
dev_dbg(musb->controller, "Start TX%d %s\n", epnum,
|
||||
musb_dbg(musb, "Start TX%d %s", epnum,
|
||||
hw_ep->tx_channel ? "dma" : "pio");
|
||||
|
||||
if (!hw_ep->tx_channel)
|
||||
@ -314,14 +304,7 @@ static void musb_giveback(struct musb *musb, struct urb *urb, int status)
|
||||
__releases(musb->lock)
|
||||
__acquires(musb->lock)
|
||||
{
|
||||
dev_dbg(musb->controller,
|
||||
"complete %p %pF (%d), dev%d ep%d%s, %d/%d\n",
|
||||
urb, urb->complete, status,
|
||||
usb_pipedevice(urb->pipe),
|
||||
usb_pipeendpoint(urb->pipe),
|
||||
usb_pipein(urb->pipe) ? "in" : "out",
|
||||
urb->actual_length, urb->transfer_buffer_length
|
||||
);
|
||||
trace_musb_urb_gb(musb, urb);
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(musb->hcd, urb);
|
||||
spin_unlock(&musb->lock);
|
||||
@ -441,7 +424,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb,
|
||||
* for RX, until we have a test case to understand the behavior of TX.
|
||||
*/
|
||||
if ((!status || !is_in) && qh && qh->is_ready) {
|
||||
dev_dbg(musb->controller, "... next ep%d %cX urb %p\n",
|
||||
musb_dbg(musb, "... next ep%d %cX urb %p",
|
||||
hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh));
|
||||
musb_start_urb(musb, is_in, qh);
|
||||
}
|
||||
@ -486,7 +469,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err)
|
||||
|
||||
/* musb_ep_select(mbase, epnum); */
|
||||
rx_count = musb_readw(epio, MUSB_RXCOUNT);
|
||||
dev_dbg(musb->controller, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count,
|
||||
musb_dbg(musb, "RX%d count %d, buffer %p len %d/%d", epnum, rx_count,
|
||||
urb->transfer_buffer, qh->offset,
|
||||
urb->transfer_buffer_length);
|
||||
|
||||
@ -508,7 +491,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err)
|
||||
status = -EOVERFLOW;
|
||||
urb->error_count++;
|
||||
}
|
||||
dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length);
|
||||
musb_dbg(musb, "OVERFLOW %d into %d", rx_count, length);
|
||||
do_flush = 1;
|
||||
} else
|
||||
length = rx_count;
|
||||
@ -526,7 +509,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err)
|
||||
if (rx_count > length) {
|
||||
if (urb->status == -EINPROGRESS)
|
||||
urb->status = -EOVERFLOW;
|
||||
dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length);
|
||||
musb_dbg(musb, "OVERFLOW %d into %d", rx_count, length);
|
||||
do_flush = 1;
|
||||
} else
|
||||
length = rx_count;
|
||||
@ -750,8 +733,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
|
||||
u8 use_dma = 1;
|
||||
u16 csr;
|
||||
|
||||
dev_dbg(musb->controller, "%s hw%d urb %p spd%d dev%d ep%d%s "
|
||||
"h_addr%02x h_port%02x bytes %d\n",
|
||||
musb_dbg(musb, "%s hw%d urb %p spd%d dev%d ep%d%s "
|
||||
"h_addr%02x h_port%02x bytes %d",
|
||||
is_out ? "-->" : "<--",
|
||||
epnum, urb, urb->dev->speed,
|
||||
qh->addr_reg, qh->epnum, is_out ? "out" : "in",
|
||||
@ -969,7 +952,7 @@ finish:
|
||||
}
|
||||
|
||||
csr |= MUSB_RXCSR_H_REQPKT;
|
||||
dev_dbg(musb->controller, "RXCSR%d := %04x\n", epnum, csr);
|
||||
musb_dbg(musb, "RXCSR%d := %04x", epnum, csr);
|
||||
musb_writew(hw_ep->regs, MUSB_RXCSR, csr);
|
||||
csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
|
||||
}
|
||||
@ -1085,15 +1068,15 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
|
||||
request = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
if (!request->wLength) {
|
||||
dev_dbg(musb->controller, "start no-DATA\n");
|
||||
musb_dbg(musb, "start no-DATA");
|
||||
break;
|
||||
} else if (request->bRequestType & USB_DIR_IN) {
|
||||
dev_dbg(musb->controller, "start IN-DATA\n");
|
||||
musb_dbg(musb, "start IN-DATA");
|
||||
musb->ep0_stage = MUSB_EP0_IN;
|
||||
more = true;
|
||||
break;
|
||||
} else {
|
||||
dev_dbg(musb->controller, "start OUT-DATA\n");
|
||||
musb_dbg(musb, "start OUT-DATA");
|
||||
musb->ep0_stage = MUSB_EP0_OUT;
|
||||
more = true;
|
||||
}
|
||||
@ -1105,7 +1088,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
|
||||
if (fifo_count) {
|
||||
fifo_dest = (u8 *) (urb->transfer_buffer
|
||||
+ urb->actual_length);
|
||||
dev_dbg(musb->controller, "Sending %d byte%s to ep0 fifo %p\n",
|
||||
musb_dbg(musb, "Sending %d byte%s to ep0 fifo %p",
|
||||
fifo_count,
|
||||
(fifo_count == 1) ? "" : "s",
|
||||
fifo_dest);
|
||||
@ -1150,7 +1133,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
|
||||
? musb_readb(epio, MUSB_COUNT0)
|
||||
: 0;
|
||||
|
||||
dev_dbg(musb->controller, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n",
|
||||
musb_dbg(musb, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d",
|
||||
csr, qh, len, urb, musb->ep0_stage);
|
||||
|
||||
/* if we just did status stage, we are done */
|
||||
@ -1161,15 +1144,15 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
|
||||
|
||||
/* prepare status */
|
||||
if (csr & MUSB_CSR0_H_RXSTALL) {
|
||||
dev_dbg(musb->controller, "STALLING ENDPOINT\n");
|
||||
musb_dbg(musb, "STALLING ENDPOINT");
|
||||
status = -EPIPE;
|
||||
|
||||
} else if (csr & MUSB_CSR0_H_ERROR) {
|
||||
dev_dbg(musb->controller, "no response, csr0 %04x\n", csr);
|
||||
musb_dbg(musb, "no response, csr0 %04x", csr);
|
||||
status = -EPROTO;
|
||||
|
||||
} else if (csr & MUSB_CSR0_H_NAKTIMEOUT) {
|
||||
dev_dbg(musb->controller, "control NAK timeout\n");
|
||||
musb_dbg(musb, "control NAK timeout");
|
||||
|
||||
/* NOTE: this code path would be a good place to PAUSE a
|
||||
* control transfer, if another one is queued, so that
|
||||
@ -1184,7 +1167,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
|
||||
}
|
||||
|
||||
if (status) {
|
||||
dev_dbg(musb->controller, "aborting\n");
|
||||
musb_dbg(musb, "aborting");
|
||||
retval = IRQ_HANDLED;
|
||||
if (urb)
|
||||
urb->status = status;
|
||||
@ -1237,7 +1220,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb)
|
||||
/* flag status stage */
|
||||
musb->ep0_stage = MUSB_EP0_STATUS;
|
||||
|
||||
dev_dbg(musb->controller, "ep0 STATUS, csr %04x\n", csr);
|
||||
musb_dbg(musb, "ep0 STATUS, csr %04x", csr);
|
||||
|
||||
}
|
||||
musb_writew(epio, MUSB_CSR0, csr);
|
||||
@ -1291,38 +1274,37 @@ void musb_host_tx(struct musb *musb, u8 epnum)
|
||||
|
||||
/* with CPPI, DMA sometimes triggers "extra" irqs */
|
||||
if (!urb) {
|
||||
dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr);
|
||||
musb_dbg(musb, "extra TX%d ready, csr %04x", epnum, tx_csr);
|
||||
return;
|
||||
}
|
||||
|
||||
pipe = urb->pipe;
|
||||
dma = is_dma_capable() ? hw_ep->tx_channel : NULL;
|
||||
dev_dbg(musb->controller, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr,
|
||||
trace_musb_urb_tx(musb, urb);
|
||||
musb_dbg(musb, "OUT/TX%d end, csr %04x%s", epnum, tx_csr,
|
||||
dma ? ", dma" : "");
|
||||
|
||||
/* check for errors */
|
||||
if (tx_csr & MUSB_TXCSR_H_RXSTALL) {
|
||||
/* dma was disabled, fifo flushed */
|
||||
dev_dbg(musb->controller, "TX end %d stall\n", epnum);
|
||||
musb_dbg(musb, "TX end %d stall", epnum);
|
||||
|
||||
/* stall; record URB status */
|
||||
status = -EPIPE;
|
||||
|
||||
} else if (tx_csr & MUSB_TXCSR_H_ERROR) {
|
||||
/* (NON-ISO) dma was disabled, fifo flushed */
|
||||
dev_dbg(musb->controller, "TX 3strikes on ep=%d\n", epnum);
|
||||
musb_dbg(musb, "TX 3strikes on ep=%d", epnum);
|
||||
|
||||
status = -ETIMEDOUT;
|
||||
|
||||
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
|
||||
if (USB_ENDPOINT_XFER_BULK == qh->type && qh->mux == 1
|
||||
&& !list_is_singular(&musb->out_bulk)) {
|
||||
dev_dbg(musb->controller,
|
||||
"NAK timeout on TX%d ep\n", epnum);
|
||||
musb_dbg(musb, "NAK timeout on TX%d ep", epnum);
|
||||
musb_bulk_nak_timeout(musb, hw_ep, 0);
|
||||
} else {
|
||||
dev_dbg(musb->controller,
|
||||
"TX end=%d device not responding\n", epnum);
|
||||
musb_dbg(musb, "TX ep%d device not responding", epnum);
|
||||
/* NOTE: this code path would be a good place to PAUSE a
|
||||
* transfer, if there's some other (nonperiodic) tx urb
|
||||
* that could use this fifo. (dma complicates it...)
|
||||
@ -1368,7 +1350,7 @@ done:
|
||||
|
||||
/* second cppi case */
|
||||
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
|
||||
dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr);
|
||||
musb_dbg(musb, "extra TX%d ready, csr %04x", epnum, tx_csr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1427,8 +1409,9 @@ done:
|
||||
* FIFO mode too...
|
||||
*/
|
||||
if (tx_csr & (MUSB_TXCSR_FIFONOTEMPTY | MUSB_TXCSR_TXPKTRDY)) {
|
||||
dev_dbg(musb->controller, "DMA complete but packet still in FIFO, "
|
||||
"CSR %04x\n", tx_csr);
|
||||
musb_dbg(musb,
|
||||
"DMA complete but FIFO not empty, CSR %04x",
|
||||
tx_csr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1494,7 +1477,7 @@ done:
|
||||
return;
|
||||
}
|
||||
} else if (tx_csr & MUSB_TXCSR_DMAENAB) {
|
||||
dev_dbg(musb->controller, "not complete, but DMA enabled?\n");
|
||||
musb_dbg(musb, "not complete, but DMA enabled?");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1723,7 +1706,7 @@ static int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma,
|
||||
d_status = -EOVERFLOW;
|
||||
urb->error_count++;
|
||||
}
|
||||
dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",
|
||||
musb_dbg(musb, "** OVERFLOW %d into %d",
|
||||
rx_count, d->length);
|
||||
|
||||
length = d->length;
|
||||
@ -1847,28 +1830,26 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
* usbtest #11 (unlinks) triggers it regularly, sometimes
|
||||
* with fifo full. (Only with DMA??)
|
||||
*/
|
||||
dev_dbg(musb->controller, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val,
|
||||
musb_readw(epio, MUSB_RXCOUNT));
|
||||
musb_dbg(musb, "BOGUS RX%d ready, csr %04x, count %d",
|
||||
epnum, val, musb_readw(epio, MUSB_RXCOUNT));
|
||||
musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG);
|
||||
return;
|
||||
}
|
||||
|
||||
pipe = urb->pipe;
|
||||
|
||||
dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n",
|
||||
epnum, rx_csr, urb->actual_length,
|
||||
dma ? dma->actual_len : 0);
|
||||
trace_musb_urb_rx(musb, urb);
|
||||
|
||||
/* check for errors, concurrent stall & unlink is not really
|
||||
* handled yet! */
|
||||
if (rx_csr & MUSB_RXCSR_H_RXSTALL) {
|
||||
dev_dbg(musb->controller, "RX end %d STALL\n", epnum);
|
||||
musb_dbg(musb, "RX end %d STALL", epnum);
|
||||
|
||||
/* stall; record URB status */
|
||||
status = -EPIPE;
|
||||
|
||||
} else if (rx_csr & MUSB_RXCSR_H_ERROR) {
|
||||
dev_dbg(musb->controller, "end %d RX proto error\n", epnum);
|
||||
musb_dbg(musb, "end %d RX proto error", epnum);
|
||||
|
||||
status = -EPROTO;
|
||||
musb_writeb(epio, MUSB_RXINTERVAL, 0);
|
||||
@ -1879,7 +1860,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
} else if (rx_csr & MUSB_RXCSR_DATAERROR) {
|
||||
|
||||
if (USB_ENDPOINT_XFER_ISOC != qh->type) {
|
||||
dev_dbg(musb->controller, "RX end %d NAK timeout\n", epnum);
|
||||
musb_dbg(musb, "RX end %d NAK timeout", epnum);
|
||||
|
||||
/* NOTE: NAKing is *NOT* an error, so we want to
|
||||
* continue. Except ... if there's a request for
|
||||
@ -1902,12 +1883,12 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
|
||||
goto finish;
|
||||
} else {
|
||||
dev_dbg(musb->controller, "RX end %d ISO data error\n", epnum);
|
||||
musb_dbg(musb, "RX end %d ISO data error", epnum);
|
||||
/* packet error reported later */
|
||||
iso_err = true;
|
||||
}
|
||||
} else if (rx_csr & MUSB_RXCSR_INCOMPRX) {
|
||||
dev_dbg(musb->controller, "end %d high bandwidth incomplete ISO packet RX\n",
|
||||
musb_dbg(musb, "end %d high bandwidth incomplete ISO packet RX",
|
||||
epnum);
|
||||
status = -EPROTO;
|
||||
}
|
||||
@ -1952,7 +1933,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
done = true;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr,
|
||||
musb_dbg(musb, "RXCSR%d %04x, reqpkt, len %zu%s", epnum, rx_csr,
|
||||
xfer_len, dma ? ", dma" : "");
|
||||
rx_csr &= ~MUSB_RXCSR_H_REQPKT;
|
||||
|
||||
@ -1973,8 +1954,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
if (musb_dma_inventra(musb) || musb_dma_ux500(musb) ||
|
||||
musb_dma_cppi41(musb)) {
|
||||
done = musb_rx_dma_inventra_cppi41(c, hw_ep, qh, urb, xfer_len);
|
||||
dev_dbg(hw_ep->musb->controller,
|
||||
"ep %d dma %s, rxcsr %04x, rxcount %d\n",
|
||||
musb_dbg(hw_ep->musb,
|
||||
"ep %d dma %s, rxcsr %04x, rxcount %d",
|
||||
epnum, done ? "off" : "reset",
|
||||
musb_readw(epio, MUSB_RXCSR),
|
||||
musb_readw(epio, MUSB_RXCOUNT));
|
||||
@ -2001,8 +1982,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
/* we are expecting IN packets */
|
||||
if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) ||
|
||||
musb_dma_cppi41(musb)) && dma) {
|
||||
dev_dbg(hw_ep->musb->controller,
|
||||
"RX%d count %d, buffer 0x%llx len %d/%d\n",
|
||||
musb_dbg(hw_ep->musb,
|
||||
"RX%d count %d, buffer 0x%llx len %d/%d",
|
||||
epnum, musb_readw(epio, MUSB_RXCOUNT),
|
||||
(unsigned long long) urb->transfer_dma
|
||||
+ urb->actual_length,
|
||||
@ -2054,7 +2035,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
done = musb_host_packet_rx(musb, urb,
|
||||
epnum, iso_err);
|
||||
}
|
||||
dev_dbg(musb->controller, "read %spacket\n", done ? "last " : "");
|
||||
musb_dbg(musb, "read %spacket", done ? "last " : "");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2178,7 +2159,7 @@ static int musb_schedule(
|
||||
idle = 1;
|
||||
qh->mux = 0;
|
||||
hw_ep = musb->endpoints + best_end;
|
||||
dev_dbg(musb->controller, "qh %p periodic slot %d\n", qh, best_end);
|
||||
musb_dbg(musb, "qh %p periodic slot %d", qh, best_end);
|
||||
success:
|
||||
if (head) {
|
||||
idle = list_empty(head);
|
||||
@ -2210,6 +2191,8 @@ static int musb_urb_enqueue(
|
||||
if (!is_host_active(musb) || !musb->is_active)
|
||||
return -ENODEV;
|
||||
|
||||
trace_musb_urb_enq(musb, urb);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
ret = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
qh = ret ? NULL : hep->hcpriv;
|
||||
@ -2400,8 +2383,7 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
|
||||
dma = is_in ? ep->rx_channel : ep->tx_channel;
|
||||
if (dma) {
|
||||
status = ep->musb->dma_controller->channel_abort(dma);
|
||||
dev_dbg(musb->controller,
|
||||
"abort %cX%d DMA for urb %p --> %d\n",
|
||||
musb_dbg(musb, "abort %cX%d DMA for urb %p --> %d",
|
||||
is_in ? 'R' : 'T', ep->epnum,
|
||||
urb, status);
|
||||
urb->actual_length += dma->actual_len;
|
||||
@ -2447,10 +2429,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||
int is_in = usb_pipein(urb->pipe);
|
||||
int ret;
|
||||
|
||||
dev_dbg(musb->controller, "urb=%p, dev%d ep%d%s\n", urb,
|
||||
usb_pipedevice(urb->pipe),
|
||||
usb_pipeendpoint(urb->pipe),
|
||||
is_in ? "in" : "out");
|
||||
trace_musb_urb_deq(musb, urb);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
ret = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
|
33
drivers/usb/musb/musb_trace.c
Normal file
33
drivers/usb/musb/musb_trace.c
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* musb_trace.c - MUSB Controller Trace Support
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Bin Liu <b-liu@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "musb_trace.h"
|
||||
|
||||
void musb_dbg(struct musb *musb, const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
trace_musb_log(musb, &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
371
drivers/usb/musb/musb_trace.h
Normal file
371
drivers/usb/musb/musb_trace.h
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* musb_trace.h - MUSB Controller Trace Support
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Bin Liu <b-liu@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM musb
|
||||
|
||||
#if !defined(__MUSB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __MUSB_TRACE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/usb.h>
|
||||
#include "musb_core.h"
|
||||
#ifdef CONFIG_USB_TI_CPPI41_DMA
|
||||
#include "cppi_dma.h"
|
||||
#endif
|
||||
|
||||
#define MUSB_MSG_MAX 500
|
||||
|
||||
TRACE_EVENT(musb_log,
|
||||
TP_PROTO(struct musb *musb, struct va_format *vaf),
|
||||
TP_ARGS(musb, vaf),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, dev_name(musb->controller))
|
||||
__dynamic_array(char, msg, MUSB_MSG_MAX)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, dev_name(musb->controller));
|
||||
vsnprintf(__get_str(msg), MUSB_MSG_MAX, vaf->fmt, *vaf->va);
|
||||
),
|
||||
TP_printk("%s: %s", __get_str(name), __get_str(msg))
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(musb_regb,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data),
|
||||
TP_ARGS(caller, addr, offset, data),
|
||||
TP_STRUCT__entry(
|
||||
__field(void *, caller)
|
||||
__field(const void *, addr)
|
||||
__field(unsigned int, offset)
|
||||
__field(u8, data)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->caller = caller;
|
||||
__entry->addr = addr;
|
||||
__entry->offset = offset;
|
||||
__entry->data = data;
|
||||
),
|
||||
TP_printk("%pS: %p + %04x: %02x",
|
||||
__entry->caller, __entry->addr, __entry->offset, __entry->data)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_regb, musb_readb,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data),
|
||||
TP_ARGS(caller, addr, offset, data)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_regb, musb_writeb,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data),
|
||||
TP_ARGS(caller, addr, offset, data)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(musb_regw,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data),
|
||||
TP_ARGS(caller, addr, offset, data),
|
||||
TP_STRUCT__entry(
|
||||
__field(void *, caller)
|
||||
__field(const void *, addr)
|
||||
__field(unsigned int, offset)
|
||||
__field(u16, data)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->caller = caller;
|
||||
__entry->addr = addr;
|
||||
__entry->offset = offset;
|
||||
__entry->data = data;
|
||||
),
|
||||
TP_printk("%pS: %p + %04x: %04x",
|
||||
__entry->caller, __entry->addr, __entry->offset, __entry->data)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_regw, musb_readw,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data),
|
||||
TP_ARGS(caller, addr, offset, data)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_regw, musb_writew,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data),
|
||||
TP_ARGS(caller, addr, offset, data)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(musb_regl,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data),
|
||||
TP_ARGS(caller, addr, offset, data),
|
||||
TP_STRUCT__entry(
|
||||
__field(void *, caller)
|
||||
__field(const void *, addr)
|
||||
__field(unsigned int, offset)
|
||||
__field(u32, data)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->caller = caller;
|
||||
__entry->addr = addr;
|
||||
__entry->offset = offset;
|
||||
__entry->data = data;
|
||||
),
|
||||
TP_printk("%pS: %p + %04x: %08x",
|
||||
__entry->caller, __entry->addr, __entry->offset, __entry->data)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_regl, musb_readl,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data),
|
||||
TP_ARGS(caller, addr, offset, data)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_regl, musb_writel,
|
||||
TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data),
|
||||
TP_ARGS(caller, addr, offset, data)
|
||||
);
|
||||
|
||||
TRACE_EVENT(musb_isr,
|
||||
TP_PROTO(struct musb *musb),
|
||||
TP_ARGS(musb),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, dev_name(musb->controller))
|
||||
__field(u8, int_usb)
|
||||
__field(u16, int_tx)
|
||||
__field(u16, int_rx)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, dev_name(musb->controller));
|
||||
__entry->int_usb = musb->int_usb;
|
||||
__entry->int_tx = musb->int_tx;
|
||||
__entry->int_rx = musb->int_rx;
|
||||
),
|
||||
TP_printk("%s: usb %02x, tx %04x, rx %04x",
|
||||
__get_str(name), __entry->int_usb,
|
||||
__entry->int_tx, __entry->int_rx
|
||||
)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(musb_urb,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb),
|
||||
TP_STRUCT__entry(
|
||||
__string(name, dev_name(musb->controller))
|
||||
__field(struct urb *, urb)
|
||||
__field(unsigned int, pipe)
|
||||
__field(int, status)
|
||||
__field(unsigned int, flag)
|
||||
__field(u32, buf_len)
|
||||
__field(u32, actual_len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__assign_str(name, dev_name(musb->controller));
|
||||
__entry->urb = urb;
|
||||
__entry->pipe = urb->pipe;
|
||||
__entry->status = urb->status;
|
||||
__entry->flag = urb->transfer_flags;
|
||||
__entry->buf_len = urb->transfer_buffer_length;
|
||||
__entry->actual_len = urb->actual_length;
|
||||
),
|
||||
TP_printk("%s: %p, dev%d ep%d%s, flag 0x%x, len %d/%d, status %d",
|
||||
__get_str(name), __entry->urb,
|
||||
usb_pipedevice(__entry->pipe),
|
||||
usb_pipeendpoint(__entry->pipe),
|
||||
usb_pipein(__entry->pipe) ? "in" : "out",
|
||||
__entry->flag,
|
||||
__entry->actual_len, __entry->buf_len,
|
||||
__entry->status
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_urb, musb_urb_start,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_urb, musb_urb_gb,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_urb, musb_urb_rx,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_urb, musb_urb_tx,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_urb, musb_urb_enq,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_urb, musb_urb_deq,
|
||||
TP_PROTO(struct musb *musb, struct urb *urb),
|
||||
TP_ARGS(musb, urb)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(musb_req,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct usb_request *, req)
|
||||
__field(u8, is_tx)
|
||||
__field(u8, epnum)
|
||||
__field(int, status)
|
||||
__field(unsigned int, buf_len)
|
||||
__field(unsigned int, actual_len)
|
||||
__field(unsigned int, zero)
|
||||
__field(unsigned int, short_not_ok)
|
||||
__field(unsigned int, no_interrupt)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->req = &req->request;
|
||||
__entry->is_tx = req->tx;
|
||||
__entry->epnum = req->epnum;
|
||||
__entry->status = req->request.status;
|
||||
__entry->buf_len = req->request.length;
|
||||
__entry->actual_len = req->request.actual;
|
||||
__entry->zero = req->request.zero;
|
||||
__entry->short_not_ok = req->request.short_not_ok;
|
||||
__entry->no_interrupt = req->request.no_interrupt;
|
||||
),
|
||||
TP_printk("%p, ep%d %s, %s%s%s, len %d/%d, status %d",
|
||||
__entry->req, __entry->epnum,
|
||||
__entry->is_tx ? "tx/IN" : "rx/OUT",
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "I" : "i",
|
||||
__entry->actual_len, __entry->buf_len,
|
||||
__entry->status
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_gb,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_tx,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_rx,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_alloc,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_free,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_start,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_enq,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_req, musb_req_deq,
|
||||
TP_PROTO(struct musb_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_USB_TI_CPPI41_DMA
|
||||
DECLARE_EVENT_CLASS(musb_cppi41,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct cppi41_dma_channel *, ch)
|
||||
__string(name, dev_name(ch->hw_ep->musb->controller))
|
||||
__field(u8, hwep)
|
||||
__field(u8, port)
|
||||
__field(u8, is_tx)
|
||||
__field(u32, len)
|
||||
__field(u32, prog_len)
|
||||
__field(u32, xferred)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ch = ch;
|
||||
__assign_str(name, dev_name(ch->hw_ep->musb->controller));
|
||||
__entry->hwep = ch->hw_ep->epnum;
|
||||
__entry->port = ch->port_num;
|
||||
__entry->is_tx = ch->is_tx;
|
||||
__entry->len = ch->total_len;
|
||||
__entry->prog_len = ch->prog_len;
|
||||
__entry->xferred = ch->transferred;
|
||||
),
|
||||
TP_printk("%s: %p, hwep%d ch%d%s, prog_len %d, len %d/%d",
|
||||
__get_str(name), __entry->ch, __entry->hwep,
|
||||
__entry->port, __entry->is_tx ? "tx" : "rx",
|
||||
__entry->prog_len, __entry->xferred, __entry->len
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_done,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_gb,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_config,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_cont,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_alloc,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_abort,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(musb_cppi41, musb_cppi41_free,
|
||||
TP_PROTO(struct cppi41_dma_channel *ch),
|
||||
TP_ARGS(ch)
|
||||
);
|
||||
#endif /* CONFIG_USB_TI_CPPI41_DMA */
|
||||
|
||||
#endif /* __MUSB_TRACE_H */
|
||||
|
||||
/* this part has to be here */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE musb_trace
|
||||
|
||||
#include <trace/define_trace.h>
|
@ -55,8 +55,7 @@ void musb_host_finish_resume(struct work_struct *work)
|
||||
|
||||
power = musb_readb(musb->mregs, MUSB_POWER);
|
||||
power &= ~MUSB_POWER_RESUME;
|
||||
dev_dbg(musb->controller, "root port resume stopped, power %02x\n",
|
||||
power);
|
||||
musb_dbg(musb, "root port resume stopped, power %02x", power);
|
||||
musb_writeb(musb->mregs, MUSB_POWER, power);
|
||||
|
||||
/*
|
||||
@ -104,7 +103,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "Root port suspended, power %02x\n", power);
|
||||
musb_dbg(musb, "Root port suspended, power %02x", power);
|
||||
|
||||
musb->port1_status |= USB_PORT_STAT_SUSPEND;
|
||||
switch (musb->xceiv->otg->state) {
|
||||
@ -123,7 +122,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
musb_platform_try_idle(musb, 0);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "bogus rh suspend? %s\n",
|
||||
musb_dbg(musb, "bogus rh suspend? %s",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
} else if (power & MUSB_POWER_SUSPENDM) {
|
||||
@ -131,7 +130,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
power |= MUSB_POWER_RESUME;
|
||||
musb_writeb(mbase, MUSB_POWER, power);
|
||||
|
||||
dev_dbg(musb->controller, "Root port resuming, power %02x\n", power);
|
||||
musb_dbg(musb, "Root port resuming, power %02x", power);
|
||||
|
||||
/* later, GetPortStatus will stop RESUME signaling */
|
||||
musb->port1_status |= MUSB_PORT_STAT_RESUME;
|
||||
@ -146,7 +145,7 @@ void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) {
|
||||
dev_dbg(musb->controller, "HNP: Returning from HNP; no hub reset from b_idle\n");
|
||||
musb_dbg(musb, "HNP: Returning from HNP; no hub reset from b_idle");
|
||||
musb->port1_status &= ~USB_PORT_STAT_RESET;
|
||||
return;
|
||||
}
|
||||
@ -194,7 +193,7 @@ void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
schedule_delayed_work(&musb->deassert_reset_work,
|
||||
msecs_to_jiffies(50));
|
||||
} else {
|
||||
dev_dbg(musb->controller, "root port reset stopped\n");
|
||||
musb_dbg(musb, "root port reset stopped");
|
||||
musb_platform_pre_root_reset_end(musb);
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power & ~MUSB_POWER_RESET);
|
||||
@ -202,7 +201,7 @@ void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (power & MUSB_POWER_HSMODE) {
|
||||
dev_dbg(musb->controller, "high-speed device connected\n");
|
||||
musb_dbg(musb, "high-speed device connected");
|
||||
musb->port1_status |= USB_PORT_STAT_HIGH_SPEED;
|
||||
}
|
||||
|
||||
@ -242,7 +241,7 @@ void musb_root_disconnect(struct musb *musb)
|
||||
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(musb->controller, "host disconnect (%s)\n",
|
||||
musb_dbg(musb, "host disconnect (%s)",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
}
|
||||
}
|
||||
@ -337,7 +336,7 @@ int musb_hub_control(
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
dev_dbg(musb->controller, "clear feature %d\n", wValue);
|
||||
musb_dbg(musb, "clear feature %d", wValue);
|
||||
musb->port1_status &= ~(1 << wValue);
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
@ -372,8 +371,7 @@ int musb_hub_control(
|
||||
(__le32 *) buf);
|
||||
|
||||
/* port change status is more interesting */
|
||||
dev_dbg(musb->controller, "port status %08x\n",
|
||||
musb->port1_status);
|
||||
musb_dbg(musb, "port status %08x", musb->port1_status);
|
||||
break;
|
||||
case SetPortFeature:
|
||||
if ((wIndex & 0xff) != 1)
|
||||
@ -443,7 +441,7 @@ int musb_hub_control(
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
dev_dbg(musb->controller, "set feature %d\n", wValue);
|
||||
musb_dbg(musb, "set feature %d", wValue);
|
||||
musb->port1_status |= 1 << wValue;
|
||||
break;
|
||||
|
||||
|
@ -117,7 +117,7 @@ static void configure_channel(struct dma_channel *channel,
|
||||
u8 bchannel = musb_channel->idx;
|
||||
u16 csr = 0;
|
||||
|
||||
dev_dbg(musb->controller, "%p, pkt_sz %d, addr %pad, len %d, mode %d\n",
|
||||
musb_dbg(musb, "%p, pkt_sz %d, addr %pad, len %d, mode %d",
|
||||
channel, packet_sz, &dma_addr, len, mode);
|
||||
|
||||
if (mode) {
|
||||
@ -152,7 +152,7 @@ static int dma_channel_program(struct dma_channel *channel,
|
||||
struct musb_dma_controller *controller = musb_channel->controller;
|
||||
struct musb *musb = controller->private_data;
|
||||
|
||||
dev_dbg(musb->controller, "ep%d-%s pkt_sz %d, dma_addr %pad length %d, mode %d\n",
|
||||
musb_dbg(musb, "ep%d-%s pkt_sz %d, dma_addr %pad length %d, mode %d",
|
||||
musb_channel->epnum,
|
||||
musb_channel->transmit ? "Tx" : "Rx",
|
||||
packet_sz, &dma_addr, len, mode);
|
||||
@ -266,7 +266,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
||||
#endif
|
||||
|
||||
if (!int_hsdma) {
|
||||
dev_dbg(musb->controller, "spurious DMA irq\n");
|
||||
musb_dbg(musb, "spurious DMA irq");
|
||||
|
||||
for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) {
|
||||
musb_channel = (struct musb_dma_channel *)
|
||||
@ -280,7 +280,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(musb->controller, "int_hsdma = 0x%x\n", int_hsdma);
|
||||
musb_dbg(musb, "int_hsdma = 0x%x", int_hsdma);
|
||||
|
||||
if (!int_hsdma)
|
||||
goto done;
|
||||
@ -307,7 +307,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
||||
channel->actual_len = addr
|
||||
- musb_channel->start_addr;
|
||||
|
||||
dev_dbg(musb->controller, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n",
|
||||
musb_dbg(musb, "ch %p, 0x%x -> 0x%x (%zu / %d) %s",
|
||||
channel, musb_channel->start_addr,
|
||||
addr, channel->actual_len,
|
||||
musb_channel->len,
|
||||
|
@ -256,12 +256,10 @@ static int sunxi_musb_init(struct musb *musb)
|
||||
writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
|
||||
|
||||
/* Register notifier before calling phy_init() */
|
||||
if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) {
|
||||
ret = extcon_register_notifier(glue->extcon, EXTCON_USB_HOST,
|
||||
&glue->host_nb);
|
||||
if (ret)
|
||||
goto error_reset_assert;
|
||||
}
|
||||
ret = extcon_register_notifier(glue->extcon, EXTCON_USB_HOST,
|
||||
&glue->host_nb);
|
||||
if (ret)
|
||||
goto error_reset_assert;
|
||||
|
||||
ret = phy_init(glue->phy);
|
||||
if (ret)
|
||||
@ -275,9 +273,8 @@ static int sunxi_musb_init(struct musb *musb)
|
||||
return 0;
|
||||
|
||||
error_unregister_notifier:
|
||||
if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
|
||||
&glue->host_nb);
|
||||
extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
|
||||
&glue->host_nb);
|
||||
error_reset_assert:
|
||||
if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
|
||||
reset_control_assert(glue->rst);
|
||||
@ -301,9 +298,8 @@ static int sunxi_musb_exit(struct musb *musb)
|
||||
|
||||
phy_exit(glue->phy);
|
||||
|
||||
if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
|
||||
&glue->host_nb);
|
||||
extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
|
||||
&glue->host_nb);
|
||||
|
||||
if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
|
||||
reset_control_assert(glue->rst);
|
||||
@ -315,25 +311,6 @@ static int sunxi_musb_exit(struct musb *musb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_set_mode(struct musb *musb, u8 mode)
|
||||
{
|
||||
struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
|
||||
int ret;
|
||||
|
||||
if (mode == MUSB_HOST) {
|
||||
ret = phy_power_on(glue->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
|
||||
/* Stop musb work from turning vbus off again */
|
||||
set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_musb_enable(struct musb *musb)
|
||||
{
|
||||
struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
|
||||
@ -354,13 +331,13 @@ static void sunxi_musb_disable(struct musb *musb)
|
||||
clear_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags);
|
||||
}
|
||||
|
||||
struct dma_controller *sunxi_musb_dma_controller_create(struct musb *musb,
|
||||
void __iomem *base)
|
||||
static struct dma_controller *
|
||||
sunxi_musb_dma_controller_create(struct musb *musb, void __iomem *base)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sunxi_musb_dma_controller_destroy(struct dma_controller *c)
|
||||
static void sunxi_musb_dma_controller_destroy(struct dma_controller *c)
|
||||
{
|
||||
}
|
||||
|
||||
@ -582,7 +559,6 @@ static const struct musb_platform_ops sunxi_musb_ops = {
|
||||
.exit = sunxi_musb_exit,
|
||||
.enable = sunxi_musb_enable,
|
||||
.disable = sunxi_musb_disable,
|
||||
.set_mode = sunxi_set_mode,
|
||||
.fifo_offset = sunxi_musb_fifo_offset,
|
||||
.ep_offset = sunxi_musb_ep_offset,
|
||||
.busctl_offset = sunxi_musb_busctl_offset,
|
||||
@ -638,10 +614,6 @@ static int sunxi_musb_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
switch (usb_get_dr_mode(&pdev->dev)) {
|
||||
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
|
||||
@ -649,15 +621,13 @@ static int sunxi_musb_probe(struct platform_device *pdev)
|
||||
pdata.mode = MUSB_PORT_MODE_HOST;
|
||||
break;
|
||||
#endif
|
||||
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
pdata.mode = MUSB_PORT_MODE_GADGET;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_DUAL_ROLE
|
||||
case USB_DR_MODE_OTG:
|
||||
glue->extcon = extcon_get_edev_by_phandle(&pdev->dev, 0);
|
||||
if (IS_ERR(glue->extcon)) {
|
||||
if (PTR_ERR(glue->extcon) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(&pdev->dev, "Invalid or missing extcon\n");
|
||||
return PTR_ERR(glue->extcon);
|
||||
}
|
||||
pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;
|
||||
break;
|
||||
#endif
|
||||
@ -668,6 +638,10 @@ static int sunxi_musb_probe(struct platform_device *pdev)
|
||||
pdata.platform_ops = &sunxi_musb_ops;
|
||||
pdata.config = &sunxi_musb_hdrc_config;
|
||||
|
||||
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue)
|
||||
return -ENOMEM;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
INIT_WORK(&glue->work, sunxi_musb_work);
|
||||
glue->host_nb.notifier_call = sunxi_musb_host_notifier;
|
||||
@ -701,6 +675,14 @@ static int sunxi_musb_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
glue->extcon = extcon_get_edev_by_phandle(&pdev->dev, 0);
|
||||
if (IS_ERR(glue->extcon)) {
|
||||
if (PTR_ERR(glue->extcon) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(&pdev->dev, "Invalid or missing extcon\n");
|
||||
return PTR_ERR(glue->extcon);
|
||||
}
|
||||
|
||||
glue->phy = devm_phy_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(glue->phy)) {
|
||||
if (PTR_ERR(glue->phy) == -EPROBE_DEFER)
|
||||
|
@ -21,6 +21,7 @@ config AB8500_USB
|
||||
config FSL_USB2_OTG
|
||||
bool "Freescale USB OTG Transceiver Driver"
|
||||
depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support Freescale USB OTG transceiver.
|
||||
@ -29,6 +30,7 @@ config ISP1301_OMAP
|
||||
tristate "Philips ISP1301 with OMAP OTG"
|
||||
depends on I2C && ARCH_OMAP_OTG
|
||||
depends on USB
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
help
|
||||
If you say yes here you get support for the Philips ISP1301
|
||||
@ -43,7 +45,7 @@ config ISP1301_OMAP
|
||||
config KEYSTONE_USB_PHY
|
||||
tristate "Keystone USB PHY Driver"
|
||||
depends on ARCH_KEYSTONE || COMPILE_TEST
|
||||
select NOP_USB_XCEIV
|
||||
depends on NOP_USB_XCEIV
|
||||
help
|
||||
Enable this to support Keystone USB phy. This driver provides
|
||||
interface to interact with USB 2.0 and USB 3.0 PHY that is part
|
||||
@ -51,6 +53,7 @@ config KEYSTONE_USB_PHY
|
||||
|
||||
config NOP_USB_XCEIV
|
||||
tristate "NOP USB Transceiver Driver"
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, NOP can't be built-in
|
||||
select USB_PHY
|
||||
help
|
||||
This driver is to be used by all the usb transceiver which are either
|
||||
@ -63,9 +66,9 @@ config AM335X_CONTROL_USB
|
||||
config AM335X_PHY_USB
|
||||
tristate "AM335x USB PHY Driver"
|
||||
depends on ARM || COMPILE_TEST
|
||||
depends on NOP_USB_XCEIV
|
||||
select USB_PHY
|
||||
select AM335X_CONTROL_USB
|
||||
select NOP_USB_XCEIV
|
||||
select USB_COMMON
|
||||
help
|
||||
This driver provides PHY support for that phy which part for the
|
||||
@ -92,6 +95,7 @@ config TWL6030_USB
|
||||
config USB_GPIO_VBUS
|
||||
tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
help
|
||||
Provides simple GPIO VBUS sensing for controllers with an
|
||||
@ -112,6 +116,7 @@ config OMAP_OTG
|
||||
config TAHVO_USB
|
||||
tristate "Tahvo USB transceiver driver"
|
||||
depends on MFD_RETU && EXTCON
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support USB transceiver on Tahvo. This is used
|
||||
@ -140,6 +145,7 @@ config USB_ISP1301
|
||||
config USB_MSM_OTG
|
||||
tristate "Qualcomm on-chip USB OTG controller support"
|
||||
depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST)
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
depends on RESET_CONTROLLER
|
||||
depends on EXTCON
|
||||
select USB_PHY
|
||||
@ -169,6 +175,7 @@ config USB_QCOM_8X16_PHY
|
||||
config USB_MV_OTG
|
||||
tristate "Marvell USB OTG support"
|
||||
depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
help
|
||||
Say Y here if you want to build Marvell USB OTG transciever
|
||||
|
@ -54,7 +54,7 @@ static int am335x_phy_probe(struct platform_device *pdev)
|
||||
return am_phy->id;
|
||||
}
|
||||
|
||||
am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node);
|
||||
am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1);
|
||||
|
||||
ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL);
|
||||
if (ret)
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
@ -35,6 +36,8 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/otg.h>
|
||||
@ -42,10 +45,183 @@
|
||||
#include <linux/usb/ulpi.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/msm_hsusb.h>
|
||||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/**
|
||||
* OTG control
|
||||
*
|
||||
* OTG_NO_CONTROL Id/VBUS notifications not required. Useful in host
|
||||
* only configuration.
|
||||
* OTG_PHY_CONTROL Id/VBUS notifications comes form USB PHY.
|
||||
* OTG_PMIC_CONTROL Id/VBUS notifications comes from PMIC hardware.
|
||||
* OTG_USER_CONTROL Id/VBUS notifcations comes from User via sysfs.
|
||||
*
|
||||
*/
|
||||
enum otg_control_type {
|
||||
OTG_NO_CONTROL = 0,
|
||||
OTG_PHY_CONTROL,
|
||||
OTG_PMIC_CONTROL,
|
||||
OTG_USER_CONTROL,
|
||||
};
|
||||
|
||||
/**
|
||||
* PHY used in
|
||||
*
|
||||
* INVALID_PHY Unsupported PHY
|
||||
* CI_45NM_INTEGRATED_PHY Chipidea 45nm integrated PHY
|
||||
* SNPS_28NM_INTEGRATED_PHY Synopsis 28nm integrated PHY
|
||||
*
|
||||
*/
|
||||
enum msm_usb_phy_type {
|
||||
INVALID_PHY = 0,
|
||||
CI_45NM_INTEGRATED_PHY,
|
||||
SNPS_28NM_INTEGRATED_PHY,
|
||||
};
|
||||
|
||||
#define IDEV_CHG_MAX 1500
|
||||
#define IUNIT 100
|
||||
|
||||
/**
|
||||
* Different states involved in USB charger detection.
|
||||
*
|
||||
* USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
|
||||
* process is not yet started.
|
||||
* USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
|
||||
* USB_CHG_STATE_DCD_DONE Data pin contact is detected.
|
||||
* USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
|
||||
* between SDP and DCP/CDP).
|
||||
* USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
|
||||
* between DCP and CDP).
|
||||
* USB_CHG_STATE_DETECTED USB charger type is determined.
|
||||
*
|
||||
*/
|
||||
enum usb_chg_state {
|
||||
USB_CHG_STATE_UNDEFINED = 0,
|
||||
USB_CHG_STATE_WAIT_FOR_DCD,
|
||||
USB_CHG_STATE_DCD_DONE,
|
||||
USB_CHG_STATE_PRIMARY_DONE,
|
||||
USB_CHG_STATE_SECONDARY_DONE,
|
||||
USB_CHG_STATE_DETECTED,
|
||||
};
|
||||
|
||||
/**
|
||||
* USB charger types
|
||||
*
|
||||
* USB_INVALID_CHARGER Invalid USB charger.
|
||||
* USB_SDP_CHARGER Standard downstream port. Refers to a downstream port
|
||||
* on USB2.0 compliant host/hub.
|
||||
* USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger).
|
||||
* USB_CDP_CHARGER Charging downstream port. Enumeration can happen and
|
||||
* IDEV_CHG_MAX can be drawn irrespective of USB state.
|
||||
*
|
||||
*/
|
||||
enum usb_chg_type {
|
||||
USB_INVALID_CHARGER = 0,
|
||||
USB_SDP_CHARGER,
|
||||
USB_DCP_CHARGER,
|
||||
USB_CDP_CHARGER,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct msm_otg_platform_data - platform device data
|
||||
* for msm_otg driver.
|
||||
* @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as
|
||||
* "do not overwrite default vaule at this address".
|
||||
* @phy_init_sz: PHY configuration sequence size.
|
||||
* @vbus_power: VBUS power on/off routine.
|
||||
* @power_budget: VBUS power budget in mA (0 will be treated as 500mA).
|
||||
* @mode: Supported mode (OTG/peripheral/host).
|
||||
* @otg_control: OTG switch controlled by user/Id pin
|
||||
*/
|
||||
struct msm_otg_platform_data {
|
||||
int *phy_init_seq;
|
||||
int phy_init_sz;
|
||||
void (*vbus_power)(bool on);
|
||||
unsigned power_budget;
|
||||
enum usb_dr_mode mode;
|
||||
enum otg_control_type otg_control;
|
||||
enum msm_usb_phy_type phy_type;
|
||||
void (*setup_gpio)(enum usb_otg_state state);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct msm_usb_cable - structure for exteternal connector cable
|
||||
* state tracking
|
||||
* @nb: hold event notification callback
|
||||
* @conn: used for notification registration
|
||||
*/
|
||||
struct msm_usb_cable {
|
||||
struct notifier_block nb;
|
||||
struct extcon_dev *extcon;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct msm_otg: OTG driver data. Shared by HCD and DCD.
|
||||
* @otg: USB OTG Transceiver structure.
|
||||
* @pdata: otg device platform data.
|
||||
* @irq: IRQ number assigned for HSUSB controller.
|
||||
* @clk: clock struct of usb_hs_clk.
|
||||
* @pclk: clock struct of usb_hs_pclk.
|
||||
* @core_clk: clock struct of usb_hs_core_clk.
|
||||
* @regs: ioremapped register base address.
|
||||
* @inputs: OTG state machine inputs(Id, SessValid etc).
|
||||
* @sm_work: OTG state machine work.
|
||||
* @in_lpm: indicates low power mode (LPM) state.
|
||||
* @async_int: Async interrupt arrived.
|
||||
* @cur_power: The amount of mA available from downstream port.
|
||||
* @chg_work: Charger detection work.
|
||||
* @chg_state: The state of charger detection process.
|
||||
* @chg_type: The type of charger attached.
|
||||
* @dcd_retires: The retry count used to track Data contact
|
||||
* detection process.
|
||||
* @manual_pullup: true if VBUS is not routed to USB controller/phy
|
||||
* and controller driver therefore enables pull-up explicitly before
|
||||
* starting controller using usbcmd run/stop bit.
|
||||
* @vbus: VBUS signal state trakining, using extcon framework
|
||||
* @id: ID signal state trakining, using extcon framework
|
||||
* @switch_gpio: Descriptor for GPIO used to control external Dual
|
||||
* SPDT USB Switch.
|
||||
* @reboot: Used to inform the driver to route USB D+/D- line to Device
|
||||
* connector
|
||||
*/
|
||||
struct msm_otg {
|
||||
struct usb_phy phy;
|
||||
struct msm_otg_platform_data *pdata;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct clk *core_clk;
|
||||
void __iomem *regs;
|
||||
#define ID 0
|
||||
#define B_SESS_VLD 1
|
||||
unsigned long inputs;
|
||||
struct work_struct sm_work;
|
||||
atomic_t in_lpm;
|
||||
int async_int;
|
||||
unsigned cur_power;
|
||||
int phy_number;
|
||||
struct delayed_work chg_work;
|
||||
enum usb_chg_state chg_state;
|
||||
enum usb_chg_type chg_type;
|
||||
u8 dcd_retries;
|
||||
struct regulator *v3p3;
|
||||
struct regulator *v1p8;
|
||||
struct regulator *vddcx;
|
||||
|
||||
struct reset_control *phy_rst;
|
||||
struct reset_control *link_rst;
|
||||
int vdd_levels[3];
|
||||
|
||||
bool manual_pullup;
|
||||
|
||||
struct msm_usb_cable vbus;
|
||||
struct msm_usb_cable id;
|
||||
|
||||
struct gpio_desc *switch_gpio;
|
||||
struct notifier_block reboot;
|
||||
};
|
||||
|
||||
#define MSM_USB_BASE (motg->regs)
|
||||
#define DRIVER_NAME "msm_otg"
|
||||
|
||||
|
@ -148,7 +148,7 @@ static int omap_otg_remove(struct platform_device *pdev)
|
||||
struct otg_device *otg_dev = platform_get_drvdata(pdev);
|
||||
struct extcon_dev *edev = otg_dev->extcon;
|
||||
|
||||
extcon_unregister_notifier(edev, EXTCON_USB_HOST,&otg_dev->id_nb);
|
||||
extcon_unregister_notifier(edev, EXTCON_USB_HOST, &otg_dev->id_nb);
|
||||
extcon_unregister_notifier(edev, EXTCON_USB, &otg_dev->vbus_nb);
|
||||
|
||||
return 0;
|
||||
|
@ -697,7 +697,7 @@ probe_end_fifo_exit:
|
||||
probe_end_pipe_exit:
|
||||
usbhs_pipe_remove(priv);
|
||||
|
||||
dev_info(&pdev->dev, "probe failed\n");
|
||||
dev_info(&pdev->dev, "probe failed (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -810,20 +810,27 @@ static void xfer_work(struct work_struct *work)
|
||||
{
|
||||
struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
|
||||
struct usbhs_fifo *fifo;
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
|
||||
struct dma_chan *chan;
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
enum dma_transfer_direction dir;
|
||||
unsigned long flags;
|
||||
|
||||
usbhs_lock(priv, flags);
|
||||
fifo = usbhs_pipe_to_fifo(pipe);
|
||||
if (!fifo)
|
||||
goto xfer_work_end;
|
||||
|
||||
chan = usbhsf_dma_chan_get(fifo, pkt);
|
||||
dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
|
||||
|
||||
desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
|
||||
pkt->trans, dir,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
return;
|
||||
goto xfer_work_end;
|
||||
|
||||
desc->callback = usbhsf_dma_complete;
|
||||
desc->callback_param = pipe;
|
||||
@ -831,7 +838,7 @@ static void xfer_work(struct work_struct *work)
|
||||
pkt->cookie = dmaengine_submit(desc);
|
||||
if (pkt->cookie < 0) {
|
||||
dev_err(dev, "Failed to submit dma descriptor\n");
|
||||
return;
|
||||
goto xfer_work_end;
|
||||
}
|
||||
|
||||
dev_dbg(dev, " %s %d (%d/ %d)\n",
|
||||
@ -842,6 +849,9 @@ static void xfer_work(struct work_struct *work)
|
||||
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
|
||||
dma_async_issue_pending(chan);
|
||||
usbhs_pipe_enable(pipe);
|
||||
|
||||
xfer_work_end:
|
||||
usbhs_unlock(priv, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user