Files
omarchy-pkgs/pkgbuilds/linux-omarchy/0603-usb-updates.patch
T

5630 lines
187 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()) }