forked from Minki/linux
Merge 3.3-rc7 into usb-next
This resolves the conflict with drivers/usb/host/ehci-fsl.h that happened with changes in Linus's and this branch at the same time. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
commit
f7a0d426f3
@ -182,3 +182,14 @@ Description:
|
||||
USB2 hardware LPM is enabled for the device. Developer can
|
||||
write y/Y/1 or n/N/0 to the file to enable/disable the
|
||||
feature.
|
||||
|
||||
What: /sys/bus/usb/devices/.../removable
|
||||
Date: February 2012
|
||||
Contact: Matthew Garrett <mjg@redhat.com>
|
||||
Description:
|
||||
Some information about whether a given USB device is
|
||||
physically fixed to the platform can be inferred from a
|
||||
combination of hub decriptor bits and platform-specific data
|
||||
such as ACPI. This file will read either "removable" or
|
||||
"fixed" if the information is available, and "unknown"
|
||||
otherwise.
|
@ -158,7 +158,7 @@ static int devboard_usbh1_hw_init(struct platform_device *pdev)
|
||||
#define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B)
|
||||
#define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE)
|
||||
|
||||
static int devboard_isp1105_init(struct otg_transceiver *otg)
|
||||
static int devboard_isp1105_init(struct usb_phy *otg)
|
||||
{
|
||||
int ret = gpio_request(USBH1_MODE, "usbh1-mode");
|
||||
if (ret)
|
||||
@ -177,7 +177,7 @@ static int devboard_isp1105_init(struct otg_transceiver *otg)
|
||||
}
|
||||
|
||||
|
||||
static int devboard_isp1105_set_vbus(struct otg_transceiver *otg, bool on)
|
||||
static int devboard_isp1105_set_vbus(struct usb_otg *otg, bool on)
|
||||
{
|
||||
if (on)
|
||||
gpio_set_value(USBH1_VBUSEN_B, 0);
|
||||
@ -194,18 +194,24 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = {
|
||||
|
||||
static int __init devboard_usbh1_init(void)
|
||||
{
|
||||
struct otg_transceiver *otg;
|
||||
struct usb_phy *phy;
|
||||
struct platform_device *pdev;
|
||||
|
||||
otg = kzalloc(sizeof(*otg), GFP_KERNEL);
|
||||
if (!otg)
|
||||
phy = kzalloc(sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
otg->label = "ISP1105";
|
||||
otg->init = devboard_isp1105_init;
|
||||
otg->set_vbus = devboard_isp1105_set_vbus;
|
||||
phy->otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
|
||||
if (!phy->otg) {
|
||||
kfree(phy);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usbh1_pdata.otg = otg;
|
||||
phy->label = "ISP1105";
|
||||
phy->init = devboard_isp1105_init;
|
||||
phy->otg->set_vbus = devboard_isp1105_set_vbus;
|
||||
|
||||
usbh1_pdata.otg = phy;
|
||||
|
||||
pdev = imx31_add_mxc_ehci_hs(1, &usbh1_pdata);
|
||||
if (IS_ERR(pdev))
|
||||
|
@ -272,7 +272,7 @@ static int marxbot_usbh1_hw_init(struct platform_device *pdev)
|
||||
#define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B)
|
||||
#define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE)
|
||||
|
||||
static int marxbot_isp1105_init(struct otg_transceiver *otg)
|
||||
static int marxbot_isp1105_init(struct usb_phy *otg)
|
||||
{
|
||||
int ret = gpio_request(USBH1_MODE, "usbh1-mode");
|
||||
if (ret)
|
||||
@ -291,7 +291,7 @@ static int marxbot_isp1105_init(struct otg_transceiver *otg)
|
||||
}
|
||||
|
||||
|
||||
static int marxbot_isp1105_set_vbus(struct otg_transceiver *otg, bool on)
|
||||
static int marxbot_isp1105_set_vbus(struct usb_otg *otg, bool on)
|
||||
{
|
||||
if (on)
|
||||
gpio_set_value(USBH1_VBUSEN_B, 0);
|
||||
@ -308,18 +308,24 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = {
|
||||
|
||||
static int __init marxbot_usbh1_init(void)
|
||||
{
|
||||
struct otg_transceiver *otg;
|
||||
struct usb_phy *phy;
|
||||
struct platform_device *pdev;
|
||||
|
||||
otg = kzalloc(sizeof(*otg), GFP_KERNEL);
|
||||
if (!otg)
|
||||
phy = kzalloc(sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
otg->label = "ISP1105";
|
||||
otg->init = marxbot_isp1105_init;
|
||||
otg->set_vbus = marxbot_isp1105_set_vbus;
|
||||
phy->otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
|
||||
if (!phy->otg) {
|
||||
kfree(phy);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usbh1_pdata.otg = otg;
|
||||
phy->label = "ISP1105";
|
||||
phy->init = marxbot_isp1105_init;
|
||||
phy->otg->set_vbus = marxbot_isp1105_set_vbus;
|
||||
|
||||
usbh1_pdata.otg = phy;
|
||||
|
||||
pdev = imx31_add_mxc_ehci_hs(1, &usbh1_pdata);
|
||||
if (IS_ERR(pdev))
|
||||
|
@ -33,7 +33,7 @@ struct pxa3xx_u2d_ulpi {
|
||||
struct clk *clk;
|
||||
void __iomem *mmio_base;
|
||||
|
||||
struct otg_transceiver *otg;
|
||||
struct usb_phy *otg;
|
||||
unsigned int ulpi_mode;
|
||||
};
|
||||
|
||||
@ -79,7 +79,7 @@ static int pxa310_ulpi_poll(void)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg)
|
||||
static int pxa310_ulpi_read(struct usb_phy *otg, u32 reg)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -98,7 +98,7 @@ static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg)
|
||||
return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA;
|
||||
}
|
||||
|
||||
static int pxa310_ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
|
||||
static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
|
||||
{
|
||||
if (pxa310_ulpi_get_phymode() != SYNCH) {
|
||||
pr_warning("%s: PHY is not in SYNCH mode!\n", __func__);
|
||||
@ -111,7 +111,7 @@ static int pxa310_ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
|
||||
return pxa310_ulpi_poll();
|
||||
}
|
||||
|
||||
struct otg_io_access_ops pxa310_ulpi_access_ops = {
|
||||
struct usb_phy_io_ops pxa310_ulpi_access_ops = {
|
||||
.read = pxa310_ulpi_read,
|
||||
.write = pxa310_ulpi_write,
|
||||
};
|
||||
@ -139,19 +139,19 @@ static int pxa310_start_otg_host_transcvr(struct usb_bus *host)
|
||||
|
||||
pxa310_otg_transceiver_rtsm();
|
||||
|
||||
err = otg_init(u2d->otg);
|
||||
err = usb_phy_init(u2d->otg);
|
||||
if (err) {
|
||||
pr_err("OTG transceiver init failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = otg_set_vbus(u2d->otg, 1);
|
||||
err = otg_set_vbus(u2d->otg->otg, 1);
|
||||
if (err) {
|
||||
pr_err("OTG transceiver VBUS set failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = otg_set_host(u2d->otg, host);
|
||||
err = otg_set_host(u2d->otg->otg, host);
|
||||
if (err)
|
||||
pr_err("OTG transceiver Host mode set failed");
|
||||
|
||||
@ -189,9 +189,9 @@ static void pxa310_stop_otg_hc(void)
|
||||
{
|
||||
pxa310_otg_transceiver_rtsm();
|
||||
|
||||
otg_set_host(u2d->otg, NULL);
|
||||
otg_set_vbus(u2d->otg, 0);
|
||||
otg_shutdown(u2d->otg);
|
||||
otg_set_host(u2d->otg->otg, NULL);
|
||||
otg_set_vbus(u2d->otg->otg, 0);
|
||||
usb_phy_shutdown(u2d->otg);
|
||||
}
|
||||
|
||||
static void pxa310_u2d_setup_otg_hc(void)
|
||||
|
@ -58,7 +58,7 @@ struct tegra_usb_phy {
|
||||
struct clk *pad_clk;
|
||||
enum tegra_usb_phy_mode mode;
|
||||
void *config;
|
||||
struct otg_transceiver *ulpi;
|
||||
struct usb_phy *ulpi;
|
||||
};
|
||||
|
||||
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
|
||||
|
@ -608,13 +608,13 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
||||
writel(val, base + ULPI_TIMING_CTRL_1);
|
||||
|
||||
/* Fix VbusInvalid due to floating VBUS */
|
||||
ret = otg_io_write(phy->ulpi, 0x40, 0x08);
|
||||
ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
|
||||
if (ret) {
|
||||
pr_err("%s: ulpi write failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = otg_io_write(phy->ulpi, 0x80, 0x0B);
|
||||
ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
|
||||
if (ret) {
|
||||
pr_err("%s: ulpi write failed\n", __func__);
|
||||
return ret;
|
||||
|
@ -44,7 +44,7 @@ struct mxc_usbh_platform_data {
|
||||
int (*exit)(struct platform_device *pdev);
|
||||
|
||||
unsigned int portsc;
|
||||
struct otg_transceiver *otg;
|
||||
struct usb_phy *otg;
|
||||
};
|
||||
|
||||
int mx51_initialize_usb_hw(int port, unsigned int flags);
|
||||
|
@ -2,15 +2,15 @@
|
||||
#define __MACH_ULPI_H
|
||||
|
||||
#ifdef CONFIG_USB_ULPI
|
||||
struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags);
|
||||
struct usb_phy *imx_otg_ulpi_create(unsigned int flags);
|
||||
#else
|
||||
static inline struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags)
|
||||
static inline struct usb_phy *imx_otg_ulpi_create(unsigned int flags)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern struct otg_io_access_ops mxc_ulpi_access_ops;
|
||||
extern struct usb_phy_io_ops mxc_ulpi_access_ops;
|
||||
|
||||
#endif /* __MACH_ULPI_H */
|
||||
|
||||
|
@ -58,7 +58,7 @@ static int ulpi_poll(void __iomem *view, u32 bit)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int ulpi_read(struct otg_transceiver *otg, u32 reg)
|
||||
static int ulpi_read(struct usb_phy *otg, u32 reg)
|
||||
{
|
||||
int ret;
|
||||
void __iomem *view = otg->io_priv;
|
||||
@ -84,7 +84,7 @@ static int ulpi_read(struct otg_transceiver *otg, u32 reg)
|
||||
return (__raw_readl(view) >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK;
|
||||
}
|
||||
|
||||
static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
|
||||
static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
|
||||
{
|
||||
int ret;
|
||||
void __iomem *view = otg->io_priv;
|
||||
@ -106,13 +106,13 @@ static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
|
||||
return ulpi_poll(view, ULPIVW_RUN);
|
||||
}
|
||||
|
||||
struct otg_io_access_ops mxc_ulpi_access_ops = {
|
||||
struct usb_phy_io_ops mxc_ulpi_access_ops = {
|
||||
.read = ulpi_read,
|
||||
.write = ulpi_write,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(mxc_ulpi_access_ops);
|
||||
|
||||
struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags)
|
||||
struct usb_phy *imx_otg_ulpi_create(unsigned int flags)
|
||||
{
|
||||
return otg_ulpi_create(&mxc_ulpi_access_ops, flags);
|
||||
}
|
||||
|
@ -117,43 +117,6 @@
|
||||
|
||||
#define UB_SENSE_SIZE 18
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/* command block wrapper */
|
||||
struct bulk_cb_wrap {
|
||||
__le32 Signature; /* contains 'USBC' */
|
||||
u32 Tag; /* unique per command id */
|
||||
__le32 DataTransferLength; /* size of data */
|
||||
u8 Flags; /* direction in bit 0 */
|
||||
u8 Lun; /* LUN */
|
||||
u8 Length; /* of of the CDB */
|
||||
u8 CDB[UB_MAX_CDB_SIZE]; /* max command */
|
||||
};
|
||||
|
||||
#define US_BULK_CB_WRAP_LEN 31
|
||||
#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
|
||||
#define US_BULK_FLAG_IN 1
|
||||
#define US_BULK_FLAG_OUT 0
|
||||
|
||||
/* command status wrapper */
|
||||
struct bulk_cs_wrap {
|
||||
__le32 Signature; /* should = 'USBS' */
|
||||
u32 Tag; /* same as original command */
|
||||
__le32 Residue; /* amount not transferred */
|
||||
u8 Status; /* see below */
|
||||
};
|
||||
|
||||
#define US_BULK_CS_WRAP_LEN 13
|
||||
#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
|
||||
#define US_BULK_STAT_OK 0
|
||||
#define US_BULK_STAT_FAIL 1
|
||||
#define US_BULK_STAT_PHASE 2
|
||||
|
||||
/* bulk-only class specific requests */
|
||||
#define US_BULK_RESET_REQUEST 0xff
|
||||
#define US_BULK_GET_MAX_LUN 0xfe
|
||||
|
||||
/*
|
||||
*/
|
||||
struct ub_dev;
|
||||
|
@ -398,6 +398,27 @@ config USB_NET_KALMIA
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called kalmia.
|
||||
|
||||
config USB_NET_QMI_WWAN
|
||||
tristate "QMI WWAN driver for Qualcomm MSM based 3G and LTE modems"
|
||||
depends on USB_USBNET
|
||||
help
|
||||
Support WWAN LTE/3G devices based on Qualcomm Mobile Data Modem
|
||||
(MDM) chipsets. Examples of such devices are
|
||||
* Huawei E392/E398
|
||||
|
||||
This driver will only drive the ethernet part of the chips.
|
||||
The devices require additional configuration to be usable.
|
||||
Multiple management interfaces with linux drivers are
|
||||
available:
|
||||
|
||||
* option: AT commands on /dev/ttyUSBx
|
||||
* cdc-wdm: Qualcomm MSM Interface (QMI) protocol on /dev/cdc-wdmx
|
||||
|
||||
A modem manager with support for QMI is recommended.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called qmi_wwan.
|
||||
|
||||
config USB_HSO
|
||||
tristate "Option USB High Speed Mobile Devices"
|
||||
depends on USB && RFKILL
|
||||
@ -461,4 +482,5 @@ config USB_VL600
|
||||
|
||||
http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
|
||||
|
||||
|
||||
endmenu
|
||||
|
@ -29,4 +29,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
|
||||
obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
|
||||
obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
|
||||
obj-$(CONFIG_USB_VL600) += lg-vl600.o
|
||||
obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
|
||||
|
||||
|
444
drivers/net/usb/qmi_wwan.c
Normal file
444
drivers/net/usb/qmi_wwan.c
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Bjørn Mork <bjorn@mork.no>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb/usbnet.h>
|
||||
#include <linux/usb/cdc-wdm.h>
|
||||
|
||||
/* The name of the CDC Device Management driver */
|
||||
#define DM_DRIVER "cdc_wdm"
|
||||
|
||||
/*
|
||||
* This driver supports wwan (3G/LTE/?) devices using a vendor
|
||||
* specific management protocol called Qualcomm MSM Interface (QMI) -
|
||||
* in addition to the more common AT commands over serial interface
|
||||
* management
|
||||
*
|
||||
* QMI is wrapped in CDC, using CDC encapsulated commands on the
|
||||
* control ("master") interface of a two-interface CDC Union
|
||||
* resembling standard CDC ECM. The devices do not use the control
|
||||
* interface for any other CDC messages. Most likely because the
|
||||
* management protocol is used in place of the standard CDC
|
||||
* notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE
|
||||
*
|
||||
* Handling a protocol like QMI is out of the scope for any driver.
|
||||
* It can be exported as a character device using the cdc-wdm driver,
|
||||
* which will enable userspace applications ("modem managers") to
|
||||
* handle it. This may be required to use the network interface
|
||||
* provided by the driver.
|
||||
*
|
||||
* These devices may alternatively/additionally be configured using AT
|
||||
* commands on any of the serial interfaces driven by the option driver
|
||||
*
|
||||
* This driver binds only to the data ("slave") interface to enable
|
||||
* the cdc-wdm driver to bind to the control interface. It still
|
||||
* parses the CDC functional descriptors on the control interface to
|
||||
* a) verify that this is indeed a handled interface (CDC Union
|
||||
* header lists it as slave)
|
||||
* b) get MAC address and other ethernet config from the CDC Ethernet
|
||||
* header
|
||||
* c) enable user bind requests against the control interface, which
|
||||
* is the common way to bind to CDC Ethernet Control Model type
|
||||
* interfaces
|
||||
* d) provide a hint to the user about which interface is the
|
||||
* corresponding management interface
|
||||
*/
|
||||
|
||||
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int status = -1;
|
||||
struct usb_interface *control = NULL;
|
||||
u8 *buf = intf->cur_altsetting->extra;
|
||||
int len = intf->cur_altsetting->extralen;
|
||||
struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
|
||||
struct usb_cdc_union_desc *cdc_union = NULL;
|
||||
struct usb_cdc_ether_desc *cdc_ether = NULL;
|
||||
u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE;
|
||||
u32 found = 0;
|
||||
atomic_t *pmcount = (void *)&dev->data[1];
|
||||
|
||||
atomic_set(pmcount, 0);
|
||||
|
||||
/*
|
||||
* assume a data interface has no additional descriptors and
|
||||
* that the control and data interface are numbered
|
||||
* consecutively - this holds for the Huawei device at least
|
||||
*/
|
||||
if (len == 0 && desc->bInterfaceNumber > 0) {
|
||||
control = usb_ifnum_to_if(dev->udev, desc->bInterfaceNumber - 1);
|
||||
if (!control)
|
||||
goto err;
|
||||
|
||||
buf = control->cur_altsetting->extra;
|
||||
len = control->cur_altsetting->extralen;
|
||||
dev_dbg(&intf->dev, "guessing \"control\" => %s, \"data\" => this\n",
|
||||
dev_name(&control->dev));
|
||||
}
|
||||
|
||||
while (len > 3) {
|
||||
struct usb_descriptor_header *h = (void *)buf;
|
||||
|
||||
/* ignore any misplaced descriptors */
|
||||
if (h->bDescriptorType != USB_DT_CS_INTERFACE)
|
||||
goto next_desc;
|
||||
|
||||
/* buf[2] is CDC descriptor subtype */
|
||||
switch (buf[2]) {
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (found & 1 << USB_CDC_HEADER_TYPE) {
|
||||
dev_dbg(&intf->dev, "extra CDC header\n");
|
||||
goto err;
|
||||
}
|
||||
if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
|
||||
dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_UNION_TYPE:
|
||||
if (found & 1 << USB_CDC_UNION_TYPE) {
|
||||
dev_dbg(&intf->dev, "extra CDC union\n");
|
||||
goto err;
|
||||
}
|
||||
if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
|
||||
dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength);
|
||||
goto err;
|
||||
}
|
||||
cdc_union = (struct usb_cdc_union_desc *)buf;
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (found & 1 << USB_CDC_ETHERNET_TYPE) {
|
||||
dev_dbg(&intf->dev, "extra CDC ether\n");
|
||||
goto err;
|
||||
}
|
||||
if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
|
||||
dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength);
|
||||
goto err;
|
||||
}
|
||||
cdc_ether = (struct usb_cdc_ether_desc *)buf;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remember which CDC functional descriptors we've seen. Works
|
||||
* for all types we care about, of which USB_CDC_ETHERNET_TYPE
|
||||
* (0x0f) is the highest numbered
|
||||
*/
|
||||
if (buf[2] < 32)
|
||||
found |= 1 << buf[2];
|
||||
|
||||
next_desc:
|
||||
len -= h->bLength;
|
||||
buf += h->bLength;
|
||||
}
|
||||
|
||||
/* did we find all the required ones? */
|
||||
if ((found & required) != required) {
|
||||
dev_err(&intf->dev, "CDC functional descriptors missing\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* give the user a helpful hint if trying to bind to the wrong interface */
|
||||
if (cdc_union && desc->bInterfaceNumber == cdc_union->bMasterInterface0) {
|
||||
dev_err(&intf->dev, "leaving \"control\" interface for " DM_DRIVER " - try binding to %s instead!\n",
|
||||
dev_name(&usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0)->dev));
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* errors aren't fatal - we can live with the dynamic address */
|
||||
if (cdc_ether) {
|
||||
dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize);
|
||||
usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress);
|
||||
}
|
||||
|
||||
/* success! point the user to the management interface */
|
||||
if (control)
|
||||
dev_info(&intf->dev, "Use \"" DM_DRIVER "\" for QMI interface %s\n",
|
||||
dev_name(&control->dev));
|
||||
|
||||
/* XXX: add a sysfs symlink somewhere to help management applications find it? */
|
||||
|
||||
/* collect bulk endpoints now that we know intf == "data" interface */
|
||||
status = usbnet_get_endpoints(dev, intf);
|
||||
|
||||
err:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* using a counter to merge subdriver requests with our own into a combined state */
|
||||
static int qmi_wwan_manage_power(struct usbnet *dev, int on)
|
||||
{
|
||||
atomic_t *pmcount = (void *)&dev->data[1];
|
||||
int rv = 0;
|
||||
|
||||
dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(pmcount), on);
|
||||
|
||||
if ((on && atomic_add_return(1, pmcount) == 1) || (!on && atomic_dec_and_test(pmcount))) {
|
||||
/* need autopm_get/put here to ensure the usbcore sees the new value */
|
||||
rv = usb_autopm_get_interface(dev->intf);
|
||||
if (rv < 0)
|
||||
goto err;
|
||||
dev->intf->needs_remote_wakeup = on;
|
||||
usb_autopm_put_interface(dev->intf);
|
||||
}
|
||||
err:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on)
|
||||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
return qmi_wwan_manage_power(dev, on);
|
||||
}
|
||||
|
||||
/* Some devices combine the "control" and "data" functions into a
|
||||
* single interface with all three endpoints: interrupt + bulk in and
|
||||
* out
|
||||
*
|
||||
* Setting up cdc-wdm as a subdriver owning the interrupt endpoint
|
||||
* will let it provide userspace access to the encapsulated QMI
|
||||
* protocol without interfering with the usbnet operations.
|
||||
*/
|
||||
static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int rv;
|
||||
struct usb_driver *subdriver = NULL;
|
||||
atomic_t *pmcount = (void *)&dev->data[1];
|
||||
|
||||
atomic_set(pmcount, 0);
|
||||
|
||||
/* collect all three endpoints */
|
||||
rv = usbnet_get_endpoints(dev, intf);
|
||||
if (rv < 0)
|
||||
goto err;
|
||||
|
||||
/* require interrupt endpoint for subdriver */
|
||||
if (!dev->status) {
|
||||
rv = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
subdriver = usb_cdc_wdm_register(intf, &dev->status->desc, 512, &qmi_wwan_cdc_wdm_manage_power);
|
||||
if (IS_ERR(subdriver)) {
|
||||
rv = PTR_ERR(subdriver);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* can't let usbnet use the interrupt endpoint */
|
||||
dev->status = NULL;
|
||||
|
||||
/* save subdriver struct for suspend/resume wrappers */
|
||||
dev->data[0] = (unsigned long)subdriver;
|
||||
|
||||
err:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Gobi devices uses identical class/protocol codes for all interfaces regardless
|
||||
* of function. Some of these are CDC ACM like and have the exact same endpoints
|
||||
* we are looking for. This leaves two possible strategies for identifying the
|
||||
* correct interface:
|
||||
* a) hardcoding interface number, or
|
||||
* b) use the fact that the wwan interface is the only one lacking additional
|
||||
* (CDC functional) descriptors
|
||||
*
|
||||
* Let's see if we can get away with the generic b) solution.
|
||||
*/
|
||||
static int qmi_wwan_bind_gobi(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int rv = -EINVAL;
|
||||
|
||||
/* ignore any interface with additional descriptors */
|
||||
if (intf->cur_altsetting->extralen)
|
||||
goto err;
|
||||
|
||||
rv = qmi_wwan_bind_shared(dev, intf);
|
||||
err:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
struct usb_driver *subdriver = (void *)dev->data[0];
|
||||
|
||||
if (subdriver && subdriver->disconnect)
|
||||
subdriver->disconnect(intf);
|
||||
|
||||
dev->data[0] = (unsigned long)NULL;
|
||||
}
|
||||
|
||||
/* suspend/resume wrappers calling both usbnet and the cdc-wdm
|
||||
* subdriver if present.
|
||||
*
|
||||
* NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide
|
||||
* wrappers for those without adding usbnet reset support first.
|
||||
*/
|
||||
static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
struct usb_driver *subdriver = (void *)dev->data[0];
|
||||
int ret;
|
||||
|
||||
ret = usbnet_suspend(intf, message);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (subdriver && subdriver->suspend)
|
||||
ret = subdriver->suspend(intf, message);
|
||||
if (ret < 0)
|
||||
usbnet_resume(intf);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_wwan_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
struct usb_driver *subdriver = (void *)dev->data[0];
|
||||
int ret = 0;
|
||||
|
||||
if (subdriver && subdriver->resume)
|
||||
ret = subdriver->resume(intf);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
ret = usbnet_resume(intf);
|
||||
if (ret < 0 && subdriver && subdriver->resume && subdriver->suspend)
|
||||
subdriver->suspend(intf, PMSG_SUSPEND);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct driver_info qmi_wwan_info = {
|
||||
.description = "QMI speaking wwan device",
|
||||
.flags = FLAG_WWAN,
|
||||
.bind = qmi_wwan_bind,
|
||||
.manage_power = qmi_wwan_manage_power,
|
||||
};
|
||||
|
||||
static const struct driver_info qmi_wwan_shared = {
|
||||
.description = "QMI speaking wwan device with combined interface",
|
||||
.flags = FLAG_WWAN,
|
||||
.bind = qmi_wwan_bind_shared,
|
||||
.unbind = qmi_wwan_unbind_shared,
|
||||
.manage_power = qmi_wwan_manage_power,
|
||||
};
|
||||
|
||||
static const struct driver_info qmi_wwan_gobi = {
|
||||
.description = "Qualcomm Gobi wwan/QMI device",
|
||||
.flags = FLAG_WWAN,
|
||||
.bind = qmi_wwan_bind_gobi,
|
||||
.unbind = qmi_wwan_unbind_shared,
|
||||
.manage_power = qmi_wwan_manage_power,
|
||||
};
|
||||
|
||||
#define HUAWEI_VENDOR_ID 0x12D1
|
||||
#define QMI_GOBI_DEVICE(vend, prod) \
|
||||
USB_DEVICE(vend, prod), \
|
||||
.driver_info = (unsigned long)&qmi_wwan_gobi
|
||||
|
||||
static const struct usb_device_id products[] = {
|
||||
{ /* Huawei E392, E398 and possibly others sharing both device id and more... */
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = HUAWEI_VENDOR_ID,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 8, /* NOTE: This is the *slave* interface of the CDC Union! */
|
||||
.driver_info = (unsigned long)&qmi_wwan_info,
|
||||
},
|
||||
{ /* Huawei E392, E398 and possibly others in "Windows mode"
|
||||
* using a combined control and data interface without any CDC
|
||||
* functional descriptors
|
||||
*/
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = HUAWEI_VENDOR_ID,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 17,
|
||||
.driver_info = (unsigned long)&qmi_wwan_shared,
|
||||
},
|
||||
{ /* Pantech UML290 */
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = 0x106c,
|
||||
.idProduct = 0x3718,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0xf0,
|
||||
.bInterfaceProtocol = 0xff,
|
||||
.driver_info = (unsigned long)&qmi_wwan_shared,
|
||||
},
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
|
||||
{QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
|
||||
{QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
|
||||
{QMI_GOBI_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9001)}, /* Generic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9002)}, /* Generic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9202)}, /* Generic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
|
||||
{QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
|
||||
{QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
|
||||
{QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
|
||||
{QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
|
||||
{QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
|
||||
{ } /* END */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver qmi_wwan_driver = {
|
||||
.name = "qmi_wwan",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = qmi_wwan_suspend,
|
||||
.resume = qmi_wwan_resume,
|
||||
.reset_resume = qmi_wwan_resume,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
static int __init qmi_wwan_init(void)
|
||||
{
|
||||
return usb_register(&qmi_wwan_driver);
|
||||
}
|
||||
module_init(qmi_wwan_init);
|
||||
|
||||
static void __exit qmi_wwan_exit(void)
|
||||
{
|
||||
usb_deregister(&qmi_wwan_driver);
|
||||
}
|
||||
module_exit(qmi_wwan_exit);
|
||||
|
||||
MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
|
||||
MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -56,7 +56,7 @@ static u16 isp170x_id[] = {
|
||||
struct isp1704_charger {
|
||||
struct device *dev;
|
||||
struct power_supply psy;
|
||||
struct otg_transceiver *otg;
|
||||
struct usb_phy *phy;
|
||||
struct notifier_block nb;
|
||||
struct work_struct work;
|
||||
|
||||
@ -71,6 +71,16 @@ struct isp1704_charger {
|
||||
unsigned max_power;
|
||||
};
|
||||
|
||||
static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
|
||||
{
|
||||
return usb_phy_io_read(isp->phy, reg);
|
||||
}
|
||||
|
||||
static inline int isp1704_write(struct isp1704_charger *isp, u32 val, u32 reg)
|
||||
{
|
||||
return usb_phy_io_write(isp->phy, val, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable/enable the power from the isp1704 if a function for it
|
||||
* has been provided with platform data.
|
||||
@ -97,31 +107,31 @@ static inline int isp1704_charger_type(struct isp1704_charger *isp)
|
||||
u8 otg_ctrl;
|
||||
int type = POWER_SUPPLY_TYPE_USB_DCP;
|
||||
|
||||
func_ctrl = otg_io_read(isp->otg, ULPI_FUNC_CTRL);
|
||||
otg_ctrl = otg_io_read(isp->otg, ULPI_OTG_CTRL);
|
||||
func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
|
||||
otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
|
||||
|
||||
/* disable pulldowns */
|
||||
reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
|
||||
otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), reg);
|
||||
isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
|
||||
|
||||
/* full speed */
|
||||
otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
|
||||
isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
|
||||
ULPI_FUNC_CTRL_XCVRSEL_MASK);
|
||||
otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL),
|
||||
isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
|
||||
ULPI_FUNC_CTRL_FULL_SPEED);
|
||||
|
||||
/* Enable strong pull-up on DP (1.5K) and reset */
|
||||
reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
|
||||
otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), reg);
|
||||
isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
reg = otg_io_read(isp->otg, ULPI_DEBUG);
|
||||
reg = isp1704_read(isp, ULPI_DEBUG);
|
||||
if ((reg & 3) != 3)
|
||||
type = POWER_SUPPLY_TYPE_USB_CDP;
|
||||
|
||||
/* recover original state */
|
||||
otg_io_write(isp->otg, ULPI_FUNC_CTRL, func_ctrl);
|
||||
otg_io_write(isp->otg, ULPI_OTG_CTRL, otg_ctrl);
|
||||
isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
|
||||
isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
|
||||
|
||||
return type;
|
||||
}
|
||||
@ -136,28 +146,28 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp)
|
||||
u8 r;
|
||||
|
||||
/* Reset the transceiver */
|
||||
r = otg_io_read(isp->otg, ULPI_FUNC_CTRL);
|
||||
r = isp1704_read(isp, ULPI_FUNC_CTRL);
|
||||
r |= ULPI_FUNC_CTRL_RESET;
|
||||
otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
|
||||
isp1704_write(isp, ULPI_FUNC_CTRL, r);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
/* Set normal mode */
|
||||
r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
|
||||
otg_io_write(isp->otg, ULPI_FUNC_CTRL, r);
|
||||
isp1704_write(isp, ULPI_FUNC_CTRL, r);
|
||||
|
||||
/* Clear the DP and DM pull-down bits */
|
||||
r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
|
||||
otg_io_write(isp->otg, ULPI_CLR(ULPI_OTG_CTRL), r);
|
||||
isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
|
||||
|
||||
/* Enable strong pull-up on DP (1.5K) and reset */
|
||||
r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
|
||||
otg_io_write(isp->otg, ULPI_SET(ULPI_FUNC_CTRL), r);
|
||||
isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
/* Read the line state */
|
||||
if (!otg_io_read(isp->otg, ULPI_DEBUG)) {
|
||||
if (!isp1704_read(isp, ULPI_DEBUG)) {
|
||||
/* Disable strong pull-up on DP (1.5K) */
|
||||
otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
|
||||
isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
|
||||
ULPI_FUNC_CTRL_TERMSELECT);
|
||||
return 1;
|
||||
}
|
||||
@ -165,23 +175,23 @@ static inline int isp1704_charger_verify(struct isp1704_charger *isp)
|
||||
/* Is it a charger or PS/2 connection */
|
||||
|
||||
/* Enable weak pull-up resistor on DP */
|
||||
otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL),
|
||||
isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
|
||||
ISP1704_PWR_CTRL_DP_WKPU_EN);
|
||||
|
||||
/* Disable strong pull-up on DP (1.5K) */
|
||||
otg_io_write(isp->otg, ULPI_CLR(ULPI_FUNC_CTRL),
|
||||
isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
|
||||
ULPI_FUNC_CTRL_TERMSELECT);
|
||||
|
||||
/* Enable weak pull-down resistor on DM */
|
||||
otg_io_write(isp->otg, ULPI_SET(ULPI_OTG_CTRL),
|
||||
isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
|
||||
ULPI_OTG_CTRL_DM_PULLDOWN);
|
||||
|
||||
/* It's a charger if the line states are clear */
|
||||
if (!(otg_io_read(isp->otg, ULPI_DEBUG)))
|
||||
if (!(isp1704_read(isp, ULPI_DEBUG)))
|
||||
ret = 1;
|
||||
|
||||
/* Disable weak pull-up resistor on DP */
|
||||
otg_io_write(isp->otg, ULPI_CLR(ISP1704_PWR_CTRL),
|
||||
isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
|
||||
ISP1704_PWR_CTRL_DP_WKPU_EN);
|
||||
|
||||
return ret;
|
||||
@ -193,14 +203,14 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
|
||||
u8 pwr_ctrl;
|
||||
int ret = 0;
|
||||
|
||||
pwr_ctrl = otg_io_read(isp->otg, ISP1704_PWR_CTRL);
|
||||
pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
|
||||
|
||||
/* set SW control bit in PWR_CTRL register */
|
||||
otg_io_write(isp->otg, ISP1704_PWR_CTRL,
|
||||
isp1704_write(isp, ISP1704_PWR_CTRL,
|
||||
ISP1704_PWR_CTRL_SWCTRL);
|
||||
|
||||
/* enable manual charger detection */
|
||||
otg_io_write(isp->otg, ULPI_SET(ISP1704_PWR_CTRL),
|
||||
isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
|
||||
ISP1704_PWR_CTRL_SWCTRL
|
||||
| ISP1704_PWR_CTRL_DPVSRC_EN);
|
||||
usleep_range(1000, 2000);
|
||||
@ -208,7 +218,7 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
|
||||
timeout = jiffies + msecs_to_jiffies(300);
|
||||
do {
|
||||
/* Check if there is a charger */
|
||||
if (otg_io_read(isp->otg, ISP1704_PWR_CTRL)
|
||||
if (isp1704_read(isp, ISP1704_PWR_CTRL)
|
||||
& ISP1704_PWR_CTRL_VDAT_DET) {
|
||||
ret = isp1704_charger_verify(isp);
|
||||
break;
|
||||
@ -216,7 +226,7 @@ static inline int isp1704_charger_detect(struct isp1704_charger *isp)
|
||||
} while (!time_after(jiffies, timeout) && isp->online);
|
||||
|
||||
/* recover original state */
|
||||
otg_io_write(isp->otg, ISP1704_PWR_CTRL, pwr_ctrl);
|
||||
isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -264,8 +274,8 @@ static void isp1704_charger_work(struct work_struct *data)
|
||||
case POWER_SUPPLY_TYPE_USB:
|
||||
default:
|
||||
/* enable data pullups */
|
||||
if (isp->otg->gadget)
|
||||
usb_gadget_connect(isp->otg->gadget);
|
||||
if (isp->phy->otg->gadget)
|
||||
usb_gadget_connect(isp->phy->otg->gadget);
|
||||
}
|
||||
break;
|
||||
case USB_EVENT_NONE:
|
||||
@ -283,8 +293,8 @@ static void isp1704_charger_work(struct work_struct *data)
|
||||
* chargers. The pullups may be enabled elsewhere, so this can
|
||||
* not be the final solution.
|
||||
*/
|
||||
if (isp->otg->gadget)
|
||||
usb_gadget_disconnect(isp->otg->gadget);
|
||||
if (isp->phy->otg->gadget)
|
||||
usb_gadget_disconnect(isp->phy->otg->gadget);
|
||||
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
break;
|
||||
@ -364,11 +374,11 @@ static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
|
||||
int ret = -ENODEV;
|
||||
|
||||
/* Test ULPI interface */
|
||||
ret = otg_io_write(isp->otg, ULPI_SCRATCH, 0xaa);
|
||||
ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = otg_io_read(isp->otg, ULPI_SCRATCH);
|
||||
ret = isp1704_read(isp, ULPI_SCRATCH);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -376,13 +386,13 @@ static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
|
||||
return -ENODEV;
|
||||
|
||||
/* Verify the product and vendor id matches */
|
||||
vendor = otg_io_read(isp->otg, ULPI_VENDOR_ID_LOW);
|
||||
vendor |= otg_io_read(isp->otg, ULPI_VENDOR_ID_HIGH) << 8;
|
||||
vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
|
||||
vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
|
||||
if (vendor != NXP_VENDOR_ID)
|
||||
return -ENODEV;
|
||||
|
||||
product = otg_io_read(isp->otg, ULPI_PRODUCT_ID_LOW);
|
||||
product |= otg_io_read(isp->otg, ULPI_PRODUCT_ID_HIGH) << 8;
|
||||
product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
|
||||
product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
|
||||
if (product == isp170x_id[i]) {
|
||||
@ -405,8 +415,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
|
||||
if (!isp)
|
||||
return -ENOMEM;
|
||||
|
||||
isp->otg = otg_get_transceiver();
|
||||
if (!isp->otg)
|
||||
isp->phy = usb_get_transceiver();
|
||||
if (!isp->phy)
|
||||
goto fail0;
|
||||
|
||||
isp->dev = &pdev->dev;
|
||||
@ -429,14 +439,14 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
|
||||
goto fail1;
|
||||
|
||||
/*
|
||||
* REVISIT: using work in order to allow the otg notifications to be
|
||||
* REVISIT: using work in order to allow the usb notifications to be
|
||||
* made atomically in the future.
|
||||
*/
|
||||
INIT_WORK(&isp->work, isp1704_charger_work);
|
||||
|
||||
isp->nb.notifier_call = isp1704_notifier_call;
|
||||
|
||||
ret = otg_register_notifier(isp->otg, &isp->nb);
|
||||
ret = usb_register_notifier(isp->phy, &isp->nb);
|
||||
if (ret)
|
||||
goto fail2;
|
||||
|
||||
@ -449,13 +459,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
|
||||
* enumerated. The charger driver should be always loaded before any
|
||||
* gadget is loaded.
|
||||
*/
|
||||
if (isp->otg->gadget)
|
||||
usb_gadget_disconnect(isp->otg->gadget);
|
||||
if (isp->phy->otg->gadget)
|
||||
usb_gadget_disconnect(isp->phy->otg->gadget);
|
||||
|
||||
/* Detect charger if VBUS is valid (the cable was already plugged). */
|
||||
ret = otg_io_read(isp->otg, ULPI_USB_INT_STS);
|
||||
ret = isp1704_read(isp, ULPI_USB_INT_STS);
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
if ((ret & ULPI_INT_VBUS_VALID) && !isp->otg->default_a) {
|
||||
if ((ret & ULPI_INT_VBUS_VALID) && !isp->phy->otg->default_a) {
|
||||
isp->event = USB_EVENT_VBUS;
|
||||
schedule_work(&isp->work);
|
||||
}
|
||||
@ -464,7 +474,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
|
||||
fail2:
|
||||
power_supply_unregister(&isp->psy);
|
||||
fail1:
|
||||
otg_put_transceiver(isp->otg);
|
||||
usb_put_transceiver(isp->phy);
|
||||
fail0:
|
||||
kfree(isp);
|
||||
|
||||
@ -477,9 +487,9 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct isp1704_charger *isp = platform_get_drvdata(pdev);
|
||||
|
||||
otg_unregister_notifier(isp->otg, &isp->nb);
|
||||
usb_unregister_notifier(isp->phy, &isp->nb);
|
||||
power_supply_unregister(&isp->psy);
|
||||
otg_put_transceiver(isp->otg);
|
||||
usb_put_transceiver(isp->phy);
|
||||
isp1704_charger_set_power(isp, 0);
|
||||
kfree(isp);
|
||||
|
||||
|
@ -40,7 +40,7 @@ static struct timer_list polling_timer;
|
||||
static int polling;
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
static struct otg_transceiver *transceiver;
|
||||
static struct usb_phy *transceiver;
|
||||
static struct notifier_block otg_nb;
|
||||
#endif
|
||||
|
||||
@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
transceiver = otg_get_transceiver();
|
||||
transceiver = usb_get_transceiver();
|
||||
if (transceiver && !pdata->is_usb_online) {
|
||||
pdata->is_usb_online = otg_is_usb_online;
|
||||
}
|
||||
@ -375,7 +375,7 @@ static int pda_power_probe(struct platform_device *pdev)
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
if (transceiver && pdata->use_otg_notifier) {
|
||||
otg_nb.notifier_call = otg_handle_notification;
|
||||
ret = otg_register_notifier(transceiver, &otg_nb);
|
||||
ret = usb_register_notifier(transceiver, &otg_nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "failure to register otg notifier\n");
|
||||
goto otg_reg_notifier_failed;
|
||||
@ -409,7 +409,7 @@ usb_supply_failed:
|
||||
free_irq(ac_irq->start, &pda_psy_ac);
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
if (transceiver)
|
||||
otg_put_transceiver(transceiver);
|
||||
usb_put_transceiver(transceiver);
|
||||
#endif
|
||||
ac_irq_failed:
|
||||
if (pdata->is_ac_online)
|
||||
@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev)
|
||||
power_supply_unregister(&pda_psy_ac);
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
if (transceiver)
|
||||
otg_put_transceiver(transceiver);
|
||||
usb_put_transceiver(transceiver);
|
||||
#endif
|
||||
if (ac_draw) {
|
||||
regulator_put(ac_draw);
|
||||
|
@ -69,8 +69,8 @@ struct twl4030_bci {
|
||||
struct device *dev;
|
||||
struct power_supply ac;
|
||||
struct power_supply usb;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct notifier_block otg_nb;
|
||||
struct usb_phy *transceiver;
|
||||
struct notifier_block usb_nb;
|
||||
struct work_struct work;
|
||||
int irq_chg;
|
||||
int irq_bci;
|
||||
@ -279,7 +279,7 @@ static void twl4030_bci_usb_work(struct work_struct *data)
|
||||
static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
|
||||
void *priv)
|
||||
{
|
||||
struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb);
|
||||
struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb);
|
||||
|
||||
dev_dbg(bci->dev, "OTG notify %lu\n", val);
|
||||
|
||||
@ -479,10 +479,10 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
|
||||
|
||||
INIT_WORK(&bci->work, twl4030_bci_usb_work);
|
||||
|
||||
bci->transceiver = otg_get_transceiver();
|
||||
bci->transceiver = usb_get_transceiver();
|
||||
if (bci->transceiver != NULL) {
|
||||
bci->otg_nb.notifier_call = twl4030_bci_usb_ncb;
|
||||
otg_register_notifier(bci->transceiver, &bci->otg_nb);
|
||||
bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
|
||||
usb_register_notifier(bci->transceiver, &bci->usb_nb);
|
||||
}
|
||||
|
||||
/* Enable interrupts now. */
|
||||
@ -508,8 +508,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
|
||||
|
||||
fail_unmask_interrupts:
|
||||
if (bci->transceiver != NULL) {
|
||||
otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
|
||||
otg_put_transceiver(bci->transceiver);
|
||||
usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
|
||||
usb_put_transceiver(bci->transceiver);
|
||||
}
|
||||
free_irq(bci->irq_bci, bci);
|
||||
fail_bci_irq:
|
||||
@ -539,8 +539,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
|
||||
TWL4030_INTERRUPTS_BCIIMR2A);
|
||||
|
||||
if (bci->transceiver != NULL) {
|
||||
otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
|
||||
otg_put_transceiver(bci->transceiver);
|
||||
usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
|
||||
usb_put_transceiver(bci->transceiver);
|
||||
}
|
||||
free_irq(bci->irq_bci, bci);
|
||||
free_irq(bci->irq_chg, bci);
|
||||
|
@ -1295,6 +1295,7 @@ EXPORT_SYMBOL(int_to_scsilun);
|
||||
* LUNs even if it's older than SCSI-3.
|
||||
* If BLIST_NOREPORTLUN is set, return 1 always.
|
||||
* If BLIST_NOLUN is set, return 0 always.
|
||||
* If starget->no_report_luns is set, return 1 always.
|
||||
*
|
||||
* Return:
|
||||
* 0: scan completed (or no memory, so further scanning is futile)
|
||||
@ -1321,6 +1322,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
|
||||
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
|
||||
* Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
|
||||
* support more than 8 LUNs.
|
||||
* Don't attempt if the target doesn't support REPORT LUNS.
|
||||
*/
|
||||
if (bflags & BLIST_NOREPORTLUN)
|
||||
return 1;
|
||||
@ -1332,6 +1334,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
|
||||
return 1;
|
||||
if (bflags & BLIST_NOLUN)
|
||||
return 0;
|
||||
if (starget->no_report_luns)
|
||||
return 1;
|
||||
|
||||
if (!(sdev = scsi_device_lookup_by_target(starget, 0))) {
|
||||
sdev = scsi_alloc_sdev(starget, 0, NULL);
|
||||
|
@ -2349,7 +2349,7 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp)
|
||||
* some USB ones crash on receiving them, and the pages
|
||||
* we currently ask for are for SPC-3 and beyond
|
||||
*/
|
||||
if (sdp->scsi_level > SCSI_SPC_2)
|
||||
if (sdp->scsi_level > SCSI_SPC_2 && !sdp->skip_vpd_pages)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,43 +3,6 @@
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
/* Bulk only data structures */
|
||||
|
||||
/* command block wrapper */
|
||||
struct bulk_cb_wrap {
|
||||
__le32 Signature; /* contains 'USBC' */
|
||||
__u32 Tag; /* unique per command id */
|
||||
__le32 DataTransferLength; /* size of data */
|
||||
__u8 Flags; /* direction in bit 0 */
|
||||
__u8 Lun; /* LUN normally 0 */
|
||||
__u8 Length; /* of of the CDB */
|
||||
__u8 CDB[16]; /* max command */
|
||||
};
|
||||
|
||||
#define US_BULK_CB_WRAP_LEN 31
|
||||
#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
|
||||
#define US_BULK_FLAG_IN 1
|
||||
#define US_BULK_FLAG_OUT 0
|
||||
|
||||
/* command status wrapper */
|
||||
struct bulk_cs_wrap {
|
||||
__le32 Signature; /* should = 'USBS' */
|
||||
__u32 Tag; /* same as original command */
|
||||
__le32 Residue; /* amount not transferred */
|
||||
__u8 Status; /* see below */
|
||||
__u8 Filler[18];
|
||||
};
|
||||
|
||||
#define US_BULK_CS_WRAP_LEN 13
|
||||
#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
|
||||
#define US_BULK_STAT_OK 0
|
||||
#define US_BULK_STAT_FAIL 1
|
||||
#define US_BULK_STAT_PHASE 2
|
||||
|
||||
/* bulk-only class specific requests */
|
||||
#define US_BULK_RESET_REQUEST 0xff
|
||||
#define US_BULK_GET_MAX_LUN 0xfe
|
||||
|
||||
/* usb_stor_bulk_transfer_xxx() return codes, in order of severity */
|
||||
#define USB_STOR_XFER_GOOD 0 /* good transfer */
|
||||
#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */
|
||||
|
@ -135,7 +135,6 @@ static struct usb_driver quausb2_usb_driver = {
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = quausb2_id_table,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1942,7 +1941,6 @@ static struct usb_serial_driver quatech2_device = {
|
||||
.name = "quatech_usb2",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.usb_driver = &quausb2_usb_driver,
|
||||
.id_table = quausb2_id_table,
|
||||
.num_ports = 8,
|
||||
.open = qt2_open,
|
||||
@ -1964,41 +1962,11 @@ static struct usb_serial_driver quatech2_device = {
|
||||
.write_bulk_callback = qt2_write_bulk_callback,
|
||||
};
|
||||
|
||||
static int __init quausb2_usb_init(void)
|
||||
{
|
||||
int retval;
|
||||
static struct usb_serial_driver * const serial_drivers[] = {
|
||||
&quatech2_device, NULL
|
||||
};
|
||||
|
||||
dbg("%s\n", __func__);
|
||||
|
||||
/* register with usb-serial */
|
||||
retval = usb_serial_register(&quatech2_device);
|
||||
|
||||
if (retval)
|
||||
goto failed_usb_serial_register;
|
||||
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
|
||||
DRIVER_DESC "\n");
|
||||
|
||||
/* register with usb */
|
||||
|
||||
retval = usb_register(&quausb2_usb_driver);
|
||||
if (retval == 0)
|
||||
return 0;
|
||||
|
||||
/* if we're here, usb_register() failed */
|
||||
usb_serial_deregister(&quatech2_device);
|
||||
failed_usb_serial_register:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit quausb2_usb_exit(void)
|
||||
{
|
||||
usb_deregister(&quausb2_usb_driver);
|
||||
usb_serial_deregister(&quatech2_device);
|
||||
}
|
||||
|
||||
module_init(quausb2_usb_init);
|
||||
module_exit(quausb2_usb_exit);
|
||||
module_usb_serial_driver(quausb2_usb_driver, serial_drivers);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
@ -200,7 +200,6 @@ static struct usb_driver serqt_usb_driver = {
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = serqt_id_table,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
static int port_paranoia_check(struct usb_serial_port *port,
|
||||
@ -1590,7 +1589,6 @@ static struct usb_serial_driver quatech_device = {
|
||||
.name = "serqt",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.usb_driver = &serqt_usb_driver,
|
||||
.id_table = serqt_id_table,
|
||||
.num_ports = 8,
|
||||
.open = qt_open,
|
||||
@ -1610,41 +1608,11 @@ static struct usb_serial_driver quatech_device = {
|
||||
.release = qt_release,
|
||||
};
|
||||
|
||||
static int __init serqt_usb_init(void)
|
||||
{
|
||||
int retval;
|
||||
static struct usb_serial_driver * const serial_drivers[] = {
|
||||
&quatech_device, NULL
|
||||
};
|
||||
|
||||
dbg("%s\n", __func__);
|
||||
|
||||
/* register with usb-serial */
|
||||
retval = usb_serial_register(&quatech_device);
|
||||
|
||||
if (retval)
|
||||
goto failed_usb_serial_register;
|
||||
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
|
||||
DRIVER_DESC "\n");
|
||||
|
||||
/* register with usb */
|
||||
|
||||
retval = usb_register(&serqt_usb_driver);
|
||||
if (retval == 0)
|
||||
return 0;
|
||||
|
||||
/* if we're here, usb_register() failed */
|
||||
usb_serial_deregister(&quatech_device);
|
||||
failed_usb_serial_register:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit serqt_usb_exit(void)
|
||||
{
|
||||
usb_deregister(&serqt_usb_driver);
|
||||
usb_serial_deregister(&quatech_device);
|
||||
}
|
||||
|
||||
module_init(serqt_usb_init);
|
||||
module_exit(serqt_usb_exit);
|
||||
module_usb_serial_driver(serqt_usb_driver, serial_drivers);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
@ -76,6 +76,7 @@ config USB_ARCH_HAS_EHCI
|
||||
default y if MICROBLAZE
|
||||
default y if SPARC_LEON
|
||||
default y if ARCH_MMP
|
||||
default y if MACH_LOONGSON1
|
||||
default PCI
|
||||
|
||||
# some non-PCI HCDs implement xHCI
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <linux/serial.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -773,10 +774,37 @@ static int acm_tty_tiocmset(struct tty_struct *tty,
|
||||
return acm_set_control(acm, acm->ctrlout = newctrl);
|
||||
}
|
||||
|
||||
static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
tmp.flags = ASYNC_LOW_LATENCY;
|
||||
tmp.xmit_fifo_size = acm->writesize;
|
||||
tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
|
||||
|
||||
if (copy_to_user(info, &tmp, sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acm_tty_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
struct acm *acm = tty->driver_data;
|
||||
int rv = -ENOIOCTLCMD;
|
||||
|
||||
switch (cmd) {
|
||||
case TIOCGSERIAL: /* gets serial port data */
|
||||
rv = get_serial_info(acm, (struct serial_struct __user *) arg);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const __u32 acm_tty_speed[] = {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/usb/cdc-wdm.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
@ -31,6 +32,8 @@
|
||||
#define DRIVER_AUTHOR "Oliver Neukum"
|
||||
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
|
||||
|
||||
#define HUAWEI_VENDOR_ID 0x12D1
|
||||
|
||||
static const struct usb_device_id wdm_ids[] = {
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
|
||||
@ -38,6 +41,20 @@ static const struct usb_device_id wdm_ids[] = {
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Huawei E392, E398 and possibly other Qualcomm based modems
|
||||
* embed the Qualcomm QMI protocol inside CDC on CDC ECM like
|
||||
* control interfaces. Userspace access to this is required
|
||||
* to configure the accompanying data interface
|
||||
*/
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
|
||||
USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = HUAWEI_VENDOR_ID,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -54,6 +71,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
|
||||
#define WDM_POLL_RUNNING 6
|
||||
#define WDM_RESPONDING 7
|
||||
#define WDM_SUSPENDING 8
|
||||
#define WDM_RESETTING 9
|
||||
|
||||
#define WDM_MAX 16
|
||||
|
||||
@ -61,6 +79,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
|
||||
#define WDM_DEFAULT_BUFSIZE 256
|
||||
|
||||
static DEFINE_MUTEX(wdm_mutex);
|
||||
static DEFINE_SPINLOCK(wdm_device_list_lock);
|
||||
static LIST_HEAD(wdm_device_list);
|
||||
|
||||
/* --- method tables --- */
|
||||
|
||||
@ -82,7 +102,6 @@ struct wdm_device {
|
||||
u16 bufsize;
|
||||
u16 wMaxCommand;
|
||||
u16 wMaxPacketSize;
|
||||
u16 bMaxPacketSize0;
|
||||
__le16 inum;
|
||||
int reslength;
|
||||
int length;
|
||||
@ -96,10 +115,40 @@ struct wdm_device {
|
||||
struct work_struct rxwork;
|
||||
int werr;
|
||||
int rerr;
|
||||
|
||||
struct list_head device_list;
|
||||
int (*manage_power)(struct usb_interface *, int);
|
||||
};
|
||||
|
||||
static struct usb_driver wdm_driver;
|
||||
|
||||
/* return intfdata if we own the interface, else look up intf in the list */
|
||||
static struct wdm_device *wdm_find_device(struct usb_interface *intf)
|
||||
{
|
||||
struct wdm_device *desc = NULL;
|
||||
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_for_each_entry(desc, &wdm_device_list, device_list)
|
||||
if (desc->intf == intf)
|
||||
break;
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static struct wdm_device *wdm_find_device_by_minor(int minor)
|
||||
{
|
||||
struct wdm_device *desc = NULL;
|
||||
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_for_each_entry(desc, &wdm_device_list, device_list)
|
||||
if (desc->intf->minor == minor)
|
||||
break;
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/* --- callbacks --- */
|
||||
static void wdm_out_callback(struct urb *urb)
|
||||
{
|
||||
@ -162,11 +211,9 @@ static void wdm_int_callback(struct urb *urb)
|
||||
int rv = 0;
|
||||
int status = urb->status;
|
||||
struct wdm_device *desc;
|
||||
struct usb_ctrlrequest *req;
|
||||
struct usb_cdc_notification *dr;
|
||||
|
||||
desc = urb->context;
|
||||
req = desc->irq;
|
||||
dr = (struct usb_cdc_notification *)desc->sbuf;
|
||||
|
||||
if (status) {
|
||||
@ -213,24 +260,6 @@ static void wdm_int_callback(struct urb *urb)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
|
||||
req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
|
||||
req->wValue = 0;
|
||||
req->wIndex = desc->inum;
|
||||
req->wLength = cpu_to_le16(desc->wMaxCommand);
|
||||
|
||||
usb_fill_control_urb(
|
||||
desc->response,
|
||||
interface_to_usbdev(desc->intf),
|
||||
/* using common endpoint 0 */
|
||||
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
|
||||
(unsigned char *)req,
|
||||
desc->inbuf,
|
||||
desc->wMaxCommand,
|
||||
wdm_in_callback,
|
||||
desc
|
||||
);
|
||||
desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
spin_lock(&desc->iuspin);
|
||||
clear_bit(WDM_READ, &desc->flags);
|
||||
set_bit(WDM_RESPONDING, &desc->flags);
|
||||
@ -279,14 +308,11 @@ static void free_urbs(struct wdm_device *desc)
|
||||
|
||||
static void cleanup(struct wdm_device *desc)
|
||||
{
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxPacketSize,
|
||||
desc->sbuf,
|
||||
desc->validity->transfer_dma);
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->bMaxPacketSize0,
|
||||
desc->inbuf,
|
||||
desc->response->transfer_dma);
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_del(&desc->device_list);
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
kfree(desc->sbuf);
|
||||
kfree(desc->inbuf);
|
||||
kfree(desc->orq);
|
||||
kfree(desc->irq);
|
||||
kfree(desc->ubuf);
|
||||
@ -351,6 +377,10 @@ static ssize_t wdm_write
|
||||
else
|
||||
if (test_bit(WDM_IN_USE, &desc->flags))
|
||||
r = -EAGAIN;
|
||||
|
||||
if (test_bit(WDM_RESETTING, &desc->flags))
|
||||
r = -EIO;
|
||||
|
||||
if (r < 0) {
|
||||
kfree(buf);
|
||||
goto out;
|
||||
@ -397,7 +427,7 @@ outnl:
|
||||
static ssize_t wdm_read
|
||||
(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
int rv, cntr = 0;
|
||||
int rv, cntr;
|
||||
int i = 0;
|
||||
struct wdm_device *desc = file->private_data;
|
||||
|
||||
@ -406,7 +436,8 @@ static ssize_t wdm_read
|
||||
if (rv < 0)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (desc->length == 0) {
|
||||
cntr = ACCESS_ONCE(desc->length);
|
||||
if (cntr == 0) {
|
||||
desc->read = 0;
|
||||
retry:
|
||||
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
|
||||
@ -430,6 +461,10 @@ retry:
|
||||
rv = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
if (test_bit(WDM_RESETTING, &desc->flags)) {
|
||||
rv = -EIO;
|
||||
goto err;
|
||||
}
|
||||
usb_mark_last_busy(interface_to_usbdev(desc->intf));
|
||||
if (rv < 0) {
|
||||
rv = -ERESTARTSYS;
|
||||
@ -456,26 +491,30 @@ retry:
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
goto retry;
|
||||
}
|
||||
clear_bit(WDM_READ, &desc->flags);
|
||||
cntr = desc->length;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
}
|
||||
|
||||
cntr = count > desc->length ? desc->length : count;
|
||||
if (cntr > count)
|
||||
cntr = count;
|
||||
rv = copy_to_user(buffer, desc->ubuf, cntr);
|
||||
if (rv > 0) {
|
||||
rv = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
|
||||
for (i = 0; i < desc->length - cntr; i++)
|
||||
desc->ubuf[i] = desc->ubuf[i + cntr];
|
||||
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
desc->length -= cntr;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
/* in case we had outstanding data */
|
||||
if (!desc->length)
|
||||
clear_bit(WDM_READ, &desc->flags);
|
||||
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
|
||||
rv = cntr;
|
||||
|
||||
err:
|
||||
@ -529,11 +568,11 @@ static int wdm_open(struct inode *inode, struct file *file)
|
||||
struct wdm_device *desc;
|
||||
|
||||
mutex_lock(&wdm_mutex);
|
||||
intf = usb_find_interface(&wdm_driver, minor);
|
||||
if (!intf)
|
||||
desc = wdm_find_device_by_minor(minor);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
desc = usb_get_intfdata(intf);
|
||||
intf = desc->intf;
|
||||
if (test_bit(WDM_DISCONNECTING, &desc->flags))
|
||||
goto out;
|
||||
file->private_data = desc;
|
||||
@ -543,7 +582,6 @@ static int wdm_open(struct inode *inode, struct file *file)
|
||||
dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
|
||||
goto out;
|
||||
}
|
||||
intf->needs_remote_wakeup = 1;
|
||||
|
||||
/* using write lock to protect desc->count */
|
||||
mutex_lock(&desc->wlock);
|
||||
@ -560,6 +598,8 @@ static int wdm_open(struct inode *inode, struct file *file)
|
||||
rv = 0;
|
||||
}
|
||||
mutex_unlock(&desc->wlock);
|
||||
if (desc->count == 1)
|
||||
desc->manage_power(intf, 1);
|
||||
usb_autopm_put_interface(desc->intf);
|
||||
out:
|
||||
mutex_unlock(&wdm_mutex);
|
||||
@ -581,7 +621,7 @@ static int wdm_release(struct inode *inode, struct file *file)
|
||||
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
|
||||
kill_urbs(desc);
|
||||
if (!test_bit(WDM_DISCONNECTING, &desc->flags))
|
||||
desc->intf->needs_remote_wakeup = 0;
|
||||
desc->manage_power(desc->intf, 0);
|
||||
}
|
||||
mutex_unlock(&wdm_mutex);
|
||||
return 0;
|
||||
@ -628,71 +668,31 @@ static void wdm_rxwork(struct work_struct *work)
|
||||
|
||||
/* --- hotplug --- */
|
||||
|
||||
static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
|
||||
u16 bufsize, int (*manage_power)(struct usb_interface *, int))
|
||||
{
|
||||
int rv = -EINVAL;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int rv = -ENOMEM;
|
||||
struct wdm_device *desc;
|
||||
struct usb_host_interface *iface;
|
||||
struct usb_endpoint_descriptor *ep;
|
||||
struct usb_cdc_dmm_desc *dmhd;
|
||||
u8 *buffer = intf->altsetting->extra;
|
||||
int buflen = intf->altsetting->extralen;
|
||||
u16 maxcom = WDM_DEFAULT_BUFSIZE;
|
||||
|
||||
if (!buffer)
|
||||
goto out;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
rv = -ENOMEM;
|
||||
desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto out;
|
||||
INIT_LIST_HEAD(&desc->device_list);
|
||||
mutex_init(&desc->rlock);
|
||||
mutex_init(&desc->wlock);
|
||||
spin_lock_init(&desc->iuspin);
|
||||
init_waitqueue_head(&desc->wait);
|
||||
desc->wMaxCommand = maxcom;
|
||||
desc->wMaxCommand = bufsize;
|
||||
/* this will be expanded and needed in hardware endianness */
|
||||
desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
|
||||
desc->intf = intf;
|
||||
INIT_WORK(&desc->rxwork, wdm_rxwork);
|
||||
|
||||
rv = -EINVAL;
|
||||
iface = intf->cur_altsetting;
|
||||
if (iface->desc.bNumEndpoints != 1)
|
||||
goto err;
|
||||
ep = &iface->endpoint[0].desc;
|
||||
if (!ep || !usb_endpoint_is_int_in(ep))
|
||||
if (!usb_endpoint_is_int_in(ep))
|
||||
goto err;
|
||||
|
||||
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
|
||||
desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
|
||||
|
||||
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||
if (!desc->orq)
|
||||
@ -717,19 +717,13 @@ next_desc:
|
||||
if (!desc->ubuf)
|
||||
goto err;
|
||||
|
||||
desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf),
|
||||
desc->wMaxPacketSize,
|
||||
GFP_KERNEL,
|
||||
&desc->validity->transfer_dma);
|
||||
desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
|
||||
if (!desc->sbuf)
|
||||
goto err;
|
||||
|
||||
desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf),
|
||||
desc->wMaxCommand,
|
||||
GFP_KERNEL,
|
||||
&desc->response->transfer_dma);
|
||||
desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
|
||||
if (!desc->inbuf)
|
||||
goto err2;
|
||||
goto err;
|
||||
|
||||
usb_fill_int_urb(
|
||||
desc->validity,
|
||||
@ -741,45 +735,149 @@ next_desc:
|
||||
desc,
|
||||
ep->bInterval
|
||||
);
|
||||
desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
usb_set_intfdata(intf, desc);
|
||||
desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
|
||||
desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
|
||||
desc->irq->wValue = 0;
|
||||
desc->irq->wIndex = desc->inum;
|
||||
desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
|
||||
|
||||
usb_fill_control_urb(
|
||||
desc->response,
|
||||
interface_to_usbdev(intf),
|
||||
/* using common endpoint 0 */
|
||||
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
|
||||
(unsigned char *)desc->irq,
|
||||
desc->inbuf,
|
||||
desc->wMaxCommand,
|
||||
wdm_in_callback,
|
||||
desc
|
||||
);
|
||||
|
||||
desc->manage_power = manage_power;
|
||||
|
||||
spin_lock(&wdm_device_list_lock);
|
||||
list_add(&desc->device_list, &wdm_device_list);
|
||||
spin_unlock(&wdm_device_list_lock);
|
||||
|
||||
rv = usb_register_dev(intf, &wdm_class);
|
||||
if (rv < 0)
|
||||
goto err3;
|
||||
goto err;
|
||||
else
|
||||
dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
|
||||
intf->minor - WDM_MINOR_BASE);
|
||||
dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
|
||||
out:
|
||||
return rv;
|
||||
err3:
|
||||
usb_set_intfdata(intf, NULL);
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->bMaxPacketSize0,
|
||||
desc->inbuf,
|
||||
desc->response->transfer_dma);
|
||||
err2:
|
||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
||||
desc->wMaxPacketSize,
|
||||
desc->sbuf,
|
||||
desc->validity->transfer_dma);
|
||||
err:
|
||||
free_urbs(desc);
|
||||
kfree(desc->ubuf);
|
||||
kfree(desc->orq);
|
||||
kfree(desc->irq);
|
||||
kfree(desc);
|
||||
cleanup(desc);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int wdm_manage_power(struct usb_interface *intf, int on)
|
||||
{
|
||||
/* need autopm_get/put here to ensure the usbcore sees the new value */
|
||||
int rv = usb_autopm_get_interface(intf);
|
||||
if (rv < 0)
|
||||
goto err;
|
||||
|
||||
intf->needs_remote_wakeup = on;
|
||||
usb_autopm_put_interface(intf);
|
||||
err:
|
||||
return rv;
|
||||
}
|
||||
|
||||
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;
|
||||
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];
|
||||
}
|
||||
|
||||
iface = intf->cur_altsetting;
|
||||
if (iface->desc.bNumEndpoints != 1)
|
||||
goto err;
|
||||
ep = &iface->endpoint[0].desc;
|
||||
|
||||
rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
|
||||
|
||||
err:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_cdc_wdm_register - register a WDM subdriver
|
||||
* @intf: usb interface the subdriver will associate with
|
||||
* @ep: interrupt endpoint to monitor for notifications
|
||||
* @bufsize: maximum message size to support for read/write
|
||||
*
|
||||
* Create WDM usb class character device and associate it with intf
|
||||
* without binding, allowing another driver to manage the interface.
|
||||
*
|
||||
* The subdriver will manage the given interrupt endpoint exclusively
|
||||
* and will issue control requests referring to the given intf. It
|
||||
* will otherwise avoid interferring, and in particular not do
|
||||
* usb_set_intfdata/usb_get_intfdata on intf.
|
||||
*
|
||||
* The return value is a pointer to the subdriver's struct usb_driver.
|
||||
* The registering driver is responsible for calling this subdriver's
|
||||
* disconnect, suspend, resume, pre_reset and post_reset methods from
|
||||
* its own.
|
||||
*/
|
||||
struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
|
||||
struct usb_endpoint_descriptor *ep,
|
||||
int bufsize,
|
||||
int (*manage_power)(struct usb_interface *, int))
|
||||
{
|
||||
int rv = -EINVAL;
|
||||
|
||||
rv = wdm_create(intf, ep, bufsize, manage_power);
|
||||
if (rv < 0)
|
||||
goto err;
|
||||
|
||||
return &wdm_driver;
|
||||
err:
|
||||
return ERR_PTR(rv);
|
||||
}
|
||||
EXPORT_SYMBOL(usb_cdc_wdm_register);
|
||||
|
||||
static void wdm_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct wdm_device *desc;
|
||||
unsigned long flags;
|
||||
|
||||
usb_deregister_dev(intf, &wdm_class);
|
||||
desc = wdm_find_device(intf);
|
||||
mutex_lock(&wdm_mutex);
|
||||
desc = usb_get_intfdata(intf);
|
||||
|
||||
/* the spinlock makes sure no new urbs are generated in the callbacks */
|
||||
spin_lock_irqsave(&desc->iuspin, flags);
|
||||
@ -803,7 +901,7 @@ static void wdm_disconnect(struct usb_interface *intf)
|
||||
#ifdef CONFIG_PM
|
||||
static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct wdm_device *desc = usb_get_intfdata(intf);
|
||||
struct wdm_device *desc = wdm_find_device(intf);
|
||||
int rv = 0;
|
||||
|
||||
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
|
||||
@ -853,7 +951,7 @@ static int recover_from_urb_loss(struct wdm_device *desc)
|
||||
#ifdef CONFIG_PM
|
||||
static int wdm_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct wdm_device *desc = usb_get_intfdata(intf);
|
||||
struct wdm_device *desc = wdm_find_device(intf);
|
||||
int rv;
|
||||
|
||||
dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
|
||||
@ -867,11 +965,7 @@ static int wdm_resume(struct usb_interface *intf)
|
||||
|
||||
static int wdm_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct wdm_device *desc = usb_get_intfdata(intf);
|
||||
|
||||
mutex_lock(&desc->rlock);
|
||||
mutex_lock(&desc->wlock);
|
||||
kill_urbs(desc);
|
||||
struct wdm_device *desc = wdm_find_device(intf);
|
||||
|
||||
/*
|
||||
* we notify everybody using poll of
|
||||
@ -880,17 +974,25 @@ static int wdm_pre_reset(struct usb_interface *intf)
|
||||
* message from the device is lost
|
||||
*/
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */
|
||||
set_bit(WDM_READ, &desc->flags); /* unblock read */
|
||||
clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */
|
||||
desc->rerr = -EINTR;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
wake_up_all(&desc->wait);
|
||||
mutex_lock(&desc->rlock);
|
||||
mutex_lock(&desc->wlock);
|
||||
kill_urbs(desc);
|
||||
cancel_work_sync(&desc->rxwork);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdm_post_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct wdm_device *desc = usb_get_intfdata(intf);
|
||||
struct wdm_device *desc = wdm_find_device(intf);
|
||||
int rv;
|
||||
|
||||
clear_bit(WDM_RESETTING, &desc->flags);
|
||||
rv = recover_from_urb_loss(desc);
|
||||
mutex_unlock(&desc->wlock);
|
||||
mutex_unlock(&desc->rlock);
|
||||
|
@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf)
|
||||
int rc;
|
||||
|
||||
/* Delayed unbind of an existing driver */
|
||||
if (intf->dev.driver) {
|
||||
struct usb_driver *driver =
|
||||
to_usb_driver(intf->dev.driver);
|
||||
|
||||
dev_dbg(&intf->dev, "forced unbind\n");
|
||||
usb_driver_release_interface(driver, intf);
|
||||
}
|
||||
if (intf->dev.driver)
|
||||
usb_forced_unbind_intf(intf);
|
||||
|
||||
/* Try to rebind the interface */
|
||||
if (!intf->dev.power.is_prepared) {
|
||||
@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#define DO_UNBIND 0
|
||||
#define DO_REBIND 1
|
||||
|
||||
/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
|
||||
* or rebind interfaces that have been unbound, according to @action.
|
||||
/* Unbind drivers for @udev's interfaces that don't support suspend/resume
|
||||
* There is no check for reset_resume here because it can be determined
|
||||
* only during resume whether reset_resume is needed.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
static void do_unbind_rebind(struct usb_device *udev, int action)
|
||||
static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
@ -996,23 +989,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
switch (action) {
|
||||
case DO_UNBIND:
|
||||
if (intf->dev.driver) {
|
||||
drv = to_usb_driver(intf->dev.driver);
|
||||
if (!drv->suspend || !drv->resume)
|
||||
usb_forced_unbind_intf(intf);
|
||||
}
|
||||
break;
|
||||
case DO_REBIND:
|
||||
if (intf->needs_binding)
|
||||
usb_rebind_intf(intf);
|
||||
break;
|
||||
|
||||
if (intf->dev.driver) {
|
||||
drv = to_usb_driver(intf->dev.driver);
|
||||
if (!drv->suspend || !drv->resume)
|
||||
usb_forced_unbind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
|
||||
* These interfaces have the needs_binding flag set by usb_resume_interface().
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
if (intf->dev.driver && intf->needs_binding)
|
||||
usb_forced_unbind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void do_rebind_interfaces(struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_config *config;
|
||||
int i;
|
||||
struct usb_interface *intf;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (config) {
|
||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||
intf = config->interface[i];
|
||||
if (intf->needs_binding)
|
||||
usb_rebind_intf(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device_driver *udriver;
|
||||
@ -1302,35 +1325,48 @@ int usb_suspend(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
do_unbind_rebind(udev, DO_UNBIND);
|
||||
unbind_no_pm_drivers_interfaces(udev);
|
||||
|
||||
/* From now on we are sure all drivers support suspend/resume
|
||||
* but not necessarily reset_resume()
|
||||
* so we may still need to unbind and rebind upon resume
|
||||
*/
|
||||
choose_wakeup(udev, msg);
|
||||
return usb_suspend_both(udev, msg);
|
||||
}
|
||||
|
||||
/* The device lock is held by the PM core */
|
||||
int usb_resume_complete(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
/* For PM complete calls, all we do is rebind interfaces
|
||||
* whose needs_binding flag is set
|
||||
*/
|
||||
if (udev->state != USB_STATE_NOTATTACHED)
|
||||
do_rebind_interfaces(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The device lock is held by the PM core */
|
||||
int usb_resume(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int status;
|
||||
|
||||
/* For PM complete calls, all we do is rebind interfaces */
|
||||
if (msg.event == PM_EVENT_ON) {
|
||||
if (udev->state != USB_STATE_NOTATTACHED)
|
||||
do_unbind_rebind(udev, DO_REBIND);
|
||||
status = 0;
|
||||
|
||||
/* For all other calls, take the device back to full power and
|
||||
/* For all calls, take the device back to full power and
|
||||
* tell the PM core in case it was autosuspended previously.
|
||||
* Unbind the interfaces that will need rebinding later.
|
||||
* Unbind the interfaces that will need rebinding later,
|
||||
* because they fail to support reset_resume.
|
||||
* (This can't be done in usb_resume_interface()
|
||||
* above because it doesn't own the right set of locks.)
|
||||
*/
|
||||
} else {
|
||||
status = usb_resume_both(udev, msg);
|
||||
if (status == 0) {
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
do_unbind_rebind(udev, DO_REBIND);
|
||||
}
|
||||
status = usb_resume_both(udev, msg);
|
||||
if (status == 0) {
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
unbind_no_reset_resume_drivers_interfaces(udev);
|
||||
}
|
||||
|
||||
/* Avoid PM error messages for devices disconnected while suspended
|
||||
|
@ -380,6 +380,7 @@ static int check_root_hub_suspended(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
|
||||
static int suspend_common(struct device *dev, bool do_wakeup)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
@ -471,6 +472,7 @@ static int resume_common(struct device *dev, int event)
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#endif /* SLEEP || RUNTIME */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
|
@ -2352,7 +2352,7 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
|
||||
"io mem" : "io base",
|
||||
(unsigned long long)hcd->rsrc_start);
|
||||
} else {
|
||||
hcd->irq = -1;
|
||||
hcd->irq = 0;
|
||||
if (hcd->rsrc_start)
|
||||
dev_info(hcd->self.controller, "%s 0x%08llx\n",
|
||||
(hcd->driver->flags & HCD_MEMORY) ?
|
||||
@ -2508,7 +2508,7 @@ err_register_root_hub:
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
err_hcd_driver_start:
|
||||
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0)
|
||||
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
|
||||
free_irq(irqnum, hcd);
|
||||
err_request_irq:
|
||||
err_hcd_driver_setup:
|
||||
@ -2573,7 +2573,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
||||
del_timer_sync(&hcd->rh_timer);
|
||||
|
||||
if (usb_hcd_is_primary_hcd(hcd)) {
|
||||
if (hcd->irq >= 0)
|
||||
if (hcd->irq > 0)
|
||||
free_irq(hcd->irq, hcd);
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,8 @@ struct usb_hub {
|
||||
resumed */
|
||||
unsigned long removed_bits[1]; /* ports with a "removed"
|
||||
device present */
|
||||
unsigned long wakeup_bits[1]; /* ports that have signaled
|
||||
remote wakeup */
|
||||
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
||||
#error event_bits[] is too short!
|
||||
#endif
|
||||
@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev)
|
||||
kick_khubd(hub);
|
||||
}
|
||||
|
||||
/*
|
||||
* Let the USB core know that a USB 3.0 device has sent a Function Wake Device
|
||||
* Notification, which indicates it had initiated remote wakeup.
|
||||
*
|
||||
* USB 3.0 hubs do not report the port link state change from U3 to U0 when the
|
||||
* device initiates resume, so the USB core will not receive notice of the
|
||||
* resume through the normal hub interrupt URB.
|
||||
*/
|
||||
void usb_wakeup_notification(struct usb_device *hdev,
|
||||
unsigned int portnum)
|
||||
{
|
||||
struct usb_hub *hub;
|
||||
|
||||
if (!hdev)
|
||||
return;
|
||||
|
||||
hub = hdev_to_hub(hdev);
|
||||
if (hub) {
|
||||
set_bit(portnum, hub->wakeup_bits);
|
||||
kick_khubd(hub);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_wakeup_notification);
|
||||
|
||||
/* completion function, fires on port status changes and various faults */
|
||||
static void hub_irq(struct urb *urb)
|
||||
@ -823,12 +848,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
||||
clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_ENABLE);
|
||||
}
|
||||
if (portchange & USB_PORT_STAT_C_LINK_STATE) {
|
||||
need_debounce_delay = true;
|
||||
clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_PORT_LINK_STATE);
|
||||
}
|
||||
|
||||
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
|
||||
hub_is_superspeed(hub->hdev)) {
|
||||
need_debounce_delay = true;
|
||||
@ -850,12 +869,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
||||
set_bit(port1, hub->change_bits);
|
||||
|
||||
} else if (portstatus & USB_PORT_STAT_ENABLE) {
|
||||
bool port_resumed = (portstatus &
|
||||
USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_U0;
|
||||
/* The power session apparently survived the resume.
|
||||
* If there was an overcurrent or suspend change
|
||||
* (i.e., remote wakeup request), have khubd
|
||||
* take care of it.
|
||||
* take care of it. Look at the port link state
|
||||
* for USB 3.0 hubs, since they don't have a suspend
|
||||
* change bit, and they don't set the port link change
|
||||
* bit on device-initiated resume.
|
||||
*/
|
||||
if (portchange)
|
||||
if (portchange || (hub_is_superspeed(hub->hdev) &&
|
||||
port_resumed))
|
||||
set_bit(port1, hub->change_bits);
|
||||
|
||||
} else if (udev->persist_enabled) {
|
||||
@ -1293,14 +1319,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
desc = intf->cur_altsetting;
|
||||
hdev = interface_to_usbdev(intf);
|
||||
|
||||
/* Hubs have proper suspend/resume support. USB 3.0 device suspend is
|
||||
* different from USB 2.0/1.1 device suspend, and unfortunately we
|
||||
* don't support it yet. So leave autosuspend disabled for USB 3.0
|
||||
* external hubs for now. Enable autosuspend for USB 3.0 roothubs,
|
||||
* since that isn't a "real" hub.
|
||||
*/
|
||||
if (!hub_is_superspeed(hdev) || !hdev->parent)
|
||||
usb_enable_autosuspend(hdev);
|
||||
/* Hubs have proper suspend/resume support. */
|
||||
usb_enable_autosuspend(hdev);
|
||||
|
||||
if (hdev->level == MAX_TOPO_LEVEL) {
|
||||
dev_err(&intf->dev,
|
||||
@ -1842,6 +1862,37 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void set_usb_port_removable(struct usb_device *udev)
|
||||
{
|
||||
struct usb_device *hdev = udev->parent;
|
||||
struct usb_hub *hub;
|
||||
u8 port = udev->portnum;
|
||||
u16 wHubCharacteristics;
|
||||
bool removable = true;
|
||||
|
||||
if (!hdev)
|
||||
return;
|
||||
|
||||
hub = hdev_to_hub(udev->parent);
|
||||
|
||||
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||
|
||||
if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
|
||||
return;
|
||||
|
||||
if (hub_is_superspeed(hdev)) {
|
||||
if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
|
||||
removable = false;
|
||||
} else {
|
||||
if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
|
||||
removable = false;
|
||||
}
|
||||
|
||||
if (removable)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_new_device - perform initial device setup (usbcore-internal)
|
||||
@ -1900,6 +1951,15 @@ int usb_new_device(struct usb_device *udev)
|
||||
announce_device(udev);
|
||||
|
||||
device_enable_async_suspend(&udev->dev);
|
||||
|
||||
/*
|
||||
* check whether the hub marks this port as non-removable. Do it
|
||||
* now so that platform-specific data can override it in
|
||||
* device_add()
|
||||
*/
|
||||
if (udev->parent)
|
||||
set_usb_port_removable(udev);
|
||||
|
||||
/* Register the device. The device driver is responsible
|
||||
* for configuring the device and invoking the add-device
|
||||
* notifier chain (used by usbfs and possibly others).
|
||||
@ -2385,11 +2445,27 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
* we don't explicitly enable it here.
|
||||
*/
|
||||
if (udev->do_remote_wakeup) {
|
||||
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
|
||||
USB_DEVICE_REMOTE_WAKEUP, 0,
|
||||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (!hub_is_superspeed(hub->hdev)) {
|
||||
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
|
||||
USB_DEVICE_REMOTE_WAKEUP, 0,
|
||||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
} else {
|
||||
/* Assume there's only one function on the USB 3.0
|
||||
* device and enable remote wake for the first
|
||||
* interface. FIXME if the interface association
|
||||
* descriptor shows there's more than one function.
|
||||
*/
|
||||
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE,
|
||||
USB_RECIP_INTERFACE,
|
||||
USB_INTRF_FUNC_SUSPEND,
|
||||
USB_INTRF_FUNC_SUSPEND_RW |
|
||||
USB_INTRF_FUNC_SUSPEND_LP,
|
||||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
if (status) {
|
||||
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
|
||||
status);
|
||||
@ -2679,6 +2755,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
unsigned port1;
|
||||
int status;
|
||||
|
||||
/* Warn if children aren't already suspended */
|
||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||
@ -2691,6 +2768,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
|
||||
/* Enable hub to send remote wakeup for all ports. */
|
||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||
status = set_port_feature(hdev,
|
||||
port1 |
|
||||
USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
|
||||
USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
|
||||
USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
|
||||
USB_PORT_FEAT_REMOTE_WAKE_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&intf->dev, "%s\n", __func__);
|
||||
|
||||
@ -3424,6 +3512,46 @@ done:
|
||||
hcd->driver->relinquish_port(hcd, port1);
|
||||
}
|
||||
|
||||
/* Returns 1 if there was a remote wakeup and a connect status change. */
|
||||
static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
|
||||
u16 portstatus, u16 portchange)
|
||||
{
|
||||
struct usb_device *hdev;
|
||||
struct usb_device *udev;
|
||||
int connect_change = 0;
|
||||
int ret;
|
||||
|
||||
hdev = hub->hdev;
|
||||
udev = hdev->children[port-1];
|
||||
if (!hub_is_superspeed(hdev)) {
|
||||
if (!(portchange & USB_PORT_STAT_C_SUSPEND))
|
||||
return 0;
|
||||
clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
|
||||
} else {
|
||||
if (!udev || udev->state != USB_STATE_SUSPENDED ||
|
||||
(portstatus & USB_PORT_STAT_LINK_STATE) !=
|
||||
USB_SS_PORT_LS_U0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (udev) {
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
|
||||
usb_lock_device(udev);
|
||||
ret = usb_remote_wakeup(udev);
|
||||
usb_unlock_device(udev);
|
||||
if (ret < 0)
|
||||
connect_change = 1;
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
hub_port_disable(hub, port, 1);
|
||||
}
|
||||
dev_dbg(hub->intfdev, "resume on port %d, status %d\n",
|
||||
port, ret);
|
||||
return connect_change;
|
||||
}
|
||||
|
||||
static void hub_events(void)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
@ -3436,7 +3564,7 @@ static void hub_events(void)
|
||||
u16 portstatus;
|
||||
u16 portchange;
|
||||
int i, ret;
|
||||
int connect_change;
|
||||
int connect_change, wakeup_change;
|
||||
|
||||
/*
|
||||
* We restart the list every time to avoid a deadlock with
|
||||
@ -3515,8 +3643,9 @@ static void hub_events(void)
|
||||
if (test_bit(i, hub->busy_bits))
|
||||
continue;
|
||||
connect_change = test_bit(i, hub->change_bits);
|
||||
wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
|
||||
if (!test_and_clear_bit(i, hub->event_bits) &&
|
||||
!connect_change)
|
||||
!connect_change && !wakeup_change)
|
||||
continue;
|
||||
|
||||
ret = hub_port_status(hub, i,
|
||||
@ -3557,31 +3686,10 @@ static void hub_events(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (portchange & USB_PORT_STAT_C_SUSPEND) {
|
||||
struct usb_device *udev;
|
||||
if (hub_handle_remote_wakeup(hub, i,
|
||||
portstatus, portchange))
|
||||
connect_change = 1;
|
||||
|
||||
clear_port_feature(hdev, i,
|
||||
USB_PORT_FEAT_C_SUSPEND);
|
||||
udev = hdev->children[i-1];
|
||||
if (udev) {
|
||||
/* TRSMRCY = 10 msec */
|
||||
msleep(10);
|
||||
|
||||
usb_lock_device(udev);
|
||||
ret = usb_remote_wakeup(hdev->
|
||||
children[i-1]);
|
||||
usb_unlock_device(udev);
|
||||
if (ret < 0)
|
||||
connect_change = 1;
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
hub_port_disable(hub, i, 1);
|
||||
}
|
||||
dev_dbg (hub_dev,
|
||||
"resume on port %d, status %d\n",
|
||||
i, ret);
|
||||
}
|
||||
|
||||
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
|
||||
u16 status = 0;
|
||||
u16 unused;
|
||||
|
@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
}
|
||||
static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_removable(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
char *state;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
|
||||
switch (udev->removable) {
|
||||
case USB_DEVICE_REMOVABLE:
|
||||
state = "removable";
|
||||
break;
|
||||
case USB_DEVICE_FIXED:
|
||||
state = "fixed";
|
||||
break;
|
||||
default:
|
||||
state = "unknown";
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", state);
|
||||
}
|
||||
static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = {
|
||||
&dev_attr_avoid_reset_quirk.attr,
|
||||
&dev_attr_authorized.attr,
|
||||
&dev_attr_remove.attr,
|
||||
&dev_attr_removable.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dev_attr_grp = {
|
||||
|
@ -403,20 +403,17 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
* cause problems in HCDs if they get it wrong.
|
||||
*/
|
||||
{
|
||||
unsigned int orig_flags = urb->transfer_flags;
|
||||
unsigned int allowed;
|
||||
static int pipetypes[4] = {
|
||||
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
|
||||
};
|
||||
|
||||
/* Check that the pipe's type matches the endpoint's type */
|
||||
if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) {
|
||||
dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
|
||||
if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
|
||||
dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
|
||||
usb_pipetype(urb->pipe), pipetypes[xfertype]);
|
||||
return -EPIPE; /* The most suitable error code :-) */
|
||||
}
|
||||
|
||||
/* enforce simple/standard policy */
|
||||
/* Check against a simple/standard policy */
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
|
||||
URB_FREE_BUFFER);
|
||||
switch (xfertype) {
|
||||
@ -435,14 +432,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
||||
allowed |= URB_ISO_ASAP;
|
||||
break;
|
||||
}
|
||||
urb->transfer_flags &= allowed;
|
||||
allowed &= urb->transfer_flags;
|
||||
|
||||
/* fail if submitter gave bogus flags */
|
||||
if (urb->transfer_flags != orig_flags) {
|
||||
dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n",
|
||||
orig_flags, urb->transfer_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* warn if submitter gave bogus flags */
|
||||
if (allowed != urb->transfer_flags)
|
||||
dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
|
||||
urb->transfer_flags, allowed);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
@ -532,10 +527,13 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
|
||||
* a driver's I/O routines to insure that all URB-related activity has
|
||||
* completed before it returns.
|
||||
*
|
||||
* This request is always asynchronous. Success is indicated by
|
||||
* returning -EINPROGRESS, at which time the URB will probably not yet
|
||||
* have been given back to the device driver. When it is eventually
|
||||
* called, the completion function will see @urb->status == -ECONNRESET.
|
||||
* This request is asynchronous, however the HCD might call the ->complete()
|
||||
* callback during unlink. Therefore when drivers call usb_unlink_urb(), they
|
||||
* must not hold any locks that may be taken by the completion function.
|
||||
* Success is indicated by returning -EINPROGRESS, at which time the URB will
|
||||
* probably not yet have been given back to the device driver. When it is
|
||||
* eventually called, the completion function will see @urb->status ==
|
||||
* -ECONNRESET.
|
||||
* Failure is indicated by usb_unlink_urb() returning any other value.
|
||||
* Unlinking will fail when @urb is not currently "linked" (i.e., it was
|
||||
* never submitted, or it was unlinked before, or the hardware is already
|
||||
|
@ -274,7 +274,7 @@ static int usb_dev_prepare(struct device *dev)
|
||||
static void usb_dev_complete(struct device *dev)
|
||||
{
|
||||
/* Currently used only for rebinding interfaces */
|
||||
usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */
|
||||
usb_resume_complete(dev);
|
||||
}
|
||||
|
||||
static int usb_dev_suspend(struct device *dev)
|
||||
|
@ -56,6 +56,7 @@ extern void usb_major_cleanup(void);
|
||||
|
||||
extern int usb_suspend(struct device *dev, pm_message_t msg);
|
||||
extern int usb_resume(struct device *dev, pm_message_t msg);
|
||||
extern int usb_resume_complete(struct device *dev);
|
||||
|
||||
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
|
||||
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
|
||||
|
@ -28,6 +28,19 @@ endif
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3-omap.o
|
||||
|
||||
##
|
||||
# REVISIT Samsung Exynos platform needs the clk API which isn't
|
||||
# defined on all architectures. If we allow dwc3-exynos.c compile
|
||||
# always we will fail the linking phase on those architectures
|
||||
# which don't provide clk api implementation and that's unnaceptable.
|
||||
#
|
||||
# When Samsung's platform start supporting pm_runtime, this check
|
||||
# for HAVE_CLK should be removed.
|
||||
##
|
||||
ifneq ($(CONFIG_HAVE_CLK),)
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3-exynos.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_PCI),)
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3-pci.o
|
||||
endif
|
||||
|
@ -48,10 +48,10 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
@ -86,7 +86,7 @@ again:
|
||||
id = -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return id;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_get_device_id);
|
||||
|
||||
@ -167,11 +167,11 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_alloc_one_event_buffer - Allocated one event buffer structure
|
||||
* dwc3_alloc_one_event_buffer - Allocates one event buffer structure
|
||||
* @dwc: Pointer to our controller context structure
|
||||
* @length: size of the event buffer
|
||||
*
|
||||
* Returns a pointer to the allocated event buffer structure on succes
|
||||
* Returns a pointer to the allocated event buffer structure on success
|
||||
* otherwise ERR_PTR(errno).
|
||||
*/
|
||||
static struct dwc3_event_buffer *__devinit
|
||||
@ -215,10 +215,10 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
||||
|
||||
/**
|
||||
* dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
|
||||
* @dwc: Pointer to out controller context structure
|
||||
* @dwc: pointer to our controller context structure
|
||||
* @length: size of event buffer
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno. In error the case, dwc
|
||||
* Returns 0 on success otherwise negative errno. In the error case, dwc
|
||||
* may contain some buffers allocated but not all which were requested.
|
||||
*/
|
||||
static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
||||
@ -251,7 +251,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
||||
|
||||
/**
|
||||
* dwc3_event_buffers_setup - setup our allocated event buffers
|
||||
* @dwc: Pointer to out controller context structure
|
||||
* @dwc: pointer to our controller context structure
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno.
|
||||
*/
|
||||
@ -350,7 +350,7 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN(3);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
||||
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
|
||||
@ -363,9 +363,9 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have a bug
|
||||
* when The device fails to connect at SuperSpeed
|
||||
* where the device can fail to connect at SuperSpeed
|
||||
* and falls back to high-speed mode which causes
|
||||
* the device to enter in a Connect/Disconnect loop
|
||||
* the device to enter a Connect/Disconnect loop
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
@ -404,8 +404,10 @@ static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
|
||||
static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
@ -415,39 +417,39 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
u8 mode;
|
||||
|
||||
mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
|
||||
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "not enough memory\n");
|
||||
goto err0;
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
|
||||
dwc->mem = mem;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "missing resource\n");
|
||||
goto err1;
|
||||
dev_err(dev, "missing resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc->res = res;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res),
|
||||
dev_name(&pdev->dev));
|
||||
res = devm_request_mem_region(dev, res->start, resource_size(res),
|
||||
dev_name(dev));
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't request mem region\n");
|
||||
goto err1;
|
||||
dev_err(dev, "can't request mem region\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
regs = ioremap(res->start, resource_size(res));
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
goto err2;
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "missing IRQ\n");
|
||||
goto err3;
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
@ -455,7 +457,7 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
dwc->dev = &pdev->dev;
|
||||
dwc->dev = dev;
|
||||
dwc->irq = irq;
|
||||
|
||||
if (!strncmp("super", maximum_speed, 5))
|
||||
@ -469,14 +471,17 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
else
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
if (of_get_property(node, "tx-fifo-resize", NULL))
|
||||
dwc->needs_fifo_resize = true;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize core\n");
|
||||
goto err3;
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mode = DWC3_MODE(dwc->hwparams.hwparams0);
|
||||
@ -486,49 +491,49 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize gadget\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err1;
|
||||
}
|
||||
break;
|
||||
case DWC3_MODE_HOST:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize host\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err1;
|
||||
}
|
||||
break;
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize host\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize gadget\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unsupported mode of operation %d\n", mode);
|
||||
goto err4;
|
||||
dev_err(dev, "Unsupported mode of operation %d\n", mode);
|
||||
goto err1;
|
||||
}
|
||||
dwc->mode = mode;
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize debugfs\n");
|
||||
goto err5;
|
||||
dev_err(dev, "failed to initialize debugfs\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
err2:
|
||||
switch (mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
dwc3_gadget_exit(dwc);
|
||||
@ -545,19 +550,9 @@ err5:
|
||||
break;
|
||||
}
|
||||
|
||||
err4:
|
||||
err1:
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
err3:
|
||||
iounmap(regs);
|
||||
|
||||
err2:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
err1:
|
||||
kfree(dwc->mem);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -590,9 +585,6 @@ static int __devexit dwc3_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
iounmap(dwc->regs);
|
||||
kfree(dwc->mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -605,19 +597,9 @@ static struct platform_driver dwc3_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_driver);
|
||||
|
||||
MODULE_ALIAS("platform:dwc3");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
|
||||
|
||||
static int __devinit dwc3_init(void)
|
||||
{
|
||||
return platform_driver_register(&dwc3_driver);
|
||||
}
|
||||
module_init(dwc3_init);
|
||||
|
||||
static void __exit dwc3_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&dwc3_driver);
|
||||
}
|
||||
module_exit(dwc3_exit);
|
||||
|
@ -145,22 +145,23 @@
|
||||
/* Bit fields */
|
||||
|
||||
/* Global Configuration Register */
|
||||
#define DWC3_GCTL_PWRDNSCALE(n) (n << 19)
|
||||
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
|
||||
#define DWC3_GCTL_U2RSTECN (1 << 16)
|
||||
#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6)
|
||||
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
|
||||
#define DWC3_GCTL_CLK_BUS (0)
|
||||
#define DWC3_GCTL_CLK_PIPE (1)
|
||||
#define DWC3_GCTL_CLK_PIPEHALF (2)
|
||||
#define DWC3_GCTL_CLK_MASK (3)
|
||||
|
||||
#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12)
|
||||
#define DWC3_GCTL_PRTCAPDIR(n) (n << 12)
|
||||
#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12)
|
||||
#define DWC3_GCTL_PRTCAP_HOST 1
|
||||
#define DWC3_GCTL_PRTCAP_DEVICE 2
|
||||
#define DWC3_GCTL_PRTCAP_OTG 3
|
||||
|
||||
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
|
||||
#define DWC3_GCTL_SCALEDOWN(n) (n << 4)
|
||||
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
|
||||
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
|
||||
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
|
||||
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
|
||||
|
||||
@ -172,8 +173,12 @@
|
||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
||||
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
|
||||
|
||||
/* Global TX Fifo Size Register */
|
||||
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
|
||||
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
|
||||
|
||||
/* Global HWPARAMS1 Register */
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24)
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
|
||||
|
||||
@ -198,6 +203,15 @@
|
||||
|
||||
#define DWC3_DCTL_APPL1RES (1 << 23)
|
||||
|
||||
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
|
||||
#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
|
||||
|
||||
#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
|
||||
#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
|
||||
#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
|
||||
#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
|
||||
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
|
||||
|
||||
#define DWC3_DCTL_INITU2ENA (1 << 12)
|
||||
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
|
||||
#define DWC3_DCTL_INITU1ENA (1 << 10)
|
||||
@ -260,10 +274,10 @@
|
||||
|
||||
/* Device Endpoint Command Register */
|
||||
#define DWC3_DEPCMD_PARAM_SHIFT 16
|
||||
#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12)
|
||||
#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) & DWC3_DEPCMD_STATUS_MASK) >> 12)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
|
||||
#define DWC3_DEPCMD_CMDACT (1 << 10)
|
||||
#define DWC3_DEPCMD_CMDIOC (1 << 8)
|
||||
@ -288,7 +302,7 @@
|
||||
|
||||
/* Structures */
|
||||
|
||||
struct dwc3_trb_hw;
|
||||
struct dwc3_trb;
|
||||
|
||||
/**
|
||||
* struct dwc3_event_buffer - Software event buffer representation
|
||||
@ -343,7 +357,7 @@ struct dwc3_ep {
|
||||
struct list_head request_list;
|
||||
struct list_head req_queued;
|
||||
|
||||
struct dwc3_trb_hw *trb_pool;
|
||||
struct dwc3_trb *trb_pool;
|
||||
dma_addr_t trb_pool_dma;
|
||||
u32 free_slot;
|
||||
u32 busy_slot;
|
||||
@ -418,102 +432,49 @@ enum dwc3_device_state {
|
||||
DWC3_CONFIGURED_STATE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc3_trb - transfer request block
|
||||
* @bpl: lower 32bit of the buffer
|
||||
* @bph: higher 32bit of the buffer
|
||||
* @length: buffer size (up to 16mb - 1)
|
||||
* @pcm1: packet count m1
|
||||
* @trbsts: trb status
|
||||
* 0 = ok
|
||||
* 1 = missed isoc
|
||||
* 2 = setup pending
|
||||
* @hwo: hardware owner of descriptor
|
||||
* @lst: last trb
|
||||
* @chn: chain buffers
|
||||
* @csp: continue on short packets (only supported on isoc eps)
|
||||
* @trbctl: trb control
|
||||
* 1 = normal
|
||||
* 2 = control-setup
|
||||
* 3 = control-status-2
|
||||
* 4 = control-status-3
|
||||
* 5 = control-data (first trb of data stage)
|
||||
* 6 = isochronous-first (first trb of service interval)
|
||||
* 7 = isochronous
|
||||
* 8 = link trb
|
||||
* others = reserved
|
||||
* @isp_imi: interrupt on short packet / interrupt on missed isoc
|
||||
* @ioc: interrupt on complete
|
||||
* @sid_sofn: Stream ID / SOF Number
|
||||
*/
|
||||
struct dwc3_trb {
|
||||
u64 bplh;
|
||||
/* TRB Length, PCM and Status */
|
||||
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
|
||||
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
|
||||
#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24)
|
||||
#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28) >> 28))
|
||||
|
||||
union {
|
||||
struct {
|
||||
u32 length:24;
|
||||
u32 pcm1:2;
|
||||
u32 reserved27_26:2;
|
||||
u32 trbsts:4;
|
||||
#define DWC3_TRB_STS_OKAY 0
|
||||
#define DWC3_TRB_STS_MISSED_ISOC 1
|
||||
#define DWC3_TRB_STS_SETUP_PENDING 2
|
||||
};
|
||||
u32 len_pcm;
|
||||
};
|
||||
#define DWC3_TRBSTS_OK 0
|
||||
#define DWC3_TRBSTS_MISSED_ISOC 1
|
||||
#define DWC3_TRBSTS_SETUP_PENDING 2
|
||||
|
||||
union {
|
||||
struct {
|
||||
u32 hwo:1;
|
||||
u32 lst:1;
|
||||
u32 chn:1;
|
||||
u32 csp:1;
|
||||
u32 trbctl:6;
|
||||
u32 isp_imi:1;
|
||||
u32 ioc:1;
|
||||
u32 reserved13_12:2;
|
||||
u32 sid_sofn:16;
|
||||
u32 reserved31_30:2;
|
||||
};
|
||||
u32 control;
|
||||
};
|
||||
} __packed;
|
||||
/* TRB Control */
|
||||
#define DWC3_TRB_CTRL_HWO (1 << 0)
|
||||
#define DWC3_TRB_CTRL_LST (1 << 1)
|
||||
#define DWC3_TRB_CTRL_CHN (1 << 2)
|
||||
#define DWC3_TRB_CTRL_CSP (1 << 3)
|
||||
#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4)
|
||||
#define DWC3_TRB_CTRL_ISP_IMI (1 << 10)
|
||||
#define DWC3_TRB_CTRL_IOC (1 << 11)
|
||||
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
|
||||
|
||||
#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
|
||||
#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2)
|
||||
#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3)
|
||||
#define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4)
|
||||
#define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5)
|
||||
#define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6)
|
||||
#define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7)
|
||||
#define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8)
|
||||
|
||||
/**
|
||||
* struct dwc3_trb_hw - transfer request block (hw format)
|
||||
* struct dwc3_trb - transfer request block (hw format)
|
||||
* @bpl: DW0-3
|
||||
* @bph: DW4-7
|
||||
* @size: DW8-B
|
||||
* @trl: DWC-F
|
||||
*/
|
||||
struct dwc3_trb_hw {
|
||||
__le32 bpl;
|
||||
__le32 bph;
|
||||
__le32 size;
|
||||
__le32 ctrl;
|
||||
struct dwc3_trb {
|
||||
u32 bpl;
|
||||
u32 bph;
|
||||
u32 size;
|
||||
u32 ctrl;
|
||||
} __packed;
|
||||
|
||||
static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw)
|
||||
{
|
||||
hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh));
|
||||
hw->bph = cpu_to_le32(upper_32_bits(nat->bplh));
|
||||
hw->size = cpu_to_le32p(&nat->len_pcm);
|
||||
/* HWO is written last */
|
||||
hw->ctrl = cpu_to_le32p(&nat->control);
|
||||
}
|
||||
|
||||
static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat)
|
||||
{
|
||||
u64 bplh;
|
||||
|
||||
bplh = le32_to_cpup(&hw->bpl);
|
||||
bplh |= (u64) le32_to_cpup(&hw->bph) << 32;
|
||||
nat->bplh = bplh;
|
||||
|
||||
nat->len_pcm = le32_to_cpup(&hw->size);
|
||||
nat->control = le32_to_cpup(&hw->ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_hwparams - copy of HWPARAMS registers
|
||||
* @hwparams0 - GHWPARAMS0
|
||||
@ -546,8 +507,13 @@ struct dwc3_hwparams {
|
||||
#define DWC3_MODE_DRD 2
|
||||
#define DWC3_MODE_HUB 3
|
||||
|
||||
#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8)
|
||||
|
||||
/* HWPARAMS1 */
|
||||
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
|
||||
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
|
||||
|
||||
/* HWPARAMS7 */
|
||||
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
|
||||
struct dwc3_request {
|
||||
struct usb_request request;
|
||||
@ -555,7 +521,7 @@ struct dwc3_request {
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
u8 epnum;
|
||||
struct dwc3_trb_hw *trb;
|
||||
struct dwc3_trb *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned direction:1;
|
||||
@ -572,7 +538,6 @@ struct dwc3_request {
|
||||
* @ctrl_req_addr: dma address of ctrl_req
|
||||
* @ep0_trb: dma address of ep0_trb
|
||||
* @ep0_usb_req: dummy req used while handling STD USB requests
|
||||
* @setup_buf_addr: dma address of setup_buf
|
||||
* @ep0_bounce_addr: dma address of ep0_bounce
|
||||
* @lock: for synchronizing
|
||||
* @dev: pointer to our struct device
|
||||
@ -594,6 +559,8 @@ struct dwc3_request {
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
@ -604,12 +571,11 @@ struct dwc3_request {
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
struct dwc3_trb_hw *ep0_trb;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *ep0_bounce;
|
||||
u8 *setup_buf;
|
||||
dma_addr_t ctrl_req_addr;
|
||||
dma_addr_t ep0_trb_addr;
|
||||
dma_addr_t setup_buf_addr;
|
||||
dma_addr_t ep0_bounce_addr;
|
||||
struct dwc3_request ep0_usb_req;
|
||||
/* device lock */
|
||||
@ -651,6 +617,8 @@ struct dwc3 {
|
||||
unsigned start_config_issued:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned resize_fifos:1;
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
@ -662,23 +630,13 @@ struct dwc3 {
|
||||
|
||||
struct dwc3_hwparams hwparams;
|
||||
struct dentry *root;
|
||||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define DWC3_TRBSTS_OK 0
|
||||
#define DWC3_TRBSTS_MISSED_ISOC 1
|
||||
#define DWC3_TRBSTS_SETUP_PENDING 2
|
||||
|
||||
#define DWC3_TRBCTL_NORMAL 1
|
||||
#define DWC3_TRBCTL_CONTROL_SETUP 2
|
||||
#define DWC3_TRBCTL_CONTROL_STATUS2 3
|
||||
#define DWC3_TRBCTL_CONTROL_STATUS3 4
|
||||
#define DWC3_TRBCTL_CONTROL_DATA 5
|
||||
#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6
|
||||
#define DWC3_TRBCTL_ISOCHRONOUS 7
|
||||
#define DWC3_TRBCTL_LINK_TRB 8
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct dwc3_event_type {
|
||||
@ -719,9 +677,14 @@ struct dwc3_event_depevt {
|
||||
u32 endpoint_event:4;
|
||||
u32 reserved11_10:2;
|
||||
u32 status:4;
|
||||
#define DEPEVT_STATUS_BUSERR (1 << 0)
|
||||
#define DEPEVT_STATUS_SHORT (1 << 1)
|
||||
#define DEPEVT_STATUS_IOC (1 << 2)
|
||||
|
||||
/* Within XferNotReady */
|
||||
#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3)
|
||||
|
||||
/* Within XferComplete */
|
||||
#define DEPEVT_STATUS_BUSERR (1 << 0)
|
||||
#define DEPEVT_STATUS_SHORT (1 << 1)
|
||||
#define DEPEVT_STATUS_IOC (1 << 2)
|
||||
#define DEPEVT_STATUS_LST (1 << 3)
|
||||
|
||||
/* Stream event only */
|
||||
@ -807,6 +770,7 @@ union dwc3_event {
|
||||
|
||||
/* prototypes */
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc);
|
||||
void dwc3_host_exit(struct dwc3 *dwc);
|
||||
|
@ -46,6 +46,8 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
@ -464,6 +466,192 @@ static const struct file_operations dwc3_mode_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dwc3_testmode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= DWC3_DCTL_TSTCTRL_MASK;
|
||||
reg >>= 1;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
switch (reg) {
|
||||
case 0:
|
||||
seq_printf(s, "no test\n");
|
||||
break;
|
||||
case TEST_J:
|
||||
seq_printf(s, "test_j\n");
|
||||
break;
|
||||
case TEST_K:
|
||||
seq_printf(s, "test_k\n");
|
||||
break;
|
||||
case TEST_SE0_NAK:
|
||||
seq_printf(s, "test_se0_nak\n");
|
||||
break;
|
||||
case TEST_PACKET:
|
||||
seq_printf(s, "test_packet\n");
|
||||
break;
|
||||
case TEST_FORCE_EN:
|
||||
seq_printf(s, "test_force_enable\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_testmode_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dwc3_testmode_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t dwc3_testmode_write(struct file *file,
|
||||
const char __user *ubuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
u32 testmode = 0;
|
||||
char buf[32];
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "test_j", 6))
|
||||
testmode = TEST_J;
|
||||
else if (!strncmp(buf, "test_k", 6))
|
||||
testmode = TEST_K;
|
||||
else if (!strncmp(buf, "test_se0_nak", 12))
|
||||
testmode = TEST_SE0_NAK;
|
||||
else if (!strncmp(buf, "test_packet", 11))
|
||||
testmode = TEST_PACKET;
|
||||
else if (!strncmp(buf, "test_force_enable", 17))
|
||||
testmode = TEST_FORCE_EN;
|
||||
else
|
||||
testmode = 0;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_set_test_mode(dwc, testmode);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_testmode_fops = {
|
||||
.open = dwc3_testmode_open,
|
||||
.write = dwc3_testmode_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
enum dwc3_link_state state;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
state = DWC3_DSTS_USBLNKST(reg);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
switch (state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
seq_printf(s, "U0\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_U1:
|
||||
seq_printf(s, "U1\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_U2:
|
||||
seq_printf(s, "U2\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_U3:
|
||||
seq_printf(s, "U3\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
seq_printf(s, "SS.Disabled\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
seq_printf(s, "Rx.Detect\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_SS_INACT:
|
||||
seq_printf(s, "SS.Inactive\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_POLL:
|
||||
seq_printf(s, "Poll\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
seq_printf(s, "Recovery\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_HRESET:
|
||||
seq_printf(s, "HRESET\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_CMPLY:
|
||||
seq_printf(s, "Compliance\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_LPBK:
|
||||
seq_printf(s, "Loopback\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_link_state_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dwc3_link_state_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t dwc3_link_state_write(struct file *file,
|
||||
const char __user *ubuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
enum dwc3_link_state state = 0;
|
||||
char buf[32];
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "SS.Disabled", 11))
|
||||
state = DWC3_LINK_STATE_SS_DIS;
|
||||
else if (!strncmp(buf, "Rx.Detect", 9))
|
||||
state = DWC3_LINK_STATE_RX_DET;
|
||||
else if (!strncmp(buf, "SS.Inactive", 11))
|
||||
state = DWC3_LINK_STATE_SS_INACT;
|
||||
else if (!strncmp(buf, "Recovery", 8))
|
||||
state = DWC3_LINK_STATE_RECOV;
|
||||
else if (!strncmp(buf, "Compliance", 10))
|
||||
state = DWC3_LINK_STATE_CMPLY;
|
||||
else if (!strncmp(buf, "Loopback", 8))
|
||||
state = DWC3_LINK_STATE_LPBK;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_set_link_state(dwc, state);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_link_state_fops = {
|
||||
.open = dwc3_link_state_open,
|
||||
.write = dwc3_link_state_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct dentry *root;
|
||||
@ -471,8 +659,8 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
int ret;
|
||||
|
||||
root = debugfs_create_dir(dev_name(dwc->dev), NULL);
|
||||
if (IS_ERR(root)) {
|
||||
ret = PTR_ERR(root);
|
||||
if (!root) {
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
@ -480,15 +668,29 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
|
||||
file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
|
||||
&dwc3_regdump_fops);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_mode_fops);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_testmode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_link_state_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
|
151
drivers/usb/dwc3/dwc3-exynos.c
Normal file
151
drivers/usb/dwc3/dwc3-exynos.c
Normal file
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Anton Tikhomirov <av.tikhomirov@samsung.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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/dwc3-exynos.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
struct dwc3_exynos {
|
||||
struct platform_device *dwc3;
|
||||
struct device *dev;
|
||||
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *dwc3;
|
||||
struct dwc3_exynos *exynos;
|
||||
struct clk *clk;
|
||||
|
||||
int devid;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
exynos = kzalloc(sizeof(*exynos), GFP_KERNEL);
|
||||
if (!exynos) {
|
||||
dev_err(&pdev->dev, "not enough memory\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, exynos);
|
||||
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0)
|
||||
goto err1;
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", devid);
|
||||
if (!dwc3) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "usbdrd30");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "couldn't get clock\n");
|
||||
ret = -EINVAL;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
|
||||
|
||||
dwc3->dev.parent = &pdev->dev;
|
||||
dwc3->dev.dma_mask = pdev->dev.dma_mask;
|
||||
dwc3->dev.dma_parms = pdev->dev.dma_parms;
|
||||
exynos->dwc3 = dwc3;
|
||||
exynos->dev = &pdev->dev;
|
||||
exynos->clk = clk;
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
|
||||
/* PHY initialization */
|
||||
if (!pdata) {
|
||||
dev_dbg(&pdev->dev, "missing platform data\n");
|
||||
} else {
|
||||
if (pdata->phy_init)
|
||||
pdata->phy_init(pdev, pdata->phy_type);
|
||||
}
|
||||
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register dwc3 device\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
if (pdata && pdata->phy_exit)
|
||||
pdata->phy_exit(pdev, pdata->phy_type);
|
||||
|
||||
clk_disable(clk);
|
||||
clk_put(clk);
|
||||
err3:
|
||||
platform_device_put(dwc3);
|
||||
err2:
|
||||
dwc3_put_device_id(devid);
|
||||
err1:
|
||||
kfree(exynos);
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit dwc3_exynos_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
|
||||
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
platform_device_unregister(exynos->dwc3);
|
||||
|
||||
dwc3_put_device_id(exynos->dwc3->id);
|
||||
|
||||
if (pdata && pdata->phy_exit)
|
||||
pdata->phy_exit(pdev, pdata->phy_type);
|
||||
|
||||
clk_disable(exynos->clk);
|
||||
clk_put(exynos->clk);
|
||||
|
||||
kfree(exynos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dwc3_exynos_driver = {
|
||||
.probe = dwc3_exynos_probe,
|
||||
.remove = __devexit_p(dwc3_exynos_remove),
|
||||
.driver = {
|
||||
.name = "exynos-dwc3",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_exynos_driver);
|
||||
|
||||
MODULE_ALIAS("platform:exynos-dwc3");
|
||||
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
|
@ -46,7 +46,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "io.h"
|
||||
@ -197,91 +197,99 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_omap_data *pdata = pdev->dev.platform_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
struct platform_device *dwc3;
|
||||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
int devid;
|
||||
int size;
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
const u32 *utmi_mode;
|
||||
u32 reg;
|
||||
|
||||
void __iomem *base;
|
||||
void *context;
|
||||
|
||||
omap = kzalloc(sizeof(*omap), GFP_KERNEL);
|
||||
omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
|
||||
if (!omap) {
|
||||
dev_err(&pdev->dev, "not enough memory\n");
|
||||
goto err0;
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, omap);
|
||||
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "missing IRQ resource\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
dev_err(dev, "missing IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "missing memory base resource\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
dev_err(dev, "missing memory base resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = ioremap_nocache(res->start, resource_size(res));
|
||||
base = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!base) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
goto err1;
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0)
|
||||
goto err2;
|
||||
return -ENODEV;
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", devid);
|
||||
if (!dwc3) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
|
||||
goto err3;
|
||||
dev_err(dev, "couldn't allocate dwc3 device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
context = kzalloc(resource_size(res), GFP_KERNEL);
|
||||
context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
|
||||
if (!context) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n");
|
||||
goto err4;
|
||||
dev_err(dev, "couldn't allocate dwc3 context memory\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
spin_lock_init(&omap->lock);
|
||||
dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
|
||||
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
|
||||
|
||||
dwc3->dev.parent = &pdev->dev;
|
||||
dwc3->dev.dma_mask = pdev->dev.dma_mask;
|
||||
dwc3->dev.dma_parms = pdev->dev.dma_parms;
|
||||
dwc3->dev.parent = dev;
|
||||
dwc3->dev.dma_mask = dev->dma_mask;
|
||||
dwc3->dev.dma_parms = dev->dma_parms;
|
||||
omap->resource_size = resource_size(res);
|
||||
omap->context = context;
|
||||
omap->dev = &pdev->dev;
|
||||
omap->dev = dev;
|
||||
omap->irq = irq;
|
||||
omap->base = base;
|
||||
omap->dwc3 = dwc3;
|
||||
|
||||
reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
if (!pdata) {
|
||||
dev_dbg(&pdev->dev, "missing platform data\n");
|
||||
utmi_mode = of_get_property(node, "utmi-mode", &size);
|
||||
if (utmi_mode && size == sizeof(*utmi_mode)) {
|
||||
reg |= *utmi_mode;
|
||||
} else {
|
||||
switch (pdata->utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n",
|
||||
pdata->utmi_mode);
|
||||
if (!pdata) {
|
||||
dev_dbg(dev, "missing platform data\n");
|
||||
} else {
|
||||
switch (pdata->utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "UNKNOWN utmi mode %d\n",
|
||||
pdata->utmi_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,12 +308,12 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
|
||||
dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
|
||||
|
||||
ret = request_irq(omap->irq, dwc3_omap_interrupt, 0,
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n",
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
goto err5;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
/* enable all IRQs */
|
||||
@ -327,37 +335,24 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err6;
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register dwc3 device\n");
|
||||
goto err6;
|
||||
dev_err(dev, "failed to register dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
free_irq(omap->irq, omap);
|
||||
|
||||
err5:
|
||||
kfree(omap->context);
|
||||
|
||||
err4:
|
||||
err2:
|
||||
platform_device_put(dwc3);
|
||||
|
||||
err3:
|
||||
err1:
|
||||
dwc3_put_device_id(devid);
|
||||
|
||||
err2:
|
||||
iounmap(base);
|
||||
|
||||
err1:
|
||||
kfree(omap);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -368,11 +363,6 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev)
|
||||
platform_device_unregister(omap->dwc3);
|
||||
|
||||
dwc3_put_device_id(omap->dwc3->id);
|
||||
free_irq(omap->irq, omap);
|
||||
iounmap(omap->base);
|
||||
|
||||
kfree(omap->context);
|
||||
kfree(omap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -61,32 +61,35 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
|
||||
struct dwc3_pci *glue;
|
||||
int ret = -ENOMEM;
|
||||
int devid;
|
||||
struct device *dev = &pci->dev;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pci->dev, "not enough memory\n");
|
||||
goto err0;
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
glue->dev = &pci->dev;
|
||||
glue->dev = dev;
|
||||
|
||||
ret = pci_enable_device(pci);
|
||||
if (ret) {
|
||||
dev_err(&pci->dev, "failed to enable pci device\n");
|
||||
goto err1;
|
||||
dev_err(dev, "failed to enable pci device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_set_master(pci);
|
||||
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0)
|
||||
goto err2;
|
||||
if (devid < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", devid);
|
||||
if (!dwc3) {
|
||||
dev_err(&pci->dev, "couldn't allocate dwc3 device\n");
|
||||
goto err3;
|
||||
dev_err(dev, "couldn't allocate dwc3 device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
@ -102,41 +105,37 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
|
||||
|
||||
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
|
||||
if (ret) {
|
||||
dev_err(&pci->dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err4;
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, glue);
|
||||
|
||||
dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask);
|
||||
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
|
||||
|
||||
dwc3->dev.dma_mask = pci->dev.dma_mask;
|
||||
dwc3->dev.dma_parms = pci->dev.dma_parms;
|
||||
dwc3->dev.parent = &pci->dev;
|
||||
glue->dwc3 = dwc3;
|
||||
dwc3->dev.dma_mask = dev->dma_mask;
|
||||
dwc3->dev.dma_parms = dev->dma_parms;
|
||||
dwc3->dev.parent = dev;
|
||||
glue->dwc3 = dwc3;
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(&pci->dev, "failed to register dwc3 device\n");
|
||||
goto err4;
|
||||
dev_err(dev, "failed to register dwc3 device\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
err3:
|
||||
pci_set_drvdata(pci, NULL);
|
||||
platform_device_put(dwc3);
|
||||
|
||||
err3:
|
||||
err2:
|
||||
dwc3_put_device_id(devid);
|
||||
|
||||
err2:
|
||||
err1:
|
||||
pci_disable_device(pci);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -148,7 +147,6 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci)
|
||||
platform_device_unregister(glue->dwc3);
|
||||
pci_set_drvdata(pci, NULL);
|
||||
pci_disable_device(pci);
|
||||
kfree(glue);
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
|
||||
|
@ -76,8 +76,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
u32 len, u32 type)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_trb_hw *trb_hw;
|
||||
struct dwc3_trb trb;
|
||||
struct dwc3_trb *trb;
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
int ret;
|
||||
@ -88,19 +87,17 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
return 0;
|
||||
}
|
||||
|
||||
trb_hw = dwc->ep0_trb;
|
||||
memset(&trb, 0, sizeof(trb));
|
||||
trb = dwc->ep0_trb;
|
||||
|
||||
trb.trbctl = type;
|
||||
trb.bplh = buf_dma;
|
||||
trb.length = len;
|
||||
trb->bpl = lower_32_bits(buf_dma);
|
||||
trb->bph = upper_32_bits(buf_dma);
|
||||
trb->size = len;
|
||||
trb->ctrl = type;
|
||||
|
||||
trb.hwo = 1;
|
||||
trb.lst = 1;
|
||||
trb.ioc = 1;
|
||||
trb.isp_imi = 1;
|
||||
|
||||
dwc3_trb_to_hw(&trb, trb_hw);
|
||||
trb->ctrl |= (DWC3_TRB_CTRL_HWO
|
||||
| DWC3_TRB_CTRL_LST
|
||||
| DWC3_TRB_CTRL_IOC
|
||||
| DWC3_TRB_CTRL_ISP_IMI);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
|
||||
@ -302,7 +299,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
||||
dep = dwc->eps[0];
|
||||
dwc->ep0_usb_req.dep = dep;
|
||||
dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
|
||||
dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr;
|
||||
dwc->ep0_usb_req.request.buf = dwc->setup_buf;
|
||||
dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
|
||||
|
||||
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
|
||||
@ -315,9 +312,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
u32 recip;
|
||||
u32 wValue;
|
||||
u32 wIndex;
|
||||
u32 reg;
|
||||
int ret;
|
||||
u32 mode;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
@ -356,25 +351,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
if (!set)
|
||||
return -EINVAL;
|
||||
|
||||
mode = wIndex >> 8;
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
|
||||
switch (mode) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
reg |= mode << 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
dwc->test_mode_nr = wIndex >> 8;
|
||||
dwc->test_mode = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -396,7 +374,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, wIndex);
|
||||
if (!dep)
|
||||
return -EINVAL;
|
||||
ret = __dwc3_gadget_ep_set_halt(dep, set);
|
||||
@ -470,8 +448,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
case DWC3_ADDRESS_STATE:
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
/* if the cfg matches and the cfg is non zero */
|
||||
if (!ret && cfg)
|
||||
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
|
||||
dwc->dev_state = DWC3_CONFIGURED_STATE;
|
||||
dwc->resize_fifos = true;
|
||||
dev_dbg(dwc->dev, "resize fifos flag SET\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case DWC3_CONFIGURED_STATE:
|
||||
@ -560,9 +541,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
{
|
||||
struct dwc3_request *r = NULL;
|
||||
struct usb_request *ur;
|
||||
struct dwc3_trb trb;
|
||||
struct dwc3_trb *trb;
|
||||
struct dwc3_ep *ep0;
|
||||
u32 transferred;
|
||||
u32 length;
|
||||
u8 epnum;
|
||||
|
||||
epnum = event->endpoint_number;
|
||||
@ -573,16 +555,16 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
r = next_request(&ep0->request_list);
|
||||
ur = &r->request;
|
||||
|
||||
dwc3_trb_to_nat(dwc->ep0_trb, &trb);
|
||||
trb = dwc->ep0_trb;
|
||||
length = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
if (dwc->ep0_bounced) {
|
||||
|
||||
transferred = min_t(u32, ur->length,
|
||||
ep0->endpoint.maxpacket - trb.length);
|
||||
ep0->endpoint.maxpacket - length);
|
||||
memcpy(ur->buf, dwc->ep0_bounce, transferred);
|
||||
dwc->ep0_bounced = false;
|
||||
} else {
|
||||
transferred = ur->length - trb.length;
|
||||
transferred = ur->length - length;
|
||||
ur->actual += transferred;
|
||||
}
|
||||
|
||||
@ -614,6 +596,17 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc,
|
||||
dwc3_gadget_giveback(dep, r, 0);
|
||||
}
|
||||
|
||||
if (dwc->test_mode) {
|
||||
int ret;
|
||||
|
||||
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dwc->dev, "Invalid Test #%d\n",
|
||||
dwc->test_mode_nr);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
}
|
||||
}
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
}
|
||||
@ -624,6 +617,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
|
||||
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
|
||||
|
||||
dep->flags &= ~DWC3_EP_BUSY;
|
||||
dep->res_trans_idx = 0;
|
||||
dwc->setup_packet_pending = false;
|
||||
|
||||
switch (dwc->ep0state) {
|
||||
@ -679,7 +673,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
DWC3_TRBCTL_CONTROL_DATA);
|
||||
} else if ((req->request.length % dep->endpoint.maxpacket)
|
||||
&& (event->endpoint_number == 0)) {
|
||||
dwc3_map_buffer_to_dma(req);
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
event->endpoint_number);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "failed to map request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
WARN_ON(req->request.length > dep->endpoint.maxpacket);
|
||||
|
||||
@ -694,7 +693,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
|
||||
DWC3_TRBCTL_CONTROL_DATA);
|
||||
} else {
|
||||
dwc3_map_buffer_to_dma(req);
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
event->endpoint_number);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "failed to map request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
|
||||
req->request.dma, req->request.length,
|
||||
@ -720,6 +724,12 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
|
||||
{
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
|
||||
if (dwc->resize_fifos) {
|
||||
dev_dbg(dwc->dev, "starting to resize fifos\n");
|
||||
dwc3_gadget_resize_tx_fifos(dwc);
|
||||
dwc->resize_fifos = 0;
|
||||
}
|
||||
|
||||
WARN_ON(dwc3_ep0_start_control_status(dep));
|
||||
}
|
||||
|
||||
|
@ -54,68 +54,162 @@
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
void dwc3_map_buffer_to_dma(struct dwc3_request *req)
|
||||
/**
|
||||
* dwc3_gadget_set_test_mode - Enables USB2 Test Modes
|
||||
* @dwc: pointer to our context structure
|
||||
* @mode: the mode to set (J, K SE0 NAK, Force Enable)
|
||||
*
|
||||
* Caller should take care of locking. This function will
|
||||
* return 0 on success or -EINVAL if wrong Test Selector
|
||||
* is passed
|
||||
*/
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
||||
{
|
||||
struct dwc3 *dwc = req->dep->dwc;
|
||||
u32 reg;
|
||||
|
||||
if (req->request.length == 0) {
|
||||
/* req->request.dma = dwc->setup_buf_addr; */
|
||||
return;
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
|
||||
switch (mode) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
reg |= mode << 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (req->request.num_sgs) {
|
||||
int mapped;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
mapped = dma_map_sg(dwc->dev, req->request.sg,
|
||||
req->request.num_sgs,
|
||||
req->direction ? DMA_TO_DEVICE
|
||||
: DMA_FROM_DEVICE);
|
||||
if (mapped < 0) {
|
||||
dev_err(dwc->dev, "failed to map SGs\n");
|
||||
return;
|
||||
}
|
||||
|
||||
req->request.num_mapped_sgs = mapped;
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->request.dma == DMA_ADDR_INVALID) {
|
||||
req->request.dma = dma_map_single(dwc->dev, req->request.buf,
|
||||
req->request.length, req->direction
|
||||
? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_unmap_buffer_from_dma(struct dwc3_request *req)
|
||||
/**
|
||||
* dwc3_gadget_set_link_state - Sets USB Link to a particular State
|
||||
* @dwc: pointer to our context structure
|
||||
* @state: the state to put link into
|
||||
*
|
||||
* Caller should take care of locking. This function will
|
||||
* return 0 on success or -ETIMEDOUT.
|
||||
*/
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
|
||||
{
|
||||
struct dwc3 *dwc = req->dep->dwc;
|
||||
int retries = 10000;
|
||||
u32 reg;
|
||||
|
||||
if (req->request.length == 0) {
|
||||
req->request.dma = DMA_ADDR_INVALID;
|
||||
return;
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||
|
||||
/* set requested state */
|
||||
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
/* wait for a change in DSTS */
|
||||
while (--retries) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
if (DWC3_DSTS_USBLNKST(reg) == state)
|
||||
return 0;
|
||||
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
if (req->request.num_mapped_sgs) {
|
||||
req->request.dma = DMA_ADDR_INVALID;
|
||||
dma_unmap_sg(dwc->dev, req->request.sg,
|
||||
req->request.num_mapped_sgs,
|
||||
req->direction ? DMA_TO_DEVICE
|
||||
: DMA_FROM_DEVICE);
|
||||
dev_vdbg(dwc->dev, "link state change request timed out\n");
|
||||
|
||||
req->request.num_mapped_sgs = 0;
|
||||
return;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
|
||||
* @dwc: pointer to our context structure
|
||||
*
|
||||
* This function will a best effort FIFO allocation in order
|
||||
* to improve FIFO usage and throughput, while still allowing
|
||||
* us to enable as many endpoints as possible.
|
||||
*
|
||||
* Keep in mind that this operation will be highly dependent
|
||||
* on the configured size for RAM1 - which contains TxFifo -,
|
||||
* the amount of endpoints enabled on coreConsultant tool, and
|
||||
* the width of the Master Bus.
|
||||
*
|
||||
* In the ideal world, we would always be able to satisfy the
|
||||
* following equation:
|
||||
*
|
||||
* ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \
|
||||
* (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes
|
||||
*
|
||||
* Unfortunately, due to many variables that's not always the case.
|
||||
*/
|
||||
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
|
||||
{
|
||||
int last_fifo_depth = 0;
|
||||
int ram1_depth;
|
||||
int fifo_size;
|
||||
int mdwidth;
|
||||
int num;
|
||||
|
||||
if (!dwc->needs_fifo_resize)
|
||||
return 0;
|
||||
|
||||
ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
|
||||
/* MDWIDTH is represented in bits, we need it in bytes */
|
||||
mdwidth >>= 3;
|
||||
|
||||
/*
|
||||
* FIXME For now we will only allocate 1 wMaxPacketSize space
|
||||
* for each enabled endpoint, later patches will come to
|
||||
* improve this algorithm so that we better use the internal
|
||||
* FIFO space
|
||||
*/
|
||||
for (num = 0; num < DWC3_ENDPOINTS_NUM; num++) {
|
||||
struct dwc3_ep *dep = dwc->eps[num];
|
||||
int fifo_number = dep->number >> 1;
|
||||
int mult = 1;
|
||||
int tmp;
|
||||
|
||||
if (!(dep->number & 1))
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc)
|
||||
|| usb_endpoint_xfer_isoc(dep->desc))
|
||||
mult = 3;
|
||||
|
||||
/*
|
||||
* REVISIT: the following assumes we will always have enough
|
||||
* space available on the FIFO RAM for all possible use cases.
|
||||
* Make sure that's true somehow and change FIFO allocation
|
||||
* accordingly.
|
||||
*
|
||||
* If we have Bulk or Isochronous endpoints, we want
|
||||
* them to be able to be very, very fast. So we're giving
|
||||
* those endpoints a fifo_size which is enough for 3 full
|
||||
* packets
|
||||
*/
|
||||
tmp = mult * (dep->endpoint.maxpacket + mdwidth);
|
||||
tmp += mdwidth;
|
||||
|
||||
fifo_size = DIV_ROUND_UP(tmp, mdwidth);
|
||||
|
||||
fifo_size |= (last_fifo_depth << 16);
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
|
||||
dep->name, last_fifo_depth, fifo_size & 0xffff);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(fifo_number),
|
||||
fifo_size);
|
||||
|
||||
last_fifo_depth += (fifo_size & 0xffff);
|
||||
}
|
||||
|
||||
if (req->mapped) {
|
||||
dma_unmap_single(dwc->dev, req->request.dma,
|
||||
req->request.length, req->direction
|
||||
? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = 0;
|
||||
req->request.dma = DMA_ADDR_INVALID;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
@ -144,14 +238,15 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
if (req->request.status == -EINPROGRESS)
|
||||
req->request.status = status;
|
||||
|
||||
dwc3_unmap_buffer_from_dma(req);
|
||||
usb_gadget_unmap_request(&dwc->gadget, &req->request,
|
||||
req->direction);
|
||||
|
||||
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
|
||||
req, dep->name, req->request.actual,
|
||||
req->request.length, status);
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
req->request.complete(&req->dep->endpoint, &req->request);
|
||||
req->request.complete(&dep->endpoint, &req->request);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
|
||||
@ -219,7 +314,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
}
|
||||
|
||||
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
|
||||
struct dwc3_trb_hw *trb)
|
||||
struct dwc3_trb *trb)
|
||||
{
|
||||
u32 offset = (char *) trb - (char *) dep->trb_pool;
|
||||
|
||||
@ -368,9 +463,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
return ret;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
struct dwc3_trb_hw *trb_st_hw;
|
||||
struct dwc3_trb_hw *trb_link_hw;
|
||||
struct dwc3_trb trb_link;
|
||||
struct dwc3_trb *trb_st_hw;
|
||||
struct dwc3_trb *trb_link;
|
||||
|
||||
ret = dwc3_gadget_set_xfer_resource(dwc, dep);
|
||||
if (ret)
|
||||
@ -390,15 +484,15 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
|
||||
memset(&trb_link, 0, sizeof(trb_link));
|
||||
|
||||
/* Link TRB for ISOC. The HWO but is never reset */
|
||||
/* Link TRB for ISOC. The HWO bit is never reset */
|
||||
trb_st_hw = &dep->trb_pool[0];
|
||||
|
||||
trb_link.bplh = dwc3_trb_dma_offset(dep, trb_st_hw);
|
||||
trb_link.trbctl = DWC3_TRBCTL_LINK_TRB;
|
||||
trb_link.hwo = true;
|
||||
trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
|
||||
|
||||
trb_link_hw = &dep->trb_pool[DWC3_TRB_NUM - 1];
|
||||
dwc3_trb_to_hw(&trb_link, trb_link_hw);
|
||||
trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
|
||||
trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
|
||||
trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
|
||||
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -440,6 +534,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
||||
|
||||
dep->stream_capable = false;
|
||||
dep->desc = NULL;
|
||||
dep->endpoint.desc = NULL;
|
||||
dep->comp_desc = NULL;
|
||||
dep->type = 0;
|
||||
dep->flags = 0;
|
||||
@ -485,16 +580,16 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
||||
|
||||
switch (usb_endpoint_type(desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
strncat(dep->name, "-control", sizeof(dep->name));
|
||||
strlcat(dep->name, "-control", sizeof(dep->name));
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
strncat(dep->name, "-isoc", sizeof(dep->name));
|
||||
strlcat(dep->name, "-isoc", sizeof(dep->name));
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
strncat(dep->name, "-bulk", sizeof(dep->name));
|
||||
strlcat(dep->name, "-bulk", sizeof(dep->name));
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
strncat(dep->name, "-int", sizeof(dep->name));
|
||||
strlcat(dep->name, "-int", sizeof(dep->name));
|
||||
break;
|
||||
default:
|
||||
dev_err(dwc->dev, "invalid endpoint transfer type\n");
|
||||
@ -562,7 +657,6 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
|
||||
|
||||
req->epnum = dep->number;
|
||||
req->dep = dep;
|
||||
req->request.dma = DMA_ADDR_INVALID;
|
||||
|
||||
return &req->request;
|
||||
}
|
||||
@ -585,8 +679,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
unsigned length, unsigned last, unsigned chain)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb_hw *trb_hw;
|
||||
struct dwc3_trb trb;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
unsigned int cur_slot;
|
||||
|
||||
@ -595,7 +688,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
length, last ? " last" : "",
|
||||
chain ? " chain" : "");
|
||||
|
||||
trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
cur_slot = dep->free_slot;
|
||||
dep->free_slot++;
|
||||
|
||||
@ -604,40 +697,32 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
usb_endpoint_xfer_isoc(dep->desc))
|
||||
return;
|
||||
|
||||
memset(&trb, 0, sizeof(trb));
|
||||
if (!req->trb) {
|
||||
dwc3_gadget_move_request_queued(req);
|
||||
req->trb = trb_hw;
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw);
|
||||
req->trb = trb;
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
trb.isp_imi = true;
|
||||
trb.csp = true;
|
||||
} else {
|
||||
trb.chn = chain;
|
||||
trb.lst = last;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
|
||||
trb.sid_sofn = req->request.stream_id;
|
||||
trb->size = DWC3_TRB_SIZE_LENGTH(length);
|
||||
trb->bpl = lower_32_bits(dma);
|
||||
trb->bph = upper_32_bits(dma);
|
||||
|
||||
switch (usb_endpoint_type(dep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
|
||||
trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
|
||||
/* IOC every DWC3_TRB_NUM / 4 so we can refill */
|
||||
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
|
||||
trb.ioc = last;
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
trb.trbctl = DWC3_TRBCTL_NORMAL;
|
||||
trb->ctrl = DWC3_TRBCTL_NORMAL;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
@ -647,11 +732,21 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
BUG();
|
||||
}
|
||||
|
||||
trb.length = length;
|
||||
trb.bplh = dma;
|
||||
trb.hwo = true;
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
} else {
|
||||
if (chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CHN;
|
||||
|
||||
dwc3_trb_to_hw(&trb, trb_hw);
|
||||
if (last)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_LST;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
|
||||
|
||||
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -659,14 +754,15 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
* @dep: endpoint for which requests are being prepared
|
||||
* @starting: true if the endpoint is idle and no requests are queued.
|
||||
*
|
||||
* The functions goes through the requests list and setups TRBs for the
|
||||
* transfers. The functions returns once there are not more TRBs available or
|
||||
* it run out of requests.
|
||||
* The function goes through the requests list and sets up TRBs for the
|
||||
* transfers. The function returns once there are no more TRBs available or
|
||||
* it runs out of requests.
|
||||
*/
|
||||
static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
{
|
||||
struct dwc3_request *req, *n;
|
||||
u32 trbs_left;
|
||||
u32 max;
|
||||
unsigned int last_one = 0;
|
||||
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
|
||||
@ -674,9 +770,16 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
/* the first request must not be queued */
|
||||
trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
|
||||
|
||||
/* Can't wrap around on a non-isoc EP since there's no link TRB */
|
||||
if (!usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
|
||||
if (trbs_left > max)
|
||||
trbs_left = max;
|
||||
}
|
||||
|
||||
/*
|
||||
* if busy & slot are equal than it is either full or empty. If we are
|
||||
* starting to proceed requests then we are empty. Otherwise we ar
|
||||
* If busy & slot are equal than it is either full or empty. If we are
|
||||
* starting to process requests then we are empty. Otherwise we are
|
||||
* full and don't do anything
|
||||
*/
|
||||
if (!trbs_left) {
|
||||
@ -687,7 +790,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
* In case we start from scratch, we queue the ISOC requests
|
||||
* starting from slot 1. This is done because we use ring
|
||||
* buffer and have no LST bit to stop us. Instead, we place
|
||||
* IOC bit TRB_NUM/4. We try to avoid to having an interrupt
|
||||
* IOC bit every TRB_NUM/4. We try to avoid having an interrupt
|
||||
* after the first request so we start at slot 1 and have
|
||||
* 7 requests proceed before we hit the first IOC.
|
||||
* Other transfer types don't use the ring buffer and are
|
||||
@ -723,8 +826,8 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
length = sg_dma_len(s);
|
||||
dma = sg_dma_address(s);
|
||||
|
||||
if (i == (request->num_mapped_sgs - 1)
|
||||
|| sg_is_last(s)) {
|
||||
if (i == (request->num_mapped_sgs - 1) ||
|
||||
sg_is_last(s)) {
|
||||
last_one = true;
|
||||
chain = false;
|
||||
}
|
||||
@ -792,8 +895,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
dwc3_prepare_trbs(dep, start_new);
|
||||
|
||||
/*
|
||||
* req points to the first request where HWO changed
|
||||
* from 0 to 1
|
||||
* req points to the first request where HWO changed from 0 to 1
|
||||
*/
|
||||
req = next_request(&dep->req_queued);
|
||||
}
|
||||
@ -819,9 +921,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
/*
|
||||
* FIXME we need to iterate over the list of requests
|
||||
* here and stop, unmap, free and del each of the linked
|
||||
* requests instead of we do now.
|
||||
* requests instead of what we do now.
|
||||
*/
|
||||
dwc3_unmap_buffer_from_dma(req);
|
||||
usb_gadget_unmap_request(&dwc->gadget, &req->request,
|
||||
req->direction);
|
||||
list_del(&req->list);
|
||||
return ret;
|
||||
}
|
||||
@ -837,6 +940,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
|
||||
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret;
|
||||
|
||||
req->request.actual = 0;
|
||||
req->request.status = -EINPROGRESS;
|
||||
req->direction = dep->direction;
|
||||
@ -852,9 +958,13 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
* particular token from the Host side.
|
||||
*
|
||||
* This will also avoid Host cancelling URBs due to too
|
||||
* many NACKs.
|
||||
* many NAKs.
|
||||
*/
|
||||
dwc3_map_buffer_to_dma(req);
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->direction);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_add_tail(&req->list, &dep->request_list);
|
||||
|
||||
/*
|
||||
@ -874,11 +984,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
int start_trans;
|
||||
|
||||
start_trans = 1;
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
dep->flags & DWC3_EP_BUSY)
|
||||
if (usb_endpoint_xfer_isoc(dep->desc) &&
|
||||
(dep->flags & DWC3_EP_BUSY))
|
||||
start_trans = 0;
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
@ -1031,8 +1141,12 @@ out:
|
||||
static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
|
||||
{
|
||||
struct dwc3_ep *dep = to_dwc3_ep(ep);
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dep->flags |= DWC3_EP_WEDGE;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return dwc3_gadget_ep_set_halt(ep, 1);
|
||||
}
|
||||
@ -1122,26 +1236,20 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
||||
/*
|
||||
* Switch link state to Recovery. In HS/FS/LS this means
|
||||
* RemoteWakeup Request
|
||||
*/
|
||||
reg |= DWC3_DCTL_ULSTCHNG_RECOVERY;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
/* wait for at least 2000us */
|
||||
usleep_range(2000, 2500);
|
||||
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
|
||||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "failed to put link in Recovery\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* write zeroes to Link Change Request */
|
||||
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
/* pool until Link State change to ON */
|
||||
/* poll until Link State changes to ON */
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
while (!(time_after(jiffies, timeout))) {
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
/* in HS, means ON */
|
||||
@ -1164,8 +1272,11 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
|
||||
int is_selfpowered)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->is_selfpowered = !!is_selfpowered;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1176,10 +1287,13 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||
u32 timeout = 500;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (is_on)
|
||||
reg |= DWC3_DCTL_RUN_STOP;
|
||||
else
|
||||
if (is_on) {
|
||||
reg &= ~DWC3_DCTL_TRGTULST_MASK;
|
||||
reg |= (DWC3_DCTL_RUN_STOP
|
||||
| DWC3_DCTL_TRGTULST_RX_DET);
|
||||
} else {
|
||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
@ -1386,7 +1500,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event, int status)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_trb trb;
|
||||
struct dwc3_trb *trb;
|
||||
unsigned int count;
|
||||
unsigned int s_pkt = 0;
|
||||
|
||||
@ -1397,20 +1511,20 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
return 1;
|
||||
}
|
||||
|
||||
dwc3_trb_to_nat(req->trb, &trb);
|
||||
trb = req->trb;
|
||||
|
||||
if (trb.hwo && status != -ESHUTDOWN)
|
||||
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
|
||||
/*
|
||||
* We continue despite the error. There is not much we
|
||||
* can do. If we don't clean in up we loop for ever. If
|
||||
* we skip the TRB than it gets overwritten reused after
|
||||
* a while since we use them in a ring buffer. a BUG()
|
||||
* would help. Lets hope that if this occures, someone
|
||||
* can do. If we don't clean it up we loop forever. If
|
||||
* we skip the TRB then it gets overwritten after a
|
||||
* while since we use them in a ring buffer. A BUG()
|
||||
* would help. Lets hope that if this occurs, someone
|
||||
* fixes the root cause instead of looking away :)
|
||||
*/
|
||||
dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
|
||||
dep->name, req->trb);
|
||||
count = trb.length;
|
||||
count = trb->size & DWC3_TRB_SIZE_MASK;
|
||||
|
||||
if (dep->direction) {
|
||||
if (count) {
|
||||
@ -1434,13 +1548,16 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
if (s_pkt)
|
||||
break;
|
||||
if ((event->status & DEPEVT_STATUS_LST) && trb.lst)
|
||||
if ((event->status & DEPEVT_STATUS_LST) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_LST))
|
||||
break;
|
||||
if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc)
|
||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc)
|
||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -1455,11 +1572,9 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
||||
if (event->status & DEPEVT_STATUS_BUSERR)
|
||||
status = -ECONNRESET;
|
||||
|
||||
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
|
||||
if (clean_busy) {
|
||||
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
|
||||
if (clean_busy)
|
||||
dep->flags &= ~DWC3_EP_BUSY;
|
||||
dep->res_trans_idx = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
|
||||
@ -1490,7 +1605,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
||||
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
||||
struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
|
||||
{
|
||||
u32 uf;
|
||||
u32 uf, mask;
|
||||
|
||||
if (list_empty(&dep->request_list)) {
|
||||
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
|
||||
@ -1498,16 +1613,10 @@ static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->parameters) {
|
||||
u32 mask;
|
||||
|
||||
mask = ~(dep->interval - 1);
|
||||
uf = event->parameters & mask;
|
||||
/* 4 micro frames in the future */
|
||||
uf += dep->interval * 4;
|
||||
} else {
|
||||
uf = 0;
|
||||
}
|
||||
mask = ~(dep->interval - 1);
|
||||
uf = event->parameters & mask;
|
||||
/* 4 micro frames in the future */
|
||||
uf += dep->interval * 4;
|
||||
|
||||
__dwc3_gadget_kick_transfer(dep, uf, 1);
|
||||
}
|
||||
@ -1519,8 +1628,8 @@ static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
|
||||
struct dwc3_event_depevt mod_ev = *event;
|
||||
|
||||
/*
|
||||
* We were asked to remove one requests. It is possible that this
|
||||
* request and a few other were started together and have the same
|
||||
* We were asked to remove one request. It is possible that this
|
||||
* request and a few others were started together and have the same
|
||||
* transfer index. Since we stopped the complete endpoint we don't
|
||||
* know how many requests were already completed (and not yet)
|
||||
* reported and how could be done (later). We purge them all until
|
||||
@ -1529,7 +1638,7 @@ static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
|
||||
mod_ev.status = DEPEVT_STATUS_LST;
|
||||
dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
|
||||
dep->flags &= ~DWC3_EP_BUSY;
|
||||
/* pending requets are ignored and are queued on XferNotReady */
|
||||
/* pending requests are ignored and are queued on XferNotReady */
|
||||
}
|
||||
|
||||
static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
|
||||
@ -1570,6 +1679,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
|
||||
switch (event->endpoint_event) {
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
dep->res_trans_idx = 0;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
|
||||
dep->name);
|
||||
@ -1594,7 +1705,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
int ret;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: reason %s\n",
|
||||
dep->name, event->status
|
||||
dep->name, event->status &
|
||||
DEPEVT_STATUS_TRANSFER_ACTIVE
|
||||
? "Transfer Active"
|
||||
: "Transfer Not Active");
|
||||
|
||||
@ -1805,6 +1917,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
dwc->test_mode = false;
|
||||
|
||||
dwc3_stop_active_transfers(dwc);
|
||||
dwc3_clear_stall_all_ep(dwc);
|
||||
@ -2082,7 +2195,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
|
||||
while (left > 0) {
|
||||
union dwc3_event event;
|
||||
|
||||
memcpy(&event.raw, (evt->buf + evt->lpos), sizeof(event.raw));
|
||||
event.raw = *(u32 *) (evt->buf + evt->lpos);
|
||||
|
||||
dwc3_process_event_entry(dwc, &event);
|
||||
/*
|
||||
* XXX we wrap around correctly to the next entry as almost all
|
||||
@ -2123,7 +2237,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
||||
|
||||
/**
|
||||
* dwc3_gadget_init - Initializes gadget related registers
|
||||
* @dwc: Pointer to out controller context structure
|
||||
* @dwc: pointer to our controller context structure
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno.
|
||||
*/
|
||||
@ -2149,9 +2263,8 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc->setup_buf = dma_alloc_coherent(dwc->dev,
|
||||
sizeof(*dwc->setup_buf) * 2,
|
||||
&dwc->setup_buf_addr, GFP_KERNEL);
|
||||
dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2,
|
||||
GFP_KERNEL);
|
||||
if (!dwc->setup_buf) {
|
||||
dev_err(dwc->dev, "failed to allocate setup buffer\n");
|
||||
ret = -ENOMEM;
|
||||
@ -2242,8 +2355,7 @@ err4:
|
||||
dwc->ep0_bounce_addr);
|
||||
|
||||
err3:
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
|
||||
dwc->setup_buf, dwc->setup_buf_addr);
|
||||
kfree(dwc->setup_buf);
|
||||
|
||||
err2:
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
||||
@ -2272,8 +2384,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
|
||||
dwc->ep0_bounce_addr);
|
||||
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
|
||||
dwc->setup_buf, dwc->setup_buf_addr);
|
||||
kfree(dwc->setup_buf);
|
||||
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
|
@ -100,6 +100,9 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event);
|
||||
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
||||
@ -108,8 +111,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
void dwc3_map_buffer_to_dma(struct dwc3_request *req);
|
||||
void dwc3_unmap_buffer_from_dma(struct dwc3_request *req);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
@ -53,7 +53,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
struct platform_device *xhci;
|
||||
int ret;
|
||||
|
||||
xhci = platform_device_alloc("xhci", -1);
|
||||
xhci = platform_device_alloc("xhci-hcd", -1);
|
||||
if (!xhci) {
|
||||
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -599,16 +599,29 @@ config USB_AUDIO
|
||||
depends on SND
|
||||
select SND_PCM
|
||||
help
|
||||
Gadget Audio is compatible with USB Audio Class specification 1.0.
|
||||
It will include at least one AudioControl interface, zero or more
|
||||
AudioStream interface and zero or more MIDIStream interface.
|
||||
|
||||
Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to
|
||||
playback or capture audio stream.
|
||||
This Gadget Audio driver is compatible with USB Audio Class
|
||||
specification 2.0. It implements 1 AudioControl interface,
|
||||
1 AudioStreaming Interface each for USB-OUT and USB-IN.
|
||||
Number of channels, sample rate and sample size can be
|
||||
specified as module parameters.
|
||||
This driver doesn't expect any real Audio codec to be present
|
||||
on the device - the audio streams are simply sinked to and
|
||||
sourced from a virtual ALSA sound card created. The user-space
|
||||
application may choose to do whatever it wants with the data
|
||||
received from the USB Host and choose to provide whatever it
|
||||
wants as audio data to the USB Host.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_audio".
|
||||
|
||||
config GADGET_UAC1
|
||||
bool "UAC 1.0 (Legacy)"
|
||||
depends on USB_AUDIO
|
||||
help
|
||||
If you instead want older UAC Spec-1.0 driver that also has audio
|
||||
paths hardwired to the Audio codec chip on-board and doesn't work
|
||||
without one.
|
||||
|
||||
config USB_ETH
|
||||
tristate "Ethernet Gadget (with CDC Ethernet support)"
|
||||
depends on NET
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
/* Driver strings */
|
||||
#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"
|
||||
#define UDC_DRIVER_VERSION_STRING "01.00.0206 - $Revision: #3 $"
|
||||
#define UDC_DRIVER_VERSION_STRING "01.00.0206"
|
||||
|
||||
/* system */
|
||||
#include <linux/module.h>
|
||||
@ -140,7 +140,7 @@ static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
|
||||
|
||||
/* endpoint names used for print */
|
||||
static const char ep0_string[] = "ep0in";
|
||||
static const char *ep_string[] = {
|
||||
static const char *const ep_string[] = {
|
||||
ep0_string,
|
||||
"ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk",
|
||||
"ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk",
|
||||
@ -204,9 +204,8 @@ static void print_regs(struct udc *dev)
|
||||
DBG(dev, "DMA mode = BF (buffer fill mode)\n");
|
||||
dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");
|
||||
}
|
||||
if (!use_dma) {
|
||||
if (!use_dma)
|
||||
dev_info(&dev->pdev->dev, "FIFO mode\n");
|
||||
}
|
||||
DBG(dev, "-------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
@ -445,6 +444,7 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
|
||||
|
||||
VDBG(ep->dev, "ep-%d reset\n", ep->num);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->ep.ops = &udc_ep_ops;
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
|
||||
@ -569,9 +569,8 @@ udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
|
||||
VDBG(ep->dev, "req->td_data=%p\n", req->td_data);
|
||||
|
||||
/* free dma chain if created */
|
||||
if (req->chain_len > 1) {
|
||||
if (req->chain_len > 1)
|
||||
udc_free_dma_chain(ep->dev, req);
|
||||
}
|
||||
|
||||
pci_pool_free(ep->dev->data_requests, req->td_data,
|
||||
req->td_phys);
|
||||
@ -639,9 +638,8 @@ udc_txfifo_write(struct udc_ep *ep, struct usb_request *req)
|
||||
bytes = remaining;
|
||||
|
||||
/* dwords first */
|
||||
for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) {
|
||||
for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
|
||||
writel(*(buf + i), ep->txfifo);
|
||||
}
|
||||
|
||||
/* remaining bytes must be written by byte access */
|
||||
for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
|
||||
@ -660,9 +658,8 @@ static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords)
|
||||
|
||||
VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords);
|
||||
|
||||
for (i = 0; i < dwords; i++) {
|
||||
for (i = 0; i < dwords; i++)
|
||||
*(buf + i) = readl(dev->rxfifo);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -675,9 +672,8 @@ static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes)
|
||||
VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes);
|
||||
|
||||
/* dwords first */
|
||||
for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) {
|
||||
for (i = 0; i < bytes / UDC_DWORD_BYTES; i++)
|
||||
*((u32 *)(buf + (i<<2))) = readl(dev->rxfifo);
|
||||
}
|
||||
|
||||
/* remaining bytes must be read by byte access */
|
||||
if (bytes % UDC_DWORD_BYTES) {
|
||||
@ -831,20 +827,8 @@ __acquires(ep->dev->lock)
|
||||
|
||||
dev = ep->dev;
|
||||
/* unmap DMA */
|
||||
if (req->dma_mapping) {
|
||||
if (ep->in)
|
||||
pci_unmap_single(dev->pdev,
|
||||
req->req.dma,
|
||||
req->req.length,
|
||||
PCI_DMA_TODEVICE);
|
||||
else
|
||||
pci_unmap_single(dev->pdev,
|
||||
req->req.dma,
|
||||
req->req.length,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
req->dma_mapping = 0;
|
||||
req->req.dma = DMA_DONT_USE;
|
||||
}
|
||||
if (ep->dma)
|
||||
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
|
||||
|
||||
halted = ep->halted;
|
||||
ep->halted = 1;
|
||||
@ -897,9 +881,8 @@ static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
|
||||
struct udc_data_dma *td;
|
||||
|
||||
td = req->td_data;
|
||||
while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) {
|
||||
while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L)))
|
||||
td = phys_to_virt(td->next);
|
||||
}
|
||||
|
||||
return td;
|
||||
|
||||
@ -949,21 +932,18 @@ static int udc_create_dma_chain(
|
||||
dma_addr = DMA_DONT_USE;
|
||||
|
||||
/* unset L bit in first desc for OUT */
|
||||
if (!ep->in) {
|
||||
if (!ep->in)
|
||||
req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
|
||||
}
|
||||
|
||||
/* alloc only new desc's if not already available */
|
||||
len = req->req.length / ep->ep.maxpacket;
|
||||
if (req->req.length % ep->ep.maxpacket) {
|
||||
if (req->req.length % ep->ep.maxpacket)
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len > req->chain_len) {
|
||||
/* shorter chain already allocated before */
|
||||
if (req->chain_len > 1) {
|
||||
if (req->chain_len > 1)
|
||||
udc_free_dma_chain(ep->dev, req);
|
||||
}
|
||||
req->chain_len = len;
|
||||
create_new_chain = 1;
|
||||
}
|
||||
@ -1006,11 +986,12 @@ static int udc_create_dma_chain(
|
||||
|
||||
/* link td and assign tx bytes */
|
||||
if (i == buf_len) {
|
||||
if (create_new_chain) {
|
||||
if (create_new_chain)
|
||||
req->td_data->next = dma_addr;
|
||||
} else {
|
||||
/* req->td_data->next = virt_to_phys(td); */
|
||||
}
|
||||
/*
|
||||
else
|
||||
req->td_data->next = virt_to_phys(td);
|
||||
*/
|
||||
/* write tx bytes */
|
||||
if (ep->in) {
|
||||
/* first desc */
|
||||
@ -1024,11 +1005,12 @@ static int udc_create_dma_chain(
|
||||
UDC_DMA_IN_STS_TXBYTES);
|
||||
}
|
||||
} else {
|
||||
if (create_new_chain) {
|
||||
if (create_new_chain)
|
||||
last->next = dma_addr;
|
||||
} else {
|
||||
/* last->next = virt_to_phys(td); */
|
||||
}
|
||||
/*
|
||||
else
|
||||
last->next = virt_to_phys(td);
|
||||
*/
|
||||
if (ep->in) {
|
||||
/* write tx bytes */
|
||||
td->status = AMD_ADDBITS(td->status,
|
||||
@ -1095,20 +1077,11 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
/* map dma (usually done before) */
|
||||
if (ep->dma && usbreq->length != 0
|
||||
&& (usbreq->dma == DMA_DONT_USE || usbreq->dma == 0)) {
|
||||
if (ep->dma) {
|
||||
VDBG(dev, "DMA map req %p\n", req);
|
||||
if (ep->in)
|
||||
usbreq->dma = pci_map_single(dev->pdev,
|
||||
usbreq->buf,
|
||||
usbreq->length,
|
||||
PCI_DMA_TODEVICE);
|
||||
else
|
||||
usbreq->dma = pci_map_single(dev->pdev,
|
||||
usbreq->buf,
|
||||
usbreq->length,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
req->dma_mapping = 1;
|
||||
retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n",
|
||||
@ -1479,11 +1452,10 @@ static int startup_registers(struct udc *dev)
|
||||
|
||||
/* program speed */
|
||||
tmp = readl(&dev->regs->cfg);
|
||||
if (use_fullspeed) {
|
||||
if (use_fullspeed)
|
||||
tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
|
||||
} else {
|
||||
else
|
||||
tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
|
||||
}
|
||||
writel(tmp, &dev->regs->cfg);
|
||||
|
||||
return 0;
|
||||
@ -1504,9 +1476,8 @@ static void udc_basic_init(struct udc *dev)
|
||||
mod_timer(&udc_timer, jiffies - 1);
|
||||
}
|
||||
/* stop poll stall timer */
|
||||
if (timer_pending(&udc_pollstall_timer)) {
|
||||
if (timer_pending(&udc_pollstall_timer))
|
||||
mod_timer(&udc_pollstall_timer, jiffies - 1);
|
||||
}
|
||||
/* disable DMA */
|
||||
tmp = readl(&dev->regs->ctl);
|
||||
tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
|
||||
@ -1540,11 +1511,10 @@ static void udc_setup_endpoints(struct udc *dev)
|
||||
/* read enum speed */
|
||||
tmp = readl(&dev->regs->sts);
|
||||
tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
|
||||
if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) {
|
||||
if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
|
||||
dev->gadget.speed = USB_SPEED_HIGH;
|
||||
} else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) {
|
||||
else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
|
||||
dev->gadget.speed = USB_SPEED_FULL;
|
||||
}
|
||||
|
||||
/* set basic ep parameters */
|
||||
for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
|
||||
@ -1570,9 +1540,8 @@ static void udc_setup_endpoints(struct udc *dev)
|
||||
* disabling ep interrupts when ENUM interrupt occurs but ep is
|
||||
* not enabled by gadget driver
|
||||
*/
|
||||
if (!ep->desc) {
|
||||
if (!ep->desc)
|
||||
ep_init(dev->regs, ep);
|
||||
}
|
||||
|
||||
if (use_dma) {
|
||||
/*
|
||||
@ -1670,9 +1639,8 @@ static void udc_tasklet_disconnect(unsigned long par)
|
||||
spin_lock(&dev->lock);
|
||||
|
||||
/* empty queues */
|
||||
for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
|
||||
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
|
||||
empty_req_queue(&dev->ep[tmp]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1746,9 +1714,8 @@ static void udc_timer_function(unsigned long v)
|
||||
* open the fifo
|
||||
*/
|
||||
udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV;
|
||||
if (!stop_timer) {
|
||||
if (!stop_timer)
|
||||
add_timer(&udc_timer);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* fifo contains data now, setup timer for opening
|
||||
@ -1760,9 +1727,8 @@ static void udc_timer_function(unsigned long v)
|
||||
set_rde++;
|
||||
/* debug: lhadmot_timer_start = 221070 */
|
||||
udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS;
|
||||
if (!stop_timer) {
|
||||
if (!stop_timer)
|
||||
add_timer(&udc_timer);
|
||||
}
|
||||
}
|
||||
|
||||
} else
|
||||
@ -1907,19 +1873,17 @@ static void activate_control_endpoints(struct udc *dev)
|
||||
mod_timer(&udc_timer, jiffies - 1);
|
||||
}
|
||||
/* stop pollstall timer */
|
||||
if (timer_pending(&udc_pollstall_timer)) {
|
||||
if (timer_pending(&udc_pollstall_timer))
|
||||
mod_timer(&udc_pollstall_timer, jiffies - 1);
|
||||
}
|
||||
/* enable DMA */
|
||||
tmp = readl(&dev->regs->ctl);
|
||||
tmp |= AMD_BIT(UDC_DEVCTL_MODE)
|
||||
| AMD_BIT(UDC_DEVCTL_RDE)
|
||||
| AMD_BIT(UDC_DEVCTL_TDE);
|
||||
if (use_dma_bufferfill_mode) {
|
||||
if (use_dma_bufferfill_mode)
|
||||
tmp |= AMD_BIT(UDC_DEVCTL_BF);
|
||||
} else if (use_dma_ppb_du) {
|
||||
else if (use_dma_ppb_du)
|
||||
tmp |= AMD_BIT(UDC_DEVCTL_DU);
|
||||
}
|
||||
writel(tmp, &dev->regs->ctl);
|
||||
}
|
||||
|
||||
@ -2104,9 +2068,8 @@ static void udc_ep0_set_rde(struct udc *dev)
|
||||
udc_timer.expires =
|
||||
jiffies + HZ/UDC_RDE_TIMER_DIV;
|
||||
set_rde = 1;
|
||||
if (!stop_timer) {
|
||||
if (!stop_timer)
|
||||
add_timer(&udc_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2131,7 +2094,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
|
||||
if (use_dma) {
|
||||
/* BNA event ? */
|
||||
if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
|
||||
DBG(dev, "BNA ep%dout occurred - DESPTR = %x \n",
|
||||
DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n",
|
||||
ep->num, readl(&ep->regs->desptr));
|
||||
/* clear BNA */
|
||||
writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts);
|
||||
@ -2294,9 +2257,8 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
|
||||
jiffies
|
||||
+ HZ*UDC_RDE_TIMER_SECONDS;
|
||||
set_rde = 1;
|
||||
if (!stop_timer) {
|
||||
if (!stop_timer)
|
||||
add_timer(&udc_timer);
|
||||
}
|
||||
}
|
||||
if (ep->num != UDC_EP0OUT_IX)
|
||||
dev->data_ep_queued = 0;
|
||||
@ -2318,9 +2280,8 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix)
|
||||
/* check pending CNAKS */
|
||||
if (cnak_pending) {
|
||||
/* CNAk processing when rxfifo empty only */
|
||||
if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
|
||||
if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
|
||||
udc_process_cnak_queue(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* clear OUT bits in ep status */
|
||||
@ -2348,7 +2309,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
/* BNA ? */
|
||||
if (epsts & AMD_BIT(UDC_EPSTS_BNA)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"BNA ep%din occurred - DESPTR = %08lx \n",
|
||||
"BNA ep%din occurred - DESPTR = %08lx\n",
|
||||
ep->num,
|
||||
(unsigned long) readl(&ep->regs->desptr));
|
||||
|
||||
@ -2361,7 +2322,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
/* HE event ? */
|
||||
if (epsts & AMD_BIT(UDC_EPSTS_HE)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"HE ep%dn occurred - DESPTR = %08lx \n",
|
||||
"HE ep%dn occurred - DESPTR = %08lx\n",
|
||||
ep->num, (unsigned long) readl(&ep->regs->desptr));
|
||||
|
||||
/* clear HE */
|
||||
@ -2427,9 +2388,9 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
|
||||
/* write fifo */
|
||||
udc_txfifo_write(ep, &req->req);
|
||||
len = req->req.length - req->req.actual;
|
||||
if (len > ep->ep.maxpacket)
|
||||
len = ep->ep.maxpacket;
|
||||
req->req.actual += len;
|
||||
if (len > ep->ep.maxpacket)
|
||||
len = ep->ep.maxpacket;
|
||||
req->req.actual += len;
|
||||
if (req->req.actual == req->req.length
|
||||
|| (len != ep->ep.maxpacket)) {
|
||||
/* complete req */
|
||||
@ -2581,9 +2542,8 @@ __acquires(dev->lock)
|
||||
if (!timer_pending(&udc_timer)) {
|
||||
udc_timer.expires = jiffies +
|
||||
HZ/UDC_RDE_TIMER_DIV;
|
||||
if (!stop_timer) {
|
||||
if (!stop_timer)
|
||||
add_timer(&udc_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2697,9 +2657,8 @@ __acquires(dev->lock)
|
||||
/* check pending CNAKS */
|
||||
if (cnak_pending) {
|
||||
/* CNAk processing when rxfifo empty only */
|
||||
if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
|
||||
if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))
|
||||
udc_process_cnak_queue(dev);
|
||||
}
|
||||
}
|
||||
|
||||
finished:
|
||||
@ -2723,7 +2682,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev)
|
||||
tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
|
||||
/* DMA completion */
|
||||
if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
|
||||
VDBG(dev, "isr: TDC clear \n");
|
||||
VDBG(dev, "isr: TDC clear\n");
|
||||
ret_val = IRQ_HANDLED;
|
||||
|
||||
/* clear TDC bit */
|
||||
@ -3426,7 +3385,7 @@ static int udc_remote_wakeup(struct udc *dev)
|
||||
}
|
||||
|
||||
/* PCI device parameters */
|
||||
static const struct pci_device_id pci_id[] = {
|
||||
static DEFINE_PCI_DEVICE_TABLE(pci_id) = {
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
|
||||
.class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <mach/hardware.h>
|
||||
@ -558,6 +557,7 @@ static int at91_ep_disable (struct usb_ep * _ep)
|
||||
|
||||
/* restore the endpoint's pristine config */
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->ep.maxpacket = ep->maxpacket;
|
||||
|
||||
/* reset fifos and endpoint */
|
||||
|
@ -659,6 +659,7 @@ static int usba_ep_disable(struct usb_ep *_ep)
|
||||
return -EINVAL;
|
||||
}
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
|
||||
list_splice_init(&ep->queue, &req_list);
|
||||
if (ep->can_dma) {
|
||||
|
@ -14,10 +14,8 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
|
||||
#define DRIVER_DESC "Linux USB Audio Gadget"
|
||||
#define DRIVER_VERSION "Dec 18, 2008"
|
||||
#define DRIVER_VERSION "Feb 2, 2012"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -33,8 +31,36 @@
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "u_audio.c"
|
||||
#include "f_audio.c"
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *audio_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
#include "u_uac1.h"
|
||||
#include "u_uac1.c"
|
||||
#include "f_uac1.c"
|
||||
#else
|
||||
#include "f_uac2.c"
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -54,9 +80,15 @@ static struct usb_device_descriptor device_desc = {
|
||||
|
||||
.bcdUSB = __constant_cpu_to_le16(0x200),
|
||||
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
#else
|
||||
.bDeviceClass = USB_CLASS_MISC,
|
||||
.bDeviceSubClass = 0x02,
|
||||
.bDeviceProtocol = 0x01,
|
||||
#endif
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id defaults change according to what configs
|
||||
@ -108,6 +140,9 @@ static struct usb_configuration audio_config_driver = {
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
.unbind = uac2_unbind_config,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -157,7 +192,9 @@ fail:
|
||||
|
||||
static int __exit audio_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
gaudio_cleanup();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -37,10 +37,10 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
|
||||
* Put the transceiver in non-driving mode. Otherwise host
|
||||
* may not detect soft-disconnection.
|
||||
*/
|
||||
val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL);
|
||||
val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL);
|
||||
val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
|
||||
val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
|
||||
otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
|
||||
usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "unknown ci13xxx_udc event\n");
|
||||
|
@ -2181,6 +2181,7 @@ static int ep_disable(struct usb_ep *ep)
|
||||
} while (mEp->dir != direction);
|
||||
|
||||
mEp->desc = NULL;
|
||||
mEp->ep.desc = NULL;
|
||||
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
return retval;
|
||||
@ -2537,7 +2538,7 @@ static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
|
||||
|
||||
if (udc->transceiver)
|
||||
return otg_set_power(udc->transceiver, mA);
|
||||
return usb_phy_set_power(udc->transceiver, mA);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
@ -2900,7 +2901,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
||||
if (retval < 0)
|
||||
goto free_udc;
|
||||
|
||||
udc->transceiver = otg_get_transceiver();
|
||||
udc->transceiver = usb_get_transceiver();
|
||||
|
||||
if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
|
||||
if (udc->transceiver == NULL) {
|
||||
@ -2928,7 +2929,8 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
||||
goto unreg_device;
|
||||
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||
&udc->gadget);
|
||||
if (retval)
|
||||
goto remove_dbg;
|
||||
}
|
||||
@ -2945,8 +2947,8 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
||||
|
||||
remove_trans:
|
||||
if (udc->transceiver) {
|
||||
otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
|
||||
usb_put_transceiver(udc->transceiver);
|
||||
}
|
||||
|
||||
err("error = %i", retval);
|
||||
@ -2958,7 +2960,7 @@ unreg_device:
|
||||
device_unregister(&udc->gadget.dev);
|
||||
put_transceiver:
|
||||
if (udc->transceiver)
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
usb_put_transceiver(udc->transceiver);
|
||||
free_udc:
|
||||
kfree(udc);
|
||||
_udc = NULL;
|
||||
@ -2981,8 +2983,8 @@ static void udc_remove(void)
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
|
||||
if (udc->transceiver) {
|
||||
otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
|
||||
usb_put_transceiver(udc->transceiver);
|
||||
}
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
dbg_remove_files(&udc->gadget.dev);
|
||||
|
@ -136,7 +136,7 @@ struct ci13xxx {
|
||||
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
|
||||
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
|
||||
int vbus_active; /* is VBUS active */
|
||||
struct otg_transceiver *transceiver; /* Transceiver struct */
|
||||
struct usb_phy *transceiver; /* Transceiver struct */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -275,24 +275,24 @@ struct usb_ep *usb_ep_autoconfig_ss(
|
||||
/* ep-e, ep-f are PIO with only 64 byte fifos */
|
||||
ep = find_ep (gadget, "ep-e");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
return ep;
|
||||
goto found_ep;
|
||||
ep = find_ep (gadget, "ep-f");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
return ep;
|
||||
goto found_ep;
|
||||
|
||||
} else if (gadget_is_goku (gadget)) {
|
||||
if (USB_ENDPOINT_XFER_INT == type) {
|
||||
/* single buffering is enough */
|
||||
ep = find_ep(gadget, "ep3-bulk");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
return ep;
|
||||
goto found_ep;
|
||||
} else if (USB_ENDPOINT_XFER_BULK == type
|
||||
&& (USB_DIR_IN & desc->bEndpointAddress)) {
|
||||
/* DMA may be available */
|
||||
ep = find_ep(gadget, "ep2-bulk");
|
||||
if (ep && ep_matches(gadget, ep, desc,
|
||||
ep_comp))
|
||||
return ep;
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLACKFIN
|
||||
@ -311,18 +311,22 @@ struct usb_ep *usb_ep_autoconfig_ss(
|
||||
} else
|
||||
ep = NULL;
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
return ep;
|
||||
goto found_ep;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Second, look at endpoints until an unclaimed one looks usable */
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
if (ep_matches(gadget, ep, desc, ep_comp))
|
||||
return ep;
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
/* Fail */
|
||||
return NULL;
|
||||
found_ep:
|
||||
ep->desc = NULL;
|
||||
ep->comp_desc = NULL;
|
||||
return ep;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Copyright (C) 2008 by David Brownell
|
||||
* Copyright (C) 2008 by Nokia Corporation
|
||||
* Copyright (C) 2009 by Samsung Electronics
|
||||
* Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
|
||||
* Author: Michal Nazarewicz (mina86@mina86.com)
|
||||
*
|
||||
* This software is distributed under the terms of the GNU General
|
||||
* Public License ("GPL") as published by the Free Software Foundation,
|
||||
@ -237,6 +237,42 @@ static struct usb_descriptor_header *acm_hs_function[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_ss_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor acm_ss_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
|
||||
.bLength = sizeof acm_ss_bulk_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *acm_ss_function[] = {
|
||||
(struct usb_descriptor_header *) &acm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_control_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_header_desc,
|
||||
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_descriptor,
|
||||
(struct usb_descriptor_header *) &acm_union_desc,
|
||||
(struct usb_descriptor_header *) &acm_hs_notify_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
|
||||
(struct usb_descriptor_header *) &acm_data_interface_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_in_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_out_desc,
|
||||
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string descriptors: */
|
||||
|
||||
#define ACM_CTRL_IDX 0
|
||||
@ -643,9 +679,21 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
/* copy descriptors */
|
||||
f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
|
||||
}
|
||||
if (gadget_is_superspeed(c->cdev->gadget)) {
|
||||
acm_ss_in_desc.bEndpointAddress =
|
||||
acm_fs_in_desc.bEndpointAddress;
|
||||
acm_ss_out_desc.bEndpointAddress =
|
||||
acm_fs_out_desc.bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->ss_descriptors = usb_copy_descriptors(acm_ss_function);
|
||||
if (!f->ss_descriptors)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
acm->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
acm->port.in->name, acm->port.out->name,
|
||||
acm->notify->name);
|
||||
@ -675,6 +723,8 @@ acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
if (gadget_is_superspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
kfree(acm);
|
||||
|
@ -97,6 +97,20 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g)
|
||||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_assoc_descriptor
|
||||
ecm_iad_descriptor = {
|
||||
.bLength = sizeof ecm_iad_descriptor,
|
||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||
|
||||
/* .bFirstInterface = DYNAMIC, */
|
||||
.bInterfaceCount = 2, /* control + data */
|
||||
.bFunctionClass = USB_CLASS_COMM,
|
||||
.bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
|
||||
.bFunctionProtocol = USB_CDC_PROTO_NONE,
|
||||
/* .iFunction = DYNAMIC */
|
||||
};
|
||||
|
||||
|
||||
static struct usb_interface_descriptor ecm_control_intf = {
|
||||
.bLength = sizeof ecm_control_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
@ -199,6 +213,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc = {
|
||||
|
||||
static struct usb_descriptor_header *ecm_fs_function[] = {
|
||||
/* CDC ECM control descriptors */
|
||||
(struct usb_descriptor_header *) &ecm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &ecm_control_intf,
|
||||
(struct usb_descriptor_header *) &ecm_header_desc,
|
||||
(struct usb_descriptor_header *) &ecm_union_desc,
|
||||
@ -247,6 +262,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc = {
|
||||
|
||||
static struct usb_descriptor_header *ecm_hs_function[] = {
|
||||
/* CDC ECM control descriptors */
|
||||
(struct usb_descriptor_header *) &ecm_iad_descriptor,
|
||||
(struct usb_descriptor_header *) &ecm_control_intf,
|
||||
(struct usb_descriptor_header *) &ecm_header_desc,
|
||||
(struct usb_descriptor_header *) &ecm_union_desc,
|
||||
@ -339,6 +355,7 @@ static struct usb_string ecm_string_defs[] = {
|
||||
[0].s = "CDC Ethernet Control Model (ECM)",
|
||||
[1].s = NULL /* DYNAMIC */,
|
||||
[2].s = "CDC Ethernet Data",
|
||||
[3].s = "CDC ECM",
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
@ -674,6 +691,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
ecm->ctrl_id = status;
|
||||
ecm_iad_descriptor.bFirstInterface = status;
|
||||
|
||||
ecm_control_intf.bInterfaceNumber = status;
|
||||
ecm_union_desc.bMasterInterface0 = status;
|
||||
@ -864,6 +882,13 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
return status;
|
||||
ecm_string_defs[1].id = status;
|
||||
ecm_desc.iMACAddress = status;
|
||||
|
||||
/* IAD label */
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
ecm_string_defs[3].id = status;
|
||||
ecm_iad_descriptor.iFunction = status;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
|
@ -2,7 +2,7 @@
|
||||
* f_fs.c -- user mode file system API for USB composite function controllers
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
|
||||
* Author: Michal Nazarewicz <mina86@mina86.com>
|
||||
*
|
||||
* Based on inode.c (GadgetFS) which was:
|
||||
* Copyright (C) 2003-2004 David Brownell
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2003-2008 Alan Stern
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
|
||||
* Author: Michal Nazarewicz <mina86@mina86.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -304,7 +304,6 @@
|
||||
|
||||
static const char fsg_string_interface[] = "Mass Storage";
|
||||
|
||||
#define FSG_NO_INTR_EP 1
|
||||
#define FSG_NO_DEVICE_STRINGS 1
|
||||
#define FSG_NO_OTG 1
|
||||
#define FSG_NO_INTR_EP 1
|
||||
@ -620,7 +619,7 @@ static int fsg_setup(struct usb_function *f,
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
|
||||
case USB_BULK_RESET_REQUEST:
|
||||
case US_BULK_RESET_REQUEST:
|
||||
if (ctrl->bRequestType !=
|
||||
(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
||||
break;
|
||||
@ -636,7 +635,7 @@ static int fsg_setup(struct usb_function *f,
|
||||
raise_exception(fsg->common, FSG_STATE_RESET);
|
||||
return DELAYED_STATUS;
|
||||
|
||||
case USB_BULK_GET_MAX_LUN_REQUEST:
|
||||
case US_BULK_GET_MAX_LUN:
|
||||
if (ctrl->bRequestType !=
|
||||
(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
||||
break;
|
||||
@ -1742,7 +1741,7 @@ static int send_status(struct fsg_common *common)
|
||||
struct fsg_buffhd *bh;
|
||||
struct bulk_cs_wrap *csw;
|
||||
int rc;
|
||||
u8 status = USB_STATUS_PASS;
|
||||
u8 status = US_BULK_STAT_OK;
|
||||
u32 sd, sdinfo = 0;
|
||||
|
||||
/* Wait for the next buffer to become available */
|
||||
@ -1763,11 +1762,11 @@ static int send_status(struct fsg_common *common)
|
||||
|
||||
if (common->phase_error) {
|
||||
DBG(common, "sending phase-error status\n");
|
||||
status = USB_STATUS_PHASE_ERROR;
|
||||
status = US_BULK_STAT_PHASE;
|
||||
sd = SS_INVALID_COMMAND;
|
||||
} else if (sd != SS_NO_SENSE) {
|
||||
DBG(common, "sending command-failure status\n");
|
||||
status = USB_STATUS_FAIL;
|
||||
status = US_BULK_STAT_FAIL;
|
||||
VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
|
||||
" info x%x\n",
|
||||
SK(sd), ASC(sd), ASCQ(sd), sdinfo);
|
||||
@ -1776,12 +1775,12 @@ static int send_status(struct fsg_common *common)
|
||||
/* Store and send the Bulk-only CSW */
|
||||
csw = (void *)bh->buf;
|
||||
|
||||
csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
|
||||
csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
|
||||
csw->Tag = common->tag;
|
||||
csw->Residue = cpu_to_le32(common->residue);
|
||||
csw->Status = status;
|
||||
|
||||
bh->inreq->length = USB_BULK_CS_WRAP_LEN;
|
||||
bh->inreq->length = US_BULK_CS_WRAP_LEN;
|
||||
bh->inreq->zero = 0;
|
||||
if (!start_in_transfer(common, bh))
|
||||
/* Don't know what to do if common->fsg is NULL */
|
||||
@ -2221,7 +2220,7 @@ unknown_cmnd:
|
||||
static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct usb_request *req = bh->outreq;
|
||||
struct fsg_bulk_cb_wrap *cbw = req->buf;
|
||||
struct bulk_cb_wrap *cbw = req->buf;
|
||||
struct fsg_common *common = fsg->common;
|
||||
|
||||
/* Was this a real packet? Should it be ignored? */
|
||||
@ -2229,9 +2228,9 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
return -EINVAL;
|
||||
|
||||
/* Is the CBW valid? */
|
||||
if (req->actual != USB_BULK_CB_WRAP_LEN ||
|
||||
if (req->actual != US_BULK_CB_WRAP_LEN ||
|
||||
cbw->Signature != cpu_to_le32(
|
||||
USB_BULK_CB_SIG)) {
|
||||
US_BULK_CB_SIGN)) {
|
||||
DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
|
||||
req->actual,
|
||||
le32_to_cpu(cbw->Signature));
|
||||
@ -2253,7 +2252,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
}
|
||||
|
||||
/* Is the CBW meaningful? */
|
||||
if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
|
||||
if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
|
||||
cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
|
||||
DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
|
||||
"cmdlen %u\n",
|
||||
@ -2273,7 +2272,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
/* Save the command for later */
|
||||
common->cmnd_size = cbw->Length;
|
||||
memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
|
||||
if (cbw->Flags & USB_BULK_IN_FLAG)
|
||||
if (cbw->Flags & US_BULK_FLAG_IN)
|
||||
common->data_dir = DATA_DIR_TO_HOST;
|
||||
else
|
||||
common->data_dir = DATA_DIR_FROM_HOST;
|
||||
@ -2303,7 +2302,7 @@ static int get_next_command(struct fsg_common *common)
|
||||
}
|
||||
|
||||
/* Queue a request to read a Bulk-only CBW */
|
||||
set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
|
||||
set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
|
||||
if (!start_out_transfer(common, bh))
|
||||
/* Don't know what to do if common->fsg is NULL */
|
||||
return -EIO;
|
||||
|
@ -780,7 +780,7 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
midi->out_ep->driver_data = cdev; /* claim */
|
||||
|
||||
/* allocate temporary function list */
|
||||
midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(midi_function),
|
||||
midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function),
|
||||
GFP_KERNEL);
|
||||
if (!midi_function) {
|
||||
status = -ENOMEM;
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
|
||||
* Author: Michal Nazarewicz (mina86@mina86.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
|
||||
|
@ -99,6 +99,34 @@ static struct usb_descriptor_header *gser_hs_function[] __initdata = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = {
|
||||
.bLength = sizeof gser_ss_bulk_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *gser_ss_function[] __initdata = {
|
||||
(struct usb_descriptor_header *) &gser_interface_desc,
|
||||
(struct usb_descriptor_header *) &gser_ss_in_desc,
|
||||
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
|
||||
(struct usb_descriptor_header *) &gser_ss_out_desc,
|
||||
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string descriptors: */
|
||||
|
||||
static struct usb_string gser_string_defs[] = {
|
||||
@ -201,9 +229,21 @@ gser_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
|
||||
}
|
||||
if (gadget_is_superspeed(c->cdev->gadget)) {
|
||||
gser_ss_in_desc.bEndpointAddress =
|
||||
gser_fs_in_desc.bEndpointAddress;
|
||||
gser_ss_out_desc.bEndpointAddress =
|
||||
gser_fs_out_desc.bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->ss_descriptors = usb_copy_descriptors(gser_ss_function);
|
||||
if (!f->ss_descriptors)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
gser->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
gser->port.in->name, gser->port.out->name);
|
||||
return 0;
|
||||
@ -225,6 +265,8 @@ gser_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
if (gadget_is_superspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
kfree(func_to_gser(f));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
#include "u_uac1.h"
|
||||
|
||||
#define OUT_EP_MAX_PACKET_SIZE 200
|
||||
static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
|
||||
@ -216,29 +216,6 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *audio_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
|
||||
*/
|
1449
drivers/usb/gadget/f_uac2.c
Normal file
1449
drivers/usb/gadget/f_uac2.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -855,7 +855,7 @@ static int class_setup_req(struct fsg_dev *fsg,
|
||||
if (transport_is_bbb()) {
|
||||
switch (ctrl->bRequest) {
|
||||
|
||||
case USB_BULK_RESET_REQUEST:
|
||||
case US_BULK_RESET_REQUEST:
|
||||
if (ctrl->bRequestType != (USB_DIR_OUT |
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
||||
break;
|
||||
@ -871,7 +871,7 @@ static int class_setup_req(struct fsg_dev *fsg,
|
||||
value = DELAYED_STATUS;
|
||||
break;
|
||||
|
||||
case USB_BULK_GET_MAX_LUN_REQUEST:
|
||||
case US_BULK_GET_MAX_LUN:
|
||||
if (ctrl->bRequestType != (USB_DIR_IN |
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE))
|
||||
break;
|
||||
@ -2125,7 +2125,7 @@ static int send_status(struct fsg_dev *fsg)
|
||||
struct fsg_lun *curlun = fsg->curlun;
|
||||
struct fsg_buffhd *bh;
|
||||
int rc;
|
||||
u8 status = USB_STATUS_PASS;
|
||||
u8 status = US_BULK_STAT_OK;
|
||||
u32 sd, sdinfo = 0;
|
||||
|
||||
/* Wait for the next buffer to become available */
|
||||
@ -2146,11 +2146,11 @@ static int send_status(struct fsg_dev *fsg)
|
||||
|
||||
if (fsg->phase_error) {
|
||||
DBG(fsg, "sending phase-error status\n");
|
||||
status = USB_STATUS_PHASE_ERROR;
|
||||
status = US_BULK_STAT_PHASE;
|
||||
sd = SS_INVALID_COMMAND;
|
||||
} else if (sd != SS_NO_SENSE) {
|
||||
DBG(fsg, "sending command-failure status\n");
|
||||
status = USB_STATUS_FAIL;
|
||||
status = US_BULK_STAT_FAIL;
|
||||
VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
|
||||
" info x%x\n",
|
||||
SK(sd), ASC(sd), ASCQ(sd), sdinfo);
|
||||
@ -2160,12 +2160,12 @@ static int send_status(struct fsg_dev *fsg)
|
||||
struct bulk_cs_wrap *csw = bh->buf;
|
||||
|
||||
/* Store and send the Bulk-only CSW */
|
||||
csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
|
||||
csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
|
||||
csw->Tag = fsg->tag;
|
||||
csw->Residue = cpu_to_le32(fsg->residue);
|
||||
csw->Status = status;
|
||||
|
||||
bh->inreq->length = USB_BULK_CS_WRAP_LEN;
|
||||
bh->inreq->length = US_BULK_CS_WRAP_LEN;
|
||||
bh->inreq->zero = 0;
|
||||
start_transfer(fsg, fsg->bulk_in, bh->inreq,
|
||||
&bh->inreq_busy, &bh->state);
|
||||
@ -2609,16 +2609,16 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
||||
static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct usb_request *req = bh->outreq;
|
||||
struct fsg_bulk_cb_wrap *cbw = req->buf;
|
||||
struct bulk_cb_wrap *cbw = req->buf;
|
||||
|
||||
/* Was this a real packet? Should it be ignored? */
|
||||
if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
|
||||
return -EINVAL;
|
||||
|
||||
/* Is the CBW valid? */
|
||||
if (req->actual != USB_BULK_CB_WRAP_LEN ||
|
||||
if (req->actual != US_BULK_CB_WRAP_LEN ||
|
||||
cbw->Signature != cpu_to_le32(
|
||||
USB_BULK_CB_SIG)) {
|
||||
US_BULK_CB_SIGN)) {
|
||||
DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
|
||||
req->actual,
|
||||
le32_to_cpu(cbw->Signature));
|
||||
@ -2638,7 +2638,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
}
|
||||
|
||||
/* Is the CBW meaningful? */
|
||||
if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
|
||||
if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
|
||||
cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
|
||||
DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
|
||||
"cmdlen %u\n",
|
||||
@ -2656,7 +2656,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
/* Save the command for later */
|
||||
fsg->cmnd_size = cbw->Length;
|
||||
memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size);
|
||||
if (cbw->Flags & USB_BULK_IN_FLAG)
|
||||
if (cbw->Flags & US_BULK_FLAG_IN)
|
||||
fsg->data_dir = DATA_DIR_TO_HOST;
|
||||
else
|
||||
fsg->data_dir = DATA_DIR_FROM_HOST;
|
||||
@ -2685,7 +2685,7 @@ static int get_next_command(struct fsg_dev *fsg)
|
||||
}
|
||||
|
||||
/* Queue a request to read a Bulk-only CBW */
|
||||
set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
|
||||
set_bulk_out_req_length(fsg, bh, US_BULK_CB_WRAP_LEN);
|
||||
start_transfer(fsg, fsg->bulk_out, bh->outreq,
|
||||
&bh->outreq_busy, &bh->state);
|
||||
|
||||
|
@ -1638,6 +1638,7 @@ static int qe_ep_disable(struct usb_ep *_ep)
|
||||
/* Nuke all pending requests (does flush) */
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
ep->tx_req = NULL;
|
||||
qe_ep_reset(udc, ep->epnum);
|
||||
|
@ -659,6 +659,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
@ -768,7 +769,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
|
||||
* @is_last: return flag if it is the last dTD of the request
|
||||
* return: pointer to the built dTD */
|
||||
static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
|
||||
dma_addr_t *dma, int *is_last)
|
||||
dma_addr_t *dma, int *is_last, gfp_t gfp_flags)
|
||||
{
|
||||
u32 swap_temp;
|
||||
struct ep_td_struct *dtd;
|
||||
@ -777,7 +778,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
|
||||
*length = min(req->req.length - req->req.actual,
|
||||
(unsigned)EP_MAX_LENGTH_TRANSFER);
|
||||
|
||||
dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
|
||||
dtd = dma_pool_alloc(udc_controller->td_pool, gfp_flags, dma);
|
||||
if (dtd == NULL)
|
||||
return dtd;
|
||||
|
||||
@ -827,7 +828,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
|
||||
}
|
||||
|
||||
/* Generate dtd chain for a request */
|
||||
static int fsl_req_to_dtd(struct fsl_req *req)
|
||||
static int fsl_req_to_dtd(struct fsl_req *req, gfp_t gfp_flags)
|
||||
{
|
||||
unsigned count;
|
||||
int is_last;
|
||||
@ -836,7 +837,7 @@ static int fsl_req_to_dtd(struct fsl_req *req)
|
||||
dma_addr_t dma;
|
||||
|
||||
do {
|
||||
dtd = fsl_build_dtd(req, &count, &dma, &is_last);
|
||||
dtd = fsl_build_dtd(req, &count, &dma, &is_last, gfp_flags);
|
||||
if (dtd == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -910,13 +911,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
req->req.actual = 0;
|
||||
req->dtd_count = 0;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
/* build dtds and push them to device queue */
|
||||
if (!fsl_req_to_dtd(req)) {
|
||||
if (!fsl_req_to_dtd(req, gfp_flags)) {
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
fsl_queue_td(ep, req);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1217,7 +1216,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
|
||||
udc = container_of(gadget, struct fsl_udc, gadget);
|
||||
if (udc->transceiver)
|
||||
return otg_set_power(udc->transceiver, mA);
|
||||
return usb_phy_set_power(udc->transceiver, mA);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
@ -1295,7 +1294,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)
|
||||
ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = 1;
|
||||
|
||||
if (fsl_req_to_dtd(req) == 0)
|
||||
if (fsl_req_to_dtd(req, GFP_ATOMIC) == 0)
|
||||
fsl_queue_td(ep, req);
|
||||
else
|
||||
return -ENOMEM;
|
||||
@ -1379,7 +1378,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
|
||||
req->mapped = 1;
|
||||
|
||||
/* prime the data phase */
|
||||
if ((fsl_req_to_dtd(req) == 0))
|
||||
if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0))
|
||||
fsl_queue_td(ep, req);
|
||||
else /* no mem */
|
||||
goto stall;
|
||||
@ -1966,7 +1965,8 @@ static int fsl_start(struct usb_gadget_driver *driver,
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
if (udc_controller->transceiver) {
|
||||
retval = otg_set_peripheral(udc_controller->transceiver,
|
||||
retval = otg_set_peripheral(
|
||||
udc_controller->transceiver->otg,
|
||||
&udc_controller->gadget);
|
||||
if (retval < 0) {
|
||||
ERR("can't bind to transceiver\n");
|
||||
@ -2006,7 +2006,7 @@ static int fsl_stop(struct usb_gadget_driver *driver)
|
||||
return -EINVAL;
|
||||
|
||||
if (udc_controller->transceiver)
|
||||
otg_set_peripheral(udc_controller->transceiver, NULL);
|
||||
otg_set_peripheral(udc_controller->transceiver->otg, NULL);
|
||||
|
||||
/* stop DR, disable intr */
|
||||
dr_controller_stop(udc_controller);
|
||||
@ -2430,7 +2430,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||
udc_controller->transceiver = otg_get_transceiver();
|
||||
udc_controller->transceiver = usb_get_transceiver();
|
||||
if (!udc_controller->transceiver) {
|
||||
ERR("Can't find OTG driver!\n");
|
||||
ret = -ENODEV;
|
||||
|
@ -471,7 +471,7 @@ struct fsl_udc {
|
||||
|
||||
struct usb_ctrlrequest local_setup_buff;
|
||||
spinlock_t lock;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
unsigned softconnect:1;
|
||||
unsigned vbus_active:1;
|
||||
unsigned stopped:1;
|
||||
|
@ -2,7 +2,7 @@
|
||||
* g_ffs.c -- user mode file system API for USB composite function controllers
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
|
||||
* Author: Michal Nazarewicz <mina86@mina86.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
|
||||
|
@ -235,6 +235,7 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep)
|
||||
|
||||
ep->ep.maxpacket = MAX_FIFO_SIZE;
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
ep->irqs = 0;
|
||||
ep->dma = 0;
|
||||
@ -310,12 +311,9 @@ done(struct goku_ep *ep, struct goku_request *req, int status)
|
||||
status = req->req.status;
|
||||
|
||||
dev = ep->dev;
|
||||
if (req->mapped) {
|
||||
pci_unmap_single(dev->pdev, req->req.dma, req->req.length,
|
||||
ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
||||
req->req.dma = DMA_ADDR_INVALID;
|
||||
req->mapped = 0;
|
||||
}
|
||||
|
||||
if (ep->dma)
|
||||
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
|
||||
|
||||
#ifndef USB_TRACE
|
||||
if (status && status != -ESHUTDOWN)
|
||||
@ -736,10 +734,11 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
return -EBUSY;
|
||||
|
||||
/* set up dma mapping in case the caller didn't */
|
||||
if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
|
||||
_req->dma = pci_map_single(dev->pdev, _req->buf, _req->length,
|
||||
ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
||||
req->mapped = 1;
|
||||
if (ep->dma) {
|
||||
status = usb_gadget_map_request(&dev->gadget, &req->req,
|
||||
ep->is_in);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef USB_TRACE
|
||||
|
@ -60,9 +60,9 @@ static struct usb_device_descriptor device_desc = {
|
||||
/* .bDeviceClass = USB_CLASS_COMM, */
|
||||
/* .bDeviceSubClass = 0, */
|
||||
/* .bDeviceProtocol = 0, */
|
||||
.bDeviceClass = 0xEF,
|
||||
.bDeviceSubClass = 2,
|
||||
.bDeviceProtocol = 1,
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id can be overridden by module parameters. */
|
||||
|
@ -1043,6 +1043,8 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
||||
// FIXME don't call this with the spinlock held ...
|
||||
if (copy_to_user (buf, dev->req->buf, len))
|
||||
retval = -EFAULT;
|
||||
else
|
||||
retval = len;
|
||||
clean_req (dev->gadget->ep0, dev->req);
|
||||
/* NOTE userspace can't yet choose to stall */
|
||||
}
|
||||
|
@ -401,16 +401,7 @@ static void done(struct langwell_ep *ep, struct langwell_request *req,
|
||||
dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma);
|
||||
}
|
||||
|
||||
if (req->mapped) {
|
||||
dma_unmap_single(&dev->pdev->dev,
|
||||
req->req.dma, req->req.length,
|
||||
is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
||||
req->req.dma = DMA_ADDR_INVALID;
|
||||
req->mapped = 0;
|
||||
} else
|
||||
dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma,
|
||||
req->req.length,
|
||||
is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
usb_gadget_unmap_request(&dev->gadget, &req->req, is_in(ep));
|
||||
|
||||
if (status != -ESHUTDOWN)
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
@ -487,6 +478,7 @@ static int langwell_ep_disable(struct usb_ep *_ep)
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
@ -749,7 +741,8 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
struct langwell_ep *ep;
|
||||
struct langwell_udc *dev;
|
||||
unsigned long flags;
|
||||
int is_iso = 0, zlflag = 0;
|
||||
int is_iso = 0;
|
||||
int ret;
|
||||
|
||||
/* always require a cpu-view buffer */
|
||||
req = container_of(_req, struct langwell_request, req);
|
||||
@ -776,33 +769,10 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
/* set up dma mapping in case the caller didn't */
|
||||
if (_req->dma == DMA_ADDR_INVALID) {
|
||||
/* WORKAROUND: WARN_ON(size == 0) */
|
||||
if (_req->length == 0) {
|
||||
dev_vdbg(&dev->pdev->dev, "req->length: 0->1\n");
|
||||
zlflag = 1;
|
||||
_req->length++;
|
||||
}
|
||||
|
||||
_req->dma = dma_map_single(&dev->pdev->dev,
|
||||
_req->buf, _req->length,
|
||||
is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
if (zlflag && (_req->length == 1)) {
|
||||
dev_vdbg(&dev->pdev->dev, "req->length: 1->0\n");
|
||||
zlflag = 0;
|
||||
_req->length = 0;
|
||||
}
|
||||
|
||||
req->mapped = 1;
|
||||
dev_vdbg(&dev->pdev->dev, "req->mapped = 1\n");
|
||||
} else {
|
||||
dma_sync_single_for_device(&dev->pdev->dev,
|
||||
_req->dma, _req->length,
|
||||
is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = 0;
|
||||
dev_vdbg(&dev->pdev->dev, "req->mapped = 0\n");
|
||||
}
|
||||
/* set up dma mapping */
|
||||
ret = usb_gadget_map_request(&dev->gadget, &req->req, is_in(ep));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"%s queue req %p, len %u, buf %p, dma 0x%08x\n",
|
||||
@ -1261,9 +1231,9 @@ static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
|
||||
|
||||
if (dev->transceiver) {
|
||||
dev_vdbg(&dev->pdev->dev, "otg_set_power\n");
|
||||
dev_vdbg(&dev->pdev->dev, "usb_phy_set_power\n");
|
||||
dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
|
||||
return otg_set_power(dev->transceiver, mA);
|
||||
return usb_phy_set_power(dev->transceiver, mA);
|
||||
}
|
||||
|
||||
dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
|
||||
@ -1906,7 +1876,7 @@ static int langwell_stop(struct usb_gadget *g,
|
||||
|
||||
/* unbind OTG transceiver */
|
||||
if (dev->transceiver)
|
||||
(void)otg_set_peripheral(dev->transceiver, 0);
|
||||
(void)otg_set_peripheral(dev->transceiver->otg, 0);
|
||||
|
||||
/* disable interrupt and set controller to stop state */
|
||||
langwell_udc_stop(dev);
|
||||
|
@ -162,7 +162,7 @@ struct langwell_udc {
|
||||
spinlock_t lock; /* device lock */
|
||||
struct langwell_ep *ep;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
u8 dev_addr;
|
||||
u32 usb_state;
|
||||
u32 resume_state;
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2003-2008 Alan Stern
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
|
||||
* Author: Michal Nazarewicz <mina86@mina86.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2008 David Brownell
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
|
||||
* Author: Michal Nazarewicz (mina86@mina86.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
|
||||
|
@ -217,7 +217,7 @@ struct mv_udc {
|
||||
struct work_struct vbus_work;
|
||||
struct workqueue_struct *qwork;
|
||||
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
|
||||
struct mv_usb_platform_data *pdata;
|
||||
|
||||
|
@ -608,6 +608,7 @@ static int mv_ep_disable(struct usb_ep *_ep)
|
||||
nuke(ep, -ESHUTDOWN);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
@ -771,8 +772,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
udc->ep0_state = DATA_STATE_XMIT;
|
||||
|
||||
/* irq handler advances the queue */
|
||||
if (req != NULL)
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return 0;
|
||||
@ -1384,7 +1384,8 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
|
||||
}
|
||||
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||
&udc->gadget);
|
||||
if (retval) {
|
||||
dev_err(&udc->dev->dev,
|
||||
"unable to register peripheral to otg\n");
|
||||
@ -2181,7 +2182,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
if (pdata->mode == MV_USB_MODE_OTG)
|
||||
udc->transceiver = otg_get_transceiver();
|
||||
udc->transceiver = usb_get_transceiver();
|
||||
#endif
|
||||
|
||||
udc->clknum = pdata->clknum;
|
||||
|
@ -385,12 +385,9 @@ net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status)
|
||||
status = req->req.status;
|
||||
|
||||
dev = ep->dev;
|
||||
if (use_dma && req->mapped) {
|
||||
dma_unmap_single(dev->dev, req->req.dma, req->req.length,
|
||||
ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->req.dma = DMA_ADDR_INVALID;
|
||||
req->mapped = 0;
|
||||
}
|
||||
if (use_dma && ep->dma)
|
||||
usb_gadget_unmap_request(&dev->gadget, &req->req,
|
||||
ep->is_in);
|
||||
|
||||
if (status && status != -ESHUTDOWN)
|
||||
dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n",
|
||||
@ -850,10 +847,11 @@ net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
/* set up dma mapping in case the caller didn't */
|
||||
if (use_dma && ep->dma && _req->dma == DMA_ADDR_INVALID) {
|
||||
_req->dma = dma_map_single(dev->dev, _req->buf, _req->length,
|
||||
ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = 1;
|
||||
if (use_dma && ep->dma) {
|
||||
status = usb_gadget_map_request(&dev->gadget, _req,
|
||||
ep->is_in);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n",
|
||||
|
@ -806,12 +806,8 @@ done (struct net2280_ep *ep, struct net2280_request *req, int status)
|
||||
status = req->req.status;
|
||||
|
||||
dev = ep->dev;
|
||||
if (req->mapped) {
|
||||
pci_unmap_single (dev->pdev, req->req.dma, req->req.length,
|
||||
ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
||||
req->req.dma = DMA_ADDR_INVALID;
|
||||
req->mapped = 0;
|
||||
}
|
||||
if (ep->dma)
|
||||
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
|
||||
|
||||
if (status && status != -ESHUTDOWN)
|
||||
VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n",
|
||||
@ -857,10 +853,13 @@ net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* set up dma mapping in case the caller didn't */
|
||||
if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
|
||||
_req->dma = pci_map_single (dev->pdev, _req->buf, _req->length,
|
||||
ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
|
||||
req->mapped = 1;
|
||||
if (ep->dma) {
|
||||
int ret;
|
||||
|
||||
ret = usb_gadget_map_request(&dev->gadget, _req,
|
||||
ep->is_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -251,6 +251,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
|
||||
|
||||
spin_lock_irqsave(&ep->udc->lock, flags);
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
nuke (ep, -ESHUTDOWN);
|
||||
ep->ep.maxpacket = ep->maxpacket;
|
||||
ep->has_dma = 0;
|
||||
@ -1213,7 +1214,7 @@ static int omap_wakeup(struct usb_gadget *gadget)
|
||||
/* NOTE: non-OTG systems may use SRP TOO... */
|
||||
} else if (!(udc->devstat & UDC_ATT)) {
|
||||
if (udc->transceiver)
|
||||
retval = otg_start_srp(udc->transceiver);
|
||||
retval = otg_start_srp(udc->transceiver->otg);
|
||||
}
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
@ -1345,7 +1346,7 @@ static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
|
||||
udc = container_of(gadget, struct omap_udc, gadget);
|
||||
if (udc->transceiver)
|
||||
return otg_set_power(udc->transceiver, mA);
|
||||
return usb_phy_set_power(udc->transceiver, mA);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -1839,11 +1840,13 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
if (udc->transceiver)
|
||||
otg_set_suspend(udc->transceiver, 1);
|
||||
usb_phy_set_suspend(
|
||||
udc->transceiver, 1);
|
||||
} else {
|
||||
VDBG("resume\n");
|
||||
if (udc->transceiver)
|
||||
otg_set_suspend(udc->transceiver, 0);
|
||||
usb_phy_set_suspend(
|
||||
udc->transceiver, 0);
|
||||
if (udc->gadget.speed == USB_SPEED_FULL
|
||||
&& udc->driver->resume) {
|
||||
spin_unlock(&udc->lock);
|
||||
@ -2154,7 +2157,8 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
if (udc->transceiver) {
|
||||
status = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
status = otg_set_peripheral(udc->transceiver->otg,
|
||||
&udc->gadget);
|
||||
if (status < 0) {
|
||||
ERR("can't bind to transceiver\n");
|
||||
if (driver->unbind) {
|
||||
@ -2200,7 +2204,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
|
||||
omap_vbus_session(&udc->gadget, 0);
|
||||
|
||||
if (udc->transceiver)
|
||||
(void) otg_set_peripheral(udc->transceiver, NULL);
|
||||
(void) otg_set_peripheral(udc->transceiver->otg, NULL);
|
||||
else
|
||||
pullup_disable(udc);
|
||||
|
||||
@ -2650,7 +2654,7 @@ static void omap_udc_release(struct device *dev)
|
||||
}
|
||||
|
||||
static int __init
|
||||
omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
|
||||
omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
|
||||
{
|
||||
unsigned tmp, buf;
|
||||
|
||||
@ -2790,7 +2794,7 @@ static int __init omap_udc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int status = -ENODEV;
|
||||
int hmc;
|
||||
struct otg_transceiver *xceiv = NULL;
|
||||
struct usb_phy *xceiv = NULL;
|
||||
const char *type = NULL;
|
||||
struct omap_usb_config *config = pdev->dev.platform_data;
|
||||
struct clk *dc_clk;
|
||||
@ -2863,7 +2867,7 @@ static int __init omap_udc_probe(struct platform_device *pdev)
|
||||
* use it. Except for OTG, we don't _need_ to talk to one;
|
||||
* but not having one probably means no VBUS detection.
|
||||
*/
|
||||
xceiv = otg_get_transceiver();
|
||||
xceiv = usb_get_transceiver();
|
||||
if (xceiv)
|
||||
type = xceiv->label;
|
||||
else if (config->otg) {
|
||||
@ -3009,7 +3013,7 @@ cleanup1:
|
||||
|
||||
cleanup0:
|
||||
if (xceiv)
|
||||
otg_put_transceiver(xceiv);
|
||||
usb_put_transceiver(xceiv);
|
||||
|
||||
if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) {
|
||||
clk_disable(hhc_clk);
|
||||
@ -3039,7 +3043,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
|
||||
|
||||
pullup_disable(udc);
|
||||
if (udc->transceiver) {
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
usb_put_transceiver(udc->transceiver);
|
||||
udc->transceiver = NULL;
|
||||
}
|
||||
omap_writew(0, UDC_SYSCON1);
|
||||
|
@ -164,7 +164,7 @@ struct omap_udc {
|
||||
struct omap_ep ep[32];
|
||||
u16 devstat;
|
||||
u16 clr_halt;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
struct list_head iso;
|
||||
unsigned softconnect:1;
|
||||
unsigned vbus_active:1;
|
||||
|
@ -15,6 +15,14 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
/* GPIO port for VBUS detecting */
|
||||
static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */
|
||||
|
||||
#define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */
|
||||
#define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */
|
||||
|
||||
/* Address offset of Registers */
|
||||
#define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */
|
||||
@ -295,6 +303,21 @@ struct pch_udc_ep {
|
||||
unsigned long epsts;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pch_vbus_gpio_data - Structure holding GPIO informaton
|
||||
* for detecting VBUS
|
||||
* @port: gpio port number
|
||||
* @intr: gpio interrupt number
|
||||
* @irq_work_fall Structure for WorkQueue
|
||||
* @irq_work_rise Structure for WorkQueue
|
||||
*/
|
||||
struct pch_vbus_gpio_data {
|
||||
int port;
|
||||
int intr;
|
||||
struct work_struct irq_work_fall;
|
||||
struct work_struct irq_work_rise;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pch_udc_dev - Structure holding complete information
|
||||
* of the PCH USB device
|
||||
@ -311,6 +334,7 @@ struct pch_udc_ep {
|
||||
* @registered: driver regsitered with system
|
||||
* @suspended: driver in suspended state
|
||||
* @connected: gadget driver associated
|
||||
* @vbus_session: required vbus_session state
|
||||
* @set_cfg_not_acked: pending acknowledgement 4 setup
|
||||
* @waiting_zlp_ack: pending acknowledgement 4 ZLP
|
||||
* @data_requests: DMA pool for data requests
|
||||
@ -322,6 +346,7 @@ struct pch_udc_ep {
|
||||
* @base_addr: for mapped device memory
|
||||
* @irq: IRQ line for the device
|
||||
* @cfg_data: current cfg, intf, and alt in use
|
||||
* @vbus_gpio: GPIO informaton for detecting VBUS
|
||||
*/
|
||||
struct pch_udc_dev {
|
||||
struct usb_gadget gadget;
|
||||
@ -337,6 +362,7 @@ struct pch_udc_dev {
|
||||
registered:1,
|
||||
suspended:1,
|
||||
connected:1,
|
||||
vbus_session:1,
|
||||
set_cfg_not_acked:1,
|
||||
waiting_zlp_ack:1;
|
||||
struct pci_pool *data_requests;
|
||||
@ -347,7 +373,8 @@ struct pch_udc_dev {
|
||||
unsigned long phys_addr;
|
||||
void __iomem *base_addr;
|
||||
unsigned irq;
|
||||
struct pch_udc_cfg_data cfg_data;
|
||||
struct pch_udc_cfg_data cfg_data;
|
||||
struct pch_vbus_gpio_data vbus_gpio;
|
||||
};
|
||||
|
||||
#define PCH_UDC_PCI_BAR 1
|
||||
@ -553,6 +580,29 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
|
||||
pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_udc_reconnect() - This API initializes usb device controller,
|
||||
* and clear the disconnect status.
|
||||
* @dev: Reference to pch_udc_regs structure
|
||||
*/
|
||||
static void pch_udc_init(struct pch_udc_dev *dev);
|
||||
static void pch_udc_reconnect(struct pch_udc_dev *dev)
|
||||
{
|
||||
pch_udc_init(dev);
|
||||
|
||||
/* enable device interrupts */
|
||||
/* pch_udc_enable_interrupts() */
|
||||
pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR,
|
||||
UDC_DEVINT_UR | UDC_DEVINT_ENUM);
|
||||
|
||||
/* Clear the disconnect */
|
||||
pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
|
||||
pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
|
||||
mdelay(1);
|
||||
/* Resume USB signalling */
|
||||
pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_udc_vbus_session() - set or clearr the disconnect status.
|
||||
* @dev: Reference to pch_udc_regs structure
|
||||
@ -563,10 +613,18 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
|
||||
static inline void pch_udc_vbus_session(struct pch_udc_dev *dev,
|
||||
int is_active)
|
||||
{
|
||||
if (is_active)
|
||||
pch_udc_clear_disconnect(dev);
|
||||
else
|
||||
if (is_active) {
|
||||
pch_udc_reconnect(dev);
|
||||
dev->vbus_session = 1;
|
||||
} else {
|
||||
if (dev->driver && dev->driver->disconnect) {
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->disconnect(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
pch_udc_set_disconnect(dev);
|
||||
dev->vbus_session = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1126,7 +1184,17 @@ static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
|
||||
if (!gadget)
|
||||
return -EINVAL;
|
||||
dev = container_of(gadget, struct pch_udc_dev, gadget);
|
||||
pch_udc_vbus_session(dev, is_on);
|
||||
if (is_on) {
|
||||
pch_udc_reconnect(dev);
|
||||
} else {
|
||||
if (dev->driver && dev->driver->disconnect) {
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->disconnect(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
pch_udc_set_disconnect(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1182,6 +1250,188 @@ static const struct usb_gadget_ops pch_udc_ops = {
|
||||
.stop = pch_udc_stop,
|
||||
};
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status.
|
||||
* @dev: Reference to the driver structure
|
||||
*
|
||||
* Return value:
|
||||
* 1: VBUS is high
|
||||
* 0: VBUS is low
|
||||
* -1: It is not enable to detect VBUS using GPIO
|
||||
*/
|
||||
static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev)
|
||||
{
|
||||
int vbus = 0;
|
||||
|
||||
if (dev->vbus_gpio.port)
|
||||
vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0;
|
||||
else
|
||||
vbus = -1;
|
||||
|
||||
return vbus;
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low.
|
||||
* If VBUS is Low, disconnect is processed
|
||||
* @irq_work: Structure for WorkQueue
|
||||
*
|
||||
*/
|
||||
static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
|
||||
{
|
||||
struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
|
||||
struct pch_vbus_gpio_data, irq_work_fall);
|
||||
struct pch_udc_dev *dev =
|
||||
container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
|
||||
int vbus_saved = -1;
|
||||
int vbus;
|
||||
int count;
|
||||
|
||||
if (!dev->vbus_gpio.port)
|
||||
return;
|
||||
|
||||
for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL);
|
||||
count++) {
|
||||
vbus = pch_vbus_gpio_get_value(dev);
|
||||
|
||||
if ((vbus_saved == vbus) && (vbus == 0)) {
|
||||
dev_dbg(&dev->pdev->dev, "VBUS fell");
|
||||
if (dev->driver
|
||||
&& dev->driver->disconnect) {
|
||||
dev->driver->disconnect(
|
||||
&dev->gadget);
|
||||
}
|
||||
if (dev->vbus_gpio.intr)
|
||||
pch_udc_init(dev);
|
||||
else
|
||||
pch_udc_reconnect(dev);
|
||||
return;
|
||||
}
|
||||
vbus_saved = vbus;
|
||||
mdelay(PCH_VBUS_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_work_rise() - This API checks VBUS is High.
|
||||
* If VBUS is High, connect is processed
|
||||
* @irq_work: Structure for WorkQueue
|
||||
*
|
||||
*/
|
||||
static void pch_vbus_gpio_work_rise(struct work_struct *irq_work)
|
||||
{
|
||||
struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
|
||||
struct pch_vbus_gpio_data, irq_work_rise);
|
||||
struct pch_udc_dev *dev =
|
||||
container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
|
||||
int vbus;
|
||||
|
||||
if (!dev->vbus_gpio.port)
|
||||
return;
|
||||
|
||||
mdelay(PCH_VBUS_INTERVAL);
|
||||
vbus = pch_vbus_gpio_get_value(dev);
|
||||
|
||||
if (vbus == 1) {
|
||||
dev_dbg(&dev->pdev->dev, "VBUS rose");
|
||||
pch_udc_reconnect(dev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS
|
||||
* @irq: Interrupt request number
|
||||
* @dev: Reference to the device structure
|
||||
*
|
||||
* Return codes:
|
||||
* 0: Success
|
||||
* -EINVAL: GPIO port is invalid or can't be initialized.
|
||||
*/
|
||||
static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
|
||||
{
|
||||
struct pch_udc_dev *dev = (struct pch_udc_dev *)data;
|
||||
|
||||
if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (pch_vbus_gpio_get_value(dev))
|
||||
schedule_work(&dev->vbus_gpio.irq_work_rise);
|
||||
else
|
||||
schedule_work(&dev->vbus_gpio.irq_work_fall);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
|
||||
* @dev: Reference to the driver structure
|
||||
* @vbus_gpio Number of GPIO port to detect gpio
|
||||
*
|
||||
* Return codes:
|
||||
* 0: Success
|
||||
* -EINVAL: GPIO port is invalid or can't be initialized.
|
||||
*/
|
||||
static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
|
||||
{
|
||||
int err;
|
||||
int irq_num = 0;
|
||||
|
||||
dev->vbus_gpio.port = 0;
|
||||
dev->vbus_gpio.intr = 0;
|
||||
|
||||
if (vbus_gpio_port <= -1)
|
||||
return -EINVAL;
|
||||
|
||||
err = gpio_is_valid(vbus_gpio_port);
|
||||
if (!err) {
|
||||
pr_err("%s: gpio port %d is invalid\n",
|
||||
__func__, vbus_gpio_port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = gpio_request(vbus_gpio_port, "pch_vbus");
|
||||
if (err) {
|
||||
pr_err("%s: can't request gpio port %d, err: %d\n",
|
||||
__func__, vbus_gpio_port, err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->vbus_gpio.port = vbus_gpio_port;
|
||||
gpio_direction_input(vbus_gpio_port);
|
||||
INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
|
||||
|
||||
irq_num = gpio_to_irq(vbus_gpio_port);
|
||||
if (irq_num > 0) {
|
||||
irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
|
||||
err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
|
||||
"vbus_detect", dev);
|
||||
if (!err) {
|
||||
dev->vbus_gpio.intr = irq_num;
|
||||
INIT_WORK(&dev->vbus_gpio.irq_work_rise,
|
||||
pch_vbus_gpio_work_rise);
|
||||
} else {
|
||||
pr_err("%s: can't request irq %d, err: %d\n",
|
||||
__func__, irq_num, err);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_free() - This API frees resources of GPIO port
|
||||
* @dev: Reference to the driver structure
|
||||
*/
|
||||
static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
|
||||
{
|
||||
if (dev->vbus_gpio.intr)
|
||||
free_irq(dev->vbus_gpio.intr, dev);
|
||||
|
||||
if (dev->vbus_gpio.port)
|
||||
gpio_free(dev->vbus_gpio.port);
|
||||
}
|
||||
|
||||
/**
|
||||
* complete_req() - This API is invoked from the driver when processing
|
||||
* of a request is complete
|
||||
@ -1493,6 +1743,7 @@ static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
|
||||
pch_udc_ep_disable(ep);
|
||||
pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
spin_unlock_irqrestore(&ep->dev->lock, iflags);
|
||||
return 0;
|
||||
@ -2335,8 +2586,11 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
|
||||
/* Complete request queue */
|
||||
empty_req_queue(ep);
|
||||
}
|
||||
if (dev->driver && dev->driver->disconnect)
|
||||
if (dev->driver && dev->driver->disconnect) {
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->disconnect(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2371,6 +2625,11 @@ static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev)
|
||||
pch_udc_set_dma(dev, DMA_DIR_TX);
|
||||
pch_udc_set_dma(dev, DMA_DIR_RX);
|
||||
pch_udc_ep_set_rrdy(&(dev->ep[UDC_EP0OUT_IDX]));
|
||||
|
||||
/* enable device interrupts */
|
||||
pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US |
|
||||
UDC_DEVINT_ES | UDC_DEVINT_ENUM |
|
||||
UDC_DEVINT_SI | UDC_DEVINT_SC);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2459,12 +2718,18 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
|
||||
*/
|
||||
static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
|
||||
{
|
||||
int vbus;
|
||||
|
||||
/* USB Reset Interrupt */
|
||||
if (dev_intr & UDC_DEVINT_UR)
|
||||
if (dev_intr & UDC_DEVINT_UR) {
|
||||
pch_udc_svc_ur_interrupt(dev);
|
||||
dev_dbg(&dev->pdev->dev, "USB_RESET\n");
|
||||
}
|
||||
/* Enumeration Done Interrupt */
|
||||
if (dev_intr & UDC_DEVINT_ENUM)
|
||||
if (dev_intr & UDC_DEVINT_ENUM) {
|
||||
pch_udc_svc_enum_interrupt(dev);
|
||||
dev_dbg(&dev->pdev->dev, "USB_ENUM\n");
|
||||
}
|
||||
/* Set Interface Interrupt */
|
||||
if (dev_intr & UDC_DEVINT_SI)
|
||||
pch_udc_svc_intf_interrupt(dev);
|
||||
@ -2472,8 +2737,30 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
|
||||
if (dev_intr & UDC_DEVINT_SC)
|
||||
pch_udc_svc_cfg_interrupt(dev);
|
||||
/* USB Suspend interrupt */
|
||||
if (dev_intr & UDC_DEVINT_US)
|
||||
if (dev_intr & UDC_DEVINT_US) {
|
||||
if (dev->driver
|
||||
&& dev->driver->suspend) {
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->suspend(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
|
||||
vbus = pch_vbus_gpio_get_value(dev);
|
||||
if ((dev->vbus_session == 0)
|
||||
&& (vbus != 1)) {
|
||||
if (dev->driver && dev->driver->disconnect) {
|
||||
spin_unlock(&dev->lock);
|
||||
dev->driver->disconnect(&dev->gadget);
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
pch_udc_reconnect(dev);
|
||||
} else if ((dev->vbus_session == 0)
|
||||
&& (vbus == 1)
|
||||
&& !dev->vbus_gpio.intr)
|
||||
schedule_work(&dev->vbus_gpio.irq_work_fall);
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
|
||||
}
|
||||
/* Clear the SOF interrupt, if enabled */
|
||||
if (dev_intr & UDC_DEVINT_SOF)
|
||||
dev_dbg(&dev->pdev->dev, "SOF\n");
|
||||
@ -2499,6 +2786,14 @@ static irqreturn_t pch_udc_isr(int irq, void *pdev)
|
||||
dev_intr = pch_udc_read_device_interrupts(dev);
|
||||
ep_intr = pch_udc_read_ep_interrupts(dev);
|
||||
|
||||
/* For a hot plug, this find that the controller is hung up. */
|
||||
if (dev_intr == ep_intr)
|
||||
if (dev_intr == pch_udc_readl(dev, UDC_DEVCFG_ADDR)) {
|
||||
dev_dbg(&dev->pdev->dev, "UDC: Hung up\n");
|
||||
/* The controller is reset */
|
||||
pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (dev_intr)
|
||||
/* Clear device interrupts */
|
||||
pch_udc_write_device_interrupts(dev, dev_intr);
|
||||
@ -2625,6 +2920,7 @@ static int pch_udc_pcd_init(struct pch_udc_dev *dev)
|
||||
{
|
||||
pch_udc_init(dev);
|
||||
pch_udc_pcd_reinit(dev);
|
||||
pch_vbus_gpio_init(dev, vbus_gpio_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2725,7 +3021,8 @@ static int pch_udc_start(struct usb_gadget_driver *driver,
|
||||
pch_udc_setup_ep0(dev);
|
||||
|
||||
/* clear SD */
|
||||
pch_udc_clear_disconnect(dev);
|
||||
if ((pch_vbus_gpio_get_value(dev) != 0) || !dev->vbus_gpio.intr)
|
||||
pch_udc_clear_disconnect(dev);
|
||||
|
||||
dev->connected = 1;
|
||||
return 0;
|
||||
@ -2803,6 +3100,8 @@ static void pch_udc_remove(struct pci_dev *pdev)
|
||||
UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE);
|
||||
kfree(dev->ep0out_buf);
|
||||
|
||||
pch_vbus_gpio_free(dev);
|
||||
|
||||
pch_udc_exit(dev);
|
||||
|
||||
if (dev->irq_registered)
|
||||
@ -2912,8 +3211,10 @@ static int pch_udc_probe(struct pci_dev *pdev,
|
||||
}
|
||||
pch_udc = dev;
|
||||
/* initialize the hardware */
|
||||
if (pch_udc_pcd_init(dev))
|
||||
if (pch_udc_pcd_init(dev)) {
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
}
|
||||
if (request_irq(pdev->irq, pch_udc_isr, IRQF_SHARED, KBUILD_MODNAME,
|
||||
dev)) {
|
||||
dev_err(&pdev->dev, "%s: request_irq(%d) fail\n", __func__,
|
||||
|
@ -283,6 +283,7 @@ static int pxa25x_ep_disable (struct usb_ep *_ep)
|
||||
pxa25x_ep_fifo_flush (_ep);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 1;
|
||||
|
||||
local_irq_restore(flags);
|
||||
@ -995,7 +996,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
udc = container_of(_gadget, struct pxa25x_udc, gadget);
|
||||
|
||||
if (udc->transceiver)
|
||||
return otg_set_power(udc->transceiver, mA);
|
||||
return usb_phy_set_power(udc->transceiver, mA);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -1192,6 +1193,7 @@ static void udc_reinit(struct pxa25x_udc *dev)
|
||||
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->stopped = 0;
|
||||
INIT_LIST_HEAD (&ep->queue);
|
||||
ep->pio_irqs = 0;
|
||||
@ -1301,7 +1303,8 @@ fail:
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
if (dev->transceiver) {
|
||||
retval = otg_set_peripheral(dev->transceiver, &dev->gadget);
|
||||
retval = otg_set_peripheral(dev->transceiver->otg,
|
||||
&dev->gadget);
|
||||
if (retval) {
|
||||
DMSG("can't bind to transceiver\n");
|
||||
if (driver->unbind)
|
||||
@ -1360,7 +1363,7 @@ static int pxa25x_stop(struct usb_gadget_driver *driver)
|
||||
local_irq_enable();
|
||||
|
||||
if (dev->transceiver)
|
||||
(void) otg_set_peripheral(dev->transceiver, NULL);
|
||||
(void) otg_set_peripheral(dev->transceiver->otg, NULL);
|
||||
|
||||
driver->unbind(&dev->gadget);
|
||||
dev->gadget.dev.driver = NULL;
|
||||
@ -2159,7 +2162,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
|
||||
dev->dev = &pdev->dev;
|
||||
dev->mach = pdev->dev.platform_data;
|
||||
|
||||
dev->transceiver = otg_get_transceiver();
|
||||
dev->transceiver = usb_get_transceiver();
|
||||
|
||||
if (gpio_is_valid(dev->mach->gpio_pullup)) {
|
||||
if ((retval = gpio_request(dev->mach->gpio_pullup,
|
||||
@ -2238,7 +2241,7 @@ lubbock_fail0:
|
||||
gpio_free(dev->mach->gpio_pullup);
|
||||
err_gpio_pullup:
|
||||
if (dev->transceiver) {
|
||||
otg_put_transceiver(dev->transceiver);
|
||||
usb_put_transceiver(dev->transceiver);
|
||||
dev->transceiver = NULL;
|
||||
}
|
||||
clk_put(dev->clk);
|
||||
@ -2280,7 +2283,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
|
||||
clk_put(dev->clk);
|
||||
|
||||
if (dev->transceiver) {
|
||||
otg_put_transceiver(dev->transceiver);
|
||||
usb_put_transceiver(dev->transceiver);
|
||||
dev->transceiver = NULL;
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ struct pxa25x_udc {
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
struct pxa2xx_udc_mach_info *mach;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
u64 dma_mask;
|
||||
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
|
||||
|
||||
|
@ -1666,7 +1666,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
|
||||
|
||||
udc = to_gadget_udc(_gadget);
|
||||
if (udc->transceiver)
|
||||
return otg_set_power(udc->transceiver, mA);
|
||||
return usb_phy_set_power(udc->transceiver, mA);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -1835,7 +1835,8 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
||||
driver->driver.name);
|
||||
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
retval = otg_set_peripheral(udc->transceiver->otg,
|
||||
&udc->gadget);
|
||||
if (retval) {
|
||||
dev_err(udc->dev, "can't bind to transceiver\n");
|
||||
goto transceiver_fail;
|
||||
@ -1908,7 +1909,7 @@ static int pxa27x_udc_stop(struct usb_gadget_driver *driver)
|
||||
driver->driver.name);
|
||||
|
||||
if (udc->transceiver)
|
||||
return otg_set_peripheral(udc->transceiver, NULL);
|
||||
return otg_set_peripheral(udc->transceiver->otg, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2463,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
|
||||
|
||||
udc->dev = &pdev->dev;
|
||||
udc->mach = pdev->dev.platform_data;
|
||||
udc->transceiver = otg_get_transceiver();
|
||||
udc->transceiver = usb_get_transceiver();
|
||||
|
||||
gpio = udc->mach->gpio_pullup;
|
||||
if (gpio_is_valid(gpio)) {
|
||||
@ -2542,7 +2543,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev)
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_free(gpio);
|
||||
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
usb_put_transceiver(udc->transceiver);
|
||||
|
||||
udc->transceiver = NULL;
|
||||
platform_set_drvdata(_dev, NULL);
|
||||
|
@ -447,7 +447,7 @@ struct pxa_udc {
|
||||
struct usb_gadget_driver *driver;
|
||||
struct device *dev;
|
||||
struct pxa2xx_udc_mach_info *mach;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
|
||||
enum ep0_state ep0state;
|
||||
struct udc_stats stats;
|
||||
|
@ -663,11 +663,7 @@ static int sudmac_alloc_channel(struct r8a66597 *r8a66597,
|
||||
ep->fifoctr = D0FIFOCTR;
|
||||
|
||||
/* dma mapping */
|
||||
req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597),
|
||||
req->req.buf, req->req.length,
|
||||
dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
return 0;
|
||||
return usb_gadget_map_request(&r8a66597->gadget, &req->req, dma->dir);
|
||||
}
|
||||
|
||||
static void sudmac_free_channel(struct r8a66597 *r8a66597,
|
||||
@ -677,9 +673,7 @@ static void sudmac_free_channel(struct r8a66597 *r8a66597,
|
||||
if (!r8a66597_is_sudmac(r8a66597))
|
||||
return;
|
||||
|
||||
dma_unmap_single(r8a66597_to_dev(ep->r8a66597),
|
||||
req->req.dma, req->req.length,
|
||||
ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
usb_gadget_unmap_request(&r8a66597->gadget, &req->req, ep->dma->dir);
|
||||
|
||||
r8a66597_bclr(r8a66597, DREQE, ep->fifosel);
|
||||
r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/platform_data/s3c-hsudc.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <mach/regs-s3c2443-clock.h>
|
||||
|
||||
@ -145,7 +146,7 @@ struct s3c_hsudc {
|
||||
struct usb_gadget_driver *driver;
|
||||
struct device *dev;
|
||||
struct s3c24xx_hsudc_platdata *pd;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct usb_phy *transceiver;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
@ -759,7 +760,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
|
||||
unsigned long flags;
|
||||
u32 ecr = 0;
|
||||
|
||||
hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
|
||||
hsep = our_ep(_ep);
|
||||
if (!_ep || !desc || hsep->desc || _ep->name == ep0name
|
||||
|| desc->bDescriptorType != USB_DT_ENDPOINT
|
||||
|| hsep->bEndpointAddress != desc->bEndpointAddress
|
||||
@ -816,6 +817,7 @@ static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
|
||||
s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
|
||||
|
||||
hsep->desc = 0;
|
||||
hsep->ep.desc = NULL;
|
||||
hsep->stopped = 1;
|
||||
|
||||
spin_unlock_irqrestore(&hsudc->lock, flags);
|
||||
@ -853,7 +855,7 @@ static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
|
||||
{
|
||||
struct s3c_hsudc_req *hsreq;
|
||||
|
||||
hsreq = container_of(_req, struct s3c_hsudc_req, req);
|
||||
hsreq = our_req(_req);
|
||||
WARN_ON(!list_empty(&hsreq->queue));
|
||||
kfree(hsreq);
|
||||
}
|
||||
@ -876,12 +878,12 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
u32 offset;
|
||||
u32 csr;
|
||||
|
||||
hsreq = container_of(_req, struct s3c_hsudc_req, req);
|
||||
hsreq = our_req(_req);
|
||||
if ((!_req || !_req->complete || !_req->buf ||
|
||||
!list_empty(&hsreq->queue)))
|
||||
return -EINVAL;
|
||||
|
||||
hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
|
||||
hsep = our_ep(_ep);
|
||||
hsudc = hsep->dev;
|
||||
if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
@ -935,7 +937,7 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
struct s3c_hsudc_req *hsreq;
|
||||
unsigned long flags;
|
||||
|
||||
hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
|
||||
hsep = our_ep(_ep);
|
||||
if (!_ep || hsep->ep.name == ep0name)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1005,6 +1007,7 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
|
||||
hsep->ep.ops = &s3c_hsudc_ep_ops;
|
||||
hsep->fifo = hsudc->regs + S3C_BR(epnum);
|
||||
hsep->desc = 0;
|
||||
hsep->ep.desc = NULL;
|
||||
hsep->stopped = 0;
|
||||
hsep->wedge = 0;
|
||||
|
||||
@ -1166,7 +1169,8 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
if (hsudc->transceiver) {
|
||||
ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget);
|
||||
ret = otg_set_peripheral(hsudc->transceiver->otg,
|
||||
&hsudc->gadget);
|
||||
if (ret) {
|
||||
dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
|
||||
hsudc->gadget.name);
|
||||
@ -1178,6 +1182,9 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
|
||||
dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
|
||||
|
||||
s3c_hsudc_reconfig(hsudc);
|
||||
|
||||
pm_runtime_get_sync(hsudc->dev);
|
||||
|
||||
s3c_hsudc_init_phy();
|
||||
if (hsudc->pd->gpio_init)
|
||||
hsudc->pd->gpio_init();
|
||||
@ -1208,13 +1215,16 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
|
||||
hsudc->gadget.dev.driver = NULL;
|
||||
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
s3c_hsudc_uninit_phy();
|
||||
|
||||
pm_runtime_put(hsudc->dev);
|
||||
|
||||
if (hsudc->pd->gpio_uninit)
|
||||
hsudc->pd->gpio_uninit();
|
||||
s3c_hsudc_stop_activity(hsudc);
|
||||
spin_unlock_irqrestore(&hsudc->lock, flags);
|
||||
|
||||
if (hsudc->transceiver)
|
||||
(void) otg_set_peripheral(hsudc->transceiver, NULL);
|
||||
(void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
|
||||
|
||||
disable_irq(hsudc->irq);
|
||||
|
||||
@ -1243,7 +1253,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
return -ENODEV;
|
||||
|
||||
if (hsudc->transceiver)
|
||||
return otg_set_power(hsudc->transceiver, mA);
|
||||
return usb_phy_set_power(hsudc->transceiver, mA);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -1275,7 +1285,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
|
||||
hsudc->dev = dev;
|
||||
hsudc->pd = pdev->dev.platform_data;
|
||||
|
||||
hsudc->transceiver = otg_get_transceiver();
|
||||
hsudc->transceiver = usb_get_transceiver();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
|
||||
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
|
||||
@ -1362,6 +1372,8 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_add_udc;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
err_add_udc:
|
||||
device_unregister(&hsudc->gadget.dev);
|
||||
@ -1377,7 +1389,7 @@ err_remap:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_res:
|
||||
if (hsudc->transceiver)
|
||||
otg_put_transceiver(hsudc->transceiver);
|
||||
usb_put_transceiver(hsudc->transceiver);
|
||||
|
||||
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||
err_supplies:
|
||||
|
@ -1148,6 +1148,7 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
|
||||
dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
|
||||
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->halted = 1;
|
||||
|
||||
s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);
|
||||
@ -1630,6 +1631,7 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
|
||||
|
||||
ep->dev = dev;
|
||||
ep->desc = NULL;
|
||||
ep->ep.desc = NULL;
|
||||
ep->halted = 0;
|
||||
INIT_LIST_HEAD (&ep->queue);
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ static struct usb_composite_driver gserial_driver = {
|
||||
.name = "g_serial",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2003-2008 Alan Stern
|
||||
* Copyeight (C) 2009 Samsung Electronics
|
||||
* Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
|
||||
* Author: Michal Nazarewicz (mina86@mina86.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
|
||||
@ -145,48 +145,8 @@
|
||||
|
||||
#endif /* DUMP_MSGS */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Bulk-only data structures */
|
||||
|
||||
/* Command Block Wrapper */
|
||||
struct fsg_bulk_cb_wrap {
|
||||
__le32 Signature; /* Contains 'USBC' */
|
||||
u32 Tag; /* Unique per command id */
|
||||
__le32 DataTransferLength; /* Size of the data */
|
||||
u8 Flags; /* Direction in bit 7 */
|
||||
u8 Lun; /* LUN (normally 0) */
|
||||
u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */
|
||||
u8 CDB[16]; /* Command Data Block */
|
||||
};
|
||||
|
||||
#define USB_BULK_CB_WRAP_LEN 31
|
||||
#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */
|
||||
#define USB_BULK_IN_FLAG 0x80
|
||||
|
||||
/* Command Status Wrapper */
|
||||
struct bulk_cs_wrap {
|
||||
__le32 Signature; /* Should = 'USBS' */
|
||||
u32 Tag; /* Same as original command */
|
||||
__le32 Residue; /* Amount not transferred */
|
||||
u8 Status; /* See below */
|
||||
};
|
||||
|
||||
#define USB_BULK_CS_WRAP_LEN 13
|
||||
#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */
|
||||
#define USB_STATUS_PASS 0
|
||||
#define USB_STATUS_FAIL 1
|
||||
#define USB_STATUS_PHASE_ERROR 2
|
||||
|
||||
/* Bulk-only class specific requests */
|
||||
#define USB_BULK_RESET_REQUEST 0xff
|
||||
#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe
|
||||
|
||||
|
||||
/* CBI Interrupt data structure */
|
||||
struct interrupt_data {
|
||||
u8 bType;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* u_audio.c -- ALSA audio utilities for Gadget stack
|
||||
* u_uac1.c -- ALSA audio utilities for Gadget stack
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
||||
@ -17,7 +17,7 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include "u_audio.h"
|
||||
#include "u_uac1.h"
|
||||
|
||||
/*
|
||||
* This component encapsulates the ALSA devices for USB audio gadget
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* u_audio.h -- interface to USB gadget "ALSA AUDIO" utilities
|
||||
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
|
||||
*
|
||||
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
|
||||
* Copyright (C) 2008 Analog Devices, Inc
|
@ -22,6 +22,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
@ -49,6 +50,57 @@ static DEFINE_MUTEX(udc_lock);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
int usb_gadget_map_request(struct usb_gadget *gadget,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
if (req->length == 0)
|
||||
return 0;
|
||||
|
||||
if (req->num_sgs) {
|
||||
int mapped;
|
||||
|
||||
mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
if (mapped == 0) {
|
||||
dev_err(&gadget->dev, "failed to map SGs\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
req->num_mapped_sgs = mapped;
|
||||
} else {
|
||||
req->dma = dma_map_single(&gadget->dev, req->buf, req->length,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (dma_mapping_error(&gadget->dev, req->dma)) {
|
||||
dev_err(&gadget->dev, "failed to map buffer\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_map_request);
|
||||
|
||||
void usb_gadget_unmap_request(struct usb_gadget *gadget,
|
||||
struct usb_request *req, int is_in)
|
||||
{
|
||||
if (req->length == 0)
|
||||
return;
|
||||
|
||||
if (req->num_mapped_sgs) {
|
||||
dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
req->num_mapped_sgs = 0;
|
||||
} else {
|
||||
dma_unmap_single(&gadget->dev, req->dma, req->length,
|
||||
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* usb_gadget_start - tells usb device controller to start up
|
||||
* @gadget: The gadget we want to get started
|
||||
|
@ -196,7 +196,7 @@ config USB_EHCI_S5P
|
||||
|
||||
config USB_EHCI_MV
|
||||
bool "EHCI support for Marvell on-chip controller"
|
||||
depends on USB_EHCI_HCD
|
||||
depends on USB_EHCI_HCD && (ARCH_PXA || ARCH_MMP)
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
---help---
|
||||
Enables support for Marvell (including PXA and MMP series) on-chip
|
||||
@ -606,10 +606,3 @@ config USB_OCTEON_OHCI
|
||||
config USB_OCTEON2_COMMON
|
||||
bool
|
||||
default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI
|
||||
|
||||
config USB_PXA168_EHCI
|
||||
bool "Marvell PXA168 on-chip EHCI HCD support"
|
||||
depends on USB_EHCI_HCD && ARCH_MMP
|
||||
help
|
||||
Enable support for Marvell PXA168 SoC's on-chip EHCI
|
||||
host controller
|
||||
|
@ -107,7 +107,7 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
||||
HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "",
|
||||
HCC_HW_PREFETCH(params) ? " hw prefetch" : "",
|
||||
HCC_32FRAME_PERIODIC_LIST(params) ?
|
||||
" 32 peridic list" : "");
|
||||
" 32 periodic list" : "");
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
@ -142,12 +142,12 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
||||
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
|
||||
ehci->transceiver = otg_get_transceiver();
|
||||
ehci->transceiver = usb_get_transceiver();
|
||||
dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n",
|
||||
hcd, ehci, ehci->transceiver);
|
||||
|
||||
if (ehci->transceiver) {
|
||||
retval = otg_set_host(ehci->transceiver,
|
||||
retval = otg_set_host(ehci->transceiver->otg,
|
||||
&ehci_to_hcd(ehci)->self);
|
||||
if (retval) {
|
||||
if (ehci->transceiver)
|
||||
@ -194,7 +194,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
|
||||
if (ehci->transceiver) {
|
||||
otg_set_host(ehci->transceiver, NULL);
|
||||
otg_set_host(ehci->transceiver->otg, NULL);
|
||||
put_device(ehci->transceiver->dev);
|
||||
}
|
||||
|
||||
@ -216,6 +216,8 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci,
|
||||
unsigned int port_offset)
|
||||
{
|
||||
u32 portsc;
|
||||
struct usb_hcd *hcd = ehci_to_hcd(ehci);
|
||||
void __iomem *non_ehci = hcd->regs;
|
||||
|
||||
portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
|
||||
portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
|
||||
@ -231,6 +233,8 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci,
|
||||
portsc |= PORT_PTS_PTW;
|
||||
/* fall through */
|
||||
case FSL_USB2_PHY_UTMI:
|
||||
/* enable UTMI PHY */
|
||||
setbits32(non_ehci + FSL_SOC_USB_CTRL, CTRL_UTMI_PHY_EN);
|
||||
portsc |= PORT_PTS_UTMI;
|
||||
break;
|
||||
case FSL_USB2_PHY_NONE:
|
||||
@ -252,22 +256,19 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
|
||||
if (pdata->have_sysif_regs) {
|
||||
temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
|
||||
out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
|
||||
|
||||
/*
|
||||
* Turn on cache snooping hardware, since some PowerPC platforms
|
||||
* wholly rely on hardware to deal with cache coherent
|
||||
*/
|
||||
|
||||
/* Setup Snooping for all the 4GB space */
|
||||
/* SNOOP1 starts from 0x0, size 2G */
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0 | SNOOP_SIZE_2GB);
|
||||
/* SNOOP2 starts from 0x80000000, size 2G */
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
|
||||
/*
|
||||
* Turn on cache snooping hardware, since some PowerPC platforms
|
||||
* wholly rely on hardware to deal with cache coherent
|
||||
*/
|
||||
|
||||
/* Setup Snooping for all the 4GB space */
|
||||
/* SNOOP1 starts from 0x0, size 2G */
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0 | SNOOP_SIZE_2GB);
|
||||
/* SNOOP2 starts from 0x80000000, size 2G */
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
|
||||
#endif
|
||||
|
||||
if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
|
||||
(pdata->operating_mode == FSL_USB2_DR_OTG))
|
||||
ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
|
||||
@ -316,7 +317,9 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval;
|
||||
struct fsl_usb2_platform_data *pdata;
|
||||
struct device *dev;
|
||||
|
||||
dev = hcd->self.controller;
|
||||
pdata = hcd->self.controller->platform_data;
|
||||
ehci->big_endian_desc = pdata->big_endian_desc;
|
||||
ehci->big_endian_mmio = pdata->big_endian_mmio;
|
||||
@ -346,6 +349,16 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
if (of_device_is_compatible(dev->parent->of_node,
|
||||
"fsl,mpc5121-usb2-dr")) {
|
||||
/*
|
||||
* set SBUSCFG:AHBBRST so that control msgs don't
|
||||
* fail when doing heavy PATA writes.
|
||||
*/
|
||||
ehci_writel(ehci, SBUSCFG_INCR8,
|
||||
hcd->regs + FSL_SOC_USB_SBUSCFG);
|
||||
}
|
||||
|
||||
retval = ehci_fsl_reinit(ehci);
|
||||
return retval;
|
||||
}
|
||||
@ -469,6 +482,8 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev)
|
||||
ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE,
|
||||
hcd->regs + FSL_SOC_USB_ISIPHYCTRL);
|
||||
|
||||
ehci_writel(ehci, SBUSCFG_INCR8, hcd->regs + FSL_SOC_USB_SBUSCFG);
|
||||
|
||||
/* restore EHCI registers */
|
||||
ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
|
||||
ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
|
||||
|
@ -19,6 +19,8 @@
|
||||
#define _EHCI_FSL_H
|
||||
|
||||
/* offsets for the non-ehci registers in the FSL SOC USB controller */
|
||||
#define FSL_SOC_USB_SBUSCFG 0x90
|
||||
#define SBUSCFG_INCR8 0x02 /* INCR8, specified */
|
||||
#define FSL_SOC_USB_ULPIVP 0x170
|
||||
#define FSL_SOC_USB_PORTSC1 0x184
|
||||
#define PORT_PTS_MSK (3<<30)
|
||||
@ -45,5 +47,7 @@
|
||||
#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
|
||||
#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
|
||||
#define CTRL_UTMI_PHY_EN (1<<9)
|
||||
#define CTRL_PHY_CLK_VALID (1 << 17)
|
||||
#define SNOOP_SIZE_2GB 0x1e
|
||||
#endif /* _EHCI_FSL_H */
|
||||
|
@ -1361,11 +1361,6 @@ MODULE_LICENSE ("GPL");
|
||||
#define PLATFORM_DRIVER ehci_grlib_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_PXA168_EHCI
|
||||
#include "ehci-pxa168.c"
|
||||
#define PLATFORM_DRIVER ehci_pxa168_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_XLR
|
||||
#include "ehci-xls.c"
|
||||
#define PLATFORM_DRIVER ehci_xls_driver
|
||||
@ -1376,6 +1371,11 @@ MODULE_LICENSE ("GPL");
|
||||
#define PLATFORM_DRIVER ehci_mv_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MACH_LOONGSON1
|
||||
#include "ehci-ls1x.c"
|
||||
#define PLATFORM_DRIVER ehci_ls1x_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(XILINX_OF_PLATFORM_DRIVER)
|
||||
|
@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||
ehci->owned_ports = 0;
|
||||
}
|
||||
|
||||
static int ehci_port_change(struct ehci_hcd *ehci)
|
||||
static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
|
||||
{
|
||||
int i = HCS_N_PORTS(ehci->hcs_params);
|
||||
|
||||
@ -727,7 +727,7 @@ static int ehci_hub_control (
|
||||
#ifdef CONFIG_USB_OTG
|
||||
if ((hcd->self.otg_port == (wIndex + 1))
|
||||
&& hcd->self.b_hnp_enable) {
|
||||
otg_start_hnp(ehci->transceiver);
|
||||
otg_start_hnp(ehci->transceiver->otg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@ -1076,7 +1076,8 @@ error_exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
||||
static void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd,
|
||||
int portnum)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
|
||||
@ -1085,7 +1086,8 @@ static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
||||
set_owner(ehci, --portnum, PORT_OWNER);
|
||||
}
|
||||
|
||||
static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
|
||||
static int __maybe_unused ehci_port_handed_over(struct usb_hcd *hcd,
|
||||
int portnum)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
u32 __iomem *reg;
|
||||
|
159
drivers/usb/host/ehci-ls1x.c
Normal file
159
drivers/usb/host/ehci-ls1x.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Bus Glue for Loongson LS1X built-in EHCI controller.
|
||||
*
|
||||
* Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.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 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static int ehci_ls1x_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int ret;
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
|
||||
ret = ehci_setup(hcd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_ls1x_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "LOONGSON1 EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_ls1x_reset,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_hcd_ls1x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
pr_debug("initializing loongson1 ehci USB Controller\n");
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no IRQ. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
irq = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no register addr. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ehci_ls1x_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
dev_dbg(&pdev->dev, "controller already in use\n");
|
||||
ret = -EBUSY;
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
ret = -EFAULT;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret)
|
||||
goto err_iounmap;
|
||||
|
||||
return ret;
|
||||
|
||||
err_iounmap:
|
||||
iounmap(hcd->regs);
|
||||
err_release_region:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ehci_hcd_ls1x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ehci_ls1x_driver = {
|
||||
.probe = ehci_hcd_ls1x_probe,
|
||||
.remove = ehci_hcd_ls1x_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "ls1x-ehci",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ls1x-ehci");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user