mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
USB patches for 4.6-rc1
Here is the big USB patchset for 4.6-rc1. The normal mess is here, gadget and xhci fixes and updates, and lots of other driver updates and cleanups as well. Full details are in the shortlog. All have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlbp8/EACgkQMUfUDdst+ylsyQCgnVK6ZIFVPV9VijJvBIjxS3F+ fTMAoIMQwNrRMHQOq/lhxX00AgN0B9Ch =2EQp -----END PGP SIGNATURE----- Merge tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here is the big USB patchset for 4.6-rc1. The normal mess is here, gadget and xhci fixes and updates, and lots of other driver updates and cleanups as well. Full details are in the shortlog. All have been in linux-next for a while with no reported issues" * tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (266 commits) USB: core: let USB device know device node usb: devio: Add ioctl to disallow detaching kernel USB drivers. usb: gadget: f_acm: Fix configfs attr name usb: udc: lpc32xx: remove USB PLL and USB OTG clock management usb: udc: lpc32xx: remove direct access to clock controller registers usb: udc: lpc32xx: switch to clock prepare/unprepare model usb: renesas_usbhs: gadget: fix giveback status code in usbhsg_pipe_disable() usb: gadget: renesas_usb3: Use ARCH_RENESAS usb: dwc2: Fix issues in dwc2_complete_non_isoc_xfer_ddma() usb: dwc2: Add support for Lantiq ARX and XRX SoCs usb: phy: generic: Handle late registration of gadget usb: gadget: bdc_udc: fix race condition in bdc_udc_exit() usb: musb: core: added missing const qualifier to musb_hdrc_platform_data::config usb: dwc2: Move host-specific core functions into hcd.c usb: dwc2: Move register save and restore functions usb: dwc2: Use kmem_cache_free() usb: dwc2: host: If using uframe scheduler, end splits better usb: dwc2: host: Totally redo the microframe scheduler usb: dwc2: host: Properly set even/odd frame usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call ...
This commit is contained in:
commit
48d10bda1f
192 changed files with 9738 additions and 6111 deletions
|
|
@ -26,7 +26,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/
|
|||
obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
obj-$(CONFIG_USB_HWA_HCD) += host/
|
||||
obj-$(CONFIG_USB_IMX21_HCD) += host/
|
||||
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/
|
||||
obj-$(CONFIG_USB_FSL_USB2) += host/
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += host/
|
||||
obj-$(CONFIG_USB_MAX3421_HCD) += host/
|
||||
|
||||
|
|
|
|||
|
|
@ -476,6 +476,8 @@ static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
|
|||
return -EINVAL;
|
||||
if (index < 0 || index > 0x7f)
|
||||
return -EINVAL;
|
||||
if (tmp < 0 || tmp > len - pos)
|
||||
return -EINVAL;
|
||||
pos += tmp;
|
||||
|
||||
/* skip trailing newline */
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ struct ci_hdrc_imx_platform_flag {
|
|||
bool runtime_pm;
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
|
||||
.flags = CI_HDRC_TURN_VBUS_EARLY_ON |
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
|
|
@ -66,6 +71,7 @@ static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
|
|||
};
|
||||
|
||||
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
|
||||
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
|
||||
{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
|
||||
{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
|
||||
|
|
@ -244,7 +250,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
struct ci_hdrc_platform_data pdata = {
|
||||
.name = dev_name(&pdev->dev),
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_SET_NON_ZERO_TTHA,
|
||||
};
|
||||
int ret;
|
||||
const struct of_device_id *of_id;
|
||||
|
|
@ -302,9 +307,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
&pdata);
|
||||
if (IS_ERR(data->ci_pdev)) {
|
||||
ret = PTR_ERR(data->ci_pdev);
|
||||
dev_err(&pdev->dev,
|
||||
"Can't register ci_hdrc platform device, err=%d\n",
|
||||
ret);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"ci_hdrc_add_device failed, err=%d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -721,6 +721,9 @@ static int ci_get_platdata(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL))
|
||||
platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA;
|
||||
|
||||
ext_id = ERR_PTR(-ENODEV);
|
||||
ext_vbus = ERR_PTR(-ENODEV);
|
||||
if (of_property_read_bool(dev->of_node, "extcon")) {
|
||||
|
|
|
|||
|
|
@ -175,7 +175,6 @@ static int ci_requests_show(struct seq_file *s, void *data)
|
|||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
unsigned long flags;
|
||||
struct list_head *ptr = NULL;
|
||||
struct ci_hw_req *req = NULL;
|
||||
struct td_node *node, *tmpnode;
|
||||
unsigned i, j, qsize = sizeof(struct ci_hw_td)/sizeof(u32);
|
||||
|
|
@ -187,9 +186,7 @@ static int ci_requests_show(struct seq_file *s, void *data)
|
|||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
for (i = 0; i < ci->hw_ep_max; i++)
|
||||
list_for_each(ptr, &ci->ci_hw_ep[i].qh.queue) {
|
||||
req = list_entry(ptr, struct ci_hw_req, queue);
|
||||
|
||||
list_for_each_entry(req, &ci->ci_hw_ep[i].qh.queue, queue) {
|
||||
list_for_each_entry_safe(node, tmpnode, &req->tds, td) {
|
||||
seq_printf(s, "EP=%02i: TD=%08X %s\n",
|
||||
i % (ci->hw_ep_max / 2),
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
ci->fsm.a_bus_req = 1;
|
||||
if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
|
||||
ci->gadget.host_request_flag = 1;
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
|
|
@ -144,8 +149,14 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
mutex_lock(&ci->fsm.lock);
|
||||
if (buf[0] == '0')
|
||||
ci->fsm.b_bus_req = 0;
|
||||
else if (buf[0] == '1')
|
||||
else if (buf[0] == '1') {
|
||||
ci->fsm.b_bus_req = 1;
|
||||
if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
|
||||
ci->gadget.host_request_flag = 1;
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
|
|
@ -198,6 +209,7 @@ static unsigned otg_timer_ms[] = {
|
|||
TA_AIDL_BDIS,
|
||||
TB_ASE0_BRST,
|
||||
TA_BIDL_ADIS,
|
||||
TB_AIDL_BDIS,
|
||||
TB_SE0_SRP,
|
||||
TB_SRP_FAIL,
|
||||
0,
|
||||
|
|
@ -309,6 +321,12 @@ static int a_bidl_adis_tmout(struct ci_hdrc *ci)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int b_aidl_bdis_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.a_bus_suspend = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b_se0_srp_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_se0_srp = 1;
|
||||
|
|
@ -353,6 +371,7 @@ static int (*otg_timer_handlers[])(struct ci_hdrc *) = {
|
|||
a_aidl_bdis_tmout, /* A_AIDL_BDIS */
|
||||
b_ase0_brst_tmout, /* B_ASE0_BRST */
|
||||
a_bidl_adis_tmout, /* A_BIDL_ADIS */
|
||||
b_aidl_bdis_tmout, /* B_AIDL_BDIS */
|
||||
b_se0_srp_tmout, /* B_SE0_SRP */
|
||||
b_srp_fail_tmout, /* B_SRP_FAIL */
|
||||
NULL, /* A_WAIT_ENUM */
|
||||
|
|
@ -644,9 +663,9 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
|
|||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) {
|
||||
fsm->a_bus_suspend = 1;
|
||||
ci_otg_queue_work(ci);
|
||||
ci_otg_add_timer(ci, B_AIDL_BDIS);
|
||||
} else if (intr_sts & USBi_PCI) {
|
||||
ci_otg_del_timer(ci, B_AIDL_BDIS);
|
||||
if (fsm->a_bus_suspend == 1)
|
||||
fsm->a_bus_suspend = 0;
|
||||
}
|
||||
|
|
@ -786,6 +805,10 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
|
|||
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
|
||||
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
|
||||
ci->fsm.ops = &ci_otg_ops;
|
||||
ci->gadget.hnp_polling_support = 1;
|
||||
ci->fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
|
||||
if (!ci->fsm.host_req_flag)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ci->fsm.lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@
|
|||
/* SSEND time before SRP */
|
||||
#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */
|
||||
|
||||
#define TB_AIDL_BDIS (20) /* 4ms ~ 150ms, section 5.2.1 */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_OTG_FSM)
|
||||
|
||||
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
|
||||
|
|
|
|||
|
|
@ -819,7 +819,6 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|||
ci->ep0out : ci->ep0in;
|
||||
if (!list_empty(&hwep->qh.queue)) {
|
||||
_ep_nuke(hwep);
|
||||
retval = -EOVERFLOW;
|
||||
dev_warn(hwep->ci->dev, "endpoint ctrl %X nuked\n",
|
||||
_usb_addr(hwep));
|
||||
}
|
||||
|
|
@ -1068,7 +1067,8 @@ __acquires(ci->lock)
|
|||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
|
||||
if ((type != (USB_DIR_IN|USB_RECIP_DEVICE) ||
|
||||
le16_to_cpu(req.wIndex) == OTG_STS_SELECTOR) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_INTERFACE))
|
||||
goto delegate;
|
||||
|
|
|
|||
|
|
@ -713,9 +713,20 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
}
|
||||
|
||||
if (acm->susp_count) {
|
||||
if (acm->putbuffer) {
|
||||
/* now to preserve order */
|
||||
usb_anchor_urb(acm->putbuffer->urb, &acm->delayed);
|
||||
acm->putbuffer = NULL;
|
||||
}
|
||||
usb_anchor_urb(wb->urb, &acm->delayed);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return count;
|
||||
} else {
|
||||
if (acm->putbuffer) {
|
||||
/* at this point there is no good way to handle errors */
|
||||
acm_start_wb(acm, acm->putbuffer);
|
||||
acm->putbuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
stat = acm_start_wb(acm, wb);
|
||||
|
|
@ -726,6 +737,60 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
return count;
|
||||
}
|
||||
|
||||
static void acm_tty_flush_chars(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
struct acm_wb *cur = acm->putbuffer;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
|
||||
acm->putbuffer = NULL;
|
||||
err = usb_autopm_get_interface_async(acm->control);
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
if (err < 0) {
|
||||
cur->use = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (acm->susp_count)
|
||||
usb_anchor_urb(cur->urb, &acm->delayed);
|
||||
else
|
||||
acm_start_wb(acm, cur);
|
||||
out:
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static int acm_tty_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
struct acm_wb *cur;
|
||||
int wbn;
|
||||
unsigned long flags;
|
||||
|
||||
overflow:
|
||||
cur = acm->putbuffer;
|
||||
if (!cur) {
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
wbn = acm_wb_alloc(acm);
|
||||
if (wbn >= 0) {
|
||||
cur = &acm->wb[wbn];
|
||||
acm->putbuffer = cur;
|
||||
}
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
if (!cur)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cur->len == acm->writesize) {
|
||||
acm_tty_flush_chars(tty);
|
||||
goto overflow;
|
||||
}
|
||||
|
||||
cur->buf[cur->len++] = ch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acm_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
|
@ -1905,6 +1970,8 @@ static const struct tty_operations acm_ops = {
|
|||
.cleanup = acm_tty_cleanup,
|
||||
.hangup = acm_tty_hangup,
|
||||
.write = acm_tty_write,
|
||||
.put_char = acm_tty_put_char,
|
||||
.flush_chars = acm_tty_flush_chars,
|
||||
.write_room = acm_tty_write_room,
|
||||
.ioctl = acm_tty_ioctl,
|
||||
.throttle = acm_tty_throttle,
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ struct acm {
|
|||
unsigned long read_urbs_free;
|
||||
struct urb *read_urbs[ACM_NR];
|
||||
struct acm_rb read_buffers[ACM_NR];
|
||||
struct acm_wb *putbuffer; /* for acm_tty_put_char() */
|
||||
int rx_buflimit;
|
||||
int rx_endpoint;
|
||||
spinlock_t read_lock;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/tmc.h>
|
||||
|
|
@ -87,6 +88,23 @@ struct usbtmc_device_data {
|
|||
u8 bTag_last_write; /* needed for abort */
|
||||
u8 bTag_last_read; /* needed for abort */
|
||||
|
||||
/* data for interrupt in endpoint handling */
|
||||
u8 bNotify1;
|
||||
u8 bNotify2;
|
||||
u16 ifnum;
|
||||
u8 iin_bTag;
|
||||
u8 *iin_buffer;
|
||||
atomic_t iin_data_valid;
|
||||
unsigned int iin_ep;
|
||||
int iin_ep_present;
|
||||
int iin_interval;
|
||||
struct urb *iin_urb;
|
||||
u16 iin_wMaxPacketSize;
|
||||
atomic_t srq_asserted;
|
||||
|
||||
/* coalesced usb488_caps from usbtmc_dev_capabilities */
|
||||
__u8 usb488_caps;
|
||||
|
||||
u8 rigol_quirk;
|
||||
|
||||
/* attributes from the USB TMC spec for this device */
|
||||
|
|
@ -99,6 +117,8 @@ struct usbtmc_device_data {
|
|||
struct usbtmc_dev_capabilities capabilities;
|
||||
struct kref kref;
|
||||
struct mutex io_mutex; /* only one i/o function running at a time */
|
||||
wait_queue_head_t waitq;
|
||||
struct fasync_struct *fasync;
|
||||
};
|
||||
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
|
||||
|
||||
|
|
@ -373,6 +393,142 @@ exit:
|
|||
return rv;
|
||||
}
|
||||
|
||||
static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
|
||||
void __user *arg)
|
||||
{
|
||||
struct device *dev = &data->intf->dev;
|
||||
u8 *buffer;
|
||||
u8 tag;
|
||||
__u8 stb;
|
||||
int rv;
|
||||
|
||||
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
|
||||
data->iin_ep_present);
|
||||
|
||||
buffer = kmalloc(8, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_set(&data->iin_data_valid, 0);
|
||||
|
||||
/* must issue read_stb before using poll or select */
|
||||
atomic_set(&data->srq_asserted, 0);
|
||||
|
||||
rv = usb_control_msg(data->usb_dev,
|
||||
usb_rcvctrlpipe(data->usb_dev, 0),
|
||||
USBTMC488_REQUEST_READ_STATUS_BYTE,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
data->iin_bTag,
|
||||
data->ifnum,
|
||||
buffer, 0x03, USBTMC_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "stb usb_control_msg returned %d\n", rv);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (buffer[0] != USBTMC_STATUS_SUCCESS) {
|
||||
dev_err(dev, "control status returned %x\n", buffer[0]);
|
||||
rv = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data->iin_ep_present) {
|
||||
rv = wait_event_interruptible_timeout(
|
||||
data->waitq,
|
||||
atomic_read(&data->iin_data_valid) != 0,
|
||||
USBTMC_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_dbg(dev, "wait interrupted %d\n", rv);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
dev_dbg(dev, "wait timed out\n");
|
||||
rv = -ETIME;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
tag = data->bNotify1 & 0x7f;
|
||||
if (tag != data->iin_bTag) {
|
||||
dev_err(dev, "expected bTag %x got %x\n",
|
||||
data->iin_bTag, tag);
|
||||
}
|
||||
|
||||
stb = data->bNotify2;
|
||||
} else {
|
||||
stb = buffer[2];
|
||||
}
|
||||
|
||||
rv = copy_to_user(arg, &stb, sizeof(stb));
|
||||
if (rv)
|
||||
rv = -EFAULT;
|
||||
|
||||
exit:
|
||||
/* bump interrupt bTag */
|
||||
data->iin_bTag += 1;
|
||||
if (data->iin_bTag > 127)
|
||||
/* 1 is for SRQ see USBTMC-USB488 subclass spec section 4.3.1 */
|
||||
data->iin_bTag = 2;
|
||||
|
||||
kfree(buffer);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
|
||||
void __user *arg, unsigned int cmd)
|
||||
{
|
||||
struct device *dev = &data->intf->dev;
|
||||
__u8 val;
|
||||
u8 *buffer;
|
||||
u16 wValue;
|
||||
int rv;
|
||||
|
||||
if (!(data->usb488_caps & USBTMC488_CAPABILITY_SIMPLE))
|
||||
return -EINVAL;
|
||||
|
||||
buffer = kmalloc(8, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cmd == USBTMC488_REQUEST_REN_CONTROL) {
|
||||
rv = copy_from_user(&val, arg, sizeof(val));
|
||||
if (rv) {
|
||||
rv = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
wValue = val ? 1 : 0;
|
||||
} else {
|
||||
wValue = 0;
|
||||
}
|
||||
|
||||
rv = usb_control_msg(data->usb_dev,
|
||||
usb_rcvctrlpipe(data->usb_dev, 0),
|
||||
cmd,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
wValue,
|
||||
data->ifnum,
|
||||
buffer, 0x01, USBTMC_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "simple usb_control_msg failed %d\n", rv);
|
||||
goto exit;
|
||||
} else if (rv != 1) {
|
||||
dev_warn(dev, "simple usb_control_msg returned %d\n", rv);
|
||||
rv = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (buffer[0] != USBTMC_STATUS_SUCCESS) {
|
||||
dev_err(dev, "simple control status returned %x\n", buffer[0]);
|
||||
rv = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
rv = 0;
|
||||
|
||||
exit:
|
||||
kfree(buffer);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint.
|
||||
* @transfer_size: number of bytes to request from the device.
|
||||
|
|
@ -895,6 +1051,7 @@ static int get_capabilities(struct usbtmc_device_data *data)
|
|||
data->capabilities.device_capabilities = buffer[5];
|
||||
data->capabilities.usb488_interface_capabilities = buffer[14];
|
||||
data->capabilities.usb488_device_capabilities = buffer[15];
|
||||
data->usb488_caps = (buffer[14] & 0x07) | ((buffer[15] & 0x0f) << 4);
|
||||
rv = 0;
|
||||
|
||||
err_out:
|
||||
|
|
@ -1069,6 +1226,33 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
case USBTMC_IOCTL_ABORT_BULK_IN:
|
||||
retval = usbtmc_ioctl_abort_bulk_in(data);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_GET_CAPS:
|
||||
retval = copy_to_user((void __user *)arg,
|
||||
&data->usb488_caps,
|
||||
sizeof(data->usb488_caps));
|
||||
if (retval)
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_READ_STB:
|
||||
retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_REN_CONTROL:
|
||||
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
|
||||
USBTMC488_REQUEST_REN_CONTROL);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_GOTO_LOCAL:
|
||||
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
|
||||
USBTMC488_REQUEST_GOTO_LOCAL);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_LOCAL_LOCKOUT:
|
||||
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
|
||||
USBTMC488_REQUEST_LOCAL_LOCKOUT);
|
||||
break;
|
||||
}
|
||||
|
||||
skip_io_on_zombie:
|
||||
|
|
@ -1076,6 +1260,34 @@ skip_io_on_zombie:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int usbtmc_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct usbtmc_device_data *data = file->private_data;
|
||||
|
||||
return fasync_helper(fd, file, on, &data->fasync);
|
||||
}
|
||||
|
||||
static unsigned int usbtmc_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct usbtmc_device_data *data = file->private_data;
|
||||
unsigned int mask;
|
||||
|
||||
mutex_lock(&data->io_mutex);
|
||||
|
||||
if (data->zombie) {
|
||||
mask = POLLHUP | POLLERR;
|
||||
goto no_poll;
|
||||
}
|
||||
|
||||
poll_wait(file, &data->waitq, wait);
|
||||
|
||||
mask = (atomic_read(&data->srq_asserted)) ? POLLIN | POLLRDNORM : 0;
|
||||
|
||||
no_poll:
|
||||
mutex_unlock(&data->io_mutex);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = usbtmc_read,
|
||||
|
|
@ -1083,6 +1295,8 @@ static const struct file_operations fops = {
|
|||
.open = usbtmc_open,
|
||||
.release = usbtmc_release,
|
||||
.unlocked_ioctl = usbtmc_ioctl,
|
||||
.fasync = usbtmc_fasync,
|
||||
.poll = usbtmc_poll,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
|
|
@ -1092,6 +1306,67 @@ static struct usb_class_driver usbtmc_class = {
|
|||
.minor_base = USBTMC_MINOR_BASE,
|
||||
};
|
||||
|
||||
static void usbtmc_interrupt(struct urb *urb)
|
||||
{
|
||||
struct usbtmc_device_data *data = urb->context;
|
||||
struct device *dev = &data->intf->dev;
|
||||
int status = urb->status;
|
||||
int rv;
|
||||
|
||||
dev_dbg(&data->intf->dev, "int status: %d len %d\n",
|
||||
status, urb->actual_length);
|
||||
|
||||
switch (status) {
|
||||
case 0: /* SUCCESS */
|
||||
/* check for valid STB notification */
|
||||
if (data->iin_buffer[0] > 0x81) {
|
||||
data->bNotify1 = data->iin_buffer[0];
|
||||
data->bNotify2 = data->iin_buffer[1];
|
||||
atomic_set(&data->iin_data_valid, 1);
|
||||
wake_up_interruptible(&data->waitq);
|
||||
goto exit;
|
||||
}
|
||||
/* check for SRQ notification */
|
||||
if (data->iin_buffer[0] == 0x81) {
|
||||
if (data->fasync)
|
||||
kill_fasync(&data->fasync,
|
||||
SIGIO, POLL_IN);
|
||||
|
||||
atomic_set(&data->srq_asserted, 1);
|
||||
wake_up_interruptible(&data->waitq);
|
||||
goto exit;
|
||||
}
|
||||
dev_warn(dev, "invalid notification: %x\n", data->iin_buffer[0]);
|
||||
break;
|
||||
case -EOVERFLOW:
|
||||
dev_err(dev, "overflow with length %d, actual length is %d\n",
|
||||
data->iin_wMaxPacketSize, urb->actual_length);
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
case -EILSEQ:
|
||||
case -ETIME:
|
||||
/* urb terminated, clean up */
|
||||
dev_dbg(dev, "urb terminated, status: %d\n", status);
|
||||
return;
|
||||
default:
|
||||
dev_err(dev, "unknown status received: %d\n", status);
|
||||
}
|
||||
exit:
|
||||
rv = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (rv)
|
||||
dev_err(dev, "usb_submit_urb failed: %d\n", rv);
|
||||
}
|
||||
|
||||
static void usbtmc_free_int(struct usbtmc_device_data *data)
|
||||
{
|
||||
if (!data->iin_ep_present || !data->iin_urb)
|
||||
return;
|
||||
usb_kill_urb(data->iin_urb);
|
||||
kfree(data->iin_buffer);
|
||||
usb_free_urb(data->iin_urb);
|
||||
kref_put(&data->kref, usbtmc_delete);
|
||||
}
|
||||
|
||||
static int usbtmc_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
|
|
@ -1114,6 +1389,9 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
usb_set_intfdata(intf, data);
|
||||
kref_init(&data->kref);
|
||||
mutex_init(&data->io_mutex);
|
||||
init_waitqueue_head(&data->waitq);
|
||||
atomic_set(&data->iin_data_valid, 0);
|
||||
atomic_set(&data->srq_asserted, 0);
|
||||
data->zombie = 0;
|
||||
|
||||
/* Determine if it is a Rigol or not */
|
||||
|
|
@ -1134,9 +1412,12 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
data->bTag = 1;
|
||||
data->TermCharEnabled = 0;
|
||||
data->TermChar = '\n';
|
||||
/* 2 <= bTag <= 127 USBTMC-USB488 subclass specification 4.3.1 */
|
||||
data->iin_bTag = 2;
|
||||
|
||||
/* USBTMC devices have only one setting, so use that */
|
||||
iface_desc = data->intf->cur_altsetting;
|
||||
data->ifnum = iface_desc->desc.bInterfaceNumber;
|
||||
|
||||
/* Find bulk in endpoint */
|
||||
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
|
||||
|
|
@ -1161,6 +1442,20 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
break;
|
||||
}
|
||||
}
|
||||
/* Find int endpoint */
|
||||
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
|
||||
endpoint = &iface_desc->endpoint[n].desc;
|
||||
|
||||
if (usb_endpoint_is_int_in(endpoint)) {
|
||||
data->iin_ep_present = 1;
|
||||
data->iin_ep = endpoint->bEndpointAddress;
|
||||
data->iin_wMaxPacketSize = usb_endpoint_maxp(endpoint);
|
||||
data->iin_interval = endpoint->bInterval;
|
||||
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
|
||||
data->iin_ep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
retcode = get_capabilities(data);
|
||||
if (retcode)
|
||||
|
|
@ -1169,6 +1464,39 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
retcode = sysfs_create_group(&intf->dev.kobj,
|
||||
&capability_attr_grp);
|
||||
|
||||
if (data->iin_ep_present) {
|
||||
/* allocate int urb */
|
||||
data->iin_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!data->iin_urb) {
|
||||
dev_err(&intf->dev, "Failed to allocate int urb\n");
|
||||
goto error_register;
|
||||
}
|
||||
|
||||
/* will reference data in int urb */
|
||||
kref_get(&data->kref);
|
||||
|
||||
/* allocate buffer for interrupt in */
|
||||
data->iin_buffer = kmalloc(data->iin_wMaxPacketSize,
|
||||
GFP_KERNEL);
|
||||
if (!data->iin_buffer) {
|
||||
dev_err(&intf->dev, "Failed to allocate int buf\n");
|
||||
goto error_register;
|
||||
}
|
||||
|
||||
/* fill interrupt urb */
|
||||
usb_fill_int_urb(data->iin_urb, data->usb_dev,
|
||||
usb_rcvintpipe(data->usb_dev, data->iin_ep),
|
||||
data->iin_buffer, data->iin_wMaxPacketSize,
|
||||
usbtmc_interrupt,
|
||||
data, data->iin_interval);
|
||||
|
||||
retcode = usb_submit_urb(data->iin_urb, GFP_KERNEL);
|
||||
if (retcode) {
|
||||
dev_err(&intf->dev, "Failed to submit iin_urb\n");
|
||||
goto error_register;
|
||||
}
|
||||
}
|
||||
|
||||
retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);
|
||||
|
||||
retcode = usb_register_dev(intf, &usbtmc_class);
|
||||
|
|
@ -1185,6 +1513,7 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
error_register:
|
||||
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
|
||||
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
|
||||
usbtmc_free_int(data);
|
||||
kref_put(&data->kref, usbtmc_delete);
|
||||
return retcode;
|
||||
}
|
||||
|
|
@ -1201,7 +1530,9 @@ static void usbtmc_disconnect(struct usb_interface *intf)
|
|||
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
|
||||
mutex_lock(&data->io_mutex);
|
||||
data->zombie = 1;
|
||||
wake_up_all(&data->waitq);
|
||||
mutex_unlock(&data->io_mutex);
|
||||
usbtmc_free_int(data);
|
||||
kref_put(&data->kref, usbtmc_delete);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ static const char *const speed_names[] = {
|
|||
[USB_SPEED_HIGH] = "high-speed",
|
||||
[USB_SPEED_WIRELESS] = "wireless",
|
||||
[USB_SPEED_SUPER] = "super-speed",
|
||||
[USB_SPEED_SUPER_PLUS] = "super-speed-plus",
|
||||
};
|
||||
|
||||
const char *usb_speed_string(enum usb_device_speed speed)
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
fsm->b_srp_done = 0;
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (fsm->otg->gadget)
|
||||
fsm->otg->gadget->host_request_flag = 0;
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
otg_del_timer(fsm, B_ASE0_BRST);
|
||||
|
|
@ -107,6 +109,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
case OTG_STATE_A_PERIPHERAL:
|
||||
otg_del_timer(fsm, A_BIDL_ADIS);
|
||||
fsm->a_bidl_adis_tmout = 0;
|
||||
if (fsm->otg->gadget)
|
||||
fsm->otg->gadget->host_request_flag = 0;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
otg_del_timer(fsm, A_WAIT_VFALL);
|
||||
|
|
@ -120,6 +124,87 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
}
|
||||
}
|
||||
|
||||
static void otg_hnp_polling_work(struct work_struct *work)
|
||||
{
|
||||
struct otg_fsm *fsm = container_of(to_delayed_work(work),
|
||||
struct otg_fsm, hnp_polling_work);
|
||||
struct usb_device *udev;
|
||||
enum usb_otg_state state = fsm->otg->state;
|
||||
u8 flag;
|
||||
int retval;
|
||||
|
||||
if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
|
||||
return;
|
||||
|
||||
udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
|
||||
if (!udev) {
|
||||
dev_err(fsm->otg->host->controller,
|
||||
"no usb dev connected, can't start HNP polling\n");
|
||||
return;
|
||||
}
|
||||
|
||||
*fsm->host_req_flag = 0;
|
||||
/* Get host request flag from connected USB device */
|
||||
retval = usb_control_msg(udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
USB_REQ_GET_STATUS,
|
||||
USB_DIR_IN | USB_RECIP_DEVICE,
|
||||
0,
|
||||
OTG_STS_SELECTOR,
|
||||
fsm->host_req_flag,
|
||||
1,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (retval != 1) {
|
||||
dev_err(&udev->dev, "Get one byte OTG status failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
flag = *fsm->host_req_flag;
|
||||
if (flag == 0) {
|
||||
/* Continue HNP polling */
|
||||
schedule_delayed_work(&fsm->hnp_polling_work,
|
||||
msecs_to_jiffies(T_HOST_REQ_POLL));
|
||||
return;
|
||||
} else if (flag != HOST_REQUEST_FLAG) {
|
||||
dev_err(&udev->dev, "host request flag %d is invalid\n", flag);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Host request flag is set */
|
||||
if (state == OTG_STATE_A_HOST) {
|
||||
/* Set b_hnp_enable */
|
||||
if (!fsm->otg->host->b_hnp_enable) {
|
||||
retval = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE, 0,
|
||||
USB_DEVICE_B_HNP_ENABLE,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (retval >= 0)
|
||||
fsm->otg->host->b_hnp_enable = 1;
|
||||
}
|
||||
fsm->a_bus_req = 0;
|
||||
} else if (state == OTG_STATE_B_HOST) {
|
||||
fsm->b_bus_req = 0;
|
||||
}
|
||||
|
||||
otg_statemachine(fsm);
|
||||
}
|
||||
|
||||
static void otg_start_hnp_polling(struct otg_fsm *fsm)
|
||||
{
|
||||
/*
|
||||
* The memory of host_req_flag should be allocated by
|
||||
* controller driver, otherwise, hnp polling is not started.
|
||||
*/
|
||||
if (!fsm->host_req_flag)
|
||||
return;
|
||||
|
||||
INIT_DELAYED_WORK(&fsm->hnp_polling_work, otg_hnp_polling_work);
|
||||
schedule_delayed_work(&fsm->hnp_polling_work,
|
||||
msecs_to_jiffies(T_HOST_REQ_POLL));
|
||||
}
|
||||
|
||||
/* Called when entering a state */
|
||||
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
||||
{
|
||||
|
|
@ -169,6 +254,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
|||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
usb_bus_start_enum(fsm->otg->host,
|
||||
fsm->otg->host->otg_port);
|
||||
otg_start_hnp_polling(fsm);
|
||||
break;
|
||||
case OTG_STATE_A_IDLE:
|
||||
otg_drv_vbus(fsm, 0);
|
||||
|
|
@ -203,6 +289,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
|||
*/
|
||||
if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
|
||||
otg_add_timer(fsm, A_WAIT_ENUM);
|
||||
otg_start_hnp_polling(fsm);
|
||||
break;
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
otg_drv_vbus(fsm, 1);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
|
||||
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
|
||||
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
|
||||
usbcore-y += port.o
|
||||
usbcore-y += port.o of.o
|
||||
|
||||
usbcore-$(CONFIG_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@ int hcd_buffer_create(struct usb_hcd *hcd)
|
|||
char name[16];
|
||||
int i, size;
|
||||
|
||||
if (!hcd->self.controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM))
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!hcd->self.controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
|
|
@ -93,6 +94,9 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA))
|
||||
return;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
struct dma_pool *pool = hcd->pool[i];
|
||||
|
||||
|
|
@ -119,8 +123,9 @@ void *hcd_buffer_alloc(
|
|||
int i;
|
||||
|
||||
/* some USB hosts just use PIO */
|
||||
if (!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)) {
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
|
||||
*dma = ~(dma_addr_t) 0;
|
||||
return kmalloc(size, mem_flags);
|
||||
}
|
||||
|
|
@ -145,8 +150,9 @@ void hcd_buffer_free(
|
|||
if (!addr)
|
||||
return;
|
||||
|
||||
if (!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)) {
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
|
||||
kfree(addr);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,27 @@ static int find_next_descriptor(unsigned char *buffer, int size,
|
|||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
|
||||
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
unsigned char *buffer, int size)
|
||||
{
|
||||
struct usb_ssp_isoc_ep_comp_descriptor *desc;
|
||||
|
||||
/*
|
||||
* The SuperSpeedPlus Isoc endpoint companion descriptor immediately
|
||||
* follows the SuperSpeed Endpoint Companion descriptor
|
||||
*/
|
||||
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
|
||||
if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
|
||||
size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
|
||||
dev_warn(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
|
||||
"for config %d interface %d altsetting %d ep %d.\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
return;
|
||||
}
|
||||
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
|
||||
}
|
||||
|
||||
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||||
int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
unsigned char *buffer, int size)
|
||||
|
|
@ -54,6 +75,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
* be the first thing immediately following the endpoint descriptor.
|
||||
*/
|
||||
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
|
||||
buffer += desc->bLength;
|
||||
size -= desc->bLength;
|
||||
|
||||
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP ||
|
||||
size < USB_DT_SS_EP_COMP_SIZE) {
|
||||
dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
|
||||
|
|
@ -112,6 +136,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bmAttributes = 16;
|
||||
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
!USB_SS_SSP_ISOC_COMP(desc->bmAttributes) &&
|
||||
USB_SS_MULT(desc->bmAttributes) > 3) {
|
||||
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
|
|
@ -121,6 +146,12 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
ep->ss_ep_comp.bmAttributes = 2;
|
||||
}
|
||||
|
||||
/* Parse a possible SuperSpeedPlus isoc ep companion descriptor */
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
USB_SS_SSP_ISOC_COMP(desc->bmAttributes))
|
||||
usb_parse_ssp_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
|
||||
ep, buffer, size);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
max_tx = (desc->bMaxBurst + 1) *
|
||||
(USB_SS_MULT(desc->bmAttributes)) *
|
||||
|
|
@ -191,6 +222,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
|||
if (usb_endpoint_xfer_int(d)) {
|
||||
i = 1;
|
||||
switch (to_usb_device(ddev)->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_HIGH:
|
||||
/* Many device manufacturers are using full-speed
|
||||
|
|
@ -274,7 +306,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
|||
}
|
||||
|
||||
/* Parse a possible SuperSpeed endpoint companion descriptor */
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_SUPER)
|
||||
if (to_usb_device(ddev)->speed >= USB_SPEED_SUPER)
|
||||
usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
inum, asnum, endpoint, buffer, size);
|
||||
|
||||
|
|
@ -862,6 +894,9 @@ int usb_get_bos_descriptor(struct usb_device *dev)
|
|||
dev->bos->ss_id =
|
||||
(struct usb_ss_container_id_descriptor *)buffer;
|
||||
break;
|
||||
case USB_PTM_CAP_TYPE:
|
||||
dev->bos->ptm_cap =
|
||||
(struct usb_ptm_cap_descriptor *)buffer;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,13 +110,6 @@ static const char format_endpt[] =
|
|||
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
|
||||
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
|
||||
|
||||
|
||||
/*
|
||||
* Need access to the driver and USB bus lists.
|
||||
* extern struct list_head usb_bus_list;
|
||||
* However, these will come from functions that return ptrs to each of them.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wait for an connect/disconnect event to happen. We initialize
|
||||
* the event counter with an odd number, and each event will increment
|
||||
|
|
@ -221,7 +214,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
|||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
type = "Int.";
|
||||
if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER)
|
||||
if (speed == USB_SPEED_HIGH || speed >= USB_SPEED_SUPER)
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
else
|
||||
interval = desc->bInterval;
|
||||
|
|
@ -230,7 +223,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
|||
return start;
|
||||
}
|
||||
interval *= (speed == USB_SPEED_HIGH ||
|
||||
speed == USB_SPEED_SUPER) ? 125 : 1000;
|
||||
speed >= USB_SPEED_SUPER) ? 125 : 1000;
|
||||
if (interval % 1000)
|
||||
unit = 'u';
|
||||
else {
|
||||
|
|
@ -322,7 +315,7 @@ static char *usb_dump_config_descriptor(char *start, char *end,
|
|||
|
||||
if (start > end)
|
||||
return start;
|
||||
if (speed == USB_SPEED_SUPER)
|
||||
if (speed >= USB_SPEED_SUPER)
|
||||
mul = 8;
|
||||
else
|
||||
mul = 2;
|
||||
|
|
@ -534,6 +527,8 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
|||
speed = "480"; break;
|
||||
case USB_SPEED_SUPER:
|
||||
speed = "5000"; break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
speed = "10000"; break;
|
||||
default:
|
||||
speed = "??";
|
||||
}
|
||||
|
|
@ -553,7 +548,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
|||
|
||||
/* super/high speed reserves 80%, full/low reserves 90% */
|
||||
if (usbdev->speed == USB_SPEED_HIGH ||
|
||||
usbdev->speed == USB_SPEED_SUPER)
|
||||
usbdev->speed >= USB_SPEED_SUPER)
|
||||
max = 800;
|
||||
else
|
||||
max = FRAME_TIME_MAX_USECS_ALLOC;
|
||||
|
|
@ -616,6 +611,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
|
|||
struct usb_bus *bus;
|
||||
ssize_t ret, total_written = 0;
|
||||
loff_t skip_bytes = *ppos;
|
||||
int id;
|
||||
|
||||
if (*ppos < 0)
|
||||
return -EINVAL;
|
||||
|
|
@ -624,9 +620,9 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
|
|||
if (!access_ok(VERIFY_WRITE, buf, nbytes))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
/* print devices for all busses */
|
||||
list_for_each_entry(bus, &usb_bus_list, bus_list) {
|
||||
idr_for_each_entry(&usb_bus_idr, bus, id) {
|
||||
/* recurse through all children of the root hub */
|
||||
if (!bus_to_hcd(bus)->rh_registered)
|
||||
continue;
|
||||
|
|
@ -635,12 +631,12 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
|
|||
bus->root_hub, bus, 0, 0, 0);
|
||||
usb_unlock_device(bus->root_hub);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
return ret;
|
||||
}
|
||||
total_written += ret;
|
||||
}
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
return total_written;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include <linux/user_namespace.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ struct usb_dev_state {
|
|||
spinlock_t lock; /* protects the async urb lists */
|
||||
struct list_head async_pending;
|
||||
struct list_head async_completed;
|
||||
struct list_head memory_list;
|
||||
wait_queue_head_t wait; /* wake up if a request completed */
|
||||
unsigned int discsignr;
|
||||
struct pid *disc_pid;
|
||||
|
|
@ -77,6 +79,19 @@ struct usb_dev_state {
|
|||
unsigned long ifclaimed;
|
||||
u32 secid;
|
||||
u32 disabled_bulk_eps;
|
||||
bool privileges_dropped;
|
||||
unsigned long interface_allowed_mask;
|
||||
};
|
||||
|
||||
struct usb_memory {
|
||||
struct list_head memlist;
|
||||
int vma_use_count;
|
||||
int urb_use_count;
|
||||
u32 size;
|
||||
void *mem;
|
||||
dma_addr_t dma_handle;
|
||||
unsigned long vm_start;
|
||||
struct usb_dev_state *ps;
|
||||
};
|
||||
|
||||
struct async {
|
||||
|
|
@ -89,6 +104,7 @@ struct async {
|
|||
void __user *userbuffer;
|
||||
void __user *userurb;
|
||||
struct urb *urb;
|
||||
struct usb_memory *usbm;
|
||||
unsigned int mem_usage;
|
||||
int status;
|
||||
u32 secid;
|
||||
|
|
@ -162,6 +178,111 @@ static int connected(struct usb_dev_state *ps)
|
|||
ps->dev->state != USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
|
||||
{
|
||||
struct usb_dev_state *ps = usbm->ps;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
--*count;
|
||||
if (usbm->urb_use_count == 0 && usbm->vma_use_count == 0) {
|
||||
list_del(&usbm->memlist);
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
||||
usb_free_coherent(ps->dev, usbm->size, usbm->mem,
|
||||
usbm->dma_handle);
|
||||
usbfs_decrease_memory_usage(
|
||||
usbm->size + sizeof(struct usb_memory));
|
||||
kfree(usbm);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void usbdev_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct usb_memory *usbm = vma->vm_private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&usbm->ps->lock, flags);
|
||||
++usbm->vma_use_count;
|
||||
spin_unlock_irqrestore(&usbm->ps->lock, flags);
|
||||
}
|
||||
|
||||
static void usbdev_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct usb_memory *usbm = vma->vm_private_data;
|
||||
|
||||
dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
|
||||
}
|
||||
|
||||
struct vm_operations_struct usbdev_vm_ops = {
|
||||
.open = usbdev_vm_open,
|
||||
.close = usbdev_vm_close
|
||||
};
|
||||
|
||||
static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct usb_memory *usbm = NULL;
|
||||
struct usb_dev_state *ps = file->private_data;
|
||||
size_t size = vma->vm_end - vma->vm_start;
|
||||
void *mem;
|
||||
unsigned long flags;
|
||||
dma_addr_t dma_handle;
|
||||
int ret;
|
||||
|
||||
ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory));
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL);
|
||||
if (!usbm) {
|
||||
ret = -ENOMEM;
|
||||
goto error_decrease_mem;
|
||||
}
|
||||
|
||||
mem = usb_alloc_coherent(ps->dev, size, GFP_USER, &dma_handle);
|
||||
if (!mem) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_usbm;
|
||||
}
|
||||
|
||||
memset(mem, 0, size);
|
||||
|
||||
usbm->mem = mem;
|
||||
usbm->dma_handle = dma_handle;
|
||||
usbm->size = size;
|
||||
usbm->ps = ps;
|
||||
usbm->vm_start = vma->vm_start;
|
||||
usbm->vma_use_count = 1;
|
||||
INIT_LIST_HEAD(&usbm->memlist);
|
||||
|
||||
if (remap_pfn_range(vma, vma->vm_start,
|
||||
virt_to_phys(usbm->mem) >> PAGE_SHIFT,
|
||||
size, vma->vm_page_prot) < 0) {
|
||||
dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
vma->vm_flags |= VM_IO;
|
||||
vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);
|
||||
vma->vm_ops = &usbdev_vm_ops;
|
||||
vma->vm_private_data = usbm;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
list_add_tail(&usbm->memlist, &ps->memory_list);
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_usbm:
|
||||
kfree(usbm);
|
||||
error_decrease_mem:
|
||||
usbfs_decrease_memory_usage(size + sizeof(struct usb_memory));
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,
|
||||
loff_t *ppos)
|
||||
{
|
||||
|
|
@ -278,8 +399,13 @@ static void free_async(struct async *as)
|
|||
if (sg_page(&as->urb->sg[i]))
|
||||
kfree(sg_virt(&as->urb->sg[i]));
|
||||
}
|
||||
|
||||
kfree(as->urb->sg);
|
||||
kfree(as->urb->transfer_buffer);
|
||||
if (as->usbm == NULL)
|
||||
kfree(as->urb->transfer_buffer);
|
||||
else
|
||||
dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count);
|
||||
|
||||
kfree(as->urb->setup_packet);
|
||||
usb_free_urb(as->urb);
|
||||
usbfs_decrease_memory_usage(as->mem_usage);
|
||||
|
|
@ -624,6 +750,10 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
|
|||
if (test_bit(ifnum, &ps->ifclaimed))
|
||||
return 0;
|
||||
|
||||
if (ps->privileges_dropped &&
|
||||
!test_bit(ifnum, &ps->interface_allowed_mask))
|
||||
return -EACCES;
|
||||
|
||||
intf = usb_ifnum_to_if(dev, ifnum);
|
||||
if (!intf)
|
||||
err = -ENOENT;
|
||||
|
|
@ -848,7 +978,7 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
|
|||
(void *) (unsigned long) devt, match_devt);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
return container_of(dev, struct usb_device, dev);
|
||||
return to_usb_device(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -861,7 +991,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
|
||||
ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
|
||||
if (!ps)
|
||||
goto out_free_ps;
|
||||
|
||||
|
|
@ -889,16 +1019,15 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|||
|
||||
ps->dev = dev;
|
||||
ps->file = file;
|
||||
ps->interface_allowed_mask = 0xFFFFFFFF; /* 32 bits */
|
||||
spin_lock_init(&ps->lock);
|
||||
INIT_LIST_HEAD(&ps->list);
|
||||
INIT_LIST_HEAD(&ps->async_pending);
|
||||
INIT_LIST_HEAD(&ps->async_completed);
|
||||
INIT_LIST_HEAD(&ps->memory_list);
|
||||
init_waitqueue_head(&ps->wait);
|
||||
ps->discsignr = 0;
|
||||
ps->disc_pid = get_pid(task_pid(current));
|
||||
ps->cred = get_current_cred();
|
||||
ps->disccontext = NULL;
|
||||
ps->ifclaimed = 0;
|
||||
security_task_getsecid(current, &ps->secid);
|
||||
smp_wmb();
|
||||
list_add_tail(&ps->list, &dev->filelist);
|
||||
|
|
@ -945,6 +1074,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
|
|||
free_async(as);
|
||||
as = async_getcompleted(ps);
|
||||
}
|
||||
|
||||
kfree(ps);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1198,6 +1328,28 @@ static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
|
|||
|
||||
static int proc_resetdevice(struct usb_dev_state *ps)
|
||||
{
|
||||
struct usb_host_config *actconfig = ps->dev->actconfig;
|
||||
struct usb_interface *interface;
|
||||
int i, number;
|
||||
|
||||
/* Don't allow a device reset if the process has dropped the
|
||||
* privilege to do such things and any of the interfaces are
|
||||
* currently claimed.
|
||||
*/
|
||||
if (ps->privileges_dropped && actconfig) {
|
||||
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
|
||||
interface = actconfig->interface[i];
|
||||
number = interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
if (usb_interface_claimed(interface) &&
|
||||
!test_bit(number, &ps->ifclaimed)) {
|
||||
dev_warn(&ps->dev->dev,
|
||||
"usbfs: interface %d claimed by %s while '%s' resets device\n",
|
||||
number, interface->dev.driver->name, current->comm);
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return usb_reset_device(ps->dev);
|
||||
}
|
||||
|
||||
|
|
@ -1266,6 +1418,31 @@ static int proc_setconfig(struct usb_dev_state *ps, void __user *arg)
|
|||
return status;
|
||||
}
|
||||
|
||||
static struct usb_memory *
|
||||
find_memory_area(struct usb_dev_state *ps, const struct usbdevfs_urb *uurb)
|
||||
{
|
||||
struct usb_memory *usbm = NULL, *iter;
|
||||
unsigned long flags;
|
||||
unsigned long uurb_start = (unsigned long)uurb->buffer;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
list_for_each_entry(iter, &ps->memory_list, memlist) {
|
||||
if (uurb_start >= iter->vm_start &&
|
||||
uurb_start < iter->vm_start + iter->size) {
|
||||
if (uurb->buffer_length > iter->vm_start + iter->size -
|
||||
uurb_start) {
|
||||
usbm = ERR_PTR(-EINVAL);
|
||||
} else {
|
||||
usbm = iter;
|
||||
usbm->urb_use_count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
return usbm;
|
||||
}
|
||||
|
||||
static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
|
||||
void __user *arg)
|
||||
|
|
@ -1378,11 +1555,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
number_of_packets = uurb->number_of_packets;
|
||||
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
|
||||
number_of_packets;
|
||||
isopkt = kmalloc(isofrmlen, GFP_KERNEL);
|
||||
if (!isopkt)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
|
||||
ret = -EFAULT;
|
||||
isopkt = memdup_user(iso_frame_desc, isofrmlen);
|
||||
if (IS_ERR(isopkt)) {
|
||||
ret = PTR_ERR(isopkt);
|
||||
isopkt = NULL;
|
||||
goto error;
|
||||
}
|
||||
for (totlen = u = 0; u < number_of_packets; u++) {
|
||||
|
|
@ -1422,6 +1598,19 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
goto error;
|
||||
}
|
||||
|
||||
as->usbm = find_memory_area(ps, uurb);
|
||||
if (IS_ERR(as->usbm)) {
|
||||
ret = PTR_ERR(as->usbm);
|
||||
as->usbm = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* do not use SG buffers when memory mapped segments
|
||||
* are in use
|
||||
*/
|
||||
if (as->usbm)
|
||||
num_sgs = 0;
|
||||
|
||||
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
|
||||
num_sgs * sizeof(struct scatterlist);
|
||||
ret = usbfs_increase_memory_usage(u);
|
||||
|
|
@ -1459,29 +1648,35 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
totlen -= u;
|
||||
}
|
||||
} else if (uurb->buffer_length > 0) {
|
||||
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
|
||||
GFP_KERNEL);
|
||||
if (!as->urb->transfer_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
if (as->usbm) {
|
||||
unsigned long uurb_start = (unsigned long)uurb->buffer;
|
||||
|
||||
if (!is_in) {
|
||||
if (copy_from_user(as->urb->transfer_buffer,
|
||||
uurb->buffer,
|
||||
uurb->buffer_length)) {
|
||||
ret = -EFAULT;
|
||||
as->urb->transfer_buffer = as->usbm->mem +
|
||||
(uurb_start - as->usbm->vm_start);
|
||||
} else {
|
||||
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
|
||||
GFP_KERNEL);
|
||||
if (!as->urb->transfer_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
|
||||
/*
|
||||
* Isochronous input data may end up being
|
||||
* discontiguous if some of the packets are short.
|
||||
* Clear the buffer so that the gaps don't leak
|
||||
* kernel data to userspace.
|
||||
*/
|
||||
memset(as->urb->transfer_buffer, 0,
|
||||
uurb->buffer_length);
|
||||
if (!is_in) {
|
||||
if (copy_from_user(as->urb->transfer_buffer,
|
||||
uurb->buffer,
|
||||
uurb->buffer_length)) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
|
||||
/*
|
||||
* Isochronous input data may end up being
|
||||
* discontiguous if some of the packets are
|
||||
* short. Clear the buffer so that the gaps
|
||||
* don't leak kernel data to userspace.
|
||||
*/
|
||||
memset(as->urb->transfer_buffer, 0,
|
||||
uurb->buffer_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
as->urb->dev = ps->dev;
|
||||
|
|
@ -1528,10 +1723,14 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
isopkt = NULL;
|
||||
as->ps = ps;
|
||||
as->userurb = arg;
|
||||
if (is_in && uurb->buffer_length > 0)
|
||||
if (as->usbm) {
|
||||
unsigned long uurb_start = (unsigned long)uurb->buffer;
|
||||
|
||||
as->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
as->urb->transfer_dma = as->usbm->dma_handle +
|
||||
(uurb_start - as->usbm->vm_start);
|
||||
} else if (is_in && uurb->buffer_length > 0)
|
||||
as->userbuffer = uurb->buffer;
|
||||
else
|
||||
as->userbuffer = NULL;
|
||||
as->signr = uurb->signr;
|
||||
as->ifnum = ifnum;
|
||||
as->pid = get_pid(task_pid(current));
|
||||
|
|
@ -1587,6 +1786,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
return 0;
|
||||
|
||||
error:
|
||||
if (as && as->usbm)
|
||||
dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count);
|
||||
kfree(isopkt);
|
||||
kfree(dr);
|
||||
if (as)
|
||||
|
|
@ -1903,7 +2104,7 @@ static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg)
|
|||
ret = releaseintf(ps, ifnum);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
destroy_async_on_interface (ps, ifnum);
|
||||
destroy_async_on_interface(ps, ifnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1915,6 +2116,9 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
|
|||
struct usb_interface *intf = NULL;
|
||||
struct usb_driver *driver = NULL;
|
||||
|
||||
if (ps->privileges_dropped)
|
||||
return -EACCES;
|
||||
|
||||
/* alloc buffer */
|
||||
size = _IOC_SIZE(ctl->ioctl_code);
|
||||
if (size > 0) {
|
||||
|
|
@ -2040,7 +2244,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
|
|||
__u32 caps;
|
||||
|
||||
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
|
||||
USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
|
||||
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
|
||||
USBDEVFS_CAP_DROP_PRIVILEGES;
|
||||
if (!ps->dev->bus->no_stop_on_short)
|
||||
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
|
||||
if (ps->dev->bus->sg_tablesize)
|
||||
|
|
@ -2067,6 +2272,9 @@ static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
|
|||
if (intf->dev.driver) {
|
||||
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
if (ps->privileges_dropped)
|
||||
return -EACCES;
|
||||
|
||||
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
|
||||
strncmp(dc.driver, intf->dev.driver->name,
|
||||
sizeof(dc.driver)) != 0)
|
||||
|
|
@ -2123,6 +2331,23 @@ static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
|
|||
return r;
|
||||
}
|
||||
|
||||
static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
if (copy_from_user(&data, arg, sizeof(data)))
|
||||
return -EFAULT;
|
||||
|
||||
/* This is an one way operation. Once privileges are
|
||||
* dropped, you cannot regain them. You may however reissue
|
||||
* this ioctl to shrink the allowed interfaces mask.
|
||||
*/
|
||||
ps->interface_allowed_mask &= data;
|
||||
ps->privileges_dropped = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: All requests here that have interface numbers as parameters
|
||||
* are assuming that somehow the configuration has been prevented from
|
||||
|
|
@ -2311,6 +2536,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
case USBDEVFS_FREE_STREAMS:
|
||||
ret = proc_free_streams(ps, p);
|
||||
break;
|
||||
case USBDEVFS_DROP_PRIVILEGES:
|
||||
ret = proc_drop_privileges(ps, p);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
|
|
@ -2366,6 +2594,7 @@ const struct file_operations usbdev_file_operations = {
|
|||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = usbdev_compat_ioctl,
|
||||
#endif
|
||||
.mmap = usbdev_mmap,
|
||||
.open = usbdev_open,
|
||||
.release = usbdev_release,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
|
@ -155,7 +156,6 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
int minor_base = class_driver->minor_base;
|
||||
int minor;
|
||||
char name[20];
|
||||
char *temp;
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
/*
|
||||
|
|
@ -192,14 +192,9 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
|
||||
/* create a usb class device for this usb interface */
|
||||
snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
|
||||
temp = strrchr(name, '/');
|
||||
if (temp && (temp[1] != '\0'))
|
||||
++temp;
|
||||
else
|
||||
temp = name;
|
||||
intf->usb_dev = device_create(usb_class->class, &intf->dev,
|
||||
MKDEV(USB_MAJOR, minor), class_driver,
|
||||
"%s", temp);
|
||||
"%s", kbasename(name));
|
||||
if (IS_ERR(intf->usb_dev)) {
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[minor] = NULL;
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
* The xHCI driver has its own irq management
|
||||
* make sure irq setup is not touched for xhci in generic hcd code
|
||||
*/
|
||||
if ((driver->flags & HCD_MASK) != HCD_USB3) {
|
||||
if ((driver->flags & HCD_MASK) < HCD_USB3) {
|
||||
if (!dev->irq) {
|
||||
dev_err(&dev->dev,
|
||||
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
|
||||
|
|
|
|||
|
|
@ -90,16 +90,15 @@ unsigned long usb_hcds_loaded;
|
|||
EXPORT_SYMBOL_GPL(usb_hcds_loaded);
|
||||
|
||||
/* host controllers we manage */
|
||||
LIST_HEAD (usb_bus_list);
|
||||
EXPORT_SYMBOL_GPL (usb_bus_list);
|
||||
DEFINE_IDR (usb_bus_idr);
|
||||
EXPORT_SYMBOL_GPL (usb_bus_idr);
|
||||
|
||||
/* used when allocating bus numbers */
|
||||
#define USB_MAXBUS 64
|
||||
static DECLARE_BITMAP(busmap, USB_MAXBUS);
|
||||
|
||||
/* used when updating list of hcds */
|
||||
DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
|
||||
EXPORT_SYMBOL_GPL (usb_bus_list_lock);
|
||||
DEFINE_MUTEX(usb_bus_idr_lock); /* exported only for usbfs */
|
||||
EXPORT_SYMBOL_GPL (usb_bus_idr_lock);
|
||||
|
||||
/* used for controlling access to virtual root hubs */
|
||||
static DEFINE_SPINLOCK(hcd_root_hub_lock);
|
||||
|
|
@ -128,6 +127,27 @@ static inline int is_root_hub(struct usb_device *udev)
|
|||
#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
|
||||
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
|
||||
|
||||
/* usb 3.1 root hub device descriptor */
|
||||
static const u8 usb31_rh_dev_descriptor[18] = {
|
||||
0x12, /* __u8 bLength; */
|
||||
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
||||
0x10, 0x03, /* __le16 bcdUSB; v3.1 */
|
||||
|
||||
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 bDeviceSubClass; */
|
||||
0x03, /* __u8 bDeviceProtocol; USB 3 hub */
|
||||
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
|
||||
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
||||
0x03, 0x00, /* __le16 idProduct; device 0x0003 */
|
||||
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
||||
|
||||
0x03, /* __u8 iManufacturer; */
|
||||
0x02, /* __u8 iProduct; */
|
||||
0x01, /* __u8 iSerialNumber; */
|
||||
0x01 /* __u8 bNumConfigurations; */
|
||||
};
|
||||
|
||||
/* usb 3.0 root hub device descriptor */
|
||||
static const u8 usb3_rh_dev_descriptor[18] = {
|
||||
0x12, /* __u8 bLength; */
|
||||
|
|
@ -557,6 +577,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
|
|||
case USB_DT_DEVICE << 8:
|
||||
switch (hcd->speed) {
|
||||
case HCD_USB31:
|
||||
bufp = usb31_rh_dev_descriptor;
|
||||
break;
|
||||
case HCD_USB3:
|
||||
bufp = usb3_rh_dev_descriptor;
|
||||
break;
|
||||
|
|
@ -645,9 +667,15 @@ nongeneric:
|
|||
/* non-generic request */
|
||||
switch (typeReq) {
|
||||
case GetHubStatus:
|
||||
case GetPortStatus:
|
||||
len = 4;
|
||||
break;
|
||||
case GetPortStatus:
|
||||
if (wValue == HUB_PORT_STATUS)
|
||||
len = 4;
|
||||
else
|
||||
/* other port status types return 8 bytes */
|
||||
len = 8;
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
len = sizeof (struct usb_hub_descriptor);
|
||||
break;
|
||||
|
|
@ -967,8 +995,6 @@ static void usb_bus_init (struct usb_bus *bus)
|
|||
bus->bandwidth_int_reqs = 0;
|
||||
bus->bandwidth_isoc_reqs = 0;
|
||||
mutex_init(&bus->usb_address0_mutex);
|
||||
|
||||
INIT_LIST_HEAD (&bus->bus_list);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -988,18 +1014,14 @@ static int usb_register_bus(struct usb_bus *bus)
|
|||
int result = -E2BIG;
|
||||
int busnum;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
|
||||
if (busnum >= USB_MAXBUS) {
|
||||
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
busnum = idr_alloc(&usb_bus_idr, bus, 1, USB_MAXBUS, GFP_KERNEL);
|
||||
if (busnum < 0) {
|
||||
pr_err("%s: failed to get bus number\n", usbcore_name);
|
||||
goto error_find_busnum;
|
||||
}
|
||||
set_bit(busnum, busmap);
|
||||
bus->busnum = busnum;
|
||||
|
||||
/* Add it to the local list of buses */
|
||||
list_add (&bus->bus_list, &usb_bus_list);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
usb_notify_add_bus(bus);
|
||||
|
||||
|
|
@ -1008,7 +1030,7 @@ static int usb_register_bus(struct usb_bus *bus)
|
|||
return 0;
|
||||
|
||||
error_find_busnum:
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1029,13 +1051,11 @@ static void usb_deregister_bus (struct usb_bus *bus)
|
|||
* controller code, as well as having it call this when cleaning
|
||||
* itself up
|
||||
*/
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
list_del (&bus->bus_list);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
idr_remove(&usb_bus_idr, bus->busnum);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
usb_notify_remove_bus(bus);
|
||||
|
||||
clear_bit(bus->busnum, busmap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1063,12 +1083,12 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
set_bit (devnum, usb_dev->bus->devmap.devicemap);
|
||||
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
|
||||
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
|
||||
if (retval != sizeof usb_dev->descriptor) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
return (retval < 0) ? retval : -EMSGSIZE;
|
||||
|
|
@ -1078,8 +1098,8 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
retval = usb_get_bos_descriptor(usb_dev);
|
||||
if (!retval) {
|
||||
usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
|
||||
} else if (usb_dev->speed == USB_SPEED_SUPER) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
} else if (usb_dev->speed >= USB_SPEED_SUPER) {
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
return retval;
|
||||
|
|
@ -1099,7 +1119,7 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
if (HCD_DEAD(hcd))
|
||||
usb_hc_died (hcd); /* This time clean up */
|
||||
}
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1408,7 +1428,8 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
|
|||
|
||||
void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
if (urb->transfer_flags & URB_SETUP_MAP_SINGLE)
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_SETUP_MAP_SINGLE))
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
|
|
@ -1440,17 +1461,20 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
|||
usb_hcd_unmap_urb_setup_for_dma(hcd, urb);
|
||||
|
||||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (urb->transfer_flags & URB_DMA_MAP_SG)
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_DMA_MAP_SG))
|
||||
dma_unmap_sg(hcd->self.controller,
|
||||
urb->sg,
|
||||
urb->num_sgs,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_DMA_MAP_PAGE)
|
||||
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_DMA_MAP_PAGE))
|
||||
dma_unmap_page(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_DMA_MAP_SINGLE)
|
||||
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_DMA_MAP_SINGLE))
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
|
|
@ -1492,7 +1516,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
|
||||
if (hcd->self.uses_pio_for_control)
|
||||
return ret;
|
||||
if (hcd->self.uses_dma) {
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
|
||||
urb->setup_dma = dma_map_single(
|
||||
hcd->self.controller,
|
||||
urb->setup_packet,
|
||||
|
|
@ -1518,7 +1542,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (urb->transfer_buffer_length != 0
|
||||
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
|
||||
if (hcd->self.uses_dma) {
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
|
||||
if (urb->num_sgs) {
|
||||
int n;
|
||||
|
||||
|
|
@ -2112,7 +2136,7 @@ int usb_alloc_streams(struct usb_interface *interface,
|
|||
hcd = bus_to_hcd(dev->bus);
|
||||
if (!hcd->driver->alloc_streams || !hcd->driver->free_streams)
|
||||
return -EINVAL;
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
if (dev->speed < USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
if (dev->state < USB_STATE_CONFIGURED)
|
||||
return -ENODEV;
|
||||
|
|
@ -2160,7 +2184,7 @@ int usb_free_streams(struct usb_interface *interface,
|
|||
|
||||
dev = interface_to_usbdev(interface);
|
||||
hcd = bus_to_hcd(dev->bus);
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
if (dev->speed < USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
/* Double-free is not allowed */
|
||||
|
|
@ -2208,7 +2232,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
|
|||
|
||||
int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
|
|
@ -2257,7 +2281,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
|||
|
||||
int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
|
|
@ -2371,7 +2395,7 @@ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
|
|||
* boards with root hubs hooked up to internal devices (instead of
|
||||
* just the OTG port) may need more attention to resetting...
|
||||
*/
|
||||
hcd = container_of (bus, struct usb_hcd, self);
|
||||
hcd = bus_to_hcd(bus);
|
||||
if (port_num && hcd->driver->start_port_reset)
|
||||
status = hcd->driver->start_port_reset(hcd, port_num);
|
||||
|
||||
|
|
@ -2778,9 +2802,11 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|||
rhdev->speed = USB_SPEED_WIRELESS;
|
||||
break;
|
||||
case HCD_USB3:
|
||||
case HCD_USB31:
|
||||
rhdev->speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
case HCD_USB31:
|
||||
rhdev->speed = USB_SPEED_SUPER_PLUS;
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
goto err_set_rh_speed;
|
||||
|
|
@ -2863,9 +2889,9 @@ error_create_attr_group:
|
|||
#ifdef CONFIG_PM
|
||||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
err_register_root_hub:
|
||||
hcd->rh_pollable = 0;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
|
@ -2932,9 +2958,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
/*
|
||||
* tasklet_kill() isn't needed here because:
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ static void hub_event(struct work_struct *work);
|
|||
DEFINE_MUTEX(usb_port_peer_mutex);
|
||||
|
||||
/* cycle leds on hubs that aren't blinking for attention */
|
||||
static bool blinkenlights = 0;
|
||||
static bool blinkenlights;
|
||||
module_param(blinkenlights, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(blinkenlights, "true to cycle leds on hubs");
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ MODULE_PARM_DESC(initial_descriptor_timeout,
|
|||
* otherwise the new scheme is used. If that fails and "use_both_schemes"
|
||||
* is set, then the driver will make another attempt, using the other scheme.
|
||||
*/
|
||||
static bool old_scheme_first = 0;
|
||||
static bool old_scheme_first;
|
||||
module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(old_scheme_first,
|
||||
"start with the old device initialization scheme");
|
||||
|
|
@ -298,7 +298,7 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
|
|||
unsigned int hub_u1_del;
|
||||
unsigned int hub_u2_del;
|
||||
|
||||
if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER)
|
||||
if (!udev->lpm_capable || udev->speed < USB_SPEED_SUPER)
|
||||
return;
|
||||
|
||||
hub = usb_hub_to_struct_hub(udev->parent);
|
||||
|
|
@ -537,29 +537,34 @@ static int get_hub_status(struct usb_device *hdev,
|
|||
|
||||
/*
|
||||
* USB 2.0 spec Section 11.24.2.7
|
||||
* USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6
|
||||
*/
|
||||
static int get_port_status(struct usb_device *hdev, int port1,
|
||||
struct usb_port_status *data)
|
||||
void *data, u16 value, u16 length)
|
||||
{
|
||||
int i, status = -ETIMEDOUT;
|
||||
|
||||
for (i = 0; i < USB_STS_RETRIES &&
|
||||
(status == -ETIMEDOUT || status == -EPIPE); i++) {
|
||||
status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
|
||||
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
|
||||
data, sizeof(*data), USB_STS_TIMEOUT);
|
||||
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
|
||||
port1, data, length, USB_STS_TIMEOUT);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hub_port_status(struct usb_hub *hub, int port1,
|
||||
u16 *status, u16 *change)
|
||||
static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
|
||||
u16 *status, u16 *change, u32 *ext_status)
|
||||
{
|
||||
int ret;
|
||||
int len = 4;
|
||||
|
||||
if (type != HUB_PORT_STATUS)
|
||||
len = 8;
|
||||
|
||||
mutex_lock(&hub->status_mutex);
|
||||
ret = get_port_status(hub->hdev, port1, &hub->status->port);
|
||||
if (ret < 4) {
|
||||
ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
|
||||
if (ret < len) {
|
||||
if (ret != -ENODEV)
|
||||
dev_err(hub->intfdev,
|
||||
"%s failed (err = %d)\n", __func__, ret);
|
||||
|
|
@ -568,13 +573,22 @@ static int hub_port_status(struct usb_hub *hub, int port1,
|
|||
} else {
|
||||
*status = le16_to_cpu(hub->status->port.wPortStatus);
|
||||
*change = le16_to_cpu(hub->status->port.wPortChange);
|
||||
|
||||
if (type != HUB_PORT_STATUS && ext_status)
|
||||
*ext_status = le32_to_cpu(
|
||||
hub->status->port.dwExtPortStatus);
|
||||
ret = 0;
|
||||
}
|
||||
mutex_unlock(&hub->status_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hub_port_status(struct usb_hub *hub, int port1,
|
||||
u16 *status, u16 *change)
|
||||
{
|
||||
return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
|
||||
status, change, NULL);
|
||||
}
|
||||
|
||||
static void kick_hub_wq(struct usb_hub *hub)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
|
|
@ -2131,7 +2145,7 @@ static void hub_disconnect_children(struct usb_device *udev)
|
|||
* Something got disconnected. Get rid of it and all of its children.
|
||||
*
|
||||
* If *pdev is a normal device then the parent hub must already be locked.
|
||||
* If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
|
||||
* If *pdev is a root hub then the caller must hold the usb_bus_idr_lock,
|
||||
* which protects the set of root hubs as well as the list of buses.
|
||||
*
|
||||
* Only hub drivers (including virtual root hub drivers for host
|
||||
|
|
@ -2429,7 +2443,7 @@ static void set_usb_port_removable(struct usb_device *udev)
|
|||
* enumerated. The device descriptor is available, but not descriptors
|
||||
* for any device configuration. The caller must have locked either
|
||||
* the parent hub (if udev is a normal device) or else the
|
||||
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
|
||||
* usb_bus_idr_lock (if udev is a root hub). The parent's pointer to
|
||||
* udev has already been installed, but udev is not yet visible through
|
||||
* sysfs or other filesystem code.
|
||||
*
|
||||
|
|
@ -2612,6 +2626,32 @@ out_authorized:
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1 if port speed is SuperSpeedPlus, 0 otherwise
|
||||
* check it from the link protocol field of the current speed ID attribute.
|
||||
* current speed ID is got from ext port status request. Sublink speed attribute
|
||||
* table is returned with the hub BOS SSP device capability descriptor
|
||||
*/
|
||||
static int port_speed_is_ssp(struct usb_device *hdev, int speed_id)
|
||||
{
|
||||
int ssa_count;
|
||||
u32 ss_attr;
|
||||
int i;
|
||||
struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap;
|
||||
|
||||
if (!ssp_cap)
|
||||
return 0;
|
||||
|
||||
ssa_count = le32_to_cpu(ssp_cap->bmAttributes) &
|
||||
USB_SSP_SUBLINK_SPEED_ATTRIBS;
|
||||
|
||||
for (i = 0; i <= ssa_count; i++) {
|
||||
ss_attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]);
|
||||
if (speed_id == (ss_attr & USB_SSP_SUBLINK_SPEED_SSID))
|
||||
return !!(ss_attr & USB_SSP_SUBLINK_SPEED_LP);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
|
||||
static unsigned hub_is_wusb(struct usb_hub *hub)
|
||||
|
|
@ -2619,7 +2659,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
|
|||
struct usb_hcd *hcd;
|
||||
if (hub->hdev->parent != NULL) /* not a root hub? */
|
||||
return 0;
|
||||
hcd = container_of(hub->hdev->bus, struct usb_hcd, self);
|
||||
hcd = bus_to_hcd(hub->hdev->bus);
|
||||
return hcd->wireless;
|
||||
}
|
||||
|
||||
|
|
@ -2645,7 +2685,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
|
|||
*/
|
||||
static bool use_new_scheme(struct usb_device *udev, int retry)
|
||||
{
|
||||
if (udev->speed == USB_SPEED_SUPER)
|
||||
if (udev->speed >= USB_SPEED_SUPER)
|
||||
return false;
|
||||
|
||||
return USE_NEW_SCHEME(retry);
|
||||
|
|
@ -2676,6 +2716,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|||
int delay_time, ret;
|
||||
u16 portstatus;
|
||||
u16 portchange;
|
||||
u32 ext_portstatus = 0;
|
||||
|
||||
for (delay_time = 0;
|
||||
delay_time < HUB_RESET_TIMEOUT;
|
||||
|
|
@ -2684,7 +2725,14 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|||
msleep(delay);
|
||||
|
||||
/* read and decode port status */
|
||||
ret = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||
if (hub_is_superspeedplus(hub->hdev))
|
||||
ret = hub_ext_port_status(hub, port1,
|
||||
HUB_EXT_PORT_STATUS,
|
||||
&portstatus, &portchange,
|
||||
&ext_portstatus);
|
||||
else
|
||||
ret = hub_port_status(hub, port1, &portstatus,
|
||||
&portchange);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -2727,6 +2775,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|||
|
||||
if (hub_is_wusb(hub))
|
||||
udev->speed = USB_SPEED_WIRELESS;
|
||||
else if (hub_is_superspeedplus(hub->hdev) &&
|
||||
port_speed_is_ssp(hub->hdev, ext_portstatus &
|
||||
USB_EXT_PORT_STAT_RX_SPEED_ID))
|
||||
udev->speed = USB_SPEED_SUPER_PLUS;
|
||||
else if (hub_is_superspeed(hub->hdev))
|
||||
udev->speed = USB_SPEED_SUPER;
|
||||
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
|
||||
|
|
@ -3989,7 +4041,7 @@ int usb_disable_lpm(struct usb_device *udev)
|
|||
struct usb_hcd *hcd;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
udev->speed < USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable ||
|
||||
udev->state < USB_STATE_DEFAULT)
|
||||
return 0;
|
||||
|
|
@ -4048,7 +4100,7 @@ void usb_enable_lpm(struct usb_device *udev)
|
|||
struct usb_port *port_dev;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
udev->speed < USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable ||
|
||||
udev->state < USB_STATE_DEFAULT)
|
||||
return;
|
||||
|
|
@ -4292,7 +4344,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
int i, j, retval;
|
||||
int retries, operations, retval, i;
|
||||
unsigned delay = HUB_SHORT_RESET_TIME;
|
||||
enum usb_device_speed oldspeed = udev->speed;
|
||||
const char *speed;
|
||||
|
|
@ -4323,7 +4375,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
retval = -ENODEV;
|
||||
|
||||
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
|
||||
/* Don't allow speed changes at reset, except usb 3.0 to faster */
|
||||
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed &&
|
||||
!(oldspeed == USB_SPEED_SUPER && udev->speed > oldspeed)) {
|
||||
dev_dbg(&udev->dev, "device reset changed speed!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -4335,6 +4389,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
|
||||
*/
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_WIRELESS: /* fixed at 512 */
|
||||
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
|
@ -4361,7 +4416,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
else
|
||||
speed = usb_speed_string(udev->speed);
|
||||
|
||||
if (udev->speed != USB_SPEED_SUPER)
|
||||
if (udev->speed < USB_SPEED_SUPER)
|
||||
dev_info(&udev->dev,
|
||||
"%s %s USB device number %d using %s\n",
|
||||
(udev->config) ? "reset" : "new", speed,
|
||||
|
|
@ -4394,7 +4449,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* first 8 bytes of the device descriptor to get the ep0 maxpacket
|
||||
* value.
|
||||
*/
|
||||
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
|
||||
for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
|
||||
bool did_new_scheme = false;
|
||||
|
||||
if (use_new_scheme(udev, retry_counter)) {
|
||||
|
|
@ -4421,7 +4476,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* 255 is for WUSB devices, we actually need to use
|
||||
* 512 (WUSB1.0[4.8.1]).
|
||||
*/
|
||||
for (j = 0; j < 3; ++j) {
|
||||
for (operations = 0; operations < 3; ++operations) {
|
||||
buf->bMaxPacketSize0 = 0;
|
||||
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
|
||||
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
|
|
@ -4441,7 +4496,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
r = -EPROTO;
|
||||
break;
|
||||
}
|
||||
if (r == 0)
|
||||
/*
|
||||
* Some devices time out if they are powered on
|
||||
* when already connected. They need a second
|
||||
* reset. But only on the first attempt,
|
||||
* lest we get into a time out/reset loop
|
||||
*/
|
||||
if (r == 0 || (r == -ETIMEDOUT && retries == 0))
|
||||
break;
|
||||
}
|
||||
udev->descriptor.bMaxPacketSize0 =
|
||||
|
|
@ -4473,7 +4534,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* authorization will assign the final address.
|
||||
*/
|
||||
if (udev->wusb == 0) {
|
||||
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
|
||||
for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
|
||||
retval = hub_set_address(udev, devnum);
|
||||
if (retval >= 0)
|
||||
break;
|
||||
|
|
@ -4485,11 +4546,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
devnum, retval);
|
||||
goto fail;
|
||||
}
|
||||
if (udev->speed == USB_SPEED_SUPER) {
|
||||
if (udev->speed >= USB_SPEED_SUPER) {
|
||||
devnum = udev->devnum;
|
||||
dev_info(&udev->dev,
|
||||
"%s SuperSpeed USB device number %d using %s\n",
|
||||
"%s SuperSpeed%s USB device number %d using %s\n",
|
||||
(udev->config) ? "reset" : "new",
|
||||
(udev->speed == USB_SPEED_SUPER_PLUS) ? "Plus" : "",
|
||||
devnum, udev->bus->controller->driver->name);
|
||||
}
|
||||
|
||||
|
|
@ -4528,7 +4590,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* got from those devices show they aren't superspeed devices. Warm
|
||||
* reset the port attached by the devices can fix them.
|
||||
*/
|
||||
if ((udev->speed == USB_SPEED_SUPER) &&
|
||||
if ((udev->speed >= USB_SPEED_SUPER) &&
|
||||
(le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
|
||||
dev_err(&udev->dev, "got a wrong device descriptor, "
|
||||
"warm reset device\n");
|
||||
|
|
@ -4539,7 +4601,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
}
|
||||
|
||||
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
|
||||
udev->speed == USB_SPEED_SUPER)
|
||||
udev->speed >= USB_SPEED_SUPER)
|
||||
i = 512;
|
||||
else
|
||||
i = udev->descriptor.bMaxPacketSize0;
|
||||
|
|
@ -4749,7 +4811,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
|||
udev->level = hdev->level + 1;
|
||||
udev->wusb = hub_is_wusb(hub);
|
||||
|
||||
/* Only USB 3.0 devices are connected to SuperSpeed hubs. */
|
||||
/* Devices connected to SuperSpeed hubs are USB 3.0 or later */
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
udev->speed = USB_SPEED_SUPER;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -140,6 +140,13 @@ static inline int hub_is_superspeed(struct usb_device *hdev)
|
|||
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
|
||||
}
|
||||
|
||||
static inline int hub_is_superspeedplus(struct usb_device *hdev)
|
||||
{
|
||||
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS &&
|
||||
le16_to_cpu(hdev->descriptor.bcdUSB) >= 0x0310 &&
|
||||
hdev->bos->ssp_cap);
|
||||
}
|
||||
|
||||
static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
|
||||
{
|
||||
unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
|
||||
|
|
|
|||
47
drivers/usb/core/of.c
Normal file
47
drivers/usb/core/of.c
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* of.c The helpers for hcd device tree support
|
||||
*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Author: Peter Chen <peter.chen@freescale.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
|
||||
/**
|
||||
* usb_of_get_child_node - Find the device node match port number
|
||||
* @parent: the parent device node
|
||||
* @portnum: the port number which device is connecting
|
||||
*
|
||||
* Find the node from device tree according to its port number.
|
||||
*
|
||||
* Return: On success, a pointer to the device node, %NULL on failure.
|
||||
*/
|
||||
struct device_node *usb_of_get_child_node(struct device_node *parent,
|
||||
int portnum)
|
||||
{
|
||||
struct device_node *node;
|
||||
u32 port;
|
||||
|
||||
for_each_child_of_node(parent, node) {
|
||||
if (!of_property_read_u32(node, "reg", &port)) {
|
||||
if (port == portnum)
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_of_get_child_node);
|
||||
|
||||
|
|
@ -23,10 +23,12 @@ static ssize_t field##_show(struct device *dev, \
|
|||
{ \
|
||||
struct usb_device *udev; \
|
||||
struct usb_host_config *actconfig; \
|
||||
ssize_t rc = 0; \
|
||||
ssize_t rc; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
usb_lock_device(udev); \
|
||||
rc = usb_lock_device_interruptible(udev); \
|
||||
if (rc < 0) \
|
||||
return -EINTR; \
|
||||
actconfig = udev->actconfig; \
|
||||
if (actconfig) \
|
||||
rc = sprintf(buf, format_string, \
|
||||
|
|
@ -47,10 +49,12 @@ static ssize_t bMaxPower_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_host_config *actconfig;
|
||||
ssize_t rc = 0;
|
||||
ssize_t rc;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
actconfig = udev->actconfig;
|
||||
if (actconfig)
|
||||
rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
|
||||
|
|
@ -64,10 +68,12 @@ static ssize_t configuration_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_host_config *actconfig;
|
||||
ssize_t rc = 0;
|
||||
ssize_t rc;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
actconfig = udev->actconfig;
|
||||
if (actconfig && actconfig->string)
|
||||
rc = sprintf(buf, "%s\n", actconfig->string);
|
||||
|
|
@ -84,11 +90,13 @@ static ssize_t bConfigurationValue_store(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int config, value;
|
||||
int config, value, rc;
|
||||
|
||||
if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255)
|
||||
return -EINVAL;
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
value = usb_set_configuration(udev, config);
|
||||
usb_unlock_device(udev);
|
||||
return (value < 0) ? value : count;
|
||||
|
|
@ -105,7 +113,9 @@ static ssize_t name##_show(struct device *dev, \
|
|||
int retval; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
usb_lock_device(udev); \
|
||||
retval = usb_lock_device_interruptible(udev); \
|
||||
if (retval < 0) \
|
||||
return -EINTR; \
|
||||
retval = sprintf(buf, "%s\n", udev->name); \
|
||||
usb_unlock_device(udev); \
|
||||
return retval; \
|
||||
|
|
@ -141,6 +151,9 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
|
|||
case USB_SPEED_SUPER:
|
||||
speed = "5000";
|
||||
break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
speed = "10000";
|
||||
break;
|
||||
default:
|
||||
speed = "unknown";
|
||||
}
|
||||
|
|
@ -224,11 +237,13 @@ static ssize_t avoid_reset_quirk_store(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int val;
|
||||
int val, rc;
|
||||
|
||||
if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
if (val)
|
||||
udev->quirks |= USB_QUIRK_RESET;
|
||||
else
|
||||
|
|
@ -294,7 +309,7 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int value;
|
||||
int value, rc;
|
||||
|
||||
/* Hubs are always enabled for USB_PERSIST */
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
|
|
@ -303,7 +318,9 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr,
|
|||
if (sscanf(buf, "%d", &value) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
udev->persist_enabled = !!value;
|
||||
usb_unlock_device(udev);
|
||||
return count;
|
||||
|
|
@ -420,13 +437,16 @@ static ssize_t level_store(struct device *dev, struct device_attribute *attr,
|
|||
int len = count;
|
||||
char *cp;
|
||||
int rc = count;
|
||||
int rv;
|
||||
|
||||
warn_level();
|
||||
cp = memchr(buf, '\n', count);
|
||||
if (cp)
|
||||
len = cp - buf;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rv = usb_lock_device_interruptible(udev);
|
||||
if (rv < 0)
|
||||
return -EINTR;
|
||||
|
||||
if (len == sizeof on_string - 1 &&
|
||||
strncmp(buf, on_string, len) == 0)
|
||||
|
|
@ -466,7 +486,9 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
|
|||
bool value;
|
||||
int ret;
|
||||
|
||||
usb_lock_device(udev);
|
||||
ret = usb_lock_device_interruptible(udev);
|
||||
if (ret < 0)
|
||||
return -EINTR;
|
||||
|
||||
ret = strtobool(buf, &value);
|
||||
|
||||
|
|
@ -536,8 +558,11 @@ static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
const char *p;
|
||||
int rc;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
|
||||
if (udev->usb3_lpm_u1_enabled)
|
||||
p = "enabled";
|
||||
|
|
@ -555,8 +580,11 @@ static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
const char *p;
|
||||
int rc;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
|
||||
if (udev->usb3_lpm_u2_enabled)
|
||||
p = "enabled";
|
||||
|
|
@ -822,7 +850,6 @@ read_descriptors(struct file *filp, struct kobject *kobj,
|
|||
* Following that are the raw descriptor entries for all the
|
||||
* configurations (config plus subsidiary descriptors).
|
||||
*/
|
||||
usb_lock_device(udev);
|
||||
for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
|
||||
nleft > 0; ++cfgno) {
|
||||
if (cfgno < 0) {
|
||||
|
|
@ -843,7 +870,6 @@ read_descriptors(struct file *filp, struct kobject *kobj,
|
|||
off -= srclen;
|
||||
}
|
||||
}
|
||||
usb_unlock_device(udev);
|
||||
return count - nleft;
|
||||
}
|
||||
|
||||
|
|
@ -969,7 +995,9 @@ static ssize_t supports_autosuspend_show(struct device *dev,
|
|||
{
|
||||
int s;
|
||||
|
||||
device_lock(dev);
|
||||
s = device_lock_interruptible(dev);
|
||||
if (s < 0)
|
||||
return -EINTR;
|
||||
/* Devices will be autosuspended even when an interface isn't claimed */
|
||||
s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
|
||||
device_unlock(dev);
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
/* SuperSpeed isoc endpoints have up to 16 bursts of up to
|
||||
* 3 packets each
|
||||
*/
|
||||
if (dev->speed == USB_SPEED_SUPER) {
|
||||
if (dev->speed >= USB_SPEED_SUPER) {
|
||||
int burst = 1 + ep->ss_ep_comp.bMaxBurst;
|
||||
int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
|
||||
max *= burst;
|
||||
|
|
@ -499,6 +499,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
}
|
||||
/* too big? */
|
||||
switch (dev->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER: /* units are 125us */
|
||||
/* Handle up to 2^(16-1) microframes */
|
||||
if (urb->interval > (1 << 15))
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
|
@ -241,7 +242,7 @@ static int __each_dev(struct device *dev, void *data)
|
|||
if (!is_usb_device(dev))
|
||||
return 0;
|
||||
|
||||
return arg->fn(container_of(dev, struct usb_device, dev), arg->data);
|
||||
return arg->fn(to_usb_device(dev), arg->data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -397,7 +398,7 @@ struct device_type usb_device_type = {
|
|||
/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
|
||||
static unsigned usb_bus_is_wusb(struct usb_bus *bus)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(bus, struct usb_hcd, self);
|
||||
struct usb_hcd *hcd = bus_to_hcd(bus);
|
||||
return hcd->wireless;
|
||||
}
|
||||
|
||||
|
|
@ -470,6 +471,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->route = 0;
|
||||
|
||||
dev->dev.parent = bus->controller;
|
||||
dev->dev.of_node = bus->controller->of_node;
|
||||
dev_set_name(&dev->dev, "usb%d", bus->busnum);
|
||||
root_hub = 1;
|
||||
} else {
|
||||
|
|
@ -494,6 +496,14 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->dev.parent = &parent->dev;
|
||||
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
|
||||
|
||||
if (!parent->parent) {
|
||||
/* device under root hub's port */
|
||||
port1 = usb_hcd_find_raw_port_number(usb_hcd,
|
||||
port1);
|
||||
}
|
||||
dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node,
|
||||
port1);
|
||||
|
||||
/* hub driver sets up TT records */
|
||||
}
|
||||
|
||||
|
|
@ -1115,6 +1125,7 @@ static void __exit usb_exit(void)
|
|||
bus_unregister(&usb_bus_type);
|
||||
usb_acpi_unregister();
|
||||
usb_debugfs_cleanup();
|
||||
idr_destroy(&usb_bus_idr);
|
||||
}
|
||||
|
||||
subsys_initcall(usb_init);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ static inline unsigned usb_get_max_power(struct usb_device *udev,
|
|||
struct usb_host_config *c)
|
||||
{
|
||||
/* SuperSpeed power is in 8 mA units; others are in 2 mA units */
|
||||
unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2);
|
||||
unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2);
|
||||
|
||||
return c->desc.bMaxPower * mul;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ config USB_DWC2
|
|||
tristate "DesignWare USB2 DRD Core Support"
|
||||
depends on HAS_DMA
|
||||
depends on USB || USB_GADGET
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say Y here if your system has a Dual Role Hi-Speed USB
|
||||
controller based on the DesignWare HSOTG IP Core.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -44,6 +44,26 @@
|
|||
#include <linux/usb/phy.h>
|
||||
#include "hw.h"
|
||||
|
||||
/*
|
||||
* Suggested defines for tracers:
|
||||
* - no_printk: Disable tracing
|
||||
* - pr_info: Print this info to the console
|
||||
* - trace_printk: Print this info to trace buffer (good for verbose logging)
|
||||
*/
|
||||
|
||||
#define DWC2_TRACE_SCHEDULER no_printk
|
||||
#define DWC2_TRACE_SCHEDULER_VB no_printk
|
||||
|
||||
/* Detailed scheduler tracing, but won't overwhelm console */
|
||||
#define dwc2_sch_dbg(hsotg, fmt, ...) \
|
||||
DWC2_TRACE_SCHEDULER(pr_fmt("%s: SCH: " fmt), \
|
||||
dev_name(hsotg->dev), ##__VA_ARGS__)
|
||||
|
||||
/* Verbose scheduler tracing */
|
||||
#define dwc2_sch_vdbg(hsotg, fmt, ...) \
|
||||
DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt), \
|
||||
dev_name(hsotg->dev), ##__VA_ARGS__)
|
||||
|
||||
static inline u32 dwc2_readl(const void __iomem *addr)
|
||||
{
|
||||
u32 value = __raw_readl(addr);
|
||||
|
|
@ -572,6 +592,84 @@ struct dwc2_hregs_backup {
|
|||
bool valid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants related to high speed periodic scheduling
|
||||
*
|
||||
* We have a periodic schedule that is DWC2_HS_SCHEDULE_UFRAMES long. From a
|
||||
* reservation point of view it's assumed that the schedule goes right back to
|
||||
* the beginning after the end of the schedule.
|
||||
*
|
||||
* What does that mean for scheduling things with a long interval? It means
|
||||
* we'll reserve time for them in every possible microframe that they could
|
||||
* ever be scheduled in. ...but we'll still only actually schedule them as
|
||||
* often as they were requested.
|
||||
*
|
||||
* We keep our schedule in a "bitmap" structure. This simplifies having
|
||||
* to keep track of and merge intervals: we just let the bitmap code do most
|
||||
* of the heavy lifting. In a way scheduling is much like memory allocation.
|
||||
*
|
||||
* We schedule 100us per uframe or 80% of 125us (the maximum amount you're
|
||||
* supposed to schedule for periodic transfers). That's according to spec.
|
||||
*
|
||||
* Note that though we only schedule 80% of each microframe, the bitmap that we
|
||||
* keep the schedule in is tightly packed (AKA it doesn't have 100us worth of
|
||||
* space for each uFrame).
|
||||
*
|
||||
* Requirements:
|
||||
* - DWC2_HS_SCHEDULE_UFRAMES must even divide 0x4000 (HFNUM_MAX_FRNUM + 1)
|
||||
* - DWC2_HS_SCHEDULE_UFRAMES must be 8 times DWC2_LS_SCHEDULE_FRAMES (probably
|
||||
* could be any multiple of 8 times DWC2_LS_SCHEDULE_FRAMES, but there might
|
||||
* be bugs). The 8 comes from the USB spec: number of microframes per frame.
|
||||
*/
|
||||
#define DWC2_US_PER_UFRAME 125
|
||||
#define DWC2_HS_PERIODIC_US_PER_UFRAME 100
|
||||
|
||||
#define DWC2_HS_SCHEDULE_UFRAMES 8
|
||||
#define DWC2_HS_SCHEDULE_US (DWC2_HS_SCHEDULE_UFRAMES * \
|
||||
DWC2_HS_PERIODIC_US_PER_UFRAME)
|
||||
|
||||
/*
|
||||
* Constants related to low speed scheduling
|
||||
*
|
||||
* For high speed we schedule every 1us. For low speed that's a bit overkill,
|
||||
* so we make up a unit called a "slice" that's worth 25us. There are 40
|
||||
* slices in a full frame and we can schedule 36 of those (90%) for periodic
|
||||
* transfers.
|
||||
*
|
||||
* Our low speed schedule can be as short as 1 frame or could be longer. When
|
||||
* we only schedule 1 frame it means that we'll need to reserve a time every
|
||||
* frame even for things that only transfer very rarely, so something that runs
|
||||
* every 2048 frames will get time reserved in every frame. Our low speed
|
||||
* schedule can be longer and we'll be able to handle more overlap, but that
|
||||
* will come at increased memory cost and increased time to schedule.
|
||||
*
|
||||
* Note: one other advantage of a short low speed schedule is that if we mess
|
||||
* up and miss scheduling we can jump in and use any of the slots that we
|
||||
* happened to reserve.
|
||||
*
|
||||
* With 25 us per slice and 1 frame in the schedule, we only need 4 bytes for
|
||||
* the schedule. There will be one schedule per TT.
|
||||
*
|
||||
* Requirements:
|
||||
* - DWC2_US_PER_SLICE must evenly divide DWC2_LS_PERIODIC_US_PER_FRAME.
|
||||
*/
|
||||
#define DWC2_US_PER_SLICE 25
|
||||
#define DWC2_SLICES_PER_UFRAME (DWC2_US_PER_UFRAME / DWC2_US_PER_SLICE)
|
||||
|
||||
#define DWC2_ROUND_US_TO_SLICE(us) \
|
||||
(DIV_ROUND_UP((us), DWC2_US_PER_SLICE) * \
|
||||
DWC2_US_PER_SLICE)
|
||||
|
||||
#define DWC2_LS_PERIODIC_US_PER_FRAME \
|
||||
900
|
||||
#define DWC2_LS_PERIODIC_SLICES_PER_FRAME \
|
||||
(DWC2_LS_PERIODIC_US_PER_FRAME / \
|
||||
DWC2_US_PER_SLICE)
|
||||
|
||||
#define DWC2_LS_SCHEDULE_FRAMES 1
|
||||
#define DWC2_LS_SCHEDULE_SLICES (DWC2_LS_SCHEDULE_FRAMES * \
|
||||
DWC2_LS_PERIODIC_SLICES_PER_FRAME)
|
||||
|
||||
/**
|
||||
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
|
||||
* and periodic schedules
|
||||
|
|
@ -657,11 +755,14 @@ struct dwc2_hregs_backup {
|
|||
* periodic_sched_ready because it must be rescheduled for
|
||||
* the next frame. Otherwise, the item moves to
|
||||
* periodic_sched_inactive.
|
||||
* @split_order: List keeping track of channels doing splits, in order.
|
||||
* @periodic_usecs: Total bandwidth claimed so far for periodic transfers.
|
||||
* This value is in microseconds per (micro)frame. The
|
||||
* assumption is that all periodic transfers may occur in
|
||||
* the same (micro)frame.
|
||||
* @frame_usecs: Internal variable used by the microframe scheduler
|
||||
* @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
|
||||
* host is in high speed mode; low speed schedules are
|
||||
* stored elsewhere since we need one per TT.
|
||||
* @frame_number: Frame number read from the core at SOF. The value ranges
|
||||
* from 0 to HFNUM_MAX_FRNUM.
|
||||
* @periodic_qh_count: Count of periodic QHs, if using several eps. Used for
|
||||
|
|
@ -780,16 +881,19 @@ struct dwc2_hsotg {
|
|||
struct list_head periodic_sched_ready;
|
||||
struct list_head periodic_sched_assigned;
|
||||
struct list_head periodic_sched_queued;
|
||||
struct list_head split_order;
|
||||
u16 periodic_usecs;
|
||||
u16 frame_usecs[8];
|
||||
unsigned long hs_periodic_bitmap[
|
||||
DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
|
||||
u16 frame_number;
|
||||
u16 periodic_qh_count;
|
||||
bool bus_suspended;
|
||||
bool new_connection;
|
||||
|
||||
u16 last_frame_num;
|
||||
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
#define FRAME_NUM_ARRAY_SIZE 1000
|
||||
u16 last_frame_num;
|
||||
u16 *frame_num_array;
|
||||
u16 *last_frame_num_array;
|
||||
int frame_num_idx;
|
||||
|
|
@ -885,34 +989,11 @@ enum dwc2_halt_status {
|
|||
*/
|
||||
extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
|
||||
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Host core Functions.
|
||||
* The following functions support managing the DWC_otg controller in host
|
||||
* mode.
|
||||
*/
|
||||
extern void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
|
||||
enum dwc2_halt_status halt_status);
|
||||
extern void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
|
||||
extern bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
|
|
@ -924,7 +1005,6 @@ extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
|
|||
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
|
||||
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
|
||||
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
|
||||
|
|
@ -1191,6 +1271,8 @@ extern void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
|
|||
extern void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
|
||||
extern int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
|
||||
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
|
||||
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
|
|
@ -1208,22 +1290,37 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
|||
int testmode)
|
||||
{ return 0; }
|
||||
#define dwc2_is_device_connected(hsotg) (0)
|
||||
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
|
||||
extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg,
|
||||
int us)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
|
||||
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{ return 0; }
|
||||
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __DWC2_CORE_H__ */
|
||||
|
|
|
|||
|
|
@ -3668,3 +3668,105 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_backup_device_registers() - Backup controller device registers.
|
||||
* When suspending usb bus, registers needs to be backuped
|
||||
* if controller power is disabled once suspended.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Backup dev regs */
|
||||
dr = &hsotg->dr_backup;
|
||||
|
||||
dr->dcfg = dwc2_readl(hsotg->regs + DCFG);
|
||||
dr->dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
dr->daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
|
||||
dr->diepmsk = dwc2_readl(hsotg->regs + DIEPMSK);
|
||||
dr->doepmsk = dwc2_readl(hsotg->regs + DOEPMSK);
|
||||
|
||||
for (i = 0; i < hsotg->num_of_eps; i++) {
|
||||
/* Backup IN EPs */
|
||||
dr->diepctl[i] = dwc2_readl(hsotg->regs + DIEPCTL(i));
|
||||
|
||||
/* Ensure DATA PID is correctly configured */
|
||||
if (dr->diepctl[i] & DXEPCTL_DPID)
|
||||
dr->diepctl[i] |= DXEPCTL_SETD1PID;
|
||||
else
|
||||
dr->diepctl[i] |= DXEPCTL_SETD0PID;
|
||||
|
||||
dr->dieptsiz[i] = dwc2_readl(hsotg->regs + DIEPTSIZ(i));
|
||||
dr->diepdma[i] = dwc2_readl(hsotg->regs + DIEPDMA(i));
|
||||
|
||||
/* Backup OUT EPs */
|
||||
dr->doepctl[i] = dwc2_readl(hsotg->regs + DOEPCTL(i));
|
||||
|
||||
/* Ensure DATA PID is correctly configured */
|
||||
if (dr->doepctl[i] & DXEPCTL_DPID)
|
||||
dr->doepctl[i] |= DXEPCTL_SETD1PID;
|
||||
else
|
||||
dr->doepctl[i] |= DXEPCTL_SETD0PID;
|
||||
|
||||
dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i));
|
||||
dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i));
|
||||
}
|
||||
dr->valid = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_restore_device_registers() - Restore controller device registers.
|
||||
* When resuming usb bus, device registers needs to be restored
|
||||
* if controller power were disabled.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
u32 dctl;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Restore dev regs */
|
||||
dr = &hsotg->dr_backup;
|
||||
if (!dr->valid) {
|
||||
dev_err(hsotg->dev, "%s: no device registers to restore\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
dr->valid = false;
|
||||
|
||||
dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
|
||||
dwc2_writel(dr->dctl, hsotg->regs + DCTL);
|
||||
dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK);
|
||||
dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK);
|
||||
dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK);
|
||||
|
||||
for (i = 0; i < hsotg->num_of_eps; i++) {
|
||||
/* Restore IN EPs */
|
||||
dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
|
||||
dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
|
||||
dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
|
||||
|
||||
/* Restore OUT EPs */
|
||||
dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
|
||||
dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
|
||||
dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
|
||||
}
|
||||
|
||||
/* Set the Power-On Programming done bit */
|
||||
dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
dctl |= DCTL_PWRONPRGDONE;
|
||||
dwc2_writel(dctl, hsotg->regs + DCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -75,8 +75,6 @@ struct dwc2_qh;
|
|||
* (micro)frame
|
||||
* @xfer_buf: Pointer to current transfer buffer position
|
||||
* @xfer_dma: DMA address of xfer_buf
|
||||
* @align_buf: In Buffer DMA mode this will be used if xfer_buf is not
|
||||
* DWORD aligned
|
||||
* @xfer_len: Total number of bytes to transfer
|
||||
* @xfer_count: Number of bytes transferred so far
|
||||
* @start_pkt_count: Packet count at start of transfer
|
||||
|
|
@ -108,6 +106,7 @@ struct dwc2_qh;
|
|||
* @hc_list_entry: For linking to list of host channels
|
||||
* @desc_list_addr: Current QH's descriptor list DMA address
|
||||
* @desc_list_sz: Current QH's descriptor list size
|
||||
* @split_order_list_entry: List entry for keeping track of the order of splits
|
||||
*
|
||||
* This structure represents the state of a single host channel when acting in
|
||||
* host mode. It contains the data items needed to transfer packets to an
|
||||
|
|
@ -133,7 +132,6 @@ struct dwc2_host_chan {
|
|||
|
||||
u8 *xfer_buf;
|
||||
dma_addr_t xfer_dma;
|
||||
dma_addr_t align_buf;
|
||||
u32 xfer_len;
|
||||
u32 xfer_count;
|
||||
u16 start_pkt_count;
|
||||
|
|
@ -161,6 +159,7 @@ struct dwc2_host_chan {
|
|||
struct list_head hc_list_entry;
|
||||
dma_addr_t desc_list_addr;
|
||||
u32 desc_list_sz;
|
||||
struct list_head split_order_list_entry;
|
||||
};
|
||||
|
||||
struct dwc2_hcd_pipe_info {
|
||||
|
|
@ -213,9 +212,47 @@ enum dwc2_transaction_type {
|
|||
DWC2_TRANSACTION_ALL,
|
||||
};
|
||||
|
||||
/* The number of elements per LS bitmap (per port on multi_tt) */
|
||||
#define DWC2_ELEMENTS_PER_LS_BITMAP DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
|
||||
BITS_PER_LONG)
|
||||
|
||||
/**
|
||||
* struct dwc2_tt - dwc2 data associated with a usb_tt
|
||||
*
|
||||
* @refcount: Number of Queue Heads (QHs) holding a reference.
|
||||
* @usb_tt: Pointer back to the official usb_tt.
|
||||
* @periodic_bitmaps: Bitmap for which parts of the 1ms frame are accounted
|
||||
* for already. Each is DWC2_ELEMENTS_PER_LS_BITMAP
|
||||
* elements (so sizeof(long) times that in bytes).
|
||||
*
|
||||
* This structure is stored in the hcpriv of the official usb_tt.
|
||||
*/
|
||||
struct dwc2_tt {
|
||||
int refcount;
|
||||
struct usb_tt *usb_tt;
|
||||
unsigned long periodic_bitmaps[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
|
||||
*
|
||||
* @start_schedule_usecs: The start time on the main bus schedule. Note that
|
||||
* the main bus schedule is tightly packed and this
|
||||
* time should be interpreted as tightly packed (so
|
||||
* uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
|
||||
* instead of 125 us).
|
||||
* @duration_us: How long this transfer goes.
|
||||
*/
|
||||
|
||||
struct dwc2_hs_transfer_time {
|
||||
u32 start_schedule_us;
|
||||
u16 duration_us;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_qh - Software queue head structure
|
||||
*
|
||||
* @hsotg: The HCD state structure for the DWC OTG controller
|
||||
* @ep_type: Endpoint type. One of the following values:
|
||||
* - USB_ENDPOINT_XFER_CONTROL
|
||||
* - USB_ENDPOINT_XFER_BULK
|
||||
|
|
@ -236,17 +273,35 @@ enum dwc2_transaction_type {
|
|||
* @do_split: Full/low speed endpoint on high-speed hub requires split
|
||||
* @td_first: Index of first activated isochronous transfer descriptor
|
||||
* @td_last: Index of last activated isochronous transfer descriptor
|
||||
* @usecs: Bandwidth in microseconds per (micro)frame
|
||||
* @interval: Interval between transfers in (micro)frames
|
||||
* @sched_frame: (Micro)frame to initialize a periodic transfer.
|
||||
* The transfer executes in the following (micro)frame.
|
||||
* @frame_usecs: Internal variable used by the microframe scheduler
|
||||
* @start_split_frame: (Micro)frame at which last start split was initialized
|
||||
* @host_us: Bandwidth in microseconds per transfer as seen by host
|
||||
* @device_us: Bandwidth in microseconds per transfer as seen by device
|
||||
* @host_interval: Interval between transfers as seen by the host. If
|
||||
* the host is high speed and the device is low speed this
|
||||
* will be 8 times device interval.
|
||||
* @device_interval: Interval between transfers as seen by the device.
|
||||
* interval.
|
||||
* @next_active_frame: (Micro)frame _before_ we next need to put something on
|
||||
* the bus. We'll move the qh to active here. If the
|
||||
* host is in high speed mode this will be a uframe. If
|
||||
* the host is in low speed mode this will be a full frame.
|
||||
* @start_active_frame: If we are partway through a split transfer, this will be
|
||||
* what next_active_frame was when we started. Otherwise
|
||||
* it should always be the same as next_active_frame.
|
||||
* @num_hs_transfers: Number of transfers in hs_transfers.
|
||||
* Normally this is 1 but can be more than one for splits.
|
||||
* Always >= 1 unless the host is in low/full speed mode.
|
||||
* @hs_transfers: Transfers that are scheduled as seen by the high speed
|
||||
* bus. Not used if host is in low or full speed mode (but
|
||||
* note that it IS USED if the device is low or full speed
|
||||
* as long as the HOST is in high speed mode).
|
||||
* @ls_start_schedule_slice: Start time (in slices) on the low speed bus
|
||||
* schedule that's being used by this device. This
|
||||
* will be on the periodic_bitmap in a
|
||||
* "struct dwc2_tt". Not used if this device is high
|
||||
* speed. Note that this is in "schedule slice" which
|
||||
* is tightly packed.
|
||||
* @ls_duration_us: Duration on the low speed bus schedule.
|
||||
* @ntd: Actual number of transfer descriptors in a list
|
||||
* @dw_align_buf: Used instead of original buffer if its physical address
|
||||
* is not dword-aligned
|
||||
* @dw_align_buf_size: Size of dw_align_buf
|
||||
* @dw_align_buf_dma: DMA address for dw_align_buf
|
||||
* @qtd_list: List of QTDs for this QH
|
||||
* @channel: Host channel currently processing transfers for this QH
|
||||
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
|
||||
|
|
@ -257,13 +312,20 @@ enum dwc2_transaction_type {
|
|||
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
|
||||
* descriptor and indicates original XferSize value for the
|
||||
* descriptor
|
||||
* @unreserve_timer: Timer for releasing periodic reservation.
|
||||
* @dwc2_tt: Pointer to our tt info (or NULL if no tt).
|
||||
* @ttport: Port number within our tt.
|
||||
* @tt_buffer_dirty True if clear_tt_buffer_complete is pending
|
||||
* @unreserve_pending: True if we planned to unreserve but haven't yet.
|
||||
* @schedule_low_speed: True if we have a low/full speed component (either the
|
||||
* host is in low/full speed mode or do_split).
|
||||
*
|
||||
* A Queue Head (QH) holds the static characteristics of an endpoint and
|
||||
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
|
||||
* be entered in either the non-periodic or periodic schedule.
|
||||
*/
|
||||
struct dwc2_qh {
|
||||
struct dwc2_hsotg *hsotg;
|
||||
u8 ep_type;
|
||||
u8 ep_is_in;
|
||||
u16 maxp;
|
||||
|
|
@ -273,15 +335,16 @@ struct dwc2_qh {
|
|||
u8 do_split;
|
||||
u8 td_first;
|
||||
u8 td_last;
|
||||
u16 usecs;
|
||||
u16 interval;
|
||||
u16 sched_frame;
|
||||
u16 frame_usecs[8];
|
||||
u16 start_split_frame;
|
||||
u16 host_us;
|
||||
u16 device_us;
|
||||
u16 host_interval;
|
||||
u16 device_interval;
|
||||
u16 next_active_frame;
|
||||
u16 start_active_frame;
|
||||
s16 num_hs_transfers;
|
||||
struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
|
||||
u32 ls_start_schedule_slice;
|
||||
u16 ntd;
|
||||
u8 *dw_align_buf;
|
||||
int dw_align_buf_size;
|
||||
dma_addr_t dw_align_buf_dma;
|
||||
struct list_head qtd_list;
|
||||
struct dwc2_host_chan *channel;
|
||||
struct list_head qh_list_entry;
|
||||
|
|
@ -289,7 +352,12 @@ struct dwc2_qh {
|
|||
dma_addr_t desc_list_dma;
|
||||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
struct timer_list unreserve_timer;
|
||||
struct dwc2_tt *dwc_tt;
|
||||
int ttport;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
unsigned unreserve_pending:1;
|
||||
unsigned schedule_low_speed:1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -362,6 +430,8 @@ struct hc_xfer_info {
|
|||
};
|
||||
#endif
|
||||
|
||||
u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/* Gets the struct usb_hcd that contains a struct dwc2_hsotg */
|
||||
static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
|
|
@ -383,6 +453,12 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
|
|||
dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
|
||||
}
|
||||
|
||||
void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
|
||||
void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
|
||||
enum dwc2_halt_status halt_status);
|
||||
void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
|
||||
/*
|
||||
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
|
||||
* are read as 1, they won't clear when written back.
|
||||
|
|
@ -456,7 +532,6 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
|
|||
|
||||
/* Schedule Queue Functions */
|
||||
/* Implemented in hcd_queue.c */
|
||||
extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
|
||||
extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_hcd_urb *urb,
|
||||
gfp_t mem_flags);
|
||||
|
|
@ -571,6 +646,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
|
|||
return (frame + inc) & HFNUM_MAX_FRNUM;
|
||||
}
|
||||
|
||||
static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
|
||||
{
|
||||
return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
|
||||
}
|
||||
|
||||
static inline u16 dwc2_full_frame_num(u16 frame)
|
||||
{
|
||||
return (frame & HFNUM_MAX_FRNUM) >> 3;
|
||||
|
|
@ -648,7 +728,7 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
return qh->usecs;
|
||||
return qh->host_us;
|
||||
}
|
||||
|
||||
extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
|
||||
|
|
@ -717,6 +797,12 @@ extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
|
|||
extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
|
||||
int *hub_addr, int *hub_port);
|
||||
extern struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
|
||||
void *context, gfp_t mem_flags,
|
||||
int *ttport);
|
||||
|
||||
extern void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_tt *dwc_tt);
|
||||
extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
|
||||
extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
||||
int status);
|
||||
|
|
@ -739,7 +825,7 @@ do { \
|
|||
_qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \
|
||||
qtd_list_entry); \
|
||||
if (usb_pipeint(_qtd_->urb->pipe) && \
|
||||
(_qh_)->start_split_frame != 0 && !_qtd_->complete_split) { \
|
||||
(_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
|
||||
_hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \
|
||||
switch (_hfnum_.b.frnum & 0x7) { \
|
||||
case 7: \
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ static u16 dwc2_max_desc_num(struct dwc2_qh *qh)
|
|||
static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
|
||||
{
|
||||
return qh->dev_speed == USB_SPEED_HIGH ?
|
||||
(qh->interval + 8 - 1) / 8 : qh->interval;
|
||||
(qh->host_interval + 8 - 1) / 8 : qh->host_interval;
|
||||
}
|
||||
|
||||
static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
|
|
@ -111,7 +111,7 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
|
||||
qh->desc_list_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
kfree(qh->desc_list);
|
||||
kmem_cache_free(desc_cache, qh->desc_list);
|
||||
qh->desc_list = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
chan = qh->channel;
|
||||
inc = dwc2_frame_incr_val(qh);
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
|
||||
i = dwc2_frame_list_idx(qh->sched_frame);
|
||||
i = dwc2_frame_list_idx(qh->next_active_frame);
|
||||
else
|
||||
i = 0;
|
||||
|
||||
|
|
@ -278,13 +278,13 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
return;
|
||||
|
||||
chan->schinfo = 0;
|
||||
if (chan->speed == USB_SPEED_HIGH && qh->interval) {
|
||||
if (chan->speed == USB_SPEED_HIGH && qh->host_interval) {
|
||||
j = 1;
|
||||
/* TODO - check this */
|
||||
inc = (8 + qh->interval - 1) / qh->interval;
|
||||
inc = (8 + qh->host_interval - 1) / qh->host_interval;
|
||||
for (i = 0; i < inc; i++) {
|
||||
chan->schinfo |= j;
|
||||
j = j << qh->interval;
|
||||
j = j << qh->host_interval;
|
||||
}
|
||||
} else {
|
||||
chan->schinfo = 0xff;
|
||||
|
|
@ -431,7 +431,10 @@ static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg,
|
|||
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
|
||||
/* sched_frame is always frame number (not uFrame) both in FS and HS! */
|
||||
/*
|
||||
* next_active_frame is always frame number (not uFrame) both in FS
|
||||
* and HS!
|
||||
*/
|
||||
|
||||
/*
|
||||
* skip_frames is used to limit activated descriptors number
|
||||
|
|
@ -514,13 +517,13 @@ static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg,
|
|||
*/
|
||||
fr_idx_tmp = dwc2_frame_list_idx(frame);
|
||||
fr_idx = (FRLISTEN_64_SIZE +
|
||||
dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp)
|
||||
% dwc2_frame_incr_val(qh);
|
||||
dwc2_frame_list_idx(qh->next_active_frame) -
|
||||
fr_idx_tmp) % dwc2_frame_incr_val(qh);
|
||||
fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE;
|
||||
} else {
|
||||
qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh,
|
||||
qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh,
|
||||
&skip_frames);
|
||||
fr_idx = dwc2_frame_list_idx(qh->sched_frame);
|
||||
fr_idx = dwc2_frame_list_idx(qh->next_active_frame);
|
||||
}
|
||||
|
||||
qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx);
|
||||
|
|
@ -583,7 +586,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
u16 next_idx;
|
||||
|
||||
idx = qh->td_last;
|
||||
inc = qh->interval;
|
||||
inc = qh->host_interval;
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
|
||||
|
|
@ -605,11 +608,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
}
|
||||
}
|
||||
|
||||
if (qh->interval) {
|
||||
ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
|
||||
qh->interval;
|
||||
if (qh->host_interval) {
|
||||
ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) /
|
||||
qh->host_interval;
|
||||
if (skip_frames && !qh->channel)
|
||||
ntd_max -= skip_frames / qh->interval;
|
||||
ntd_max -= skip_frames / qh->host_interval;
|
||||
}
|
||||
|
||||
max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
|
||||
|
|
@ -1029,7 +1032,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
idx);
|
||||
if (rc < 0)
|
||||
return;
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->interval,
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
|
||||
chan->speed);
|
||||
if (!rc)
|
||||
continue;
|
||||
|
|
@ -1039,7 +1042,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
|
||||
/* rc == DWC2_CMPL_STOP */
|
||||
|
||||
if (qh->interval >= 32)
|
||||
if (qh->host_interval >= 32)
|
||||
goto stop_scan;
|
||||
|
||||
qh->td_first = idx;
|
||||
|
|
@ -1242,8 +1245,10 @@ static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
for (i = 0; i < qtd_desc_count; i++) {
|
||||
if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd,
|
||||
desc_num, halt_status,
|
||||
&xfer_done))
|
||||
&xfer_done)) {
|
||||
qtd = NULL;
|
||||
goto stop_scan;
|
||||
}
|
||||
|
||||
desc_num++;
|
||||
}
|
||||
|
|
@ -1258,7 +1263,7 @@ stop_scan:
|
|||
if (halt_status == DWC2_HC_XFER_STALL)
|
||||
qh->data_toggle = DWC2_HC_PID_DATA0;
|
||||
else
|
||||
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
||||
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, NULL);
|
||||
}
|
||||
|
||||
if (halt_status == DWC2_HC_XFER_COMPLETE) {
|
||||
|
|
@ -1326,8 +1331,8 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
dwc2_hcd_qh_unlink(hsotg, qh);
|
||||
} else {
|
||||
/* Keep in assigned schedule to continue transfer */
|
||||
list_move(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
list_move_tail(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
/*
|
||||
* If channel has been halted during giveback of urb
|
||||
* then prevent any new scheduling.
|
||||
|
|
|
|||
|
|
@ -55,12 +55,16 @@
|
|||
/* This function is for debug only */
|
||||
static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
u16 curr_frame_number = hsotg->frame_number;
|
||||
u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
|
||||
|
||||
if (expected != curr_frame_number)
|
||||
dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
|
||||
expected, curr_frame_number);
|
||||
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
|
||||
if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) !=
|
||||
curr_frame_number) {
|
||||
if (expected != curr_frame_number) {
|
||||
hsotg->frame_num_array[hsotg->frame_num_idx] =
|
||||
curr_frame_number;
|
||||
hsotg->last_frame_num_array[hsotg->frame_num_idx] =
|
||||
|
|
@ -79,14 +83,15 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
hsotg->dumped_frame_num_array = 1;
|
||||
}
|
||||
hsotg->last_frame_num = curr_frame_number;
|
||||
#endif
|
||||
hsotg->last_frame_num = curr_frame_number;
|
||||
}
|
||||
|
||||
static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan,
|
||||
struct dwc2_qtd *qtd)
|
||||
{
|
||||
struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub;
|
||||
struct urb *usb_urb;
|
||||
|
||||
if (!chan->qh)
|
||||
|
|
@ -102,6 +107,15 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
|
|||
if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The root hub doesn't really have a TT, but Linux thinks it
|
||||
* does because how could you have a "high speed hub" that
|
||||
* directly talks directly to low speed devices without a TT?
|
||||
* It's all lies. Lies, I tell you.
|
||||
*/
|
||||
if (usb_urb->dev->tt->hub == root_hub)
|
||||
return;
|
||||
|
||||
if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) {
|
||||
chan->qh->tt_buffer_dirty = 1;
|
||||
if (usb_hub_clear_tt_buffer(usb_urb))
|
||||
|
|
@ -138,13 +152,19 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
|||
while (qh_entry != &hsotg->periodic_sched_inactive) {
|
||||
qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
|
||||
qh_entry = qh_entry->next;
|
||||
if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
|
||||
if (dwc2_frame_num_le(qh->next_active_frame,
|
||||
hsotg->frame_number)) {
|
||||
dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
|
||||
qh, hsotg->frame_number,
|
||||
qh->next_active_frame);
|
||||
|
||||
/*
|
||||
* Move QH to the ready list to be executed next
|
||||
* (micro)frame
|
||||
*/
|
||||
list_move(&qh->qh_list_entry,
|
||||
list_move_tail(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_ready);
|
||||
}
|
||||
}
|
||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
|
|
@ -472,18 +492,6 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
|
|||
xfer_length = urb->length - urb->actual_length;
|
||||
}
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + urb->actual_length,
|
||||
chan->qh->dw_align_buf, xfer_length);
|
||||
}
|
||||
|
||||
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
|
||||
urb->actual_length, xfer_length);
|
||||
urb->actual_length += xfer_length;
|
||||
|
|
@ -573,21 +581,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|||
frame_desc->status = 0;
|
||||
frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
|
||||
chan, chnum, qtd, halt_status, NULL);
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && frame_desc->actual_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset,
|
||||
chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
}
|
||||
break;
|
||||
case DWC2_HC_XFER_FRAME_OVERRUN:
|
||||
urb->error_count++;
|
||||
|
|
@ -608,21 +601,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|||
frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
|
||||
chan, chnum, qtd, halt_status, NULL);
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && frame_desc->actual_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset,
|
||||
chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
}
|
||||
|
||||
/* Skip whole frame */
|
||||
if (chan->qh->do_split &&
|
||||
chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
|
||||
|
|
@ -688,8 +666,6 @@ static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
}
|
||||
|
||||
no_qtd:
|
||||
if (qh->channel)
|
||||
qh->channel->align_buf = 0;
|
||||
qh->channel = NULL;
|
||||
dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
|
||||
}
|
||||
|
|
@ -846,7 +822,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
|
|||
* halt to be queued when the periodic schedule is
|
||||
* processed.
|
||||
*/
|
||||
list_move(&chan->qh->qh_list_entry,
|
||||
list_move_tail(&chan->qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
|
||||
/*
|
||||
|
|
@ -954,14 +930,6 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
|
|||
|
||||
frame_desc->actual_length += len;
|
||||
|
||||
if (chan->align_buf) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
|
||||
memcpy(qtd->urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
|
||||
}
|
||||
|
||||
qtd->isoc_split_offset += len;
|
||||
|
||||
if (frame_desc->actual_length >= frame_desc->length) {
|
||||
|
|
@ -1184,19 +1152,6 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
|
|||
xfer_length = urb->length - urb->actual_length;
|
||||
}
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length && chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + urb->actual_length,
|
||||
chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
}
|
||||
|
||||
urb->actual_length += xfer_length;
|
||||
|
||||
hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
|
||||
|
|
@ -1416,14 +1371,50 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
|
|||
|
||||
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
|
||||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
|
||||
int frnum = dwc2_hcd_get_frame_number(hsotg);
|
||||
struct dwc2_qh *qh = chan->qh;
|
||||
bool past_end;
|
||||
|
||||
if (hsotg->core_params->uframe_sched <= 0) {
|
||||
int frnum = dwc2_hcd_get_frame_number(hsotg);
|
||||
|
||||
/* Don't have num_hs_transfers; simple logic */
|
||||
past_end = dwc2_full_frame_num(frnum) !=
|
||||
dwc2_full_frame_num(qh->next_active_frame);
|
||||
} else {
|
||||
int end_frnum;
|
||||
|
||||
if (dwc2_full_frame_num(frnum) !=
|
||||
dwc2_full_frame_num(chan->qh->sched_frame)) {
|
||||
/*
|
||||
* No longer in the same full speed frame.
|
||||
* Treat this as a transaction error.
|
||||
*/
|
||||
* Figure out the end frame based on schedule.
|
||||
*
|
||||
* We don't want to go on trying again and again
|
||||
* forever. Let's stop when we've done all the
|
||||
* transfers that were scheduled.
|
||||
*
|
||||
* We're going to be comparing start_active_frame
|
||||
* and next_active_frame, both of which are 1
|
||||
* before the time the packet goes on the wire,
|
||||
* so that cancels out. Basically if had 1
|
||||
* transfer and we saw 1 NYET then we're done.
|
||||
* We're getting a NYET here so if next >=
|
||||
* (start + num_transfers) we're done. The
|
||||
* complexity is that for all but ISOC_OUT we
|
||||
* skip one slot.
|
||||
*/
|
||||
end_frnum = dwc2_frame_num_inc(
|
||||
qh->start_active_frame,
|
||||
qh->num_hs_transfers);
|
||||
|
||||
if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
|
||||
qh->ep_is_in)
|
||||
end_frnum =
|
||||
dwc2_frame_num_inc(end_frnum, 1);
|
||||
|
||||
past_end = dwc2_frame_num_le(
|
||||
end_frnum, qh->next_active_frame);
|
||||
}
|
||||
|
||||
if (past_end) {
|
||||
/* Treat this as a transaction error. */
|
||||
#if 0
|
||||
/*
|
||||
* Todo: Fix system performance so this can
|
||||
|
|
@ -2008,6 +1999,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
|
|||
}
|
||||
|
||||
dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
|
||||
|
||||
/*
|
||||
* If we got an interrupt after someone called
|
||||
* dwc2_hcd_endpoint_disable() we don't want to crash below
|
||||
*/
|
||||
if (!chan->qh) {
|
||||
dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
chan->hcint = hcint;
|
||||
hcint &= hcintmsk;
|
||||
|
||||
|
|
@ -2130,6 +2131,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
|
|||
{
|
||||
u32 haint;
|
||||
int i;
|
||||
struct dwc2_host_chan *chan, *chan_tmp;
|
||||
|
||||
haint = dwc2_readl(hsotg->regs + HAINT);
|
||||
if (dbg_perio()) {
|
||||
|
|
@ -2138,6 +2140,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
|
|||
dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
|
||||
}
|
||||
|
||||
/*
|
||||
* According to USB 2.0 spec section 11.18.8, a host must
|
||||
* issue complete-split transactions in a microframe for a
|
||||
* set of full-/low-speed endpoints in the same relative
|
||||
* order as the start-splits were issued in a microframe for.
|
||||
*/
|
||||
list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
|
||||
split_order_list_entry) {
|
||||
int hc_num = chan->hc_num;
|
||||
|
||||
if (haint & (1 << hc_num)) {
|
||||
dwc2_hc_n_intr(hsotg, hc_num);
|
||||
haint &= ~(1 << hc_num);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < hsotg->core_params->host_channels; i++) {
|
||||
if (haint & (1 << i))
|
||||
dwc2_hc_n_intr(hsotg, i);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -126,10 +126,10 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.speed = -1,
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
.host_rx_fifo_size = 520, /* 520 DWORDs */
|
||||
.host_rx_fifo_size = 525, /* 525 DWORDs */
|
||||
.host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
|
||||
.host_perio_tx_fifo_size = 256, /* 256 DWORDs */
|
||||
.max_transfer_size = 65535,
|
||||
.max_transfer_size = -1,
|
||||
.max_packet_count = -1,
|
||||
.host_channels = -1,
|
||||
.phy_type = -1,
|
||||
|
|
@ -149,6 +149,38 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.hibernation = -1,
|
||||
};
|
||||
|
||||
static const struct dwc2_core_params params_ltq = {
|
||||
.otg_cap = 2, /* non-HNP/non-SRP */
|
||||
.otg_ver = -1,
|
||||
.dma_enable = -1,
|
||||
.dma_desc_enable = -1,
|
||||
.dma_desc_fs_enable = -1,
|
||||
.speed = -1,
|
||||
.enable_dynamic_fifo = -1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
.host_rx_fifo_size = 288, /* 288 DWORDs */
|
||||
.host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
|
||||
.host_perio_tx_fifo_size = 96, /* 96 DWORDs */
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = 511,
|
||||
.host_channels = -1,
|
||||
.phy_type = -1,
|
||||
.phy_utmi_width = -1,
|
||||
.phy_ulpi_ddr = -1,
|
||||
.phy_ulpi_ext_vbus = -1,
|
||||
.i2c_enable = -1,
|
||||
.ulpi_fs_ls = -1,
|
||||
.host_support_fs_ls_low_power = -1,
|
||||
.host_ls_low_power_phy_clk = -1,
|
||||
.ts_dline = -1,
|
||||
.reload_ctl = -1,
|
||||
.ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT,
|
||||
.uframe_sched = -1,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the dr_mode against the module configuration and hardware
|
||||
* capabilities.
|
||||
|
|
@ -428,6 +460,8 @@ static const struct of_device_id dwc2_of_match_table[] = {
|
|||
{ .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 },
|
||||
{ .compatible = "hisilicon,hi6220-usb", .data = ¶ms_hi6220 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 },
|
||||
{ .compatible = "lantiq,arx100-usb", .data = ¶ms_ltq },
|
||||
{ .compatible = "lantiq,xrx200-usb", .data = ¶ms_ltq },
|
||||
{ .compatible = "snps,dwc2", .data = NULL },
|
||||
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -962,10 +962,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
fladj = pdata->fladj_value;
|
||||
}
|
||||
|
||||
/* default to superspeed if no maximum_speed passed */
|
||||
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
|
|
@ -1016,6 +1012,33 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
/* Check the maximum_speed parameter */
|
||||
switch (dwc->maximum_speed) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid maximum_speed parameter %d\n",
|
||||
dwc->maximum_speed);
|
||||
/* fall through */
|
||||
case USB_SPEED_UNKNOWN:
|
||||
/* default to superspeed */
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
/*
|
||||
* default to superspeed plus if we are capable.
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) &&
|
||||
(DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
|
||||
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Adjust Frame Length */
|
||||
dwc3_frame_length_adjustment(dwc, fladj);
|
||||
|
||||
|
|
|
|||
|
|
@ -223,7 +223,8 @@
|
|||
/* Global HWPARAMS3 Register */
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
|
||||
|
|
@ -249,6 +250,7 @@
|
|||
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
|
||||
|
||||
#define DWC3_DCFG_SPEED_MASK (7 << 0)
|
||||
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
|
||||
#define DWC3_DCFG_SUPERSPEED (4 << 0)
|
||||
#define DWC3_DCFG_HIGHSPEED (0 << 0)
|
||||
#define DWC3_DCFG_FULLSPEED2 (1 << 0)
|
||||
|
|
@ -339,6 +341,7 @@
|
|||
|
||||
#define DWC3_DSTS_CONNECTSPD (7 << 0)
|
||||
|
||||
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
|
||||
#define DWC3_DSTS_SUPERSPEED (4 << 0)
|
||||
#define DWC3_DSTS_HIGHSPEED (0 << 0)
|
||||
#define DWC3_DSTS_FULLSPEED2 (1 << 0)
|
||||
|
|
@ -1024,6 +1027,12 @@ struct dwc3_gadget_ep_cmd_params {
|
|||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
|
||||
|
||||
/* check whether we are on the DWC_usb31 core */
|
||||
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
|
||||
{
|
||||
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_host_init(struct dwc3 *dwc);
|
||||
void dwc3_host_exit(struct dwc3 *dwc);
|
||||
|
|
|
|||
|
|
@ -356,7 +356,8 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
|||
*/
|
||||
usb_status |= dwc->gadget.is_selfpowered;
|
||||
|
||||
if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
|
||||
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
|
||||
|
|
@ -426,7 +427,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
case USB_DEVICE_U1_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
|
@ -440,7 +442,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
case USB_DEVICE_U2_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
|
|
|||
|
|
@ -463,7 +463,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
|
||||
|
||||
/* Burst size is only needed in SuperSpeed mode */
|
||||
if (dwc->gadget.speed == USB_SPEED_SUPER) {
|
||||
if (dwc->gadget.speed >= USB_SPEED_SUPER) {
|
||||
u32 burst = dep->endpoint.maxburst - 1;
|
||||
|
||||
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
|
||||
|
|
@ -1441,7 +1441,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
if (speed == DWC3_DSTS_SUPERSPEED) {
|
||||
if ((speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
|
@ -1666,10 +1667,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
case USB_SPEED_HIGH:
|
||||
reg |= DWC3_DSTS_HIGHSPEED;
|
||||
break;
|
||||
case USB_SPEED_SUPER: /* FALLTHROUGH */
|
||||
case USB_SPEED_UNKNOWN: /* FALTHROUGH */
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
reg |= DWC3_DSTS_SUPERSPEED_PLUS;
|
||||
break;
|
||||
default:
|
||||
reg |= DWC3_DSTS_SUPERSPEED;
|
||||
dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
|
||||
dwc->maximum_speed);
|
||||
/* fall through */
|
||||
case USB_SPEED_SUPER:
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
|
@ -2340,7 +2347,8 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
|
|||
* this. Maybe it becomes part of the power saving plan.
|
||||
*/
|
||||
|
||||
if (speed != DWC3_DSTS_SUPERSPEED)
|
||||
if ((speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(speed != DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
|
@ -2369,6 +2377,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
dwc3_update_ram_clk_sel(dwc, speed);
|
||||
|
||||
switch (speed) {
|
||||
case DWC3_DCFG_SUPERSPEED_PLUS:
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
dwc->gadget.ep0->maxpacket = 512;
|
||||
dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
|
||||
break;
|
||||
case DWC3_DCFG_SUPERSPEED:
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have an issue which
|
||||
|
|
@ -2410,8 +2423,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
|
||||
/* Enable USB2 LPM Capability */
|
||||
|
||||
if ((dwc->revision > DWC3_REVISION_194A)
|
||||
&& (speed != DWC3_DCFG_SUPERSPEED)) {
|
||||
if ((dwc->revision > DWC3_REVISION_194A) &&
|
||||
(speed != DWC3_DCFG_SUPERSPEED) &&
|
||||
(speed != DWC3_DCFG_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,36 @@ static struct usb_gadget_strings **get_containers_gs(
|
|||
return (struct usb_gadget_strings **)uc->stash;
|
||||
}
|
||||
|
||||
/**
|
||||
* function_descriptors() - get function descriptors for speed
|
||||
* @f: the function
|
||||
* @speed: the speed
|
||||
*
|
||||
* Returns the descriptors or NULL if not set.
|
||||
*/
|
||||
static struct usb_descriptor_header **
|
||||
function_descriptors(struct usb_function *f,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
struct usb_descriptor_header **descriptors;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
descriptors = f->ssp_descriptors;
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
break;
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* next_ep_desc() - advance to the next EP descriptor
|
||||
* @t: currect pointer within descriptor array
|
||||
|
|
@ -118,6 +148,13 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||
|
||||
/* select desired speed */
|
||||
switch (g->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if (gadget_is_superspeed_plus(g)) {
|
||||
speed_desc = f->ssp_descriptors;
|
||||
want_comp_desc = 1;
|
||||
break;
|
||||
}
|
||||
/* else: Fall trough */
|
||||
case USB_SPEED_SUPER:
|
||||
if (gadget_is_superspeed(g)) {
|
||||
speed_desc = f->ss_descriptors;
|
||||
|
|
@ -161,7 +198,7 @@ ep_found:
|
|||
(comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
|
||||
return -EIO;
|
||||
_ep->comp_desc = comp_desc;
|
||||
if (g->speed == USB_SPEED_SUPER) {
|
||||
if (g->speed >= USB_SPEED_SUPER) {
|
||||
switch (usb_endpoint_type(_ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
/* mult: bits 1:0 of bmAttributes */
|
||||
|
|
@ -237,6 +274,8 @@ int usb_add_function(struct usb_configuration *config,
|
|||
config->highspeed = true;
|
||||
if (!config->superspeed && function->ss_descriptors)
|
||||
config->superspeed = true;
|
||||
if (!config->superspeed_plus && function->ssp_descriptors)
|
||||
config->superspeed_plus = true;
|
||||
|
||||
done:
|
||||
if (value)
|
||||
|
|
@ -417,17 +456,7 @@ static int config_buf(struct usb_configuration *config,
|
|||
list_for_each_entry(f, &config->functions, list) {
|
||||
struct usb_descriptor_header **descriptors;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
break;
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
|
||||
descriptors = function_descriptors(f, speed);
|
||||
if (!descriptors)
|
||||
continue;
|
||||
status = usb_descriptor_fillbuf(next, len,
|
||||
|
|
@ -451,7 +480,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||
u8 type = w_value >> 8;
|
||||
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
if (gadget->speed == USB_SPEED_SUPER)
|
||||
if (gadget->speed >= USB_SPEED_SUPER)
|
||||
speed = gadget->speed;
|
||||
else if (gadget_is_dualspeed(gadget)) {
|
||||
int hs = 0;
|
||||
|
|
@ -482,6 +511,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||
check_config:
|
||||
/* ignore configs that won't work at this speed */
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if (!c->superspeed_plus)
|
||||
continue;
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
if (!c->superspeed)
|
||||
continue;
|
||||
|
|
@ -509,18 +542,24 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
|||
unsigned count = 0;
|
||||
int hs = 0;
|
||||
int ss = 0;
|
||||
int ssp = 0;
|
||||
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
if (gadget->speed == USB_SPEED_HIGH)
|
||||
hs = 1;
|
||||
if (gadget->speed == USB_SPEED_SUPER)
|
||||
ss = 1;
|
||||
if (gadget->speed == USB_SPEED_SUPER_PLUS)
|
||||
ssp = 1;
|
||||
if (type == USB_DT_DEVICE_QUALIFIER)
|
||||
hs = !hs;
|
||||
}
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
/* ignore configs that won't work at this speed */
|
||||
if (ss) {
|
||||
if (ssp) {
|
||||
if (!c->superspeed_plus)
|
||||
continue;
|
||||
} else if (ss) {
|
||||
if (!c->superspeed)
|
||||
continue;
|
||||
} else if (hs) {
|
||||
|
|
@ -597,6 +636,48 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
|||
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
|
||||
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
|
||||
|
||||
/* The SuperSpeedPlus USB Device Capability descriptor */
|
||||
if (gadget_is_superspeed_plus(cdev->gadget)) {
|
||||
struct usb_ssp_cap_descriptor *ssp_cap;
|
||||
|
||||
ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
|
||||
bos->bNumDeviceCaps++;
|
||||
|
||||
/*
|
||||
* Report typical values.
|
||||
*/
|
||||
|
||||
le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1));
|
||||
ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1);
|
||||
ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||
ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE;
|
||||
|
||||
/* SSAC = 1 (2 attributes) */
|
||||
ssp_cap->bmAttributes = cpu_to_le32(1);
|
||||
|
||||
/* Min RX/TX Lane Count = 1 */
|
||||
ssp_cap->wFunctionalitySupport = (1 << 8) | (1 << 12);
|
||||
|
||||
/*
|
||||
* bmSublinkSpeedAttr[0]:
|
||||
* ST = Symmetric, RX
|
||||
* LSE = 3 (Gbps)
|
||||
* LP = 1 (SuperSpeedPlus)
|
||||
* LSM = 10 (10 Gbps)
|
||||
*/
|
||||
ssp_cap->bmSublinkSpeedAttr[0] =
|
||||
(3 << 4) | (1 << 14) | (0xa << 16);
|
||||
/*
|
||||
* bmSublinkSpeedAttr[1] =
|
||||
* ST = Symmetric, TX
|
||||
* LSE = 3 (Gbps)
|
||||
* LP = 1 (SuperSpeedPlus)
|
||||
* LSM = 10 (10 Gbps)
|
||||
*/
|
||||
ssp_cap->bmSublinkSpeedAttr[1] =
|
||||
(3 << 4) | (1 << 14) | (0xa << 16) | (1 << 7);
|
||||
}
|
||||
|
||||
return le16_to_cpu(bos->wTotalLength);
|
||||
}
|
||||
|
||||
|
|
@ -690,16 +771,7 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||
* function's setup callback instead of the current
|
||||
* configuration's setup callback.
|
||||
*/
|
||||
switch (gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
break;
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
descriptors = function_descriptors(f, gadget->speed);
|
||||
|
||||
for (; *descriptors; ++descriptors) {
|
||||
struct usb_endpoint_descriptor *ep;
|
||||
|
|
@ -819,8 +891,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
|||
} else {
|
||||
unsigned i;
|
||||
|
||||
DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
|
||||
DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
|
||||
config->bConfigurationValue, config,
|
||||
config->superspeed_plus ? " superplus" : "",
|
||||
config->superspeed ? " super" : "",
|
||||
config->highspeed ? " high" : "",
|
||||
config->fullspeed
|
||||
|
|
@ -1499,7 +1572,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
cdev->gadget->ep0->maxpacket;
|
||||
if (gadget_is_superspeed(gadget)) {
|
||||
if (gadget->speed >= USB_SPEED_SUPER) {
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0300);
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0310);
|
||||
cdev->desc.bMaxPacketSize0 = 9;
|
||||
} else {
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
|
||||
|
|
@ -1634,15 +1707,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
*((u8 *)req->buf) = value;
|
||||
value = min(w_length, (u16) 1);
|
||||
break;
|
||||
|
||||
/*
|
||||
* USB 3.0 additions:
|
||||
* Function driver should handle get_status request. If such cb
|
||||
* wasn't supplied we respond with default value = 0
|
||||
* Note: function driver should supply such cb only for the first
|
||||
* interface of the function
|
||||
*/
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
|
||||
(w_index == OTG_STS_SELECTOR)) {
|
||||
if (ctrl->bRequestType != (USB_DIR_IN |
|
||||
USB_RECIP_DEVICE))
|
||||
goto unknown;
|
||||
*((u8 *)req->buf) = gadget->host_request_flag;
|
||||
value = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* USB 3.0 additions:
|
||||
* Function driver should handle get_status request. If such cb
|
||||
* wasn't supplied we respond with default value = 0
|
||||
* Note: function driver should supply such cb only for the
|
||||
* first interface of the function
|
||||
*/
|
||||
if (!gadget_is_superspeed(gadget))
|
||||
goto unknown;
|
||||
if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@ EXPORT_SYMBOL_GPL(usb_copy_descriptors);
|
|||
int usb_assign_descriptors(struct usb_function *f,
|
||||
struct usb_descriptor_header **fs,
|
||||
struct usb_descriptor_header **hs,
|
||||
struct usb_descriptor_header **ss)
|
||||
struct usb_descriptor_header **ss,
|
||||
struct usb_descriptor_header **ssp)
|
||||
{
|
||||
struct usb_gadget *g = f->config->cdev->gadget;
|
||||
|
||||
|
|
@ -182,6 +183,11 @@ int usb_assign_descriptors(struct usb_function *f,
|
|||
if (!f->ss_descriptors)
|
||||
goto err;
|
||||
}
|
||||
if (ssp && gadget_is_superspeed_plus(g)) {
|
||||
f->ssp_descriptors = usb_copy_descriptors(ssp);
|
||||
if (!f->ssp_descriptors)
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
usb_free_all_descriptors(f);
|
||||
|
|
@ -194,6 +200,7 @@ void usb_free_all_descriptors(struct usb_function *f)
|
|||
usb_free_descriptors(f->fs_descriptors);
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
usb_free_descriptors(f->ssp_descriptors);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_free_all_descriptors);
|
||||
|
||||
|
|
|
|||
|
|
@ -1229,6 +1229,7 @@ static void purge_configs_funcs(struct gadget_info *gi)
|
|||
}
|
||||
c->next_interface_id = 0;
|
||||
memset(c->interface, 0, sizeof(c->interface));
|
||||
c->superspeed_plus = 0;
|
||||
c->superspeed = 0;
|
||||
c->highspeed = 0;
|
||||
c->fullspeed = 0;
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
|
||||
acm_ss_function);
|
||||
acm_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
@ -777,10 +777,10 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
|
|||
return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_RO(f_acm_port_, num);
|
||||
CONFIGFS_ATTR_RO(f_acm_, port_num);
|
||||
|
||||
static struct configfs_attribute *acm_attrs[] = {
|
||||
&f_acm_port_attr_num,
|
||||
&f_acm_attr_port_num,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -786,7 +786,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fs_ecm_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
|
||||
ecm_ss_function);
|
||||
ecm_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
|
||||
eem_ss_function);
|
||||
eem_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -684,44 +684,38 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
|||
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
{
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
struct usb_request *req;
|
||||
struct ffs_ep *ep;
|
||||
char *data = NULL;
|
||||
ssize_t ret, data_len = -EINVAL;
|
||||
int halt;
|
||||
|
||||
/* Are we still active? */
|
||||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
||||
return -ENODEV;
|
||||
|
||||
/* Wait for endpoint to be enabled */
|
||||
ep = epfile->ep;
|
||||
if (!ep) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
goto error;
|
||||
}
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
|
||||
if (ret) {
|
||||
ret = -EINTR;
|
||||
goto error;
|
||||
}
|
||||
if (ret)
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
/* Do we halt? */
|
||||
halt = (!io_data->read == !epfile->in);
|
||||
if (halt && epfile->isoc) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (halt && epfile->isoc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Allocate & copy */
|
||||
if (!halt) {
|
||||
/*
|
||||
* if we _do_ wait above, the epfile->ffs->gadget might be NULL
|
||||
* before the waiting completes, so do not assign to 'gadget' earlier
|
||||
* before the waiting completes, so do not assign to 'gadget'
|
||||
* earlier
|
||||
*/
|
||||
struct usb_gadget *gadget = epfile->ffs->gadget;
|
||||
size_t copied;
|
||||
|
|
@ -763,17 +757,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
if (epfile->ep != ep) {
|
||||
/* In the meantime, endpoint got disabled or changed. */
|
||||
ret = -ESHUTDOWN;
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
} else if (halt) {
|
||||
/* Halt */
|
||||
if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
|
||||
usb_ep_set_halt(ep->ep);
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = -EBADMSG;
|
||||
} else {
|
||||
/* Fire the request */
|
||||
struct usb_request *req;
|
||||
|
||||
} else if (unlikely(data_len == -EINVAL)) {
|
||||
/*
|
||||
* Sanity Check: even though data_len can't be used
|
||||
* uninitialized at the time I write this comment, some
|
||||
|
|
@ -785,80 +774,80 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
* For such reason, we're adding this redundant sanity check
|
||||
* here.
|
||||
*/
|
||||
if (unlikely(data_len == -EINVAL)) {
|
||||
WARN(1, "%s: data_len == -EINVAL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
WARN(1, "%s: data_len == -EINVAL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
} else if (!io_data->aio) {
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
bool interrupted = false;
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_lock;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (unlikely(wait_for_completion_interruptible(&done))) {
|
||||
/*
|
||||
* To avoid race condition with ffs_epfile_io_complete,
|
||||
* dequeue the request first then check
|
||||
* status. usb_ep_dequeue API should guarantee no race
|
||||
* condition with req->complete callback.
|
||||
*/
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
interrupted = ep->status < 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX We may end up silently droping data here. Since data_len
|
||||
* (i.e. req->length) may be bigger than len (after being
|
||||
* rounded up to maxpacketsize), we may end up with more data
|
||||
* then user space has space for.
|
||||
*/
|
||||
ret = interrupted ? -EINTR : ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = copy_to_iter(data, ret, &io_data->data);
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
goto error_mutex;
|
||||
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) {
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
io_data->buf = data;
|
||||
io_data->ep = ep->ep;
|
||||
io_data->req = req;
|
||||
io_data->ffs = epfile->ffs;
|
||||
|
||||
req->context = io_data;
|
||||
req->complete = ffs_epfile_async_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret)) {
|
||||
usb_ep_free_request(ep->ep, req);
|
||||
goto error_lock;
|
||||
}
|
||||
|
||||
if (io_data->aio) {
|
||||
req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
|
||||
if (unlikely(!req))
|
||||
goto error_lock;
|
||||
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
io_data->buf = data;
|
||||
io_data->ep = ep->ep;
|
||||
io_data->req = req;
|
||||
io_data->ffs = epfile->ffs;
|
||||
|
||||
req->context = io_data;
|
||||
req->complete = ffs_epfile_async_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret)) {
|
||||
usb_ep_free_request(ep->ep, req);
|
||||
goto error_lock;
|
||||
}
|
||||
ret = -EIOCBQUEUED;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
} else {
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
/* nop */
|
||||
} else if (unlikely(
|
||||
wait_for_completion_interruptible(&done))) {
|
||||
ret = -EINTR;
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
} else {
|
||||
/*
|
||||
* XXX We may end up silently droping data
|
||||
* here. Since data_len (i.e. req->length) may
|
||||
* be bigger than len (after being rounded up
|
||||
* to maxpacketsize), we may end up with more
|
||||
* data then user space has space for.
|
||||
*/
|
||||
ret = ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = copy_to_iter(data, ret, &io_data->data);
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
}
|
||||
kfree(data);
|
||||
}
|
||||
ret = -EIOCBQUEUED;
|
||||
/*
|
||||
* Do not kfree the buffer in this function. It will be freed
|
||||
* by ffs_user_copy_worker.
|
||||
*/
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&epfile->mutex);
|
||||
return ret;
|
||||
|
||||
error_lock:
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
error_mutex:
|
||||
mutex_unlock(&epfile->mutex);
|
||||
error:
|
||||
kfree(data);
|
||||
|
|
|
|||
|
|
@ -646,7 +646,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
hidg_fs_out_ep_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, hidg_fs_descriptors,
|
||||
hidg_hs_descriptors, NULL);
|
||||
hidg_hs_descriptors, NULL, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ autoconf_fail:
|
|||
ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
|
||||
ss_loopback_descs);
|
||||
ss_loopback_descs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -3093,7 +3093,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
|
||||
|
||||
ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
|
||||
fsg_ss_function);
|
||||
fsg_ss_function, fsg_ss_function);
|
||||
if (ret)
|
||||
goto autoconf_fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ static const char f_midi_longname[] = "MIDI Gadget";
|
|||
* USB <- IN endpoint <- rawmidi
|
||||
*/
|
||||
struct gmidi_in_port {
|
||||
struct f_midi *midi;
|
||||
struct snd_rawmidi_substream *substream;
|
||||
int active;
|
||||
uint8_t cable;
|
||||
uint8_t state;
|
||||
|
|
@ -78,9 +78,7 @@ struct f_midi {
|
|||
struct snd_rawmidi *rmidi;
|
||||
u8 ms_id;
|
||||
|
||||
struct snd_rawmidi_substream *in_substream[MAX_PORTS];
|
||||
struct snd_rawmidi_substream *out_substream[MAX_PORTS];
|
||||
struct gmidi_in_port *in_port[MAX_PORTS];
|
||||
|
||||
unsigned long out_triggered;
|
||||
struct tasklet_struct tasklet;
|
||||
|
|
@ -92,6 +90,8 @@ struct f_midi {
|
|||
/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
|
||||
DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
|
||||
unsigned int in_last_port;
|
||||
|
||||
struct gmidi_in_port in_ports_array[/* in_ports */];
|
||||
};
|
||||
|
||||
static inline struct f_midi *func_to_midi(struct usb_function *f)
|
||||
|
|
@ -518,98 +518,95 @@ static void f_midi_drop_out_substreams(struct f_midi *midi)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
for (i = 0; i < midi->in_ports; i++) {
|
||||
struct gmidi_in_port *port = midi->in_ports_array + i;
|
||||
struct snd_rawmidi_substream *substream = port->substream;
|
||||
if (port->active && substream)
|
||||
snd_rawmidi_drop_output(substream);
|
||||
}
|
||||
}
|
||||
|
||||
if (!port)
|
||||
break;
|
||||
static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
|
||||
{
|
||||
struct usb_request *req = NULL;
|
||||
unsigned int len, i;
|
||||
bool active = false;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We peek the request in order to reuse it if it fails to enqueue on
|
||||
* its endpoint
|
||||
*/
|
||||
len = kfifo_peek(&midi->in_req_fifo, &req);
|
||||
if (len != 1) {
|
||||
ERROR(midi, "%s: Couldn't get usb request\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If buffer overrun, then we ignore this transmission.
|
||||
* IMPORTANT: This will cause the user-space rawmidi device to block
|
||||
* until a) usb requests have been completed or b) snd_rawmidi_write()
|
||||
* times out.
|
||||
*/
|
||||
if (req->length > 0)
|
||||
return 0;
|
||||
|
||||
for (i = midi->in_last_port; i < midi->in_ports; ++i) {
|
||||
struct gmidi_in_port *port = midi->in_ports_array + i;
|
||||
struct snd_rawmidi_substream *substream = port->substream;
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
snd_rawmidi_drop_output(substream);
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
|
||||
active = !!port->active;
|
||||
if (active)
|
||||
break;
|
||||
}
|
||||
midi->in_last_port = active ? i : 0;
|
||||
|
||||
if (req->length <= 0)
|
||||
goto done;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
ERROR(midi, "%s failed to queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
req->length = 0; /* Re-use request next time. */
|
||||
} else {
|
||||
/* Upon success, put request at the back of the queue. */
|
||||
kfifo_skip(&midi->in_req_fifo);
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
|
||||
done:
|
||||
return active;
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi)
|
||||
{
|
||||
struct usb_ep *ep = midi->in_ep;
|
||||
bool active;
|
||||
int ret;
|
||||
|
||||
/* We only care about USB requests if IN endpoint is enabled */
|
||||
if (!ep || !ep->enabled)
|
||||
goto drop_out;
|
||||
|
||||
do {
|
||||
struct usb_request *req = NULL;
|
||||
unsigned int len, i;
|
||||
|
||||
active = false;
|
||||
|
||||
/* We peek the request in order to reuse it if it fails
|
||||
* to enqueue on its endpoint */
|
||||
len = kfifo_peek(&midi->in_req_fifo, &req);
|
||||
if (len != 1) {
|
||||
ERROR(midi, "%s: Couldn't get usb request\n", __func__);
|
||||
ret = f_midi_do_transmit(midi, ep);
|
||||
if (ret < 0)
|
||||
goto drop_out;
|
||||
}
|
||||
|
||||
/* If buffer overrun, then we ignore this transmission.
|
||||
* IMPORTANT: This will cause the user-space rawmidi device to block until a) usb
|
||||
* requests have been completed or b) snd_rawmidi_write() times out. */
|
||||
if (req->length > 0)
|
||||
return;
|
||||
|
||||
for (i = midi->in_last_port; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
|
||||
if (!port) {
|
||||
/* Reset counter when we reach the last available port */
|
||||
midi->in_last_port = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
|
||||
active = !!port->active;
|
||||
/* Check if last port is still active, which means that
|
||||
* there is still data on that substream but this current
|
||||
* request run out of space. */
|
||||
if (active) {
|
||||
midi->in_last_port = i;
|
||||
/* There is no need to re-iterate though midi ports. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->length > 0) {
|
||||
int err;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
ERROR(midi, "%s failed to queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
req->length = 0; /* Re-use request next time. */
|
||||
} else {
|
||||
/* Upon success, put request at the back of the queue. */
|
||||
kfifo_skip(&midi->in_req_fifo);
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
}
|
||||
} while (active);
|
||||
} while (ret);
|
||||
|
||||
return;
|
||||
|
||||
|
|
@ -626,13 +623,15 @@ static void f_midi_in_tasklet(unsigned long data)
|
|||
static int f_midi_in_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct f_midi *midi = substream->rmidi->private_data;
|
||||
struct gmidi_in_port *port;
|
||||
|
||||
if (!midi->in_port[substream->number])
|
||||
if (substream->number >= midi->in_ports)
|
||||
return -EINVAL;
|
||||
|
||||
VDBG(midi, "%s()\n", __func__);
|
||||
midi->in_substream[substream->number] = substream;
|
||||
midi->in_port[substream->number]->state = STATE_UNKNOWN;
|
||||
port = midi->in_ports_array + substream->number;
|
||||
port->substream = substream;
|
||||
port->state = STATE_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -648,11 +647,11 @@ static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
|
|||
{
|
||||
struct f_midi *midi = substream->rmidi->private_data;
|
||||
|
||||
if (!midi->in_port[substream->number])
|
||||
if (substream->number >= midi->in_ports)
|
||||
return;
|
||||
|
||||
VDBG(midi, "%s() %d\n", __func__, up);
|
||||
midi->in_port[substream->number]->active = up;
|
||||
midi->in_ports_array[substream->number].active = up;
|
||||
if (up)
|
||||
tasklet_hi_schedule(&midi->tasklet);
|
||||
}
|
||||
|
|
@ -1128,14 +1127,11 @@ static void f_midi_free(struct usb_function *f)
|
|||
{
|
||||
struct f_midi *midi;
|
||||
struct f_midi_opts *opts;
|
||||
int i;
|
||||
|
||||
midi = func_to_midi(f);
|
||||
opts = container_of(f->fi, struct f_midi_opts, func_inst);
|
||||
kfree(midi->id);
|
||||
mutex_lock(&opts->lock);
|
||||
for (i = opts->in_ports - 1; i >= 0; --i)
|
||||
kfree(midi->in_port[i]);
|
||||
kfifo_free(&midi->in_req_fifo);
|
||||
kfree(midi);
|
||||
--opts->refcnt;
|
||||
|
|
@ -1163,7 +1159,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
struct f_midi *midi = NULL;
|
||||
struct f_midi_opts *opts;
|
||||
int status, i;
|
||||
|
||||
|
|
@ -1172,37 +1168,26 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
|||
mutex_lock(&opts->lock);
|
||||
/* sanity check */
|
||||
if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-EINVAL);
|
||||
status = -EINVAL;
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
midi = kzalloc(sizeof(*midi), GFP_KERNEL);
|
||||
midi = kzalloc(
|
||||
sizeof(*midi) + opts->in_ports * sizeof(*midi->in_ports_array),
|
||||
GFP_KERNEL);
|
||||
if (!midi) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
status = -ENOMEM;
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < opts->in_ports; i++) {
|
||||
struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
|
||||
if (!port) {
|
||||
status = -ENOMEM;
|
||||
mutex_unlock(&opts->lock);
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
port->midi = midi;
|
||||
port->active = 0;
|
||||
port->cable = i;
|
||||
midi->in_port[i] = port;
|
||||
}
|
||||
for (i = 0; i < opts->in_ports; i++)
|
||||
midi->in_ports_array[i].cable = i;
|
||||
|
||||
/* set up ALSA midi devices */
|
||||
midi->id = kstrdup(opts->id, GFP_KERNEL);
|
||||
if (opts->id && !midi->id) {
|
||||
status = -ENOMEM;
|
||||
mutex_unlock(&opts->lock);
|
||||
goto setup_fail;
|
||||
}
|
||||
midi->in_ports = opts->in_ports;
|
||||
|
|
@ -1229,8 +1214,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
|||
return &midi->func;
|
||||
|
||||
setup_fail:
|
||||
for (--i; i >= 0; i--)
|
||||
kfree(midi->in_port[i]);
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(midi);
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1432,7 +1432,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fs_ncm_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,8 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
obex_hs_ep_out_desc.bEndpointAddress =
|
||||
obex_fs_ep_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, fs_function, hs_function, NULL);
|
||||
status = usb_assign_descriptors(f, fs_function, hs_function, NULL,
|
||||
NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
/* Do not try to bind Phonet twice... */
|
||||
status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
if (status)
|
||||
goto err;
|
||||
|
||||
|
|
|
|||
|
|
@ -1051,7 +1051,7 @@ autoconf_fail:
|
|||
ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_printer_function,
|
||||
hs_printer_function, ss_printer_function);
|
||||
hs_printer_function, ss_printer_function, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -783,7 +783,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
|
||||
eth_ss_function);
|
||||
eth_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
|
||||
gser_ss_function);
|
||||
gser_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ no_iso:
|
|||
ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_source_sink_descs,
|
||||
hs_source_sink_descs, ss_source_sink_descs);
|
||||
hs_source_sink_descs, ss_source_sink_descs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fs_subset_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
|
||||
ss_eth_function);
|
||||
ss_eth_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -2098,7 +2098,7 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, uasp_fs_function_desc,
|
||||
uasp_hs_function_desc, uasp_ss_function_desc);
|
||||
uasp_hs_function_desc, uasp_ss_function_desc, NULL);
|
||||
if (ret)
|
||||
goto ep_fail;
|
||||
|
||||
|
|
|
|||
|
|
@ -721,7 +721,8 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
status = -ENOMEM;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL);
|
||||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
|
||||
NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1100,7 +1100,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
|
||||
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL);
|
||||
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
|||
|
|
@ -914,7 +914,7 @@ struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
|
|||
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
|
||||
params->resp_avail = resp_avail;
|
||||
params->v = v;
|
||||
INIT_LIST_HEAD(&(params->resp_queue));
|
||||
INIT_LIST_HEAD(¶ms->resp_queue);
|
||||
pr_debug("%s: configNr = %d\n", __func__, i);
|
||||
|
||||
return params;
|
||||
|
|
@ -1006,13 +1006,10 @@ EXPORT_SYMBOL_GPL(rndis_add_hdr);
|
|||
|
||||
void rndis_free_response(struct rndis_params *params, u8 *buf)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
struct list_head *act, *tmp;
|
||||
rndis_resp_t *r, *n;
|
||||
|
||||
list_for_each_safe(act, tmp, &(params->resp_queue))
|
||||
{
|
||||
r = list_entry(act, rndis_resp_t, list);
|
||||
if (r && r->buf == buf) {
|
||||
list_for_each_entry_safe(r, n, ¶ms->resp_queue, list) {
|
||||
if (r->buf == buf) {
|
||||
list_del(&r->list);
|
||||
kfree(r);
|
||||
}
|
||||
|
|
@ -1022,14 +1019,11 @@ EXPORT_SYMBOL_GPL(rndis_free_response);
|
|||
|
||||
u8 *rndis_get_next_response(struct rndis_params *params, u32 *length)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
struct list_head *act, *tmp;
|
||||
rndis_resp_t *r, *n;
|
||||
|
||||
if (!length) return NULL;
|
||||
|
||||
list_for_each_safe(act, tmp, &(params->resp_queue))
|
||||
{
|
||||
r = list_entry(act, rndis_resp_t, list);
|
||||
list_for_each_entry_safe(r, n, ¶ms->resp_queue, list) {
|
||||
if (!r->send) {
|
||||
r->send = 1;
|
||||
*length = r->length;
|
||||
|
|
@ -1053,7 +1047,7 @@ static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length)
|
|||
r->length = length;
|
||||
r->send = 0;
|
||||
|
||||
list_add_tail(&r->list, &(params->resp_queue));
|
||||
list_add_tail(&r->list, ¶ms->resp_queue);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1699,28 +1699,6 @@ static struct usb_gadget_driver gadgetfs_driver = {
|
|||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static void gadgetfs_nop(struct usb_gadget *arg) { }
|
||||
|
||||
static int gadgetfs_probe(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
CHIP = gadget->name;
|
||||
return -EISNAM;
|
||||
}
|
||||
|
||||
static struct usb_gadget_driver probe_driver = {
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.bind = gadgetfs_probe,
|
||||
.unbind = gadgetfs_nop,
|
||||
.setup = (void *)gadgetfs_nop,
|
||||
.disconnect = gadgetfs_nop,
|
||||
.driver = {
|
||||
.name = "nop",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* DEVICE INITIALIZATION
|
||||
*
|
||||
* fd = open ("/dev/gadget/$CHIP", O_RDWR)
|
||||
|
|
@ -1971,9 +1949,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
|
|||
if (the_device)
|
||||
return -ESRCH;
|
||||
|
||||
/* fake probe to determine $CHIP */
|
||||
CHIP = NULL;
|
||||
usb_gadget_probe_driver(&probe_driver);
|
||||
CHIP = usb_get_gadget_udc_name();
|
||||
if (!CHIP)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -2034,6 +2010,8 @@ gadgetfs_kill_sb (struct super_block *sb)
|
|||
put_dev (the_device);
|
||||
the_device = NULL;
|
||||
}
|
||||
kfree(CHIP);
|
||||
CHIP = NULL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ config USB_BCM63XX_UDC
|
|||
config USB_FSL_USB2
|
||||
tristate "Freescale Highspeed USB DR Peripheral Controller"
|
||||
depends on FSL_SOC || ARCH_MXC
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
help
|
||||
Some of Freescale PowerPC and i.MX processors have a High Speed
|
||||
Dual-Role(DR) USB controller, which supports device mode.
|
||||
|
|
@ -128,6 +127,7 @@ config USB_OMAP
|
|||
config USB_PXA25X
|
||||
tristate "PXA 25x or IXP 4xx"
|
||||
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Intel's PXA 25x series XScale ARM-5TE processors include
|
||||
an integrated full speed USB 1.1 device controller. The
|
||||
|
|
@ -176,7 +176,7 @@ config USB_RENESAS_USBHS_UDC
|
|||
|
||||
config USB_RENESAS_USB3
|
||||
tristate 'Renesas USB3.0 Peripheral controller'
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
Renesas USB3.0 Peripheral controller is a USB peripheral controller
|
||||
that supports super, high, and full speed USB 3.0 data transfers.
|
||||
|
|
@ -187,6 +187,7 @@ config USB_RENESAS_USB3
|
|||
|
||||
config USB_PXA27X
|
||||
tristate "PXA 27x"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Intel's PXA 27x series XScale ARM v5TE processors include
|
||||
an integrated full speed USB 1.1 device controller.
|
||||
|
|
@ -244,6 +245,7 @@ config USB_MV_U3D
|
|||
|
||||
config USB_M66592
|
||||
tristate "Renesas M66592 USB Peripheral Controller"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
M66592 is a discrete USB peripheral controller chip that
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
|
|
@ -287,6 +289,7 @@ config USB_FSL_QE
|
|||
dynamically linked module called "fsl_qe_udc".
|
||||
|
||||
config USB_NET2272
|
||||
depends on HAS_IOMEM
|
||||
tristate "PLX NET2272"
|
||||
help
|
||||
PLX NET2272 is a USB peripheral controller which supports
|
||||
|
|
|
|||
|
|
@ -581,8 +581,13 @@ err0:
|
|||
|
||||
void bdc_udc_exit(struct bdc *bdc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
bdc_ep_disable(bdc->bdc_ep_array[1]);
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
usb_del_gadget_udc(&bdc->gadget);
|
||||
bdc_free_ep(bdc);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@
|
|||
#endif
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/platform.h>
|
||||
|
||||
/*
|
||||
* USB device configuration structure
|
||||
|
|
@ -147,9 +146,7 @@ struct lpc32xx_udc {
|
|||
u32 io_p_size;
|
||||
void __iomem *udp_baseaddr;
|
||||
int udp_irq[4];
|
||||
struct clk *usb_pll_clk;
|
||||
struct clk *usb_slv_clk;
|
||||
struct clk *usb_otg_clk;
|
||||
|
||||
/* DMA support */
|
||||
u32 *udca_v_base;
|
||||
|
|
@ -210,16 +207,6 @@ static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g)
|
|||
|
||||
#define UDCA_BUFF_SIZE (128)
|
||||
|
||||
/* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will
|
||||
* be replaced with an inremap()ed pointer
|
||||
* */
|
||||
#define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64)
|
||||
|
||||
/* USB_CTRL bit defines */
|
||||
#define USB_SLAVE_HCLK_EN (1 << 24)
|
||||
#define USB_HOST_NEED_CLK_EN (1 << 21)
|
||||
#define USB_DEV_NEED_CLK_EN (1 << 22)
|
||||
|
||||
/**********************************************************************
|
||||
* USB device controller register offsets
|
||||
**********************************************************************/
|
||||
|
|
@ -639,9 +626,6 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc)
|
|||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
|
||||
|
||||
/* Enable usb_need_clk clock after transceiver is initialized */
|
||||
writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL);
|
||||
|
||||
dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n",
|
||||
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
|
||||
dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n",
|
||||
|
|
@ -980,31 +964,13 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
|
|||
return;
|
||||
|
||||
udc->clocked = 1;
|
||||
|
||||
/* 48MHz PLL up */
|
||||
clk_enable(udc->usb_pll_clk);
|
||||
|
||||
/* Enable the USB device clock */
|
||||
writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN,
|
||||
USB_CTRL);
|
||||
|
||||
clk_enable(udc->usb_otg_clk);
|
||||
clk_prepare_enable(udc->usb_slv_clk);
|
||||
} else {
|
||||
if (!udc->clocked)
|
||||
return;
|
||||
|
||||
udc->clocked = 0;
|
||||
|
||||
/* Never disable the USB_HCLK during normal operation */
|
||||
|
||||
/* 48MHz PLL dpwn */
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
|
||||
/* Disable the USB device clock */
|
||||
writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN,
|
||||
USB_CTRL);
|
||||
|
||||
clk_disable(udc->usb_otg_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3125,58 +3091,21 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
|||
goto io_map_fail;
|
||||
}
|
||||
|
||||
/* Enable AHB slave USB clock, needed for further USB clock control */
|
||||
writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL);
|
||||
|
||||
/* Get required clocks */
|
||||
udc->usb_pll_clk = clk_get(&pdev->dev, "ck_pll5");
|
||||
if (IS_ERR(udc->usb_pll_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB PLL\n");
|
||||
retval = PTR_ERR(udc->usb_pll_clk);
|
||||
goto pll_get_fail;
|
||||
}
|
||||
udc->usb_slv_clk = clk_get(&pdev->dev, "ck_usbd");
|
||||
/* Get USB device clock */
|
||||
udc->usb_slv_clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(udc->usb_slv_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB device clock\n");
|
||||
retval = PTR_ERR(udc->usb_slv_clk);
|
||||
goto usb_clk_get_fail;
|
||||
}
|
||||
udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
|
||||
if (IS_ERR(udc->usb_otg_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB otg clock\n");
|
||||
retval = PTR_ERR(udc->usb_otg_clk);
|
||||
goto usb_otg_clk_get_fail;
|
||||
}
|
||||
|
||||
/* Setup PLL clock to 48MHz */
|
||||
retval = clk_enable(udc->usb_pll_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB PLL\n");
|
||||
goto pll_enable_fail;
|
||||
}
|
||||
|
||||
retval = clk_set_rate(udc->usb_pll_clk, 48000);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to set USB clock rate\n");
|
||||
goto pll_set_fail;
|
||||
}
|
||||
|
||||
writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, USB_CTRL);
|
||||
|
||||
/* Enable USB device clock */
|
||||
retval = clk_enable(udc->usb_slv_clk);
|
||||
retval = clk_prepare_enable(udc->usb_slv_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB device clock\n");
|
||||
goto usb_clk_enable_fail;
|
||||
}
|
||||
|
||||
/* Enable USB OTG clock */
|
||||
retval = clk_enable(udc->usb_otg_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB otg clock\n");
|
||||
goto usb_otg_clk_enable_fail;
|
||||
}
|
||||
|
||||
/* Setup deferred workqueue data */
|
||||
udc->poweron = udc->pullup = 0;
|
||||
INIT_WORK(&udc->pullup_job, pullup_work);
|
||||
|
|
@ -3287,19 +3216,10 @@ dma_alloc_fail:
|
|||
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
|
||||
udc->udca_v_base, udc->udca_p_base);
|
||||
i2c_fail:
|
||||
clk_disable(udc->usb_otg_clk);
|
||||
usb_otg_clk_enable_fail:
|
||||
clk_disable(udc->usb_slv_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
usb_clk_enable_fail:
|
||||
pll_set_fail:
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
pll_enable_fail:
|
||||
clk_put(udc->usb_otg_clk);
|
||||
usb_otg_clk_get_fail:
|
||||
clk_put(udc->usb_slv_clk);
|
||||
usb_clk_get_fail:
|
||||
clk_put(udc->usb_pll_clk);
|
||||
pll_get_fail:
|
||||
iounmap(udc->udp_baseaddr);
|
||||
io_map_fail:
|
||||
release_mem_region(udc->io_p_start, udc->io_p_size);
|
||||
|
|
@ -3336,12 +3256,9 @@ static int lpc32xx_udc_remove(struct platform_device *pdev)
|
|||
free_irq(udc->udp_irq[IRQ_USB_HP], udc);
|
||||
free_irq(udc->udp_irq[IRQ_USB_LP], udc);
|
||||
|
||||
clk_disable(udc->usb_otg_clk);
|
||||
clk_put(udc->usb_otg_clk);
|
||||
clk_disable(udc->usb_slv_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
clk_put(udc->usb_slv_clk);
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
clk_put(udc->usb_pll_clk);
|
||||
|
||||
iounmap(udc->udp_baseaddr);
|
||||
release_mem_region(udc->io_p_start, udc->io_p_size);
|
||||
kfree(udc);
|
||||
|
|
@ -3367,7 +3284,7 @@ static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|||
udc->clocked = 1;
|
||||
|
||||
/* Kill global USB clock */
|
||||
clk_disable(udc->usb_slv_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -3379,7 +3296,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev)
|
|||
|
||||
if (udc->clocked) {
|
||||
/* Enable global USB clock */
|
||||
clk_enable(udc->usb_slv_clk);
|
||||
clk_prepare_enable(udc->usb_slv_clk);
|
||||
|
||||
/* Enable clocking */
|
||||
udc_clk_set(udc, 1);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -56,9 +56,9 @@ struct pxa25x_ep {
|
|||
* UDDR = UDC Endpoint Data Register (the fifo)
|
||||
* DRCM = DMA Request Channel Map
|
||||
*/
|
||||
volatile u32 *reg_udccs;
|
||||
volatile u32 *reg_ubcr;
|
||||
volatile u32 *reg_uddr;
|
||||
u32 regoff_udccs;
|
||||
u32 regoff_ubcr;
|
||||
u32 regoff_uddr;
|
||||
};
|
||||
|
||||
struct pxa25x_request {
|
||||
|
|
@ -125,6 +125,7 @@ struct pxa25x_udc {
|
|||
#ifdef CONFIG_USB_GADGET_DEBUG_FS
|
||||
struct dentry *debugfs_udc;
|
||||
#endif
|
||||
void __iomem *regs;
|
||||
};
|
||||
#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget))
|
||||
|
||||
|
|
@ -197,6 +198,8 @@ dump_udccs0(const char *label)
|
|||
(udccs0 & UDCCS0_OPR) ? " opr" : "");
|
||||
}
|
||||
|
||||
static inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *);
|
||||
|
||||
static void __maybe_unused
|
||||
dump_state(struct pxa25x_udc *dev)
|
||||
{
|
||||
|
|
@ -228,7 +231,7 @@ dump_state(struct pxa25x_udc *dev)
|
|||
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
|
||||
if (dev->ep[i].ep.desc == NULL)
|
||||
continue;
|
||||
DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
|
||||
DMSG ("udccs%d = %02x\n", i, udc_ep_get_UDCCS(&dev->ep[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -442,6 +442,36 @@ err1:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
|
||||
|
||||
/**
|
||||
* usb_get_gadget_udc_name - get the name of the first UDC controller
|
||||
* This functions returns the name of the first UDC controller in the system.
|
||||
* Please note that this interface is usefull only for legacy drivers which
|
||||
* assume that there is only one UDC controller in the system and they need to
|
||||
* get its name before initialization. There is no guarantee that the UDC
|
||||
* of the returned name will be still available, when gadget driver registers
|
||||
* itself.
|
||||
*
|
||||
* Returns pointer to string with UDC controller name on success, NULL
|
||||
* otherwise. Caller should kfree() returned string.
|
||||
*/
|
||||
char *usb_get_gadget_udc_name(void)
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
char *name = NULL;
|
||||
|
||||
/* For now we take the first available UDC */
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
if (!udc->driver) {
|
||||
name = kstrdup(udc->gadget->name, GFP_KERNEL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
return name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name);
|
||||
|
||||
/**
|
||||
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
|
||||
* @parent: the parent device to this udc. Usually the controller
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ comment "USB Host Controller Drivers"
|
|||
|
||||
config USB_C67X00_HCD
|
||||
tristate "Cypress C67x00 HCD support"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
|
||||
host/peripheral/OTG USB controllers.
|
||||
|
|
@ -17,6 +18,7 @@ config USB_C67X00_HCD
|
|||
|
||||
config USB_XHCI_HCD
|
||||
tristate "xHCI HCD (USB 3.0) support"
|
||||
depends on HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
|
||||
"SuperSpeed" host controller hardware.
|
||||
|
|
@ -53,6 +55,7 @@ config USB_XHCI_MTK
|
|||
config USB_XHCI_MVEBU
|
||||
tristate "xHCI support for Marvell Armada 375/38x"
|
||||
select USB_XHCI_PLATFORM
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
|
|
@ -61,7 +64,7 @@ config USB_XHCI_MVEBU
|
|||
config USB_XHCI_RCAR
|
||||
tristate "xHCI support for Renesas R-Car SoCs"
|
||||
select USB_XHCI_PLATFORM
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
found in Renesas R-Car ARM SoCs.
|
||||
|
|
@ -70,6 +73,7 @@ endif # USB_XHCI_HCD
|
|||
|
||||
config USB_EHCI_HCD
|
||||
tristate "EHCI HCD (USB 2.0) support"
|
||||
depends on HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
|
||||
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
|
||||
|
|
@ -121,9 +125,6 @@ config USB_EHCI_TT_NEWSCHED
|
|||
|
||||
If unsure, say Y.
|
||||
|
||||
config USB_FSL_MPH_DR_OF
|
||||
tristate
|
||||
|
||||
if USB_EHCI_HCD
|
||||
|
||||
config USB_EHCI_PCI
|
||||
|
|
@ -156,7 +157,6 @@ config USB_EHCI_FSL
|
|||
tristate "Support for Freescale PPC on-chip EHCI USB controller"
|
||||
depends on FSL_SOC
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
---help---
|
||||
Variation of ARC USB block used in some Freescale chips.
|
||||
|
||||
|
|
@ -328,6 +328,7 @@ endif # USB_EHCI_HCD
|
|||
|
||||
config USB_OXU210HP_HCD
|
||||
tristate "OXU210HP HCD support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
The OXU210HP is an USB host/OTG/device controller. Enable this
|
||||
option if your board has this chip. If unsure, say N.
|
||||
|
|
@ -340,6 +341,7 @@ config USB_OXU210HP_HCD
|
|||
|
||||
config USB_ISP116X_HCD
|
||||
tristate "ISP116X HCD support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
The ISP1160 and ISP1161 chips are USB host controllers. Enable this
|
||||
option if your board has this chip. If unsure, say N.
|
||||
|
|
@ -351,6 +353,7 @@ config USB_ISP116X_HCD
|
|||
|
||||
config USB_ISP1362_HCD
|
||||
tristate "ISP1362 HCD support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
Supports the Philips ISP1362 chip as a host controller
|
||||
|
||||
|
|
@ -361,7 +364,7 @@ config USB_ISP1362_HCD
|
|||
|
||||
config USB_FOTG210_HCD
|
||||
tristate "FOTG210 HCD support"
|
||||
depends on USB
|
||||
depends on USB && HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
Faraday FOTG210 is an OTG controller which can be configured as
|
||||
an USB2.0 host. It is designed to meet USB2.0 EHCI specification
|
||||
|
|
@ -383,6 +386,7 @@ config USB_MAX3421_HCD
|
|||
|
||||
config USB_OHCI_HCD
|
||||
tristate "OHCI HCD (USB 1.1) support"
|
||||
depends on HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
The Open Host Controller Interface (OHCI) is a standard for accessing
|
||||
USB 1.1 host controller hardware. It does more in hardware than Intel's
|
||||
|
|
@ -668,6 +672,7 @@ config USB_U132_HCD
|
|||
|
||||
config USB_SL811_HCD
|
||||
tristate "SL811HS HCD support"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
The SL811HS is a single-port USB controller that supports either
|
||||
host side or peripheral side roles. Enable this option if your
|
||||
|
|
@ -699,6 +704,7 @@ config USB_SL811_CS
|
|||
|
||||
config USB_R8A66597_HCD
|
||||
tristate "R8A66597 HCD support"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
The R8A66597 is a USB 2.0 host and peripheral controller.
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
|
|||
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
|
||||
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
|
||||
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
|
||||
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
|
||||
obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
|
||||
obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ MODULE_DESCRIPTION("Common USB driver for BCMA Bus");
|
|||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct bcma_hcd_device {
|
||||
struct bcma_device *core;
|
||||
struct platform_device *ehci_dev;
|
||||
struct platform_device *ohci_dev;
|
||||
struct gpio_desc *gpio_desc;
|
||||
|
|
@ -244,7 +245,10 @@ static const struct usb_ehci_pdata ehci_pdata = {
|
|||
static const struct usb_ohci_pdata ohci_pdata = {
|
||||
};
|
||||
|
||||
static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr)
|
||||
static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev,
|
||||
const char *name, u32 addr,
|
||||
const void *data,
|
||||
size_t size)
|
||||
{
|
||||
struct platform_device *hci_dev;
|
||||
struct resource hci_res[2];
|
||||
|
|
@ -259,8 +263,7 @@ static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, boo
|
|||
hci_res[1].start = dev->irq;
|
||||
hci_res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
|
||||
"ehci-platform" , 0);
|
||||
hci_dev = platform_device_alloc(name, 0);
|
||||
if (!hci_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
@ -271,12 +274,8 @@ static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, boo
|
|||
ARRAY_SIZE(hci_res));
|
||||
if (ret)
|
||||
goto err_alloc;
|
||||
if (ohci)
|
||||
ret = platform_device_add_data(hci_dev, &ohci_pdata,
|
||||
sizeof(ohci_pdata));
|
||||
else
|
||||
ret = platform_device_add_data(hci_dev, &ehci_pdata,
|
||||
sizeof(ehci_pdata));
|
||||
if (data)
|
||||
ret = platform_device_add_data(hci_dev, data, size);
|
||||
if (ret)
|
||||
goto err_alloc;
|
||||
ret = platform_device_add(hci_dev);
|
||||
|
|
@ -290,31 +289,16 @@ err_alloc:
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int bcma_hcd_probe(struct bcma_device *dev)
|
||||
static int bcma_hcd_usb20_init(struct bcma_hcd_device *usb_dev)
|
||||
{
|
||||
int err;
|
||||
struct bcma_device *dev = usb_dev->core;
|
||||
struct bcma_chipinfo *chipinfo = &dev->bus->chipinfo;
|
||||
u32 ohci_addr;
|
||||
struct bcma_hcd_device *usb_dev;
|
||||
struct bcma_chipinfo *chipinfo;
|
||||
|
||||
chipinfo = &dev->bus->chipinfo;
|
||||
|
||||
/* TODO: Probably need checks here; is the core connected? */
|
||||
int err;
|
||||
|
||||
if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
usb_dev = devm_kzalloc(&dev->dev, sizeof(struct bcma_hcd_device),
|
||||
GFP_KERNEL);
|
||||
if (!usb_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev->dev.of_node)
|
||||
usb_dev->gpio_desc = devm_get_gpiod_from_child(&dev->dev, "vcc",
|
||||
&dev->dev.of_node->fwnode);
|
||||
if (!IS_ERR_OR_NULL(usb_dev->gpio_desc))
|
||||
gpiod_direction_output(usb_dev->gpio_desc, 1);
|
||||
|
||||
switch (dev->id.id) {
|
||||
case BCMA_CORE_NS_USB20:
|
||||
bcma_hcd_init_chip_arm(dev);
|
||||
|
|
@ -333,17 +317,20 @@ static int bcma_hcd_probe(struct bcma_device *dev)
|
|||
&& chipinfo->rev == 0)
|
||||
ohci_addr = 0x18009000;
|
||||
|
||||
usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr);
|
||||
usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, "ohci-platform",
|
||||
ohci_addr, &ohci_pdata,
|
||||
sizeof(ohci_pdata));
|
||||
if (IS_ERR(usb_dev->ohci_dev))
|
||||
return PTR_ERR(usb_dev->ohci_dev);
|
||||
|
||||
usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr);
|
||||
usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, "ehci-platform",
|
||||
dev->addr, &ehci_pdata,
|
||||
sizeof(ehci_pdata));
|
||||
if (IS_ERR(usb_dev->ehci_dev)) {
|
||||
err = PTR_ERR(usb_dev->ehci_dev);
|
||||
goto err_unregister_ohci_dev;
|
||||
}
|
||||
|
||||
bcma_set_drvdata(dev, usb_dev);
|
||||
return 0;
|
||||
|
||||
err_unregister_ohci_dev:
|
||||
|
|
@ -351,6 +338,40 @@ err_unregister_ohci_dev:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int bcma_hcd_probe(struct bcma_device *core)
|
||||
{
|
||||
int err;
|
||||
struct bcma_hcd_device *usb_dev;
|
||||
|
||||
/* TODO: Probably need checks here; is the core connected? */
|
||||
|
||||
usb_dev = devm_kzalloc(&core->dev, sizeof(struct bcma_hcd_device),
|
||||
GFP_KERNEL);
|
||||
if (!usb_dev)
|
||||
return -ENOMEM;
|
||||
usb_dev->core = core;
|
||||
|
||||
if (core->dev.of_node)
|
||||
usb_dev->gpio_desc = devm_get_gpiod_from_child(&core->dev, "vcc",
|
||||
&core->dev.of_node->fwnode);
|
||||
if (!IS_ERR_OR_NULL(usb_dev->gpio_desc))
|
||||
gpiod_direction_output(usb_dev->gpio_desc, 1);
|
||||
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_USB20_HOST:
|
||||
case BCMA_CORE_NS_USB20:
|
||||
err = bcma_hcd_usb20_init(usb_dev);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bcma_set_drvdata(core, usb_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcma_hcd_remove(struct bcma_device *dev)
|
||||
{
|
||||
struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
|
||||
|
|
|
|||
|
|
@ -185,8 +185,7 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ehci_atmel_drv_suspend(struct device *dev)
|
||||
static int __maybe_unused ehci_atmel_drv_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
|
||||
|
|
@ -200,7 +199,7 @@ static int ehci_atmel_drv_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_atmel_drv_resume(struct device *dev)
|
||||
static int __maybe_unused ehci_atmel_drv_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
|
||||
|
|
@ -208,7 +207,6 @@ static int ehci_atmel_drv_resume(struct device *dev)
|
|||
atmel_start_clock(atmel_ehci);
|
||||
return ehci_resume(hcd, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id atmel_ehci_dt_ids[] = {
|
||||
|
|
|
|||
|
|
@ -11,76 +11,73 @@
|
|||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* this file is part of ehci-hcd.c */
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
/* check the values in the HCSPARAMS register
|
||||
/*
|
||||
* check the values in the HCSPARAMS register
|
||||
* (host controller _Structural_ parameters)
|
||||
* see EHCI spec, Table 2-4 for each value
|
||||
*/
|
||||
static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
|
||||
static void dbg_hcs_params(struct ehci_hcd *ehci, char *label)
|
||||
{
|
||||
u32 params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
|
||||
label, params,
|
||||
HCS_DEBUG_PORT (params),
|
||||
HCS_INDICATOR (params) ? " ind" : "",
|
||||
HCS_N_CC (params),
|
||||
HCS_N_PCC (params),
|
||||
HCS_PORTROUTED (params) ? "" : " ordered",
|
||||
HCS_PPC (params) ? "" : " !ppc",
|
||||
HCS_N_PORTS (params)
|
||||
);
|
||||
HCS_DEBUG_PORT(params),
|
||||
HCS_INDICATOR(params) ? " ind" : "",
|
||||
HCS_N_CC(params),
|
||||
HCS_N_PCC(params),
|
||||
HCS_PORTROUTED(params) ? "" : " ordered",
|
||||
HCS_PPC(params) ? "" : " !ppc",
|
||||
HCS_N_PORTS(params));
|
||||
/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
|
||||
if (HCS_PORTROUTED (params)) {
|
||||
if (HCS_PORTROUTED(params)) {
|
||||
int i;
|
||||
char buf [46], tmp [7], byte;
|
||||
char buf[46], tmp[7], byte;
|
||||
|
||||
buf[0] = 0;
|
||||
for (i = 0; i < HCS_N_PORTS (params); i++) {
|
||||
// FIXME MIPS won't readb() ...
|
||||
byte = readb (&ehci->caps->portroute[(i>>1)]);
|
||||
for (i = 0; i < HCS_N_PORTS(params); i++) {
|
||||
/* FIXME MIPS won't readb() ... */
|
||||
byte = readb(&ehci->caps->portroute[(i >> 1)]);
|
||||
sprintf(tmp, "%d ",
|
||||
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
|
||||
(i & 0x1) ? byte & 0xf : (byte >> 4) & 0xf);
|
||||
strcat(buf, tmp);
|
||||
}
|
||||
ehci_dbg (ehci, "%s portroute %s\n",
|
||||
label, buf);
|
||||
ehci_dbg(ehci, "%s portroute %s\n", label, buf);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
|
||||
static inline void dbg_hcs_params(struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
/* check the values in the HCCPARAMS register
|
||||
/*
|
||||
* check the values in the HCCPARAMS register
|
||||
* (host controller _Capability_ parameters)
|
||||
* see EHCI Spec, Table 2-5 for each value
|
||||
* */
|
||||
static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
||||
*/
|
||||
static void dbg_hcc_params(struct ehci_hcd *ehci, char *label)
|
||||
{
|
||||
u32 params = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||
|
||||
if (HCC_ISOC_CACHE (params)) {
|
||||
ehci_dbg (ehci,
|
||||
if (HCC_ISOC_CACHE(params)) {
|
||||
ehci_dbg(ehci,
|
||||
"%s hcc_params %04x caching frame %s%s%s\n",
|
||||
label, params,
|
||||
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK(params) ? " park" : "",
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
|
||||
} else {
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
"%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
|
||||
label,
|
||||
params,
|
||||
|
|
@ -97,21 +94,21 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
|||
}
|
||||
#else
|
||||
|
||||
static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
|
||||
static inline void dbg_hcc_params(struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
dbg_qtd(const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
{
|
||||
ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
|
||||
hc32_to_cpup(ehci, &qtd->hw_next),
|
||||
hc32_to_cpup(ehci, &qtd->hw_alt_next),
|
||||
hc32_to_cpup(ehci, &qtd->hw_token),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf [0]));
|
||||
if (qtd->hw_buf [1])
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[0]));
|
||||
if (qtd->hw_buf[1])
|
||||
ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[1]),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[2]),
|
||||
|
|
@ -120,22 +117,22 @@ dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
|||
}
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
dbg_qh(const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
struct ehci_qh_hw *hw = qh->hw;
|
||||
|
||||
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
|
||||
ehci_dbg(ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
|
||||
qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
|
||||
dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next);
|
||||
}
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
||||
dbg_itd(const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
|
||||
ehci_dbg(ehci, "%s [%d] itd %p, next %08x, urb %p\n",
|
||||
label, itd->frame, itd, hc32_to_cpu(ehci, itd->hw_next),
|
||||
itd->urb);
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[0]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[1]),
|
||||
|
|
@ -145,7 +142,7 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
|||
hc32_to_cpu(ehci, itd->hw_transaction[5]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[6]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[7]));
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[0]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[1]),
|
||||
|
|
@ -154,19 +151,19 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
|||
hc32_to_cpu(ehci, itd->hw_bufp[4]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[5]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[6]));
|
||||
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
|
||||
ehci_dbg(ehci, " index: %d %d %d %d %d %d %d %d\n",
|
||||
itd->index[0], itd->index[1], itd->index[2],
|
||||
itd->index[3], itd->index[4], itd->index[5],
|
||||
itd->index[6], itd->index[7]);
|
||||
}
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
||||
dbg_sitd(const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
|
||||
ehci_dbg(ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
|
||||
label, sitd->frame, sitd, hc32_to_cpu(ehci, sitd->hw_next),
|
||||
sitd->urb);
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
" addr %08x sched %04x result %08x buf %08x %08x\n",
|
||||
hc32_to_cpu(ehci, sitd->hw_fullspeed_ep),
|
||||
hc32_to_cpu(ehci, sitd->hw_uframe),
|
||||
|
|
@ -176,11 +173,11 @@ dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
|||
}
|
||||
|
||||
static int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", status,
|
||||
label, label[0] ? " " : "", status,
|
||||
(status & STS_PPCE_MASK) ? " PPCE" : "",
|
||||
(status & STS_ASS) ? " Async" : "",
|
||||
(status & STS_PSS) ? " Periodic" : "",
|
||||
|
|
@ -191,79 +188,83 @@ dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
|||
(status & STS_FLR) ? " FLR" : "",
|
||||
(status & STS_PCD) ? " PCD" : "",
|
||||
(status & STS_ERR) ? " ERR" : "",
|
||||
(status & STS_INT) ? " INT" : ""
|
||||
);
|
||||
(status & STS_INT) ? " INT" : "");
|
||||
}
|
||||
|
||||
static int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%sintrenable %02x%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", enable,
|
||||
label, label[0] ? " " : "", enable,
|
||||
(enable & STS_PPCE_MASK) ? " PPCE" : "",
|
||||
(enable & STS_IAA) ? " IAA" : "",
|
||||
(enable & STS_FATAL) ? " FATAL" : "",
|
||||
(enable & STS_FLR) ? " FLR" : "",
|
||||
(enable & STS_PCD) ? " PCD" : "",
|
||||
(enable & STS_ERR) ? " ERR" : "",
|
||||
(enable & STS_INT) ? " INT" : ""
|
||||
);
|
||||
(enable & STS_INT) ? " INT" : "");
|
||||
}
|
||||
|
||||
static const char *const fls_strings [] =
|
||||
{ "1024", "512", "256", "??" };
|
||||
static const char *const fls_strings[] = { "1024", "512", "256", "??" };
|
||||
|
||||
static int
|
||||
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
|
||||
dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
|
||||
"period=%s%s %s",
|
||||
label, label [0] ? " " : "", command,
|
||||
label, label[0] ? " " : "", command,
|
||||
(command & CMD_HIRD) ? " HIRD" : "",
|
||||
(command & CMD_PPCEE) ? " PPCEE" : "",
|
||||
(command & CMD_FSP) ? " FSP" : "",
|
||||
(command & CMD_ASPE) ? " ASPE" : "",
|
||||
(command & CMD_PSPE) ? " PSPE" : "",
|
||||
(command & CMD_PARK) ? " park" : "(park)",
|
||||
CMD_PARK_CNT (command),
|
||||
CMD_PARK_CNT(command),
|
||||
(command >> 16) & 0x3f,
|
||||
(command & CMD_LRESET) ? " LReset" : "",
|
||||
(command & CMD_IAAD) ? " IAAD" : "",
|
||||
(command & CMD_ASE) ? " Async" : "",
|
||||
(command & CMD_PSE) ? " Periodic" : "",
|
||||
fls_strings [(command >> 2) & 0x3],
|
||||
fls_strings[(command >> 2) & 0x3],
|
||||
(command & CMD_RESET) ? " Reset" : "",
|
||||
(command & CMD_RUN) ? "RUN" : "HALT"
|
||||
);
|
||||
(command & CMD_RUN) ? "RUN" : "HALT");
|
||||
}
|
||||
|
||||
static int
|
||||
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{
|
||||
char *sig;
|
||||
|
||||
/* signaling state */
|
||||
switch (status & (3 << 10)) {
|
||||
case 0 << 10: sig = "se0"; break;
|
||||
case 1 << 10: sig = "k"; break; /* low speed */
|
||||
case 2 << 10: sig = "j"; break;
|
||||
default: sig = "?"; break;
|
||||
case 0 << 10:
|
||||
sig = "se0";
|
||||
break;
|
||||
case 1 << 10: /* low speed */
|
||||
sig = "k";
|
||||
break;
|
||||
case 2 << 10:
|
||||
sig = "j";
|
||||
break;
|
||||
default:
|
||||
sig = "?";
|
||||
break;
|
||||
}
|
||||
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%sport:%d status %06x %d %s%s%s%s%s%s "
|
||||
"sig=%s%s%s%s%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", port, status,
|
||||
status>>25,/*device address */
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
|
||||
label, label[0] ? " " : "", port, status,
|
||||
status >> 25, /*device address */
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_ACK ?
|
||||
" ACK" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_NYET ?
|
||||
" NYET" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_STALL ?
|
||||
" STALL" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_ERR ?
|
||||
" ERR" : "",
|
||||
(status & PORT_POWER) ? " POWER" : "",
|
||||
(status & PORT_OWNER) ? " OWNER" : "",
|
||||
|
|
@ -282,52 +283,68 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
|||
|
||||
#else
|
||||
static inline void __maybe_unused
|
||||
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
dbg_qh(char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
{ return 0; }
|
||||
dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
|
||||
{ return 0; }
|
||||
dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
{ return 0; }
|
||||
dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{ return 0; }
|
||||
dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
||||
/* functions have the "wrong" filename when they're output... */
|
||||
#define dbg_status(ehci, label, status) { \
|
||||
char _buf [80]; \
|
||||
dbg_status_buf (_buf, sizeof _buf, label, status); \
|
||||
ehci_dbg (ehci, "%s\n", _buf); \
|
||||
static inline void
|
||||
dbg_status(struct ehci_hcd *ehci, const char *label, u32 status)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
dbg_status_buf(buf, sizeof(buf), label, status);
|
||||
ehci_dbg(ehci, "%s\n", buf);
|
||||
}
|
||||
|
||||
#define dbg_cmd(ehci, label, command) { \
|
||||
char _buf [80]; \
|
||||
dbg_command_buf (_buf, sizeof _buf, label, command); \
|
||||
ehci_dbg (ehci, "%s\n", _buf); \
|
||||
static inline void
|
||||
dbg_cmd(struct ehci_hcd *ehci, const char *label, u32 command)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
dbg_command_buf(buf, sizeof(buf), label, command);
|
||||
ehci_dbg(ehci, "%s\n", buf);
|
||||
}
|
||||
|
||||
#define dbg_port(ehci, label, port, status) { \
|
||||
char _buf [80]; \
|
||||
dbg_port_buf (_buf, sizeof _buf, label, port, status); \
|
||||
ehci_dbg (ehci, "%s\n", _buf); \
|
||||
static inline void
|
||||
dbg_port(struct ehci_hcd *ehci, const char *label, int port, u32 status)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
dbg_port_buf(buf, sizeof(buf), label, port, status);
|
||||
ehci_dbg(ehci, "%s\n", buf);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef STUB_DEBUG_FILES
|
||||
#ifndef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
static inline void create_debug_files (struct ehci_hcd *bus) { }
|
||||
static inline void remove_debug_files (struct ehci_hcd *bus) { }
|
||||
static inline void create_debug_files(struct ehci_hcd *bus) { }
|
||||
static inline void remove_debug_files(struct ehci_hcd *bus) { }
|
||||
|
||||
#else
|
||||
|
||||
|
|
@ -348,6 +365,7 @@ static const struct file_operations debug_async_fops = {
|
|||
.release = debug_close,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_bandwidth_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_bandwidth_open,
|
||||
|
|
@ -355,6 +373,7 @@ static const struct file_operations debug_bandwidth_fops = {
|
|||
.release = debug_close,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_periodic_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_periodic_open,
|
||||
|
|
@ -362,6 +381,7 @@ static const struct file_operations debug_periodic_fops = {
|
|||
.release = debug_close,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_registers_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_registers_open,
|
||||
|
|
@ -381,13 +401,19 @@ struct debug_buffer {
|
|||
size_t alloc_size;
|
||||
};
|
||||
|
||||
#define speed_char(info1) ({ char tmp; \
|
||||
switch (info1 & (3 << 12)) { \
|
||||
case QH_FULL_SPEED: tmp = 'f'; break; \
|
||||
case QH_LOW_SPEED: tmp = 'l'; break; \
|
||||
case QH_HIGH_SPEED: tmp = 'h'; break; \
|
||||
default: tmp = '?'; break; \
|
||||
} tmp; })
|
||||
static inline char speed_char(u32 info1)
|
||||
{
|
||||
switch (info1 & (3 << 12)) {
|
||||
case QH_FULL_SPEED:
|
||||
return 'f';
|
||||
case QH_LOW_SPEED:
|
||||
return 'l';
|
||||
case QH_HIGH_SPEED:
|
||||
return 'h';
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
|
||||
{
|
||||
|
|
@ -397,18 +423,14 @@ static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
|
|||
return '*';
|
||||
if (v & QTD_STS_HALT)
|
||||
return '-';
|
||||
if (!IS_SHORT_READ (v))
|
||||
if (!IS_SHORT_READ(v))
|
||||
return ' ';
|
||||
/* tries to advance through hw_alt_next */
|
||||
return '/';
|
||||
}
|
||||
|
||||
static void qh_lines (
|
||||
struct ehci_hcd *ehci,
|
||||
struct ehci_qh *qh,
|
||||
char **nextp,
|
||||
unsigned *sizep
|
||||
)
|
||||
static void qh_lines(struct ehci_hcd *ehci, struct ehci_qh *qh,
|
||||
char **nextp, unsigned *sizep)
|
||||
{
|
||||
u32 scratch;
|
||||
u32 hw_curr;
|
||||
|
|
@ -435,7 +457,7 @@ static void qh_lines (
|
|||
}
|
||||
scratch = hc32_to_cpup(ehci, &hw->hw_info1);
|
||||
hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0;
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)"
|
||||
" [cur %08x next %08x buf[0] %08x]",
|
||||
qh, scratch & 0x007f,
|
||||
|
|
@ -453,46 +475,52 @@ static void qh_lines (
|
|||
next += temp;
|
||||
|
||||
/* hc may be modifying the list as we read it ... */
|
||||
list_for_each (entry, &qh->qtd_list) {
|
||||
td = list_entry (entry, struct ehci_qtd, qtd_list);
|
||||
list_for_each(entry, &qh->qtd_list) {
|
||||
char *type;
|
||||
|
||||
td = list_entry(entry, struct ehci_qtd, qtd_list);
|
||||
scratch = hc32_to_cpup(ehci, &td->hw_token);
|
||||
mark = ' ';
|
||||
if (hw_curr == td->qtd_dma)
|
||||
if (hw_curr == td->qtd_dma) {
|
||||
mark = '*';
|
||||
else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
|
||||
} else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma)) {
|
||||
mark = '+';
|
||||
else if (QTD_LENGTH (scratch)) {
|
||||
} else if (QTD_LENGTH(scratch)) {
|
||||
if (td->hw_alt_next == ehci->async->hw->hw_alt_next)
|
||||
mark = '#';
|
||||
else if (td->hw_alt_next != list_end)
|
||||
mark = '/';
|
||||
}
|
||||
temp = snprintf (next, size,
|
||||
switch ((scratch >> 8) & 0x03) {
|
||||
case 0:
|
||||
type = "out";
|
||||
break;
|
||||
case 1:
|
||||
type = "in";
|
||||
break;
|
||||
case 2:
|
||||
type = "setup";
|
||||
break;
|
||||
default:
|
||||
type = "?";
|
||||
break;
|
||||
}
|
||||
temp = scnprintf(next, size,
|
||||
"\n\t%p%c%s len=%d %08x urb %p"
|
||||
" [td %08x buf[0] %08x]",
|
||||
td, mark, ({ char *tmp;
|
||||
switch ((scratch>>8)&0x03) {
|
||||
case 0: tmp = "out"; break;
|
||||
case 1: tmp = "in"; break;
|
||||
case 2: tmp = "setup"; break;
|
||||
default: tmp = "?"; break;
|
||||
} tmp;}),
|
||||
td, mark, type,
|
||||
(scratch >> 16) & 0x7fff,
|
||||
scratch,
|
||||
td->urb,
|
||||
(u32) td->qtd_dma,
|
||||
hc32_to_cpup(ehci, &td->hw_buf[0]));
|
||||
if (size < temp)
|
||||
temp = size;
|
||||
size -= temp;
|
||||
next += temp;
|
||||
if (temp == size)
|
||||
goto done;
|
||||
}
|
||||
|
||||
temp = snprintf (next, size, "\n");
|
||||
if (size < temp)
|
||||
temp = size;
|
||||
temp = scnprintf(next, size, "\n");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
|
|
@ -511,19 +539,20 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
|||
struct ehci_qh *qh;
|
||||
|
||||
hcd = bus_to_hcd(buf->bus);
|
||||
ehci = hcd_to_ehci (hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
next = buf->output_buf;
|
||||
size = buf->alloc_size;
|
||||
|
||||
*next = 0;
|
||||
|
||||
/* dumps a snapshot of the async schedule.
|
||||
/*
|
||||
* dumps a snapshot of the async schedule.
|
||||
* usually empty except for long-term bulk reads, or head.
|
||||
* one QH per line, and TDs we know about
|
||||
*/
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
|
||||
qh_lines (ehci, qh, &next, &size);
|
||||
qh_lines(ehci, qh, &next, &size);
|
||||
if (!list_empty(&ehci->async_unlink) && size > 0) {
|
||||
temp = scnprintf(next, size, "\nunlink =\n");
|
||||
size -= temp;
|
||||
|
|
@ -535,7 +564,7 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
|||
qh_lines(ehci, qh, &next, &size);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
return strlen(buf->output_buf);
|
||||
}
|
||||
|
|
@ -623,6 +652,33 @@ static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
|
|||
return next - buf->output_buf;
|
||||
}
|
||||
|
||||
static unsigned output_buf_tds_dir(char *buf, struct ehci_hcd *ehci,
|
||||
struct ehci_qh_hw *hw, struct ehci_qh *qh, unsigned size)
|
||||
{
|
||||
u32 scratch = hc32_to_cpup(ehci, &hw->hw_info1);
|
||||
struct ehci_qtd *qtd;
|
||||
char *type = "";
|
||||
unsigned temp = 0;
|
||||
|
||||
/* count tds, get ep direction */
|
||||
list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
|
||||
temp++;
|
||||
switch ((hc32_to_cpu(ehci, qtd->hw_token) >> 8) & 0x03) {
|
||||
case 0:
|
||||
type = "out";
|
||||
continue;
|
||||
case 1:
|
||||
type = "in";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return scnprintf(buf, size, " (%c%d ep%d%s [%d/%d] q%d p%d)",
|
||||
speed_char(scratch), scratch & 0x007f,
|
||||
(scratch >> 8) & 0x000f, type, qh->ps.usecs,
|
||||
qh->ps.c_usecs, temp, 0x7ff & (scratch >> 16));
|
||||
}
|
||||
|
||||
#define DBG_SCHED_LIMIT 64
|
||||
static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
{
|
||||
|
|
@ -635,31 +691,32 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
unsigned i;
|
||||
__hc32 tag;
|
||||
|
||||
seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC);
|
||||
seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
|
||||
if (!seen)
|
||||
return 0;
|
||||
seen_count = 0;
|
||||
|
||||
hcd = bus_to_hcd(buf->bus);
|
||||
ehci = hcd_to_ehci (hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
next = buf->output_buf;
|
||||
size = buf->alloc_size;
|
||||
|
||||
temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size);
|
||||
temp = scnprintf(next, size, "size = %d\n", ehci->periodic_size);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
/* dump a snapshot of the periodic schedule.
|
||||
/*
|
||||
* dump a snapshot of the periodic schedule.
|
||||
* iso changes, interrupt usually doesn't.
|
||||
*/
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
for (i = 0; i < ehci->periodic_size; i++) {
|
||||
p = ehci->pshadow [i];
|
||||
if (likely (!p.ptr))
|
||||
p = ehci->pshadow[i];
|
||||
if (likely(!p.ptr))
|
||||
continue;
|
||||
tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]);
|
||||
tag = Q_NEXT_TYPE(ehci, ehci->periodic[i]);
|
||||
|
||||
temp = scnprintf (next, size, "%4d: ", i);
|
||||
temp = scnprintf(next, size, "%4d: ", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
|
|
@ -669,7 +726,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
switch (hc32_to_cpu(ehci, tag)) {
|
||||
case Q_TYPE_QH:
|
||||
hw = p.qh->hw;
|
||||
temp = scnprintf (next, size, " qh%d-%04x/%p",
|
||||
temp = scnprintf(next, size, " qh%d-%04x/%p",
|
||||
p.qh->ps.period,
|
||||
hc32_to_cpup(ehci,
|
||||
&hw->hw_info2)
|
||||
|
|
@ -680,10 +737,10 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
next += temp;
|
||||
/* don't repeat what follows this qh */
|
||||
for (temp = 0; temp < seen_count; temp++) {
|
||||
if (seen [temp].ptr != p.ptr)
|
||||
if (seen[temp].ptr != p.ptr)
|
||||
continue;
|
||||
if (p.qh->qh_next.ptr) {
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" ...");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
|
@ -692,58 +749,32 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
}
|
||||
/* show more info the first time around */
|
||||
if (temp == seen_count) {
|
||||
u32 scratch = hc32_to_cpup(ehci,
|
||||
&hw->hw_info1);
|
||||
struct ehci_qtd *qtd;
|
||||
char *type = "";
|
||||
|
||||
/* count tds, get ep direction */
|
||||
temp = 0;
|
||||
list_for_each_entry (qtd,
|
||||
&p.qh->qtd_list,
|
||||
qtd_list) {
|
||||
temp++;
|
||||
switch (0x03 & (hc32_to_cpu(
|
||||
ehci,
|
||||
qtd->hw_token) >> 8)) {
|
||||
case 0: type = "out"; continue;
|
||||
case 1: type = "in"; continue;
|
||||
}
|
||||
}
|
||||
|
||||
temp = scnprintf (next, size,
|
||||
" (%c%d ep%d%s "
|
||||
"[%d/%d] q%d p%d)",
|
||||
speed_char (scratch),
|
||||
scratch & 0x007f,
|
||||
(scratch >> 8) & 0x000f, type,
|
||||
p.qh->ps.usecs,
|
||||
p.qh->ps.c_usecs,
|
||||
temp,
|
||||
0x7ff & (scratch >> 16));
|
||||
temp = output_buf_tds_dir(next, ehci,
|
||||
hw, p.qh, size);
|
||||
|
||||
if (seen_count < DBG_SCHED_LIMIT)
|
||||
seen [seen_count++].qh = p.qh;
|
||||
} else
|
||||
seen[seen_count++].qh = p.qh;
|
||||
} else {
|
||||
temp = 0;
|
||||
}
|
||||
tag = Q_NEXT_TYPE(ehci, hw->hw_next);
|
||||
p = p.qh->qh_next;
|
||||
break;
|
||||
case Q_TYPE_FSTN:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" fstn-%8x/%p", p.fstn->hw_prev,
|
||||
p.fstn);
|
||||
tag = Q_NEXT_TYPE(ehci, p.fstn->hw_next);
|
||||
p = p.fstn->fstn_next;
|
||||
break;
|
||||
case Q_TYPE_ITD:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" itd/%p", p.itd);
|
||||
tag = Q_NEXT_TYPE(ehci, p.itd->hw_next);
|
||||
p = p.itd->itd_next;
|
||||
break;
|
||||
case Q_TYPE_SITD:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" sitd%d-%04x/%p",
|
||||
p.sitd->stream->ps.period,
|
||||
hc32_to_cpup(ehci, &p.sitd->hw_uframe)
|
||||
|
|
@ -757,12 +788,12 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
next += temp;
|
||||
} while (p.ptr);
|
||||
|
||||
temp = scnprintf (next, size, "\n");
|
||||
temp = scnprintf(next, size, "\n");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
}
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
kfree (seen);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
kfree(seen);
|
||||
|
||||
return buf->alloc_size - size;
|
||||
}
|
||||
|
|
@ -789,19 +820,19 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
struct ehci_hcd *ehci;
|
||||
unsigned long flags;
|
||||
unsigned temp, size, i;
|
||||
char *next, scratch [80];
|
||||
static char fmt [] = "%*s\n";
|
||||
static char label [] = "";
|
||||
char *next, scratch[80];
|
||||
static char fmt[] = "%*s\n";
|
||||
static char label[] = "";
|
||||
|
||||
hcd = bus_to_hcd(buf->bus);
|
||||
ehci = hcd_to_ehci (hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
next = buf->output_buf;
|
||||
size = buf->alloc_size;
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
size = scnprintf (next, size,
|
||||
size = scnprintf(next, size,
|
||||
"bus %s, device %s\n"
|
||||
"%s\n"
|
||||
"SUSPENDED (no register access)\n",
|
||||
|
|
@ -813,7 +844,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
|
||||
/* Capability Registers */
|
||||
i = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"bus %s, device %s\n"
|
||||
"%s\n"
|
||||
"EHCI %x.%02x, rh state %s\n",
|
||||
|
|
@ -829,16 +860,16 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
if (dev_is_pci(hcd->self.controller)) {
|
||||
struct pci_dev *pdev;
|
||||
u32 offset, cap, cap2;
|
||||
unsigned count = 256/4;
|
||||
unsigned count = 256 / 4;
|
||||
|
||||
pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
|
||||
offset = HCC_EXT_CAPS(ehci_readl(ehci,
|
||||
&ehci->caps->hcc_params));
|
||||
while (offset && count--) {
|
||||
pci_read_config_dword (pdev, offset, &cap);
|
||||
pci_read_config_dword(pdev, offset, &cap);
|
||||
switch (cap & 0xff) {
|
||||
case 1:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"ownership %08x%s%s\n", cap,
|
||||
(cap & (1 << 24)) ? " linux" : "",
|
||||
(cap & (1 << 16)) ? " firmware" : "");
|
||||
|
|
@ -846,8 +877,8 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
next += temp;
|
||||
|
||||
offset += 4;
|
||||
pci_read_config_dword (pdev, offset, &cap2);
|
||||
temp = scnprintf (next, size,
|
||||
pci_read_config_dword(pdev, offset, &cap2);
|
||||
temp = scnprintf(next, size,
|
||||
"SMI sts/enable 0x%08x\n", cap2);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
|
@ -863,50 +894,50 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
}
|
||||
#endif
|
||||
|
||||
// FIXME interpret both types of params
|
||||
/* FIXME interpret both types of params */
|
||||
i = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
temp = scnprintf (next, size, "structural params 0x%08x\n", i);
|
||||
temp = scnprintf(next, size, "structural params 0x%08x\n", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
i = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||
temp = scnprintf (next, size, "capability params 0x%08x\n", i);
|
||||
temp = scnprintf(next, size, "capability params 0x%08x\n", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
/* Operational Registers */
|
||||
temp = dbg_status_buf (scratch, sizeof scratch, label,
|
||||
temp = dbg_status_buf(scratch, sizeof(scratch), label,
|
||||
ehci_readl(ehci, &ehci->regs->status));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = dbg_command_buf (scratch, sizeof scratch, label,
|
||||
temp = dbg_command_buf(scratch, sizeof(scratch), label,
|
||||
ehci_readl(ehci, &ehci->regs->command));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = dbg_intr_buf (scratch, sizeof scratch, label,
|
||||
temp = dbg_intr_buf(scratch, sizeof(scratch), label,
|
||||
ehci_readl(ehci, &ehci->regs->intr_enable));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = scnprintf (next, size, "uframe %04x\n",
|
||||
temp = scnprintf(next, size, "uframe %04x\n",
|
||||
ehci_read_frame_index(ehci));
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
|
||||
temp = dbg_port_buf (scratch, sizeof scratch, label, i,
|
||||
for (i = 1; i <= HCS_N_PORTS(ehci->hcs_params); i++) {
|
||||
temp = dbg_port_buf(scratch, sizeof(scratch), label, i,
|
||||
ehci_readl(ehci,
|
||||
&ehci->regs->port_status[i - 1]));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" debug control %08x\n",
|
||||
ehci_readl(ehci,
|
||||
&ehci->debug->control));
|
||||
|
|
@ -924,31 +955,31 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
}
|
||||
|
||||
#ifdef EHCI_STATS
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"irq normal %ld err %ld iaa %ld (lost %ld)\n",
|
||||
ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
|
||||
ehci->stats.lost_iaa);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = scnprintf (next, size, "complete %ld unlink %ld\n",
|
||||
temp = scnprintf(next, size, "complete %ld unlink %ld\n",
|
||||
ehci->stats.complete, ehci->stats.unlink);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
#endif
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
return buf->alloc_size - size;
|
||||
}
|
||||
|
||||
static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
|
||||
ssize_t (*fill_func)(struct debug_buffer *))
|
||||
ssize_t (*fill_func)(struct debug_buffer *))
|
||||
{
|
||||
struct debug_buffer *buf;
|
||||
|
||||
buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
|
||||
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
||||
|
||||
if (buf) {
|
||||
buf->bus = bus;
|
||||
|
|
@ -984,7 +1015,7 @@ out:
|
|||
}
|
||||
|
||||
static ssize_t debug_output(struct file *file, char __user *user_buf,
|
||||
size_t len, loff_t *offset)
|
||||
size_t len, loff_t *offset)
|
||||
{
|
||||
struct debug_buffer *buf = file->private_data;
|
||||
int ret = 0;
|
||||
|
|
@ -1004,7 +1035,6 @@ static ssize_t debug_output(struct file *file, char __user *user_buf,
|
|||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int debug_close(struct inode *inode, struct file *file)
|
||||
|
|
@ -1037,11 +1067,12 @@ static int debug_bandwidth_open(struct inode *inode, struct file *file)
|
|||
static int debug_periodic_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct debug_buffer *buf;
|
||||
|
||||
buf = alloc_buffer(inode->i_private, fill_periodic_buffer);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
|
||||
buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8) * PAGE_SIZE;
|
||||
file->private_data = buf;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1054,7 +1085,7 @@ static int debug_registers_open(struct inode *inode, struct file *file)
|
|||
return file->private_data ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static inline void create_debug_files (struct ehci_hcd *ehci)
|
||||
static inline void create_debug_files(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
|
||||
|
||||
|
|
@ -1084,9 +1115,9 @@ file_error:
|
|||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
static inline void remove_debug_files (struct ehci_hcd *ehci)
|
||||
static inline void remove_debug_files(struct ehci_hcd *ehci)
|
||||
{
|
||||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
#endif /* STUB_DEBUG_FILES */
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/usb/otg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include "ehci.h"
|
||||
#include "ehci-fsl.h"
|
||||
|
|
@ -241,7 +242,8 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
|
|||
* to portsc
|
||||
*/
|
||||
if (pdata->check_phy_clk_valid) {
|
||||
if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID)) {
|
||||
if (!(ioread32be(non_ehci + FSL_SOC_USB_CTRL) &
|
||||
PHY_CLK_VALID)) {
|
||||
dev_warn(hcd->self.controller,
|
||||
"USB PHY clock invalid\n");
|
||||
return -EINVAL;
|
||||
|
|
@ -273,9 +275,11 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
|
|||
|
||||
/* 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);
|
||||
iowrite32be(0x0 | SNOOP_SIZE_2GB,
|
||||
non_ehci + FSL_SOC_USB_SNOOP1);
|
||||
/* SNOOP2 starts from 0x80000000, size 2G */
|
||||
out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
|
||||
iowrite32be(0x80000000 | SNOOP_SIZE_2GB,
|
||||
non_ehci + FSL_SOC_USB_SNOOP2);
|
||||
}
|
||||
|
||||
/* Deal with USB erratum A-005275 */
|
||||
|
|
@ -309,13 +313,13 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
|
|||
|
||||
if (pdata->have_sysif_regs) {
|
||||
#ifdef CONFIG_FSL_SOC_BOOKE
|
||||
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
|
||||
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
|
||||
iowrite32be(0x00000008, non_ehci + FSL_SOC_USB_PRICTRL);
|
||||
iowrite32be(0x00000080, non_ehci + FSL_SOC_USB_AGECNTTHRSH);
|
||||
#else
|
||||
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
|
||||
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
|
||||
iowrite32be(0x0000000c, non_ehci + FSL_SOC_USB_PRICTRL);
|
||||
iowrite32be(0x00000040, non_ehci + FSL_SOC_USB_AGECNTTHRSH);
|
||||
#endif
|
||||
out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
|
||||
iowrite32be(0x00000001, non_ehci + FSL_SOC_USB_SICTRL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -554,7 +558,7 @@ static int ehci_fsl_drv_suspend(struct device *dev)
|
|||
if (!fsl_deep_sleep())
|
||||
return 0;
|
||||
|
||||
ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL);
|
||||
ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -577,7 +581,7 @@ static int ehci_fsl_drv_resume(struct device *dev)
|
|||
usb_root_hub_lost_power(hcd->self.root_hub);
|
||||
|
||||
/* Restore USB PHY settings and enable the controller. */
|
||||
out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl);
|
||||
iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
|
||||
|
||||
ehci_reset(ehci);
|
||||
ehci_fsl_reinit(ehci);
|
||||
|
|
|
|||
|
|
@ -306,9 +306,9 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void end_iaa_cycle(struct ehci_hcd *ehci);
|
||||
static void end_unlink_async(struct ehci_hcd *ehci);
|
||||
static void unlink_empty_async(struct ehci_hcd *ehci);
|
||||
static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
|
||||
static void ehci_work(struct ehci_hcd *ehci);
|
||||
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||
|
|
@ -565,6 +565,9 @@ static int ehci_init(struct usb_hcd *hcd)
|
|||
/* Accept arbitrarily long scatter-gather lists */
|
||||
if (!(hcd->driver->flags & HCD_LOCAL_MEM))
|
||||
hcd->self.sg_tablesize = ~0;
|
||||
|
||||
/* Prepare for unlinking active QHs */
|
||||
ehci->old_current = ~0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -675,8 +678,10 @@ int ehci_setup(struct usb_hcd *hcd)
|
|||
return retval;
|
||||
|
||||
retval = ehci_halt(ehci);
|
||||
if (retval)
|
||||
if (retval) {
|
||||
ehci_mem_cleanup(ehci);
|
||||
return retval;
|
||||
}
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
|
|
@ -756,7 +761,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
|||
ehci_dbg(ehci, "IAA with IAAD still set?\n");
|
||||
if (ehci->iaa_in_progress)
|
||||
COUNT(ehci->stats.iaa);
|
||||
end_unlink_async(ehci);
|
||||
end_iaa_cycle(ehci);
|
||||
}
|
||||
|
||||
/* remote wakeup [4.3.1] */
|
||||
|
|
@ -909,7 +914,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
|||
*/
|
||||
} else {
|
||||
qh = (struct ehci_qh *) urb->hcpriv;
|
||||
qh->exception = 1;
|
||||
qh->unlink_reason |= QH_UNLINK_REQUESTED;
|
||||
switch (qh->qh_state) {
|
||||
case QH_STATE_LINKED:
|
||||
if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)
|
||||
|
|
@ -970,10 +975,13 @@ rescan:
|
|||
goto done;
|
||||
}
|
||||
|
||||
qh->exception = 1;
|
||||
qh->unlink_reason |= QH_UNLINK_REQUESTED;
|
||||
switch (qh->qh_state) {
|
||||
case QH_STATE_LINKED:
|
||||
WARN_ON(!list_empty(&qh->qtd_list));
|
||||
if (list_empty(&qh->qtd_list))
|
||||
qh->unlink_reason |= QH_UNLINK_QUEUE_EMPTY;
|
||||
else
|
||||
WARN_ON(1);
|
||||
if (usb_endpoint_type(&ep->desc) != USB_ENDPOINT_XFER_INT)
|
||||
start_unlink_async(ehci, qh);
|
||||
else
|
||||
|
|
@ -1040,7 +1048,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
|||
* re-linking will call qh_refresh().
|
||||
*/
|
||||
usb_settoggle(qh->ps.udev, epnum, is_out, 0);
|
||||
qh->exception = 1;
|
||||
qh->unlink_reason |= QH_UNLINK_REQUESTED;
|
||||
if (eptype == USB_ENDPOINT_XFER_BULK)
|
||||
start_unlink_async(ehci, qh);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
|
||||
|
||||
static int persist_enabled_on_companion(struct usb_device *udev, void *unused)
|
||||
{
|
||||
return !udev->maxchild && udev->persist_enabled &&
|
||||
|
|
@ -347,8 +349,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||
goto done;
|
||||
ehci->rh_state = EHCI_RH_SUSPENDED;
|
||||
|
||||
end_unlink_async(ehci);
|
||||
unlink_empty_async_suspended(ehci);
|
||||
|
||||
/* Any IAA cycle that started before the suspend is now invalid */
|
||||
end_iaa_cycle(ehci);
|
||||
ehci_handle_start_intr_unlinks(ehci);
|
||||
ehci_handle_intr_unlinks(ehci);
|
||||
end_free_itds(ehci);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
|
|
@ -55,12 +56,16 @@ static int ehci_msm_reset(struct usb_hcd *hcd)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* select ULPI phy and clear other status/control bits in PORTSC */
|
||||
writel(PORTSC_PTS_ULPI, USB_PORTSC);
|
||||
/* bursts of unspecified length. */
|
||||
writel(0, USB_AHBBURST);
|
||||
/* Use the AHB transactor, allow posted data writes */
|
||||
writel(0x8, USB_AHBMODE);
|
||||
/* Disable streaming mode and select host mode */
|
||||
writel(0x13, USB_USBMODE);
|
||||
/* Disable ULPI_TX_PKT_EN_CLR_FIX which is valid only for HSIC */
|
||||
writel(readl(USB_GENCONFIG_2) & ~ULPI_TX_PKT_EN_CLR_FIX, USB_GENCONFIG_2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -104,9 +109,9 @@ static int ehci_msm_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/*
|
||||
* OTG driver takes care of PHY initialization, clock management,
|
||||
* powering up VBUS, mapping of registers address space and power
|
||||
* management.
|
||||
* If there is an OTG driver, let it take care of PHY initialization,
|
||||
* clock management, powering up VBUS, mapping of registers address
|
||||
* space and power management.
|
||||
*/
|
||||
if (pdev->dev.of_node)
|
||||
phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
|
||||
|
|
@ -114,27 +119,35 @@ static int ehci_msm_probe(struct platform_device *pdev)
|
|||
phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
|
||||
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(&pdev->dev, "unable to find transceiver\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
ret = otg_set_host(phy->otg, &hcd->self);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "unable to register with transceiver\n");
|
||||
goto put_hcd;
|
||||
if (PTR_ERR(phy) == -EPROBE_DEFER) {
|
||||
dev_err(&pdev->dev, "unable to find transceiver\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto put_hcd;
|
||||
}
|
||||
phy = NULL;
|
||||
}
|
||||
|
||||
hcd->usb_phy = phy;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
/*
|
||||
* OTG device parent of HCD takes care of putting
|
||||
* hardware into low power mode.
|
||||
*/
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* FIXME: need to call usb_add_hcd() here? */
|
||||
if (phy && phy->otg) {
|
||||
/*
|
||||
* MSM OTG driver takes care of adding the HCD and
|
||||
* placing hardware into low power mode via runtime PM.
|
||||
*/
|
||||
ret = otg_set_host(phy->otg, &hcd->self);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "unable to register with transceiver\n");
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
} else {
|
||||
ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -152,9 +165,10 @@ static int ehci_msm_remove(struct platform_device *pdev)
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
otg_set_host(hcd->usb_phy->otg, NULL);
|
||||
|
||||
/* FIXME: need to call usb_remove_hcd() here? */
|
||||
if (hcd->usb_phy && hcd->usb_phy->otg)
|
||||
otg_set_host(hcd->usb_phy->otg, NULL);
|
||||
else
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
|
@ -191,6 +205,12 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
|
|||
.resume = ehci_msm_pm_resume,
|
||||
};
|
||||
|
||||
static const struct acpi_device_id msm_ehci_acpi_ids[] = {
|
||||
{ "QCOM8040", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, msm_ehci_acpi_ids);
|
||||
|
||||
static const struct of_device_id msm_ehci_dt_match[] = {
|
||||
{ .compatible = "qcom,ehci-host", },
|
||||
{}
|
||||
|
|
@ -200,10 +220,12 @@ MODULE_DEVICE_TABLE(of, msm_ehci_dt_match);
|
|||
static struct platform_driver ehci_msm_driver = {
|
||||
.probe = ehci_msm_probe,
|
||||
.remove = ehci_msm_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "msm_hsusb_host",
|
||||
.pm = &ehci_msm_dev_pm_ops,
|
||||
.of_match_table = msm_ehci_dt_match,
|
||||
.acpi_match_table = ACPI_PTR(msm_ehci_acpi_ids),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -377,6 +377,12 @@ static int ehci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
return usb_hcd_pci_probe(pdev, id);
|
||||
}
|
||||
|
||||
static void ehci_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
pci_clear_mwi(pdev);
|
||||
usb_hcd_pci_remove(pdev);
|
||||
}
|
||||
|
||||
/* PCI driver selection metadata; PCI hotplugging uses this */
|
||||
static const struct pci_device_id pci_ids [] = { {
|
||||
/* handle any USB 2.0 EHCI controller */
|
||||
|
|
@ -396,7 +402,7 @@ static struct pci_driver ehci_pci_driver = {
|
|||
.id_table = pci_ids,
|
||||
|
||||
.probe = ehci_pci_probe,
|
||||
.remove = usb_hcd_pci_remove,
|
||||
.remove = ehci_pci_remove,
|
||||
.shutdown = usb_hcd_pci_shutdown,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
|
|
|||
|
|
@ -345,8 +345,7 @@ static int ehci_platform_suspend(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
bool do_wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
|
|
@ -364,8 +363,7 @@ static int ehci_platform_resume(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
|
||||
if (pdata->power_on) {
|
||||
|
|
|
|||
|
|
@ -394,6 +394,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
goto retry_xacterr;
|
||||
}
|
||||
stopped = 1;
|
||||
qh->unlink_reason |= QH_UNLINK_HALTED;
|
||||
|
||||
/* magic dummy for some short reads; qh won't advance.
|
||||
* that silicon quirk can kick in with this dummy too.
|
||||
|
|
@ -408,6 +409,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
&& !(qtd->hw_alt_next
|
||||
& EHCI_LIST_END(ehci))) {
|
||||
stopped = 1;
|
||||
qh->unlink_reason |= QH_UNLINK_SHORT_READ;
|
||||
}
|
||||
|
||||
/* stop scanning when we reach qtds the hc is using */
|
||||
|
|
@ -420,8 +422,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
stopped = 1;
|
||||
|
||||
/* cancel everything if we halt, suspend, etc */
|
||||
if (ehci->rh_state < EHCI_RH_RUNNING)
|
||||
if (ehci->rh_state < EHCI_RH_RUNNING) {
|
||||
last_status = -ESHUTDOWN;
|
||||
qh->unlink_reason |= QH_UNLINK_SHUTDOWN;
|
||||
}
|
||||
|
||||
/* this qtd is active; skip it unless a previous qtd
|
||||
* for its urb faulted, or its urb was canceled.
|
||||
|
|
@ -538,10 +542,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
* except maybe high bandwidth ...
|
||||
*/
|
||||
if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci))
|
||||
qh->exception = 1;
|
||||
qh->unlink_reason |= QH_UNLINK_DUMMY_OVERLAY;
|
||||
|
||||
/* Let the caller know if the QH needs to be unlinked. */
|
||||
return qh->exception;
|
||||
return qh->unlink_reason;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -1003,7 +1007,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
|
||||
qh->qh_state = QH_STATE_LINKED;
|
||||
qh->xacterrs = 0;
|
||||
qh->exception = 0;
|
||||
qh->unlink_reason = 0;
|
||||
/* qtd completions reported later by interrupt */
|
||||
|
||||
enable_async(ehci);
|
||||
|
|
@ -1279,17 +1283,13 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
|
||||
static void start_iaa_cycle(struct ehci_hcd *ehci)
|
||||
{
|
||||
/* Do nothing if an IAA cycle is already running */
|
||||
if (ehci->iaa_in_progress)
|
||||
return;
|
||||
ehci->iaa_in_progress = true;
|
||||
|
||||
/* If the controller isn't running, we don't have to wait for it */
|
||||
if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
|
||||
end_unlink_async(ehci);
|
||||
|
||||
/* Otherwise start a new IAA cycle */
|
||||
} else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
|
||||
/* Otherwise start a new IAA cycle if one isn't already running */
|
||||
} else if (ehci->rh_state == EHCI_RH_RUNNING &&
|
||||
!ehci->iaa_in_progress) {
|
||||
|
||||
/* Make sure the unlinks are all visible to the hardware */
|
||||
wmb();
|
||||
|
|
@ -1297,17 +1297,13 @@ static void start_iaa_cycle(struct ehci_hcd *ehci)
|
|||
ehci_writel(ehci, ehci->command | CMD_IAAD,
|
||||
&ehci->regs->command);
|
||||
ehci_readl(ehci, &ehci->regs->command);
|
||||
ehci->iaa_in_progress = true;
|
||||
ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* the async qh for the qtds being unlinked are now gone from the HC */
|
||||
|
||||
static void end_unlink_async(struct ehci_hcd *ehci)
|
||||
static void end_iaa_cycle(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct ehci_qh *qh;
|
||||
bool early_exit;
|
||||
|
||||
if (ehci->has_synopsys_hc_bug)
|
||||
ehci_writel(ehci, (u32) ehci->async->qh_dma,
|
||||
&ehci->regs->async_next);
|
||||
|
|
@ -1315,6 +1311,16 @@ static void end_unlink_async(struct ehci_hcd *ehci)
|
|||
/* The current IAA cycle has ended */
|
||||
ehci->iaa_in_progress = false;
|
||||
|
||||
end_unlink_async(ehci);
|
||||
}
|
||||
|
||||
/* See if the async qh for the qtds being unlinked are now gone from the HC */
|
||||
|
||||
static void end_unlink_async(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct ehci_qh *qh;
|
||||
bool early_exit;
|
||||
|
||||
if (list_empty(&ehci->async_unlink))
|
||||
return;
|
||||
qh = list_first_entry(&ehci->async_unlink, struct ehci_qh,
|
||||
|
|
@ -1335,14 +1341,60 @@ static void end_unlink_async(struct ehci_hcd *ehci)
|
|||
* after the IAA interrupt occurs. In self-defense, always go
|
||||
* through two IAA cycles for each QH.
|
||||
*/
|
||||
else if (qh->qh_state == QH_STATE_UNLINK_WAIT) {
|
||||
else if (qh->qh_state == QH_STATE_UNLINK) {
|
||||
/*
|
||||
* Second IAA cycle has finished. Process only the first
|
||||
* waiting QH (NVIDIA (?) bug).
|
||||
*/
|
||||
list_move_tail(&qh->unlink_node, &ehci->async_idle);
|
||||
}
|
||||
|
||||
/*
|
||||
* AMD/ATI (?) bug: The HC can continue to use an active QH long
|
||||
* after the IAA interrupt occurs. To prevent problems, QHs that
|
||||
* may still be active will wait until 2 ms have passed with no
|
||||
* change to the hw_current and hw_token fields (this delay occurs
|
||||
* between the two IAA cycles).
|
||||
*
|
||||
* The EHCI spec (4.8.2) says that active QHs must not be removed
|
||||
* from the async schedule and recommends waiting until the QH
|
||||
* goes inactive. This is ridiculous because the QH will _never_
|
||||
* become inactive if the endpoint NAKs indefinitely.
|
||||
*/
|
||||
|
||||
/* Some reasons for unlinking guarantee the QH can't be active */
|
||||
else if (qh->unlink_reason & (QH_UNLINK_HALTED |
|
||||
QH_UNLINK_SHORT_READ | QH_UNLINK_DUMMY_OVERLAY))
|
||||
goto DelayDone;
|
||||
|
||||
/* The QH can't be active if the queue was and still is empty... */
|
||||
else if ((qh->unlink_reason & QH_UNLINK_QUEUE_EMPTY) &&
|
||||
list_empty(&qh->qtd_list))
|
||||
goto DelayDone;
|
||||
|
||||
/* ... or if the QH has halted */
|
||||
else if (qh->hw->hw_token & cpu_to_hc32(ehci, QTD_STS_HALT))
|
||||
goto DelayDone;
|
||||
|
||||
/* Otherwise we have to wait until the QH stops changing */
|
||||
else {
|
||||
__hc32 qh_current, qh_token;
|
||||
|
||||
qh_current = qh->hw->hw_current;
|
||||
qh_token = qh->hw->hw_token;
|
||||
if (qh_current != ehci->old_current ||
|
||||
qh_token != ehci->old_token) {
|
||||
ehci->old_current = qh_current;
|
||||
ehci->old_token = qh_token;
|
||||
ehci_enable_event(ehci,
|
||||
EHCI_HRTIMER_ACTIVE_UNLINK, true);
|
||||
return;
|
||||
}
|
||||
DelayDone:
|
||||
qh->qh_state = QH_STATE_UNLINK;
|
||||
early_exit = true;
|
||||
}
|
||||
|
||||
/* Otherwise process only the first waiting QH (NVIDIA bug?) */
|
||||
else
|
||||
list_move_tail(&qh->unlink_node, &ehci->async_idle);
|
||||
ehci->old_current = ~0; /* Prepare for next QH */
|
||||
|
||||
/* Start a new IAA cycle if any QHs are waiting for it */
|
||||
if (!list_empty(&ehci->async_unlink))
|
||||
|
|
@ -1395,6 +1447,7 @@ static void unlink_empty_async(struct ehci_hcd *ehci)
|
|||
|
||||
/* If nothing else is being unlinked, unlink the last empty QH */
|
||||
if (list_empty(&ehci->async_unlink) && qh_to_unlink) {
|
||||
qh_to_unlink->unlink_reason |= QH_UNLINK_QUEUE_EMPTY;
|
||||
start_unlink_async(ehci, qh_to_unlink);
|
||||
--count;
|
||||
}
|
||||
|
|
@ -1406,8 +1459,10 @@ static void unlink_empty_async(struct ehci_hcd *ehci)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* The root hub is suspended; unlink all the async QHs */
|
||||
static void __maybe_unused unlink_empty_async_suspended(struct ehci_hcd *ehci)
|
||||
static void unlink_empty_async_suspended(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct ehci_qh *qh;
|
||||
|
||||
|
|
@ -1416,9 +1471,10 @@ static void __maybe_unused unlink_empty_async_suspended(struct ehci_hcd *ehci)
|
|||
WARN_ON(!list_empty(&qh->qtd_list));
|
||||
single_unlink_async(ehci, qh);
|
||||
}
|
||||
start_iaa_cycle(ehci);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* makes sure the async qh will become idle */
|
||||
/* caller must own ehci->lock */
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -287,8 +287,7 @@ static int st_ehci_suspend(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
bool do_wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
|
|
@ -308,8 +307,7 @@ static int st_ehci_resume(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int err;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
|
|||
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
|
||||
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||
2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
|
||||
2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ACTIVE_UNLINK */
|
||||
5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */
|
||||
6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
|
||||
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
|
||||
|
|
@ -237,6 +238,7 @@ static void ehci_handle_start_intr_unlinks(struct ehci_hcd *ehci)
|
|||
ehci->intr_unlink_wait_cycle))
|
||||
break;
|
||||
list_del_init(&qh->unlink_node);
|
||||
qh->unlink_reason |= QH_UNLINK_QUEUE_EMPTY;
|
||||
start_unlink_intr(ehci, qh);
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +362,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
|
|||
}
|
||||
|
||||
ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd);
|
||||
end_unlink_async(ehci);
|
||||
end_iaa_cycle(ehci);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -394,6 +396,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
|
|||
ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
|
||||
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||
end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
|
||||
end_unlink_async, /* EHCI_HRTIMER_ACTIVE_UNLINK */
|
||||
ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */
|
||||
unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
|
||||
ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ enum ehci_hrtimer_event {
|
|||
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
|
||||
EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
|
||||
EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
|
||||
EHCI_HRTIMER_ACTIVE_UNLINK, /* Wait while unlinking an active QH */
|
||||
EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */
|
||||
EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
|
||||
EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
|
||||
|
|
@ -156,6 +157,8 @@ struct ehci_hcd { /* one per controller */
|
|||
struct list_head async_idle;
|
||||
unsigned async_unlink_cycle;
|
||||
unsigned async_count; /* async activity count */
|
||||
__hc32 old_current; /* Test for QH becoming */
|
||||
__hc32 old_token; /* inactive during unlink */
|
||||
|
||||
/* periodic schedule support */
|
||||
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
|
||||
|
|
@ -185,7 +188,7 @@ struct ehci_hcd { /* one per controller */
|
|||
struct ehci_sitd *last_sitd_to_free;
|
||||
|
||||
/* per root hub port */
|
||||
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
|
||||
unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
|
||||
|
||||
/* bit vectors (one bit per port) */
|
||||
unsigned long bus_suspended; /* which ports were
|
||||
|
|
@ -244,9 +247,9 @@ struct ehci_hcd { /* one per controller */
|
|||
/* irq statistics */
|
||||
#ifdef EHCI_STATS
|
||||
struct ehci_stats stats;
|
||||
# define COUNT(x) do { (x)++; } while (0)
|
||||
# define COUNT(x) ((x)++)
|
||||
#else
|
||||
# define COUNT(x) do {} while (0)
|
||||
# define COUNT(x)
|
||||
#endif
|
||||
|
||||
/* debug files */
|
||||
|
|
@ -268,13 +271,13 @@ struct ehci_hcd { /* one per controller */
|
|||
};
|
||||
|
||||
/* convert between an HCD pointer and the corresponding EHCI_HCD */
|
||||
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
|
||||
static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct ehci_hcd *) (hcd->hcd_priv);
|
||||
}
|
||||
static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
|
||||
static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
|
||||
{
|
||||
return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
|
||||
return container_of((void *) ehci, struct usb_hcd, hcd_priv);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -316,25 +319,25 @@ struct ehci_qtd {
|
|||
#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)
|
||||
#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)
|
||||
|
||||
__hc32 hw_buf [5]; /* see EHCI 3.5.4 */
|
||||
__hc32 hw_buf_hi [5]; /* Appendix B */
|
||||
__hc32 hw_buf[5]; /* see EHCI 3.5.4 */
|
||||
__hc32 hw_buf_hi[5]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t qtd_dma; /* qtd address */
|
||||
struct list_head qtd_list; /* sw qtd list */
|
||||
struct urb *urb; /* qtd's urb */
|
||||
size_t length; /* length of buffer */
|
||||
} __attribute__ ((aligned (32)));
|
||||
} __aligned(32);
|
||||
|
||||
/* mask NakCnt+T in qh->hw_alt_next */
|
||||
#define QTD_MASK(ehci) cpu_to_hc32 (ehci, ~0x1f)
|
||||
#define QTD_MASK(ehci) cpu_to_hc32(ehci, ~0x1f)
|
||||
|
||||
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
|
||||
#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* type tag from {qh,itd,sitd,fstn}->hw_next */
|
||||
#define Q_NEXT_TYPE(ehci,dma) ((dma) & cpu_to_hc32(ehci, 3 << 1))
|
||||
#define Q_NEXT_TYPE(ehci, dma) ((dma) & cpu_to_hc32(ehci, 3 << 1))
|
||||
|
||||
/*
|
||||
* Now the following defines are not converted using the
|
||||
|
|
@ -350,7 +353,8 @@ struct ehci_qtd {
|
|||
#define Q_TYPE_FSTN (3 << 1)
|
||||
|
||||
/* next async queue entry, or pointer to interrupt/periodic QH */
|
||||
#define QH_NEXT(ehci,dma) (cpu_to_hc32(ehci, (((u32)dma)&~0x01f)|Q_TYPE_QH))
|
||||
#define QH_NEXT(ehci, dma) \
|
||||
(cpu_to_hc32(ehci, (((u32) dma) & ~0x01f) | Q_TYPE_QH))
|
||||
|
||||
/* for periodic/async schedules and qtd lists, mark end of list */
|
||||
#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
|
||||
|
|
@ -405,9 +409,9 @@ struct ehci_qh_hw {
|
|||
__hc32 hw_qtd_next;
|
||||
__hc32 hw_alt_next;
|
||||
__hc32 hw_token;
|
||||
__hc32 hw_buf [5];
|
||||
__hc32 hw_buf_hi [5];
|
||||
} __attribute__ ((aligned(32)));
|
||||
__hc32 hw_buf[5];
|
||||
__hc32 hw_buf_hi[5];
|
||||
} __aligned(32);
|
||||
|
||||
struct ehci_qh {
|
||||
struct ehci_qh_hw *hw; /* Must come first */
|
||||
|
|
@ -432,13 +436,19 @@ struct ehci_qh {
|
|||
u8 xacterrs; /* XactErr retry counter */
|
||||
#define QH_XACTERR_MAX 32 /* XactErr retry limit */
|
||||
|
||||
u8 unlink_reason;
|
||||
#define QH_UNLINK_HALTED 0x01 /* Halt flag is set */
|
||||
#define QH_UNLINK_SHORT_READ 0x02 /* Recover from a short read */
|
||||
#define QH_UNLINK_DUMMY_OVERLAY 0x04 /* QH overlayed the dummy TD */
|
||||
#define QH_UNLINK_SHUTDOWN 0x08 /* The HC isn't running */
|
||||
#define QH_UNLINK_QUEUE_EMPTY 0x10 /* Reached end of the queue */
|
||||
#define QH_UNLINK_REQUESTED 0x20 /* Disable, reset, or dequeue */
|
||||
|
||||
u8 gap_uf; /* uframes split/csplit gap */
|
||||
|
||||
unsigned is_out:1; /* bulk or intr OUT */
|
||||
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
|
||||
unsigned dequeue_during_giveback:1;
|
||||
unsigned exception:1; /* got a fault, or an unlink
|
||||
was requested */
|
||||
unsigned should_be_inactive:1;
|
||||
};
|
||||
|
||||
|
|
@ -462,7 +472,7 @@ struct ehci_iso_sched {
|
|||
struct list_head td_list;
|
||||
unsigned span;
|
||||
unsigned first_packet;
|
||||
struct ehci_iso_packet packet [0];
|
||||
struct ehci_iso_packet packet[0];
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -510,7 +520,7 @@ struct ehci_iso_stream {
|
|||
struct ehci_itd {
|
||||
/* first part defined by EHCI spec */
|
||||
__hc32 hw_next; /* see EHCI 3.3.1 */
|
||||
__hc32 hw_transaction [8]; /* see EHCI 3.3.2 */
|
||||
__hc32 hw_transaction[8]; /* see EHCI 3.3.2 */
|
||||
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
|
||||
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
|
||||
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
|
||||
|
|
@ -520,8 +530,8 @@ struct ehci_itd {
|
|||
|
||||
#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
|
||||
|
||||
__hc32 hw_bufp [7]; /* see EHCI 3.3.3 */
|
||||
__hc32 hw_bufp_hi [7]; /* Appendix B */
|
||||
__hc32 hw_bufp[7]; /* see EHCI 3.3.3 */
|
||||
__hc32 hw_bufp_hi[7]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t itd_dma; /* for this itd */
|
||||
|
|
@ -535,7 +545,7 @@ struct ehci_itd {
|
|||
unsigned frame; /* where scheduled */
|
||||
unsigned pg;
|
||||
unsigned index[8]; /* in urb->iso_frame_desc */
|
||||
} __attribute__ ((aligned (32)));
|
||||
} __aligned(32);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -554,7 +564,7 @@ struct ehci_sitd {
|
|||
__hc32 hw_results; /* EHCI table 3-11 */
|
||||
#define SITD_IOC (1 << 31) /* interrupt on completion */
|
||||
#define SITD_PAGE (1 << 30) /* buffer 0/1 */
|
||||
#define SITD_LENGTH(x) (0x3ff & ((x)>>16))
|
||||
#define SITD_LENGTH(x) (((x) >> 16) & 0x3ff)
|
||||
#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */
|
||||
#define SITD_STS_ERR (1 << 6) /* error from TT */
|
||||
#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */
|
||||
|
|
@ -565,9 +575,9 @@ struct ehci_sitd {
|
|||
|
||||
#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE)
|
||||
|
||||
__hc32 hw_buf [2]; /* EHCI table 3-12 */
|
||||
__hc32 hw_buf[2]; /* EHCI table 3-12 */
|
||||
__hc32 hw_backpointer; /* EHCI table 3-13 */
|
||||
__hc32 hw_buf_hi [2]; /* Appendix B */
|
||||
__hc32 hw_buf_hi[2]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t sitd_dma;
|
||||
|
|
@ -578,7 +588,7 @@ struct ehci_sitd {
|
|||
struct list_head sitd_list; /* list of stream's sitds */
|
||||
unsigned frame;
|
||||
unsigned index;
|
||||
} __attribute__ ((aligned (32)));
|
||||
} __aligned(32);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -598,7 +608,7 @@ struct ehci_fstn {
|
|||
/* the rest is HCD-private */
|
||||
dma_addr_t fstn_dma;
|
||||
union ehci_shadow fstn_next; /* ptr to periodic q entry */
|
||||
} __attribute__ ((aligned (32)));
|
||||
} __aligned(32);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -634,10 +644,10 @@ struct ehci_tt {
|
|||
/* Prepare the PORTSC wakeup flags during controller suspend/resume */
|
||||
|
||||
#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup);
|
||||
ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup)
|
||||
|
||||
#define ehci_prepare_ports_for_controller_resume(ehci) \
|
||||
ehci_adjust_port_wakeup_flags(ehci, false, false);
|
||||
ehci_adjust_port_wakeup_flags(ehci, false, false)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -731,7 +741,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
|
|||
#endif
|
||||
|
||||
static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
|
||||
__u32 __iomem * regs)
|
||||
__u32 __iomem *regs)
|
||||
{
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
|
||||
return ehci_big_endian_mmio(ehci) ?
|
||||
|
|
@ -806,7 +816,7 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
|
|||
#define ehci_big_endian_desc(e) ((e)->big_endian_desc)
|
||||
|
||||
/* cpu to ehci */
|
||||
static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
|
||||
static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x)
|
||||
{
|
||||
return ehci_big_endian_desc(ehci)
|
||||
? (__force __hc32)cpu_to_be32(x)
|
||||
|
|
@ -814,14 +824,14 @@ static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
|
|||
}
|
||||
|
||||
/* ehci to cpu */
|
||||
static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
|
||||
static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x)
|
||||
{
|
||||
return ehci_big_endian_desc(ehci)
|
||||
? be32_to_cpu((__force __be32)x)
|
||||
: le32_to_cpu((__force __le32)x);
|
||||
}
|
||||
|
||||
static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
{
|
||||
return ehci_big_endian_desc(ehci)
|
||||
? be32_to_cpup((__force __be32 *)x)
|
||||
|
|
@ -831,18 +841,18 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
|||
#else
|
||||
|
||||
/* cpu to ehci */
|
||||
static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
|
||||
static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x)
|
||||
{
|
||||
return cpu_to_le32(x);
|
||||
}
|
||||
|
||||
/* ehci to cpu */
|
||||
static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
|
||||
static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x)
|
||||
{
|
||||
return le32_to_cpu(x);
|
||||
}
|
||||
|
||||
static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
{
|
||||
return le32_to_cpup(x);
|
||||
}
|
||||
|
|
@ -852,18 +862,13 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
|||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#define ehci_dbg(ehci, fmt, args...) \
|
||||
dev_dbg(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
|
||||
dev_dbg(ehci_to_hcd(ehci)->self.controller, fmt, ## args)
|
||||
#define ehci_err(ehci, fmt, args...) \
|
||||
dev_err(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
|
||||
dev_err(ehci_to_hcd(ehci)->self.controller, fmt, ## args)
|
||||
#define ehci_info(ehci, fmt, args...) \
|
||||
dev_info(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
|
||||
dev_info(ehci_to_hcd(ehci)->self.controller, fmt, ## args)
|
||||
#define ehci_warn(ehci, fmt, args...) \
|
||||
dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args)
|
||||
|
||||
|
||||
#ifndef CONFIG_DYNAMIC_DEBUG
|
||||
#define STUB_DEBUG_FILES
|
||||
#endif
|
||||
dev_warn(ehci_to_hcd(ehci)->self.controller, fmt, ## args)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -883,12 +888,10 @@ extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
|
|||
u32 mask, u32 done, int usec);
|
||||
extern int ehci_reset(struct ehci_hcd *ehci);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
|
||||
extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
|
||||
extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
bool suspending, bool do_wakeup);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength);
|
||||
|
|
|
|||
|
|
@ -2267,7 +2267,7 @@ static unsigned qh_completions(struct fotg210_hcd *fotg210,
|
|||
struct fotg210_qh *qh)
|
||||
{
|
||||
struct fotg210_qtd *last, *end = qh->dummy;
|
||||
struct list_head *entry, *tmp;
|
||||
struct fotg210_qtd *qtd, *tmp;
|
||||
int last_status;
|
||||
int stopped;
|
||||
unsigned count = 0;
|
||||
|
|
@ -2301,12 +2301,10 @@ rescan:
|
|||
* then let the queue advance.
|
||||
* if queue is stopped, handles unlinks.
|
||||
*/
|
||||
list_for_each_safe(entry, tmp, &qh->qtd_list) {
|
||||
struct fotg210_qtd *qtd;
|
||||
list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) {
|
||||
struct urb *urb;
|
||||
u32 token = 0;
|
||||
|
||||
qtd = list_entry(entry, struct fotg210_qtd, qtd_list);
|
||||
urb = qtd->urb;
|
||||
|
||||
/* clean up any state from previous QTD ...*/
|
||||
|
|
@ -2544,14 +2542,11 @@ retry_xacterr:
|
|||
* used for cleanup after errors, before HC sees an URB's TDs.
|
||||
*/
|
||||
static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb,
|
||||
struct list_head *qtd_list)
|
||||
struct list_head *head)
|
||||
{
|
||||
struct list_head *entry, *temp;
|
||||
struct fotg210_qtd *qtd, *temp;
|
||||
|
||||
list_for_each_safe(entry, temp, qtd_list) {
|
||||
struct fotg210_qtd *qtd;
|
||||
|
||||
qtd = list_entry(entry, struct fotg210_qtd, qtd_list);
|
||||
list_for_each_entry_safe(qtd, temp, head, qtd_list) {
|
||||
list_del(&qtd->qtd_list);
|
||||
fotg210_qtd_free(fotg210, qtd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
struct fsl_usb2_dev_data {
|
||||
char *dr_mode; /* controller mode */
|
||||
|
|
@ -96,7 +97,11 @@ static struct platform_device *fsl_usb2_device_register(
|
|||
pdev->dev.parent = &ofdev->dev;
|
||||
|
||||
pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask;
|
||||
*pdev->dev.dma_mask = *ofdev->dev.dma_mask;
|
||||
|
||||
if (!pdev->dev.dma_mask)
|
||||
pdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask;
|
||||
else
|
||||
dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||||
|
||||
retval = platform_device_add_data(pdev, pdata, sizeof(*pdata));
|
||||
if (retval)
|
||||
|
|
|
|||
|
|
@ -797,19 +797,16 @@ max3421_check_unlink(struct usb_hcd *hcd)
|
|||
{
|
||||
struct spi_device *spi = to_spi_device(hcd->self.controller);
|
||||
struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
|
||||
struct list_head *pos, *upos, *next_upos;
|
||||
struct max3421_ep *max3421_ep;
|
||||
struct usb_host_endpoint *ep;
|
||||
struct urb *urb;
|
||||
struct urb *urb, *next;
|
||||
unsigned long flags;
|
||||
int retval = 0;
|
||||
|
||||
spin_lock_irqsave(&max3421_hcd->lock, flags);
|
||||
list_for_each(pos, &max3421_hcd->ep_list) {
|
||||
max3421_ep = container_of(pos, struct max3421_ep, ep_list);
|
||||
list_for_each_entry(max3421_ep, &max3421_hcd->ep_list, ep_list) {
|
||||
ep = max3421_ep->ep;
|
||||
list_for_each_safe(upos, next_upos, &ep->urb_list) {
|
||||
urb = container_of(upos, struct urb, urb_list);
|
||||
list_for_each_entry_safe(urb, next, &ep->urb_list, urb_list) {
|
||||
if (urb->unlinked) {
|
||||
retval = 1;
|
||||
dev_dbg(&spi->dev, "%s: URB %p unlinked=%d",
|
||||
|
|
@ -1184,22 +1181,19 @@ dump_eps(struct usb_hcd *hcd)
|
|||
struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd);
|
||||
struct max3421_ep *max3421_ep;
|
||||
struct usb_host_endpoint *ep;
|
||||
struct list_head *pos, *upos;
|
||||
char ubuf[512], *dp, *end;
|
||||
unsigned long flags;
|
||||
struct urb *urb;
|
||||
int epnum, ret;
|
||||
|
||||
spin_lock_irqsave(&max3421_hcd->lock, flags);
|
||||
list_for_each(pos, &max3421_hcd->ep_list) {
|
||||
max3421_ep = container_of(pos, struct max3421_ep, ep_list);
|
||||
list_for_each_entry(max3421_ep, &max3421_hcd->ep_list, ep_list) {
|
||||
ep = max3421_ep->ep;
|
||||
|
||||
dp = ubuf;
|
||||
end = dp + sizeof(ubuf);
|
||||
*dp = '\0';
|
||||
list_for_each(upos, &ep->urb_list) {
|
||||
urb = container_of(upos, struct urb, urb_list);
|
||||
list_for_each_entry(urb, &ep->urb_list, urb_list) {
|
||||
ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb,
|
||||
usb_pipetype(urb->pipe),
|
||||
usb_urb_dir_in(urb) ? "IN" : "OUT",
|
||||
|
|
|
|||
|
|
@ -583,9 +583,7 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int
|
||||
static int __maybe_unused
|
||||
ohci_hcd_at91_drv_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
|
|
@ -630,7 +628,8 @@ ohci_hcd_at91_drv_suspend(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ohci_hcd_at91_drv_resume(struct device *dev)
|
||||
static int __maybe_unused
|
||||
ohci_hcd_at91_drv_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
|
||||
|
|
@ -643,7 +642,6 @@ static int ohci_hcd_at91_drv_resume(struct device *dev)
|
|||
ohci_resume(hcd, false);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend,
|
||||
ohci_hcd_at91_drv_resume);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
@ -32,25 +31,9 @@
|
|||
|
||||
#include "ohci.h"
|
||||
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <mach/platform.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
#define USB_CONFIG_BASE 0x31020000
|
||||
#define PWRMAN_BASE 0x40004000
|
||||
|
||||
#define USB_CTRL IO_ADDRESS(PWRMAN_BASE + 0x64)
|
||||
|
||||
/* USB_CTRL bit defines */
|
||||
#define USB_SLAVE_HCLK_EN (1 << 24)
|
||||
#define USB_DEV_NEED_CLK_EN (1 << 22)
|
||||
#define USB_HOST_NEED_CLK_EN (1 << 21)
|
||||
#define PAD_CONTROL_LAST_DRIVEN (1 << 19)
|
||||
|
||||
#define USB_OTG_STAT_CONTROL IO_ADDRESS(USB_CONFIG_BASE + 0x110)
|
||||
|
||||
/* USB_OTG_STAT_CONTROL bit defines */
|
||||
|
|
@ -75,9 +58,7 @@ static struct i2c_client *isp1301_i2c_client;
|
|||
|
||||
extern int usb_disabled(void);
|
||||
|
||||
static struct clk *usb_pll_clk;
|
||||
static struct clk *usb_dev_clk;
|
||||
static struct clk *usb_otg_clk;
|
||||
static struct clk *usb_host_clk;
|
||||
|
||||
static void isp1301_configure_lpc32xx(void)
|
||||
{
|
||||
|
|
@ -117,9 +98,6 @@ static void isp1301_configure_lpc32xx(void)
|
|||
i2c_smbus_write_byte_data(isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
|
||||
|
||||
/* Enable usb_need_clk clock after transceiver is initialized */
|
||||
__raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
|
||||
|
||||
printk(KERN_INFO "ISP1301 Vendor ID : 0x%04x\n",
|
||||
i2c_smbus_read_word_data(isp1301_i2c_client, 0x00));
|
||||
printk(KERN_INFO "ISP1301 Product ID : 0x%04x\n",
|
||||
|
|
@ -192,59 +170,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
|
|||
goto fail_disable;
|
||||
}
|
||||
|
||||
/* Enable AHB slave USB clock, needed for further USB clock control */
|
||||
__raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL);
|
||||
|
||||
/* Enable USB PLL */
|
||||
usb_pll_clk = devm_clk_get(&pdev->dev, "ck_pll5");
|
||||
if (IS_ERR(usb_pll_clk)) {
|
||||
dev_err(&pdev->dev, "failed to acquire USB PLL\n");
|
||||
ret = PTR_ERR(usb_pll_clk);
|
||||
/* Enable USB host clock */
|
||||
usb_host_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(usb_host_clk)) {
|
||||
dev_err(&pdev->dev, "failed to acquire USB OHCI clock\n");
|
||||
ret = PTR_ERR(usb_host_clk);
|
||||
goto fail_disable;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(usb_pll_clk);
|
||||
ret = clk_prepare_enable(usb_host_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to start USB PLL\n");
|
||||
dev_err(&pdev->dev, "failed to start USB OHCI clock\n");
|
||||
goto fail_disable;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(usb_pll_clk, 48000);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to set USB clock rate\n");
|
||||
goto fail_rate;
|
||||
}
|
||||
|
||||
/* Enable USB device clock */
|
||||
usb_dev_clk = devm_clk_get(&pdev->dev, "ck_usbd");
|
||||
if (IS_ERR(usb_dev_clk)) {
|
||||
dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
|
||||
ret = PTR_ERR(usb_dev_clk);
|
||||
goto fail_rate;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(usb_dev_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to start USB DEV Clock\n");
|
||||
goto fail_rate;
|
||||
}
|
||||
|
||||
/* Enable USB otg clocks */
|
||||
usb_otg_clk = devm_clk_get(&pdev->dev, "ck_usb_otg");
|
||||
if (IS_ERR(usb_otg_clk)) {
|
||||
dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
|
||||
ret = PTR_ERR(usb_otg_clk);
|
||||
goto fail_otg;
|
||||
}
|
||||
|
||||
__raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
|
||||
|
||||
ret = clk_prepare_enable(usb_otg_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to start USB DEV Clock\n");
|
||||
goto fail_otg;
|
||||
}
|
||||
|
||||
isp1301_configure();
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
|
|
@ -283,11 +222,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
|
|||
fail_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_hcd:
|
||||
clk_disable_unprepare(usb_otg_clk);
|
||||
fail_otg:
|
||||
clk_disable_unprepare(usb_dev_clk);
|
||||
fail_rate:
|
||||
clk_disable_unprepare(usb_pll_clk);
|
||||
clk_disable_unprepare(usb_host_clk);
|
||||
fail_disable:
|
||||
isp1301_i2c_client = NULL;
|
||||
return ret;
|
||||
|
|
@ -300,9 +235,7 @@ static int ohci_hcd_nxp_remove(struct platform_device *pdev)
|
|||
usb_remove_hcd(hcd);
|
||||
ohci_nxp_stop_hc();
|
||||
usb_put_hcd(hcd);
|
||||
clk_disable_unprepare(usb_otg_clk);
|
||||
clk_disable_unprepare(usb_dev_clk);
|
||||
clk_disable_unprepare(usb_pll_clk);
|
||||
clk_disable_unprepare(usb_host_clk);
|
||||
isp1301_i2c_client = NULL;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -310,8 +310,7 @@ static int ohci_platform_suspend(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev->platform_data;
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
bool do_wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
|
|
@ -329,8 +328,7 @@ static int ohci_platform_resume(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
if (pdata->power_on) {
|
||||
int err = pdata->power_on(pdev);
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
|
|||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
pr_err("no resource of IORESOURCE_IRQ");
|
||||
return -ENXIO;
|
||||
return irq;
|
||||
}
|
||||
|
||||
usb_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
|
|
|||
|
|
@ -270,8 +270,7 @@ static int st_ohci_suspend(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev->platform_data;
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
bool do_wakeup = device_may_wakeup(dev);
|
||||
int ret;
|
||||
|
||||
|
|
@ -289,8 +288,7 @@ static int st_ohci_resume(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int err;
|
||||
|
||||
if (pdata->power_on) {
|
||||
|
|
|
|||
|
|
@ -735,10 +735,8 @@ extern void ohci_init_driver(struct hc_driver *drv,
|
|||
const struct ohci_driver_overrides *over);
|
||||
extern int ohci_restart(struct ohci_hcd *ohci);
|
||||
extern int ohci_setup(struct usb_hcd *hcd);
|
||||
#ifdef CONFIG_PM
|
||||
extern int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup);
|
||||
extern int ohci_resume(struct usb_hcd *hcd, bool hibernated);
|
||||
#endif
|
||||
extern int ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength);
|
||||
extern int ohci_hub_status_data(struct usb_hcd *hcd, char *buf);
|
||||
|
|
|
|||
|
|
@ -981,7 +981,7 @@ static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh);
|
|||
static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh)
|
||||
{
|
||||
struct ehci_qtd *last = NULL, *end = qh->dummy;
|
||||
struct list_head *entry, *tmp;
|
||||
struct ehci_qtd *qtd, *tmp;
|
||||
int stopped;
|
||||
unsigned count = 0;
|
||||
int do_status = 0;
|
||||
|
|
@ -1006,12 +1006,10 @@ static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh)
|
|||
* then let the queue advance.
|
||||
* if queue is stopped, handles unlinks.
|
||||
*/
|
||||
list_for_each_safe(entry, tmp, &qh->qtd_list) {
|
||||
struct ehci_qtd *qtd;
|
||||
list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) {
|
||||
struct urb *urb;
|
||||
u32 token = 0;
|
||||
|
||||
qtd = list_entry(entry, struct ehci_qtd, qtd_list);
|
||||
urb = qtd->urb;
|
||||
|
||||
/* Clean up any state from previous QTD ...*/
|
||||
|
|
@ -1174,14 +1172,11 @@ halt:
|
|||
* used for cleanup after errors, before HC sees an URB's TDs.
|
||||
*/
|
||||
static void qtd_list_free(struct oxu_hcd *oxu,
|
||||
struct urb *urb, struct list_head *qtd_list)
|
||||
struct urb *urb, struct list_head *head)
|
||||
{
|
||||
struct list_head *entry, *temp;
|
||||
struct ehci_qtd *qtd, *temp;
|
||||
|
||||
list_for_each_safe(entry, temp, qtd_list) {
|
||||
struct ehci_qtd *qtd;
|
||||
|
||||
qtd = list_entry(entry, struct ehci_qtd, qtd_list);
|
||||
list_for_each_entry_safe(qtd, temp, head, qtd_list) {
|
||||
list_del(&qtd->qtd_list);
|
||||
oxu_qtd_free(oxu, qtd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -992,7 +992,7 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
|
|||
if ((ext_cap_offset + sizeof(val)) > len) {
|
||||
/* We're reading garbage from the controller */
|
||||
dev_warn(&pdev->dev, "xHCI controller failing to respond");
|
||||
return;
|
||||
goto iounmap;
|
||||
}
|
||||
val = readl(base + ext_cap_offset);
|
||||
|
||||
|
|
@ -1055,6 +1055,7 @@ hc_init:
|
|||
XHCI_MAX_HALT_USEC, val);
|
||||
}
|
||||
|
||||
iounmap:
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2099,16 +2099,13 @@ static void r8a66597_check_detect_child(struct r8a66597 *r8a66597,
|
|||
|
||||
memset(now_map, 0, sizeof(now_map));
|
||||
|
||||
list_for_each_entry(bus, &usb_bus_list, bus_list) {
|
||||
if (!bus->root_hub)
|
||||
continue;
|
||||
|
||||
if (bus->busnum != hcd->self.busnum)
|
||||
continue;
|
||||
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
bus = idr_find(&usb_bus_idr, hcd->self.busnum);
|
||||
if (bus && bus->root_hub) {
|
||||
collect_usb_address_map(bus->root_hub, now_map);
|
||||
update_usb_address_map(r8a66597, bus->root_hub, now_map);
|
||||
}
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
}
|
||||
|
||||
static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue