mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-09-21 20:22:13 +08:00
USB driver fixes for 6.8-rc3
Here are a bunch of small USB driver fixes for 6.8-rc3. Included in here are: - new usb-serial driver ids - new dwc3 driver id added - typec driver change revert - ncm gadget driver endian bugfix - xhci bugfixes for a number of reported issues - usb hub bugfix for alternate settings - ulpi driver debugfs memory leak fix - chipidea driver bugfix - usb gadget driver fixes All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZb60Zg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymczwCcCwsgyz86WT5ncgcMTcCFJ0RHEFUAoMLTb7PO Ilvy8z+Wn2I2QEtnDLqT =H8kH -----END PGP SIGNATURE----- Merge tag 'usb-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB driver fixes from Greg KH: "Here are a bunch of small USB driver fixes for 6.8-rc3. Included in here are: - new usb-serial driver ids - new dwc3 driver id added - typec driver change revert - ncm gadget driver endian bugfix - xhci bugfixes for a number of reported issues - usb hub bugfix for alternate settings - ulpi driver debugfs memory leak fix - chipidea driver bugfix - usb gadget driver fixes All of these have been in linux-next for a while with no reported issues" * tag 'usb-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (24 commits) USB: serial: option: add Fibocom FM101-GL variant USB: serial: qcserial: add new usb-id for Dell Wireless DW5826e USB: serial: cp210x: add ID for IMST iM871A-USB usb: typec: tcpm: fix the PD disabled case usb: ucsi_acpi: Quirk to ack a connector change ack cmd usb: ucsi_acpi: Fix command completion handling usb: ucsi: Add missing ppm_lock usb: ulpi: Fix debugfs directory leak Revert "usb: typec: tcpm: fix cc role at port reset" usb: gadget: pch_udc: fix an Excess kernel-doc warning usb: f_mass_storage: forbid async queue when shutdown happen USB: hub: check for alternate port before enabling A_ALT_HNP_SUPPORT usb: chipidea: core: handle power lost in workqueue usb: dwc3: gadget: Fix NULL pointer dereference in dwc3_gadget_suspend usb: dwc3: pci: add support for the Intel Arrow Lake-H usb: core: Prevent null pointer dereference in update_port_device_state xhci: handle isoc Babble and Buffer Overrun events properly xhci: process isoc TD properly when there was a transaction error mid TD. xhci: fix off by one check when adding a secondary interrupter. xhci: fix possible null pointer dereference at secondary interrupter removal ...
This commit is contained in:
commit
809be620dc
@ -448,17 +448,17 @@ Function-specific configfs interface
|
|||||||
The function name to use when creating the function directory is "ncm".
|
The function name to use when creating the function directory is "ncm".
|
||||||
The NCM function provides these attributes in its function directory:
|
The NCM function provides these attributes in its function directory:
|
||||||
|
|
||||||
=============== ==================================================
|
======================= ==================================================
|
||||||
ifname network device interface name associated with this
|
ifname network device interface name associated with this
|
||||||
function instance
|
function instance
|
||||||
qmult queue length multiplier for high and super speed
|
qmult queue length multiplier for high and super speed
|
||||||
host_addr MAC address of host's end of this
|
host_addr MAC address of host's end of this
|
||||||
Ethernet over USB link
|
Ethernet over USB link
|
||||||
dev_addr MAC address of device's end of this
|
dev_addr MAC address of device's end of this
|
||||||
Ethernet over USB link
|
Ethernet over USB link
|
||||||
max_segment_size Segment size required for P2P connections. This
|
max_segment_size Segment size required for P2P connections. This
|
||||||
will set MTU to (max_segment_size - 14 bytes)
|
will set MTU to 14 bytes
|
||||||
=============== ==================================================
|
======================= ==================================================
|
||||||
|
|
||||||
and after creating the functions/ncm.<instance name> they contain default
|
and after creating the functions/ncm.<instance name> they contain default
|
||||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||||
|
@ -176,6 +176,7 @@ struct hw_bank {
|
|||||||
* @enabled_otg_timer_bits: bits of enabled otg timers
|
* @enabled_otg_timer_bits: bits of enabled otg timers
|
||||||
* @next_otg_timer: next nearest enabled timer to be expired
|
* @next_otg_timer: next nearest enabled timer to be expired
|
||||||
* @work: work for role changing
|
* @work: work for role changing
|
||||||
|
* @power_lost_work: work for power lost handling
|
||||||
* @wq: workqueue thread
|
* @wq: workqueue thread
|
||||||
* @qh_pool: allocation pool for queue heads
|
* @qh_pool: allocation pool for queue heads
|
||||||
* @td_pool: allocation pool for transfer descriptors
|
* @td_pool: allocation pool for transfer descriptors
|
||||||
@ -226,6 +227,7 @@ struct ci_hdrc {
|
|||||||
enum otg_fsm_timer next_otg_timer;
|
enum otg_fsm_timer next_otg_timer;
|
||||||
struct usb_role_switch *role_switch;
|
struct usb_role_switch *role_switch;
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
|
struct work_struct power_lost_work;
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
|
|
||||||
struct dma_pool *qh_pool;
|
struct dma_pool *qh_pool;
|
||||||
|
@ -856,6 +856,27 @@ static int ci_extcon_register(struct ci_hdrc *ci)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ci_power_lost_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, power_lost_work);
|
||||||
|
enum ci_role role;
|
||||||
|
|
||||||
|
disable_irq_nosync(ci->irq);
|
||||||
|
pm_runtime_get_sync(ci->dev);
|
||||||
|
if (!ci_otg_is_fsm_mode(ci)) {
|
||||||
|
role = ci_get_role(ci);
|
||||||
|
|
||||||
|
if (ci->role != role) {
|
||||||
|
ci_handle_id_switch(ci);
|
||||||
|
} else if (role == CI_ROLE_GADGET) {
|
||||||
|
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
|
||||||
|
usb_gadget_vbus_connect(&ci->gadget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pm_runtime_put_sync(ci->dev);
|
||||||
|
enable_irq(ci->irq);
|
||||||
|
}
|
||||||
|
|
||||||
static DEFINE_IDA(ci_ida);
|
static DEFINE_IDA(ci_ida);
|
||||||
|
|
||||||
struct platform_device *ci_hdrc_add_device(struct device *dev,
|
struct platform_device *ci_hdrc_add_device(struct device *dev,
|
||||||
@ -1045,6 +1066,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
spin_lock_init(&ci->lock);
|
spin_lock_init(&ci->lock);
|
||||||
mutex_init(&ci->mutex);
|
mutex_init(&ci->mutex);
|
||||||
|
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
|
||||||
|
|
||||||
ci->dev = dev;
|
ci->dev = dev;
|
||||||
ci->platdata = dev_get_platdata(dev);
|
ci->platdata = dev_get_platdata(dev);
|
||||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||||
@ -1396,25 +1419,6 @@ static int ci_suspend(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ci_handle_power_lost(struct ci_hdrc *ci)
|
|
||||||
{
|
|
||||||
enum ci_role role;
|
|
||||||
|
|
||||||
disable_irq_nosync(ci->irq);
|
|
||||||
if (!ci_otg_is_fsm_mode(ci)) {
|
|
||||||
role = ci_get_role(ci);
|
|
||||||
|
|
||||||
if (ci->role != role) {
|
|
||||||
ci_handle_id_switch(ci);
|
|
||||||
} else if (role == CI_ROLE_GADGET) {
|
|
||||||
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
|
|
||||||
usb_gadget_vbus_connect(&ci->gadget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enable_irq(ci->irq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ci_resume(struct device *dev)
|
static int ci_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||||
@ -1446,7 +1450,7 @@ static int ci_resume(struct device *dev)
|
|||||||
ci_role(ci)->resume(ci, power_lost);
|
ci_role(ci)->resume(ci, power_lost);
|
||||||
|
|
||||||
if (power_lost)
|
if (power_lost)
|
||||||
ci_handle_power_lost(ci);
|
queue_work(system_freezable_wq, &ci->power_lost_work);
|
||||||
|
|
||||||
if (ci->supports_runtime_pm) {
|
if (ci->supports_runtime_pm) {
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
|
@ -301,7 +301,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
root = debugfs_create_dir(dev_name(dev), ulpi_root);
|
root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
|
||||||
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
|
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
|
||||||
|
|
||||||
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
||||||
|
@ -2053,9 +2053,19 @@ static void update_port_device_state(struct usb_device *udev)
|
|||||||
|
|
||||||
if (udev->parent) {
|
if (udev->parent) {
|
||||||
hub = usb_hub_to_struct_hub(udev->parent);
|
hub = usb_hub_to_struct_hub(udev->parent);
|
||||||
port_dev = hub->ports[udev->portnum - 1];
|
|
||||||
WRITE_ONCE(port_dev->state, udev->state);
|
/*
|
||||||
sysfs_notify_dirent(port_dev->state_kn);
|
* The Link Layer Validation System Driver (lvstest)
|
||||||
|
* has a test step to unbind the hub before running the
|
||||||
|
* rest of the procedure. This triggers hub_disconnect
|
||||||
|
* which will set the hub's maxchild to 0, further
|
||||||
|
* resulting in usb_hub_to_struct_hub returning NULL.
|
||||||
|
*/
|
||||||
|
if (hub) {
|
||||||
|
port_dev = hub->ports[udev->portnum - 1];
|
||||||
|
WRITE_ONCE(port_dev->state, udev->state);
|
||||||
|
sysfs_notify_dirent(port_dev->state_kn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2388,17 +2398,25 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
|
|||||||
}
|
}
|
||||||
} else if (desc->bLength == sizeof
|
} else if (desc->bLength == sizeof
|
||||||
(struct usb_otg_descriptor)) {
|
(struct usb_otg_descriptor)) {
|
||||||
/* Set a_alt_hnp_support for legacy otg device */
|
/*
|
||||||
err = usb_control_msg(udev,
|
* We are operating on a legacy OTP device
|
||||||
usb_sndctrlpipe(udev, 0),
|
* These should be told that they are operating
|
||||||
USB_REQ_SET_FEATURE, 0,
|
* on the wrong port if we have another port that does
|
||||||
USB_DEVICE_A_ALT_HNP_SUPPORT,
|
* support HNP
|
||||||
0, NULL, 0,
|
*/
|
||||||
USB_CTRL_SET_TIMEOUT);
|
if (bus->otg_port != 0) {
|
||||||
if (err < 0)
|
/* Set a_alt_hnp_support for legacy otg device */
|
||||||
dev_err(&udev->dev,
|
err = usb_control_msg(udev,
|
||||||
"set a_alt_hnp_support failed: %d\n",
|
usb_sndctrlpipe(udev, 0),
|
||||||
err);
|
USB_REQ_SET_FEATURE, 0,
|
||||||
|
USB_DEVICE_A_ALT_HNP_SUPPORT,
|
||||||
|
0, NULL, 0,
|
||||||
|
USB_CTRL_SET_TIMEOUT);
|
||||||
|
if (err < 0)
|
||||||
|
dev_err(&udev->dev,
|
||||||
|
"set a_alt_hnp_support failed: %d\n",
|
||||||
|
err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -51,6 +51,8 @@
|
|||||||
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
|
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
|
||||||
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
|
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
|
||||||
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
|
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ARLH 0x7ec1
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
|
||||||
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
|
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
|
||||||
#define PCI_DEVICE_ID_AMD_MR 0x163a
|
#define PCI_DEVICE_ID_AMD_MR 0x163a
|
||||||
|
|
||||||
@ -421,6 +423,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
|||||||
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
|
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
|
{ PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
|
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
|
||||||
|
{ PCI_DEVICE_DATA(INTEL, ARLH, &dwc3_pci_intel_swnode) },
|
||||||
|
{ PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
|
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
|
||||||
|
|
||||||
{ PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) },
|
{ PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) },
|
||||||
|
@ -4709,15 +4709,13 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dwc->gadget_driver)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ret = dwc3_gadget_soft_disconnect(dwc);
|
ret = dwc3_gadget_soft_disconnect(dwc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
spin_lock_irqsave(&dwc->lock, flags);
|
spin_lock_irqsave(&dwc->lock, flags);
|
||||||
dwc3_disconnect_gadget(dwc);
|
if (dwc->gadget_driver)
|
||||||
|
dwc3_disconnect_gadget(dwc);
|
||||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -61,7 +61,7 @@ out:
|
|||||||
|
|
||||||
int dwc3_host_init(struct dwc3 *dwc)
|
int dwc3_host_init(struct dwc3 *dwc)
|
||||||
{
|
{
|
||||||
struct property_entry props[4];
|
struct property_entry props[5];
|
||||||
struct platform_device *xhci;
|
struct platform_device *xhci;
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
int prop_idx = 0;
|
int prop_idx = 0;
|
||||||
@ -89,6 +89,8 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||||||
|
|
||||||
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
|
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
|
||||||
|
|
||||||
|
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
|
||||||
|
|
||||||
if (dwc->usb3_lpm_capable)
|
if (dwc->usb3_lpm_capable)
|
||||||
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
|
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
|
||||||
|
|
||||||
|
@ -545,21 +545,37 @@ static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
|
|||||||
|
|
||||||
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (!fsg_is_set(common))
|
if (!fsg_is_set(common))
|
||||||
return false;
|
return false;
|
||||||
bh->state = BUF_STATE_SENDING;
|
bh->state = BUF_STATE_SENDING;
|
||||||
if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq))
|
rc = start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq);
|
||||||
|
if (rc) {
|
||||||
bh->state = BUF_STATE_EMPTY;
|
bh->state = BUF_STATE_EMPTY;
|
||||||
|
if (rc == -ESHUTDOWN) {
|
||||||
|
common->running = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (!fsg_is_set(common))
|
if (!fsg_is_set(common))
|
||||||
return false;
|
return false;
|
||||||
bh->state = BUF_STATE_RECEIVING;
|
bh->state = BUF_STATE_RECEIVING;
|
||||||
if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq))
|
rc = start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq);
|
||||||
|
if (rc) {
|
||||||
bh->state = BUF_STATE_FULL;
|
bh->state = BUF_STATE_FULL;
|
||||||
|
if (rc == -ESHUTDOWN) {
|
||||||
|
common->running = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +105,8 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Although max mtu as dictated by u_ether is 15412 bytes, setting
|
* Although max mtu as dictated by u_ether is 15412 bytes, setting
|
||||||
* max_segment_sizeto 15426 would not be efficient. If user chooses segment
|
* max_segment_size to 15426 would not be efficient. If user chooses segment
|
||||||
* size to be (>= 8192), then we can't aggregate more than one buffer in each
|
* size to be (>= 8192), then we can't aggregate more than one buffer in each
|
||||||
* NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
|
* NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
|
||||||
* maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
|
* maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
|
||||||
* at least 2 packets to be aggregated reducing wastage of NTB buffer space
|
* at least 2 packets to be aggregated reducing wastage of NTB buffer space
|
||||||
@ -1489,7 +1489,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
ncm_data_intf.bInterfaceNumber = status;
|
ncm_data_intf.bInterfaceNumber = status;
|
||||||
ncm_union_desc.bSlaveInterface0 = status;
|
ncm_union_desc.bSlaveInterface0 = status;
|
||||||
|
|
||||||
ecm_desc.wMaxSegmentSize = ncm_opts->max_segment_size;
|
ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
|
||||||
|
|
||||||
status = -ENODEV;
|
status = -ENODEV;
|
||||||
|
|
||||||
@ -1685,7 +1685,7 @@ static struct usb_function_instance *ncm_alloc_inst(void)
|
|||||||
kfree(opts);
|
kfree(opts);
|
||||||
return ERR_CAST(net);
|
return ERR_CAST(net);
|
||||||
}
|
}
|
||||||
opts->max_segment_size = cpu_to_le16(ETH_FRAME_LEN);
|
opts->max_segment_size = ETH_FRAME_LEN;
|
||||||
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
||||||
|
|
||||||
descs[0] = &opts->ncm_os_desc;
|
descs[0] = &opts->ncm_os_desc;
|
||||||
|
@ -274,7 +274,6 @@ struct pch_udc_cfg_data {
|
|||||||
* @td_data: for data request
|
* @td_data: for data request
|
||||||
* @dev: reference to device struct
|
* @dev: reference to device struct
|
||||||
* @offset_addr: offset address of ep register
|
* @offset_addr: offset address of ep register
|
||||||
* @desc: for this ep
|
|
||||||
* @queue: queue for requests
|
* @queue: queue for requests
|
||||||
* @num: endpoint number
|
* @num: endpoint number
|
||||||
* @in: endpoint is IN
|
* @in: endpoint is IN
|
||||||
|
@ -1861,15 +1861,15 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
|
|||||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||||
unsigned int intr_num;
|
unsigned int intr_num;
|
||||||
|
|
||||||
/* interrupter 0 is primary interrupter, don't touch it */
|
|
||||||
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
|
|
||||||
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
|
|
||||||
|
|
||||||
/* fixme, should we check xhci->interrupter[intr_num] == ir */
|
|
||||||
/* fixme locking */
|
|
||||||
|
|
||||||
spin_lock_irq(&xhci->lock);
|
spin_lock_irq(&xhci->lock);
|
||||||
|
|
||||||
|
/* interrupter 0 is primary interrupter, don't touch it */
|
||||||
|
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) {
|
||||||
|
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
|
||||||
|
spin_unlock_irq(&xhci->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
intr_num = ir->intr_num;
|
intr_num = ir->intr_num;
|
||||||
|
|
||||||
xhci_remove_interrupter(xhci, ir);
|
xhci_remove_interrupter(xhci, ir);
|
||||||
@ -2322,7 +2322,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
|||||||
u64 erst_base;
|
u64 erst_base;
|
||||||
u32 erst_size;
|
u32 erst_size;
|
||||||
|
|
||||||
if (intr_num > xhci->max_interrupters) {
|
if (intr_num >= xhci->max_interrupters) {
|
||||||
xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
|
xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
|
||||||
intr_num, xhci->max_interrupters);
|
intr_num, xhci->max_interrupters);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -253,6 +253,9 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
|
|||||||
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
|
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
|
||||||
xhci->quirks |= XHCI_BROKEN_PORT_PED;
|
xhci->quirks |= XHCI_BROKEN_PORT_PED;
|
||||||
|
|
||||||
|
if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
|
||||||
|
xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
|
||||||
|
|
||||||
device_property_read_u32(tmpdev, "imod-interval-ns",
|
device_property_read_u32(tmpdev, "imod-interval-ns",
|
||||||
&xhci->imod_interval);
|
&xhci->imod_interval);
|
||||||
}
|
}
|
||||||
|
@ -2376,6 +2376,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
|||||||
/* handle completion code */
|
/* handle completion code */
|
||||||
switch (trb_comp_code) {
|
switch (trb_comp_code) {
|
||||||
case COMP_SUCCESS:
|
case COMP_SUCCESS:
|
||||||
|
/* Don't overwrite status if TD had an error, see xHCI 4.9.1 */
|
||||||
|
if (td->error_mid_td)
|
||||||
|
break;
|
||||||
if (remaining) {
|
if (remaining) {
|
||||||
frame->status = short_framestatus;
|
frame->status = short_framestatus;
|
||||||
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
|
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
|
||||||
@ -2391,9 +2394,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
|||||||
case COMP_BANDWIDTH_OVERRUN_ERROR:
|
case COMP_BANDWIDTH_OVERRUN_ERROR:
|
||||||
frame->status = -ECOMM;
|
frame->status = -ECOMM;
|
||||||
break;
|
break;
|
||||||
case COMP_ISOCH_BUFFER_OVERRUN:
|
|
||||||
case COMP_BABBLE_DETECTED_ERROR:
|
case COMP_BABBLE_DETECTED_ERROR:
|
||||||
|
sum_trbs_for_length = true;
|
||||||
|
fallthrough;
|
||||||
|
case COMP_ISOCH_BUFFER_OVERRUN:
|
||||||
frame->status = -EOVERFLOW;
|
frame->status = -EOVERFLOW;
|
||||||
|
if (ep_trb != td->last_trb)
|
||||||
|
td->error_mid_td = true;
|
||||||
break;
|
break;
|
||||||
case COMP_INCOMPATIBLE_DEVICE_ERROR:
|
case COMP_INCOMPATIBLE_DEVICE_ERROR:
|
||||||
case COMP_STALL_ERROR:
|
case COMP_STALL_ERROR:
|
||||||
@ -2401,8 +2408,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
|||||||
break;
|
break;
|
||||||
case COMP_USB_TRANSACTION_ERROR:
|
case COMP_USB_TRANSACTION_ERROR:
|
||||||
frame->status = -EPROTO;
|
frame->status = -EPROTO;
|
||||||
|
sum_trbs_for_length = true;
|
||||||
if (ep_trb != td->last_trb)
|
if (ep_trb != td->last_trb)
|
||||||
return 0;
|
td->error_mid_td = true;
|
||||||
break;
|
break;
|
||||||
case COMP_STOPPED:
|
case COMP_STOPPED:
|
||||||
sum_trbs_for_length = true;
|
sum_trbs_for_length = true;
|
||||||
@ -2422,6 +2430,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (td->urb_length_set)
|
||||||
|
goto finish_td;
|
||||||
|
|
||||||
if (sum_trbs_for_length)
|
if (sum_trbs_for_length)
|
||||||
frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) +
|
frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) +
|
||||||
ep_trb_len - remaining;
|
ep_trb_len - remaining;
|
||||||
@ -2430,6 +2441,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
|||||||
|
|
||||||
td->urb->actual_length += frame->actual_length;
|
td->urb->actual_length += frame->actual_length;
|
||||||
|
|
||||||
|
finish_td:
|
||||||
|
/* Don't give back TD yet if we encountered an error mid TD */
|
||||||
|
if (td->error_mid_td && ep_trb != td->last_trb) {
|
||||||
|
xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n");
|
||||||
|
td->urb_length_set = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
|
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2808,17 +2827,51 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ep_seg) {
|
if (!ep_seg) {
|
||||||
if (!ep->skip ||
|
|
||||||
!usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
|
||||||
/* Some host controllers give a spurious
|
skip_isoc_td(xhci, td, ep, status);
|
||||||
* successful event after a short transfer.
|
goto cleanup;
|
||||||
* Ignore it.
|
}
|
||||||
*/
|
|
||||||
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
|
/*
|
||||||
ep_ring->last_td_was_short) {
|
* Some hosts give a spurious success event after a short
|
||||||
ep_ring->last_td_was_short = false;
|
* transfer. Ignore it.
|
||||||
goto cleanup;
|
*/
|
||||||
|
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
|
||||||
|
ep_ring->last_td_was_short) {
|
||||||
|
ep_ring->last_td_was_short = false;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xhci 4.10.2 states isoc endpoints should continue
|
||||||
|
* processing the next TD if there was an error mid TD.
|
||||||
|
* So host like NEC don't generate an event for the last
|
||||||
|
* isoc TRB even if the IOC flag is set.
|
||||||
|
* xhci 4.9.1 states that if there are errors in mult-TRB
|
||||||
|
* TDs xHC should generate an error for that TRB, and if xHC
|
||||||
|
* proceeds to the next TD it should genete an event for
|
||||||
|
* any TRB with IOC flag on the way. Other host follow this.
|
||||||
|
* So this event might be for the next TD.
|
||||||
|
*/
|
||||||
|
if (td->error_mid_td &&
|
||||||
|
!list_is_last(&td->td_list, &ep_ring->td_list)) {
|
||||||
|
struct xhci_td *td_next = list_next_entry(td, td_list);
|
||||||
|
|
||||||
|
ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
|
||||||
|
td_next->last_trb, ep_trb_dma, false);
|
||||||
|
if (ep_seg) {
|
||||||
|
/* give back previous TD, start handling new */
|
||||||
|
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
|
||||||
|
ep_ring->dequeue = td->last_trb;
|
||||||
|
ep_ring->deq_seg = td->last_trb_seg;
|
||||||
|
inc_deq(xhci, ep_ring);
|
||||||
|
xhci_td_cleanup(xhci, td, ep_ring, td->status);
|
||||||
|
td = td_next;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ep_seg) {
|
||||||
/* HC is busted, give up! */
|
/* HC is busted, give up! */
|
||||||
xhci_err(xhci,
|
xhci_err(xhci,
|
||||||
"ERROR Transfer event TRB DMA ptr not "
|
"ERROR Transfer event TRB DMA ptr not "
|
||||||
@ -2830,9 +2883,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||||||
ep_trb_dma, true);
|
ep_trb_dma, true);
|
||||||
return -ESHUTDOWN;
|
return -ESHUTDOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_isoc_td(xhci, td, ep, status);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
if (trb_comp_code == COMP_SHORT_PACKET)
|
if (trb_comp_code == COMP_SHORT_PACKET)
|
||||||
ep_ring->last_td_was_short = true;
|
ep_ring->last_td_was_short = true;
|
||||||
|
@ -1549,6 +1549,7 @@ struct xhci_td {
|
|||||||
struct xhci_segment *bounce_seg;
|
struct xhci_segment *bounce_seg;
|
||||||
/* actual_length of the URB has already been set */
|
/* actual_length of the URB has already been set */
|
||||||
bool urb_length_set;
|
bool urb_length_set;
|
||||||
|
bool error_mid_td;
|
||||||
unsigned int num_trbs;
|
unsigned int num_trbs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,6 +146,7 @@ static const struct usb_device_id id_table[] = {
|
|||||||
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
|
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
|
||||||
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
|
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
|
||||||
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
|
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
|
||||||
|
{ USB_DEVICE(0x10C4, 0x87ED) }, /* IMST USB-Stick for Smart Meter */
|
||||||
{ USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */
|
{ USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */
|
||||||
{ USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */
|
{ USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */
|
||||||
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
|
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
|
||||||
|
@ -2269,6 +2269,7 @@ static const struct usb_device_id option_ids[] = {
|
|||||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */
|
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */
|
||||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */
|
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */
|
||||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
|
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
|
||||||
|
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a3, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
|
||||||
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */
|
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */
|
||||||
.driver_info = RSVD(4) },
|
.driver_info = RSVD(4) },
|
||||||
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
|
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
|
||||||
|
@ -184,6 +184,8 @@ static const struct usb_device_id id_table[] = {
|
|||||||
{DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
|
{DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
|
||||||
{DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
|
{DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
|
||||||
{DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
|
{DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
|
||||||
|
{DEVICE_SWI(0x413c, 0x8217)}, /* Dell Wireless DW5826e */
|
||||||
|
{DEVICE_SWI(0x413c, 0x8218)}, /* Dell Wireless DW5826e QDL */
|
||||||
|
|
||||||
/* Huawei devices */
|
/* Huawei devices */
|
||||||
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
|
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
|
||||||
|
@ -4876,8 +4876,7 @@ static void run_state_machine(struct tcpm_port *port)
|
|||||||
break;
|
break;
|
||||||
case PORT_RESET:
|
case PORT_RESET:
|
||||||
tcpm_reset_port(port);
|
tcpm_reset_port(port);
|
||||||
tcpm_set_cc(port, tcpm_default_state(port) == SNK_UNATTACHED ?
|
tcpm_set_cc(port, TYPEC_CC_OPEN);
|
||||||
TYPEC_CC_RD : tcpm_rp_cc(port));
|
|
||||||
tcpm_set_state(port, PORT_RESET_WAIT_OFF,
|
tcpm_set_state(port, PORT_RESET_WAIT_OFF,
|
||||||
PD_T_ERROR_RECOVERY);
|
PD_T_ERROR_RECOVERY);
|
||||||
break;
|
break;
|
||||||
@ -6848,7 +6847,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_role_sw_put;
|
goto out_role_sw_put;
|
||||||
|
|
||||||
port->typec_caps.pd = port->pds[0];
|
if (port->pds)
|
||||||
|
port->typec_caps.pd = port->pds[0];
|
||||||
|
|
||||||
port->typec_port = typec_register_port(port->dev, &port->typec_caps);
|
port->typec_port = typec_register_port(port->dev, &port->typec_caps);
|
||||||
if (IS_ERR(port->typec_port)) {
|
if (IS_ERR(port->typec_port)) {
|
||||||
|
@ -938,7 +938,9 @@ static void ucsi_handle_connector_change(struct work_struct *work)
|
|||||||
|
|
||||||
clear_bit(EVENT_PENDING, &con->ucsi->flags);
|
clear_bit(EVENT_PENDING, &con->ucsi->flags);
|
||||||
|
|
||||||
|
mutex_lock(&ucsi->ppm_lock);
|
||||||
ret = ucsi_acknowledge_connector_change(ucsi);
|
ret = ucsi_acknowledge_connector_change(ucsi);
|
||||||
|
mutex_unlock(&ucsi->ppm_lock);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
|
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ struct ucsi_acpi {
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
guid_t guid;
|
guid_t guid;
|
||||||
u64 cmd;
|
u64 cmd;
|
||||||
|
bool dell_quirk_probed;
|
||||||
|
bool dell_quirk_active;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
|
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
|
||||||
@ -73,9 +75,13 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
|||||||
const void *val, size_t val_len)
|
const void *val, size_t val_len)
|
||||||
{
|
{
|
||||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||||
|
bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
set_bit(COMMAND_PENDING, &ua->flags);
|
if (ack)
|
||||||
|
set_bit(ACK_PENDING, &ua->flags);
|
||||||
|
else
|
||||||
|
set_bit(COMMAND_PENDING, &ua->flags);
|
||||||
|
|
||||||
ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
|
ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -85,7 +91,10 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
|||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
out_clear_bit:
|
out_clear_bit:
|
||||||
clear_bit(COMMAND_PENDING, &ua->flags);
|
if (ack)
|
||||||
|
clear_bit(ACK_PENDING, &ua->flags);
|
||||||
|
else
|
||||||
|
clear_bit(COMMAND_PENDING, &ua->flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -119,12 +128,73 @@ static const struct ucsi_operations ucsi_zenbook_ops = {
|
|||||||
.async_write = ucsi_acpi_async_write
|
.async_write = ucsi_acpi_async_write
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dmi_system_id zenbook_dmi_id[] = {
|
/*
|
||||||
|
* Some Dell laptops expect that an ACK command with the
|
||||||
|
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate)
|
||||||
|
* ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set.
|
||||||
|
* If this is not done events are not delivered to OSPM and
|
||||||
|
* subsequent commands will timeout.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||||
|
const void *val, size_t val_len)
|
||||||
|
{
|
||||||
|
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||||
|
u64 cmd = *(u64 *)val, ack = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI &&
|
||||||
|
cmd & UCSI_ACK_CONNECTOR_CHANGE)
|
||||||
|
ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
|
||||||
|
|
||||||
|
ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
if (ack == 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!ua->dell_quirk_probed) {
|
||||||
|
ua->dell_quirk_probed = true;
|
||||||
|
|
||||||
|
cmd = UCSI_GET_CAPABILITY;
|
||||||
|
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd,
|
||||||
|
sizeof(cmd));
|
||||||
|
if (ret == 0)
|
||||||
|
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL,
|
||||||
|
&ack, sizeof(ack));
|
||||||
|
if (ret != -ETIMEDOUT)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ua->dell_quirk_active = true;
|
||||||
|
dev_err(ua->dev, "Firmware bug: Additional ACK required after ACKing a connector change.\n");
|
||||||
|
dev_err(ua->dev, "Firmware bug: Enabling workaround\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ua->dell_quirk_active)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ucsi_operations ucsi_dell_ops = {
|
||||||
|
.read = ucsi_acpi_read,
|
||||||
|
.sync_write = ucsi_dell_sync_write,
|
||||||
|
.async_write = ucsi_acpi_async_write
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct dmi_system_id ucsi_acpi_quirks[] = {
|
||||||
{
|
{
|
||||||
.matches = {
|
.matches = {
|
||||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
|
||||||
},
|
},
|
||||||
|
.driver_data = (void *)&ucsi_zenbook_ops,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)&ucsi_dell_ops,
|
||||||
},
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
@ -142,8 +212,10 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
|
|||||||
if (UCSI_CCI_CONNECTOR(cci))
|
if (UCSI_CCI_CONNECTOR(cci))
|
||||||
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
|
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
|
||||||
|
|
||||||
if (test_bit(COMMAND_PENDING, &ua->flags) &&
|
if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags))
|
||||||
cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
|
complete(&ua->complete);
|
||||||
|
if (cci & UCSI_CCI_COMMAND_COMPLETE &&
|
||||||
|
test_bit(COMMAND_PENDING, &ua->flags))
|
||||||
complete(&ua->complete);
|
complete(&ua->complete);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +223,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||||
const struct ucsi_operations *ops = &ucsi_acpi_ops;
|
const struct ucsi_operations *ops = &ucsi_acpi_ops;
|
||||||
|
const struct dmi_system_id *id;
|
||||||
struct ucsi_acpi *ua;
|
struct ucsi_acpi *ua;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
@ -180,8 +253,9 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
|
|||||||
init_completion(&ua->complete);
|
init_completion(&ua->complete);
|
||||||
ua->dev = &pdev->dev;
|
ua->dev = &pdev->dev;
|
||||||
|
|
||||||
if (dmi_check_system(zenbook_dmi_id))
|
id = dmi_first_match(ucsi_acpi_quirks);
|
||||||
ops = &ucsi_zenbook_ops;
|
if (id)
|
||||||
|
ops = id->driver_data;
|
||||||
|
|
||||||
ua->ucsi = ucsi_create(&pdev->dev, ops);
|
ua->ucsi = ucsi_create(&pdev->dev, ops);
|
||||||
if (IS_ERR(ua->ucsi))
|
if (IS_ERR(ua->ucsi))
|
||||||
|
Loading…
Reference in New Issue
Block a user