Пример #1
0
/**
 * ipa_data_disconnect() - Restore USB ep operation and disable USB endpoint
 * @gp: USB gadget IPA Port
 * @port_num: Port num used by function driver which need to be disable
 *
 * It is being called from atomic context from gadget driver when particular
 * function is being disable due to USB cable disconnect or USB composition
 * switch is being trigger. This API performs restoring USB endpoint operation
 * and disable USB endpoint used for accelerated path.
 */
void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
{
	struct ipa_data_ch_info *port;
	unsigned long flags;
	struct usb_gadget *gadget = NULL;

	pr_debug("dev:%p port number:%d\n", gp, port_num);
	if (port_num >= n_ipa_ports) {
		pr_err("invalid ipa portno#%d\n", port_num);
		return;
	}

	if (!gp) {
		pr_err("data port is null\n");
		return;
	}

	port = ipa_data_ports[port_num];
	if (!port) {
		pr_err("port %u is NULL", port_num);
		return;
	}

	spin_lock_irqsave(&port->port_lock, flags);
	if (port->port_usb) {
		gadget = port->port_usb->cdev->gadget;
		port->port_usb->ipa_consumer_ep = -1;
		port->port_usb->ipa_producer_ep = -1;

		if (port->port_usb->in) {
			/*
			 * Disable endpoints.
			 * Unlocking is needed since disabling the eps might
			 * stop active transfers and therefore the request
			 * complete function will be called, where we try
			 * to obtain the spinlock as well.
			 */
			if (gadget_is_dwc3(gadget))
				msm_ep_unconfig(port->port_usb->in);
			spin_unlock_irqrestore(&port->port_lock, flags);
			usb_ep_disable(port->port_usb->in);
			spin_lock_irqsave(&port->port_lock, flags);
			port->port_usb->in->endless = false;
		}

		if (port->port_usb->out) {
			if (gadget_is_dwc3(gadget))
				msm_ep_unconfig(port->port_usb->out);
			spin_unlock_irqrestore(&port->port_lock, flags);
			usb_ep_disable(port->port_usb->out);
			spin_lock_irqsave(&port->port_lock, flags);
			port->port_usb->out->endless = false;
		}

		port->port_usb = NULL;
	}
	spin_unlock_irqrestore(&port->port_lock, flags);
	queue_work(ipa_data_wq, &port->disconnect_w);
}
Пример #2
0
int uninit_data(struct usb_ep *ep)
{
	struct f_qdss *qdss = ep->driver_data;
	struct usb_gadget *gadget = qdss->cdev->gadget;
	int res = 0;

	pr_err("uninit_data\n");

	if (gadget_is_dwc3(gadget)) {
		res = msm_ep_unconfig(ep);
		if (res)
			pr_err("msm_ep_unconfig failed\n");
	}

	return res;
}
Пример #3
0
/**
 * ipa_data_connect_work() - Perform USB IPA BAM connect
 * @w: connect work
 *
 * It is being schedule from ipa_data_connect() API when particular function
 * which is using USB IPA accelerated path. This API performs allocating request
 * for USB endpoint (tx/rx) for endless purpose, configure USB endpoint to be
 * used in accelerated path, connect of USB BAM pipe, IPA BAM pipe and also
 * initiate USB IPA BAM pipe handshake for connect sequence.
 */
static void ipa_data_connect_work(struct work_struct *w)
{
	struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info,
								connect_w);
	struct gadget_ipa_port	*gport;
	struct usb_gadget	*gadget = NULL;
	u32			sps_params;
	int			ret;
	unsigned long		flags;
	bool			is_ipa_disconnected = true;

	pr_debug("%s: Connect workqueue started", __func__);

	spin_lock_irqsave(&port->port_lock, flags);

	if (!port->port_usb) {
		spin_unlock_irqrestore(&port->port_lock, flags);
		pr_err("%s(): port_usb is NULL.\n", __func__);
		return;
	}

	gport = port->port_usb;
	if (gport && gport->cdev)
		gadget = gport->cdev->gadget;

	if (!gadget) {
		spin_unlock_irqrestore(&port->port_lock, flags);
		pr_err("%s: gport is NULL.\n", __func__);
		return;
	}

	gport->ipa_consumer_ep = -1;
	gport->ipa_producer_ep = -1;
	if (gport->out) {
		port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC);
		if (!port->rx_req) {
			spin_unlock_irqrestore(&port->port_lock, flags);
			pr_err("%s: failed to allocate rx_req\n", __func__);
			return;
		}
		port->rx_req->context = port;
		port->rx_req->complete = ipa_data_endless_complete;
		port->rx_req->length = 0;
		port->rx_req->no_interrupt = 1;
	}

	if (gport->in) {
		port->tx_req = usb_ep_alloc_request(gport->in, GFP_ATOMIC);
		if (!port->tx_req) {
			spin_unlock_irqrestore(&port->port_lock, flags);
			pr_err("%s: failed to allocate tx_req\n", __func__);
			goto free_rx_req;
		}
		port->tx_req->context = port;
		port->tx_req->complete = ipa_data_endless_complete;
		port->tx_req->length = 0;
		port->tx_req->no_interrupt = 1;
	}

	port->is_connected = true;
	spin_unlock_irqrestore(&port->port_lock, flags);

	/* update IPA Parameteres here. */
	port->ipa_params.usb_connection_speed = gadget->speed;
	if (gadget_is_dwc3(gadget))
		port->ipa_params.reset_pipe_after_lpm =
				msm_dwc3_reset_ep_after_lpm(gadget);
	port->ipa_params.skip_ep_cfg = true;
	port->ipa_params.keep_ipa_awake = true;
	port->ipa_params.cons_clnt_hdl = -1;
	port->ipa_params.prod_clnt_hdl = -1;

	/*
	 * Perform below operations for Tx from Device (OUT transfer)
	 * 1. Connect with pipe of USB BAM with IPA BAM pipe
	 * 2. Update USB Endpoint related information using SPS Param.
	 * 3. Configure USB Endpoint/DBM for the same.
	 * 4. Override USB ep queue functionality for endless transfer.
	 */
	if (gport->out) {
		pr_debug("configure bam ipa connect for USB OUT\n");
		port->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
		ret = usb_bam_connect_ipa(&port->ipa_params);
		if (ret) {
			pr_err("usb_bam_connect_ipa out failed err:%d\n", ret);
			goto free_rx_tx_req;
		}
		gadget->bam2bam_func_enabled = true;

		gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx;

		if (gadget_is_dwc3(gadget)) {
			sps_params = MSM_SPS_MODE | MSM_DISABLE_WB
					| MSM_PRODUCER | port->src_pipe_idx;
			port->rx_req->length = 32*1024;
		} else {
			sps_params = (MSM_SPS_MODE | port->src_pipe_idx |
				       MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
		}
		port->rx_req->udc_priv = sps_params;

		port->src_bam_idx = usb_bam_get_connection_idx(
					gadget->name, IPA_P_BAM,
					USB_TO_PEER_PERIPHERAL,
					USB_BAM_DEVICE, 1);
		if (port->src_bam_idx < 0) {
			pr_err("src_bam: get_connection_idx failed\n");
			goto disconnect_usb_bam_ipa_out;
		}

		if (gadget_is_dwc3(gadget)) {
			configure_fifo(port->src_bam_idx, port->port_usb->out);
			ret = msm_ep_config(port->port_usb->out);
			if (ret) {
				pr_err("msm_ep_config() failed for OUT EP\n");
				goto disconnect_usb_bam_ipa_out;
			}
		}
		is_ipa_disconnected = false;
	}

	if (gport->in) {
		pr_debug("configure bam ipa connect for USB IN\n");
		port->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
		port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS;
		ret = usb_bam_connect_ipa(&port->ipa_params);
		if (ret) {
			pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret);
			goto unconfig_msm_ep_out;
		}
		gadget->bam2bam_func_enabled = true;

		gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx;
		if (gadget_is_dwc3(gadget)) {
			sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
							port->dst_pipe_idx;
			port->tx_req->length = 32*1024;
		} else {
			sps_params = (MSM_SPS_MODE | port->dst_pipe_idx |
				       MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
		}
		port->tx_req->udc_priv = sps_params;

		port->dst_bam_idx = usb_bam_get_connection_idx(gadget->name,
					IPA_P_BAM, PEER_PERIPHERAL_TO_USB,
					USB_BAM_DEVICE, 1);
		if (port->dst_bam_idx < 0) {
			pr_err("dst_bam: get_connection_idx failed\n");
			goto disconnect_usb_bam_ipa_in;
		}
		if (gadget_is_dwc3(gadget)) {
			configure_fifo(port->dst_bam_idx, gport->in);
			ret = msm_ep_config(gport->in);
			if (ret) {
				pr_err("msm_ep_config() failed for IN EP\n");
				goto disconnect_usb_bam_ipa_in;
			}
		}
		is_ipa_disconnected = false;
	}

	pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
				gport->ipa_producer_ep,
				gport->ipa_consumer_ep);

	gqti_ctrl_update_ipa_pipes(NULL, DPL_QTI_CTRL_PORT_NO,
				gport->ipa_producer_ep,
				gport->ipa_consumer_ep);

	pr_debug("src_bam_idx:%d dst_bam_idx:%d\n",
				port->src_bam_idx, port->dst_bam_idx);

	if (gport->out)
		ipa_data_start_endless_xfer(port, false);
	if (gport->in)
		ipa_data_start_endless_xfer(port, true);

	pr_debug("Connect workqueue done (port %p)", port);
	return;

disconnect_usb_bam_ipa_in:
	if (!is_ipa_disconnected) {
		usb_bam_disconnect_ipa(&port->ipa_params);
		is_ipa_disconnected = true;
	}
unconfig_msm_ep_out:
	if (gport->out)
		msm_ep_unconfig(port->port_usb->out);
disconnect_usb_bam_ipa_out:
	if (!is_ipa_disconnected) {
		usb_bam_disconnect_ipa(&port->ipa_params);
		is_ipa_disconnected = true;
	}
free_rx_tx_req:
	spin_lock_irqsave(&port->port_lock, flags);
	port->is_connected = false;
	spin_unlock_irqrestore(&port->port_lock, flags);
	if (gport->in && port->tx_req)
		usb_ep_free_request(gport->in, port->tx_req);
free_rx_req:
	if (gport->out && port->rx_req)
		usb_ep_free_request(gport->out, port->rx_req);
}