5630 lines
187 KiB
Diff
5630 lines
187 KiB
Diff
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
|
||
--- a/drivers/power/supply/power_supply_core.c
|
||
+++ b/drivers/power/supply/power_supply_core.c
|
||
@@ -477,6 +477,159 @@ struct power_supply *power_supply_get_by_name(const char *name)
|
||
}
|
||
EXPORT_SYMBOL_GPL(power_supply_get_by_name);
|
||
|
||
+static bool power_supply_is_system_battery(struct power_supply *psy)
|
||
+{
|
||
+ union power_supply_propval val;
|
||
+
|
||
+ if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY)
|
||
+ return false;
|
||
+
|
||
+ if (!power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_SCOPE,
|
||
+ &val))
|
||
+ if (val.intval == POWER_SUPPLY_SCOPE_DEVICE)
|
||
+ return false;
|
||
+
|
||
+ return true;
|
||
+}
|
||
+
|
||
+static int __power_supply_get_num_system_batteries(struct power_supply *epsy,
|
||
+ void *data)
|
||
+{
|
||
+ int *count = data;
|
||
+
|
||
+ if (power_supply_is_system_battery(epsy))
|
||
+ (*count)++;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int power_supply_get_num_system_batteries(struct device *dev)
|
||
+{
|
||
+ int ret, count = 0;
|
||
+
|
||
+ ret = power_supply_for_each_psy(&count,
|
||
+ __power_supply_get_num_system_batteries);
|
||
+
|
||
+ dev_dbg(dev, "%s: count: %d ret %d\n", __func__, count, ret);
|
||
+
|
||
+ if (ret)
|
||
+ return ret;
|
||
+
|
||
+ return count;
|
||
+}
|
||
+
|
||
+struct psy_get_supplies_data {
|
||
+ int cnt;
|
||
+ int size;
|
||
+ struct power_supply **psys;
|
||
+};
|
||
+
|
||
+static int
|
||
+__power_supply_populate_system_batteries_array(struct power_supply *epsy,
|
||
+ void *_data)
|
||
+{
|
||
+ struct psy_get_supplies_data *data = _data;
|
||
+
|
||
+ if (power_supply_is_system_battery(epsy)) {
|
||
+ if (data->size <= data->cnt)
|
||
+ return -EOVERFLOW;
|
||
+
|
||
+ get_device(&epsy->dev);
|
||
+ data->psys[data->cnt] = epsy;
|
||
+ atomic_inc(&epsy->use_cnt);
|
||
+ data->cnt++;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int
|
||
+power_supply_populate_system_batteries_array(struct device *dev, int size,
|
||
+ struct power_supply **batteries)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ struct psy_get_supplies_data data = {
|
||
+ .cnt = 0,
|
||
+ .size = size,
|
||
+ .psys = batteries,
|
||
+ };
|
||
+
|
||
+ ret = power_supply_for_each_psy(&data,
|
||
+ __power_supply_populate_system_batteries_array);
|
||
+
|
||
+ dev_dbg(dev, "%s Found %d batteries with array size %d ret %d\n",
|
||
+ __func__, data.cnt, data.size, ret);
|
||
+
|
||
+ if (ret < 0 || !data.cnt) {
|
||
+ power_supply_put_system_batteries(batteries, data.cnt);
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ return data.cnt;
|
||
+}
|
||
+
|
||
+/**
|
||
+ * power_supply_get_system_batteries() - Fetches references to battery type
|
||
+ * power supplies in the system.
|
||
+ * @dev: Pointer to device requesting the power supply refs.
|
||
+ * @psys: Pointer to an array of power supply refs.
|
||
+ *
|
||
+ * Helper function to get handles to battery type power supplies in the system.
|
||
+ * If acquiring a ref to a power supply fails, then the search for battery type
|
||
+ * power supplies will abort and the acquired power supply references will be
|
||
+ * released.
|
||
+ *
|
||
+ * Return: Indicates the number of battery type power supplies returned on
|
||
+ * success or a negative error code on failure.
|
||
+ *
|
||
+ * Call power_supply_put_system_batteries() after use to cleanup resources.
|
||
+ */
|
||
+int __must_check power_supply_get_system_batteries(struct device *dev,
|
||
+ struct power_supply ***psys)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ if (!psys)
|
||
+ return -EINVAL;
|
||
+
|
||
+ ret = power_supply_get_num_system_batteries(dev);
|
||
+ if (ret <= 0) {
|
||
+ *psys = NULL;
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ *psys = kzalloc_objs(**psys, ret);
|
||
+ if (!*psys)
|
||
+ return -ENOMEM;
|
||
+
|
||
+ ret = power_supply_populate_system_batteries_array(dev, ret, *psys);
|
||
+ if (ret <= 0)
|
||
+ *psys = NULL;
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+EXPORT_SYMBOL_GPL(power_supply_get_system_batteries);
|
||
+
|
||
+/**
|
||
+ * power_supply_put_system_batteries() - Cleanup resources allocated by
|
||
+ * power_supply_get_system_batteries()
|
||
+ * @psys: Array of power supply references to release and free.
|
||
+ * @count: Number of elements in the array.
|
||
+ */
|
||
+void power_supply_put_system_batteries(struct power_supply **psys, int count)
|
||
+{
|
||
+ int i;
|
||
+
|
||
+ for (i = 0; i < count; i++) {
|
||
+ if (psys[i])
|
||
+ power_supply_put(psys[i]);
|
||
+ }
|
||
+
|
||
+ kfree(psys);
|
||
+}
|
||
+EXPORT_SYMBOL_GPL(power_supply_put_system_batteries);
|
||
+
|
||
/**
|
||
* power_supply_put() - Drop reference obtained with power_supply_get_by_name
|
||
* @psy: Reference to put
|
||
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
|
||
--- a/drivers/usb/cdns3/cdns3-plat.c
|
||
+++ b/drivers/usb/cdns3/cdns3-plat.c
|
||
@@ -147,12 +147,12 @@ static int cdns3_plat_probe(struct platform_device *pdev)
|
||
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
|
||
if (IS_ERR(cdns->usb2_phy))
|
||
return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy),
|
||
- "Failed to get cdn3,usb2-phy\n");
|
||
+ "Failed to get cdns3,usb2-phy\n");
|
||
|
||
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
|
||
if (IS_ERR(cdns->usb3_phy))
|
||
return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy),
|
||
- "Failed to get cdn3,usb3-phy\n");
|
||
+ "Failed to get cdns3,usb3-phy\n");
|
||
|
||
ret = phy_init(cdns->usb2_phy);
|
||
if (ret)
|
||
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
|
||
--- a/drivers/usb/core/config.c
|
||
+++ b/drivers/usb/core/config.c
|
||
@@ -151,7 +151,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||
usb_endpoint_xfer_int(&ep->desc)) &&
|
||
desc->bmAttributes != 0) {
|
||
dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
|
||
- usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
|
||
+ usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Interrupt",
|
||
desc->bmAttributes,
|
||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||
ep->ss_ep_comp.bmAttributes = 0;
|
||
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
|
||
--- a/drivers/usb/core/devices.c
|
||
+++ b/drivers/usb/core/devices.c
|
||
@@ -37,6 +37,7 @@
|
||
*/
|
||
|
||
#include <linux/fs.h>
|
||
+#include <linux/slab.h>
|
||
#include <linux/mm.h>
|
||
#include <linux/gfp.h>
|
||
#include <linux/usb.h>
|
||
@@ -408,7 +409,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
||
return 0;
|
||
/* allocate 2^1 pages = 8K (on i386);
|
||
* should be more than enough for one device */
|
||
- pages_start = (char *)__get_free_pages(GFP_NOIO, 1);
|
||
+ pages_start = kmalloc(PAGE_SIZE << 1, GFP_NOIO);
|
||
if (!pages_start)
|
||
return -ENOMEM;
|
||
|
||
@@ -479,7 +480,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
||
if (length > *nbytes)
|
||
length = *nbytes;
|
||
if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) {
|
||
- free_pages((unsigned long)pages_start, 1);
|
||
+ kfree(pages_start);
|
||
return -EFAULT;
|
||
}
|
||
*nbytes -= length;
|
||
@@ -490,7 +491,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
||
} else
|
||
*skip_bytes -= length;
|
||
|
||
- free_pages((unsigned long)pages_start, 1);
|
||
+ kfree(pages_start);
|
||
|
||
/* Now look at all of this device's children. */
|
||
usb_hub_for_each_child(usbdev, chix, childdev) {
|
||
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
|
||
--- a/drivers/usb/core/devio.c
|
||
+++ b/drivers/usb/core/devio.c
|
||
@@ -2329,6 +2329,13 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
|
||
if (!connected(ps))
|
||
return -ENODEV;
|
||
|
||
+ if (ps->dev->state != USB_STATE_CONFIGURED)
|
||
+ return -EHOSTUNREACH;
|
||
+
|
||
+ intf = usb_ifnum_to_if(ps->dev, ctl->ifno);
|
||
+ if (!intf)
|
||
+ return -EINVAL;
|
||
+
|
||
/* alloc buffer */
|
||
size = _IOC_SIZE(ctl->ioctl_code);
|
||
if (size > 0) {
|
||
@@ -2345,11 +2352,7 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
|
||
}
|
||
}
|
||
|
||
- if (ps->dev->state != USB_STATE_CONFIGURED)
|
||
- retval = -EHOSTUNREACH;
|
||
- else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno)))
|
||
- retval = -EINVAL;
|
||
- else switch (ctl->ioctl_code) {
|
||
+ switch (ctl->ioctl_code) {
|
||
|
||
/* disconnect kernel driver from interface */
|
||
case USBDEVFS_DISCONNECT:
|
||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
||
--- a/drivers/usb/core/hub.c
|
||
+++ b/drivers/usb/core/hub.c
|
||
@@ -623,11 +623,11 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
|
||
mutex_lock(&hub->status_mutex);
|
||
ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
|
||
if (ret < len) {
|
||
- if (ret != -ENODEV)
|
||
- dev_err(hub->intfdev,
|
||
- "%s failed (err = %d)\n", __func__, ret);
|
||
if (ret >= 0)
|
||
ret = -EIO;
|
||
+ if (ret != -ENODEV)
|
||
+ dev_dbg(hub->intfdev,
|
||
+ "get_port_status failed: err = %d\n", ret);
|
||
} else {
|
||
*status = le16_to_cpu(hub->status->port.wPortStatus);
|
||
*change = le16_to_cpu(hub->status->port.wPortChange);
|
||
@@ -995,11 +995,11 @@ static int hub_hub_status(struct usb_hub *hub,
|
||
mutex_lock(&hub->status_mutex);
|
||
ret = get_hub_status(hub->hdev, &hub->status->hub);
|
||
if (ret < (int)sizeof(hub->status->hub)) {
|
||
- if (ret != -ENODEV)
|
||
- dev_err(hub->intfdev,
|
||
- "%s failed (err = %d)\n", __func__, ret);
|
||
if (ret >= 0)
|
||
ret = -EIO;
|
||
+ if (ret != -ENODEV)
|
||
+ dev_dbg(hub->intfdev,
|
||
+ "get_hub_status failed: err = %d\n", ret);
|
||
} else {
|
||
*status = le16_to_cpu(hub->status->hub.wHubStatus);
|
||
*change = le16_to_cpu(hub->status->hub.wHubChange);
|
||
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
|
||
--- a/drivers/usb/dwc2/core_intr.c
|
||
+++ b/drivers/usb/dwc2/core_intr.c
|
||
@@ -334,9 +334,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
||
|
||
/**
|
||
* dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state
|
||
- *
|
||
* @hsotg: Programming view of DWC_otg controller
|
||
- *
|
||
+ * @remotewakeup: Whether this is a remote wakeup
|
||
*/
|
||
void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup)
|
||
{
|
||
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
|
||
--- a/drivers/usb/dwc3/core.c
|
||
+++ b/drivers/usb/dwc3/core.c
|
||
@@ -2188,22 +2188,89 @@ static void dwc3_vbus_draw_work(struct work_struct *work)
|
||
ret, dwc->current_limit);
|
||
}
|
||
|
||
-static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
||
+static int dwc3_psy_notifier(struct notifier_block *nb,
|
||
+ unsigned long event, void *data)
|
||
{
|
||
- struct power_supply *usb_psy;
|
||
- const char *usb_psy_name;
|
||
+ struct dwc3 *dwc = container_of(nb, struct dwc3, psy_nb);
|
||
+ struct power_supply *psy = data;
|
||
+ unsigned long flags;
|
||
+
|
||
+ if (dwc->usb_psy)
|
||
+ return NOTIFY_DONE;
|
||
+
|
||
+ if (strcmp(psy->desc->name, dwc->usb_psy_name) != 0)
|
||
+ return NOTIFY_DONE;
|
||
+
|
||
+ /* Explicitly get the reference for this psy */
|
||
+ psy = power_supply_get_by_name(dwc->usb_psy_name);
|
||
+ if (!psy)
|
||
+ return NOTIFY_DONE;
|
||
+
|
||
+ spin_lock_irqsave(&dwc->lock, flags);
|
||
+ /*
|
||
+ * The USB power_supply may already be set. This can happen if notifier
|
||
+ * callbacks for the USB power_supply race, or if a previous notifier
|
||
+ * callback has already successfully fetched and associated the instance.
|
||
+ * In such cases, release the newly acquired reference and ignore
|
||
+ * subsequent notifications until the notifier is unregistered.
|
||
+ */
|
||
+ if (dwc->usb_psy) {
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
+ power_supply_put(psy);
|
||
+ return NOTIFY_DONE;
|
||
+ }
|
||
+
|
||
+ dwc->usb_psy = psy;
|
||
+ if (dwc->current_limit != DWC3_CURRENT_UNSPECIFIED)
|
||
+ schedule_work(&dwc->vbus_draw_work);
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
+
|
||
+ return NOTIFY_OK;
|
||
+}
|
||
+
|
||
+static void dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
||
+{
|
||
+ struct power_supply *psy;
|
||
+ unsigned long flags;
|
||
int ret;
|
||
|
||
- ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name);
|
||
+ ret = device_property_read_string(dwc->dev, "usb-psy-name", &dwc->usb_psy_name);
|
||
if (ret < 0)
|
||
- return NULL;
|
||
-
|
||
- usb_psy = power_supply_get_by_name(usb_psy_name);
|
||
- if (!usb_psy)
|
||
- return ERR_PTR(-EPROBE_DEFER);
|
||
+ return;
|
||
|
||
INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
|
||
- return usb_psy;
|
||
+
|
||
+ dwc->current_limit = DWC3_CURRENT_UNSPECIFIED;
|
||
+ dwc->psy_nb.notifier_call = dwc3_psy_notifier;
|
||
+ ret = power_supply_reg_notifier(&dwc->psy_nb);
|
||
+ if (ret) {
|
||
+ dev_err(dwc->dev, "Failed to register power supply notifier: %d\n", ret);
|
||
+ dwc->psy_nb.notifier_call = NULL;
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ psy = power_supply_get_by_name(dwc->usb_psy_name);
|
||
+ if (!psy)
|
||
+ return;
|
||
+
|
||
+ /* Unregister the notifier now that we have the power supply */
|
||
+ power_supply_unreg_notifier(&dwc->psy_nb);
|
||
+ dwc->psy_nb.notifier_call = NULL;
|
||
+
|
||
+ spin_lock_irqsave(&dwc->lock, flags);
|
||
+ /*
|
||
+ * It is possible that the notifier callback ran before we reached here
|
||
+ * and successfully fetched the power supply. In that case we need to
|
||
+ * release the above reference.
|
||
+ */
|
||
+ if (dwc->usb_psy) {
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
+ power_supply_put(psy);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ dwc->usb_psy = psy;
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
}
|
||
|
||
int dwc3_core_probe(const struct dwc3_probe_data *data)
|
||
@@ -2251,9 +2318,9 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
|
||
|
||
dwc3_get_software_properties(dwc, &data->properties);
|
||
|
||
- dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
|
||
- if (IS_ERR(dwc->usb_psy))
|
||
- return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
|
||
+ spin_lock_init(&dwc->lock);
|
||
+
|
||
+ dwc3_get_usb_power_supply(dwc);
|
||
|
||
if (!data->ignore_clocks_and_resets) {
|
||
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
|
||
@@ -2305,7 +2372,6 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
|
||
dwc->num_usb3_ports = 1;
|
||
}
|
||
|
||
- spin_lock_init(&dwc->lock);
|
||
mutex_init(&dwc->mutex);
|
||
|
||
pm_runtime_get_noresume(dev);
|
||
@@ -2373,6 +2439,8 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
|
||
err_assert_reset:
|
||
reset_control_assert(dwc->reset);
|
||
err_put_psy:
|
||
+ if (dwc->psy_nb.notifier_call)
|
||
+ power_supply_unreg_notifier(&dwc->psy_nb);
|
||
if (dwc->usb_psy)
|
||
power_supply_put(dwc->usb_psy);
|
||
|
||
@@ -2429,6 +2497,9 @@ void dwc3_core_remove(struct dwc3 *dwc)
|
||
|
||
dwc3_free_event_buffers(dwc);
|
||
|
||
+ if (dwc->psy_nb.notifier_call)
|
||
+ power_supply_unreg_notifier(&dwc->psy_nb);
|
||
+
|
||
if (dwc->usb_psy) {
|
||
cancel_work_sync(&dwc->vbus_draw_work);
|
||
power_supply_put(dwc->usb_psy);
|
||
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
|
||
--- a/drivers/usb/dwc3/core.h
|
||
+++ b/drivers/usb/dwc3/core.h
|
||
@@ -722,7 +722,6 @@ struct dwc3_event_buffer {
|
||
* @cancelled_list: list of cancelled requests for this endpoint
|
||
* @pending_list: list of pending requests for this endpoint
|
||
* @started_list: list of started requests on this endpoint
|
||
- * @regs: pointer to first endpoint register
|
||
* @trb_pool: array of transaction buffers
|
||
* @trb_pool_dma: dma address of @trb_pool
|
||
* @trb_enqueue: enqueue 'pointer' into TRB array
|
||
@@ -1059,6 +1058,8 @@ struct dwc3_glue_ops {
|
||
* @role_switch_default_mode: default operation mode of controller while
|
||
* usb role is USB_ROLE_NONE.
|
||
* @usb_psy: pointer to power supply interface.
|
||
+ * @usb_psy_name: name of the USB power supply
|
||
+ * @psy_nb: power supply notifier block
|
||
* @vbus_draw_work: Work to set the vbus drawing limit
|
||
* @current_limit: How much current to draw from vbus, in milliAmperes.
|
||
* @usb2_phy: pointer to USB2 PHY
|
||
@@ -1251,9 +1252,13 @@ struct dwc3 {
|
||
enum usb_dr_mode role_switch_default_mode;
|
||
|
||
struct power_supply *usb_psy;
|
||
+ const char *usb_psy_name;
|
||
+ struct notifier_block psy_nb;
|
||
struct work_struct vbus_draw_work;
|
||
unsigned int current_limit;
|
||
|
||
+#define DWC3_CURRENT_UNSPECIFIED UINT_MAX
|
||
+
|
||
u32 fladj;
|
||
u32 ref_clk_per;
|
||
u32 irq_gadget;
|
||
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
|
||
--- a/drivers/usb/dwc3/dwc3-am62.c
|
||
+++ b/drivers/usb/dwc3/dwc3-am62.c
|
||
@@ -205,7 +205,9 @@ static int dwc3_ti_init(struct dwc3_am62 *am62)
|
||
|
||
dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
|
||
|
||
- clk_prepare_enable(am62->usb2_refclk);
|
||
+ ret = clk_prepare_enable(am62->usb2_refclk);
|
||
+ if (ret)
|
||
+ return ret;
|
||
|
||
/* Set mode valid bit to indicate role is valid */
|
||
reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
|
||
@@ -361,14 +363,19 @@ static int dwc3_ti_resume_common(struct device *dev)
|
||
{
|
||
struct dwc3_am62 *am62 = dev_get_drvdata(dev);
|
||
u32 reg;
|
||
+ int ret;
|
||
|
||
reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG);
|
||
if (reg != USBSS_DEBUG_CFG_DISABLED) {
|
||
/* lost power/context */
|
||
- dwc3_ti_init(am62);
|
||
+ ret = dwc3_ti_init(am62);
|
||
+ if (ret)
|
||
+ return ret;
|
||
} else {
|
||
dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF);
|
||
- clk_prepare_enable(am62->usb2_refclk);
|
||
+ ret = clk_prepare_enable(am62->usb2_refclk);
|
||
+ if (ret)
|
||
+ return ret;
|
||
}
|
||
|
||
if (device_may_wakeup(dev)) {
|
||
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
|
||
--- a/drivers/usb/dwc3/dwc3-qcom.c
|
||
+++ b/drivers/usb/dwc3/dwc3-qcom.c
|
||
@@ -602,7 +602,7 @@ static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on)
|
||
pm_runtime_mark_last_busy(qcom->dev);
|
||
}
|
||
|
||
-struct dwc3_glue_ops dwc3_qcom_glue_ops = {
|
||
+static struct dwc3_glue_ops dwc3_qcom_glue_ops = {
|
||
.pre_set_role = dwc3_qcom_set_role_notifier,
|
||
.pre_run_stop = dwc3_qcom_run_stop_notifier,
|
||
};
|
||
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
|
||
--- a/drivers/usb/dwc3/dwc3-xilinx.c
|
||
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
|
||
@@ -98,18 +98,10 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
|
||
|
||
dwc3_xlnx_mask_phy_rst(priv_data, false);
|
||
|
||
- /* Assert and De-assert reset */
|
||
- ret = reset_control_assert(crst);
|
||
- if (ret < 0) {
|
||
- dev_err_probe(dev, ret, "failed to assert Reset\n");
|
||
- return ret;
|
||
- }
|
||
-
|
||
- ret = reset_control_deassert(crst);
|
||
- if (ret < 0) {
|
||
- dev_err_probe(dev, ret, "failed to De-assert Reset\n");
|
||
- return ret;
|
||
- }
|
||
+ /* assert and deassert reset */
|
||
+ ret = reset_control_reset(crst);
|
||
+ if (ret)
|
||
+ return dev_err_probe(dev, ret, "failed to assert and deassert reset\n");
|
||
|
||
dwc3_xlnx_mask_phy_rst(priv_data, true);
|
||
dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG);
|
||
@@ -194,7 +186,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
|
||
}
|
||
|
||
if (priv_data->usb3_phy) {
|
||
- /* Set PIPE Power Present signal in FPD Power Present Register*/
|
||
+ /* Set PIPE Power Present signal in FPD Power Present Register */
|
||
writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
|
||
/* Set the PIPE Clock Select bit in FPD PIPE Clock register */
|
||
writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
|
||
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
|
||
--- a/drivers/usb/dwc3/gadget.c
|
||
+++ b/drivers/usb/dwc3/gadget.c
|
||
@@ -3129,15 +3129,26 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
|
||
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
||
{
|
||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||
+ unsigned long flags;
|
||
|
||
if (dwc->usb2_phy)
|
||
return usb_phy_set_power(dwc->usb2_phy, mA);
|
||
|
||
- if (!dwc->usb_psy)
|
||
- return -EOPNOTSUPP;
|
||
+ spin_lock_irqsave(&dwc->lock, flags);
|
||
+ if (!dwc->usb_psy) {
|
||
+ if (!dwc->psy_nb.notifier_call) {
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
+ return -EOPNOTSUPP;
|
||
+ }
|
||
+ dwc->current_limit = mA;
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
+ dev_dbg(dwc->dev, "Stored VBUS draw: %u mA (power supply not ready)\n", mA);
|
||
+ return 0;
|
||
+ }
|
||
|
||
dwc->current_limit = mA;
|
||
schedule_work(&dwc->vbus_draw_work);
|
||
+ spin_unlock_irqrestore(&dwc->lock, flags);
|
||
|
||
return 0;
|
||
}
|
||
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
|
||
--- a/drivers/usb/dwc3/host.c
|
||
+++ b/drivers/usb/dwc3/host.c
|
||
@@ -12,6 +12,7 @@
|
||
#include <linux/platform_device.h>
|
||
#include <linux/usb.h>
|
||
#include <linux/usb/hcd.h>
|
||
+#include <linux/bitfield.h>
|
||
|
||
#include "../host/xhci-port.h"
|
||
#include "../host/xhci-ext-caps.h"
|
||
@@ -46,9 +47,9 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
|
||
return;
|
||
}
|
||
|
||
- op_regs_base = HC_LENGTH(readl(xhci_regs));
|
||
+ op_regs_base = FIELD_GET(HC_LENGTH, readl(xhci_regs));
|
||
reg = readl(xhci_regs + XHCI_HCSPARAMS1);
|
||
- port_num = HCS_MAX_PORTS(reg);
|
||
+ port_num = FIELD_GET(HCS_MAX_PORTS, reg);
|
||
|
||
for (i = 1; i <= port_num; i++) {
|
||
offset = op_regs_base + XHCI_PORTSC_BASE + 0x10 * (i - 1);
|
||
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
|
||
--- a/drivers/usb/gadget/configfs.c
|
||
+++ b/drivers/usb/gadget/configfs.c
|
||
@@ -125,11 +125,11 @@ static int usb_string_copy(const char *s, char **s_copy)
|
||
if (copy) {
|
||
str = copy;
|
||
} else {
|
||
- str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL);
|
||
+ str = kzalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL);
|
||
if (!str)
|
||
return -ENOMEM;
|
||
}
|
||
- strcpy(str, s);
|
||
+ memcpy(str, s, ret + 1);
|
||
if (str[ret - 1] == '\n')
|
||
str[ret - 1] = '\0';
|
||
*s_copy = str;
|
||
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
|
||
--- a/drivers/usb/gadget/function/f_fs.c
|
||
+++ b/drivers/usb/gadget/function/f_fs.c
|
||
@@ -159,7 +159,9 @@ struct ffs_epfile {
|
||
struct mutex mutex;
|
||
|
||
struct ffs_data *ffs;
|
||
- struct ffs_ep *ep; /* P: ffs->eps_lock */
|
||
+ struct ffs_ep *ep; /* P: ffs->eps_lock */
|
||
+ struct ffs_epfile *epfile_in; /* P: ffs->eps_lock */
|
||
+ struct ffs_epfile *epfile_out; /* P: ffs->eps_lock */
|
||
|
||
/*
|
||
* Buffer for holding data from partial reads which may happen since
|
||
@@ -219,12 +221,13 @@ struct ffs_epfile {
|
||
struct ffs_buffer *read_buffer;
|
||
#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
|
||
|
||
- char name[5];
|
||
+ char name[8];
|
||
|
||
unsigned char in; /* P: ffs->eps_lock */
|
||
unsigned char isoc; /* P: ffs->eps_lock */
|
||
|
||
- unsigned char _pad;
|
||
+ u8 zlp_enabled; /* P: ffs->eps_lock */
|
||
+ bool is_rw_proxy;
|
||
|
||
/* Protects dmabufs */
|
||
struct mutex dmabufs_mutex;
|
||
@@ -991,9 +994,8 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
|
||
return ret;
|
||
}
|
||
|
||
-static struct ffs_ep *ffs_epfile_wait_ep(struct file *file)
|
||
+static struct ffs_ep *ffs_epfile_wait_ep(struct ffs_epfile *epfile, struct file *file)
|
||
{
|
||
- struct ffs_epfile *epfile = file->private_data;
|
||
struct ffs_ep *ep;
|
||
int ret;
|
||
|
||
@@ -1020,17 +1022,22 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||
char *data = NULL;
|
||
ssize_t ret, data_len = -EINVAL;
|
||
int halt;
|
||
+ bool is_rw_proxy = epfile->is_rw_proxy;
|
||
|
||
/* Are we still active? */
|
||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
||
return -ENODEV;
|
||
|
||
- ep = ffs_epfile_wait_ep(file);
|
||
+ /* Proxy to base endpoint if rw_proxy */
|
||
+ if (is_rw_proxy)
|
||
+ epfile = io_data->read ? epfile->epfile_out : epfile->epfile_in;
|
||
+
|
||
+ ep = ffs_epfile_wait_ep(epfile, file);
|
||
if (IS_ERR(ep))
|
||
return PTR_ERR(ep);
|
||
|
||
/* Do we halt? */
|
||
- halt = (!io_data->read == !epfile->in);
|
||
+ halt = is_rw_proxy ? 0 : (!io_data->read == !epfile->in);
|
||
if (halt && epfile->isoc)
|
||
return -EINVAL;
|
||
|
||
@@ -1127,6 +1134,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||
req->buf = data;
|
||
req->num_sgs = 0;
|
||
}
|
||
+
|
||
+ req->zero = !io_data->read ? epfile->zlp_enabled : 0;
|
||
req->length = data_len;
|
||
|
||
io_data->buf = data;
|
||
@@ -1178,6 +1187,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||
req->buf = data;
|
||
req->num_sgs = 0;
|
||
}
|
||
+
|
||
+ req->zero = !io_data->read ? epfile->zlp_enabled : 0;
|
||
req->length = data_len;
|
||
|
||
io_data->buf = data;
|
||
@@ -1666,7 +1677,7 @@ static int ffs_dmabuf_transfer(struct file *file,
|
||
|
||
priv = attach->importer_priv;
|
||
|
||
- ep = ffs_epfile_wait_ep(file);
|
||
+ ep = ffs_epfile_wait_ep(epfile, file);
|
||
if (IS_ERR(ep)) {
|
||
ret = PTR_ERR(ep);
|
||
goto err_attachment_put;
|
||
@@ -1732,6 +1743,7 @@ static int ffs_dmabuf_transfer(struct file *file,
|
||
|
||
/* Now that the dma_fence is in place, queue the transfer. */
|
||
|
||
+ usb_req->zero = epfile->zlp_enabled;
|
||
usb_req->length = req->length;
|
||
usb_req->buf = NULL;
|
||
usb_req->sg = priv->sgt->sgl;
|
||
@@ -1779,10 +1791,14 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
||
struct ffs_epfile *epfile = file->private_data;
|
||
struct ffs_ep *ep;
|
||
int ret;
|
||
+ __u32 enable_zlp = 0;
|
||
|
||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
||
return -ENODEV;
|
||
|
||
+ if (epfile->is_rw_proxy)
|
||
+ return -ENOTTY;
|
||
+
|
||
switch (code) {
|
||
case FUNCTIONFS_DMABUF_ATTACH:
|
||
{
|
||
@@ -1811,12 +1827,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
||
|
||
return ffs_dmabuf_transfer(file, &req);
|
||
}
|
||
+ /*
|
||
+ * We handle this IOCTL before ffs_epfile_wait_ep() to allow userspace
|
||
+ * to configure ZLP behavior immediately without blocking indefinitely
|
||
+ * while waiting for the USB host to connect and enable the endpoint.
|
||
+ */
|
||
+ case FUNCTIONFS_ENDPOINT_ENABLE_ZLP:
|
||
+ if (!epfile->in)
|
||
+ return -EINVAL;
|
||
+
|
||
+ if (copy_from_user(&enable_zlp, (void __user *)value, sizeof(enable_zlp)))
|
||
+ return -EFAULT;
|
||
+
|
||
+ spin_lock_irq(&epfile->ffs->eps_lock);
|
||
+ epfile->zlp_enabled = !!enable_zlp;
|
||
+ spin_unlock_irq(&epfile->ffs->eps_lock);
|
||
+
|
||
+ return 0;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
/* Wait for endpoint to be enabled */
|
||
- ep = ffs_epfile_wait_ep(file);
|
||
+ ep = ffs_epfile_wait_ep(epfile, file);
|
||
if (IS_ERR(ep))
|
||
return PTR_ERR(ep);
|
||
|
||
@@ -2214,7 +2247,7 @@ static void ffs_data_closed(struct ffs_data *ffs)
|
||
|
||
if (epfiles)
|
||
ffs_epfiles_destroy(ffs->sb, epfiles,
|
||
- ffs->eps_count);
|
||
+ ffs->epfiles_count);
|
||
|
||
if (ffs->setup_state == FFS_SETUP_PENDING)
|
||
__ffs_ep0_stall(ffs);
|
||
@@ -2273,7 +2306,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
|
||
* copy of epfile will save us from use-after-free.
|
||
*/
|
||
if (epfiles) {
|
||
- ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count);
|
||
+ ffs_epfiles_destroy(ffs->sb, epfiles, ffs->epfiles_count);
|
||
ffs->epfiles = NULL;
|
||
}
|
||
|
||
@@ -2371,11 +2404,16 @@ static void functionfs_unbind(struct ffs_data *ffs)
|
||
static int ffs_epfiles_create(struct ffs_data *ffs)
|
||
{
|
||
struct ffs_epfile *epfile, *epfiles;
|
||
- unsigned i, count;
|
||
+ unsigned int i, count, epfiles_count;
|
||
int err;
|
||
|
||
count = ffs->eps_count;
|
||
- epfiles = kzalloc_objs(*epfiles, count);
|
||
+ epfiles_count = count;
|
||
+ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS)
|
||
+ epfiles_count += count / 2;
|
||
+ ffs->epfiles_count = epfiles_count;
|
||
+
|
||
+ epfiles = kzalloc_objs(*epfiles, epfiles_count);
|
||
if (!epfiles)
|
||
return -ENOMEM;
|
||
|
||
@@ -2398,6 +2436,32 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
|
||
}
|
||
}
|
||
|
||
+ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) {
|
||
+ struct ffs_epfile *comp = epfiles + count;
|
||
+
|
||
+ for (i = 0; i < count; i += 2, ++comp) {
|
||
+ struct ffs_epfile *ep1 = &epfiles[i];
|
||
+ struct ffs_epfile *ep2 = &epfiles[i + 1];
|
||
+ bool ep1_in = ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK;
|
||
+
|
||
+ comp->ffs = ffs;
|
||
+ comp->is_rw_proxy = true;
|
||
+ comp->epfile_in = ep1_in ? ep1 : ep2;
|
||
+ comp->epfile_out = ep1_in ? ep2 : ep1;
|
||
+ mutex_init(&comp->mutex);
|
||
+ mutex_init(&comp->dmabufs_mutex);
|
||
+ INIT_LIST_HEAD(&comp->dmabufs);
|
||
+ snprintf(comp->name, sizeof(comp->name), "%s_rw",
|
||
+ epfiles[i].name);
|
||
+ err = ffs_sb_create_file(ffs->sb, comp->name,
|
||
+ comp, &ffs_epfile_operations);
|
||
+ if (err) {
|
||
+ ffs_epfiles_destroy(ffs->sb, epfiles, count + (i / 2));
|
||
+ return err;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
ffs->epfiles = epfiles;
|
||
return 0;
|
||
}
|
||
@@ -2975,7 +3039,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||
FUNCTIONFS_VIRTUAL_ADDR |
|
||
FUNCTIONFS_EVENTFD |
|
||
FUNCTIONFS_ALL_CTRL_RECIP |
|
||
- FUNCTIONFS_CONFIG0_SETUP)) {
|
||
+ FUNCTIONFS_CONFIG0_SETUP |
|
||
+ FUNCTIONFS_RW_PROXY_EPS)) {
|
||
ret = -ENOSYS;
|
||
goto error;
|
||
}
|
||
@@ -3063,6 +3128,21 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||
goto error;
|
||
}
|
||
|
||
+ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) {
|
||
+ if (ffs->eps_count % 2) {
|
||
+ ret = -EINVAL;
|
||
+ goto error;
|
||
+ }
|
||
+
|
||
+ for (i = 1; i < ffs->eps_count; i += 2) {
|
||
+ if ((ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ==
|
||
+ (ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK)) {
|
||
+ ret = -EINVAL;
|
||
+ goto error;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
ffs->raw_descs_data = _data;
|
||
ffs->raw_descs = raw_descs;
|
||
ffs->raw_descs_length = data - raw_descs;
|
||
@@ -3358,7 +3438,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
||
struct usb_request *req;
|
||
struct usb_ep *ep;
|
||
u8 bEndpointAddress;
|
||
- u16 wMaxPacketSize;
|
||
+ __le16 wMaxPacketSize;
|
||
|
||
/*
|
||
* We back up bEndpointAddress because autoconfig overwrites
|
||
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
|
||
--- a/drivers/usb/gadget/function/f_mass_storage.c
|
||
+++ b/drivers/usb/gadget/function/f_mass_storage.c
|
||
@@ -203,7 +203,6 @@
|
||
/*------------------------------------------------------------------------*/
|
||
|
||
#define FSG_DRIVER_DESC "Mass Storage Function"
|
||
-#define FSG_DRIVER_VERSION "2009/09/11"
|
||
|
||
static const char fsg_string_interface[] = "Mass Storage";
|
||
|
||
@@ -3516,8 +3515,6 @@ static struct usb_function_instance *fsg_alloc_inst(void)
|
||
if (rc)
|
||
goto release_common;
|
||
|
||
- pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
|
||
-
|
||
memset(&config, 0, sizeof(config));
|
||
config.removable = true;
|
||
rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
|
||
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
|
||
--- a/drivers/usb/gadget/function/u_audio.c
|
||
+++ b/drivers/usb/gadget/function/u_audio.c
|
||
@@ -641,15 +641,15 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
||
ret = config_ep_by_speed(gadget, &audio_dev->func, ep_fback);
|
||
if (ret < 0) {
|
||
dev_err(dev, "config_ep_by_speed in_ep_fback failed (%d)\n", ret);
|
||
- return ret; // TODO: Clean up out_ep
|
||
+ goto err_out_ep;
|
||
}
|
||
|
||
- prm->fb_ep_enabled = true;
|
||
ret = usb_ep_enable(ep_fback);
|
||
if (ret < 0) {
|
||
dev_err(dev, "usb_ep_enable failed for in_ep_fback (%d)\n", ret);
|
||
- return ret; // TODO: Clean up out_ep
|
||
+ goto err_out_ep;
|
||
}
|
||
+ prm->fb_ep_enabled = true;
|
||
req_len = ep_fback->maxpacket;
|
||
|
||
req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC);
|
||
@@ -680,6 +680,12 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||
|
||
return 0;
|
||
+
|
||
+err_out_ep:
|
||
+ set_active(prm, false);
|
||
+ free_ep(prm, ep);
|
||
+
|
||
+ return ret;
|
||
}
|
||
EXPORT_SYMBOL_GPL(u_audio_start_capture);
|
||
|
||
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
|
||
--- a/drivers/usb/gadget/function/u_fs.h
|
||
+++ b/drivers/usb/gadget/function/u_fs.h
|
||
@@ -252,8 +252,14 @@ struct ffs_data {
|
||
|
||
unsigned short strings_count;
|
||
unsigned short interfaces_count;
|
||
+
|
||
+ /*
|
||
+ * eps_count tracks the number of underlying hardware endpoints.
|
||
+ * epfiles_count tracks the total number of VFS endpoint files.
|
||
+ * When companion endpoints are active, epfiles_count > eps_count.
|
||
+ */
|
||
unsigned short eps_count;
|
||
- unsigned short _pad1;
|
||
+ unsigned short epfiles_count;
|
||
|
||
/* filled by __ffs_data_got_strings() */
|
||
/* ids in stringtabs are set in functionfs_bind() */
|
||
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
|
||
--- a/drivers/usb/gadget/legacy/inode.c
|
||
+++ b/drivers/usb/gadget/legacy/inode.c
|
||
@@ -471,11 +471,17 @@ static void ep_user_copy_worker(struct work_struct *work)
|
||
struct kiocb *iocb = priv->iocb;
|
||
size_t ret;
|
||
|
||
- kthread_use_mm(mm);
|
||
- ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
|
||
- kthread_unuse_mm(mm);
|
||
- if (!ret)
|
||
+ if (mmget_not_zero(mm)) {
|
||
+ kthread_use_mm(mm);
|
||
+ ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
|
||
+ kthread_unuse_mm(mm);
|
||
+ mmput(mm);
|
||
+ if (!ret)
|
||
+ ret = -EFAULT;
|
||
+ } else {
|
||
ret = -EFAULT;
|
||
+ }
|
||
+ mmdrop(mm);
|
||
|
||
/* completing the iocb can drop the ctx and mm, don't touch mm after */
|
||
iocb->ki_complete(iocb, ret);
|
||
@@ -501,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
|
||
* complete the aio request immediately.
|
||
*/
|
||
if (priv->to_free == NULL || unlikely(req->actual == 0)) {
|
||
+ mmdrop(priv->mm);
|
||
kfree(req->buf);
|
||
kfree(priv->to_free);
|
||
kfree(priv);
|
||
@@ -541,6 +548,7 @@ static ssize_t ep_aio(struct kiocb *iocb,
|
||
priv->epdata = epdata;
|
||
priv->actual = 0;
|
||
priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */
|
||
+ mmgrab(priv->mm);
|
||
|
||
/* each kiocb is coupled to one usb_request, but we can't
|
||
* allocate or submit those if the host disconnected.
|
||
@@ -570,6 +578,7 @@ static ssize_t ep_aio(struct kiocb *iocb,
|
||
|
||
fail:
|
||
spin_unlock_irq(&epdata->dev->lock);
|
||
+ mmdrop(priv->mm);
|
||
kfree(priv->to_free);
|
||
kfree(priv);
|
||
put_ep(epdata);
|
||
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
|
||
--- a/drivers/usb/gadget/legacy/nokia.c
|
||
+++ b/drivers/usb/gadget/legacy/nokia.c
|
||
@@ -50,7 +50,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
|
||
*/
|
||
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
|
||
|
||
-#endif /* CONFIG_USB_DEBUG */
|
||
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
|
||
|
||
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
|
||
|
||
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
|
||
--- a/drivers/usb/gadget/udc/Kconfig
|
||
+++ b/drivers/usb/gadget/udc/Kconfig
|
||
@@ -90,7 +90,7 @@ config USB_BCM63XX_UDC
|
||
|
||
config USB_FSL_USB2
|
||
tristate "Freescale Highspeed USB DR Peripheral Controller"
|
||
- depends on FSL_SOC
|
||
+ depends on FSL_SOC || COMPILE_TEST
|
||
help
|
||
Some of Freescale PowerPC and i.MX processors have a High Speed
|
||
Dual-Role(DR) USB controller, which supports device mode.
|
||
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
|
||
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
|
||
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
|
||
@@ -1945,6 +1945,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
|
||
u16 index, u16 length)
|
||
{
|
||
u16 usb_status = 0;
|
||
+ struct usb_request *usb_req;
|
||
struct qe_req *req;
|
||
struct qe_ep *ep;
|
||
int status = 0;
|
||
@@ -1983,8 +1984,11 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
|
||
}
|
||
}
|
||
|
||
- req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL),
|
||
- struct qe_req, req);
|
||
+ usb_req = qe_alloc_request(&ep->ep, GFP_KERNEL);
|
||
+ if (!usb_req)
|
||
+ goto stall;
|
||
+
|
||
+ req = container_of(usb_req, struct qe_req, req);
|
||
req->req.length = 2;
|
||
req->req.buf = udc->statusbuf;
|
||
*(u16 *)req->req.buf = cpu_to_le16(usb_status);
|
||
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
|
||
--- a/drivers/usb/gadget/udc/pch_udc.c
|
||
+++ b/drivers/usb/gadget/udc/pch_udc.c
|
||
@@ -320,7 +320,6 @@ struct pch_vbus_gpio_data {
|
||
* @lock: protects all state
|
||
* @stall: stall requested
|
||
* @prot_stall: protcol stall requested
|
||
- * @registered: driver registered with system
|
||
* @suspended: driver in suspended state
|
||
* @connected: gadget driver associated
|
||
* @vbus_session: required vbus_session state
|
||
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
|
||
--- a/drivers/usb/host/fsl-mph-dr-of.c
|
||
+++ b/drivers/usb/host/fsl-mph-dr-of.c
|
||
@@ -15,6 +15,7 @@
|
||
#include <linux/clk.h>
|
||
#include <linux/module.h>
|
||
#include <linux/dma-mapping.h>
|
||
+#include <linux/regulator/consumer.h>
|
||
|
||
struct fsl_usb2_dev_data {
|
||
char *dr_mode; /* controller mode */
|
||
@@ -183,7 +184,7 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
|
||
const struct of_device_id *match;
|
||
const unsigned char *prop;
|
||
static unsigned int idx;
|
||
- int i;
|
||
+ int i, err;
|
||
|
||
if (!of_device_is_available(np))
|
||
return -ENODEV;
|
||
@@ -246,6 +247,10 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
|
||
}
|
||
}
|
||
|
||
+ err = devm_regulator_get_enable_optional(&ofdev->dev, "vbus");
|
||
+ if (err)
|
||
+ return dev_err_probe(&ofdev->dev, err, "failed to get vbus regulator\n");
|
||
+
|
||
for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) {
|
||
if (!dev_data->drivers[i])
|
||
continue;
|
||
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
|
||
--- a/drivers/usb/host/ohci-dbg.c
|
||
+++ b/drivers/usb/host/ohci-dbg.c
|
||
@@ -683,7 +683,7 @@ static int fill_buffer(struct debug_buffer *buf)
|
||
int ret;
|
||
|
||
if (!buf->page)
|
||
- buf->page = (char *)get_zeroed_page(GFP_KERNEL);
|
||
+ buf->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||
|
||
if (!buf->page) {
|
||
ret = -ENOMEM;
|
||
@@ -729,11 +729,8 @@ static int debug_close(struct inode *inode, struct file *file)
|
||
{
|
||
struct debug_buffer *buf = file->private_data;
|
||
|
||
- if (buf) {
|
||
- if (buf->page)
|
||
- free_page((unsigned long)buf->page);
|
||
- kfree(buf);
|
||
- }
|
||
+ kfree(buf->page);
|
||
+ kfree(buf);
|
||
|
||
return 0;
|
||
}
|
||
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
|
||
--- a/drivers/usb/host/xhci-caps.h
|
||
+++ b/drivers/usb/host/xhci-caps.h
|
||
@@ -5,22 +5,23 @@
|
||
*/
|
||
|
||
#include <linux/bits.h>
|
||
+#include <linux/bitfield.h>
|
||
|
||
/* hc_capbase - bitmasks */
|
||
/* bits 7:0 - Capability Registers Length */
|
||
-#define HC_LENGTH(p) ((p) & 0xff)
|
||
+#define HC_LENGTH GENMASK(7, 0)
|
||
/* bits 15:8 - Rsvd */
|
||
/* bits 31:16 - Host Controller Interface Version Number */
|
||
-#define HC_VERSION(p) (((p) >> 16) & 0xffff)
|
||
+#define HC_VERSION GENMASK(31, 16)
|
||
|
||
/* HCSPARAMS1 - hcs_params1 - bitmasks */
|
||
/* bits 7:0 - Number of Device Slots */
|
||
-#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
|
||
-#define HCS_SLOTS_MASK 0xff
|
||
-/* bits 18:8 - Number of Interrupters, max values is 1024 */
|
||
-#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff)
|
||
-/* bits 31:24, Max Ports - max value is 255 */
|
||
-#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff)
|
||
+#define HCS_SLOTS_MASK GENMASK(7, 0)
|
||
+/* bits 18:8 - Number of Interrupters, max values is 1024 */
|
||
+#define HCS_MAX_INTRS GENMASK(18, 8)
|
||
+/* bits 23:19 - Rsvd */
|
||
+/* bits 31:24 - Max Ports, max values is 255 */
|
||
+#define HCS_MAX_PORTS GENMASK(31, 24)
|
||
|
||
/* HCSPARAMS2 - hcs_params2 - bitmasks */
|
||
/*
|
||
@@ -33,24 +34,25 @@
|
||
* Note: 1 Frame = 8 Microframes
|
||
* xHCI specification section 5.3.4.
|
||
*/
|
||
-#define HCS_IST_VALUE(p) ((p) & 0x7)
|
||
+#define HCS_IST_VALUE GENMASK(2, 0)
|
||
#define HCS_IST_UNIT BIT(3)
|
||
/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */
|
||
-#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
|
||
+#define HCS_ERST_MAX GENMASK(7, 4)
|
||
/* bits 20:8 - Rsvd */
|
||
/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */
|
||
-#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f)
|
||
+#define HCS_MAX_SP_HI GENMASK(25, 21)
|
||
/* bit 26 - Scratchpad restore, for save/restore HW state */
|
||
/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */
|
||
-#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f)
|
||
-#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p))
|
||
+#define HCS_MAX_SP_LO GENMASK(31, 27)
|
||
+#define HCS_MAX_SCRATCHPAD(p) (FIELD_GET(HCS_MAX_SP_HI, (p)) << 5 | \
|
||
+ FIELD_GET(HCS_MAX_SP_LO, (p)))
|
||
|
||
/* HCSPARAMS3 - hcs_params3 - bitmasks */
|
||
/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */
|
||
-#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff)
|
||
+#define HCS_U1_LATENCY GENMASK(7, 0)
|
||
/* bits 15:8 - Rsvd */
|
||
/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */
|
||
-#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff)
|
||
+#define HCS_U2_LATENCY GENMASK(31, 16)
|
||
|
||
/* HCCPARAMS1 - hcc_params - bitmasks */
|
||
/* bit 0 - 64-bit Addressing Capability */
|
||
@@ -77,19 +79,20 @@
|
||
/* bit 11 - Contiguous Frame ID Capability */
|
||
#define HCC_CFC BIT(11)
|
||
/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */
|
||
-#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
|
||
+#define HCC_MAX_PSA GENMASK(15, 12)
|
||
+#define GET_MAX_PSA_SIZE(p) (1 << (FIELD_GET(HCC_MAX_PSA, (p)) + 1))
|
||
/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
|
||
-#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff)
|
||
+#define HCC_EXT_CAPS GENMASK(31, 16)
|
||
|
||
/* DBOFF - db_off - bitmasks */
|
||
/* bits 1:0 - Rsvd */
|
||
/* bits 31:2 - Doorbell Array Offset */
|
||
-#define DBOFF_MASK (0xfffffffc)
|
||
+#define DBOFF_MASK GENMASK(31, 2)
|
||
|
||
/* RTSOFF - run_regs_off - bitmasks */
|
||
/* bits 4:0 - Rsvd */
|
||
/* bits 31:5 - Runtime Register Space Offse */
|
||
-#define RTSOFF_MASK (~0x1f)
|
||
+#define RTSOFF_MASK GENMASK(31, 5)
|
||
|
||
/* HCCPARAMS2 - hcc_params2 - bitmasks */
|
||
/* bit 0 - U3 Entry Capability */
|
||
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
|
||
--- a/drivers/usb/host/xhci-dbgtty.c
|
||
+++ b/drivers/usb/host/xhci-dbgtty.c
|
||
@@ -665,11 +665,11 @@ int dbc_tty_init(void)
|
||
|
||
void dbc_tty_exit(void)
|
||
{
|
||
- if (dbc_tty_driver) {
|
||
- tty_unregister_driver(dbc_tty_driver);
|
||
- tty_driver_kref_put(dbc_tty_driver);
|
||
- dbc_tty_driver = NULL;
|
||
- }
|
||
+ if (IS_ERR_OR_NULL(dbc_tty_driver))
|
||
+ return;
|
||
|
||
+ tty_unregister_driver(dbc_tty_driver);
|
||
+ tty_driver_kref_put(dbc_tty_driver);
|
||
idr_destroy(&dbc_tty_minors);
|
||
+ dbc_tty_driver = NULL;
|
||
}
|
||
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
|
||
--- a/drivers/usb/host/xhci-debugfs.c
|
||
+++ b/drivers/usb/host/xhci-debugfs.c
|
||
@@ -9,6 +9,7 @@
|
||
|
||
#include <linux/slab.h>
|
||
#include <linux/uaccess.h>
|
||
+#include <linux/bitfield.h>
|
||
|
||
#include "xhci.h"
|
||
#include "xhci-debugfs.h"
|
||
@@ -791,7 +792,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
|
||
xhci->debugfs_root, "reg-cap");
|
||
|
||
xhci_debugfs_regset(xhci,
|
||
- HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)),
|
||
+ FIELD_GET(HC_LENGTH, readl(&xhci->cap_regs->hc_capbase)),
|
||
xhci_op_regs, ARRAY_SIZE(xhci_op_regs),
|
||
xhci->debugfs_root, "reg-op");
|
||
|
||
diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c
|
||
--- a/drivers/usb/host/xhci-histb.c
|
||
+++ b/drivers/usb/host/xhci-histb.c
|
||
@@ -276,7 +276,7 @@ static int xhci_histb_probe(struct platform_device *pdev)
|
||
if (ret)
|
||
goto put_usb3_hcd;
|
||
|
||
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
||
+ if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4)
|
||
xhci->shared_hcd->can_do_streams = 1;
|
||
|
||
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
|
||
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
|
||
--- a/drivers/usb/host/xhci-hub.c
|
||
+++ b/drivers/usb/host/xhci-hub.c
|
||
@@ -115,8 +115,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
|
||
|
||
if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
|
||
reg = readl(&xhci->cap_regs->hcs_params3);
|
||
- ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg);
|
||
- ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg));
|
||
+ ss_cap->bU1devExitLat = FIELD_GET(HCS_U1_LATENCY, reg);
|
||
+ ss_cap->bU2DevExitLat = cpu_to_le16(FIELD_GET(HCS_U2_LATENCY, reg));
|
||
}
|
||
|
||
if (wLength < le16_to_cpu(bos->wTotalLength))
|
||
@@ -1295,7 +1295,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||
}
|
||
/* In spec software should not attempt to suspend
|
||
* a port unless the port reports that it is in the
|
||
- * enabled (PED = ‘1’,PLS < ‘3’) state.
|
||
+ * enabled (PED = '1',PLS < '3') state.
|
||
*/
|
||
portsc = xhci_portsc_readl(port);
|
||
if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) ||
|
||
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
|
||
--- a/drivers/usb/host/xhci-mem.c
|
||
+++ b/drivers/usb/host/xhci-mem.c
|
||
@@ -14,6 +14,7 @@
|
||
#include <linux/slab.h>
|
||
#include <linux/dmapool.h>
|
||
#include <linux/dma-mapping.h>
|
||
+#include <linux/bitfield.h>
|
||
|
||
#include "xhci.h"
|
||
#include "xhci-trace.h"
|
||
@@ -1001,7 +1002,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
|
||
for (i = 0; i < 31; i++) {
|
||
dev->eps[i].ep_index = i;
|
||
dev->eps[i].vdev = dev;
|
||
- dev->eps[i].xhci = xhci;
|
||
INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
|
||
INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
|
||
}
|
||
@@ -1493,6 +1493,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
||
return -ENOMEM;
|
||
|
||
virt_dev->eps[ep_index].skip = false;
|
||
+ virt_dev->eps[ep_index].next_uframe = -1;
|
||
ep_ring = virt_dev->eps[ep_index].new_ring;
|
||
xhci_ring_init(xhci, ep_ring);
|
||
|
||
@@ -2083,7 +2084,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
|
||
addr, port_offset, port_count, major_revision);
|
||
/* Port count includes the current port offset */
|
||
if (port_offset == 0 || (port_offset + port_count - 1) > num_ports)
|
||
- /* WTF? "Valid values are ‘1’ to MaxPorts" */
|
||
+ /* WTF? "Valid values are '1' to MaxPorts" */
|
||
return;
|
||
|
||
port_cap = &xhci->port_caps[xhci->num_port_caps++];
|
||
@@ -2300,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
|
||
if (!segs)
|
||
segs = ERST_DEFAULT_SEGS;
|
||
|
||
- max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2));
|
||
+ max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2;
|
||
segs = min(segs, max_segs);
|
||
|
||
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
|
||
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
|
||
--- a/drivers/usb/host/xhci-mtk.c
|
||
+++ b/drivers/usb/host/xhci-mtk.c
|
||
@@ -468,7 +468,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||
* MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream,
|
||
* and it's 3 when support it.
|
||
*/
|
||
- if (xhci->hci_version < 0x100 && HCC_MAX_PSA(xhci->hcc_params) == 4)
|
||
+ if (xhci->hci_version < 0x100 && GET_MAX_PSA_SIZE(xhci->hcc_params) == 4)
|
||
xhci->quirks |= XHCI_BROKEN_STREAMS;
|
||
}
|
||
|
||
@@ -650,7 +650,7 @@ static int xhci_mtk_probe(struct platform_device *pdev)
|
||
}
|
||
|
||
usb3_hcd = xhci_get_usb3_hcd(xhci);
|
||
- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 &&
|
||
+ if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 &&
|
||
!(xhci->quirks & XHCI_BROKEN_STREAMS))
|
||
usb3_hcd->can_do_streams = 1;
|
||
|
||
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
|
||
--- a/drivers/usb/host/xhci-pci.c
|
||
+++ b/drivers/usb/host/xhci-pci.c
|
||
@@ -14,6 +14,7 @@
|
||
#include <linux/acpi.h>
|
||
#include <linux/reset.h>
|
||
#include <linux/suspend.h>
|
||
+#include <linux/bitfield.h>
|
||
|
||
#include "xhci.h"
|
||
#include "xhci-trace.h"
|
||
@@ -662,7 +663,8 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||
}
|
||
|
||
usb3_hcd = xhci_get_usb3_hcd(xhci);
|
||
- if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
||
+ if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) &&
|
||
+ GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4)
|
||
usb3_hcd->can_do_streams = 1;
|
||
|
||
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
|
||
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
|
||
--- a/drivers/usb/host/xhci-plat.c
|
||
+++ b/drivers/usb/host/xhci-plat.c
|
||
@@ -340,7 +340,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
|
||
}
|
||
|
||
usb3_hcd = xhci_get_usb3_hcd(xhci);
|
||
- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 &&
|
||
+ if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 &&
|
||
!(xhci->quirks & XHCI_BROKEN_STREAMS))
|
||
usb3_hcd->can_do_streams = 1;
|
||
|
||
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
|
||
--- a/drivers/usb/host/xhci-ring.c
|
||
+++ b/drivers/usb/host/xhci-ring.c
|
||
@@ -57,6 +57,8 @@
|
||
#include <linux/slab.h>
|
||
#include <linux/string_choices.h>
|
||
#include <linux/dma-mapping.h>
|
||
+#include <linux/bitfield.h>
|
||
+
|
||
#include "xhci.h"
|
||
#include "xhci-trace.h"
|
||
|
||
@@ -561,8 +563,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
|
||
* pointer command pending because the device can choose to start any
|
||
* stream once the endpoint is on the HW schedule.
|
||
*/
|
||
- if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) ||
|
||
- (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT))
|
||
+ if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED |
|
||
+ EP_CLEARING_TT | EP_DROP_PENDING))
|
||
return;
|
||
|
||
trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
|
||
@@ -573,9 +575,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
|
||
}
|
||
|
||
/* Ring the doorbell for any rings with pending URBs */
|
||
-static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
|
||
- unsigned int slot_id,
|
||
- unsigned int ep_index)
|
||
+void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, unsigned int slot_id,
|
||
+ unsigned int ep_index)
|
||
{
|
||
unsigned int stream_id;
|
||
struct xhci_virt_ep *ep;
|
||
@@ -598,13 +599,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
|
||
}
|
||
}
|
||
|
||
-void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
|
||
- unsigned int slot_id,
|
||
- unsigned int ep_index)
|
||
-{
|
||
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
-}
|
||
-
|
||
static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci,
|
||
unsigned int slot_id,
|
||
unsigned int ep_index)
|
||
@@ -686,112 +680,100 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev,
|
||
return le64_to_cpu(ep_ctx->deq);
|
||
}
|
||
|
||
-static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
|
||
- unsigned int slot_id, unsigned int ep_index,
|
||
- unsigned int stream_id, struct xhci_td *td)
|
||
+/*
|
||
+ * Move the endpoint dequeue pointer to the next queued TD on ring->td_list or
|
||
+ * to enqueue if no TDs are queued (empty ring)
|
||
+ * All cancelled TDs on ring->td_list should be moved to ep->cancelled_td_list
|
||
+ * before calling this function
|
||
+ */
|
||
+static int xhci_move_deq_to_next_td(struct xhci_hcd *xhci,
|
||
+ struct xhci_virt_ep *ep,
|
||
+ unsigned int stream_id)
|
||
{
|
||
- struct xhci_virt_device *dev = xhci->devs[slot_id];
|
||
- struct xhci_virt_ep *ep = &dev->eps[ep_index];
|
||
- struct xhci_ring *ep_ring;
|
||
struct xhci_command *cmd;
|
||
- struct xhci_segment *new_seg;
|
||
- union xhci_trb *new_deq;
|
||
- int new_cycle;
|
||
+ struct xhci_ring *ring;
|
||
+ struct xhci_td *td;
|
||
dma_addr_t addr;
|
||
- u64 hw_dequeue;
|
||
- bool hw_dequeue_found = false;
|
||
- bool td_last_trb_found = false;
|
||
+ int new_cycle;
|
||
u32 trb_sct = 0;
|
||
- int ret;
|
||
+ int ret = 0;
|
||
|
||
- ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
|
||
- ep_index, stream_id);
|
||
- if (!ep_ring) {
|
||
+ ring = xhci_virt_ep_to_ring(xhci, ep, stream_id);
|
||
+ if (!ring) {
|
||
xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n",
|
||
stream_id);
|
||
return -ENODEV;
|
||
}
|
||
|
||
- hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK;
|
||
- new_seg = ep_ring->deq_seg;
|
||
- new_deq = ep_ring->dequeue;
|
||
- new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE;
|
||
-
|
||
- /*
|
||
- * Walk the ring until both the next TRB and hw_dequeue are found (don't
|
||
- * move hw_dequeue back if it went forward due to a HW bug). Cycle state
|
||
- * is loaded from a known good TRB, track later toggles to maintain it.
|
||
- */
|
||
- do {
|
||
- if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq)
|
||
- == (dma_addr_t)hw_dequeue) {
|
||
- hw_dequeue_found = true;
|
||
- if (td_last_trb_found)
|
||
- break;
|
||
- }
|
||
- if (new_deq == td->end_trb)
|
||
- td_last_trb_found = true;
|
||
-
|
||
- if (td_last_trb_found && trb_is_link(new_deq) &&
|
||
- link_trb_toggles_cycle(new_deq))
|
||
- new_cycle ^= 0x1;
|
||
-
|
||
- next_trb(&new_seg, &new_deq);
|
||
-
|
||
- /* Search wrapped around, bail out */
|
||
- if (new_deq == ep->ring->dequeue) {
|
||
- xhci_err(xhci, "Error: Failed finding new dequeue state\n");
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
- } while (!hw_dequeue_found || !td_last_trb_found);
|
||
-
|
||
- /* Don't update the ring cycle state for the producer (us). */
|
||
- addr = xhci_trb_virt_to_dma(new_seg, new_deq);
|
||
- if (addr == 0) {
|
||
- xhci_warn(xhci, "Can't find dma of new dequeue ptr\n");
|
||
- xhci_warn(xhci, "deq seg = %p, deq ptr = %p\n", new_seg, new_deq);
|
||
- return -EINVAL;
|
||
- }
|
||
-
|
||
if ((ep->ep_state & SET_DEQ_PENDING)) {
|
||
- xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n",
|
||
- &addr);
|
||
+ xhci_warn(xhci, "Set TR Deq already pending\n");
|
||
return -EBUSY;
|
||
}
|
||
|
||
/* This function gets called from contexts where it cannot sleep */
|
||
cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||
if (!cmd) {
|
||
- xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr);
|
||
+ xhci_warn(xhci, "Can't alloc Set TR Deq cmd\n");
|
||
return -ENOMEM;
|
||
}
|
||
|
||
+ /*
|
||
+ * Move dequeue to the beginning of next td, or to enqueue if ring is
|
||
+ * empty. Avoid moving dequeue to a link trb (empty ring) as it causes
|
||
+ * issues on some hosts. In that case advance the enqueue to next segment
|
||
+ * before moving dequeue to it
|
||
+ */
|
||
+
|
||
+ if (list_empty(&ring->td_list)) {
|
||
+ if (trb_is_link(ring->enqueue))
|
||
+ inc_enq_past_link(xhci, ring, 0);
|
||
+ ep->queued_deq_seg = ring->enq_seg;
|
||
+ ep->queued_deq_ptr = ring->enqueue;
|
||
+ new_cycle = ring->cycle_state;
|
||
+ } else {
|
||
+ td = list_first_entry(&ring->td_list, struct xhci_td, td_list);
|
||
+ ep->queued_deq_seg = td->start_seg;
|
||
+ ep->queued_deq_ptr = td->start_trb;
|
||
+ new_cycle = le32_to_cpu(td->start_trb->generic.field[3]) & TRB_CYCLE;
|
||
+ }
|
||
+
|
||
+ addr = xhci_trb_virt_to_dma(ep->queued_deq_seg, ep->queued_deq_ptr);
|
||
+ if (addr == 0) {
|
||
+ xhci_warn(xhci, "Can't find new dequeue dma of seg %p, ptr %p\n",
|
||
+ ep->queued_deq_seg, ep->queued_deq_ptr);
|
||
+ ret = -EINVAL;
|
||
+ goto err_out;
|
||
+ }
|
||
+
|
||
if (stream_id)
|
||
trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
|
||
ret = queue_command(xhci, cmd,
|
||
lower_32_bits(addr) | trb_sct | new_cycle,
|
||
upper_32_bits(addr),
|
||
- STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) |
|
||
- EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
|
||
- if (ret < 0) {
|
||
- xhci_free_command(xhci, cmd);
|
||
- return ret;
|
||
- }
|
||
- ep->queued_deq_seg = new_seg;
|
||
- ep->queued_deq_ptr = new_deq;
|
||
+ STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(ep->vdev->slot_id) |
|
||
+ EP_INDEX_FOR_TRB(ep->ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
|
||
+ if (ret < 0)
|
||
+ goto err_out;
|
||
|
||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||
"Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle);
|
||
|
||
- /* Stop the TD queueing code from ringing the doorbell until
|
||
- * this command completes. The HC won't set the dequeue pointer
|
||
- * if the ring is running, and ringing the doorbell starts the
|
||
- * ring running.
|
||
+ /*
|
||
+ * Stop the TD queueing code from ringing the doorbell until this
|
||
+ * command completes. The HC won't set the dequeue pointer if the ring
|
||
+ * is running, and ringing the doorbell starts the ring.
|
||
*/
|
||
ep->ep_state |= SET_DEQ_PENDING;
|
||
xhci_ring_cmd_db(xhci);
|
||
+
|
||
return 0;
|
||
+
|
||
+err_out:
|
||
+ xhci_free_command(xhci, cmd);
|
||
+ ep->queued_deq_seg = NULL;
|
||
+ ep->queued_deq_ptr = NULL;
|
||
+
|
||
+ return ret;
|
||
}
|
||
|
||
/* flip_cycle means flip the cycle bit of all but the first and last TRB.
|
||
@@ -950,7 +932,7 @@ static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xh
|
||
}
|
||
|
||
/* Complete the cancelled URBs we unlinked from td_list. */
|
||
-static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep)
|
||
+static void xhci_giveback_invalidated_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep)
|
||
{
|
||
struct xhci_ring *ring;
|
||
struct xhci_td *td, *tmp_td;
|
||
@@ -958,17 +940,17 @@ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep)
|
||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list,
|
||
cancelled_td_list) {
|
||
|
||
- ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb);
|
||
+ ring = xhci_urb_to_transfer_ring(xhci, td->urb);
|
||
|
||
if (td->cancel_status == TD_CLEARED) {
|
||
- xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
|
||
+ xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n",
|
||
__func__, td->urb);
|
||
- xhci_td_cleanup(ep->xhci, td, ring, td->status);
|
||
+ xhci_td_cleanup(xhci, td, ring, td->status);
|
||
} else {
|
||
- xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||
+ xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||
__func__, td->urb, td->cancel_status);
|
||
}
|
||
- if (ep->xhci->xhc_state & XHCI_STATE_DYING)
|
||
+ if (xhci->xhc_state & XHCI_STATE_DYING)
|
||
return;
|
||
}
|
||
}
|
||
@@ -1002,15 +984,19 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
||
struct xhci_td *td,
|
||
enum xhci_ep_reset_type reset_type)
|
||
{
|
||
+ struct xhci_port *rhub_port = ep->vdev->rhub_port;
|
||
unsigned int slot_id = ep->vdev->slot_id;
|
||
int err;
|
||
|
||
/*
|
||
- * Avoid resetting endpoint if link is inactive. Can cause host hang.
|
||
- * Device will be reset soon to recover the link so don't do anything
|
||
+ * Avoid resetting endpoint if link is inactive or device disonnected.
|
||
+ * Can cause host hang.
|
||
+ * Device will be reset to recover an inactive link, so don't do anything
|
||
*/
|
||
- if (ep->vdev->flags & VDEV_PORT_ERROR)
|
||
+ if (rhub_port->link_inactive || !rhub_port->connected) {
|
||
+ ep->ep_state |= EP_DROP_PENDING;
|
||
return -ENODEV;
|
||
+ }
|
||
|
||
/* add td to cancelled list and let reset ep handler take care of it */
|
||
if (reset_type == EP_HARD_RESET) {
|
||
@@ -1047,15 +1033,13 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
|
||
* only call this when ring is not in a running state
|
||
*/
|
||
|
||
-static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||
+static int xhci_invalidate_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep)
|
||
{
|
||
- struct xhci_hcd *xhci;
|
||
struct xhci_td *td = NULL;
|
||
struct xhci_td *tmp_td = NULL;
|
||
struct xhci_td *cached_td = NULL;
|
||
struct xhci_ring *ring;
|
||
u64 hw_deq;
|
||
- unsigned int slot_id = ep->vdev->slot_id;
|
||
int err;
|
||
|
||
/*
|
||
@@ -1065,8 +1049,6 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||
if (ep->ep_state & SET_DEQ_PENDING)
|
||
return 0;
|
||
|
||
- xhci = ep->xhci;
|
||
-
|
||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
|
||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||
"Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p",
|
||
@@ -1080,6 +1062,13 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||
td->urb, td->urb->stream_id);
|
||
continue;
|
||
}
|
||
+
|
||
+ /* device disconnected or link error, ep will be dropped */
|
||
+ if (ep->ep_state & EP_DROP_PENDING) {
|
||
+ td->cancel_status = TD_CLEARED;
|
||
+ continue;
|
||
+ }
|
||
+
|
||
/*
|
||
* If a ring stopped on the TD we need to cancel then we have to
|
||
* move the xHC endpoint ring dequeue pointer past this TD.
|
||
@@ -1131,9 +1120,8 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||
if (!cached_td)
|
||
return 0;
|
||
|
||
- err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index,
|
||
- cached_td->urb->stream_id,
|
||
- cached_td);
|
||
+ err = xhci_move_deq_to_next_td(xhci, ep, cached_td->urb->stream_id);
|
||
+
|
||
if (err) {
|
||
/* Failed to move past cached td, just set cached TDs to no-op */
|
||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
|
||
@@ -1161,23 +1149,23 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
|
||
*
|
||
* Call under xhci->lock on a stopped endpoint.
|
||
*/
|
||
-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep)
|
||
+void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep)
|
||
{
|
||
- xhci_invalidate_cancelled_tds(ep);
|
||
- xhci_giveback_invalidated_tds(ep);
|
||
+ xhci_invalidate_cancelled_tds(xhci, ep);
|
||
+ xhci_giveback_invalidated_tds(xhci, ep);
|
||
}
|
||
|
||
/*
|
||
* Returns the TD the endpoint ring halted on.
|
||
* Only call for non-running rings without streams.
|
||
*/
|
||
-static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
|
||
+static struct xhci_td *find_halted_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep)
|
||
{
|
||
struct xhci_td *td;
|
||
u64 hw_deq;
|
||
|
||
if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */
|
||
- hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
|
||
+ hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index, 0);
|
||
hw_deq &= TR_DEQ_PTR_MASK;
|
||
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
|
||
if (trb_in_td(td, hw_deq))
|
||
@@ -1252,7 +1240,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
||
reset_type = EP_SOFT_RESET;
|
||
} else {
|
||
reset_type = EP_HARD_RESET;
|
||
- td = find_halted_td(ep);
|
||
+ td = find_halted_td(xhci, ep);
|
||
if (td)
|
||
td->status = -EPROTO;
|
||
}
|
||
@@ -1310,13 +1298,17 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
||
}
|
||
}
|
||
|
||
+ /* link is inactive or disconnected, ep is not running and shouldn't be restarted */
|
||
+ if (ep->vdev->rhub_port->link_inactive || !ep->vdev->rhub_port->connected)
|
||
+ ep->ep_state |= EP_DROP_PENDING;
|
||
+
|
||
/* will queue a set TR deq if stopped on a cancelled, uncleared TD */
|
||
- xhci_invalidate_cancelled_tds(ep);
|
||
+ xhci_invalidate_cancelled_tds(xhci, ep);
|
||
ep->ep_state &= ~EP_STOP_CMD_PENDING;
|
||
|
||
/* Otherwise ring the doorbell(s) to restart queued transfers */
|
||
- xhci_giveback_invalidated_tds(ep);
|
||
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
+ xhci_giveback_invalidated_tds(xhci, ep);
|
||
+ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
}
|
||
|
||
static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
|
||
@@ -1537,14 +1529,14 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
||
/* HW cached TDs cleared from cache, give them back */
|
||
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list,
|
||
cancelled_td_list) {
|
||
- ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb);
|
||
+ ep_ring = xhci_urb_to_transfer_ring(xhci, td->urb);
|
||
if (td->cancel_status == TD_CLEARING_CACHE) {
|
||
td->cancel_status = TD_CLEARED;
|
||
- xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
|
||
+ xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n",
|
||
__func__, td->urb);
|
||
- xhci_td_cleanup(ep->xhci, td, ep_ring, td->status);
|
||
+ xhci_td_cleanup(xhci, td, ep_ring, td->status);
|
||
} else {
|
||
- xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||
+ xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
|
||
__func__, td->urb, td->cancel_status);
|
||
}
|
||
}
|
||
@@ -1555,17 +1547,17 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
|
||
|
||
/* Check for deferred or newly cancelled TDs */
|
||
if (!list_empty(&ep->cancelled_td_list)) {
|
||
- xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n",
|
||
+ xhci_dbg(xhci, "%s: Pending TDs to clear, continuing with invalidation\n",
|
||
__func__);
|
||
- xhci_invalidate_cancelled_tds(ep);
|
||
+ xhci_invalidate_cancelled_tds(xhci, ep);
|
||
/* Try to restart the endpoint if all is done */
|
||
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
+ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
/* Start giving back any TDs invalidated above */
|
||
- xhci_giveback_invalidated_tds(ep);
|
||
+ xhci_giveback_invalidated_tds(xhci, ep);
|
||
} else {
|
||
/* Restart any rings with pending URBs */
|
||
- xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__);
|
||
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
+ xhci_dbg(xhci, "%s: All TDs cleared, ring doorbell\n", __func__);
|
||
+ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
}
|
||
}
|
||
|
||
@@ -1591,16 +1583,16 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
||
"Ignoring reset ep completion code of %u", cmd_comp_code);
|
||
|
||
/* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */
|
||
- xhci_invalidate_cancelled_tds(ep);
|
||
+ xhci_invalidate_cancelled_tds(xhci, ep);
|
||
|
||
/* Clear our internal halted state */
|
||
ep->ep_state &= ~EP_HALTED;
|
||
|
||
- xhci_giveback_invalidated_tds(ep);
|
||
+ xhci_giveback_invalidated_tds(xhci, ep);
|
||
|
||
/* if this was a soft reset, then restart */
|
||
if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP)
|
||
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
+ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||
}
|
||
|
||
static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command,
|
||
@@ -2008,13 +2000,15 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
|
||
static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
|
||
{
|
||
struct xhci_virt_device *vdev = NULL;
|
||
- struct usb_hcd *hcd;
|
||
- u32 port_id;
|
||
- u32 portsc, cmd_reg;
|
||
- unsigned int hcd_portnum;
|
||
struct xhci_bus_state *bus_state;
|
||
- bool bogus_port_status = false;
|
||
struct xhci_port *port;
|
||
+ struct usb_hcd *hcd;
|
||
+ bool bogus_port_status = false;
|
||
+ unsigned int hcd_portnum;
|
||
+ u32 cmd_reg;
|
||
+ u32 port_id;
|
||
+ u32 portsc;
|
||
+ u32 pls;
|
||
|
||
/* Port status change events always have a successful completion code */
|
||
if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS)
|
||
@@ -2051,6 +2045,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
|
||
bus_state = &port->rhub->bus_state;
|
||
hcd_portnum = port->hcd_portnum;
|
||
portsc = xhci_portsc_readl(port);
|
||
+ pls = portsc & PORT_PLS_MASK;
|
||
|
||
xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n",
|
||
hcd->self.busnum, hcd_portnum + 1, port_id, portsc);
|
||
@@ -2062,11 +2057,13 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
|
||
usb_hcd_resume_root_hub(hcd);
|
||
}
|
||
|
||
- if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) {
|
||
- if (!(portsc & PORT_RESET))
|
||
- vdev->flags |= VDEV_PORT_ERROR;
|
||
- } else if (vdev && portsc & PORT_RC) {
|
||
- vdev->flags &= ~VDEV_PORT_ERROR;
|
||
+ /*
|
||
+ * Tag broken links to avoid retries while hub driver sorts it out.
|
||
+ * Link status is not relible while port is in reset.
|
||
+ */
|
||
+ if (!(portsc & PORT_RESET)) {
|
||
+ port->link_inactive = (pls == XDEV_INACTIVE);
|
||
+ port->connected = !!(portsc & PORT_CONNECT);
|
||
}
|
||
|
||
if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
|
||
@@ -3978,88 +3975,102 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
|
||
/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */
|
||
static int xhci_ist_microframes(struct xhci_hcd *xhci)
|
||
{
|
||
- int ist = HCS_IST_VALUE(xhci->hcs_params2);
|
||
+ int ist = FIELD_GET(HCS_IST_VALUE, xhci->hcs_params2);
|
||
|
||
if (xhci->hcs_params2 & HCS_IST_UNIT)
|
||
ist *= 8;
|
||
return ist;
|
||
}
|
||
|
||
-/*
|
||
- * Calculates Frame ID field of the isochronous TRB identifies the
|
||
- * target frame that the Interval associated with this Isochronous
|
||
- * Transfer Descriptor will start on. Refer to 4.11.2.5 in 1.1 spec.
|
||
- *
|
||
- * Returns actual frame id on success, negative value on error.
|
||
- */
|
||
-static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
|
||
- struct urb *urb, int index)
|
||
+
|
||
+static bool xhci_isoc_td_uses_frame_id(struct xhci_hcd *xhci, struct urb *urb,
|
||
+ struct xhci_virt_ep *ep, int i)
|
||
{
|
||
- int start_frame, ist, ret = 0;
|
||
- int start_frame_id, end_frame_id, current_frame_id;
|
||
+ if (urb->transfer_flags & URB_ISO_ASAP)
|
||
+ return false;
|
||
|
||
- if (urb->dev->speed == USB_SPEED_LOW ||
|
||
- urb->dev->speed == USB_SPEED_FULL)
|
||
- start_frame = urb->start_frame + index * urb->interval;
|
||
- else
|
||
- start_frame = (urb->start_frame + index * urb->interval) >> 3;
|
||
+ if (xhci->hcc_params & HCC_CFC)
|
||
+ return true;
|
||
|
||
+ /* set frame id for first TD of first URB in stream */
|
||
+ if (ep->next_uframe == -1 && i == 0)
|
||
+ return true;
|
||
+
|
||
+ return false;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Check if frame is in the valid frame window, including start and end.
|
||
+ * If start > end then assume window wrapped around at a limit the frame
|
||
+ * value won't exceed.
|
||
+ */
|
||
+static bool xhci_frame_in_range(u32 frame, u32 start, u32 end)
|
||
+{
|
||
+ /* frame window end wrapped around */
|
||
+ if (start > end)
|
||
+ return frame >= start || frame <= end;
|
||
+
|
||
+ return frame >= start && frame <= end;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Set the urb->start_frame of the URB.
|
||
+ *
|
||
+ * Returns microframe index of first TD
|
||
+ */
|
||
+static int xhci_get_isoc_start_frame(struct xhci_hcd *xhci, struct urb *urb,
|
||
+ struct xhci_virt_ep *ep)
|
||
+{
|
||
+ u32 curr_frame, start_uframe;
|
||
+ u32 urb_start, urb_end;
|
||
+ u32 win_start, win_end;
|
||
+ bool frame_unit;
|
||
+ int uinterval;
|
||
+ u32 mfindex;
|
||
+ int ist;
|
||
+
|
||
+ /* check if urb uses frame units instead of microframes */
|
||
+ frame_unit = (urb->dev->speed == USB_SPEED_FULL ||
|
||
+ urb->dev->speed == USB_SPEED_LOW);
|
||
+
|
||
+ uinterval = urb->interval;
|
||
+ if (frame_unit)
|
||
+ uinterval *= 8;
|
||
+
|
||
+ /* get current microframe index and isoc scheduling threshold */
|
||
+ mfindex = readl(&xhci->run_regs->microframe_index);
|
||
ist = xhci_ist_microframes(xhci);
|
||
|
||
- /* Software shall not schedule an Isoch TD with a Frame ID value that
|
||
- * is less than the Start Frame ID or greater than the End Frame ID,
|
||
- * where:
|
||
- *
|
||
- * End Frame ID = (Current MFINDEX register value + 895 ms.) MOD 2048
|
||
- * Start Frame ID = (Current MFINDEX register value + IST + 1) MOD 2048
|
||
- *
|
||
- * Both the End Frame ID and Start Frame ID values are calculated
|
||
- * in microframes. When software determines the valid Frame ID value;
|
||
- * The End Frame ID value should be rounded down to the nearest Frame
|
||
- * boundary, and the Start Frame ID value should be rounded up to the
|
||
- * nearest Frame boundary.
|
||
- */
|
||
- current_frame_id = readl(&xhci->run_regs->microframe_index);
|
||
- start_frame_id = roundup(current_frame_id + ist + 1, 8);
|
||
- end_frame_id = rounddown(current_frame_id + 895 * 8, 8);
|
||
+ /* calculate valid frame window, in frame units, see xhci 4.11.2.5 */
|
||
+ curr_frame = MFINDEX_TO_FRAME(mfindex);
|
||
+ win_start = (curr_frame + DIV_ROUND_UP_POW2(ist, 8) + 1) % MAX_FRAMES;
|
||
+ win_end = (curr_frame + 895) % MAX_FRAMES;
|
||
|
||
- start_frame &= 0x7ff;
|
||
- start_frame_id = (start_frame_id >> 3) & 0x7ff;
|
||
- end_frame_id = (end_frame_id >> 3) & 0x7ff;
|
||
-
|
||
- if (start_frame_id < end_frame_id) {
|
||
- if (start_frame > end_frame_id ||
|
||
- start_frame < start_frame_id)
|
||
- ret = -EINVAL;
|
||
- } else if (start_frame_id > end_frame_id) {
|
||
- if ((start_frame > end_frame_id &&
|
||
- start_frame < start_frame_id))
|
||
- ret = -EINVAL;
|
||
+ /* Is this the first URB starting the whole isoc data flow? */
|
||
+ if (ep->next_uframe < 0) {
|
||
+ /* align first URB to next interval boundary, or at last to full frame */
|
||
+ start_uframe = mfindex + ist + XHCI_CFC_DELAY;
|
||
+ start_uframe = roundup(start_uframe, 8);
|
||
+ start_uframe = roundup(start_uframe, uinterval) % MAX_UFRAMES;
|
||
} else {
|
||
- ret = -EINVAL;
|
||
- }
|
||
+ /* URB is mid stream and expected to handle the next frame */
|
||
+ start_uframe = ep->next_uframe;
|
||
+ urb_start = start_uframe / 8;
|
||
+ urb_end = (start_uframe + urb->number_of_packets * uinterval) / 8;
|
||
+ urb_end %= MAX_FRAMES;
|
||
|
||
- if (index == 0) {
|
||
- if (ret == -EINVAL || start_frame == start_frame_id) {
|
||
- start_frame = start_frame_id + 1;
|
||
- if (urb->dev->speed == USB_SPEED_LOW ||
|
||
- urb->dev->speed == USB_SPEED_FULL)
|
||
- urb->start_frame = start_frame;
|
||
- else
|
||
- urb->start_frame = start_frame << 3;
|
||
- ret = 0;
|
||
- }
|
||
- }
|
||
+ if (!xhci_frame_in_range(urb_start, win_start, win_end))
|
||
+ xhci_dbg(xhci, "Ill-timed isoc URB %p for start frame %d, range %d-%d\n",
|
||
+ urb, urb_start, win_start, win_end);
|
||
|
||
- if (ret) {
|
||
- xhci_warn(xhci, "Frame ID %d (reg %d, index %d) beyond range (%d, %d)\n",
|
||
- start_frame, current_frame_id, index,
|
||
- start_frame_id, end_frame_id);
|
||
- xhci_warn(xhci, "Ignore frame ID field, use SIA bit instead\n");
|
||
- return ret;
|
||
+ if (!xhci_frame_in_range(urb_end, win_start, win_end))
|
||
+ xhci_dbg(xhci, "Ill-timed isoc URB %p for end frame %d, range %d-%d\n",
|
||
+ urb, urb_start, win_start, win_end);
|
||
}
|
||
+ /* set urb->start_frame */
|
||
+ urb->start_frame = frame_unit ? start_uframe / 8 : start_uframe;
|
||
|
||
- return start_frame;
|
||
+ return start_uframe;
|
||
}
|
||
|
||
/* Check if we should generate event interrupt for a TD in an isoc URB */
|
||
@@ -4099,7 +4110,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||
int i, j;
|
||
bool more_trbs_coming;
|
||
struct xhci_virt_ep *xep;
|
||
- int frame_id;
|
||
+ int uinterval = urb->interval;
|
||
+ int start_uframe;
|
||
|
||
xep = &xhci->devs[slot_id]->eps[ep_index];
|
||
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
|
||
@@ -4115,6 +4127,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||
start_cycle = ep_ring->cycle_state;
|
||
|
||
urb_priv = urb->hcpriv;
|
||
+
|
||
+ if (urb->dev->speed == USB_SPEED_FULL || urb->dev->speed == USB_SPEED_LOW)
|
||
+ uinterval = urb->interval * 8;
|
||
+
|
||
+ start_uframe = xhci_get_isoc_start_frame(xhci, urb, xep);
|
||
+
|
||
/* Queue the TRBs for each TD, even if they are zero-length */
|
||
for (i = 0; i < num_tds; i++) {
|
||
unsigned int total_pkt_count, max_pkt;
|
||
@@ -4146,14 +4164,16 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||
goto cleanup;
|
||
}
|
||
td = &urb_priv->td[i];
|
||
- /* use SIA as default, if frame id is used overwrite it */
|
||
- sia_frame_id = TRB_SIA;
|
||
- if (!(urb->transfer_flags & URB_ISO_ASAP) &&
|
||
- (xhci->hcc_params & HCC_CFC)) {
|
||
- frame_id = xhci_get_isoc_frame_id(xhci, urb, i);
|
||
- if (frame_id >= 0)
|
||
- sia_frame_id = TRB_FRAME_ID(frame_id);
|
||
+
|
||
+
|
||
+ /* Choose SIA or frame ID based scheduling for this TD */
|
||
+ if (xhci_isoc_td_uses_frame_id(xhci, urb, xep, i)) {
|
||
+ sia_frame_id = (start_uframe + i * uinterval) / 8;
|
||
+ sia_frame_id = TRB_FRAME_ID(sia_frame_id % MAX_FRAMES);
|
||
+ } else {
|
||
+ sia_frame_id = TRB_SIA;
|
||
}
|
||
+
|
||
/*
|
||
* Set isoc specific data for the first TRB in a TD.
|
||
* Prevent HW from getting the TRBs by keeping the cycle state
|
||
@@ -4232,9 +4252,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||
}
|
||
}
|
||
|
||
- /* store the next frame id */
|
||
- if (xhci->hcc_params & HCC_CFC)
|
||
- xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
|
||
+ xep->next_uframe = (start_uframe + num_tds * uinterval) % MAX_UFRAMES;
|
||
|
||
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
|
||
if (xhci->quirks & XHCI_AMD_PLL_FIX)
|
||
@@ -4281,11 +4299,9 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||
struct xhci_virt_device *xdev;
|
||
struct xhci_ring *ep_ring;
|
||
struct xhci_ep_ctx *ep_ctx;
|
||
- int start_frame;
|
||
+ struct xhci_virt_ep *xep;
|
||
int num_tds, num_trbs, i;
|
||
int ret;
|
||
- struct xhci_virt_ep *xep;
|
||
- int ist;
|
||
|
||
xdev = xhci->devs[slot_id];
|
||
xep = &xhci->devs[slot_id]->eps[ep_index];
|
||
@@ -4311,38 +4327,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||
*/
|
||
check_interval(urb, ep_ctx);
|
||
|
||
- /* Calculate the start frame and put it in urb->start_frame. */
|
||
- if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) {
|
||
- if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
|
||
- urb->start_frame = xep->next_frame_id;
|
||
- goto skip_start_over;
|
||
- }
|
||
- }
|
||
-
|
||
- start_frame = readl(&xhci->run_regs->microframe_index);
|
||
- start_frame &= 0x3fff;
|
||
/*
|
||
- * Round up to the next frame and consider the time before trb really
|
||
- * gets scheduled by hardare.
|
||
+ * Check if this starts the isoc data flow. Relies on hw setting ep ctx
|
||
+ * state after doorbell ring. Consider adding list_empty(td_list) check
|
||
*/
|
||
- ist = xhci_ist_microframes(xhci);
|
||
- start_frame += ist + XHCI_CFC_DELAY;
|
||
- start_frame = roundup(start_frame, 8);
|
||
-
|
||
- /*
|
||
- * Round up to the next ESIT (Endpoint Service Interval Time) if ESIT
|
||
- * is greate than 8 microframes.
|
||
- */
|
||
- if (urb->dev->speed == USB_SPEED_LOW ||
|
||
- urb->dev->speed == USB_SPEED_FULL) {
|
||
- start_frame = roundup(start_frame, urb->interval << 3);
|
||
- urb->start_frame = start_frame >> 3;
|
||
- } else {
|
||
- start_frame = roundup(start_frame, urb->interval);
|
||
- urb->start_frame = start_frame;
|
||
- }
|
||
-
|
||
-skip_start_over:
|
||
+ if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING)
|
||
+ xep->next_uframe = -1;
|
||
|
||
return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
|
||
}
|
||
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
|
||
--- a/drivers/usb/host/xhci-tegra.c
|
||
+++ b/drivers/usb/host/xhci-tegra.c
|
||
@@ -6,6 +6,7 @@
|
||
* Copyright (C) 2014 Google, Inc.
|
||
*/
|
||
|
||
+#include <linux/bitfield.h>
|
||
#include <linux/clk.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/dma-mapping.h>
|
||
@@ -993,7 +994,7 @@ static int tegra_xusb_wait_for_falcon(struct tegra_xusb *tegra)
|
||
u32 value;
|
||
|
||
cap_regs = tegra->regs;
|
||
- op_regs = tegra->regs + HC_LENGTH(readl(&cap_regs->hc_capbase));
|
||
+ op_regs = tegra->regs + FIELD_GET(HC_LENGTH, readl(&cap_regs->hc_capbase)),
|
||
|
||
ret = readl_poll_timeout(&op_regs->status, value, !(value & STS_CNR), 1000, 200000);
|
||
|
||
@@ -1895,7 +1896,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||
goto remove_usb2;
|
||
}
|
||
|
||
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
||
+ if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4)
|
||
xhci->shared_hcd->can_do_streams = 1;
|
||
|
||
err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED);
|
||
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
|
||
--- a/drivers/usb/host/xhci.c
|
||
+++ b/drivers/usb/host/xhci.c
|
||
@@ -21,6 +21,7 @@
|
||
#include <linux/dmi.h>
|
||
#include <linux/dma-mapping.h>
|
||
#include <linux/usb/xhci-sideband.h>
|
||
+#include <linux/bitfield.h>
|
||
|
||
#include "xhci.h"
|
||
#include "xhci-trace.h"
|
||
@@ -1664,7 +1665,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
|
||
goto free_priv;
|
||
}
|
||
|
||
- if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) {
|
||
+ if (xhci->devs[slot_id]->rhub_port->link_inactive) {
|
||
xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n");
|
||
ret = -ENODEV;
|
||
goto free_priv;
|
||
@@ -1852,11 +1853,11 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||
}
|
||
|
||
/* In this case no commands are pending but the endpoint is stopped */
|
||
- if (ep->ep_state & EP_CLEARING_TT) {
|
||
+ if (ep->ep_state & (EP_CLEARING_TT | EP_DROP_PENDING)) {
|
||
/* and cancelled TDs can be given back right away */
|
||
xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n",
|
||
urb->dev->slot_id, ep_index, ep->ep_state);
|
||
- xhci_process_cancelled_tds(ep);
|
||
+ xhci_process_cancelled_tds(xhci, ep);
|
||
} else {
|
||
/* Otherwise, queue a new Stop Endpoint command */
|
||
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
|
||
@@ -3507,7 +3508,7 @@ static void xhci_calculate_streams_entries(struct xhci_hcd *xhci,
|
||
* level page entries), but that's an optional feature for xHCI host
|
||
* controllers. xHCs must support at least 4 stream IDs.
|
||
*/
|
||
- max_streams = HCC_MAX_PSA(xhci->hcc_params);
|
||
+ max_streams = GET_MAX_PSA_SIZE(xhci->hcc_params);
|
||
if (*num_stream_ctxs > max_streams) {
|
||
xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n",
|
||
max_streams);
|
||
@@ -3637,7 +3638,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
|
||
|
||
/* MaxPSASize value 0 (2 streams) means streams are not supported */
|
||
if ((xhci->quirks & XHCI_BROKEN_STREAMS) ||
|
||
- HCC_MAX_PSA(xhci->hcc_params) < 4) {
|
||
+ GET_MAX_PSA_SIZE(xhci->hcc_params) < 4) {
|
||
xhci_dbg(xhci, "xHCI controller does not support streams.\n");
|
||
return -ENOSYS;
|
||
}
|
||
@@ -4035,7 +4036,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
||
xhci_get_slot_state(xhci, virt_dev->out_ctx));
|
||
xhci_dbg(xhci, "Not freeing device rings.\n");
|
||
/* Don't treat this as an error. May change my mind later. */
|
||
- virt_dev->flags = 0;
|
||
ret = 0;
|
||
goto command_cleanup;
|
||
case COMP_SUCCESS:
|
||
@@ -4087,10 +4087,12 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
|
||
}
|
||
/* If necessary, update the number of active TTs on this root port */
|
||
xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
|
||
- virt_dev->flags = 0;
|
||
ret = 0;
|
||
|
||
command_cleanup:
|
||
+ for (i = 0; i < EP_CTX_PER_DEV; i++)
|
||
+ virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING;
|
||
+
|
||
xhci_free_command(xhci, reset_device_cmd);
|
||
return ret;
|
||
}
|
||
@@ -4607,7 +4609,7 @@ static int xhci_calculate_hird_besl(struct xhci_hcd *xhci,
|
||
int besl_device = 0;
|
||
u32 field;
|
||
|
||
- u2del = HCS_U2_LATENCY(xhci->hcs_params3);
|
||
+ u2del = FIELD_GET(HCS_U2_LATENCY, xhci->hcs_params3);
|
||
field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
|
||
|
||
if (field & USB_BESL_SUPPORT) {
|
||
@@ -5459,26 +5461,28 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
|
||
xhci_warn(xhci, "Host controller not accessible, removed?\n");
|
||
return -ENODEV;
|
||
}
|
||
- xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase);
|
||
+ xhci->op_regs = hcd->regs + FIELD_GET(HC_LENGTH, hc_capbase);
|
||
+
|
||
xhci->run_regs = hcd->regs +
|
||
(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
|
||
/* Cache read-only capability registers */
|
||
hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
|
||
xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
|
||
xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
|
||
- xhci->hci_version = HC_VERSION(hc_capbase);
|
||
+ xhci->hci_version = FIELD_GET(HC_VERSION, hc_capbase);
|
||
xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
|
||
if (xhci->hci_version > 0x100)
|
||
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
|
||
|
||
xhci->dma_mask_bits = 64;
|
||
- xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS);
|
||
- xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
|
||
+ xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS);
|
||
+ xhci->max_ports = min(FIELD_GET(HCS_MAX_PORTS, hcs_params1), MAX_HC_PORTS);
|
||
+
|
||
/* xhci-plat or xhci-pci might have set max_interrupters already */
|
||
if (!xhci->max_interrupters)
|
||
- xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS);
|
||
- else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
|
||
- xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
|
||
+ xhci->max_interrupters = min(FIELD_GET(HCS_MAX_INTRS, hcs_params1), MAX_HC_INTRS);
|
||
+ else if (xhci->max_interrupters > FIELD_GET(HCS_MAX_INTRS, hcs_params1))
|
||
+ xhci->max_interrupters = FIELD_GET(HCS_MAX_INTRS, hcs_params1);
|
||
|
||
xhci->quirks |= quirks;
|
||
|
||
@@ -5519,7 +5523,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
|
||
* DMA_BIT_MASK(32)) in this xhci_gen_setup().
|
||
*/
|
||
if (xhci->quirks & XHCI_NO_64BIT_SUPPORT)
|
||
- xhci->hcc_params &= ~BIT(0);
|
||
+ xhci->hcc_params &= ~HCC_64BIT_ADDR;
|
||
|
||
/*
|
||
* Set dma_mask and coherent_dma_mask to 64-bits if xHC supports
|
||
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
|
||
--- a/drivers/usb/host/xhci.h
|
||
+++ b/drivers/usb/host/xhci.h
|
||
@@ -290,6 +290,11 @@ struct xhci_run_regs {
|
||
struct xhci_intr_reg ir_set[1024];
|
||
};
|
||
|
||
+/* Bits [13:3] of the microframe index equals the 1ms frame index */
|
||
+#define MFINDEX_TO_FRAME(p) (((p) >> 3) & 0x7ff)
|
||
+#define MAX_FRAMES 2048
|
||
+#define MAX_UFRAMES (MAX_FRAMES * 8)
|
||
+
|
||
/**
|
||
* struct doorbell_array
|
||
*
|
||
@@ -677,9 +682,9 @@ struct xhci_virt_ep {
|
||
#define EP_SOFT_CLEAR_TOGGLE BIT(7)
|
||
/* usb_hub_clear_tt_buffer is in progress */
|
||
#define EP_CLEARING_TT BIT(8)
|
||
+#define EP_DROP_PENDING BIT(9) /* port disconnect or link error, don't restart */
|
||
/* ---- Related to URB cancellation ---- */
|
||
struct list_head cancelled_td_list;
|
||
- struct xhci_hcd *xhci;
|
||
/* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
|
||
* command. We'll need to update the ring's dequeue segment and dequeue
|
||
* pointer after the command completes.
|
||
@@ -699,7 +704,7 @@ struct xhci_virt_ep {
|
||
struct list_head bw_endpoint_list;
|
||
unsigned long stop_time;
|
||
/* Isoch Frame ID checking storage */
|
||
- int next_frame_id;
|
||
+ int next_uframe;
|
||
/* Use new Isoch TRB layout needed for extended TBC support */
|
||
bool use_extended_tbc;
|
||
/* set if this endpoint is controlled via sideband access*/
|
||
@@ -753,14 +758,6 @@ struct xhci_virt_device {
|
||
struct xhci_port *rhub_port;
|
||
struct xhci_interval_bw_table *bw_table;
|
||
struct xhci_tt_bw_info *tt_info;
|
||
- /*
|
||
- * flags for state tracking based on events and issued commands.
|
||
- * Software can not rely on states from output contexts because of
|
||
- * latency between events and xHC updating output context values.
|
||
- * See xhci 1.1 section 4.8.3 for more details
|
||
- */
|
||
- unsigned long flags;
|
||
-#define VDEV_PORT_ERROR BIT(0) /* Port error, link inactive */
|
||
|
||
/* The current max exit latency for the enabled USB3 link states. */
|
||
u16 current_mel;
|
||
@@ -1480,6 +1477,8 @@ struct xhci_port {
|
||
int hcd_portnum;
|
||
struct xhci_hub *rhub;
|
||
struct xhci_port_cap *port_cap;
|
||
+ unsigned int link_inactive:1;
|
||
+ unsigned int connected:1;
|
||
unsigned int lpm_incapable:1;
|
||
unsigned long resume_timestamp;
|
||
bool rexit_active;
|
||
@@ -1961,7 +1960,7 @@ unsigned int count_trbs(u64 addr, u64 len);
|
||
unsigned int xhci_num_trbs_free(struct xhci_ring *ring);
|
||
int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
|
||
int suspend, gfp_t gfp_flags);
|
||
-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep);
|
||
+void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep);
|
||
void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
|
||
struct xhci_interrupter *ir,
|
||
bool clear_ehb);
|
||
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
|
||
--- a/drivers/usb/misc/cypress_cy7c63.c
|
||
+++ b/drivers/usb/misc/cypress_cy7c63.c
|
||
@@ -89,8 +89,11 @@ static int vendor_command(struct cypress *dev, unsigned char request,
|
||
address, data, iobuf, CYPRESS_MAX_REQSIZE,
|
||
USB_CTRL_GET_TIMEOUT);
|
||
/* we must not process garbage */
|
||
- if (retval < 2)
|
||
+ if (retval < 2) {
|
||
+ if (retval >= 0)
|
||
+ retval = -EIO;
|
||
goto err_buf;
|
||
+ }
|
||
|
||
/* store returned data (more READs to be added) */
|
||
switch (request) {
|
||
@@ -175,8 +178,9 @@ static ssize_t read_port(struct device *dev, struct device_attribute *attr,
|
||
dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num);
|
||
|
||
result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0);
|
||
-
|
||
dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result);
|
||
+ if (result < 0)
|
||
+ return result;
|
||
|
||
return sprintf(buf, "%d", cyp->port[port_num]);
|
||
}
|
||
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
|
||
--- a/drivers/usb/misc/uss720.c
|
||
+++ b/drivers/usb/misc/uss720.c
|
||
@@ -223,6 +223,7 @@ static int get_1284_register(struct parport *pp, unsigned char reg, unsigned cha
|
||
}
|
||
printk(KERN_WARNING "get_1284_register timeout\n");
|
||
kill_all_async_requests_priv(priv);
|
||
+ kref_put(&rq->ref_count, destroy_async);
|
||
return -EIO;
|
||
}
|
||
|
||
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
|
||
--- a/drivers/usb/mtu3/mtu3_host.c
|
||
+++ b/drivers/usb/mtu3/mtu3_host.c
|
||
@@ -46,6 +46,14 @@
|
||
#define WC1_IS_P_95 BIT(12)
|
||
#define WC1_IS_EN_P0_95 BIT(6)
|
||
|
||
+/* mt8196 */
|
||
+#define PERI_WK_CTRL0_8196 0x08
|
||
+#define WC0_IS_EN_P0_96 BIT(0)
|
||
+#define WC0_IS_EN_P1_96 BIT(7)
|
||
+
|
||
+#define PERI_WK_CTRL1_8196 0x10
|
||
+#define WC1_IS_EN_P2_96 BIT(0)
|
||
+
|
||
/* mt2712 etc */
|
||
#define PERI_SSUSB_SPM_CTRL 0x0
|
||
#define SSC_IP_SLEEP_EN BIT(4)
|
||
@@ -59,6 +67,9 @@ enum ssusb_uwk_vers {
|
||
SSUSB_UWK_V1_3, /* mt8195 IP0 */
|
||
SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */
|
||
SSUSB_UWK_V1_6, /* mt8195 IP3 */
|
||
+ SSUSB_UWK_V1_7, /* mt8196 IP0 */
|
||
+ SSUSB_UWK_V1_8, /* mt8196 IP1 */
|
||
+ SSUSB_UWK_V1_9, /* mt8196 IP2 */
|
||
};
|
||
|
||
/*
|
||
@@ -100,6 +111,21 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable)
|
||
msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95;
|
||
val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0;
|
||
break;
|
||
+ case SSUSB_UWK_V1_7:
|
||
+ reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196;
|
||
+ msk = WC0_IS_EN_P0_96;
|
||
+ val = enable ? msk : 0;
|
||
+ break;
|
||
+ case SSUSB_UWK_V1_8:
|
||
+ reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196;
|
||
+ msk = WC0_IS_EN_P1_96;
|
||
+ val = enable ? msk : 0;
|
||
+ break;
|
||
+ case SSUSB_UWK_V1_9:
|
||
+ reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8196;
|
||
+ msk = WC1_IS_EN_P2_96;
|
||
+ val = enable ? msk : 0;
|
||
+ break;
|
||
case SSUSB_UWK_V2:
|
||
reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
|
||
msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
|
||
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
|
||
--- a/drivers/usb/mtu3/mtu3_plat.c
|
||
+++ b/drivers/usb/mtu3/mtu3_plat.c
|
||
@@ -544,7 +544,8 @@ static int mtu3_suspend_common(struct device *dev, pm_message_t msg)
|
||
|
||
ssusb_phy_power_off(ssusb);
|
||
clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks);
|
||
- ssusb_wakeup_set(ssusb, true);
|
||
+ if (device_may_wakeup(dev) && (ssusb->is_host || PMSG_IS_AUTO(msg)))
|
||
+ ssusb_wakeup_set(ssusb, true);
|
||
return 0;
|
||
|
||
sleep_err:
|
||
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
|
||
--- a/drivers/usb/musb/Kconfig
|
||
+++ b/drivers/usb/musb/Kconfig
|
||
@@ -116,7 +116,6 @@ config USB_MUSB_POLARFIRE_SOC
|
||
tristate "Microchip PolarFire SoC platforms"
|
||
depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST
|
||
depends on NOP_USB_XCEIV
|
||
- select USB_MUSB_DUAL_ROLE
|
||
help
|
||
Say Y here to enable support for USB on Microchip's PolarFire SoC.
|
||
|
||
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
|
||
--- a/drivers/usb/musb/musb_host.c
|
||
+++ b/drivers/usb/musb/musb_host.c
|
||
@@ -1774,7 +1774,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||
status = -EPIPE;
|
||
|
||
} else if (rx_csr & MUSB_RXCSR_H_ERROR) {
|
||
- dev_err(musb->controller, "ep%d RX three-strikes error", epnum);
|
||
+ dev_err_ratelimited(musb->controller,
|
||
+ "ep%d RX three-strikes error\n", epnum);
|
||
|
||
/*
|
||
* The three-strikes error could only happen when the USB
|
||
@@ -1788,6 +1789,17 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||
rx_csr &= ~MUSB_RXCSR_H_ERROR;
|
||
musb_writew(epio, MUSB_RXCSR, rx_csr);
|
||
|
||
+ /*
|
||
+ * Unplugging a USB-Ethernet adapter while it is busy can make
|
||
+ * the controller keep re-asserting the three-strikes error for
|
||
+ * this endpoint before the disconnect is processed. That floods
|
||
+ * the log and can wedge the host port until reboot. Drop the
|
||
+ * stale pending RX interrupt on platforms that support it (e.g.
|
||
+ * AM335x/DSPS) to break the storm; the transfer is still
|
||
+ * aborted below via the fault path.
|
||
+ */
|
||
+ musb_platform_clear_ep_rxintr(musb, epnum);
|
||
+
|
||
} else if (rx_csr & MUSB_RXCSR_DATAERROR) {
|
||
|
||
if (USB_ENDPOINT_XFER_ISOC != qh->type) {
|
||
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
|
||
--- a/drivers/usb/serial/digi_acceleport.c
|
||
+++ b/drivers/usb/serial/digi_acceleport.c
|
||
@@ -32,10 +32,6 @@
|
||
/* so we can be sure to send the full buffer in one urb */
|
||
#define DIGI_OUT_BUF_SIZE 8
|
||
|
||
-/* port input buffer length -- must be >= transfer buffer length - 3 */
|
||
-/* so we can be sure to hold at least one full buffer from one urb */
|
||
-#define DIGI_IN_BUF_SIZE 64
|
||
-
|
||
/* retry timeout while sleeping */
|
||
#define DIGI_RETRY_TIMEOUT (HZ/10)
|
||
|
||
@@ -176,10 +172,10 @@
|
||
/* Structures */
|
||
|
||
struct digi_serial {
|
||
- spinlock_t ds_serial_lock;
|
||
+ struct mutex open_mutex;
|
||
struct usb_serial_port *ds_oob_port; /* out-of-band port */
|
||
int ds_oob_port_num; /* index of out-of-band port */
|
||
- int ds_device_started;
|
||
+ int open_count;
|
||
};
|
||
|
||
struct digi_port {
|
||
@@ -194,7 +190,6 @@ struct digi_port {
|
||
int dp_throttled;
|
||
int dp_throttle_restart;
|
||
wait_queue_head_t dp_flush_wait;
|
||
- wait_queue_head_t dp_close_wait; /* wait queue for close */
|
||
wait_queue_head_t write_wait;
|
||
struct usb_serial_port *dp_port;
|
||
};
|
||
@@ -227,9 +222,7 @@ static unsigned int digi_chars_in_buffer(struct tty_struct *tty);
|
||
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port);
|
||
static void digi_close(struct usb_serial_port *port);
|
||
static void digi_dtr_rts(struct usb_serial_port *port, int on);
|
||
-static int digi_startup_device(struct usb_serial *serial);
|
||
static int digi_startup(struct usb_serial *serial);
|
||
-static void digi_disconnect(struct usb_serial *serial);
|
||
static void digi_release(struct usb_serial *serial);
|
||
static int digi_port_probe(struct usb_serial_port *port);
|
||
static void digi_port_remove(struct usb_serial_port *port);
|
||
@@ -272,7 +265,7 @@ static struct usb_serial_driver digi_acceleport_2_device = {
|
||
.dtr_rts = digi_dtr_rts,
|
||
.write = digi_write,
|
||
.write_room = digi_write_room,
|
||
- .write_bulk_callback = digi_write_bulk_callback,
|
||
+ .write_bulk_callback = digi_write_bulk_callback,
|
||
.read_bulk_callback = digi_read_bulk_callback,
|
||
.chars_in_buffer = digi_chars_in_buffer,
|
||
.throttle = digi_rx_throttle,
|
||
@@ -282,7 +275,6 @@ static struct usb_serial_driver digi_acceleport_2_device = {
|
||
.tiocmget = digi_tiocmget,
|
||
.tiocmset = digi_tiocmset,
|
||
.attach = digi_startup,
|
||
- .disconnect = digi_disconnect,
|
||
.release = digi_release,
|
||
.port_probe = digi_port_probe,
|
||
.port_remove = digi_port_remove,
|
||
@@ -301,7 +293,7 @@ static struct usb_serial_driver digi_acceleport_4_device = {
|
||
.close = digi_close,
|
||
.write = digi_write,
|
||
.write_room = digi_write_room,
|
||
- .write_bulk_callback = digi_write_bulk_callback,
|
||
+ .write_bulk_callback = digi_write_bulk_callback,
|
||
.read_bulk_callback = digi_read_bulk_callback,
|
||
.chars_in_buffer = digi_chars_in_buffer,
|
||
.throttle = digi_rx_throttle,
|
||
@@ -311,7 +303,6 @@ static struct usb_serial_driver digi_acceleport_4_device = {
|
||
.tiocmget = digi_tiocmget,
|
||
.tiocmset = digi_tiocmset,
|
||
.attach = digi_startup,
|
||
- .disconnect = digi_disconnect,
|
||
.release = digi_release,
|
||
.port_probe = digi_port_probe,
|
||
.port_remove = digi_port_remove,
|
||
@@ -335,7 +326,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
|
||
* interruptible_sleep_on_timeout is deprecated and has been replaced
|
||
* with the equivalent code.
|
||
*/
|
||
-
|
||
static long cond_wait_interruptible_timeout_irqrestore(
|
||
wait_queue_head_t *q, long timeout,
|
||
spinlock_t *lock, unsigned long flags)
|
||
@@ -351,6 +341,13 @@ __releases(lock)
|
||
return timeout;
|
||
}
|
||
|
||
+static struct usb_serial_port *digi_get_oob_port(struct usb_serial *serial)
|
||
+{
|
||
+ struct digi_serial *serial_priv = usb_get_serial_data(serial);
|
||
+
|
||
+ return serial_priv->ds_oob_port;
|
||
+}
|
||
+
|
||
/*
|
||
* Digi Write OOB Command
|
||
*
|
||
@@ -361,15 +358,14 @@ __releases(lock)
|
||
* the interruptible flag is true, or a negative error
|
||
* returned by usb_submit_urb.
|
||
*/
|
||
-
|
||
static int digi_write_oob_command(struct usb_serial_port *port,
|
||
unsigned char *buf, int count, int interruptible)
|
||
{
|
||
- int ret = 0;
|
||
- int len;
|
||
- struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
|
||
+ struct usb_serial_port *oob_port = digi_get_oob_port(port->serial);
|
||
struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
|
||
unsigned long flags;
|
||
+ int ret = 0;
|
||
+ int len;
|
||
|
||
dev_dbg(&port->dev,
|
||
"digi_write_oob_command: TOP: port=%d, count=%d\n",
|
||
@@ -406,10 +402,8 @@ static int digi_write_oob_command(struct usb_serial_port *port,
|
||
dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n",
|
||
__func__, ret);
|
||
return ret;
|
||
-
|
||
}
|
||
|
||
-
|
||
/*
|
||
* Digi Write In Band Command
|
||
*
|
||
@@ -421,16 +415,15 @@ static int digi_write_oob_command(struct usb_serial_port *port,
|
||
* timeout ticks. Returns 0 if successful, or a negative
|
||
* error returned by digi_write.
|
||
*/
|
||
-
|
||
static int digi_write_inb_command(struct usb_serial_port *port,
|
||
unsigned char *buf, int count, unsigned long timeout)
|
||
{
|
||
- int ret = 0;
|
||
- int len;
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
unsigned char *data = port->write_urb->transfer_buffer;
|
||
unsigned long expire;
|
||
unsigned long flags;
|
||
+ int ret = 0;
|
||
+ int len;
|
||
|
||
dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n",
|
||
priv->dp_port_num, count);
|
||
@@ -459,7 +452,7 @@ static int digi_write_inb_command(struct usb_serial_port *port,
|
||
/* len must be a multiple of 4 and small enough to */
|
||
/* guarantee the write will send buffered data first, */
|
||
/* so commands are in order with data and not split */
|
||
- len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
|
||
+ len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len);
|
||
if (len > 4)
|
||
len &= ~3;
|
||
|
||
@@ -467,11 +460,10 @@ static int digi_write_inb_command(struct usb_serial_port *port,
|
||
if (priv->dp_out_buf_len > 0) {
|
||
data[0] = DIGI_CMD_SEND_DATA;
|
||
data[1] = priv->dp_out_buf_len;
|
||
- memcpy(data + 2, priv->dp_out_buf,
|
||
- priv->dp_out_buf_len);
|
||
+ memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len);
|
||
memcpy(data + 2 + priv->dp_out_buf_len, buf, len);
|
||
- port->write_urb->transfer_buffer_length
|
||
- = priv->dp_out_buf_len + 2 + len;
|
||
+ port->write_urb->transfer_buffer_length =
|
||
+ priv->dp_out_buf_len + 2 + len;
|
||
} else {
|
||
memcpy(data, buf, len);
|
||
port->write_urb->transfer_buffer_length = len;
|
||
@@ -484,7 +476,6 @@ static int digi_write_inb_command(struct usb_serial_port *port,
|
||
count -= len;
|
||
buf += len;
|
||
}
|
||
-
|
||
}
|
||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||
|
||
@@ -495,7 +486,6 @@ static int digi_write_inb_command(struct usb_serial_port *port,
|
||
return ret;
|
||
}
|
||
|
||
-
|
||
/*
|
||
* Digi Set Modem Signals
|
||
*
|
||
@@ -505,17 +495,15 @@ static int digi_write_inb_command(struct usb_serial_port *port,
|
||
* -EINTR if interrupted while sleeping, or a non-zero error
|
||
* returned by usb_submit_urb.
|
||
*/
|
||
-
|
||
static int digi_set_modem_signals(struct usb_serial_port *port,
|
||
unsigned int modem_signals, int interruptible)
|
||
{
|
||
-
|
||
- int ret;
|
||
struct digi_port *port_priv = usb_get_serial_port_data(port);
|
||
- struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
|
||
+ struct usb_serial_port *oob_port = digi_get_oob_port(port->serial);
|
||
struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
|
||
unsigned char *data = oob_port->write_urb->transfer_buffer;
|
||
unsigned long flags;
|
||
+ int ret;
|
||
|
||
dev_dbg(&port->dev,
|
||
"digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n",
|
||
@@ -573,14 +561,13 @@ static int digi_set_modem_signals(struct usb_serial_port *port,
|
||
* is only called from close, and only one process can be in close on a
|
||
* port at a time, so its ok.
|
||
*/
|
||
-
|
||
static int digi_transmit_idle(struct usb_serial_port *port,
|
||
unsigned long timeout)
|
||
{
|
||
- int ret;
|
||
- unsigned char buf[2];
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
+ unsigned char buf[2];
|
||
unsigned long flags;
|
||
+ int ret;
|
||
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
priv->dp_transmit_idle = 0;
|
||
@@ -607,16 +594,15 @@ static int digi_transmit_idle(struct usb_serial_port *port,
|
||
}
|
||
priv->dp_transmit_idle = 0;
|
||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||
+
|
||
return 0;
|
||
-
|
||
}
|
||
|
||
-
|
||
static void digi_rx_throttle(struct tty_struct *tty)
|
||
{
|
||
- unsigned long flags;
|
||
struct usb_serial_port *port = tty->driver_data;
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
+ unsigned long flags;
|
||
|
||
/* stop receiving characters by not resubmitting the read urb */
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
@@ -625,13 +611,12 @@ static void digi_rx_throttle(struct tty_struct *tty)
|
||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||
}
|
||
|
||
-
|
||
static void digi_rx_unthrottle(struct tty_struct *tty)
|
||
{
|
||
- int ret = 0;
|
||
- unsigned long flags;
|
||
struct usb_serial_port *port = tty->driver_data;
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
+ unsigned long flags;
|
||
+ int ret = 0;
|
||
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
|
||
@@ -651,7 +636,6 @@ static void digi_rx_unthrottle(struct tty_struct *tty)
|
||
__func__, ret, priv->dp_port_num);
|
||
}
|
||
|
||
-
|
||
static void digi_set_termios(struct tty_struct *tty,
|
||
struct usb_serial_port *port,
|
||
const struct ktermios *old_termios)
|
||
@@ -762,7 +746,6 @@ static void digi_set_termios(struct tty_struct *tty,
|
||
|
||
/* set stop bits */
|
||
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
|
||
-
|
||
if ((cflag & CSTOPB))
|
||
arg = DIGI_STOP_BITS_2;
|
||
else
|
||
@@ -772,7 +755,6 @@ static void digi_set_termios(struct tty_struct *tty,
|
||
buf[i++] = priv->dp_port_num;
|
||
buf[i++] = arg;
|
||
buf[i++] = 0;
|
||
-
|
||
}
|
||
|
||
/* set input flow control */
|
||
@@ -841,7 +823,6 @@ static void digi_set_termios(struct tty_struct *tty,
|
||
tty_encode_baud_rate(tty, baud, baud);
|
||
}
|
||
|
||
-
|
||
static int digi_break_ctl(struct tty_struct *tty, int break_state)
|
||
{
|
||
struct usb_serial_port *port = tty->driver_data;
|
||
@@ -855,50 +836,48 @@ static int digi_break_ctl(struct tty_struct *tty, int break_state)
|
||
return digi_write_inb_command(port, buf, 4, 0);
|
||
}
|
||
|
||
-
|
||
static int digi_tiocmget(struct tty_struct *tty)
|
||
{
|
||
struct usb_serial_port *port = tty->driver_data;
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
- unsigned int val;
|
||
unsigned long flags;
|
||
+ unsigned int val;
|
||
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
val = priv->dp_modem_signals;
|
||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||
+
|
||
return val;
|
||
}
|
||
|
||
-
|
||
static int digi_tiocmset(struct tty_struct *tty,
|
||
unsigned int set, unsigned int clear)
|
||
{
|
||
struct usb_serial_port *port = tty->driver_data;
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
- unsigned int val;
|
||
unsigned long flags;
|
||
+ unsigned int val;
|
||
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
val = (priv->dp_modem_signals & ~clear) | set;
|
||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||
+
|
||
return digi_set_modem_signals(port, val, 1);
|
||
}
|
||
|
||
-
|
||
static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||
const unsigned char *buf, int count)
|
||
{
|
||
-
|
||
- int ret, data_len, new_len;
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
unsigned char *data = port->write_urb->transfer_buffer;
|
||
+ int ret, data_len, new_len;
|
||
unsigned long flags;
|
||
|
||
dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n",
|
||
priv->dp_port_num, count);
|
||
|
||
/* copy user data (which can sleep) before getting spin lock */
|
||
- count = min(count, port->bulk_out_size-2);
|
||
+ count = min(count, port->bulk_out_size - 2);
|
||
count = min(64, count);
|
||
|
||
/* be sure only one write proceeds at a time */
|
||
@@ -920,7 +899,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||
|
||
/* allow space for any buffered data and for new data, up to */
|
||
/* transfer buffer size - 2 (for command and length bytes) */
|
||
- new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
|
||
+ new_len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len);
|
||
data_len = new_len + priv->dp_out_buf_len;
|
||
|
||
if (data_len == 0) {
|
||
@@ -928,7 +907,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||
return 0;
|
||
}
|
||
|
||
- port->write_urb->transfer_buffer_length = data_len+2;
|
||
+ port->write_urb->transfer_buffer_length = data_len + 2;
|
||
|
||
*data++ = DIGI_CMD_SEND_DATA;
|
||
*data++ = data_len;
|
||
@@ -954,39 +933,22 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
||
__func__, ret, priv->dp_port_num);
|
||
dev_dbg(&port->dev, "digi_write: returning %d\n", ret);
|
||
- return ret;
|
||
|
||
+ return ret;
|
||
}
|
||
|
||
static void digi_write_bulk_callback(struct urb *urb)
|
||
{
|
||
-
|
||
struct usb_serial_port *port = urb->context;
|
||
- struct usb_serial *serial;
|
||
- struct digi_port *priv;
|
||
- struct digi_serial *serial_priv;
|
||
+ struct digi_serial *serial_priv = usb_get_serial_data(port->serial);
|
||
+ struct digi_port *priv = usb_get_serial_port_data(port);
|
||
+ unsigned char *data = urb->transfer_buffer;
|
||
unsigned long flags;
|
||
- int ret = 0;
|
||
- int status = urb->status;
|
||
bool wakeup;
|
||
-
|
||
- /* port and serial sanity check */
|
||
- if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
|
||
- pr_err("%s: port or port->private is NULL, status=%d\n",
|
||
- __func__, status);
|
||
- return;
|
||
- }
|
||
- serial = port->serial;
|
||
- if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) {
|
||
- dev_err(&port->dev,
|
||
- "%s: serial or serial->private is NULL, status=%d\n",
|
||
- __func__, status);
|
||
- return;
|
||
- }
|
||
+ int ret = 0;
|
||
|
||
/* handle oob callback */
|
||
if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
|
||
- dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n");
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
priv->dp_write_urb_in_use = 0;
|
||
wake_up_interruptible(&priv->write_wait);
|
||
@@ -999,15 +961,13 @@ static void digi_write_bulk_callback(struct urb *urb)
|
||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||
priv->dp_write_urb_in_use = 0;
|
||
if (priv->dp_out_buf_len > 0) {
|
||
- *((unsigned char *)(port->write_urb->transfer_buffer))
|
||
- = (unsigned char)DIGI_CMD_SEND_DATA;
|
||
- *((unsigned char *)(port->write_urb->transfer_buffer) + 1)
|
||
- = (unsigned char)priv->dp_out_buf_len;
|
||
- port->write_urb->transfer_buffer_length =
|
||
- priv->dp_out_buf_len + 2;
|
||
- memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf,
|
||
- priv->dp_out_buf_len);
|
||
- ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||
+ data[0] = DIGI_CMD_SEND_DATA;
|
||
+ data[1] = priv->dp_out_buf_len;
|
||
+ memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len);
|
||
+
|
||
+ urb->transfer_buffer_length = priv->dp_out_buf_len + 2;
|
||
+
|
||
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||
if (ret == 0) {
|
||
priv->dp_write_urb_in_use = 1;
|
||
priv->dp_out_buf_len = 0;
|
||
@@ -1041,8 +1001,8 @@ static unsigned int digi_write_room(struct tty_struct *tty)
|
||
|
||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||
dev_dbg(&port->dev, "digi_write_room: port=%d, room=%u\n", priv->dp_port_num, room);
|
||
- return room;
|
||
|
||
+ return room;
|
||
}
|
||
|
||
static unsigned int digi_chars_in_buffer(struct tty_struct *tty)
|
||
@@ -1070,17 +1030,53 @@ static void digi_dtr_rts(struct usb_serial_port *port, int on)
|
||
digi_set_modem_signals(port, on * (TIOCM_DTR | TIOCM_RTS), 1);
|
||
}
|
||
|
||
+static int digi_open_oob_port(struct usb_serial *serial)
|
||
+{
|
||
+ struct digi_serial *serial_priv = usb_get_serial_data(serial);
|
||
+ struct usb_serial_port *oob_port = serial_priv->ds_oob_port;
|
||
+ int ret = 0;
|
||
+
|
||
+ mutex_lock(&serial_priv->open_mutex);
|
||
+
|
||
+ if (serial_priv->open_count++ == 0) {
|
||
+ ret = usb_submit_urb(oob_port->read_urb, GFP_KERNEL);
|
||
+ if (ret) {
|
||
+ dev_err(&serial->interface->dev, "failed to submit OOB read urb: %d\n",
|
||
+ ret);
|
||
+ serial_priv->open_count--;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ mutex_unlock(&serial_priv->open_mutex);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static void digi_close_oob_port(struct usb_serial *serial)
|
||
+{
|
||
+ struct digi_serial *serial_priv = usb_get_serial_data(serial);
|
||
+ struct usb_serial_port *oob_port = serial_priv->ds_oob_port;
|
||
+
|
||
+ mutex_lock(&serial_priv->open_mutex);
|
||
+
|
||
+ if (serial_priv->open_count-- == 1) {
|
||
+ usb_kill_urb(oob_port->read_urb);
|
||
+ usb_kill_urb(oob_port->write_urb);
|
||
+ }
|
||
+
|
||
+ mutex_unlock(&serial_priv->open_mutex);
|
||
+}
|
||
+
|
||
static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
|
||
{
|
||
- int ret;
|
||
- unsigned char buf[32];
|
||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||
struct ktermios not_termios;
|
||
- int throttled;
|
||
+ unsigned char buf[32];
|
||
+ int ret;
|
||
|
||
- /* be sure the device is started up */
|
||
- if (digi_startup_device(port->serial) != 0)
|
||
- return -ENXIO;
|
||
+ ret = digi_open_oob_port(port->serial);
|
||
+ if (ret)
|
||
+ return ret;
|
||
|
||
/* read modem signals automatically whenever they change */
|
||
buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
|
||
@@ -1106,32 +1102,34 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
|
||
}
|
||
|
||
spin_lock_irq(&priv->dp_port_lock);
|
||
- throttled = priv->dp_throttle_restart;
|
||
priv->dp_throttled = 0;
|
||
priv->dp_throttle_restart = 0;
|
||
spin_unlock_irq(&priv->dp_port_lock);
|
||
|
||
- if (throttled) {
|
||
- ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
|
||
- if (ret) {
|
||
- dev_err(&port->dev, "failed to submit read urb: %d\n", ret);
|
||
- return ret;
|
||
- }
|
||
+ ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
|
||
+ if (ret) {
|
||
+ dev_err(&port->dev, "failed to submit read urb: %d\n", ret);
|
||
+ goto err_close_oob;
|
||
}
|
||
|
||
return 0;
|
||
-}
|
||
|
||
+err_close_oob:
|
||
+ digi_close_oob_port(port->serial);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
|
||
static void digi_close(struct usb_serial_port *port)
|
||
{
|
||
+ struct digi_port *priv = usb_get_serial_port_data(port);
|
||
+ unsigned char buf[32];
|
||
DEFINE_WAIT(wait);
|
||
int ret;
|
||
- unsigned char buf[32];
|
||
- struct digi_port *priv = usb_get_serial_port_data(port);
|
||
+
|
||
+ usb_kill_urb(port->read_urb);
|
||
|
||
mutex_lock(&port->serial->disc_mutex);
|
||
- /* if disconnected, just clear flags */
|
||
if (port->serial->disconnected)
|
||
goto exit;
|
||
|
||
@@ -1177,57 +1175,18 @@ static void digi_close(struct usb_serial_port *port)
|
||
TASK_INTERRUPTIBLE);
|
||
schedule_timeout(DIGI_CLOSE_TIMEOUT);
|
||
finish_wait(&priv->dp_flush_wait, &wait);
|
||
+exit:
|
||
+ mutex_unlock(&port->serial->disc_mutex);
|
||
|
||
/* shutdown any outstanding bulk writes */
|
||
usb_kill_urb(port->write_urb);
|
||
-exit:
|
||
- spin_lock_irq(&priv->dp_port_lock);
|
||
- priv->dp_write_urb_in_use = 0;
|
||
- wake_up_interruptible(&priv->dp_close_wait);
|
||
- spin_unlock_irq(&priv->dp_port_lock);
|
||
- mutex_unlock(&port->serial->disc_mutex);
|
||
-}
|
||
|
||
-
|
||
-/*
|
||
- * Digi Startup Device
|
||
- *
|
||
- * Starts reads on all ports. Must be called AFTER startup, with
|
||
- * urbs initialized. Returns 0 if successful, non-zero error otherwise.
|
||
- */
|
||
-
|
||
-static int digi_startup_device(struct usb_serial *serial)
|
||
-{
|
||
- int i, ret = 0;
|
||
- struct digi_serial *serial_priv = usb_get_serial_data(serial);
|
||
- struct usb_serial_port *port;
|
||
-
|
||
- /* be sure this happens exactly once */
|
||
- spin_lock(&serial_priv->ds_serial_lock);
|
||
- if (serial_priv->ds_device_started) {
|
||
- spin_unlock(&serial_priv->ds_serial_lock);
|
||
- return 0;
|
||
- }
|
||
- serial_priv->ds_device_started = 1;
|
||
- spin_unlock(&serial_priv->ds_serial_lock);
|
||
-
|
||
- /* start reading from each bulk in endpoint for the device */
|
||
- /* set USB_DISABLE_SPD flag for write bulk urbs */
|
||
- for (i = 0; i < serial->type->num_ports + 1; i++) {
|
||
- port = serial->port[i];
|
||
- ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
|
||
- if (ret != 0) {
|
||
- dev_err(&port->dev,
|
||
- "%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
||
- __func__, ret, i);
|
||
- break;
|
||
- }
|
||
- }
|
||
- return ret;
|
||
+ digi_close_oob_port(port->serial);
|
||
}
|
||
|
||
static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
|
||
{
|
||
+ struct digi_serial *serial_priv = usb_get_serial_data(port->serial);
|
||
struct digi_port *priv;
|
||
|
||
priv = kzalloc_obj(*priv);
|
||
@@ -1235,10 +1194,13 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
|
||
return -ENOMEM;
|
||
|
||
spin_lock_init(&priv->dp_port_lock);
|
||
+
|
||
+ if (port == serial_priv->ds_oob_port)
|
||
+ lockdep_set_subclass(&priv->dp_port_lock, SINGLE_DEPTH_NESTING);
|
||
+
|
||
priv->dp_port_num = port_num;
|
||
init_waitqueue_head(&priv->dp_transmit_idle_wait);
|
||
init_waitqueue_head(&priv->dp_flush_wait);
|
||
- init_waitqueue_head(&priv->dp_close_wait);
|
||
init_waitqueue_head(&priv->write_wait);
|
||
priv->dp_port = port;
|
||
|
||
@@ -1275,10 +1237,13 @@ static int digi_startup(struct usb_serial *serial)
|
||
if (!serial_priv)
|
||
return -ENOMEM;
|
||
|
||
- spin_lock_init(&serial_priv->ds_serial_lock);
|
||
+ mutex_init(&serial_priv->open_mutex);
|
||
+
|
||
serial_priv->ds_oob_port_num = oob_port_num;
|
||
serial_priv->ds_oob_port = serial->port[oob_port_num];
|
||
|
||
+ usb_set_serial_data(serial, serial_priv);
|
||
+
|
||
ret = digi_port_init(serial_priv->ds_oob_port,
|
||
serial_priv->ds_oob_port_num);
|
||
if (ret) {
|
||
@@ -1286,24 +1251,9 @@ static int digi_startup(struct usb_serial *serial)
|
||
return ret;
|
||
}
|
||
|
||
- usb_set_serial_data(serial, serial_priv);
|
||
-
|
||
return 0;
|
||
}
|
||
|
||
-
|
||
-static void digi_disconnect(struct usb_serial *serial)
|
||
-{
|
||
- int i;
|
||
-
|
||
- /* stop reads and writes on all ports */
|
||
- for (i = 0; i < serial->type->num_ports + 1; i++) {
|
||
- usb_kill_urb(serial->port[i]->read_urb);
|
||
- usb_kill_urb(serial->port[i]->write_urb);
|
||
- }
|
||
-}
|
||
-
|
||
-
|
||
static void digi_release(struct usb_serial *serial)
|
||
{
|
||
struct digi_serial *serial_priv;
|
||
@@ -1333,30 +1283,25 @@ static void digi_port_remove(struct usb_serial_port *port)
|
||
static void digi_read_bulk_callback(struct urb *urb)
|
||
{
|
||
struct usb_serial_port *port = urb->context;
|
||
- struct digi_port *priv;
|
||
- struct digi_serial *serial_priv;
|
||
- int ret;
|
||
+ struct usb_serial *serial = port->serial;
|
||
+ struct digi_serial *serial_priv = usb_get_serial_data(serial);
|
||
+ struct digi_port *priv = usb_get_serial_port_data(port);
|
||
int status = urb->status;
|
||
-
|
||
- /* port sanity check, do not resubmit if port is not valid */
|
||
- if (port == NULL)
|
||
- return;
|
||
- priv = usb_get_serial_port_data(port);
|
||
- if (priv == NULL) {
|
||
- dev_err(&port->dev, "%s: port->private is NULL, status=%d\n",
|
||
- __func__, status);
|
||
- return;
|
||
- }
|
||
- if (port->serial == NULL ||
|
||
- (serial_priv = usb_get_serial_data(port->serial)) == NULL) {
|
||
- dev_err(&port->dev, "%s: serial is bad or serial->private "
|
||
- "is NULL, status=%d\n", __func__, status);
|
||
- return;
|
||
- }
|
||
+ int ret;
|
||
|
||
/* do not resubmit urb if it has any status error */
|
||
- if (status) {
|
||
- dev_err(&port->dev,
|
||
+ switch (status) {
|
||
+ case 0:
|
||
+ break;
|
||
+ case -ENOENT:
|
||
+ case -ECONNRESET:
|
||
+ case -ESHUTDOWN:
|
||
+ dev_err(&serial->interface->dev,
|
||
+ "%s: nonzero read bulk status: status=%d, port=%d\n",
|
||
+ __func__, status, priv->dp_port_num);
|
||
+ return;
|
||
+ default:
|
||
+ dev_err(&serial->interface->dev,
|
||
"%s: nonzero read bulk status: status=%d, port=%d\n",
|
||
__func__, status, priv->dp_port_num);
|
||
return;
|
||
@@ -1374,11 +1319,10 @@ static void digi_read_bulk_callback(struct urb *urb)
|
||
/* continue read */
|
||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||
if (ret != 0 && ret != -EPERM) {
|
||
- dev_err(&port->dev,
|
||
+ dev_err(&serial->interface->dev,
|
||
"%s: failed resubmitting urb, ret=%d, port=%d\n",
|
||
__func__, ret, priv->dp_port_num);
|
||
}
|
||
-
|
||
}
|
||
|
||
/*
|
||
@@ -1390,7 +1334,6 @@ static void digi_read_bulk_callback(struct urb *urb)
|
||
* It returns 0 if successful, 1 if successful but the port is
|
||
* throttled, and -1 if the sanity checks failed.
|
||
*/
|
||
-
|
||
static int digi_read_inb_callback(struct urb *urb)
|
||
{
|
||
struct usb_serial_port *port = urb->context;
|
||
@@ -1468,10 +1411,8 @@ static int digi_read_inb_callback(struct urb *urb)
|
||
dev_dbg(&port->dev, "%s: unknown opcode: %d\n", __func__, opcode);
|
||
|
||
return throttled ? 1 : 0;
|
||
-
|
||
}
|
||
|
||
-
|
||
/*
|
||
* Digi Read OOB Callback
|
||
*
|
||
@@ -1480,10 +1421,8 @@ static int digi_read_inb_callback(struct urb *urb)
|
||
* the port->serial is valid. It returns 0 if successful, and
|
||
* -1 if the sanity checks failed.
|
||
*/
|
||
-
|
||
static int digi_read_oob_callback(struct urb *urb)
|
||
{
|
||
-
|
||
struct usb_serial_port *port = urb->context;
|
||
struct usb_serial *serial = port->serial;
|
||
struct tty_struct *tty;
|
||
@@ -1491,8 +1430,8 @@ static int digi_read_oob_callback(struct urb *urb)
|
||
unsigned char *buf = urb->transfer_buffer;
|
||
int opcode, line, status, val;
|
||
unsigned long flags;
|
||
- int i;
|
||
unsigned int rts;
|
||
+ int i;
|
||
|
||
if (urb->actual_length < 4)
|
||
return -1;
|
||
@@ -1504,7 +1443,8 @@ static int digi_read_oob_callback(struct urb *urb)
|
||
status = buf[i + 2];
|
||
val = buf[i + 3];
|
||
|
||
- dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
|
||
+ dev_dbg(&serial->interface->dev,
|
||
+ "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
|
||
opcode, line, status, val);
|
||
|
||
if (status != 0 || line >= serial->type->num_ports)
|
||
@@ -1562,8 +1502,8 @@ static int digi_read_oob_callback(struct urb *urb)
|
||
}
|
||
tty_kref_put(tty);
|
||
}
|
||
- return 0;
|
||
|
||
+ return 0;
|
||
}
|
||
|
||
module_usb_serial_driver(serial_drivers, id_table_combined);
|
||
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
|
||
--- a/drivers/usb/serial/keyspan_pda.c
|
||
+++ b/drivers/usb/serial/keyspan_pda.c
|
||
@@ -31,7 +31,6 @@
|
||
struct keyspan_pda_private {
|
||
int tx_room;
|
||
struct work_struct unthrottle_work;
|
||
- struct usb_serial *serial;
|
||
struct usb_serial_port *port;
|
||
bool throttled;
|
||
bool throttle_req;
|
||
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
|
||
--- a/drivers/usb/serial/metro-usb.c
|
||
+++ b/drivers/usb/serial/metro-usb.c
|
||
@@ -36,6 +36,7 @@
|
||
struct metrousb_private {
|
||
spinlock_t lock;
|
||
int throttled;
|
||
+ int throttle_req;
|
||
unsigned long control_state;
|
||
};
|
||
|
||
@@ -108,7 +109,7 @@ static void metrousb_read_int_callback(struct urb *urb)
|
||
unsigned char *data = urb->transfer_buffer;
|
||
unsigned long flags;
|
||
int throttled = 0;
|
||
- int result = 0;
|
||
+ int result;
|
||
|
||
dev_dbg(&port->dev, "%s\n", __func__);
|
||
|
||
@@ -143,7 +144,10 @@ static void metrousb_read_int_callback(struct urb *urb)
|
||
|
||
/* Set any port variables. */
|
||
spin_lock_irqsave(&metro_priv->lock, flags);
|
||
- throttled = metro_priv->throttled;
|
||
+ if (metro_priv->throttle_req) {
|
||
+ metro_priv->throttled = 1;
|
||
+ throttled = 1;
|
||
+ }
|
||
spin_unlock_irqrestore(&metro_priv->lock, flags);
|
||
|
||
if (throttled)
|
||
@@ -169,12 +173,13 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
|
||
struct usb_serial *serial = port->serial;
|
||
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
|
||
unsigned long flags;
|
||
- int result = 0;
|
||
+ int result;
|
||
|
||
/* Set the private data information for the port. */
|
||
spin_lock_irqsave(&metro_priv->lock, flags);
|
||
metro_priv->control_state = 0;
|
||
metro_priv->throttled = 0;
|
||
+ metro_priv->throttle_req = 0;
|
||
spin_unlock_irqrestore(&metro_priv->lock, flags);
|
||
|
||
/* Clear the urb pipe. */
|
||
@@ -269,7 +274,7 @@ static void metrousb_throttle(struct tty_struct *tty)
|
||
|
||
/* Set the private information for the port to stop reading data. */
|
||
spin_lock_irqsave(&metro_priv->lock, flags);
|
||
- metro_priv->throttled = 1;
|
||
+ metro_priv->throttle_req = 1;
|
||
spin_unlock_irqrestore(&metro_priv->lock, flags);
|
||
}
|
||
|
||
@@ -321,19 +326,23 @@ static void metrousb_unthrottle(struct tty_struct *tty)
|
||
struct usb_serial_port *port = tty->driver_data;
|
||
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
|
||
unsigned long flags;
|
||
- int result = 0;
|
||
+ int throttled;
|
||
+ int result;
|
||
|
||
/* Set the private information for the port to resume reading data. */
|
||
spin_lock_irqsave(&metro_priv->lock, flags);
|
||
+ throttled = metro_priv->throttled;
|
||
metro_priv->throttled = 0;
|
||
+ metro_priv->throttle_req = 0;
|
||
spin_unlock_irqrestore(&metro_priv->lock, flags);
|
||
|
||
- /* Submit the urb to read from the port. */
|
||
- result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
|
||
- if (result)
|
||
- dev_err(&port->dev,
|
||
- "failed submitting interrupt in urb error code=%d\n",
|
||
- result);
|
||
+ if (throttled) {
|
||
+ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||
+ if (result) {
|
||
+ dev_err(&port->dev, "failed to submit interrupt in urb: %d\n",
|
||
+ result);
|
||
+ }
|
||
+ }
|
||
}
|
||
|
||
static struct usb_serial_driver metrousb_device = {
|
||
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
|
||
--- a/drivers/usb/serial/pl2303.c
|
||
+++ b/drivers/usb/serial/pl2303.c
|
||
@@ -51,6 +51,7 @@ static const struct usb_device_id id_table[] = {
|
||
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) },
|
||
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) },
|
||
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) },
|
||
+ { USB_DEVICE(PL2303_VENDOR_ID, PL256X_PRODUCT_ID_4P) },
|
||
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
|
||
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
|
||
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID),
|
||
@@ -141,10 +142,15 @@ MODULE_DEVICE_TABLE(usb, id_table);
|
||
#define VENDOR_WRITE_REQUEST_TYPE 0x40
|
||
#define VENDOR_WRITE_REQUEST 0x01
|
||
#define VENDOR_WRITE_NREQUEST 0x80
|
||
+#define VENDOR_WRITE_MPREQUEST 0x80
|
||
|
||
#define VENDOR_READ_REQUEST_TYPE 0xc0
|
||
#define VENDOR_READ_REQUEST 0x01
|
||
#define VENDOR_READ_NREQUEST 0x81
|
||
+#define VENDOR_READ_MPREQUEST 0x80
|
||
+
|
||
+#define PL256X_RESET_REQUEST_TYPE 0x40
|
||
+#define PL256X_RESET_REQUEST 0x96
|
||
|
||
#define UART_STATE_INDEX 8
|
||
#define UART_STATE_MSR_MASK 0x8b
|
||
@@ -172,6 +178,16 @@ MODULE_DEVICE_TABLE(usb, id_table);
|
||
#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
|
||
#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
|
||
|
||
+#define PL256X_PORT_A_FLOWCTRL_REG 0xc005
|
||
+#define PL256X_PORT_B_FLOWCTRL_REG 0xd005
|
||
+#define PL256X_PORT_C_FLOWCTRL_REG 0xe005
|
||
+#define PL256X_PORT_D_FLOWCTRL_REG 0xf005
|
||
+
|
||
+#define PL256X_FLOWCTRL_MASK 0x43
|
||
+#define PL256X_FLOWCTRL_XON_XOFF 0x40
|
||
+#define PL256X_FLOWCTRL_RTS_CTS 0x03
|
||
+#define PL256X_FLOWCTRL_NONE 0x00
|
||
+
|
||
static int pl2303_set_break(struct usb_serial_port *port, bool enable);
|
||
|
||
enum pl2303_type {
|
||
@@ -181,6 +197,7 @@ enum pl2303_type {
|
||
TYPE_TB,
|
||
TYPE_HXD,
|
||
TYPE_HXN,
|
||
+ TYPE_MP,
|
||
TYPE_COUNT
|
||
};
|
||
|
||
@@ -196,6 +213,8 @@ struct pl2303_type_data {
|
||
struct pl2303_serial_private {
|
||
const struct pl2303_type_data *type;
|
||
unsigned long quirks;
|
||
+ u16 interface_num;
|
||
+ u16 flowctrl_reg;
|
||
};
|
||
|
||
struct pl2303_private {
|
||
@@ -236,8 +255,30 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
|
||
.max_baud_rate = 12000000,
|
||
.no_divisors = true,
|
||
},
|
||
+ [TYPE_MP] = {
|
||
+ .name = "MP",
|
||
+ .max_baud_rate = 24000000,
|
||
+ .no_divisors = true,
|
||
+ },
|
||
};
|
||
|
||
+static int pl256x_uart_reset(struct usb_serial *serial)
|
||
+{
|
||
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
|
||
+ struct device *dev = &serial->interface->dev;
|
||
+ int res;
|
||
+
|
||
+ res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
||
+ PL256X_RESET_REQUEST, PL256X_RESET_REQUEST_TYPE,
|
||
+ 0, spriv->interface_num, NULL, 0, 100);
|
||
+ if (res) {
|
||
+ dev_err(dev, "failed to reset device: %d\n", res);
|
||
+ return res;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
|
||
unsigned char buf[1])
|
||
{
|
||
@@ -248,6 +289,8 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
|
||
|
||
if (spriv->type == &pl2303_type_data[TYPE_HXN])
|
||
request = VENDOR_READ_NREQUEST;
|
||
+ else if (spriv->type == &pl2303_type_data[TYPE_MP])
|
||
+ request = VENDOR_READ_MPREQUEST;
|
||
else
|
||
request = VENDOR_READ_REQUEST;
|
||
|
||
@@ -279,6 +322,8 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
|
||
|
||
if (spriv->type == &pl2303_type_data[TYPE_HXN])
|
||
request = VENDOR_WRITE_NREQUEST;
|
||
+ else if (spriv->type == &pl2303_type_data[TYPE_MP])
|
||
+ request = VENDOR_WRITE_MPREQUEST;
|
||
else
|
||
request = VENDOR_WRITE_REQUEST;
|
||
|
||
@@ -304,10 +349,12 @@ static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val)
|
||
if (!buf)
|
||
return -ENOMEM;
|
||
|
||
- if (spriv->type == &pl2303_type_data[TYPE_HXN])
|
||
+ if (spriv->type == &pl2303_type_data[TYPE_HXN] ||
|
||
+ spriv->type == &pl2303_type_data[TYPE_MP]) {
|
||
ret = pl2303_vendor_read(serial, reg, buf);
|
||
- else
|
||
+ } else {
|
||
ret = pl2303_vendor_read(serial, reg | 0x80, buf);
|
||
+ }
|
||
|
||
if (ret)
|
||
goto out_free;
|
||
@@ -458,6 +505,14 @@ static int pl2303_detect_type(struct usb_serial *serial)
|
||
case 0x905: /* GT-2AB */
|
||
case 0x1005: /* GC-Q20 */
|
||
return TYPE_HXN;
|
||
+ case 0x3302: /* PL2533 VC 2 Port */
|
||
+ case 0x3304: /* PL2533 VC 4 Port */
|
||
+ case 0x4302: /* PL2543 VC 2 Port */
|
||
+ case 0x4304: /* PL2543 VC 4 Port */
|
||
+ case 0x6502: /* PL2565 VC 2 Port */
|
||
+ case 0x6504: /* PL2565 VC 4 Port */
|
||
+ case 0x6506: /* PL2565 VC 4 Port QFN64 package */
|
||
+ return TYPE_MP;
|
||
}
|
||
break;
|
||
}
|
||
@@ -491,6 +546,7 @@ static int pl2303_startup(struct usb_serial *serial)
|
||
struct pl2303_serial_private *spriv;
|
||
enum pl2303_type type;
|
||
unsigned char *buf;
|
||
+ unsigned int ifnum;
|
||
int ret;
|
||
|
||
ret = pl2303_detect_type(serial);
|
||
@@ -500,20 +556,43 @@ static int pl2303_startup(struct usb_serial *serial)
|
||
type = ret;
|
||
dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name);
|
||
|
||
+ ifnum = serial->interface->altsetting->desc.bInterfaceNumber;
|
||
+
|
||
spriv = kzalloc_obj(*spriv);
|
||
if (!spriv)
|
||
return -ENOMEM;
|
||
|
||
+ if (type == TYPE_MP) {
|
||
+ switch (ifnum) {
|
||
+ case 0:
|
||
+ spriv->flowctrl_reg = PL256X_PORT_A_FLOWCTRL_REG;
|
||
+ break;
|
||
+ case 1:
|
||
+ spriv->flowctrl_reg = PL256X_PORT_B_FLOWCTRL_REG;
|
||
+ break;
|
||
+ case 2:
|
||
+ spriv->flowctrl_reg = PL256X_PORT_C_FLOWCTRL_REG;
|
||
+ break;
|
||
+ case 3:
|
||
+ spriv->flowctrl_reg = PL256X_PORT_D_FLOWCTRL_REG;
|
||
+ break;
|
||
+ default:
|
||
+ kfree(spriv);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+ }
|
||
+
|
||
spriv->type = &pl2303_type_data[type];
|
||
spriv->quirks = (unsigned long)usb_get_serial_data(serial);
|
||
spriv->quirks |= spriv->type->quirks;
|
||
+ spriv->interface_num = ifnum;
|
||
|
||
if (type == TYPE_HXD && pl2303_is_hxd_clone(serial))
|
||
spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE;
|
||
|
||
usb_set_serial_data(serial, spriv);
|
||
|
||
- if (type != TYPE_HXN) {
|
||
+ if (type != TYPE_HXN && type != TYPE_MP) {
|
||
buf = kmalloc(1, GFP_KERNEL);
|
||
if (!buf) {
|
||
kfree(spriv);
|
||
@@ -575,13 +654,15 @@ static void pl2303_port_remove(struct usb_serial_port *port)
|
||
static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
|
||
{
|
||
struct usb_device *dev = port->serial->dev;
|
||
+ struct usb_serial *serial = port->serial;
|
||
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
|
||
int retval;
|
||
|
||
dev_dbg(&port->dev, "%s - %02x\n", __func__, value);
|
||
|
||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||
SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
|
||
- value, 0, NULL, 0, 100);
|
||
+ value, spriv->interface_num, NULL, 0, 100);
|
||
if (retval)
|
||
dev_err(&port->dev, "%s - failed: %d\n", __func__, retval);
|
||
|
||
@@ -761,7 +842,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port,
|
||
|
||
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
||
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
|
||
- 0, 0, buf, 7, 100);
|
||
+ 0, spriv->interface_num, buf, 7, 100);
|
||
if (ret != 7) {
|
||
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
|
||
|
||
@@ -780,11 +861,13 @@ static int pl2303_set_line_request(struct usb_serial_port *port,
|
||
unsigned char buf[7])
|
||
{
|
||
struct usb_device *udev = port->serial->dev;
|
||
+ struct usb_serial *serial = port->serial;
|
||
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
|
||
int ret;
|
||
|
||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
|
||
- 0, 0, buf, 7, 100);
|
||
+ 0, spriv->interface_num, buf, 7, 100);
|
||
if (ret < 0) {
|
||
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
|
||
return ret;
|
||
@@ -939,6 +1022,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
|
||
pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
|
||
PL2303_HXN_FLOWCTRL_MASK,
|
||
PL2303_HXN_FLOWCTRL_RTS_CTS);
|
||
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
|
||
+ pl2303_vendor_write(serial, spriv->flowctrl_reg,
|
||
+ PL256X_FLOWCTRL_RTS_CTS);
|
||
} else {
|
||
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60);
|
||
}
|
||
@@ -947,6 +1033,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
|
||
pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
|
||
PL2303_HXN_FLOWCTRL_MASK,
|
||
PL2303_HXN_FLOWCTRL_XON_XOFF);
|
||
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
|
||
+ pl2303_vendor_write(serial, spriv->flowctrl_reg,
|
||
+ PL256X_FLOWCTRL_XON_XOFF);
|
||
} else {
|
||
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0);
|
||
}
|
||
@@ -955,6 +1044,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
|
||
pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
|
||
PL2303_HXN_FLOWCTRL_MASK,
|
||
PL2303_HXN_FLOWCTRL_NONE);
|
||
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
|
||
+ pl2303_vendor_write(serial, spriv->flowctrl_reg,
|
||
+ PL256X_FLOWCTRL_NONE);
|
||
} else {
|
||
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0);
|
||
}
|
||
@@ -1002,6 +1094,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
|
||
pl2303_vendor_write(serial, PL2303_HXN_RESET_REG,
|
||
PL2303_HXN_RESET_UPSTREAM_PIPE |
|
||
PL2303_HXN_RESET_DOWNSTREAM_PIPE);
|
||
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
|
||
+ pl256x_uart_reset(serial);
|
||
} else {
|
||
pl2303_vendor_write(serial, 8, 0);
|
||
pl2303_vendor_write(serial, 9, 0);
|
||
@@ -1112,7 +1206,7 @@ static int pl2303_set_break(struct usb_serial_port *port, bool enable)
|
||
|
||
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
||
BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
|
||
- 0, NULL, 0, 100);
|
||
+ spriv->interface_num, NULL, 0, 100);
|
||
if (result) {
|
||
dev_err(&port->dev, "error sending break = %d\n", result);
|
||
return result;
|
||
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
|
||
--- a/drivers/usb/serial/pl2303.h
|
||
+++ b/drivers/usb/serial/pl2303.h
|
||
@@ -26,7 +26,7 @@
|
||
#define PL2303_PRODUCT_ID_HCR331 0x331a
|
||
#define PL2303_PRODUCT_ID_MOTOROLA 0x0307
|
||
#define PL2303_PRODUCT_ID_ZTEK 0xe1f1
|
||
-
|
||
+#define PL256X_PRODUCT_ID_4P 0x2533
|
||
|
||
#define ATEN_VENDOR_ID 0x0557
|
||
#define ATEN_VENDOR_ID2 0x0547
|
||
diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig
|
||
--- a/drivers/usb/typec/altmodes/Kconfig
|
||
+++ b/drivers/usb/typec/altmodes/Kconfig
|
||
@@ -5,6 +5,7 @@ menu "USB Type-C Alternate Mode drivers"
|
||
config TYPEC_DP_ALTMODE
|
||
tristate "DisplayPort Alternate Mode driver"
|
||
depends on DRM
|
||
+ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
|
||
help
|
||
DisplayPort USB Type-C Alternate Mode allows DisplayPort
|
||
displays and adapters to be attached to the USB Type-C
|
||
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
|
||
--- a/drivers/usb/typec/altmodes/displayport.c
|
||
+++ b/drivers/usb/typec/altmodes/displayport.c
|
||
@@ -764,6 +764,7 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
||
struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
|
||
struct fwnode_handle *fwnode;
|
||
struct dp_altmode *dp;
|
||
+ u32 cap = DP_CAP_CAPABILITY(alt->vdo);
|
||
|
||
/* Port can only be DFP_U. */
|
||
if (typec_altmode_get_data_role(alt) != TYPEC_HOST)
|
||
@@ -778,6 +779,18 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
||
return -ENODEV;
|
||
}
|
||
|
||
+ /*
|
||
+ * Make sure the DisplayPort VDO is valid (VESA DPAM v2.1a, Section
|
||
+ * 5.4.1, Table 5-6, DP Capabilities VDO). A device exposing DP on a
|
||
+ * USB-C receptacle must advertise at least one pin assignment for the
|
||
+ * capability it claims, otherwise Alt Mode can never be configured.
|
||
+ */
|
||
+ if ((cap == DP_CAP_DFP_D && !DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)) ||
|
||
+ (cap == DP_CAP_UFP_D && !DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo))) {
|
||
+ typec_altmode_put_plug(plug);
|
||
+ return -ENODEV;
|
||
+ }
|
||
+
|
||
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
|
||
if (!dp) {
|
||
typec_altmode_put_plug(plug);
|
||
@@ -790,7 +803,6 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
||
dp->alt = alt;
|
||
|
||
alt->desc = "DisplayPort";
|
||
- typec_altmode_set_ops(alt, &dp_altmode_ops);
|
||
|
||
if (plug) {
|
||
plug->desc = "Displayport";
|
||
@@ -811,6 +823,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
||
if (plug)
|
||
typec_altmode_set_drvdata(plug, dp);
|
||
|
||
+ if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt))
|
||
+ return 0;
|
||
+
|
||
+ typec_altmode_set_ops(alt, &dp_altmode_ops);
|
||
if (!alt->mode_selection) {
|
||
dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
|
||
schedule_work(&dp->work);
|
||
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
|
||
--- a/drivers/usb/typec/altmodes/thunderbolt.c
|
||
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
|
||
@@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
|
||
|
||
alt->desc = "Thunderbolt3";
|
||
typec_altmode_set_drvdata(alt, tbt);
|
||
+
|
||
+ if (typec_cable_altmode_unsupported(alt))
|
||
+ return 0;
|
||
+
|
||
typec_altmode_set_ops(alt, &tbt_altmode_ops);
|
||
|
||
if (!alt->mode_selection && tbt_ready(alt)) {
|
||
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
|
||
--- a/drivers/usb/typec/class.c
|
||
+++ b/drivers/usb/typec/class.c
|
||
@@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable)
|
||
}
|
||
EXPORT_SYMBOL_GPL(typec_cable_is_active);
|
||
|
||
+enum typec_cable_altmode_support {
|
||
+ CABLE_SUPPORT_UNKNOWN,
|
||
+ CABLE_SUPPORTED,
|
||
+ CABLE_NOT_SUPPORTED,
|
||
+};
|
||
+
|
||
+static enum typec_cable_altmode_support
|
||
+typec_cable_check_altmode_support(struct typec_cable *cable,
|
||
+ struct typec_altmode *alt)
|
||
+{
|
||
+ struct typec_altmode *plug;
|
||
+ u32 speed;
|
||
+
|
||
+ /*
|
||
+ * Check if the cable has an e-marker, supports modal operation, and the
|
||
+ * SOP' altmode nodes are created.
|
||
+ */
|
||
+ plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
|
||
+ if (plug) {
|
||
+ typec_altmode_put_plug(plug);
|
||
+ return CABLE_SUPPORTED;
|
||
+ }
|
||
+
|
||
+ /* The identity is not specified */
|
||
+ if (!cable->identity)
|
||
+ return CABLE_SUPPORT_UNKNOWN;
|
||
+
|
||
+ /* Non-e-marked cable */
|
||
+ if (!cable->identity->id_header)
|
||
+ return CABLE_NOT_SUPPORTED;
|
||
+
|
||
+ switch (PD_IDH_PTYPE(cable->identity->id_header)) {
|
||
+ case IDH_PTYPE_PCABLE:
|
||
+ speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]);
|
||
+ if (speed == CABLE_USB2_ONLY)
|
||
+ return CABLE_NOT_SUPPORTED;
|
||
+ return CABLE_SUPPORTED;
|
||
+ case IDH_PTYPE_ACABLE:
|
||
+ /*
|
||
+ * Active cables must establish an SOP' communication
|
||
+ * node. Since that check failed at the beginning of
|
||
+ * this function, this active cable does not support
|
||
+ * this specific altmode.
|
||
+ */
|
||
+ return CABLE_NOT_SUPPORTED;
|
||
+ }
|
||
+
|
||
+ return CABLE_SUPPORT_UNKNOWN;
|
||
+}
|
||
+
|
||
+/**
|
||
+ * typec_cable_altmode_unsupported - Check if a cable restricts altmode
|
||
+ * @alt: The Alternate Mode to evaluate
|
||
+ *
|
||
+ * Returns true if the connected cable is incapable of handling the altmode.
|
||
+ */
|
||
+bool typec_cable_altmode_unsupported(struct typec_altmode *alt)
|
||
+{
|
||
+ enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN;
|
||
+ struct typec_cable *cable;
|
||
+
|
||
+ cable = typec_cable_get(typec_altmode2port(alt));
|
||
+ if (cable) {
|
||
+ support = typec_cable_check_altmode_support(cable, alt);
|
||
+ typec_cable_put(cable);
|
||
+ }
|
||
+
|
||
+ return support == CABLE_NOT_SUPPORTED;
|
||
+}
|
||
+EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported);
|
||
+
|
||
/**
|
||
* typec_cable_set_identity - Report result from Discover Identity command
|
||
* @cable: The cable updated identity values
|
||
diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c
|
||
--- a/drivers/usb/typec/mux/tusb1046.c
|
||
+++ b/drivers/usb/typec/mux/tusb1046.c
|
||
@@ -178,6 +178,7 @@ static const struct of_device_id tusb1046_match_table[] = {
|
||
{.compatible = "ti,tusb1046"},
|
||
{},
|
||
};
|
||
+MODULE_DEVICE_TABLE(of, tusb1046_match_table);
|
||
|
||
static struct i2c_driver tusb1046_driver = {
|
||
.driver = {
|
||
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||
@@ -12,6 +12,7 @@
|
||
#include <linux/jiffies.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/kthread.h>
|
||
+#include <linux/math64.h>
|
||
#include <linux/minmax.h>
|
||
#include <linux/module.h>
|
||
#include <linux/mutex.h>
|
||
@@ -212,6 +213,24 @@ static const char * const tcpm_ams_str[] = {
|
||
FOREACH_AMS(GENERATE_STRING)
|
||
};
|
||
|
||
+#define FOREACH_VDM_DISCOVERY(S) \
|
||
+ S(VDM_DISCOVERY_UNKNOWN), \
|
||
+ S(VDM_DISCOVERY_PARTNER_IDENT), \
|
||
+ S(VDM_DISCOVERY_CABLE_IDENT), \
|
||
+ S(VDM_DISCOVERY_PARTNER_SVIDS), \
|
||
+ S(VDM_DISCOVERY_PARTNER_MODES), \
|
||
+ S(VDM_DISCOVERY_CABLE_SVIDS), \
|
||
+ S(VDM_DISCOVERY_CABLE_MODES), \
|
||
+ S(VDM_DISCOVERY_COMPLETE)
|
||
+
|
||
+enum vdm_discovery_states {
|
||
+ FOREACH_VDM_DISCOVERY(GENERATE_ENUM)
|
||
+};
|
||
+
|
||
+static const char * const vdm_discovery_state_strings[] = {
|
||
+ FOREACH_VDM_DISCOVERY(GENERATE_STRING)
|
||
+};
|
||
+
|
||
enum vdm_states {
|
||
VDM_STATE_ERR_BUSY = -3,
|
||
VDM_STATE_ERR_SEND = -2,
|
||
@@ -232,7 +251,9 @@ enum pd_msg_request {
|
||
PD_MSG_DATA_SINK_CAP,
|
||
PD_MSG_DATA_SOURCE_CAP,
|
||
PD_MSG_DATA_REV,
|
||
- PD_MSG_EXT_SINK_CAP_EXT
|
||
+ PD_MSG_EXT_SINK_CAP_EXT,
|
||
+ PD_MSG_DATA_BATT_STATUS,
|
||
+ PD_MSG_EXT_BATT_CAP,
|
||
};
|
||
|
||
enum adev_actions {
|
||
@@ -274,7 +295,7 @@ enum frs_typec_current {
|
||
#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
|
||
|
||
#define GET_SINK_CAP_RETRY_MS 100
|
||
-#define SEND_DISCOVER_RETRY_MS 100
|
||
+#define SEND_DISCOVERY_VDM_RETRY_MS 100
|
||
|
||
struct pd_mode_data {
|
||
int svid_index; /* current SVID index */
|
||
@@ -387,7 +408,15 @@ struct pd_timings {
|
||
};
|
||
|
||
/* Convert microwatt to watt */
|
||
-#define UW_TO_W(pow) ((pow) / 1000000)
|
||
+#define UW_TO_W(pow) (div_u64((pow), 1000000))
|
||
+
|
||
+/*
|
||
+ * As per USB PD Spec Rev 3.18 (Sec. 6.5.13.11), the number of fixed batteries
|
||
+ * that a port can be queried is restricted to 4.
|
||
+ */
|
||
+#define MAX_NUM_FIXED_BATT 4
|
||
+
|
||
+#define BATTERY_PROPERTY_UNKNOWN 0xffff
|
||
|
||
/*
|
||
* struct pd_identifier - Contains info about PD identifiers
|
||
@@ -483,8 +512,6 @@ struct tcpm_port {
|
||
bool vbus_source;
|
||
bool vbus_charge;
|
||
|
||
- /* Set to true when Discover_Identity Command is expected to be sent in Ready states. */
|
||
- bool send_discover;
|
||
bool op_vsafe5v;
|
||
|
||
int try_role;
|
||
@@ -510,8 +537,8 @@ struct tcpm_port {
|
||
struct kthread_work vdm_state_machine;
|
||
struct hrtimer enable_frs_timer;
|
||
struct kthread_work enable_frs;
|
||
- struct hrtimer send_discover_timer;
|
||
- struct kthread_work send_discover_work;
|
||
+ struct hrtimer vdm_discovery_timer;
|
||
+ struct kthread_work vdm_discovery_work;
|
||
bool state_machine_running;
|
||
/* Set to true when VDM State Machine has following actions. */
|
||
bool vdm_sm_running;
|
||
@@ -578,6 +605,9 @@ struct tcpm_port {
|
||
|
||
u32 bist_request;
|
||
|
||
+ /* VDM Discovery State to determine message sent */
|
||
+ enum vdm_discovery_states vdm_discovery_state;
|
||
+
|
||
/* PD state for Vendor Defined Messages */
|
||
enum vdm_states vdm_state;
|
||
u32 vdm_retries;
|
||
@@ -641,12 +671,6 @@ struct tcpm_port {
|
||
bool potential_contaminant;
|
||
|
||
/* SOP* Related Fields */
|
||
- /*
|
||
- * Flag to determine if SOP' Discover Identity is available. The flag
|
||
- * is set if Discover Identity on SOP' does not immediately follow
|
||
- * Discover Identity on SOP.
|
||
- */
|
||
- bool send_discover_prime;
|
||
/*
|
||
* tx_sop_type determines which SOP* a message is being sent on.
|
||
* For messages that are queued and not sent immediately such as in
|
||
@@ -683,6 +707,9 @@ struct tcpm_port {
|
||
|
||
struct pd_identifier pd_ident;
|
||
struct sink_caps_ext_data sink_caps_ext;
|
||
+ struct power_supply **fixed_batt;
|
||
+ u32 fixed_batt_cnt;
|
||
+ u32 batt_request_id;
|
||
#ifdef CONFIG_DEBUG_FS
|
||
struct dentry *dentry;
|
||
struct mutex logbuffer_lock; /* log buffer access lock */
|
||
@@ -771,6 +798,9 @@ static const char * const pd_rev[] = {
|
||
#define tcpm_wait_for_discharge(port) \
|
||
(((port)->auto_vbus_discharge_enabled && !(port)->vbus_vsafe0v) ? PD_T_SAFE_0V : 0)
|
||
|
||
+#define tcpm_can_send_vdm(state) \
|
||
+ ((state == SRC_READY || state == SNK_READY || state == SRC_VDM_IDENTITY_REQUEST))
|
||
+
|
||
static enum tcpm_state tcpm_default_state(struct tcpm_port *port)
|
||
{
|
||
if (port->port_type == TYPEC_PORT_DRP) {
|
||
@@ -1470,6 +1500,20 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port)
|
||
return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
|
||
}
|
||
|
||
+static void tcpm_get_fixed_batt(struct tcpm_port *port)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ if (!port->self_powered || port->fixed_batt_cnt > 0)
|
||
+ return;
|
||
+
|
||
+ ret = power_supply_get_system_batteries(port->dev, &port->fixed_batt);
|
||
+ if (ret < 0)
|
||
+ tcpm_log(port, "Failed to get battery array, ret=%d", ret);
|
||
+ else
|
||
+ port->fixed_batt_cnt = ret;
|
||
+}
|
||
+
|
||
static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port)
|
||
{
|
||
u16 operating_snk_watt = port->operating_snk_mw / 1000;
|
||
@@ -1482,6 +1526,8 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port)
|
||
if (!port->self_powered)
|
||
data->spr_op_pdp = operating_snk_watt;
|
||
|
||
+ tcpm_get_fixed_batt(port);
|
||
+
|
||
/*
|
||
* SPR Sink Minimum PDP indicates the minimum power required to operate
|
||
* a sink device in its lowest level of functionality without requiring
|
||
@@ -1507,6 +1553,7 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port)
|
||
skedb.load_step = data->load_step;
|
||
skedb.load_char = cpu_to_le16(data->load_char);
|
||
skedb.compliance = data->compliance;
|
||
+ skedb.batt_info = min(port->fixed_batt_cnt, MAX_NUM_FIXED_BATT);
|
||
skedb.modes = data->modes;
|
||
skedb.spr_min_pdp = data->spr_min_pdp;
|
||
skedb.spr_op_pdp = data->spr_op_pdp;
|
||
@@ -1525,6 +1572,153 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port)
|
||
port->message_id,
|
||
data_obj_cnt,
|
||
1 /* Denotes if ext header */));
|
||
+
|
||
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
|
||
+}
|
||
+
|
||
+static u16 tcpm_charge_to_energy(int charge, int voltage)
|
||
+{
|
||
+ u64 energy = div_u64((u64)charge * voltage, 1000000);
|
||
+
|
||
+ /* Battery telemetry is reported in increments of 0.1Wh */
|
||
+ return (u16)UW_TO_W(energy * 10);
|
||
+}
|
||
+
|
||
+static int tcpm_pd_send_batt_status(struct tcpm_port *port)
|
||
+{
|
||
+ u16 present_charge = BATTERY_PROPERTY_UNKNOWN;
|
||
+ bool batt_present = false, invalid_ref = true;
|
||
+ u32 batt_id = port->batt_request_id;
|
||
+ union power_supply_propval val;
|
||
+ struct power_supply *batt;
|
||
+ u8 charging_status = 0;
|
||
+ struct pd_message msg;
|
||
+ int ret, charge_now;
|
||
+ u32 bsdo;
|
||
+
|
||
+ tcpm_get_fixed_batt(port);
|
||
+ memset(&msg, 0, sizeof(msg));
|
||
+
|
||
+ if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT)
|
||
+ goto send_status;
|
||
+
|
||
+ invalid_ref = false;
|
||
+ batt = port->fixed_batt[batt_id];
|
||
+ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_PRESENT, &val);
|
||
+ if (ret)
|
||
+ tcpm_log(port,
|
||
+ "Failed to fetch power_supply_prop_present ret %d",
|
||
+ ret);
|
||
+ else
|
||
+ batt_present = val.intval > 0;
|
||
+
|
||
+ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_NOW,
|
||
+ &val);
|
||
+ if (!ret) {
|
||
+ charge_now = val.intval;
|
||
+ ret = power_supply_get_property(batt,
|
||
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||
+ &val);
|
||
+ if (!ret)
|
||
+ present_charge = tcpm_charge_to_energy(charge_now,
|
||
+ val.intval);
|
||
+ }
|
||
+
|
||
+ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_STATUS, &val);
|
||
+ if (!ret) {
|
||
+ switch (val.intval) {
|
||
+ case POWER_SUPPLY_STATUS_CHARGING:
|
||
+ charging_status = BSDO_BATTERY_INFO_CHARGING;
|
||
+ break;
|
||
+ case POWER_SUPPLY_STATUS_DISCHARGING:
|
||
+ charging_status = BSDO_BATTERY_INFO_DISCHARGING;
|
||
+ break;
|
||
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
||
+ case POWER_SUPPLY_STATUS_FULL:
|
||
+ charging_status = BSDO_BATTERY_INFO_IDLE;
|
||
+ break;
|
||
+ default:
|
||
+ charging_status = BSDO_BATTERY_INFO_RSVD;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+send_status:
|
||
+
|
||
+ bsdo = BSDO(present_charge, charging_status, batt_present, invalid_ref);
|
||
+ msg.payload[0] = cpu_to_le32(bsdo);
|
||
+ msg.header = PD_HEADER_LE(PD_DATA_BATT_STATUS,
|
||
+ port->pwr_role,
|
||
+ port->data_role,
|
||
+ port->negotiated_rev,
|
||
+ port->message_id,
|
||
+ 1);
|
||
+
|
||
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
|
||
+}
|
||
+
|
||
+static int tcpm_pd_send_batt_cap(struct tcpm_port *port)
|
||
+{
|
||
+ u16 design_cap = BATTERY_PROPERTY_UNKNOWN;
|
||
+ u16 charge_cap = BATTERY_PROPERTY_UNKNOWN;
|
||
+ u32 batt_id = port->batt_request_id;
|
||
+ union power_supply_propval val;
|
||
+ struct batt_cap_ext_msg bcdb;
|
||
+ struct power_supply *batt;
|
||
+ bool invalid_ref = true;
|
||
+ struct pd_message msg;
|
||
+ u8 data_obj_cnt;
|
||
+ int ret, vol;
|
||
+
|
||
+ tcpm_get_fixed_batt(port);
|
||
+ memset(&msg, 0, sizeof(msg));
|
||
+
|
||
+ if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT)
|
||
+ goto send_cap;
|
||
+
|
||
+ invalid_ref = false;
|
||
+ batt = port->fixed_batt[batt_id];
|
||
+ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||
+ &val);
|
||
+ if (!ret) {
|
||
+ vol = val.intval;
|
||
+ ret = power_supply_get_property(batt,
|
||
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||
+ &val);
|
||
+ if (!ret)
|
||
+ design_cap = tcpm_charge_to_energy(val.intval, vol);
|
||
+
|
||
+ ret = power_supply_get_property(batt,
|
||
+ POWER_SUPPLY_PROP_CHARGE_FULL,
|
||
+ &val);
|
||
+ if (!ret)
|
||
+ charge_cap = tcpm_charge_to_energy(val.intval, vol);
|
||
+ }
|
||
+
|
||
+send_cap:
|
||
+
|
||
+ /*
|
||
+ * As per the USB PD Rev3.1 v1.8 spec, if a battery VID (assigned by the
|
||
+ * USB-IF) does not exist or an invalid battery reference is made by the
|
||
+ * requestor, then set the VID field to 0xffff. If the VID field is
|
||
+ * 0xffff, set the PID field to 0.
|
||
+ */
|
||
+ bcdb.vid = BATTERY_PROPERTY_UNKNOWN;
|
||
+ bcdb.pid = 0;
|
||
+ bcdb.batt_design_cap = cpu_to_le16(design_cap);
|
||
+ bcdb.batt_last_chg_cap = cpu_to_le16(charge_cap);
|
||
+ bcdb.batt_type = invalid_ref ? BATT_CAP_BATT_TYPE_INVALID_REF : 0;
|
||
+ memcpy(msg.ext_msg.data, &bcdb, sizeof(bcdb));
|
||
+ msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(bcdb),
|
||
+ 0, /* Denotes if request chunk */
|
||
+ 0, /* Chunk number */
|
||
+ 1 /* Chunked */);
|
||
+
|
||
+ data_obj_cnt = count_chunked_data_objs(sizeof(bcdb));
|
||
+ msg.header = PD_HEADER_EXT_LE(PD_EXT_BATT_CAP, port->pwr_role,
|
||
+ port->data_role, port->negotiated_rev,
|
||
+ port->message_id, data_obj_cnt);
|
||
+
|
||
return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
|
||
}
|
||
|
||
@@ -1559,13 +1753,19 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del
|
||
}
|
||
}
|
||
|
||
-static void mod_send_discover_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
|
||
+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);
|
||
+}
|
||
+
|
||
+static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
|
||
{
|
||
if (delay_ms) {
|
||
- hrtimer_start(&port->send_discover_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
|
||
+ hrtimer_start(&port->vdm_discovery_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
|
||
} else {
|
||
- hrtimer_cancel(&port->send_discover_timer);
|
||
- kthread_queue_work(port->wq, &port->send_discover_work);
|
||
+ hrtimer_cancel(&port->vdm_discovery_timer);
|
||
+ kthread_queue_work(port->wq, &port->vdm_discovery_work);
|
||
}
|
||
}
|
||
|
||
@@ -1773,16 +1973,11 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
|
||
WARN_ON(!mutex_is_locked(&port->lock));
|
||
|
||
/* If is sending discover_identity, handle received message first */
|
||
- if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) {
|
||
- if (tx_sop_type == TCPC_TX_SOP_PRIME)
|
||
- port->send_discover_prime = true;
|
||
- else
|
||
- port->send_discover = true;
|
||
- mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS);
|
||
- } else {
|
||
+ if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT)
|
||
+ mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS);
|
||
+ else
|
||
/* Make sure we are not still processing a previous VDM packet */
|
||
WARN_ON(port->vdm_state > VDM_STATE_DONE);
|
||
- }
|
||
|
||
port->vdo_count = cnt + 1;
|
||
port->vdo_data[0] = header;
|
||
@@ -1805,8 +2000,7 @@ static void tcpm_queue_vdm_work(struct kthread_work *work)
|
||
struct tcpm_port *port = event->port;
|
||
|
||
mutex_lock(&port->lock);
|
||
- if (port->state != SRC_READY && port->state != SNK_READY &&
|
||
- port->state != SRC_VDM_IDENTITY_REQUEST) {
|
||
+ if (!tcpm_can_send_vdm(port->state)) {
|
||
tcpm_log_force(port, "dropping altmode_vdm_event");
|
||
goto port_unlock;
|
||
}
|
||
@@ -2034,7 +2228,7 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
|
||
if (!port->partner)
|
||
return;
|
||
|
||
- for (i = 0; i < modep->altmodes; i++) {
|
||
+ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) {
|
||
altmode = typec_partner_register_altmode(port->partner,
|
||
&modep->altmode_desc[i]);
|
||
if (IS_ERR(altmode)) {
|
||
@@ -2052,9 +2246,10 @@ static void tcpm_register_plug_altmodes(struct tcpm_port *port)
|
||
struct typec_altmode *altmode;
|
||
int i;
|
||
|
||
- typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes);
|
||
+ typec_plug_set_num_altmodes(port->plug_prime,
|
||
+ min(modep->altmodes, ALTMODE_DISCOVERY_MAX));
|
||
|
||
- for (i = 0; i < modep->altmodes; i++) {
|
||
+ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) {
|
||
altmode = typec_plug_register_altmode(port->plug_prime,
|
||
&modep->altmode_desc[i]);
|
||
if (IS_ERR(altmode)) {
|
||
@@ -2154,6 +2349,19 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port)
|
||
tcpm_can_communicate_sop_prime(port);
|
||
}
|
||
|
||
+static void tcpm_update_vdm_discovery_state(struct tcpm_port *port,
|
||
+ enum vdm_discovery_states new_state)
|
||
+{
|
||
+ enum vdm_discovery_states old_state = port->vdm_discovery_state;
|
||
+
|
||
+ if (old_state != new_state)
|
||
+ tcpm_log_force(port, "vdm discovery state changed: %s -> %s",
|
||
+ vdm_discovery_state_strings[old_state],
|
||
+ vdm_discovery_state_strings[new_state]);
|
||
+
|
||
+ port->vdm_discovery_state = new_state;
|
||
+}
|
||
+
|
||
static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response,
|
||
enum tcpm_transmit_type rx_sop_type,
|
||
enum tcpm_transmit_type *response_tx_sop_type)
|
||
@@ -2171,6 +2379,7 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response,
|
||
response[0] = VDO(svid, 1,
|
||
typec_get_negotiated_svdm_version(typec),
|
||
CMD_DISCOVER_MODES);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES);
|
||
return 1;
|
||
}
|
||
|
||
@@ -2179,10 +2388,12 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response,
|
||
response[0] = VDO(USB_SID_PD, 1,
|
||
typec_get_cable_svdm_version(typec),
|
||
CMD_DISCOVER_SVID);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES);
|
||
return 1;
|
||
}
|
||
|
||
tcpm_register_partner_altmodes(port);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
|
||
modep = &port->mode_data_prime;
|
||
modep->svid_index++;
|
||
@@ -2193,11 +2404,13 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response,
|
||
response[0] = VDO(svid, 1,
|
||
typec_get_cable_svdm_version(typec),
|
||
CMD_DISCOVER_MODES);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_MODES);
|
||
return 1;
|
||
}
|
||
|
||
tcpm_register_plug_altmodes(port);
|
||
tcpm_register_partner_altmodes(port);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
}
|
||
|
||
return 0;
|
||
@@ -2376,18 +2589,18 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
|
||
typec_cable_set_svdm_version(port->cable,
|
||
svdm_version);
|
||
}
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_IDENT);
|
||
+
|
||
/* 6.4.4.3.1 */
|
||
svdm_consume_identity(port, p, cnt);
|
||
/* Attempt Vconn swap, delay SOP' discovery if necessary */
|
||
if (tcpm_attempt_vconn_swap_discovery(port)) {
|
||
- port->send_discover_prime = true;
|
||
port->upcoming_state = VCONN_SWAP_SEND;
|
||
ret = tcpm_ams_start(port, VCONN_SWAP);
|
||
if (!ret)
|
||
return 0;
|
||
/* Cannot perform Vconn swap */
|
||
port->upcoming_state = INVALID_STATE;
|
||
- port->send_discover_prime = false;
|
||
}
|
||
|
||
/*
|
||
@@ -2398,7 +2611,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
|
||
if (IS_ERR_OR_NULL(port->cable) &&
|
||
tcpm_can_communicate_sop_prime(port)) {
|
||
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
- port->send_discover_prime = true;
|
||
response[0] = VDO(USB_SID_PD, 1,
|
||
typec_get_negotiated_svdm_version(typec),
|
||
CMD_DISCOVER_IDENT);
|
||
@@ -2426,6 +2638,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
|
||
tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
|
||
return 0;
|
||
}
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT);
|
||
|
||
*response_tx_sop_type = TCPC_TX_SOP;
|
||
response[0] = VDO(USB_SID_PD, 1,
|
||
@@ -2445,16 +2658,27 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
|
||
rlen = 1;
|
||
} else {
|
||
if (rx_sop_type == TCPC_TX_SOP) {
|
||
+ tcpm_update_vdm_discovery_state(port,
|
||
+ VDM_DISCOVERY_PARTNER_SVIDS);
|
||
if (modep->nsvids && supports_modal(port)) {
|
||
response[0] = VDO(modep->svids[0], 1, svdm_version,
|
||
CMD_DISCOVER_MODES);
|
||
rlen = 1;
|
||
+ } else {
|
||
+ tcpm_update_vdm_discovery_state(port,
|
||
+ VDM_DISCOVERY_COMPLETE);
|
||
}
|
||
} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
|
||
+ tcpm_update_vdm_discovery_state(port,
|
||
+ VDM_DISCOVERY_CABLE_SVIDS);
|
||
if (modep_prime->nsvids) {
|
||
response[0] = VDO(modep_prime->svids[0], 1,
|
||
svdm_version, CMD_DISCOVER_MODES);
|
||
rlen = 1;
|
||
+ } else {
|
||
+ tcpm_register_partner_altmodes(port);
|
||
+ tcpm_update_vdm_discovery_state(port,
|
||
+ VDM_DISCOVERY_COMPLETE);
|
||
}
|
||
}
|
||
}
|
||
@@ -2505,8 +2729,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
|
||
case CMDT_RSP_NAK:
|
||
tcpm_ams_finish(port);
|
||
switch (cmd) {
|
||
+ /*
|
||
+ * The cable is not allowed to respond with NAK so this must've happened over SOP
|
||
+ */
|
||
case CMD_DISCOVER_IDENT:
|
||
case CMD_DISCOVER_SVID:
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
+ break;
|
||
case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15):
|
||
break;
|
||
case CMD_DISCOVER_MODES:
|
||
@@ -2686,44 +2915,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
|
||
port->vdm_sm_running = false;
|
||
}
|
||
|
||
-static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd,
|
||
- const u32 *data, int count, enum tcpm_transmit_type tx_sop_type)
|
||
-{
|
||
- int svdm_version;
|
||
- u32 header;
|
||
-
|
||
- switch (tx_sop_type) {
|
||
- case TCPC_TX_SOP_PRIME:
|
||
- /*
|
||
- * If the port partner is discovered, then the port partner's
|
||
- * SVDM Version will be returned
|
||
- */
|
||
- svdm_version = typec_get_cable_svdm_version(port->typec_port);
|
||
- if (svdm_version < 0)
|
||
- svdm_version = SVDM_VER_MAX;
|
||
- break;
|
||
- case TCPC_TX_SOP:
|
||
- svdm_version = typec_get_negotiated_svdm_version(port->typec_port);
|
||
- if (svdm_version < 0)
|
||
- return;
|
||
- break;
|
||
- default:
|
||
- svdm_version = typec_get_negotiated_svdm_version(port->typec_port);
|
||
- if (svdm_version < 0)
|
||
- return;
|
||
- break;
|
||
- }
|
||
-
|
||
- if (WARN_ON(count > VDO_MAX_SIZE - 1))
|
||
- count = VDO_MAX_SIZE - 1;
|
||
-
|
||
- /* set VDM header with VID & CMD */
|
||
- header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ?
|
||
- 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION),
|
||
- svdm_version, cmd);
|
||
- tcpm_queue_vdm(port, header, data, count, tx_sop_type);
|
||
-}
|
||
-
|
||
static unsigned int vdm_ready_timeout(u32 vdm_hdr)
|
||
{
|
||
unsigned int timeout;
|
||
@@ -2769,8 +2960,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||
* if there's traffic or we're not in PDO ready state don't send
|
||
* a VDM.
|
||
*/
|
||
- if (port->state != SRC_READY && port->state != SNK_READY &&
|
||
- port->state != SRC_VDM_IDENTITY_REQUEST) {
|
||
+ if (!tcpm_can_send_vdm(port->state)) {
|
||
port->vdm_sm_running = false;
|
||
break;
|
||
}
|
||
@@ -2780,22 +2970,10 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||
switch (PD_VDO_CMD(vdo_hdr)) {
|
||
case CMD_DISCOVER_IDENT:
|
||
res = tcpm_ams_start(port, DISCOVER_IDENTITY);
|
||
- if (res == 0) {
|
||
- switch (port->tx_sop_type) {
|
||
- case TCPC_TX_SOP_PRIME:
|
||
- port->send_discover_prime = false;
|
||
- break;
|
||
- case TCPC_TX_SOP:
|
||
- port->send_discover = false;
|
||
- break;
|
||
- default:
|
||
- port->send_discover = false;
|
||
- break;
|
||
- }
|
||
- } else if (res == -EAGAIN) {
|
||
+ if (res == -EAGAIN) {
|
||
port->vdo_data[0] = 0;
|
||
- mod_send_discover_delayed_work(port,
|
||
- SEND_DISCOVER_RETRY_MS);
|
||
+ mod_vdm_discovery_delayed_work(port,
|
||
+ SEND_DISCOVERY_VDM_RETRY_MS);
|
||
}
|
||
break;
|
||
case CMD_DISCOVER_SVID:
|
||
@@ -2853,6 +3031,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||
*/
|
||
if (port->state == SRC_VDM_IDENTITY_REQUEST) {
|
||
tcpm_ams_finish(port);
|
||
+ port->vdo_data[0] = 0;
|
||
port->vdm_state = VDM_STATE_DONE;
|
||
tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
|
||
/*
|
||
@@ -2869,6 +3048,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||
tcpm_ams_finish(port);
|
||
} else {
|
||
tcpm_ams_finish(port);
|
||
+ port->vdo_data[0] = 0;
|
||
if (port->tx_sop_type == TCPC_TX_SOP)
|
||
break;
|
||
/* Handle SOP' Transmission Errors */
|
||
@@ -2878,11 +3058,11 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||
* discovery process on SOP only.
|
||
*/
|
||
case CMD_DISCOVER_IDENT:
|
||
- port->vdo_data[0] = 0;
|
||
response[0] = VDO(USB_SID_PD, 1,
|
||
typec_get_negotiated_svdm_version(
|
||
port->typec_port),
|
||
CMD_DISCOVER_SVID);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT);
|
||
tcpm_queue_vdm(port, response[0], &response[1],
|
||
0, TCPC_TX_SOP);
|
||
break;
|
||
@@ -2892,9 +3072,11 @@ static void vdm_run_state_machine(struct tcpm_port *port)
|
||
*/
|
||
case CMD_DISCOVER_SVID:
|
||
tcpm_register_partner_altmodes(port);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
break;
|
||
case CMD_DISCOVER_MODES:
|
||
tcpm_register_partner_altmodes(port);
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
break;
|
||
default:
|
||
break;
|
||
@@ -3807,7 +3989,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
|
||
PD_MSG_CTRL_NOT_SUPP,
|
||
NONE_AMS);
|
||
} else {
|
||
- if (port->send_discover && port->negotiated_rev < PD_REV30) {
|
||
+ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN &&
|
||
+ port->negotiated_rev < PD_REV30) {
|
||
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
|
||
break;
|
||
}
|
||
@@ -3823,7 +4006,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
|
||
PD_MSG_CTRL_NOT_SUPP,
|
||
NONE_AMS);
|
||
} else {
|
||
- if (port->send_discover && port->negotiated_rev < PD_REV30) {
|
||
+ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN &&
|
||
+ port->negotiated_rev < PD_REV30) {
|
||
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
|
||
break;
|
||
}
|
||
@@ -3832,7 +4016,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
|
||
}
|
||
break;
|
||
case PD_CTRL_VCONN_SWAP:
|
||
- if (port->send_discover && port->negotiated_rev < PD_REV30) {
|
||
+ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN &&
|
||
+ port->negotiated_rev < PD_REV30) {
|
||
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
|
||
break;
|
||
}
|
||
@@ -3891,6 +4076,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
|
||
{
|
||
enum pd_ext_msg_type type = pd_header_type_le(msg->header);
|
||
unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header);
|
||
+ const struct pd_chunked_ext_message_data *ext_msg = &msg->ext_msg;
|
||
|
||
/* stopping VDM state machine if interrupted by other Messages */
|
||
if (tcpm_vdm_ams(port)) {
|
||
@@ -3899,7 +4085,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
|
||
mod_vdm_delayed_work(port, 0);
|
||
}
|
||
|
||
- if (!(le16_to_cpu(msg->ext_msg.header) & PD_EXT_HDR_CHUNKED)) {
|
||
+ if (!(le16_to_cpu(ext_msg->header) & PD_EXT_HDR_CHUNKED)) {
|
||
tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS);
|
||
tcpm_log(port, "Unchunked extended messages unsupported");
|
||
return;
|
||
@@ -3924,9 +4110,25 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
|
||
NONE_AMS, 0);
|
||
}
|
||
break;
|
||
- case PD_EXT_SOURCE_CAP_EXT:
|
||
- case PD_EXT_GET_BATT_CAP:
|
||
case PD_EXT_GET_BATT_STATUS:
|
||
+ if (data_size >= 1) {
|
||
+ port->batt_request_id = ext_msg->data[0];
|
||
+ tcpm_pd_handle_msg(port, PD_MSG_DATA_BATT_STATUS,
|
||
+ GETTING_BATTERY_STATUS);
|
||
+ } else {
|
||
+ tcpm_set_state(port, SOFT_RESET_SEND, 0);
|
||
+ }
|
||
+ break;
|
||
+ case PD_EXT_GET_BATT_CAP:
|
||
+ if (data_size >= 1) {
|
||
+ port->batt_request_id = ext_msg->data[0];
|
||
+ tcpm_pd_handle_msg(port, PD_MSG_EXT_BATT_CAP,
|
||
+ GETTING_BATTERY_CAPABILITIES);
|
||
+ } else {
|
||
+ tcpm_set_state(port, SOFT_RESET_SEND, 0);
|
||
+ }
|
||
+ break;
|
||
+ case PD_EXT_SOURCE_CAP_EXT:
|
||
case PD_EXT_BATT_CAP:
|
||
case PD_EXT_GET_MANUFACTURER_INFO:
|
||
case PD_EXT_MANUFACTURER_INFO:
|
||
@@ -4137,6 +4339,22 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
|
||
ret);
|
||
tcpm_ams_finish(port);
|
||
break;
|
||
+ case PD_MSG_DATA_BATT_STATUS:
|
||
+ ret = tcpm_pd_send_batt_status(port);
|
||
+ if (ret)
|
||
+ tcpm_log(port,
|
||
+ "Failed to send battery status ret=%d",
|
||
+ ret);
|
||
+ tcpm_ams_finish(port);
|
||
+ break;
|
||
+ case PD_MSG_EXT_BATT_CAP:
|
||
+ ret = tcpm_pd_send_batt_cap(port);
|
||
+ if (ret)
|
||
+ tcpm_log(port,
|
||
+ "Failed to send battery cap ret=%d",
|
||
+ ret);
|
||
+ tcpm_ams_finish(port);
|
||
+ break;
|
||
default:
|
||
break;
|
||
}
|
||
@@ -4853,8 +5071,7 @@ static int tcpm_src_attach(struct tcpm_port *port)
|
||
port->partner = NULL;
|
||
|
||
port->attached = true;
|
||
- port->send_discover = true;
|
||
- port->send_discover_prime = false;
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN);
|
||
|
||
return 0;
|
||
|
||
@@ -4896,11 +5113,11 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port)
|
||
struct pd_mode_data *modep_prime = &port->mode_data_prime;
|
||
int i;
|
||
|
||
- for (i = 0; i < modep->altmodes; i++) {
|
||
+ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) {
|
||
typec_unregister_altmode(port->partner_altmode[i]);
|
||
port->partner_altmode[i] = NULL;
|
||
}
|
||
- for (i = 0; i < modep_prime->altmodes; i++) {
|
||
+ for (i = 0; i < modep_prime->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) {
|
||
typec_unregister_altmode(port->plug_prime_altmode[i]);
|
||
port->plug_prime_altmode[i] = NULL;
|
||
}
|
||
@@ -4931,6 +5148,8 @@ static void tcpm_reset_port(struct tcpm_port *port)
|
||
port->in_ams = false;
|
||
port->ams = NONE_AMS;
|
||
port->vdm_sm_running = false;
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN);
|
||
+ mod_vdm_discovery_cancel_delayed_work(port);
|
||
tcpm_unregister_altmodes(port);
|
||
tcpm_typec_disconnect(port);
|
||
port->attached = false;
|
||
@@ -5014,8 +5233,7 @@ static int tcpm_snk_attach(struct tcpm_port *port)
|
||
port->partner = NULL;
|
||
|
||
port->attached = true;
|
||
- port->send_discover = true;
|
||
- port->send_discover_prime = false;
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN);
|
||
|
||
return 0;
|
||
}
|
||
@@ -5422,16 +5640,11 @@ static void run_state_machine(struct tcpm_port *port)
|
||
* as well.
|
||
*/
|
||
if (port->explicit_contract) {
|
||
- if (port->send_discover_prime) {
|
||
- port->tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
- } else {
|
||
- port->tx_sop_type = TCPC_TX_SOP;
|
||
+ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN)
|
||
tcpm_set_initial_svdm_version(port);
|
||
- }
|
||
- mod_send_discover_delayed_work(port, 0);
|
||
+ mod_vdm_discovery_delayed_work(port, 0);
|
||
} else {
|
||
- port->send_discover = false;
|
||
- port->send_discover_prime = false;
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
}
|
||
|
||
/*
|
||
@@ -5814,16 +6027,11 @@ static void run_state_machine(struct tcpm_port *port)
|
||
* as well.
|
||
*/
|
||
if (port->explicit_contract) {
|
||
- if (port->send_discover_prime) {
|
||
- port->tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
- } else {
|
||
- port->tx_sop_type = TCPC_TX_SOP;
|
||
+ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN)
|
||
tcpm_set_initial_svdm_version(port);
|
||
- }
|
||
- mod_send_discover_delayed_work(port, 0);
|
||
+ mod_vdm_discovery_delayed_work(port, 0);
|
||
} else {
|
||
- port->send_discover = false;
|
||
- port->send_discover_prime = false;
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
}
|
||
|
||
power_supply_changed(port->psy);
|
||
@@ -5869,8 +6077,8 @@ static void run_state_machine(struct tcpm_port *port)
|
||
port->tcpc->set_pd_rx(port->tcpc, false);
|
||
tcpm_unregister_altmodes(port);
|
||
port->nr_sink_caps = 0;
|
||
- port->send_discover = true;
|
||
- port->send_discover_prime = false;
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN);
|
||
+ mod_vdm_discovery_cancel_delayed_work(port);
|
||
if (port->pwr_role == TYPEC_SOURCE)
|
||
tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF,
|
||
PD_T_PS_HARD_RESET);
|
||
@@ -6017,25 +6225,15 @@ static void run_state_machine(struct tcpm_port *port)
|
||
/* DR_Swap states */
|
||
case DR_SWAP_SEND:
|
||
tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP);
|
||
- if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) {
|
||
- port->send_discover = true;
|
||
- port->send_discover_prime = false;
|
||
- }
|
||
tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT,
|
||
PD_T_SENDER_RESPONSE);
|
||
break;
|
||
case DR_SWAP_ACCEPT:
|
||
tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP);
|
||
- if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) {
|
||
- port->send_discover = true;
|
||
- port->send_discover_prime = false;
|
||
- }
|
||
tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0);
|
||
break;
|
||
case DR_SWAP_SEND_TIMEOUT:
|
||
tcpm_swap_complete(port, -ETIMEDOUT);
|
||
- port->send_discover = false;
|
||
- port->send_discover_prime = false;
|
||
tcpm_ams_finish(port);
|
||
tcpm_set_state(port, ready_state(port), 0);
|
||
break;
|
||
@@ -6047,6 +6245,8 @@ static void run_state_machine(struct tcpm_port *port)
|
||
else
|
||
tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role,
|
||
TYPEC_HOST);
|
||
+ if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20)
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN);
|
||
tcpm_ams_finish(port);
|
||
tcpm_set_state(port, ready_state(port), 0);
|
||
break;
|
||
@@ -6330,9 +6530,7 @@ static void run_state_machine(struct tcpm_port *port)
|
||
|
||
/* Cable states */
|
||
case SRC_VDM_IDENTITY_REQUEST:
|
||
- port->send_discover_prime = true;
|
||
- port->tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
- mod_send_discover_delayed_work(port, 0);
|
||
+ mod_vdm_discovery_delayed_work(port, 0);
|
||
port->upcoming_state = SRC_SEND_CAPABILITIES;
|
||
break;
|
||
|
||
@@ -7052,8 +7250,8 @@ static void tcpm_enable_frs_work(struct kthread_work *work)
|
||
goto unlock;
|
||
|
||
/* Send when the state machine is idle */
|
||
- if (port->state != SNK_READY || port->vdm_sm_running || port->send_discover ||
|
||
- port->send_discover_prime)
|
||
+ if (port->state != SNK_READY || port->vdm_sm_running ||
|
||
+ port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN)
|
||
goto resched;
|
||
|
||
port->upcoming_state = GET_SINK_CAP;
|
||
@@ -7070,29 +7268,160 @@ static void tcpm_enable_frs_work(struct kthread_work *work)
|
||
mutex_unlock(&port->lock);
|
||
}
|
||
|
||
-static void tcpm_send_discover_work(struct kthread_work *work)
|
||
+static void tcpm_vdm_discovery_work(struct kthread_work *work)
|
||
{
|
||
- struct tcpm_port *port = container_of(work, struct tcpm_port, send_discover_work);
|
||
+ struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_discovery_work);
|
||
+ enum tcpm_transmit_type tx_sop_type = TCPC_TX_SOP;
|
||
+ struct typec_port *typec = port->typec_port;
|
||
+ struct pd_mode_data *modep, *modep_prime;
|
||
+ u32 msg[2] = { };
|
||
+ int svdm_version;
|
||
|
||
mutex_lock(&port->lock);
|
||
- /* No need to send DISCOVER_IDENTITY anymore */
|
||
- if (!port->send_discover && !port->send_discover_prime)
|
||
- goto unlock;
|
||
|
||
- if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) {
|
||
- port->send_discover = false;
|
||
- port->send_discover_prime = false;
|
||
+ tcpm_log_force(port, "%s state [%s]", __func__,
|
||
+ vdm_discovery_state_strings[port->vdm_discovery_state]);
|
||
+
|
||
+ /* No need to perform work if Discovery process is complete */
|
||
+ if (port->vdm_discovery_state == VDM_DISCOVERY_COMPLETE)
|
||
goto unlock;
|
||
- }
|
||
|
||
/* Retry if the port is not idle */
|
||
- if ((port->state != SRC_READY && port->state != SNK_READY &&
|
||
- port->state != SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) {
|
||
- mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS);
|
||
+ if (!tcpm_can_send_vdm(port->state) || port->vdm_sm_running) {
|
||
+ mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS);
|
||
goto unlock;
|
||
}
|
||
|
||
- tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop_type);
|
||
+ modep = &port->mode_data;
|
||
+ modep_prime = &port->mode_data_prime;
|
||
+
|
||
+ svdm_version = typec_get_negotiated_svdm_version(typec);
|
||
+
|
||
+ switch (port->vdm_discovery_state) {
|
||
+ /*
|
||
+ * The port has not received a Discover Identity response from the port partner.
|
||
+ *
|
||
+ * 1. The port will send Discover Identity to the partner over SOP in the SRC_READY and
|
||
+ * SNK_READY states if there is an explicit contract
|
||
+ * 2. The port will send Discover Identity to the cable over SOP' in the
|
||
+ * SRC_VDM_IDENTITY_REQUEST state if capable of doing so.
|
||
+ */
|
||
+ case VDM_DISCOVERY_UNKNOWN:
|
||
+ /* Can't send Discover Identity, VDM discovery is complete */
|
||
+ if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) {
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
+ goto unlock;
|
||
+ }
|
||
+
|
||
+ if (port->state == SRC_VDM_IDENTITY_REQUEST) {
|
||
+ tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
+ svdm_version = SVDM_VER_MAX;
|
||
+ }
|
||
+
|
||
+ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT);
|
||
+ break;
|
||
+ /*
|
||
+ * The port has received a Discover Identity ACK from the port partner.
|
||
+ *
|
||
+ * 1. The port will send Discover Identity to the cable over SOP' in the SRC_READY and
|
||
+ * SNK_READY states if it did not previously discover the cable but is capable of doing
|
||
+ * so.
|
||
+ * 2. The port will send Discover SVIDs to the partner over SOP in the SRC_READY and
|
||
+ * SNK_READY states otherwise.
|
||
+ */
|
||
+ case VDM_DISCOVERY_PARTNER_IDENT:
|
||
+ if (tcpm_can_communicate_sop_prime(port) && !port->cable) {
|
||
+ tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
+ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT);
|
||
+ } else {
|
||
+ if (tcpm_can_communicate_sop_prime(port))
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT);
|
||
+ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
|
||
+ }
|
||
+ break;
|
||
+ /*
|
||
+ * The port has received a Discover Identity ACK from the cable.
|
||
+ *
|
||
+ * 1. The port will send Discover SVIDs to the partner over SOP.
|
||
+ */
|
||
+ case VDM_DISCOVERY_CABLE_IDENT:
|
||
+ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
|
||
+ break;
|
||
+ /*
|
||
+ * The port has received a Discover SVIDs ACK from the partner or the last SVIDs supported
|
||
+ * by the partner.
|
||
+ *
|
||
+ * 1. The port will send Discover Modes for the first SVID over SOP if the partner supports
|
||
+ * modal operation and valid SVIDs were registered.
|
||
+ * 2. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise.
|
||
+ */
|
||
+ case VDM_DISCOVERY_PARTNER_SVIDS:
|
||
+ if (modep->nsvids && supports_modal(port)) {
|
||
+ msg[0] = VDO(modep->svids[0], 1, svdm_version, CMD_DISCOVER_MODES);
|
||
+ } else {
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
+ goto unlock;
|
||
+ }
|
||
+ break;
|
||
+ /*
|
||
+ * The port has received a Discover Modes ACK from the partner for any mode.
|
||
+ *
|
||
+ * 1. The port will send Discover Modes for the next SVID that has not been discovered to
|
||
+ * the port partner over SOP.
|
||
+ * 2. The port will send Discover SVIDs over SOP' if the port can communicate over SOP'
|
||
+ * and the cable supports VDMs.
|
||
+ * 3. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise.
|
||
+ */
|
||
+ case VDM_DISCOVERY_PARTNER_MODES:
|
||
+ /* Not all modes have been discovered yet */
|
||
+ if (modep->svid_index < modep->nsvids) {
|
||
+ msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version,
|
||
+ CMD_DISCOVER_MODES);
|
||
+ } else if (tcpm_can_communicate_sop_prime(port) && tcpm_cable_vdm_supported(port)) {
|
||
+ tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
+ svdm_version = typec_get_cable_svdm_version(typec);
|
||
+ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
|
||
+ } else {
|
||
+ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE);
|
||
+ goto unlock;
|
||
+ }
|
||
+ break;
|
||
+ /*
|
||
+ * The port has received a Discover SVIDs ACK from the cable over SOP'.
|
||
+ *
|
||
+ * 1. The port will send Discover Modes for the first SVID over SOP'.
|
||
+ */
|
||
+ case VDM_DISCOVERY_CABLE_SVIDS:
|
||
+ if (modep_prime->nsvids) {
|
||
+ tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
+ svdm_version = typec_get_cable_svdm_version(typec);
|
||
+ msg[0] = VDO(modep_prime->svids[0], 1, svdm_version, CMD_DISCOVER_MODES);
|
||
+ } else {
|
||
+ goto unlock;
|
||
+ }
|
||
+ break;
|
||
+ /*
|
||
+ * The port has received a Discover Modes ACK from the cable for any mode.
|
||
+ *
|
||
+ * 1. The port will send Discover Modes for the next SVID that has not been discovered to
|
||
+ * the cable over SOP'.
|
||
+ */
|
||
+ case VDM_DISCOVERY_CABLE_MODES:
|
||
+ if (modep_prime->svid_index < modep_prime->nsvids) {
|
||
+ tx_sop_type = TCPC_TX_SOP_PRIME;
|
||
+ svdm_version = typec_get_cable_svdm_version(typec);
|
||
+ msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version,
|
||
+ CMD_DISCOVER_MODES);
|
||
+ } else {
|
||
+ goto unlock;
|
||
+ }
|
||
+ break;
|
||
+ default:
|
||
+ goto unlock;
|
||
+ }
|
||
+
|
||
+ if (svdm_version >= 0)
|
||
+ tcpm_queue_vdm(port, msg[0], &msg[1], 0, tx_sop_type);
|
||
|
||
unlock:
|
||
mutex_unlock(&port->lock);
|
||
@@ -8508,12 +8837,12 @@ static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer)
|
||
return HRTIMER_NORESTART;
|
||
}
|
||
|
||
-static enum hrtimer_restart send_discover_timer_handler(struct hrtimer *timer)
|
||
+static enum hrtimer_restart vdm_discovery_timer_handler(struct hrtimer *timer)
|
||
{
|
||
- struct tcpm_port *port = container_of(timer, struct tcpm_port, send_discover_timer);
|
||
+ struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_discovery_timer);
|
||
|
||
if (port->registered)
|
||
- kthread_queue_work(port->wq, &port->send_discover_work);
|
||
+ kthread_queue_work(port->wq, &port->vdm_discovery_work);
|
||
return HRTIMER_NORESTART;
|
||
}
|
||
|
||
@@ -8547,14 +8876,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
||
kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work);
|
||
kthread_init_work(&port->event_work, tcpm_pd_event_handler);
|
||
kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
|
||
- kthread_init_work(&port->send_discover_work, tcpm_send_discover_work);
|
||
+ kthread_init_work(&port->vdm_discovery_work, tcpm_vdm_discovery_work);
|
||
hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC,
|
||
HRTIMER_MODE_REL);
|
||
hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler,
|
||
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||
hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC,
|
||
HRTIMER_MODE_REL);
|
||
- hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC,
|
||
+ hrtimer_setup(&port->vdm_discovery_timer, vdm_discovery_timer_handler, CLOCK_MONOTONIC,
|
||
HRTIMER_MODE_REL);
|
||
|
||
spin_lock_init(&port->pd_event_lock);
|
||
@@ -8649,11 +8978,12 @@ void tcpm_unregister_port(struct tcpm_port *port)
|
||
port->registered = false;
|
||
kthread_destroy_worker(port->wq);
|
||
|
||
- hrtimer_cancel(&port->send_discover_timer);
|
||
+ hrtimer_cancel(&port->vdm_discovery_timer);
|
||
hrtimer_cancel(&port->enable_frs_timer);
|
||
hrtimer_cancel(&port->vdm_state_machine_timer);
|
||
hrtimer_cancel(&port->state_machine_timer);
|
||
|
||
+ power_supply_put_system_batteries(port->fixed_batt, port->fixed_batt_cnt);
|
||
tcpm_reset_port(port);
|
||
|
||
tcpm_port_unregister_pd(port);
|
||
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
|
||
--- a/drivers/usb/typec/tipd/core.c
|
||
+++ b/drivers/usb/typec/tipd/core.c
|
||
@@ -129,6 +129,7 @@ enum {
|
||
TPS_MODE_BIST,
|
||
TPS_MODE_DISC,
|
||
TPS_MODE_PTCH,
|
||
+ TPS_MODE_APP1,
|
||
};
|
||
|
||
static const char *const modes[] = {
|
||
@@ -137,6 +138,7 @@ static const char *const modes[] = {
|
||
[TPS_MODE_BIST] = "BIST",
|
||
[TPS_MODE_DISC] = "DISC",
|
||
[TPS_MODE_PTCH] = "PTCH",
|
||
+ [TPS_MODE_APP1] = "APP1",
|
||
};
|
||
|
||
/* Unrecognized commands will be replaced with "!CMD" */
|
||
@@ -158,6 +160,7 @@ struct tipd_data {
|
||
int (*init)(struct tps6598x *tps);
|
||
int (*switch_power_state)(struct tps6598x *tps, u8 target_state);
|
||
bool (*read_data_status)(struct tps6598x *tps);
|
||
+ bool (*read_power_status)(struct tps6598x *tps);
|
||
int (*reset)(struct tps6598x *tps);
|
||
int (*connect)(struct tps6598x *tps, u32 status);
|
||
};
|
||
@@ -629,6 +632,34 @@ static bool tps6598x_read_power_status(struct tps6598x *tps)
|
||
return true;
|
||
}
|
||
|
||
+/*
|
||
+ * TPS66993 deprecated Power_Status register (0x3F). BC1.2 is not supported
|
||
+ * and the remaining bits are redundant with STATUS register (0x1A).
|
||
+ * Synthesize pwr_status from the already-read STATUS register.
|
||
+ */
|
||
+static bool tps66993_read_power_status(struct tps6598x *tps)
|
||
+{
|
||
+ u16 pwr_status = 0;
|
||
+
|
||
+ /* Same masks as TPS_POWER_STATUS_CONNECTION() / SOURCESINK() / PWROPMODE() in tps6598x.h */
|
||
+ if (tps->status & TPS_STATUS_PLUG_PRESENT)
|
||
+ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_CONNECTION_MASK, 1);
|
||
+
|
||
+ /* SOURCESINK: 1=sink; STATUS.PortRole 1=source, opposite convention */
|
||
+ if (!TPS_STATUS_TO_TYPEC_PORTROLE(tps->status))
|
||
+ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_SOURCESINK_MASK, 1);
|
||
+
|
||
+ if (TPS_STATUS_VBUS_STATUS(tps->status) == TPS_STATUS_VBUS_STATUS_PD)
|
||
+ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_TYPEC_CURRENT_MASK,
|
||
+ TPS_POWER_STATUS_TYPEC_CURRENT_PD);
|
||
+
|
||
+ tps->pwr_status = pwr_status;
|
||
+
|
||
+ tps->data->trace_power_status(pwr_status);
|
||
+
|
||
+ return true;
|
||
+}
|
||
+
|
||
static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status)
|
||
{
|
||
int ret;
|
||
@@ -906,7 +937,7 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
|
||
goto err_unlock;
|
||
|
||
if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) {
|
||
- if (!tps6598x_read_power_status(tps))
|
||
+ if (!tps->data->read_power_status(tps))
|
||
goto err_unlock;
|
||
if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) {
|
||
if (tps6598x_read_partner_identity(tps)) {
|
||
@@ -961,7 +992,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data)
|
||
goto err_clear_ints;
|
||
|
||
if (event[0] & TPS_REG_INT_POWER_STATUS_UPDATE)
|
||
- if (!tps6598x_read_power_status(tps))
|
||
+ if (!tps->data->read_power_status(tps))
|
||
goto err_clear_ints;
|
||
|
||
if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE)
|
||
@@ -1034,17 +1065,25 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data)
|
||
if (!tps6598x_read_status(tps, &status))
|
||
goto err_unlock;
|
||
|
||
+ tps->status = status;
|
||
+
|
||
if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE)
|
||
- if (!tps6598x_read_power_status(tps))
|
||
+ if (!tps->data->read_power_status(tps))
|
||
goto err_unlock;
|
||
|
||
if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE)
|
||
if (!tps->data->read_data_status(tps))
|
||
goto err_unlock;
|
||
|
||
- /* Handle plug insert or removal */
|
||
- if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT)
|
||
+ /*
|
||
+ * Refresh power status before connect - needed for TPS66993 which
|
||
+ * synthesizes pwr_status from STATUS and never gets POWER_STATUS_UPDATE.
|
||
+ */
|
||
+ if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) {
|
||
+ if (!tps->data->read_power_status(tps))
|
||
+ goto err_unlock;
|
||
tps6598x_handle_plug_event(tps, status);
|
||
+ }
|
||
|
||
err_unlock:
|
||
mutex_unlock(&tps->lock);
|
||
@@ -1080,6 +1119,7 @@ static int tps6598x_check_mode(struct tps6598x *tps)
|
||
|
||
switch (ret) {
|
||
case TPS_MODE_APP:
|
||
+ case TPS_MODE_APP1:
|
||
case TPS_MODE_PTCH:
|
||
return ret;
|
||
case TPS_MODE_BOOT:
|
||
@@ -1753,7 +1793,7 @@ static int tps6598x_probe(struct i2c_client *client)
|
||
struct tps6598x *tps;
|
||
struct fwnode_handle *fwnode;
|
||
u32 status;
|
||
- u32 vid;
|
||
+ u32 vid = 0;
|
||
int ret;
|
||
|
||
data = i2c_get_match_data(client);
|
||
@@ -1781,8 +1821,11 @@ static int tps6598x_probe(struct i2c_client *client)
|
||
|
||
if (!device_is_compatible(tps->dev, "ti,tps25750")) {
|
||
ret = tps6598x_read32(tps, TPS_REG_VID, &vid);
|
||
- if (ret < 0 || !vid)
|
||
+ if (ret < 0 || !vid) {
|
||
+ dev_err(tps->dev, "failed to read vendor ID: %d, vid: %#x\n",
|
||
+ ret, vid);
|
||
return -ENODEV;
|
||
+ }
|
||
}
|
||
|
||
/*
|
||
@@ -1818,6 +1861,8 @@ static int tps6598x_probe(struct i2c_client *client)
|
||
goto err_clear_mask;
|
||
}
|
||
|
||
+ tps->status = status;
|
||
+
|
||
/*
|
||
* This fwnode has a "compatible" property, but is never populated as a
|
||
* struct device. Instead we simply parse it to read the properties.
|
||
@@ -1845,7 +1890,7 @@ static int tps6598x_probe(struct i2c_client *client)
|
||
|
||
if (status & TPS_STATUS_PLUG_PRESENT) {
|
||
ret = -EINVAL;
|
||
- if (!tps6598x_read_power_status(tps))
|
||
+ if (!tps->data->read_power_status(tps))
|
||
goto err_unregister_port;
|
||
if (!tps->data->read_data_status(tps))
|
||
goto err_unregister_port;
|
||
@@ -1860,7 +1905,7 @@ static int tps6598x_probe(struct i2c_client *client)
|
||
IRQF_SHARED | IRQF_ONESHOT,
|
||
dev_name(&client->dev), tps);
|
||
} else {
|
||
- dev_warn(tps->dev, "Unable to find the interrupt, switching to polling\n");
|
||
+ dev_dbg(tps->dev, "no IRQ specified, using polling mode\n");
|
||
INIT_DELAYED_WORK(&tps->wq_poll, tps6598x_poll_work);
|
||
queue_delayed_work(system_power_efficient_wq, &tps->wq_poll,
|
||
msecs_to_jiffies(POLL_INTERVAL));
|
||
@@ -1987,6 +2032,7 @@ static const struct tipd_data cd321x_data = {
|
||
.trace_status = trace_tps6598x_status,
|
||
.init = cd321x_init,
|
||
.read_data_status = cd321x_read_data_status,
|
||
+ .read_power_status = tps6598x_read_power_status,
|
||
.reset = cd321x_reset,
|
||
.switch_power_state = cd321x_switch_power_state,
|
||
.connect = cd321x_connect,
|
||
@@ -2006,6 +2052,25 @@ static const struct tipd_data tps6598x_data = {
|
||
.apply_patch = tps6598x_apply_patch,
|
||
.init = tps6598x_init,
|
||
.read_data_status = tps6598x_read_data_status,
|
||
+ .read_power_status = tps6598x_read_power_status,
|
||
+ .reset = tps6598x_reset,
|
||
+ .connect = tps6598x_connect,
|
||
+};
|
||
+
|
||
+static const struct tipd_data tps66993_data = {
|
||
+ .irq_handler = tps6598x_interrupt,
|
||
+ .irq_mask1 = TPS_REG_INT_DATA_STATUS_UPDATE |
|
||
+ TPS_REG_INT_PLUG_EVENT,
|
||
+ .tps_struct_size = sizeof(struct tps6598x),
|
||
+ .register_port = tps6598x_register_port,
|
||
+ .unregister_port = tps6598x_unregister_port,
|
||
+ .trace_data_status = trace_tps6598x_data_status,
|
||
+ .trace_power_status = trace_tps6598x_power_status,
|
||
+ .trace_status = trace_tps6598x_status,
|
||
+ .apply_patch = tps6598x_apply_patch,
|
||
+ .init = tps6598x_init,
|
||
+ .read_data_status = tps6598x_read_data_status,
|
||
+ .read_power_status = tps66993_read_power_status,
|
||
.reset = tps6598x_reset,
|
||
.connect = tps6598x_connect,
|
||
};
|
||
@@ -2024,12 +2089,14 @@ static const struct tipd_data tps25750_data = {
|
||
.apply_patch = tps25750_apply_patch,
|
||
.init = tps25750_init,
|
||
.read_data_status = tps6598x_read_data_status,
|
||
+ .read_power_status = tps6598x_read_power_status,
|
||
.reset = tps25750_reset,
|
||
.connect = tps6598x_connect,
|
||
};
|
||
|
||
static const struct of_device_id tps6598x_of_match[] = {
|
||
{ .compatible = "ti,tps6598x", &tps6598x_data},
|
||
+ { .compatible = "ti,tps66993", &tps66993_data},
|
||
{ .compatible = "apple,cd321x", &cd321x_data},
|
||
{ .compatible = "ti,tps25750", &tps25750_data},
|
||
{}
|
||
diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h
|
||
--- a/drivers/usb/typec/tipd/tps6598x.h
|
||
+++ b/drivers/usb/typec/tipd/tps6598x.h
|
||
@@ -142,9 +142,13 @@
|
||
#define TPS_SYSTEM_POWER_STATE_S4 0x04
|
||
#define TPS_SYSTEM_POWER_STATE_S5 0x05
|
||
|
||
-/* TPS_REG_POWER_STATUS bits */
|
||
-#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x))
|
||
-#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x))
|
||
+/* TPS_REG_POWER_STATUS bits (masks shared by TPS_FIELD_GET accessors and FIELD_PREP) */
|
||
+#define TPS_POWER_STATUS_CONNECTION_MASK BIT(0)
|
||
+#define TPS_POWER_STATUS_SOURCESINK_MASK BIT(1)
|
||
+#define TPS_POWER_STATUS_CONNECTION(x) \
|
||
+ TPS_FIELD_GET(TPS_POWER_STATUS_CONNECTION_MASK, (x))
|
||
+#define TPS_POWER_STATUS_SOURCESINK(x) \
|
||
+ TPS_FIELD_GET(TPS_POWER_STATUS_SOURCESINK_MASK, (x))
|
||
#define TPS_POWER_STATUS_BC12_DET(x) TPS_FIELD_GET(BIT(2), (x))
|
||
|
||
#define TPS_POWER_STATUS_TYPEC_CURRENT_MASK GENMASK(3, 2)
|
||
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
|
||
--- a/drivers/usb/typec/ucsi/debugfs.c
|
||
+++ b/drivers/usb/typec/ucsi/debugfs.c
|
||
@@ -82,8 +82,8 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used)
|
||
if (ucsi->debugfs->status)
|
||
return ucsi->debugfs->status;
|
||
|
||
- seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high,
|
||
- ucsi->debugfs->response.low);
|
||
+ seq_printf(s, "0x%016llx%016llx%016llx\n", ucsi->debugfs->response.ext,
|
||
+ ucsi->debugfs->response.high, ucsi->debugfs->response.low);
|
||
return 0;
|
||
}
|
||
DEFINE_SHOW_ATTRIBUTE(ucsi_resp);
|
||
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
|
||
--- a/drivers/usb/typec/ucsi/ucsi.c
|
||
+++ b/drivers/usb/typec/ucsi/ucsi.c
|
||
@@ -1102,8 +1102,8 @@ static int ucsi_register_plug(struct ucsi_connector *con)
|
||
plug = typec_register_plug(con->cable, &desc);
|
||
if (IS_ERR(plug)) {
|
||
dev_err(con->ucsi->dev,
|
||
- "con%d: failed to register plug (%ld)\n", con->num,
|
||
- PTR_ERR(plug));
|
||
+ "con%d: failed to register plug (%pe)\n", con->num,
|
||
+ plug);
|
||
return PTR_ERR(plug);
|
||
}
|
||
|
||
@@ -1161,8 +1161,8 @@ static int ucsi_register_cable(struct ucsi_connector *con)
|
||
cable = typec_register_cable(con->port, &desc);
|
||
if (IS_ERR(cable)) {
|
||
dev_err(con->ucsi->dev,
|
||
- "con%d: failed to register cable (%ld)\n", con->num,
|
||
- PTR_ERR(cable));
|
||
+ "con%d: failed to register cable (%pe)\n", con->num,
|
||
+ cable);
|
||
return PTR_ERR(cable);
|
||
}
|
||
|
||
@@ -1291,8 +1291,8 @@ static int ucsi_register_partner(struct ucsi_connector *con)
|
||
partner = typec_register_partner(con->port, &desc);
|
||
if (IS_ERR(partner)) {
|
||
dev_err(con->ucsi->dev,
|
||
- "con%d: failed to register partner (%ld)\n", con->num,
|
||
- PTR_ERR(partner));
|
||
+ "con%d: failed to register partner (%pe)\n", con->num,
|
||
+ partner);
|
||
return PTR_ERR(partner);
|
||
}
|
||
|
||
@@ -2221,7 +2221,7 @@ static void ucsi_init_work(struct work_struct *work)
|
||
return;
|
||
}
|
||
|
||
- queue_delayed_work(system_long_wq, &ucsi->work,
|
||
+ queue_delayed_work(system_dfl_long_wq, &ucsi->work,
|
||
UCSI_ROLE_SWITCH_INTERVAL);
|
||
}
|
||
}
|
||
@@ -2345,7 +2345,7 @@ int ucsi_register(struct ucsi *ucsi)
|
||
UCSI_BCD_GET_MINOR(ucsi->version),
|
||
UCSI_BCD_GET_SUBMINOR(ucsi->version));
|
||
|
||
- queue_delayed_work(system_long_wq, &ucsi->work, 0);
|
||
+ queue_delayed_work(system_dfl_long_wq, &ucsi->work, 0);
|
||
|
||
ucsi_debugfs_register(ucsi);
|
||
return 0;
|
||
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
|
||
--- a/drivers/usb/typec/ucsi/ucsi.h
|
||
+++ b/drivers/usb/typec/ucsi/ucsi.h
|
||
@@ -466,6 +466,7 @@ struct ucsi_debugfs_entry {
|
||
struct ucsi_data {
|
||
u64 low;
|
||
u64 high;
|
||
+ u64 ext;
|
||
} response;
|
||
int status;
|
||
u8 message_out[MESSAGE_OUT_MAX_LEN];
|
||
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
|
||
--- a/include/linux/power_supply.h
|
||
+++ b/include/linux/power_supply.h
|
||
@@ -806,11 +806,26 @@ extern int power_supply_reg_notifier(struct notifier_block *nb);
|
||
extern void power_supply_unreg_notifier(struct notifier_block *nb);
|
||
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
|
||
extern struct power_supply *power_supply_get_by_name(const char *name);
|
||
+extern int __must_check power_supply_get_system_batteries(struct device *dev,
|
||
+ struct power_supply ***psys);
|
||
+extern void power_supply_put_system_batteries(struct power_supply **psys, int count);
|
||
extern void power_supply_put(struct power_supply *psy);
|
||
#else
|
||
static inline void power_supply_put(struct power_supply *psy) {}
|
||
static inline struct power_supply *power_supply_get_by_name(const char *name)
|
||
{ return NULL; }
|
||
+static inline int __must_check power_supply_get_system_batteries(struct device *dev,
|
||
+ struct power_supply ***psys)
|
||
+{
|
||
+ if (psys)
|
||
+ *psys = NULL;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static inline void power_supply_put_system_batteries(struct power_supply **psys,
|
||
+ int count)
|
||
+{
|
||
+}
|
||
#endif
|
||
extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *fwnode,
|
||
const char *property);
|
||
diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h
|
||
--- a/include/linux/usb/pd.h
|
||
+++ b/include/linux/usb/pd.h
|
||
@@ -106,6 +106,9 @@ enum pd_ext_msg_type {
|
||
#define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \
|
||
cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0)))
|
||
|
||
+#define PD_HEADER_EXT_LE(type, pwr, data, rev, id, cnt) \
|
||
+ cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (1)))
|
||
+
|
||
static inline unsigned int pd_header_cnt(u16 header)
|
||
{
|
||
return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK;
|
||
@@ -219,6 +222,25 @@ static inline u8 count_chunked_data_objs(u32 size)
|
||
return ((size / 4) + (size % 4 ? 1 : 0));
|
||
}
|
||
|
||
+/**
|
||
+ * batt_cap_ext_msg - Battery capability extended PD message
|
||
+ * @vid: Battery Vendor ID (assigned by USB-IF)
|
||
+ * @pid: Battery Product ID (assigned by battery or device vendor)
|
||
+ * @batt_design_cap: Battery design capacity in 0.1Wh
|
||
+ * @batt_last_chg_cap: Battery last full charge capacity in 0.1Wh
|
||
+ * @batt_type: Battery Type. bit0 when set indicates invalid battery reference.
|
||
+ * Rest of the bits are reserved.
|
||
+ */
|
||
+struct batt_cap_ext_msg {
|
||
+ __le16 vid;
|
||
+ __le16 pid;
|
||
+ __le16 batt_design_cap;
|
||
+ __le16 batt_last_chg_cap;
|
||
+ u8 batt_type;
|
||
+} __packed;
|
||
+
|
||
+#define BATT_CAP_BATT_TYPE_INVALID_REF BIT(0)
|
||
+
|
||
/* Sink Caps Extended Data Block Version */
|
||
#define SKEDB_VER_1_0 1
|
||
|
||
@@ -724,4 +746,33 @@ void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct devi
|
||
|
||
#endif /* CONFIG_TYPEC */
|
||
|
||
+/* Battery Status Data Object */
|
||
+#define BSDO_PRESENT_CAPACITY GENMASK(31, 16)
|
||
+#define BSDO_CHG_STATUS GENMASK(11, 10)
|
||
+#define BSDO_BATTERY_PRESENT BIT(9)
|
||
+#define BSDO_INVALID_BATTERY_REFERENCE BIT(8)
|
||
+
|
||
+/*
|
||
+ * Battery Charge Status: Battery Charging Status Values as defined in
|
||
+ * "USB PD Spec Rev3.1 Ver1.8", "Table 6-46 Battery Status Data Object (BSDO)".
|
||
+ */
|
||
+#define BSDO_BATTERY_INFO_CHARGING 0x0
|
||
+#define BSDO_BATTERY_INFO_DISCHARGING 0x1
|
||
+#define BSDO_BATTERY_INFO_IDLE 0x2
|
||
+#define BSDO_BATTERY_INFO_RSVD 0x3
|
||
+
|
||
+/**
|
||
+ * BSDO() - Pack data into Battery Status Data Object format.
|
||
+ * @batt_charge: Battery's present state of charge in 0.1WH increment.
|
||
+ * @chg_status: Battery charge status.
|
||
+ * @batt_present: Indicates that battery is present/attached when set else absent when unset.
|
||
+ * @invalid_ref: Indicates that an invalid battery reference was made in the Get_Battery_Status
|
||
+ * request.
|
||
+ */
|
||
+#define BSDO(batt_charge, chg_status, batt_present, invalid_ref) \
|
||
+ ((FIELD_PREP(BSDO_PRESENT_CAPACITY, batt_charge)) | \
|
||
+ (FIELD_PREP(BSDO_CHG_STATUS, chg_status)) | \
|
||
+ ((batt_present) ? BSDO_BATTERY_PRESENT : 0) | \
|
||
+ ((invalid_ref) ? BSDO_INVALID_BATTERY_REFERENCE : 0))
|
||
+
|
||
#endif /* __LINUX_USB_PD_H */
|
||
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
|
||
--- a/include/linux/usb/typec.h
|
||
+++ b/include/linux/usb/typec.h
|
||
@@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable);
|
||
struct typec_cable *typec_cable_get(struct typec_port *port);
|
||
void typec_cable_put(struct typec_cable *cable);
|
||
int typec_cable_is_active(struct typec_cable *cable);
|
||
+bool typec_cable_altmode_unsupported(struct typec_altmode *alt);
|
||
|
||
struct typec_plug *typec_register_plug(struct typec_cable *cable,
|
||
struct typec_plug_desc *desc);
|
||
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
|
||
--- a/include/uapi/linux/usb/functionfs.h
|
||
+++ b/include/uapi/linux/usb/functionfs.h
|
||
@@ -25,6 +25,7 @@ enum functionfs_flags {
|
||
FUNCTIONFS_EVENTFD = 32,
|
||
FUNCTIONFS_ALL_CTRL_RECIP = 64,
|
||
FUNCTIONFS_CONFIG0_SETUP = 128,
|
||
+ FUNCTIONFS_RW_PROXY_EPS = 256,
|
||
};
|
||
|
||
/* Descriptor of an non-audio endpoint */
|
||
@@ -414,4 +415,27 @@ struct usb_functionfs_event {
|
||
#define FUNCTIONFS_DMABUF_TRANSFER _IOW('g', 133, \
|
||
struct usb_ffs_dmabuf_transfer_req)
|
||
|
||
+/*
|
||
+ * Enable or disable automatic zero-length packet (ZLP) appending for the
|
||
+ * endpoint. The argument is a pointer to a __u32: 0 to disable, non-zero to
|
||
+ * enable.
|
||
+ *
|
||
+ * When enabled, the kernel will automatically append a ZLP at the end of
|
||
+ * a transfer if the payload length is an exact multiple of the endpoint's
|
||
+ * max packet size.
|
||
+ *
|
||
+ * This is useful for compatibility with legacy protocols which require
|
||
+ * automatic ZLP appending to data written from userspace.
|
||
+ *
|
||
+ * This ioctl can only be used on IN endpoints. It can be called at any time
|
||
+ * after the FunctionFS instance is active, even before the host has connected
|
||
+ * or enabled the endpoint.
|
||
+ *
|
||
+ * Returns zero on success, or a negative errno value on error:
|
||
+ * -ENODEV: The FunctionFS instance is not active.
|
||
+ * -EINVAL: The endpoint is not an IN endpoint.
|
||
+ * -EFAULT: Invalid user space pointer for the argument.
|
||
+ */
|
||
+#define FUNCTIONFS_ENDPOINT_ENABLE_ZLP _IOW('g', 134, __u32)
|
||
+
|
||
#endif /* _UAPI__LINUX_FUNCTIONFS_H__ */
|
||
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
|
||
--- a/rust/kernel/usb.rs
|
||
+++ b/rust/kernel/usb.rs
|
||
@@ -24,11 +24,8 @@
|
||
};
|
||
use core::{
|
||
marker::PhantomData,
|
||
- mem::{
|
||
- offset_of,
|
||
- MaybeUninit, //
|
||
- },
|
||
- ptr::NonNull,
|
||
+ mem::offset_of,
|
||
+ ptr::NonNull, //
|
||
};
|
||
|
||
/// An adapter for the registration of USB drivers.
|
||
@@ -130,8 +127,7 @@ pub const fn from_id(vendor: u16, product: u16) -> Self {
|
||
match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16,
|
||
idVendor: vendor,
|
||
idProduct: product,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -143,8 +139,7 @@ pub const fn from_device_ver(vendor: u16, product: u16, bcd_lo: u16, bcd_hi: u16
|
||
idProduct: product,
|
||
bcdDevice_lo: bcd_lo,
|
||
bcdDevice_hi: bcd_hi,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -155,8 +150,7 @@ pub const fn from_device_info(class: u8, subclass: u8, protocol: u8) -> Self {
|
||
bDeviceClass: class,
|
||
bDeviceSubClass: subclass,
|
||
bDeviceProtocol: protocol,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -167,8 +161,7 @@ pub const fn from_interface_info(class: u8, subclass: u8, protocol: u8) -> Self
|
||
bInterfaceClass: class,
|
||
bInterfaceSubClass: subclass,
|
||
bInterfaceProtocol: protocol,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -180,8 +173,7 @@ pub const fn from_device_interface_class(vendor: u16, product: u16, class: u8) -
|
||
idVendor: vendor,
|
||
idProduct: product,
|
||
bInterfaceClass: class,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -193,8 +185,7 @@ pub const fn from_device_interface_protocol(vendor: u16, product: u16, protocol:
|
||
idVendor: vendor,
|
||
idProduct: product,
|
||
bInterfaceProtocol: protocol,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -206,8 +197,7 @@ pub const fn from_device_interface_number(vendor: u16, product: u16, number: u8)
|
||
idVendor: vendor,
|
||
idProduct: product,
|
||
bInterfaceNumber: number,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
|
||
@@ -227,8 +217,7 @@ pub const fn from_device_and_interface_info(
|
||
bInterfaceClass: class,
|
||
bInterfaceSubClass: subclass,
|
||
bInterfaceProtocol: protocol,
|
||
- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
|
||
- ..unsafe { MaybeUninit::zeroed().assume_init() }
|
||
+ ..pin_init::zeroed()
|
||
})
|
||
}
|
||
}
|
||
@@ -393,6 +382,7 @@ fn as_ref(&self) -> &Device {
|
||
|
||
// SAFETY: Instances of `Interface` are always reference-counted.
|
||
unsafe impl AlwaysRefCounted for Interface {
|
||
+ #[inline]
|
||
fn inc_ref(&self) {
|
||
// SAFETY: The invariants of `Interface` guarantee that `self.as_raw()`
|
||
// returns a valid `struct usb_interface` pointer, for which we will
|
||
@@ -400,6 +390,7 @@ fn inc_ref(&self) {
|
||
unsafe { bindings::usb_get_intf(self.as_raw()) };
|
||
}
|
||
|
||
+ #[inline]
|
||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||
unsafe { bindings::usb_put_intf(obj.cast().as_ptr()) }
|
||
@@ -444,6 +435,7 @@ fn as_raw(&self) -> *mut bindings::usb_device {
|
||
|
||
// SAFETY: Instances of `Device` are always reference-counted.
|
||
unsafe impl AlwaysRefCounted for Device {
|
||
+ #[inline]
|
||
fn inc_ref(&self) {
|
||
// SAFETY: The invariants of `Device` guarantee that `self.as_raw()`
|
||
// returns a valid `struct usb_device` pointer, for which we will
|
||
@@ -451,6 +443,7 @@ fn inc_ref(&self) {
|
||
unsafe { bindings::usb_get_dev(self.as_raw()) };
|
||
}
|
||
|
||
+ #[inline]
|
||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||
unsafe { bindings::usb_put_dev(obj.cast().as_ptr()) }
|