Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4669bb1 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'fixes-for-v4.2-rc2' of...

Merge tag 'fixes-for-v4.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb

 into usb-linus

Felipe writes:

usb: fixes for-v4.2-rc2

The first set of fixes for this -rc cycle. Most importantly
we have a NULL pointer dereference fix on DWC3, a fix to a
really old bug on musb_start() and another NULL pointer
dereference fix on MXS phy driver.

Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parents d770e558 b2e2c94b
Loading
Loading
Loading
Loading
+15 −40
Original line number Diff line number Diff line
@@ -72,17 +72,7 @@ static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
	dev_dbg(hsotg->dev, "%s\n", __func__);

	/* Backup Host regs */
	hr = hsotg->hr_backup;
	if (!hr) {
		hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL);
		if (!hr) {
			dev_err(hsotg->dev, "%s: can't allocate host regs\n",
					__func__);
			return -ENOMEM;
		}

		hsotg->hr_backup = hr;
	}
	hr = &hsotg->hr_backup;
	hr->hcfg = readl(hsotg->regs + HCFG);
	hr->haintmsk = readl(hsotg->regs + HAINTMSK);
	for (i = 0; i < hsotg->core_params->host_channels; ++i)
@@ -90,6 +80,7 @@ static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)

	hr->hprt0 = readl(hsotg->regs + HPRT0);
	hr->hfir = readl(hsotg->regs + HFIR);
	hr->valid = true;

	return 0;
}
@@ -109,12 +100,13 @@ static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
	dev_dbg(hsotg->dev, "%s\n", __func__);

	/* Restore host regs */
	hr = hsotg->hr_backup;
	if (!hr) {
	hr = &hsotg->hr_backup;
	if (!hr->valid) {
		dev_err(hsotg->dev, "%s: no host registers to restore\n",
				__func__);
		return -EINVAL;
	}
	hr->valid = false;

	writel(hr->hcfg, hsotg->regs + HCFG);
	writel(hr->haintmsk, hsotg->regs + HAINTMSK);
@@ -152,17 +144,7 @@ static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
	dev_dbg(hsotg->dev, "%s\n", __func__);

	/* Backup dev regs */
	dr = hsotg->dr_backup;
	if (!dr) {
		dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL);
		if (!dr) {
			dev_err(hsotg->dev, "%s: can't allocate device regs\n",
					__func__);
			return -ENOMEM;
		}

		hsotg->dr_backup = dr;
	}
	dr = &hsotg->dr_backup;

	dr->dcfg = readl(hsotg->regs + DCFG);
	dr->dctl = readl(hsotg->regs + DCTL);
@@ -195,7 +177,7 @@ static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
		dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i));
		dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i));
	}

	dr->valid = true;
	return 0;
}

@@ -215,12 +197,13 @@ static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
	dev_dbg(hsotg->dev, "%s\n", __func__);

	/* Restore dev regs */
	dr = hsotg->dr_backup;
	if (!dr) {
	dr = &hsotg->dr_backup;
	if (!dr->valid) {
		dev_err(hsotg->dev, "%s: no device registers to restore\n",
				__func__);
		return -EINVAL;
	}
	dr->valid = false;

	writel(dr->dcfg, hsotg->regs + DCFG);
	writel(dr->dctl, hsotg->regs + DCTL);
@@ -268,17 +251,7 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
	int i;

	/* Backup global regs */
	gr = hsotg->gr_backup;
	if (!gr) {
		gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL);
		if (!gr) {
			dev_err(hsotg->dev, "%s: can't allocate global regs\n",
					__func__);
			return -ENOMEM;
		}

		hsotg->gr_backup = gr;
	}
	gr = &hsotg->gr_backup;

	gr->gotgctl = readl(hsotg->regs + GOTGCTL);
	gr->gintmsk = readl(hsotg->regs + GINTMSK);
@@ -291,6 +264,7 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
	for (i = 0; i < MAX_EPS_CHANNELS; i++)
		gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i));

	gr->valid = true;
	return 0;
}

@@ -309,12 +283,13 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
	dev_dbg(hsotg->dev, "%s\n", __func__);

	/* Restore global regs */
	gr = hsotg->gr_backup;
	if (!gr) {
	gr = &hsotg->gr_backup;
	if (!gr->valid) {
		dev_err(hsotg->dev, "%s: no global registers to restore\n",
				__func__);
		return -EINVAL;
	}
	gr->valid = false;

	writel(0xffffffff, hsotg->regs + GINTSTS);
	writel(gr->gotgctl, hsotg->regs + GOTGCTL);
+6 −3
Original line number Diff line number Diff line
@@ -492,6 +492,7 @@ struct dwc2_gregs_backup {
	u32 gdfifocfg;
	u32 dtxfsiz[MAX_EPS_CHANNELS];
	u32 gpwrdn;
	bool valid;
};

/**
@@ -521,6 +522,7 @@ struct dwc2_dregs_backup {
	u32 doepctl[MAX_EPS_CHANNELS];
	u32 doeptsiz[MAX_EPS_CHANNELS];
	u32 doepdma[MAX_EPS_CHANNELS];
	bool valid;
};

/**
@@ -538,6 +540,7 @@ struct dwc2_hregs_backup {
	u32 hcintmsk[MAX_EPS_CHANNELS];
	u32 hprt0;
	u32 hfir;
	bool valid;
};

/**
@@ -705,9 +708,9 @@ struct dwc2_hsotg {
	struct work_struct wf_otg;
	struct timer_list wkp_timer;
	enum dwc2_lx_state lx_state;
	struct dwc2_gregs_backup *gr_backup;
	struct dwc2_dregs_backup *dr_backup;
	struct dwc2_hregs_backup *hr_backup;
	struct dwc2_gregs_backup gr_backup;
	struct dwc2_dregs_backup dr_backup;
	struct dwc2_hregs_backup hr_backup;

	struct dentry *debug_root;
	struct debugfs_regset32 *regset;
+42 −13
Original line number Diff line number Diff line
@@ -359,10 +359,9 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)

/* Caller must hold driver lock */
static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
				struct dwc2_hcd_urb *urb, void **ep_handle,
				gfp_t mem_flags)
				struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
				struct dwc2_qtd *qtd)
{
	struct dwc2_qtd *qtd;
	u32 intr_mask;
	int retval;
	int dev_speed;
@@ -386,18 +385,15 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
			return -ENODEV;
	}

	qtd = kzalloc(sizeof(*qtd), mem_flags);
	if (!qtd)
		return -ENOMEM;
		return -EINVAL;

	dwc2_hcd_qtd_init(qtd, urb);
	retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle,
				  mem_flags);
	retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);
	if (retval) {
		dev_err(hsotg->dev,
			"DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n",
			retval);
		kfree(qtd);
		return retval;
	}

@@ -2445,6 +2441,9 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
	u32 tflags = 0;
	void *buf;
	unsigned long flags;
	struct dwc2_qh *qh;
	bool qh_allocated = false;
	struct dwc2_qtd *qtd;

	if (dbg_urb(urb)) {
		dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n");
@@ -2523,15 +2522,32 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
						 urb->iso_frame_desc[i].length);

	urb->hcpriv = dwc2_urb;
	qh = (struct dwc2_qh *) ep->hcpriv;
	/* Create QH for the endpoint if it doesn't exist */
	if (!qh) {
		qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, mem_flags);
		if (!qh) {
			retval = -ENOMEM;
			goto fail0;
		}
		ep->hcpriv = qh;
		qh_allocated = true;
	}

	qtd = kzalloc(sizeof(*qtd), mem_flags);
	if (!qtd) {
		retval = -ENOMEM;
		goto fail1;
	}

	spin_lock_irqsave(&hsotg->lock, flags);
	retval = usb_hcd_link_urb_to_ep(hcd, urb);
	if (retval)
		goto fail1;
		goto fail2;

	retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &ep->hcpriv, mem_flags);
	retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, qtd);
	if (retval)
		goto fail2;
		goto fail3;

	if (alloc_bandwidth) {
		dwc2_allocate_bus_bandwidth(hcd,
@@ -2543,12 +2559,25 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,

	return 0;

fail2:
fail3:
	dwc2_urb->priv = NULL;
	usb_hcd_unlink_urb_from_ep(hcd, urb);
fail1:
fail2:
	spin_unlock_irqrestore(&hsotg->lock, flags);
	urb->hcpriv = NULL;
	kfree(qtd);
fail1:
	if (qh_allocated) {
		struct dwc2_qtd *qtd2, *qtd2_tmp;

		ep->hcpriv = NULL;
		dwc2_hcd_qh_unlink(hsotg, qh);
		/* Free each QTD in the QH's QTD list */
		list_for_each_entry_safe(qtd2, qtd2_tmp, &qh->qtd_list,
							 qtd_list_entry)
			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh);
		dwc2_hcd_qh_free(hsotg, qh);
	}
fail0:
	kfree(dwc2_urb);

+4 −1
Original line number Diff line number Diff line
@@ -463,6 +463,9 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
/* Schedule Queue Functions */
/* Implemented in hcd_queue.c */
extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
					  struct dwc2_hcd_urb *urb,
					  gfp_t mem_flags);
extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
@@ -471,7 +474,7 @@ extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,

extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
			    struct dwc2_qh **qh, gfp_t mem_flags);
			    struct dwc2_qh *qh);

/* Unlinks and frees a QTD */
static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
+12 −37
Original line number Diff line number Diff line
@@ -191,7 +191,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 *
 * Return: Pointer to the newly allocated QH, or NULL on error
 */
static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
					  struct dwc2_hcd_urb *urb,
					  gfp_t mem_flags)
{
@@ -767,57 +767,32 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
 *
 * @hsotg:        The DWC HCD structure
 * @qtd:          The QTD to add
 * @qh:           Out parameter to return queue head
 * @atomic_alloc: Flag to do atomic alloc if needed
 * @qh:           Queue head to add qtd to
 *
 * Return: 0 if successful, negative error code otherwise
 *
 * Finds the correct QH to place the QTD into. If it does not find a QH, it
 * will create a new QH. If the QH to which the QTD is added is not currently
 * scheduled, it is placed into the proper schedule based on its EP type.
 * If the QH to which the QTD is added is not currently scheduled, it is placed
 * into the proper schedule based on its EP type.
 */
int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
		     struct dwc2_qh **qh, gfp_t mem_flags)
		     struct dwc2_qh *qh)
{
	struct dwc2_hcd_urb *urb = qtd->urb;
	int allocated = 0;
	int retval;

	/*
	 * Get the QH which holds the QTD-list to insert to. Create QH if it
	 * doesn't exist.
	 */
	if (*qh == NULL) {
		*qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags);
		if (*qh == NULL)
			return -ENOMEM;
		allocated = 1;
	if (unlikely(!qh)) {
		dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
		retval = -EINVAL;
		goto fail;
	}

	retval = dwc2_hcd_qh_add(hsotg, *qh);
	retval = dwc2_hcd_qh_add(hsotg, qh);
	if (retval)
		goto fail;

	qtd->qh = *qh;
	list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
	qtd->qh = qh;
	list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);

	return 0;

fail:
	if (allocated) {
		struct dwc2_qtd *qtd2, *qtd2_tmp;
		struct dwc2_qh *qh_tmp = *qh;

		*qh = NULL;
		dwc2_hcd_qh_unlink(hsotg, qh_tmp);

		/* Free each QTD in the QH's QTD list */
		list_for_each_entry_safe(qtd2, qtd2_tmp, &qh_tmp->qtd_list,
					 qtd_list_entry)
			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);

		dwc2_hcd_qh_free(hsotg, qh_tmp);
	}

	return retval;
}
Loading