mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
USB/Thunderbolt fixes for 7.3-rc6
Here is a big set of USB and Thunderbolt driver fixes for 7.3-rc6.
They were delayed on my side due to conference travel, not the fault of
the submitters at all. Included in here are:
- lots of small thunderbolt fixes for reported issues due to more
testing and devices and a few reverts as well based on that work
- more usb-serial device ids added
- usb-serial and cdc-acm driver hangup and other fixes
- dwc3 driver fixes for reported problems
- lots of usb gadget driver fixes as people again fuzz these drivers
and send in fixes, which is nice to finally see
- typec driver fixes for reported problems
- octeon-hcd driver fixes for reported problems
- more usb-storage quirks added
- other small USB driver bugs resolved for reported problems
All of these have been in linux-next without any reported issues
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-----BEGIN PGP SIGNATURE-----
iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCasDlTQ8cZ3JlZ0Brcm9h
aC5jb20ACgkQMUfUDdst+ylOTQCdEBigGDRRb0smeyW4VTv5cwCTXyMAoMvaredS
oOoIRhrQdBX3jlOUhm1N
=XZLj
-----END PGP SIGNATURE-----
Merge tag 'usb-7.3-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/Thunderbolt fixes from Greg KH:
"Here is a big set of USB and Thunderbolt driver fixes for 7.3-rc6.
They were delayed on my side due to conference travel, not the fault
of the submitters at all. Included in here are:
- lots of small thunderbolt fixes for reported issues due to more
testing and devices and a few reverts as well based on that work
- more usb-serial device ids added
- usb-serial and cdc-acm driver hangup and other fixes
- dwc3 driver fixes for reported problems
- lots of usb gadget driver fixes as people again fuzz these drivers
and send in fixes, which is nice to finally see
- typec driver fixes for reported problems
- octeon-hcd driver fixes for reported problems
- more usb-storage quirks added
- other small USB driver bugs resolved for reported problems
All of these have been in linux-next without any reported issues"
* tag 'usb-7.3-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (63 commits)
usb: dwc3: gadget: fix IRQ storm on invalid event buffer count
Revert "usb: dwc3: gadget: fix IRQ storm on invalid event buffer count"
USB: gadget: dummy-hcd: Fix wait for outstanding request completions
usb: typec: port-mapper: Only match USB4 port if host interface is available
usb: cdns3: Fix NULL pointer dereference in cdns3_pci_probe
usb: dwc3: gadget: fix IRQ storm on invalid event buffer count
usb: typec: ucsi: Get the connector fwnode based on reg value
usb: gadget: f_uac1_legacy: validate bRequest index in generic_{set,get}_cmd
usb: core: clear both ep_in and ep_out for non-ep0 control endpoints
USB: cdc-acm: skip URB restart in port_shutdown if disconnected
usb: gadget: f_fs: Fix NULL pointer dereference in FUNCTIONFS_ENDPOINT_DESC
usb: gadget: aspeed-vhub: cancel wake work on device removal
thunderbolt: Disable CL states for the Anker Prime TB5 dock
thunderbolt: stream: Announce support for FMODE_NOWAIT
usb: typec: ucsi: displayport: Current CAM OOB index fixup
usb: ohci-st: disable controller wakeup on removal
usb: ohci-spear: disable controller wakeup on removal
usb: ohci-s3c2410: disable controller wakeup on removal
usb: ohci-da8xx: disable controller wakeup on cleanup
usb: cdns3: fix use-after-free in cdns3_gadget_exit()
...
This commit is contained in:
commit
903e23eda5
|
|
@ -73,7 +73,6 @@ struct tb_ctl {
|
|||
#define tb_ctl_dbg_once(ctl, format, arg...) \
|
||||
dev_dbg_once((ctl)->nhi->dev, format, ## arg)
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue);
|
||||
/* Serializes access to request kref_get/put */
|
||||
static DEFINE_MUTEX(tb_cfg_request_lock);
|
||||
|
||||
|
|
@ -133,41 +132,42 @@ void tb_cfg_request_put(struct tb_cfg_request *req)
|
|||
static int tb_cfg_request_enqueue(struct tb_ctl *ctl,
|
||||
struct tb_cfg_request *req)
|
||||
{
|
||||
tb_cfg_request_get(req);
|
||||
|
||||
WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags));
|
||||
WARN_ON(req->ctl);
|
||||
|
||||
mutex_lock(&ctl->request_queue_lock);
|
||||
guard(mutex)(&ctl->request_queue_lock);
|
||||
if (!ctl->running) {
|
||||
mutex_unlock(&ctl->request_queue_lock);
|
||||
tb_cfg_request_put(req);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
req->ctl = ctl;
|
||||
list_add_tail(&req->list, &ctl->request_queue);
|
||||
set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
|
||||
mutex_unlock(&ctl->request_queue_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
|
||||
{
|
||||
return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
|
||||
}
|
||||
|
||||
static bool tb_cfg_request_is_canceled(struct tb_cfg_request *req)
|
||||
{
|
||||
return test_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
|
||||
}
|
||||
|
||||
static void tb_cfg_request_dequeue(struct tb_cfg_request *req)
|
||||
{
|
||||
struct tb_ctl *ctl = req->ctl;
|
||||
|
||||
mutex_lock(&ctl->request_queue_lock);
|
||||
if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) {
|
||||
mutex_unlock(&ctl->request_queue_lock);
|
||||
return;
|
||||
guard(mutex)(&ctl->request_queue_lock);
|
||||
if (tb_cfg_request_is_active(req)) {
|
||||
list_del(&req->list);
|
||||
clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
|
||||
tb_cfg_request_put(req);
|
||||
}
|
||||
|
||||
list_del(&req->list);
|
||||
clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
|
||||
if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
|
||||
wake_up(&tb_cfg_request_cancel_queue);
|
||||
mutex_unlock(&ctl->request_queue_lock);
|
||||
}
|
||||
|
||||
static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
|
||||
{
|
||||
return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
|
||||
}
|
||||
|
||||
static struct tb_cfg_request *
|
||||
|
|
@ -178,7 +178,7 @@ tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg)
|
|||
mutex_lock(&pkg->ctl->request_queue_lock);
|
||||
list_for_each_entry(iter, &pkg->ctl->request_queue, list) {
|
||||
tb_cfg_request_get(iter);
|
||||
if (iter->match(iter, pkg)) {
|
||||
if (!tb_cfg_request_is_canceled(iter) && iter->match(iter, pkg)) {
|
||||
req = iter;
|
||||
break;
|
||||
}
|
||||
|
|
@ -512,8 +512,11 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
|
|||
trace_tb_rx(pkg->ctl->index, frame->eof, pkg->buffer, frame->size, !req);
|
||||
|
||||
if (req) {
|
||||
if (req->copy(req, pkg))
|
||||
schedule_work(&req->work);
|
||||
scoped_guard(mutex, &pkg->ctl->request_queue_lock) {
|
||||
if (!tb_cfg_request_is_canceled(req) &&
|
||||
req->copy(req, pkg))
|
||||
schedule_work(&req->work);
|
||||
}
|
||||
tb_cfg_request_put(req);
|
||||
}
|
||||
|
||||
|
|
@ -525,11 +528,10 @@ static void tb_cfg_request_work(struct work_struct *work)
|
|||
{
|
||||
struct tb_cfg_request *req = container_of(work, typeof(*req), work);
|
||||
|
||||
if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
|
||||
if (!tb_cfg_request_is_canceled(req))
|
||||
req->callback(req->callback_data);
|
||||
|
||||
tb_cfg_request_dequeue(req);
|
||||
tb_cfg_request_put(req);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -555,10 +557,9 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req,
|
|||
INIT_WORK(&req->work, tb_cfg_request_work);
|
||||
INIT_LIST_HEAD(&req->list);
|
||||
|
||||
tb_cfg_request_get(req);
|
||||
ret = tb_cfg_request_enqueue(ctl, req);
|
||||
if (ret)
|
||||
goto err_put;
|
||||
return ret;
|
||||
|
||||
ret = tb_ctl_tx(ctl, req->request, req->request_size,
|
||||
req->request_type);
|
||||
|
|
@ -572,9 +573,6 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req,
|
|||
|
||||
err_dequeue:
|
||||
tb_cfg_request_dequeue(req);
|
||||
err_put:
|
||||
tb_cfg_request_put(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -588,9 +586,10 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req,
|
|||
*/
|
||||
void tb_cfg_request_cancel(struct tb_cfg_request *req, int err)
|
||||
{
|
||||
set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
|
||||
schedule_work(&req->work);
|
||||
wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req));
|
||||
scoped_guard(mutex, &req->ctl->request_queue_lock)
|
||||
set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
|
||||
cancel_work_sync(&req->work);
|
||||
tb_cfg_request_dequeue(req);
|
||||
req->result.err = err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -788,21 +788,6 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
|
|||
transmit_ring, receive_path, receive_ring);
|
||||
}
|
||||
|
||||
static void tb_domain_reset_interface(struct tb *tb)
|
||||
{
|
||||
struct tb_nhi *nhi = tb->nhi;
|
||||
|
||||
if (!nhi->ops->reset_interface)
|
||||
return;
|
||||
|
||||
guard(mutex)(&tb->lock);
|
||||
|
||||
/* The reset clears the ring state so stop the control channel */
|
||||
tb_ctl_stop(tb->ctl);
|
||||
nhi->ops->reset_interface(nhi);
|
||||
tb_ctl_start(tb->ctl);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain
|
||||
* @tb: Domain disabling the DMA paths
|
||||
|
|
@ -825,20 +810,11 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
|
|||
int transmit_path, int transmit_ring,
|
||||
int receive_path, int receive_ring)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!tb->cm_ops->disconnect_xdomain_paths)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path,
|
||||
return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path,
|
||||
transmit_ring, receive_path, receive_ring);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN)
|
||||
tb_domain_reset_interface(tb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disconnect_xdomain(struct device *dev, void *data)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/property.h>
|
||||
|
|
@ -560,6 +561,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
|
|||
INIT_LIST_HEAD(&ring->in_flight);
|
||||
INIT_WORK(&ring->work, ring_work);
|
||||
init_waitqueue_head(&ring->wait);
|
||||
lockdep_register_key(&ring->lock_key);
|
||||
lockdep_init_map(&ring->work.lockdep_map, "ring.work", &ring->lock_key, 0);
|
||||
|
||||
ring->nhi = nhi;
|
||||
ring->hop = hop;
|
||||
|
|
@ -599,6 +602,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
|
|||
ring->size * sizeof(*ring->descriptors),
|
||||
ring->descriptors, ring->descriptors_dma);
|
||||
err_free_ring:
|
||||
lockdep_unregister_key(&ring->lock_key);
|
||||
kfree(ring);
|
||||
|
||||
return NULL;
|
||||
|
|
@ -848,6 +852,7 @@ void tb_ring_free(struct tb_ring *ring)
|
|||
* to finish before freeing the ring.
|
||||
*/
|
||||
flush_work(&ring->work);
|
||||
lockdep_unregister_key(&ring->lock_key);
|
||||
kfree(ring);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tb_ring_free);
|
||||
|
|
@ -1175,32 +1180,6 @@ static void nhi_reset(struct tb_nhi *nhi)
|
|||
dev_warn(nhi->dev, "timeout resetting host router\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* nhi_reset_interface() - Reset the host interface
|
||||
* @nhi: Host interface to reset
|
||||
*
|
||||
* Brings the registers in the memory BAR back to their default state and
|
||||
* clears the End-to-End Flow Control state. The caller is responsible for
|
||||
* stopping the control channel over the reset because it clears the ring
|
||||
* state as well.
|
||||
*/
|
||||
void nhi_reset_interface(struct tb_nhi *nhi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = ioread32(nhi->iobase + REG_CAPS);
|
||||
/* Only v1 host interfaces implement the reset */
|
||||
if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2)
|
||||
return;
|
||||
|
||||
dev_dbg(nhi->dev, "issuing host interface reset\n");
|
||||
|
||||
iowrite32(REG_HOST_INTERFACE_RESET_RST,
|
||||
nhi->iobase + REG_HOST_INTERFACE_RESET);
|
||||
/* Wait for tHIReset (10 ms) to complete */
|
||||
usleep_range(10000, 20000);
|
||||
}
|
||||
|
||||
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
|
||||
{
|
||||
struct tb *tb;
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@ irqreturn_t nhi_msi(int irq, void *data);
|
|||
irqreturn_t ring_msix(int irq, void *data);
|
||||
int nhi_probe(struct tb_nhi *nhi);
|
||||
void nhi_shutdown(struct tb_nhi *nhi);
|
||||
void nhi_reset_interface(struct tb_nhi *nhi);
|
||||
|
||||
extern const struct dev_pm_ops nhi_pm_ops;
|
||||
|
||||
/**
|
||||
|
|
@ -54,7 +52,6 @@ extern const struct dev_pm_ops nhi_pm_ops;
|
|||
* @release_ring_irq: NHI specific interrupt release hook
|
||||
* @is_present: Whether the device is currently present on the parent bus
|
||||
* @init_interrupts: NHI specific interrupt initialization hook
|
||||
* @reset_interface: Resets the host interface
|
||||
*/
|
||||
struct tb_nhi_ops {
|
||||
int (*init)(struct tb_nhi *nhi);
|
||||
|
|
@ -69,7 +66,6 @@ struct tb_nhi_ops {
|
|||
void (*release_ring_irq)(struct tb_ring *ring);
|
||||
bool (*is_present)(struct tb_nhi *nhi);
|
||||
int (*init_interrupts)(struct tb_nhi *nhi);
|
||||
void (*reset_interface)(struct tb_nhi *nhi);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -120,24 +116,11 @@ struct tb_nhi_ops {
|
|||
#define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433
|
||||
#define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e
|
||||
|
||||
#define PCI_CLASS_SERIAL_USB_USB4 0x0c0340
|
||||
|
||||
/* Host interface quirks */
|
||||
#define QUIRK_AUTO_CLEAR_INT BIT(0)
|
||||
#define QUIRK_E2E BIT(1)
|
||||
#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2)
|
||||
#define QUIRK_AUTO_CLEAR_INT BIT(0)
|
||||
#define QUIRK_E2E BIT(1)
|
||||
|
||||
/*
|
||||
* Minimal number of vectors when we use MSI-X. Two for control channel
|
||||
|
|
|
|||
|
|
@ -115,10 +115,6 @@ struct ring_desc {
|
|||
#define REG_CAPS_VERSION_MASK GENMASK(23, 16)
|
||||
#define REG_CAPS_VERSION_2 0x40
|
||||
|
||||
/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */
|
||||
#define REG_HOST_INTERFACE_RESET 0x39858
|
||||
#define REG_HOST_INTERFACE_RESET_RST BIT(0)
|
||||
|
||||
#define REG_DMA_MISC 0x39864
|
||||
#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
|
||||
#define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17)
|
||||
|
|
|
|||
|
|
@ -62,27 +62,6 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci)
|
|||
nhi->quirks |= QUIRK_E2E;
|
||||
break;
|
||||
}
|
||||
} else if (pdev->vendor == PCI_VENDOR_ID_AMD) {
|
||||
switch (pdev->device) {
|
||||
case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0:
|
||||
case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1:
|
||||
/*
|
||||
* These AMD hosts may hang the Tx ring when the
|
||||
* DMA paths are torn down so they need the host
|
||||
* interface reset after each teardown.
|
||||
*/
|
||||
nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +258,6 @@ static const struct tb_nhi_ops pci_nhi_default_ops = {
|
|||
.shutdown = nhi_pci_release_irq,
|
||||
.is_present = nhi_pci_is_present,
|
||||
.init_interrupts = nhi_pci_init_msi,
|
||||
.reset_interface = nhi_reset_interface,
|
||||
};
|
||||
|
||||
/* Ice Lake specific NHI operations */
|
||||
|
|
@ -463,7 +441,6 @@ static const struct tb_nhi_ops icl_nhi_ops = {
|
|||
.release_ring_irq = nhi_pci_ring_release_msix,
|
||||
.is_present = nhi_pci_is_present,
|
||||
.init_interrupts = nhi_pci_init_msi,
|
||||
.reset_interface = nhi_reset_interface,
|
||||
};
|
||||
|
||||
static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ static const struct tb_quirk tb_quirks[] = {
|
|||
quirk_usb3_maximum_bandwidth },
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000,
|
||||
quirk_usb3_maximum_bandwidth },
|
||||
/* CLx is unstable on the Anker Prime TB5 dock (DROM 01ea:83b5) */
|
||||
{ 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x01ea, 0x83b5,
|
||||
quirk_clx_disable },
|
||||
/*
|
||||
* Block Runtime PM in DP redrive mode for Intel Barlow Ridge host
|
||||
* controllers.
|
||||
|
|
|
|||
|
|
@ -978,6 +978,9 @@ static int tbstream_dev_fops_open(struct inode *inode, struct file *file)
|
|||
}
|
||||
|
||||
mutex_unlock(&sdev->lock);
|
||||
|
||||
/* Stream handles IOCB_NOWAIT just fine */
|
||||
file->f_mode |= FMODE_NOWAIT;
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
|
|
|
|||
|
|
@ -774,6 +774,7 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
|
|||
{
|
||||
int port_max_hopid;
|
||||
struct ida *ida;
|
||||
int ret;
|
||||
|
||||
if (in) {
|
||||
port_max_hopid = port->config.max_in_hop_id;
|
||||
|
|
@ -793,7 +794,11 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
|
|||
if (max_hopid < 0 || max_hopid > port_max_hopid)
|
||||
max_hopid = port_max_hopid;
|
||||
|
||||
return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
|
||||
ret = ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
|
||||
if (ret >= 0)
|
||||
tb_switch_get(port->sw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -832,6 +837,7 @@ int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid)
|
|||
void tb_port_release_in_hopid(struct tb_port *port, int hopid)
|
||||
{
|
||||
ida_free(&port->in_hopids, hopid);
|
||||
tb_switch_put(port->sw);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -842,6 +848,7 @@ void tb_port_release_in_hopid(struct tb_port *port, int hopid)
|
|||
void tb_port_release_out_hopid(struct tb_port *port, int hopid)
|
||||
{
|
||||
ida_free(&port->out_hopids, hopid);
|
||||
tb_switch_put(port->sw);
|
||||
}
|
||||
|
||||
static inline bool tb_switch_is_reachable(const struct tb_switch *parent,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port,
|
|||
const char *reason);
|
||||
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
|
||||
int retry, unsigned long delay);
|
||||
static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data);
|
||||
|
||||
static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
|
||||
{
|
||||
|
|
@ -385,7 +386,8 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw,
|
|||
|
||||
switch (port->config.type) {
|
||||
case TB_TYPE_DP_HDMI_IN:
|
||||
tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids);
|
||||
tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids,
|
||||
tb_dp_tunnel_active, tb);
|
||||
tb_increase_tmu_accuracy(tunnel);
|
||||
break;
|
||||
|
||||
|
|
@ -1910,6 +1912,18 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
|
|||
struct tb *tb = data;
|
||||
|
||||
mutex_lock(&tb->lock);
|
||||
|
||||
/*
|
||||
* If the DPRX read was canceled the tunnel is already being torn
|
||||
* down by whoever canceled it. Do not touch the adapters here
|
||||
* because the routers may be gone by now.
|
||||
*/
|
||||
if (tunnel->dprx_canceled) {
|
||||
tb_tunnel_dbg(tunnel, "DPRX read canceled, not activating\n");
|
||||
mutex_unlock(&tb->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tb_tunnel_is_active(tunnel)) {
|
||||
int consumed_up, consumed_down, ret;
|
||||
|
||||
|
|
@ -1964,8 +1978,6 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
|
|||
tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed");
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
tb_domain_put(tb);
|
||||
}
|
||||
|
||||
static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
|
||||
|
|
@ -2026,8 +2038,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
|
|||
available_up, available_down);
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
|
||||
available_down, tb_dp_tunnel_active,
|
||||
tb_domain_get(tb));
|
||||
available_down, tb_dp_tunnel_active, tb);
|
||||
if (!tunnel) {
|
||||
tb_port_dbg(out, "could not allocate DP tunnel\n");
|
||||
goto err_reclaim_usb;
|
||||
|
|
@ -2048,7 +2059,6 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
|
|||
tb_tunnel_put(tunnel);
|
||||
err_reclaim_usb:
|
||||
tb_reclaim_usb3_bandwidth(tb, in, out);
|
||||
tb_domain_put(tb);
|
||||
err_detach_group:
|
||||
tb_detach_bandwidth_group(in);
|
||||
err_dealloc_dp:
|
||||
|
|
@ -2950,11 +2960,12 @@ static void tb_stop(struct tb *tb)
|
|||
/* tunnels are only present after everything has been initialized */
|
||||
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
|
||||
/*
|
||||
* DMA tunnels require the driver to be functional so we
|
||||
* tear them down. Other protocol tunnels can be left
|
||||
* intact.
|
||||
* DMA tunnels and DP tunnels which are not yet active require
|
||||
* the driver to be functional so we tear them down.
|
||||
* Other protocol tunnels can be left intact.
|
||||
*/
|
||||
if (tb_tunnel_is_dma(tunnel))
|
||||
if (tb_tunnel_is_dma(tunnel) ||
|
||||
(tb_tunnel_is_dp(tunnel) && !tb_tunnel_is_active(tunnel)))
|
||||
tb_tunnel_deactivate(tunnel);
|
||||
tb_tunnel_put(tunnel);
|
||||
}
|
||||
|
|
@ -3274,11 +3285,11 @@ static void tb_remove_work(struct work_struct *work)
|
|||
struct tb *tb = tcm_to_tb(tcm);
|
||||
|
||||
mutex_lock(&tb->lock);
|
||||
if (tb->root_switch)
|
||||
if (tb->root_switch) {
|
||||
tb_free_unplugged_children(tb->root_switch);
|
||||
tb_free_unplugged_xdomains(tb->root_switch);
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
tb_free_unplugged_xdomains(tb->root_switch);
|
||||
}
|
||||
|
||||
static int tb_runtime_resume(struct tb *tb)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,18 @@ static void kunit_ida_init(struct kunit *test, struct ida *ida)
|
|||
kunit_alloc_resource(test, __ida_init, __ida_destroy, GFP_KERNEL, ida);
|
||||
}
|
||||
|
||||
static void tb_test_switch_release(struct device *dev)
|
||||
{
|
||||
/* The memory is owned by KUnit, nothing to do here */
|
||||
}
|
||||
|
||||
static void tb_test_switch_put(void *data)
|
||||
{
|
||||
struct tb_switch *sw = data;
|
||||
|
||||
put_device(&sw->dev);
|
||||
}
|
||||
|
||||
static struct tb_switch *alloc_switch(struct kunit *test, u64 route,
|
||||
u8 upstream_port, u8 max_port_number)
|
||||
{
|
||||
|
|
@ -44,6 +56,15 @@ static struct tb_switch *alloc_switch(struct kunit *test, u64 route,
|
|||
if (!sw)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* HopID allocations take a reference to their routers and those devices
|
||||
* have to be initialized for that to work.
|
||||
*/
|
||||
sw->dev.release = tb_test_switch_release;
|
||||
device_initialize(&sw->dev);
|
||||
if (kunit_add_action_or_reset(test, tb_test_switch_put, sw))
|
||||
return NULL;
|
||||
|
||||
sw->config.upstream_port_number = upstream_port;
|
||||
sw->config.depth = tb_route_length(route);
|
||||
sw->config.route_hi = upper_32_bits(route);
|
||||
|
|
@ -1386,6 +1407,10 @@ static void tb_test_tunnel_pcie(struct kunit *test)
|
|||
tb_tunnel_put(tunnel1);
|
||||
}
|
||||
|
||||
static void tb_test_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void tb_test_tunnel_dp(struct kunit *test)
|
||||
{
|
||||
struct tb_switch *host, *dev;
|
||||
|
|
@ -1406,7 +1431,8 @@ static void tb_test_tunnel_dp(struct kunit *test)
|
|||
in = &host->ports[5];
|
||||
out = &dev->ports[13];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, tunnel);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
|
|
@ -1452,7 +1478,8 @@ static void tb_test_tunnel_dp_chain(struct kunit *test)
|
|||
in = &host->ports[5];
|
||||
out = &dev4->ports[14];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, tunnel);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
|
|
@ -1502,7 +1529,8 @@ static void tb_test_tunnel_dp_tree(struct kunit *test)
|
|||
in = &dev2->ports[13];
|
||||
out = &dev5->ports[13];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, tunnel);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
|
|
@ -1567,7 +1595,8 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test)
|
|||
in = &dev6->ports[13];
|
||||
out = &dev12->ports[13];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, tunnel);
|
||||
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
|
||||
|
|
@ -1637,7 +1666,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
|
|||
out2 = &dev5->ports[13];
|
||||
out3 = &dev4->ports[14];
|
||||
|
||||
tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, NULL, NULL);
|
||||
tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel1 != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1);
|
||||
|
|
@ -1645,7 +1675,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
|
|||
KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3);
|
||||
KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3);
|
||||
|
||||
tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, NULL, NULL);
|
||||
tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel2 != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2);
|
||||
|
|
@ -1653,7 +1684,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
|
|||
KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3);
|
||||
KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4);
|
||||
|
||||
tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, NULL, NULL);
|
||||
tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_TRUE(test, tunnel3 != NULL);
|
||||
KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP);
|
||||
KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3);
|
||||
|
|
@ -1751,7 +1783,8 @@ static void tb_test_tunnel_port_on_path(struct kunit *test)
|
|||
in = &dev2->ports[13];
|
||||
out = &dev5->ports[13];
|
||||
|
||||
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
|
||||
|
|
@ -2183,7 +2216,8 @@ static void tb_test_credit_alloc_dp(struct kunit *test)
|
|||
in = &host->ports[5];
|
||||
out = &dev->ports[14];
|
||||
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, tunnel);
|
||||
KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
|
||||
|
||||
|
|
@ -2419,7 +2453,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test,
|
|||
|
||||
in = &host->ports[5];
|
||||
out = &dev->ports[13];
|
||||
dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1);
|
||||
KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
|
||||
|
||||
|
|
@ -2456,7 +2491,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test,
|
|||
|
||||
in = &host->ports[6];
|
||||
out = &dev->ports[14];
|
||||
dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
|
||||
dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
|
||||
tb_test_dp_tunnel_active, NULL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2);
|
||||
KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
|
||||
|
||||
|
|
|
|||
|
|
@ -510,6 +510,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
|
|||
goto err_deactivate;
|
||||
}
|
||||
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
tb_tunnel_dbg(tunnel, "discovered\n");
|
||||
return tunnel;
|
||||
|
||||
|
|
@ -1093,8 +1094,14 @@ static void tb_dp_dprx_work(struct work_struct *work)
|
|||
struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work);
|
||||
struct tb *tb = tunnel->tb;
|
||||
|
||||
/*
|
||||
* The DPRX read can be canceled while this work is waiting for
|
||||
* tb->lock. Check the flag only once it is held: while the lock is
|
||||
* held the tunnel cannot be torn down under us and the adapters are
|
||||
* safe to access.
|
||||
*/
|
||||
mutex_lock(&tb->lock);
|
||||
if (!tunnel->dprx_canceled) {
|
||||
mutex_lock(&tb->lock);
|
||||
if (tb_dp_is_usb4(tunnel->src_port->sw) &&
|
||||
tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) {
|
||||
if (ktime_before(ktime_get(), tunnel->dprx_timeout)) {
|
||||
|
|
@ -1106,41 +1113,42 @@ static void tb_dp_dprx_work(struct work_struct *work)
|
|||
} else {
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
|
||||
if (tunnel->callback)
|
||||
tunnel->callback(tunnel, tunnel->callback_data);
|
||||
tunnel->callback(tunnel, tunnel->callback_data);
|
||||
tb_tunnel_put(tunnel);
|
||||
tb_domain_put(tb);
|
||||
}
|
||||
|
||||
static int tb_dp_dprx_start(struct tb_tunnel *tunnel)
|
||||
{
|
||||
/*
|
||||
* Bump up the reference to keep the tunnel around. It will be
|
||||
* dropped in tb_dp_dprx_stop() once the tunnel is deactivated.
|
||||
* Bump up the references to keep the tunnel and the domain around
|
||||
* until the work has run or has been canceled.
|
||||
*/
|
||||
tb_tunnel_get(tunnel);
|
||||
tb_domain_get(tunnel->tb);
|
||||
|
||||
tunnel->dprx_started = true;
|
||||
tunnel->dprx_canceled = false;
|
||||
tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
|
||||
queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0);
|
||||
|
||||
if (tunnel->callback) {
|
||||
tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
|
||||
queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0);
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
return tb_dp_is_usb4(tunnel->src_port->sw) ?
|
||||
tb_dp_wait_dprx(tunnel, dprx_timeout) : 0;
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
static void tb_dp_dprx_stop(struct tb_tunnel *tunnel)
|
||||
{
|
||||
struct tb *tb = tunnel->tb;
|
||||
|
||||
if (tunnel->dprx_started) {
|
||||
tunnel->dprx_started = false;
|
||||
tunnel->dprx_canceled = true;
|
||||
if (cancel_delayed_work(&tunnel->dprx_work))
|
||||
if (cancel_delayed_work(&tunnel->dprx_work)) {
|
||||
tb_tunnel_put(tunnel);
|
||||
tb_domain_put(tb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1582,20 +1590,28 @@ static void tb_dp_dump(struct tb_tunnel *tunnel)
|
|||
* @tb: Pointer to the domain structure
|
||||
* @in: DP in adapter
|
||||
* @alloc_hopid: Allocate HopIDs from visited ports
|
||||
* @callback: Callback that is called when the DP tunnel is fully
|
||||
* activated (or there is an error)
|
||||
* @callback_data: Data for @callback
|
||||
*
|
||||
* If @in adapter is active, follows the tunnel to the DP out adapter
|
||||
* and back. Returns the discovered tunnel or %NULL if there was no
|
||||
* tunnel.
|
||||
* tunnel. See tb_tunnel_alloc_dp() for @callback.
|
||||
*
|
||||
* Return: Pointer to &struct tb_tunnel or %NULL if no tunnel found.
|
||||
*/
|
||||
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
bool alloc_hopid)
|
||||
bool alloc_hopid,
|
||||
void (*callback)(struct tb_tunnel *, void *),
|
||||
void *callback_data)
|
||||
{
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb_port *port;
|
||||
struct tb_path *path;
|
||||
|
||||
if (WARN_ON(!callback))
|
||||
return NULL;
|
||||
|
||||
if (!tb_dp_port_is_enabled(in))
|
||||
return NULL;
|
||||
|
||||
|
|
@ -1611,6 +1627,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
|||
tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth;
|
||||
tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth;
|
||||
tunnel->src_port = in;
|
||||
tunnel->callback = callback;
|
||||
tunnel->callback_data = callback_data;
|
||||
INIT_DELAYED_WORK(&tunnel->dprx_work, tb_dp_dprx_work);
|
||||
|
||||
path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1,
|
||||
&tunnel->dst_port, "Video", alloc_hopid);
|
||||
|
|
@ -1656,6 +1675,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
|||
|
||||
tb_dp_dump(tunnel);
|
||||
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
tb_tunnel_dbg(tunnel, "discovered\n");
|
||||
return tunnel;
|
||||
|
||||
|
|
@ -1677,16 +1697,16 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
|||
* %0 if no available bandwidth.
|
||||
* @max_down: Maximum available downstream bandwidth for the DP tunnel.
|
||||
* %0 if no available bandwidth.
|
||||
* @callback: Optional callback that is called when the DP tunnel is
|
||||
* fully activated (or there is an error)
|
||||
* @callback_data: Optional data for @callback
|
||||
* @callback: Callback that is called when the DP tunnel is fully
|
||||
* activated (or there is an error)
|
||||
* @callback_data: Data for @callback
|
||||
*
|
||||
* Allocates a tunnel between @in and @out that is capable of tunneling
|
||||
* Display Port traffic. If @callback is not %NULL it will be called
|
||||
* after tb_tunnel_activate() once the tunnel has been fully activated.
|
||||
* It can call tb_tunnel_is_active() to check if activation was
|
||||
* successful (or if it returns %false there was some sort of issue).
|
||||
* The @callback is called without @tb->lock held.
|
||||
* Display Port traffic. The @callback is called after tb_tunnel_activate()
|
||||
* once the tunnel has been fully activated. It can call
|
||||
* tb_tunnel_is_active() to check if activation was successful (or if it
|
||||
* returns %false there was some sort of issue). The @callback is called
|
||||
* without @tb->lock held.
|
||||
*
|
||||
* Return: Pointer to @struct tb_tunnel or %NULL in case of failure.
|
||||
*/
|
||||
|
|
@ -1701,7 +1721,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
|||
struct tb_path *path;
|
||||
bool pm_support;
|
||||
|
||||
if (WARN_ON(!in->cap_adap || !out->cap_adap))
|
||||
if (WARN_ON(!in->cap_adap || !out->cap_adap || !callback))
|
||||
return NULL;
|
||||
|
||||
tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP);
|
||||
|
|
@ -2288,6 +2308,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
|
|||
tb_usb3_reclaim_available_bandwidth;
|
||||
}
|
||||
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
tb_tunnel_dbg(tunnel, "discovered\n");
|
||||
return tunnel;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ enum tb_tunnel_state {
|
|||
* @dprx_canceled: Was DPRX capabilities read poll canceled
|
||||
* @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes
|
||||
* @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read
|
||||
* @callback: Optional callback called when DP tunnel is fully activated
|
||||
* @callback_data: Optional data for @callback
|
||||
* @callback: Callback called when DP tunnel is fully activated
|
||||
* @callback_data: Data for @callback
|
||||
* @paths: All paths required by the tunnel
|
||||
*/
|
||||
struct tb_tunnel {
|
||||
|
|
@ -117,7 +117,9 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
|
|||
bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up,
|
||||
int *reserved_down);
|
||||
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
|
||||
bool alloc_hopid);
|
||||
bool alloc_hopid,
|
||||
void (*callback)(struct tb_tunnel *, void *),
|
||||
void *callback_data);
|
||||
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
|
||||
struct tb_port *out, int link_nr,
|
||||
int max_up, int max_down,
|
||||
|
|
|
|||
|
|
@ -377,6 +377,10 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route,
|
|||
|
||||
len += sizeof(res->hdr.xd_hdr) / 4;
|
||||
len -= sizeof(*res) / 4;
|
||||
if (len > TB_XDP_PROPERTIES_MAX_DATA_LENGTH) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (res->offset != req.offset) {
|
||||
ret = -EINVAL;
|
||||
|
|
@ -1814,7 +1818,6 @@ static void tb_xdomain_state_work(struct work_struct *work)
|
|||
tb_xdomain_failed(xd);
|
||||
} else {
|
||||
xd->state = XDOMAIN_STATE_ENUMERATED;
|
||||
tb_xdomain_queue_properties_changed(xd);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdns *cdns)
|
|||
usb_del_gadget(&priv_dev->gadget);
|
||||
devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
|
||||
|
||||
/* The works can still be queued until the IRQ is freed. */
|
||||
cancel_work_sync(&priv_dev->pending_status_wq);
|
||||
cancel_work_sync(&priv_dev->aligned_buf_wq);
|
||||
|
||||
cdns3_free_all_eps(priv_dev);
|
||||
|
||||
while (!list_empty(&priv_dev->aligned_buf_list)) {
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_dev *pdev,
|
|||
|
||||
if (pci_is_enabled(func)) {
|
||||
wrap = pci_get_drvdata(func);
|
||||
if (!wrap) {
|
||||
dev_err(&pdev->dev,
|
||||
"second function not initialized, retrying\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
} else {
|
||||
wrap = kzalloc_obj(*wrap);
|
||||
if (!wrap)
|
||||
|
|
|
|||
|
|
@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platform_device *pdev)
|
|||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
err = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (err)
|
||||
if (err) {
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
|
||||
usb->needs_double_reset = true;
|
||||
|
|
|
|||
|
|
@ -331,7 +331,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
|
|||
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
acm->ctrlin = newctrl;
|
||||
acm->oldcount = acm->iocount;
|
||||
|
||||
if (difference & USB_CDC_SERIAL_STATE_DSR)
|
||||
acm->iocount.dsr++;
|
||||
|
|
@ -788,6 +787,9 @@ static void acm_port_shutdown(struct tty_port *port)
|
|||
usb_autopm_put_interface_async(acm->control);
|
||||
}
|
||||
|
||||
if (acm->disconnected)
|
||||
return;
|
||||
|
||||
acm_unpoison_urbs(acm);
|
||||
|
||||
if (acm->quirks & ALWAYS_POLL_CTRL) {
|
||||
|
|
@ -1027,11 +1029,16 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
|
|||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct async_icount old, new;
|
||||
|
||||
do {
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
old = acm->iocount;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
|
||||
add_wait_queue(&acm->wioctl, &wait);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
old = acm->oldcount;
|
||||
new = acm->iocount;
|
||||
acm->oldcount = new;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
|
||||
if ((arg & TIOCM_DSR) &&
|
||||
|
|
@ -1044,22 +1051,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
|
|||
old.rng != new.rng)
|
||||
break;
|
||||
|
||||
add_wait_queue(&acm->wioctl, &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
remove_wait_queue(&acm->wioctl, &wait);
|
||||
if (acm->disconnected) {
|
||||
if (arg & TIOCM_CD)
|
||||
break;
|
||||
else
|
||||
rv = -ENODEV;
|
||||
} else {
|
||||
if (signal_pending(current))
|
||||
rv = -ERESTARTSYS;
|
||||
rv = -ENODEV;
|
||||
break;
|
||||
}
|
||||
} while (!rv);
|
||||
|
||||
|
||||
schedule();
|
||||
|
||||
if (signal_pending(current)) {
|
||||
rv = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&acm->wioctl, &wait);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ struct acm {
|
|||
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
|
||||
unsigned int ctrlout; /* output control lines (DTR, RTS) */
|
||||
struct async_icount iocount; /* counters for control line changes */
|
||||
struct async_icount oldcount; /* for comparison of counter */
|
||||
wait_queue_head_t wioctl; /* for ioctl */
|
||||
unsigned int writesize; /* max packet size for the output bulk endpoint */
|
||||
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
|
||||
|
|
|
|||
|
|
@ -73,8 +73,11 @@
|
|||
/*
|
||||
* Give SS hubs 200ms time after wake to train downstream links before
|
||||
* assuming no port activity and allowing hub to runtime suspend back.
|
||||
* Root hubs have no upstream hub whose wake propagation needs to be
|
||||
* accounted for, so they need less time, use 120ms for them.
|
||||
*/
|
||||
#define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */
|
||||
#define USB_SS_RH_PORT_U0_WAKE_TIME 120 /* ms */
|
||||
|
||||
/* Protect struct usb_device->state and ->children members
|
||||
* Note: Both are also protected by ->dev.sem, except that ->state can
|
||||
|
|
@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
|
||||
queue_delayed_work(system_power_efficient_wq,
|
||||
&hub->post_resume_work,
|
||||
msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
|
||||
msecs_to_jiffies(hdev->parent ?
|
||||
USB_SS_PORT_U0_WAKE_TIME :
|
||||
USB_SS_RH_PORT_U0_WAKE_TIME));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
|
|||
*
|
||||
* Disables the endpoint for URB submission and nukes all pending URBs.
|
||||
* If @reset_hardware is set then also deallocates hcd/hardware state
|
||||
* for the endpoint.
|
||||
* for the endpoint (clearing both ep_in and ep_out pointers for
|
||||
* bidirectional non-ep0 control endpoints).
|
||||
*/
|
||||
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
|
||||
bool reset_hardware)
|
||||
|
|
@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
|
|||
dev->ep_in[epnum] = NULL;
|
||||
}
|
||||
if (ep) {
|
||||
if (reset_hardware && epnum != 0 &&
|
||||
usb_endpoint_xfer_control(&ep->desc)) {
|
||||
dev->ep_out[epnum] = NULL;
|
||||
dev->ep_in[epnum] = NULL;
|
||||
}
|
||||
ep->enabled = 0;
|
||||
usb_hcd_flush_endpoint(dev, ep);
|
||||
if (reset_hardware)
|
||||
|
|
|
|||
|
|
@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
|
||||
cancel_work_sync(&hsotg->phy_reset_work);
|
||||
|
||||
timer_delete(&hsotg->wkp_timer);
|
||||
}
|
||||
|
||||
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* Turn off all host-specific interrupts */
|
||||
dwc2_disable_host_interrupts(hsotg);
|
||||
timer_shutdown_sync(&hsotg->wkp_timer);
|
||||
|
||||
dwc2_hcd_free(hsotg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
#define DWC3_ISOC_MAX_RETRIES 5
|
||||
#define DWC3_ERR_RECOVERY_MAX 3
|
||||
|
||||
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
|
||||
#define DWC3_EVENT_BUFFERS_SIZE 4096
|
||||
|
|
@ -841,6 +842,12 @@ enum dwc3_link_state {
|
|||
DWC3_LINK_STATE_MASK = 0x0f,
|
||||
};
|
||||
|
||||
enum dwc3_err_state {
|
||||
DWC3_ERR_NONE = 0,
|
||||
DWC3_ERR_RECOVERY,
|
||||
DWC3_ERR_UNRECOVERABLE,
|
||||
};
|
||||
|
||||
/* TRB Length, PCM and Status */
|
||||
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
|
||||
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
|
||||
|
|
@ -1004,6 +1011,7 @@ struct dwc3_glue_ops {
|
|||
/**
|
||||
* struct dwc3 - representation of our controller
|
||||
* @drd_work: workqueue used for role swapping
|
||||
* @err_recovery_work: workqueue used for controller error recovery
|
||||
* @ep0_trb: trb which is used for the ctrl_req
|
||||
* @bounce: address of bounce buffer
|
||||
* @setup_buf: used while precessing STD USB requests
|
||||
|
|
@ -1013,6 +1021,7 @@ struct dwc3_glue_ops {
|
|||
* @ep0_in_setup: one control transfer is completed and enter setup phase
|
||||
* @lock: for synchronizing
|
||||
* @mutex: for mode switching
|
||||
* @connect_mutex: for the pull-up and err_recovery_work
|
||||
* @dev: pointer to our struct device
|
||||
* @sysdev: pointer to the DMA-capable device
|
||||
* @xhci: pointer to our xHCI child
|
||||
|
|
@ -1079,6 +1088,9 @@ struct dwc3_glue_ops {
|
|||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
* @err_state: current error recovery state.
|
||||
* @err_recovery_count: number of consecutive error recovery attempts until
|
||||
* confirmed healthy and reset to 0 on reset event.
|
||||
* @speed: device speed (super, high, full, low)
|
||||
* @hwparams: copy of hwparams registers
|
||||
* @regset: debugfs pointer to regdump file
|
||||
|
|
@ -1189,6 +1201,7 @@ struct dwc3_glue_ops {
|
|||
*/
|
||||
struct dwc3 {
|
||||
struct work_struct drd_work;
|
||||
struct work_struct err_recovery_work;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *bounce;
|
||||
u8 *setup_buf;
|
||||
|
|
@ -1203,6 +1216,9 @@ struct dwc3 {
|
|||
/* mode switching lock */
|
||||
struct mutex mutex;
|
||||
|
||||
/* serializes pull-up run/stop vs error recovery */
|
||||
struct mutex connect_mutex;
|
||||
|
||||
struct device *dev;
|
||||
struct device *sysdev;
|
||||
|
||||
|
|
@ -1332,6 +1348,9 @@ struct dwc3 {
|
|||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
enum dwc3_link_state link_state;
|
||||
enum dwc3_err_state err_state;
|
||||
|
||||
u32 err_recovery_count;
|
||||
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
|
|||
|
||||
err_pm_disable:
|
||||
clk_disable_unprepare(am62->usb2_refclk);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
|
||||
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
|
||||
dep->name);
|
||||
|
|
@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
ret = __dwc3_gadget_ep0_set_halt(ep, value);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
|
|
|||
|
|
@ -1149,6 +1149,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
|||
return 0;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
|
@ -2060,6 +2064,10 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
|
@ -2292,6 +2300,10 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
|
@ -2306,6 +2318,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
dep->flags |= DWC3_EP_WEDGE;
|
||||
|
||||
if (dep->number == 0 || dep->number == 1)
|
||||
|
|
@ -2437,6 +2453,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
if (!dwc->gadget->wakeup_armed) {
|
||||
dev_err(dwc->dev, "not armed for remote wakeup\n");
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
@ -2464,6 +2484,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->err_state != DWC3_ERR_NONE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
/*
|
||||
* If the link is in U3, signal for remote wakeup and wait for the
|
||||
* link to transition to U0 before sending device notification.
|
||||
|
|
@ -2833,10 +2857,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
|
||||
/* Serialize against dwc3_err_recovery_work() */
|
||||
mutex_lock(&dwc->connect_mutex);
|
||||
if (!is_on)
|
||||
ret = dwc3_gadget_soft_disconnect(dwc);
|
||||
else
|
||||
ret = dwc3_gadget_soft_connect(dwc);
|
||||
mutex_unlock(&dwc->connect_mutex);
|
||||
|
||||
pm_runtime_put(dwc->dev);
|
||||
|
||||
|
|
@ -4153,6 +4180,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
|
||||
dwc->suspended = false;
|
||||
|
||||
/* The controller is recovered. */
|
||||
if (dwc->err_state == DWC3_ERR_NONE)
|
||||
dwc->err_recovery_count = 0;
|
||||
|
||||
/*
|
||||
* Ideally, dwc3_reset_gadget() would trigger the function
|
||||
* drivers to stop any active transfers through ep disable.
|
||||
|
|
@ -4640,6 +4671,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_schedule_err_recovery(struct dwc3 *dwc)
|
||||
{
|
||||
dwc->err_state = DWC3_ERR_RECOVERY;
|
||||
schedule_work(&dwc->err_recovery_work);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
||||
{
|
||||
struct dwc3 *dwc = evt->dwc;
|
||||
|
|
@ -4673,9 +4710,21 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|||
return IRQ_NONE;
|
||||
|
||||
if (count > evt->length) {
|
||||
dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
|
||||
dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
|
||||
count, evt->length);
|
||||
return IRQ_NONE;
|
||||
/*
|
||||
* This is a fatal error - the driver and controller are out of
|
||||
* sync on which event has been consumed. Reinitializing the
|
||||
* controller is required to recover. Write the bogus count back
|
||||
* to GEVNTCOUNT to clear the IRQ source, consistent with the
|
||||
* stale event clearing in dwc3_event_buffers_setup(), then
|
||||
* schedule error recovery.
|
||||
*/
|
||||
spin_lock(&dwc->lock);
|
||||
dwc3_schedule_err_recovery(dwc);
|
||||
spin_unlock(&dwc->lock);
|
||||
dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
evt->count = count;
|
||||
|
|
@ -4735,6 +4784,57 @@ static void dwc_gadget_release(struct device *dev)
|
|||
kfree(gadget);
|
||||
}
|
||||
|
||||
static void dwc3_err_recovery_work(struct work_struct *work)
|
||||
{
|
||||
struct dwc3 *dwc = container_of(work, struct dwc3, err_recovery_work);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* serializes against dwc3_gadget_pullup() */
|
||||
mutex_lock(&dwc->connect_mutex);
|
||||
|
||||
ret = dwc3_gadget_soft_disconnect(dwc);
|
||||
|
||||
dwc3_disconnect_gadget_sleepable(dwc);
|
||||
|
||||
if (ret)
|
||||
goto err_unrecoverable;
|
||||
|
||||
if (dwc->softconnect) {
|
||||
u32 count;
|
||||
/*
|
||||
* Wait irq to finish before soft_connect resets evt->lpos and
|
||||
* the event buffer registers to avoid racing with
|
||||
* dwc3_process_event_buf().
|
||||
*/
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
count = ++dwc->err_recovery_count;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
if (count > DWC3_ERR_RECOVERY_MAX)
|
||||
goto err_unrecoverable;
|
||||
|
||||
ret = dwc3_gadget_soft_connect(dwc);
|
||||
if (ret)
|
||||
goto err_unrecoverable;
|
||||
}
|
||||
mutex_unlock(&dwc->connect_mutex);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->err_state = DWC3_ERR_NONE;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return;
|
||||
|
||||
err_unrecoverable:
|
||||
dev_err(dwc->dev, "Unable to recover the controller\n");
|
||||
mutex_unlock(&dwc->connect_mutex);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->err_state = DWC3_ERR_UNRECOVERABLE;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_init - initializes gadget related registers
|
||||
* @dwc: pointer to our controller context structure
|
||||
|
|
@ -4778,6 +4878,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
init_completion(&dwc->ep0_in_setup);
|
||||
INIT_WORK(&dwc->err_recovery_work, dwc3_err_recovery_work);
|
||||
mutex_init(&dwc->connect_mutex);
|
||||
dwc->gadget = kzalloc_obj(struct usb_gadget);
|
||||
if (!dwc->gadget) {
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -4874,6 +4976,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
if (!dwc->gadget)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&dwc->err_recovery_work);
|
||||
mutex_destroy(&dwc->connect_mutex);
|
||||
dwc3_enable_susphy(dwc, true);
|
||||
usb_del_gadget(dwc->gadget);
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
|
|
|||
|
|
@ -1877,8 +1877,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
break;
|
||||
case FUNCTIONFS_ENDPOINT_DESC:
|
||||
{
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct usb_endpoint_descriptor desc1;
|
||||
int desc_idx;
|
||||
struct usb_endpoint_descriptor desc1, *desc;
|
||||
|
||||
switch (epfile->ffs->gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
|
|
@ -1892,7 +1893,17 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
desc_idx = 0;
|
||||
}
|
||||
|
||||
desc = epfile->ep->descs[desc_idx];
|
||||
do {
|
||||
desc = epfile->ep->descs[desc_idx];
|
||||
} while (!desc && --desc_idx >= 0);
|
||||
|
||||
if (!desc)
|
||||
desc = epfile->ep->ep->desc;
|
||||
if (!desc) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&desc1, desc, desc->bLength);
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
|
|
|||
|
|
@ -1633,8 +1633,8 @@ struct f_midi2_usb_config {
|
|||
|
||||
/* MIDI 1.0 jacks */
|
||||
unsigned char jack_in, jack_out, jack_id;
|
||||
struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
|
||||
struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
|
||||
struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES * 2];
|
||||
struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES * 2];
|
||||
};
|
||||
|
||||
static int append_config(struct f_midi2_usb_config *config, void *d)
|
||||
|
|
|
|||
|
|
@ -797,6 +797,9 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
|
||||
{
|
||||
if (cmd >= ARRAY_SIZE(con->data))
|
||||
return -EINVAL;
|
||||
|
||||
con->data[cmd] = value;
|
||||
|
||||
return 0;
|
||||
|
|
@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
|
|||
|
||||
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
|
||||
{
|
||||
if (cmd >= ARRAY_SIZE(con->data))
|
||||
return -EINVAL;
|
||||
|
||||
return con->data[cmd];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platform_device *pdev)
|
|||
for (i = 0; i < vhub->max_ports; i++)
|
||||
ast_vhub_del_dev(&vhub->ports[i].dev);
|
||||
|
||||
/* Final drain; the worker takes vhub->lock, so stay outside of it */
|
||||
cancel_work_sync(&vhub->wake_work);
|
||||
|
||||
spin_lock_irqsave(&vhub->lock, flags);
|
||||
|
||||
/* Mask & ack all interrupts */
|
||||
|
|
@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&vhub->lock);
|
||||
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
|
||||
vhub->pdev = pdev;
|
||||
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
|
||||
VHUB_IRQ_DEV1_BIT);
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usb_gadget* gadget)
|
|||
int rc = -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&d->vhub->lock, flags);
|
||||
if (!d->wakeup_en)
|
||||
if (!d->wakeup_en || !d->registered)
|
||||
goto err;
|
||||
|
||||
DDBG(d, "Device initiated wakeup\n");
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_vhub *vhub,
|
|||
ast_vhub_send_host_wakeup(vhub);
|
||||
}
|
||||
|
||||
static void ast_vhub_wake_work(struct work_struct *work)
|
||||
void ast_vhub_wake_work(struct work_struct *work)
|
||||
{
|
||||
struct ast_vhub *vhub = container_of(work,
|
||||
struct ast_vhub,
|
||||
|
|
@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct work_struct *work)
|
|||
|
||||
void ast_vhub_hub_wake_all(struct ast_vhub *vhub)
|
||||
{
|
||||
lockdep_assert_held(&vhub->lock);
|
||||
|
||||
/*
|
||||
* A device is trying to wake the world, because this
|
||||
* can recurse into the device, we break the call chain
|
||||
|
|
@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub)
|
|||
int ast_vhub_init_hub(struct ast_vhub *vhub)
|
||||
{
|
||||
vhub->speed = USB_SPEED_UNKNOWN;
|
||||
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
|
||||
|
||||
return ast_vhub_init_desc(vhub);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub);
|
|||
void ast_vhub_hub_resume(struct ast_vhub *vhub);
|
||||
void ast_vhub_hub_reset(struct ast_vhub *vhub);
|
||||
void ast_vhub_hub_wake_all(struct ast_vhub *vhub);
|
||||
void ast_vhub_wake_work(struct work_struct *work);
|
||||
|
||||
/* dev.c */
|
||||
int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx);
|
||||
|
|
|
|||
|
|
@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
|
|||
{
|
||||
bool fifo = req == &dum->fifo_req;
|
||||
|
||||
++dum->callback_usage;
|
||||
spin_unlock(&dum->lock);
|
||||
usb_gadget_giveback_request(_ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
--dum->callback_usage;
|
||||
if (fifo)
|
||||
dum->fifo_req_busy = 0;
|
||||
}
|
||||
|
|
@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
req->req.complete = fifo_complete;
|
||||
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
++dum->callback_usage;
|
||||
spin_unlock(&dum->lock);
|
||||
_req->actual = _req->length;
|
||||
_req->status = 0;
|
||||
usb_gadget_giveback_request(_ep, _req);
|
||||
spin_lock(&dum->lock);
|
||||
--dum->callback_usage;
|
||||
} else
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
|
|
|
|||
|
|
@ -378,8 +378,22 @@ struct octeon_hcd {
|
|||
struct cvmx_usb_transaction *active_split;
|
||||
struct cvmx_usb_tx_fifo periodic;
|
||||
struct cvmx_usb_tx_fifo nonperiodic;
|
||||
struct hrtimer sof_timer;
|
||||
};
|
||||
|
||||
static int cvmx_usb_poll(struct octeon_hcd *usb);
|
||||
|
||||
static enum hrtimer_restart octeon_usb_sof_timer(struct hrtimer *t)
|
||||
{
|
||||
struct octeon_hcd *usb = container_of(t, struct octeon_hcd, sof_timer);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&usb->lock, flags);
|
||||
cvmx_usb_poll(usb);
|
||||
spin_unlock_irqrestore(&usb->lock, flags);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/*
|
||||
* This macro logically sets a single field in a CSR. It does the sequence
|
||||
* read, modify, and write
|
||||
|
|
@ -578,7 +592,7 @@ static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type)
|
|||
int result;
|
||||
u64 address = CVMX_USBCX_GRSTCTL(usb->index);
|
||||
u64 done = cvmx_get_cycle() + 100 *
|
||||
(u64)octeon_get_clock_rate / 1000000;
|
||||
(u64)octeon_get_clock_rate() / 1000000;
|
||||
union cvmx_usbcx_grstctl c;
|
||||
|
||||
while (1) {
|
||||
|
|
@ -853,6 +867,14 @@ static int cvmx_usb_initialize(struct device *dev,
|
|||
* USBC_GAHBCFG[PTXFEMPLVL]
|
||||
* Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1
|
||||
*/
|
||||
usbcx_gahbcfg.u32 = cvmx_usb_read_csr32(usb,
|
||||
CVMX_USBCX_GHWCFG3(usb->index));
|
||||
if (usbcx_gahbcfg.u32 == 0xffffffff || usbcx_gahbcfg.u32 == 0) {
|
||||
dev_err(dev, "USB core is not responding (GHWCFG3=0x%08x)\n",
|
||||
usbcx_gahbcfg.u32);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
usbcx_gahbcfg.u32 = 0;
|
||||
usbcx_gahbcfg.s.dmaen = !(usb->init_flags &
|
||||
CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
|
||||
|
|
@ -1900,6 +1922,7 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
|
|||
int channel;
|
||||
struct cvmx_usb_pipe *pipe;
|
||||
int need_sof;
|
||||
u64 min_due;
|
||||
enum cvmx_usb_transfer ttype;
|
||||
|
||||
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
|
||||
|
|
@ -1940,15 +1963,33 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
|
|||
* future that might need to be scheduled
|
||||
*/
|
||||
need_sof = 0;
|
||||
min_due = ~0ull;
|
||||
for (ttype = CVMX_USB_TRANSFER_CONTROL;
|
||||
ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) {
|
||||
list_for_each_entry(pipe, &usb->active_pipes[ttype], node) {
|
||||
if (pipe->next_tx_frame > usb->frame_number) {
|
||||
need_sof = 1;
|
||||
break;
|
||||
}
|
||||
if (pipe->next_tx_frame > usb->frame_number &&
|
||||
pipe->next_tx_frame < min_due)
|
||||
min_due = pipe->next_tx_frame;
|
||||
}
|
||||
}
|
||||
if (min_due != ~0ull) {
|
||||
u64 delta = min_due - usb->frame_number;
|
||||
|
||||
/*
|
||||
* frame_number is resynced from HFNUM on every poll, so a
|
||||
* deadline that is many frames away does not need an
|
||||
* interrupt on every SOF to count them down - sleep on the
|
||||
* timer instead and keep SOF interrupts for deadlines within
|
||||
* a few frames. Stay well below the 16383-frame wrap of
|
||||
* HFNUM. One (micro)frame is 125us in high-speed mode.
|
||||
*/
|
||||
if (delta <= 4 || delta > 8000)
|
||||
need_sof = 1;
|
||||
else
|
||||
hrtimer_start(&usb->sof_timer,
|
||||
ns_to_ktime((delta - 2) * 125000),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
|
||||
cvmx_usbcx_gintmsk, sofmsk, need_sof);
|
||||
}
|
||||
|
|
@ -3638,6 +3679,8 @@ static int octeon_usb_probe(struct platform_device *pdev)
|
|||
usb = (struct octeon_hcd *)hcd->hcd_priv;
|
||||
|
||||
spin_lock_init(&usb->lock);
|
||||
hrtimer_setup(&usb->sof_timer, octeon_usb_sof_timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
usb->init_flags = initialize_flags;
|
||||
|
||||
|
|
@ -3688,6 +3731,7 @@ static void octeon_usb_remove(struct platform_device *pdev)
|
|||
unsigned long flags;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
hrtimer_cancel(&usb->sof_timer);
|
||||
spin_lock_irqsave(&usb->lock, flags);
|
||||
status = cvmx_usb_shutdown(usb);
|
||||
spin_unlock_irqrestore(&usb->lock, flags);
|
||||
|
|
|
|||
|
|
@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
|
|||
|
||||
err_remove_hcd:
|
||||
usb_remove_hcd(hcd);
|
||||
device_wakeup_disable(hcd->self.controller);
|
||||
err:
|
||||
usb_put_hcd(hcd);
|
||||
return error;
|
||||
|
|
@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct platform_device *pdev)
|
|||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
device_wakeup_disable(hcd->self.controller);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev)
|
|||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
device_wakeup_disable(hcd->self.controller);
|
||||
s3c2410_stop_hc(dev);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev)
|
|||
struct spear_ohci *sohci_p = to_spear_ohci(hcd);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
device_wakeup_disable(hcd->self.controller);
|
||||
clk_disable_unprepare(sohci_p->clk);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struct platform_device *dev)
|
|||
int clk;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
device_wakeup_disable(hcd->self.controller);
|
||||
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
|
|
|||
|
|
@ -165,7 +165,11 @@ int usb_serial_bus_register(struct usb_serial_driver *driver)
|
|||
|
||||
void usb_serial_bus_deregister(struct usb_serial_driver *driver)
|
||||
{
|
||||
free_dynids(driver);
|
||||
driver_unregister(&driver->driver);
|
||||
free_dynids(driver);
|
||||
}
|
||||
|
||||
void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver)
|
||||
{
|
||||
driver_remove_file(&driver->driver, &driver_attr_new_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = {
|
|||
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
|
||||
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
|
||||
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
|
||||
{ USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */
|
||||
{ USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */
|
||||
{ USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */
|
||||
{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
|
|||
void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
unsigned int bps;
|
||||
unsigned long period;
|
||||
unsigned long expire;
|
||||
|
|
@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
|
|||
__func__, jiffies_to_msecs(timeout),
|
||||
jiffies_to_msecs(period));
|
||||
expire = jiffies + timeout;
|
||||
while (!port->serial->type->tx_empty(port)) {
|
||||
for (;;) {
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty) || port->serial->type->tx_empty(port)) {
|
||||
mutex_unlock(&tport->mutex);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&tport->mutex);
|
||||
|
||||
schedule_timeout_interruptible(period);
|
||||
if (signal_pending(current))
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ static void option_instat_callback(struct urb *urb);
|
|||
#define QUECTEL_PRODUCT_EM060K_12a 0x012a
|
||||
#define QUECTEL_PRODUCT_EM060K_12b 0x012b
|
||||
#define QUECTEL_PRODUCT_EM060K_12c 0x012c
|
||||
#define QUECTEL_PRODUCT_RG660QB 0x013d
|
||||
#define QUECTEL_PRODUCT_EG91 0x0191
|
||||
#define QUECTEL_PRODUCT_EG95 0x0195
|
||||
#define QUECTEL_PRODUCT_BG96 0x0296
|
||||
|
|
@ -280,6 +281,7 @@ static void option_instat_callback(struct urb *urb);
|
|||
#define QUECTEL_PRODUCT_EC200U 0x0901
|
||||
#define QUECTEL_PRODUCT_EG912Y 0x6001
|
||||
#define QUECTEL_PRODUCT_EC200S_CN 0x6002
|
||||
#define QUECTEL_PRODUCT_EG060W 0x6004
|
||||
#define QUECTEL_PRODUCT_EC200A 0x6005
|
||||
#define QUECTEL_PRODUCT_EG916Q 0x6007
|
||||
#define QUECTEL_PRODUCT_EM061K_LWW 0x6008
|
||||
|
|
@ -1268,12 +1270,15 @@ static const struct usb_device_id option_ids[] = {
|
|||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) },
|
||||
|
|
@ -2168,6 +2173,8 @@ static const struct usb_device_id option_ids[] = {
|
|||
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */
|
||||
.driver_info = RSVD(7) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff),
|
||||
.driver_info = RSVD(4) },
|
||||
{ USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */
|
||||
.driver_info = RSVD(3) | RSVD(4) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */
|
||||
|
|
@ -2429,6 +2436,13 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = RSVD(5) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */
|
||||
.driver_info = RSVD(3) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) }, /* Compal EXC-T1 */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) },
|
||||
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
|
||||
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
|
||||
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#define QT2_XMIT_FLUSH 0x05 /* no following info */
|
||||
#define QT2_CONTROL_ESCAPE 0xff /* pass through previous 2 control bytes */
|
||||
|
||||
#define MIN_BAUD_RATE 50
|
||||
#define MAX_BAUD_RATE 921600
|
||||
#define DEFAULT_BAUD_RATE 9600
|
||||
|
||||
|
|
@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int baudrate)
|
|||
|
||||
static inline int qt2_set_port_config(struct usb_device *dev,
|
||||
unsigned char port_number,
|
||||
u16 baudrate, u16 lcr)
|
||||
speed_t baudrate, u16 lcr)
|
||||
{
|
||||
int divisor = calc_baud_divisor(baudrate);
|
||||
u16 index = ((u16) (lcr << 8) | (u16) (port_number));
|
||||
|
|
@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_struct *tty,
|
|||
struct usb_device *dev = port->serial->dev;
|
||||
struct qt2_port_private *port_priv;
|
||||
struct ktermios *termios = &tty->termios;
|
||||
u16 baud;
|
||||
unsigned int cflag = termios->c_cflag;
|
||||
u16 new_lcr = 0;
|
||||
speed_t baud;
|
||||
int status;
|
||||
|
||||
port_priv = usb_get_serial_port_data(port);
|
||||
|
|
@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_struct *tty,
|
|||
if (!baud)
|
||||
baud = 9600;
|
||||
|
||||
if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
|
||||
if (old_termios)
|
||||
baud = tty_termios_baud_rate(old_termios);
|
||||
else
|
||||
baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
|
||||
|
||||
tty_encode_baud_rate(tty, baud, baud);
|
||||
|
||||
if (!baud)
|
||||
baud = 9600;
|
||||
}
|
||||
|
||||
status = qt2_set_port_config(dev, port_priv->device_port, baud,
|
||||
new_lcr);
|
||||
if (status < 0)
|
||||
|
|
@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
port_priv->device_port = (u8) device_port;
|
||||
|
||||
if (tty)
|
||||
qt2_set_termios(tty, port, &tty->termios);
|
||||
qt2_set_termios(tty, port, NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR)
|
||||
|
||||
#define MIN_BAUD_RATE 50
|
||||
#define MAX_BAUD_RATE 460800
|
||||
|
||||
#define ATC_DISABLED 0x00
|
||||
|
|
@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tty_struct *tty,
|
|||
{
|
||||
struct usb_device *dev = port->serial->dev;
|
||||
struct ktermios *termios = &tty->termios;
|
||||
u16 baud, divisor, remainder;
|
||||
speed_t baud, remainder;
|
||||
unsigned int cflag = termios->c_cflag;
|
||||
u16 urb_value = 0; /* will hold the new flags */
|
||||
u16 divisor;
|
||||
int result;
|
||||
|
||||
if (cflag & PARENB) {
|
||||
|
|
@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tty_struct *tty,
|
|||
if (!baud)
|
||||
baud = 9600;
|
||||
|
||||
if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
|
||||
if (old_termios)
|
||||
baud = tty_termios_baud_rate(old_termios);
|
||||
else
|
||||
baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
|
||||
|
||||
tty_encode_baud_rate(tty, baud, baud);
|
||||
|
||||
if (!baud)
|
||||
baud = 9600;
|
||||
}
|
||||
|
||||
dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
|
||||
|
||||
|
||||
|
|
@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
|
||||
|
||||
if (tty)
|
||||
ssu100_set_termios(tty, port, &tty->termios);
|
||||
ssu100_set_termios(tty, port, NULL);
|
||||
|
||||
return usb_serial_generic_open(tty, port);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -400,17 +400,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty)
|
|||
static void serial_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
||||
dev_dbg(&port->dev, "%s\n", __func__);
|
||||
|
||||
if (!port->serial->type->wait_until_sent)
|
||||
return;
|
||||
|
||||
mutex_lock(&serial->disc_mutex);
|
||||
if (!serial->disconnected)
|
||||
port->serial->type->wait_until_sent(tty, timeout);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
port->serial->type->wait_until_sent(tty, timeout);
|
||||
}
|
||||
|
||||
static void serial_throttle(struct tty_struct *tty)
|
||||
|
|
@ -438,8 +434,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
|
|||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
unsigned int close_delay, closing_wait;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty)) {
|
||||
ret = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
close_delay = jiffies_to_msecs(tport->close_delay) / 10;
|
||||
closing_wait = tport->closing_wait;
|
||||
|
|
@ -452,10 +453,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
|
|||
|
||||
if (port->serial->type->get_serial)
|
||||
port->serial->type->get_serial(tty, ss);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&tport->mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
|
||||
|
|
@ -471,6 +472,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
|
|||
closing_wait = msecs_to_jiffies(closing_wait * 10);
|
||||
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty)) {
|
||||
ret = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
if (close_delay != tport->close_delay ||
|
||||
|
|
@ -498,6 +503,7 @@ static int serial_ioctl(struct tty_struct *tty,
|
|||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
int retval = -ENOIOCTLCMD;
|
||||
|
||||
dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd);
|
||||
|
|
@ -508,8 +514,21 @@ static int serial_ioctl(struct tty_struct *tty,
|
|||
retval = port->serial->type->tiocmiwait(tty, arg);
|
||||
break;
|
||||
default:
|
||||
if (port->serial->type->ioctl)
|
||||
if (!port->serial->type->ioctl)
|
||||
break;
|
||||
|
||||
if (cmd == TIOCSRS485)
|
||||
down_write(&tty->termios_rwsem);
|
||||
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty))
|
||||
retval = -EIO;
|
||||
else
|
||||
retval = port->serial->type->ioctl(tty, cmd, arg);
|
||||
mutex_unlock(&tport->mutex);
|
||||
|
||||
if (cmd == TIOCSRS485)
|
||||
up_write(&tty->termios_rwsem);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
|
@ -519,25 +538,40 @@ static void serial_set_termios(struct tty_struct *tty,
|
|||
const struct ktermios *old)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
|
||||
dev_dbg(&port->dev, "%s\n", __func__);
|
||||
|
||||
if (port->serial->type->set_termios)
|
||||
port->serial->type->set_termios(tty, port, old);
|
||||
else
|
||||
if (!port->serial->type->set_termios) {
|
||||
tty_termios_copy_hw(&tty->termios, old);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&tport->mutex);
|
||||
if (!tty_io_error(tty))
|
||||
port->serial->type->set_termios(tty, port, old);
|
||||
mutex_unlock(&tport->mutex);
|
||||
}
|
||||
|
||||
static int serial_break(struct tty_struct *tty, int break_state)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&port->dev, "%s\n", __func__);
|
||||
|
||||
if (port->serial->type->break_ctl)
|
||||
return port->serial->type->break_ctl(tty, break_state);
|
||||
if (!port->serial->type->break_ctl)
|
||||
return -ENOTTY;
|
||||
|
||||
return -ENOTTY;
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty))
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = port->serial->type->break_ctl(tty, break_state);
|
||||
mutex_unlock(&tport->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serial_proc_show(struct seq_file *m, void *v)
|
||||
|
|
@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v)
|
|||
static int serial_tiocmget(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&port->dev, "%s\n", __func__);
|
||||
|
||||
if (port->serial->type->tiocmget)
|
||||
return port->serial->type->tiocmget(tty);
|
||||
return -ENOTTY;
|
||||
if (!port->serial->type->tiocmget)
|
||||
return -ENOTTY;
|
||||
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty))
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = port->serial->type->tiocmget(tty);
|
||||
mutex_unlock(&tport->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serial_tiocmset(struct tty_struct *tty,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct tty_port *tport = &port->port;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&port->dev, "%s\n", __func__);
|
||||
|
||||
if (port->serial->type->tiocmset)
|
||||
return port->serial->type->tiocmset(tty, set, clear);
|
||||
return -ENOTTY;
|
||||
if (!port->serial->type->tiocmset)
|
||||
return -ENOTTY;
|
||||
|
||||
mutex_lock(&tport->mutex);
|
||||
if (tty_io_error(tty))
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = port->serial->type->tiocmset(tty, set, clear);
|
||||
mutex_unlock(&tport->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serial_get_icount(struct tty_struct *tty,
|
||||
|
|
@ -1191,11 +1245,16 @@ static void usb_serial_disconnect(struct usb_interface *interface)
|
|||
usb_serial_port_poison_urbs(port);
|
||||
wake_up_interruptible(&port->port.delta_msr_wait);
|
||||
cancel_work_sync(&port->work);
|
||||
}
|
||||
|
||||
if (serial->type->disconnect)
|
||||
serial->type->disconnect(serial);
|
||||
|
||||
for (i = 0; i < serial->num_ports; ++i) {
|
||||
port = serial->port[i];
|
||||
if (device_is_registered(&port->dev))
|
||||
device_del(&port->dev);
|
||||
}
|
||||
if (serial->type->disconnect)
|
||||
serial->type->disconnect(serial);
|
||||
|
||||
release_sibling(serial, interface);
|
||||
|
||||
|
|
@ -1464,7 +1523,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
|
|||
{
|
||||
int rc;
|
||||
struct usb_driver *udriver;
|
||||
struct usb_serial_driver * const *sd;
|
||||
struct usb_serial_driver * const *sd, * const *s;
|
||||
|
||||
/*
|
||||
* udriver must be registered before any of the serial drivers,
|
||||
|
|
@ -1517,9 +1576,11 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
|
|||
return 0;
|
||||
|
||||
err_deregister_drivers:
|
||||
for (s = serial_drivers; s < sd; ++s)
|
||||
usb_serial_bus_remove_new_id(*s);
|
||||
usb_deregister(udriver);
|
||||
while (sd-- > serial_drivers)
|
||||
usb_serial_deregister(*sd);
|
||||
usb_deregister(udriver);
|
||||
err_free_driver:
|
||||
kfree(udriver);
|
||||
return rc;
|
||||
|
|
@ -1537,10 +1598,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_drivers);
|
|||
void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[])
|
||||
{
|
||||
struct usb_driver *udriver = (*serial_drivers)->usb_driver;
|
||||
struct usb_serial_driver * const *sd;
|
||||
|
||||
/*
|
||||
* udriver must be deregistered before the serial drivers so that
|
||||
* I/O is stopped before unbinding the ports.
|
||||
*
|
||||
* Remove the new_id attributes to prevent ids from being added and
|
||||
* triggering a probe of udriver after it has been deregistered.
|
||||
*/
|
||||
for (sd = serial_drivers; *sd; ++sd)
|
||||
usb_serial_bus_remove_new_id(*sd);
|
||||
|
||||
for (; *serial_drivers; ++serial_drivers)
|
||||
usb_serial_deregister(*serial_drivers);
|
||||
usb_deregister(udriver);
|
||||
|
||||
for (sd = serial_drivers; *sd; ++sd)
|
||||
usb_serial_deregister(*sd);
|
||||
|
||||
kfree(udriver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
|
||||
|
|
|
|||
|
|
@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tty_struct *tty,
|
|||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct xr_data *data = usb_get_serial_port_data(port);
|
||||
|
||||
down_read(&tty->termios_rwsem);
|
||||
if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
|
||||
up_read(&tty->termios_rwsem);
|
||||
/* core holds port mutex */
|
||||
if (copy_to_user(argp, &data->rs485, sizeof(data->rs485)))
|
||||
return -EFAULT;
|
||||
}
|
||||
up_read(&tty->termios_rwsem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tty_struct *tty,
|
|||
return -EFAULT;
|
||||
xr_sanitize_serial_rs485(&rs485);
|
||||
|
||||
down_write(&tty->termios_rwsem);
|
||||
/* core holds termios rwsem and port mutex */
|
||||
data->rs485 = rs485;
|
||||
xr_set_flow_mode(tty, port, NULL);
|
||||
up_write(&tty->termios_rwsem);
|
||||
|
||||
if (copy_to_user(argp, &rs485, sizeof(rs485)))
|
||||
return -EFAULT;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usb_device *udev,
|
|||
sizeof(struct swoc_info), /* __u16 size */
|
||||
USB_CTRL_SET_TIMEOUT); /* int timeout */
|
||||
|
||||
/*
|
||||
* A short IN transfer leaves the tail of swocInfo uninitialized;
|
||||
* only a full transfer is valid.
|
||||
*/
|
||||
if (result != sizeof(struct swoc_info))
|
||||
return -EIO;
|
||||
|
||||
swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU);
|
||||
swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer);
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2c_client *client)
|
|||
anx7411_partner_unregister_altmode(plat);
|
||||
anx7411_unregister_partner(plat);
|
||||
|
||||
if (plat->workqueue)
|
||||
if (plat->workqueue) {
|
||||
disable_irq(plat->intp_irq);
|
||||
cancel_work_sync(&plat->work);
|
||||
destroy_workqueue(plat->workqueue);
|
||||
}
|
||||
|
||||
i2c_unregister_device(plat->spi_client);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,23 @@ static int usb4_port_compare(struct device *dev, void *fwnode)
|
|||
return usb4_usb3_port_match(dev, fwnode);
|
||||
}
|
||||
|
||||
static bool typec_has_usb4_host_interface(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
if (!IS_REACHABLE(CONFIG_USB4))
|
||||
return false;
|
||||
|
||||
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
|
||||
fwnode_find_reference(fwnode, "usb4-host-interface", 0);
|
||||
|
||||
/*
|
||||
* The USB4 port can only appear if the host interface is enabled in
|
||||
* the firmware and has been enumerated as a device. The latter is
|
||||
* the same check as in usb_acpi_add_usb4_devlink().
|
||||
*/
|
||||
return !IS_ERR(nhi_fwnode) && fwnode_device_is_available(nhi_fwnode) &&
|
||||
nhi_fwnode->dev;
|
||||
}
|
||||
|
||||
static int typec_port_compare(struct device *dev, void *fwnode)
|
||||
{
|
||||
return device_match_fwnode(dev, fwnode);
|
||||
|
|
@ -64,11 +81,11 @@ static int typec_port_match(struct device *dev, void *data)
|
|||
adev_fwnode);
|
||||
|
||||
/*
|
||||
* If dev is USB 3.x port, it may have reference to the
|
||||
* If dev is USB 3.x port, it may have reference to an available
|
||||
* USB4 host interface in which case we can also link the
|
||||
* Type-C port with the USB4 port.
|
||||
*/
|
||||
if (fwnode_property_present(adev_fwnode, "usb4-host-interface"))
|
||||
if (typec_has_usb4_host_interface(adev_fwnode))
|
||||
component_match_add(&arg->port->dev, &arg->match,
|
||||
usb4_port_compare, adev_fwnode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1756,7 +1756,8 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del
|
|||
static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port)
|
||||
{
|
||||
hrtimer_cancel(&port->vdm_discovery_timer);
|
||||
kthread_cancel_work_sync(&port->vdm_discovery_work);
|
||||
if (port->wq)
|
||||
kthread_cancel_work_sync(&port->vdm_discovery_work);
|
||||
}
|
||||
|
||||
static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
|
||||
|
|
@ -7113,7 +7114,7 @@ static void tcpm_pd_event_handler(struct kthread_work *work)
|
|||
port->upcoming_state = FR_SWAP_SEND;
|
||||
ret = tcpm_ams_start(port, FAST_ROLE_SWAP);
|
||||
if (ret == -EAGAIN)
|
||||
port->upcoming_state = INVALID_STATE;
|
||||
tcpm_set_state(port, ERROR_RECOVERY, 0);
|
||||
} else {
|
||||
tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
|
||||
}
|
||||
|
|
@ -8977,6 +8978,7 @@ void tcpm_unregister_port(struct tcpm_port *port)
|
|||
|
||||
port->registered = false;
|
||||
kthread_destroy_worker(port->wq);
|
||||
port->wq = NULL;
|
||||
|
||||
hrtimer_cancel(&port->vdm_discovery_timer);
|
||||
hrtimer_cancel(&port->enable_frs_timer);
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ static void tps6598x_set_data_role(struct tps6598x *tps,
|
|||
|
||||
static int tps6598x_connect(struct tps6598x *tps, u32 status)
|
||||
{
|
||||
struct typec_partner_desc desc;
|
||||
struct typec_partner_desc desc = { };
|
||||
enum typec_pwr_opmode mode;
|
||||
int ret;
|
||||
|
||||
|
|
@ -354,7 +354,6 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
|
|||
|
||||
desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
|
||||
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
|
||||
desc.identity = NULL;
|
||||
|
||||
if (desc.usb_pd) {
|
||||
ret = tps6598x_read_partner_identity(tps);
|
||||
|
|
@ -850,11 +849,10 @@ static void cd321x_update_work(struct work_struct *work)
|
|||
|
||||
/* Set up partner if we were previously disconnected (or changed). */
|
||||
if (!tps->partner) {
|
||||
struct typec_partner_desc desc;
|
||||
struct typec_partner_desc desc = { };
|
||||
|
||||
desc.usb_pd = is_pd;
|
||||
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
|
||||
desc.identity = NULL;
|
||||
|
||||
if (desc.usb_pd)
|
||||
desc.identity = &st.partner_identity;
|
||||
|
|
@ -1792,6 +1790,7 @@ static int tps6598x_probe(struct i2c_client *client)
|
|||
const struct tipd_data *data;
|
||||
struct tps6598x *tps;
|
||||
struct fwnode_handle *fwnode;
|
||||
bool patch_loaded = false;
|
||||
u32 status;
|
||||
u32 vid = 0;
|
||||
int ret;
|
||||
|
|
@ -1847,6 +1846,7 @@ static int tps6598x_probe(struct i2c_client *client)
|
|||
return ret;
|
||||
|
||||
if (ret == TPS_MODE_PTCH) {
|
||||
patch_loaded = true;
|
||||
ret = tps->data->init(tps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1937,7 +1937,8 @@ static int tps6598x_probe(struct i2c_client *client)
|
|||
tps6598x_write64(tps, TPS_REG_INT_MASK1, 0);
|
||||
err_reset_controller:
|
||||
/* Reset PD controller to remove any applied patch */
|
||||
tps->data->reset(tps);
|
||||
if (patch_loaded)
|
||||
tps->data->reset(tps);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,11 +71,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
|
|||
if (ret < 0) {
|
||||
if (ucsi->version > 0x0100)
|
||||
goto err_unlock;
|
||||
cur = 0xff;
|
||||
}
|
||||
|
||||
if (cur < UCSI_MAX_ALTMODES) {
|
||||
ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
|
||||
if (cur != 0xff) {
|
||||
if (cur < UCSI_MAX_ALTMODES)
|
||||
ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
|
|||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
int i = 1;
|
||||
int ret;
|
||||
u32 port;
|
||||
|
||||
device_for_each_child_node(con->ucsi->dev, fwnode)
|
||||
if (i++ == con->num)
|
||||
return fwnode;
|
||||
device_for_each_child_node(con->ucsi->dev, fwnode) {
|
||||
ret = fwnode_property_read_u32(fwnode, "reg", &port);
|
||||
if (ret < 0) {
|
||||
if (i == con->num)
|
||||
return fwnode;
|
||||
} else {
|
||||
if (port == con->num - 1)
|
||||
return fwnode;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi_acpi_ops = {
|
|||
.async_control = ucsi_acpi_async_control
|
||||
};
|
||||
|
||||
static int ucsi_acer_read_version(struct ucsi *ucsi, u16 *version)
|
||||
{
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
ret = ucsi_acpi_read_version(ucsi, version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!*version) {
|
||||
dev_warn(ua->dev, "UCSI version is zero, assuming 1.2\n");
|
||||
*version = UCSI_VERSION_1_2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ucsi_operations ucsi_acer_ops = {
|
||||
.read_version = ucsi_acer_read_version,
|
||||
.read_cci = ucsi_acpi_read_cci,
|
||||
.poll_cci = ucsi_acpi_poll_cci,
|
||||
.read_message_in = ucsi_acpi_read_message_in,
|
||||
.write_message_out = ucsi_acpi_write_message_out,
|
||||
.sync_control = ucsi_sync_control_common,
|
||||
.async_control = ucsi_acpi_async_control
|
||||
};
|
||||
|
||||
static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
|
||||
void *val, size_t len, void *msg_out,
|
||||
size_t msg_out_size)
|
||||
|
|
@ -162,6 +189,13 @@ static const struct ucsi_operations ucsi_gram_ops = {
|
|||
};
|
||||
|
||||
static const struct dmi_system_id ucsi_acpi_quirks[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
|
||||
},
|
||||
.driver_data = (void *)&ucsi_acer_ops,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
|
||||
|
|
|
|||
|
|
@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void *data)
|
|||
{
|
||||
struct pmic_glink_ucsi *ucsi = data;
|
||||
|
||||
/* Drain the work items before ucsi_destroy() frees the ucsi instance */
|
||||
cancel_work_sync(&ucsi->notify_work);
|
||||
cancel_work_sync(&ucsi->register_work);
|
||||
|
||||
/* Protect to make sure we're not in a middle of a transaction from a glink callback */
|
||||
mutex_lock(&ucsi->lock);
|
||||
ucsi_destroy(ucsi->ucsi);
|
||||
|
|
@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struct auxiliary_device *adev)
|
|||
{
|
||||
struct pmic_glink_ucsi *ucsi = dev_get_drvdata(&adev->dev);
|
||||
|
||||
/* Unregister first to stop having read & writes */
|
||||
ucsi_unregister(ucsi->ucsi);
|
||||
/* Callbacks can queue work until devres releases the client */
|
||||
disable_work_sync(&ucsi->notify_work);
|
||||
disable_work_sync(&ucsi->register_work);
|
||||
|
||||
/* register_work may have unregistered the instance already */
|
||||
if (ucsi->ucsi_registered) {
|
||||
ucsi->ucsi_registered = false;
|
||||
ucsi_unregister(ucsi->ucsi);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ struct device;
|
|||
#include <linux/device.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device-id/tb.h>
|
||||
#include <linux/pci.h>
|
||||
|
|
@ -565,6 +566,7 @@ struct tb_nhi {
|
|||
* @interval_nsec: Interval counter if interrupt throttling is to be
|
||||
* used with this ring (in ns)
|
||||
* @wait: Used to signal that the ring may be empty now
|
||||
* @lock_key: Lock validator class key per-ring
|
||||
*/
|
||||
struct tb_ring {
|
||||
spinlock_t lock;
|
||||
|
|
@ -590,6 +592,7 @@ struct tb_ring {
|
|||
void *poll_data;
|
||||
unsigned int interval_nsec;
|
||||
wait_queue_head_t wait;
|
||||
struct lock_class_key lock_key;
|
||||
};
|
||||
|
||||
/* Leave ring interrupt enabled on suspend */
|
||||
|
|
|
|||
|
|
@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
|
|||
|
||||
int usb_serial_bus_register(struct usb_serial_driver *device);
|
||||
void usb_serial_bus_deregister(struct usb_serial_driver *device);
|
||||
void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver);
|
||||
|
||||
extern const struct bus_type usb_serial_bus_type;
|
||||
extern struct tty_driver *usb_serial_tty_driver;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user