Ejemplo n.º 1
0
static int _complete(dwc_otg_pcd_t *pcd, void *ep_handle,
		     void *req_handle, int32_t status, uint32_t actual)
{
	struct out_request *oreq = (struct out_request *)req_handle;

	if (ep_handle != NULL) {
		static_data->usb_ep_complete((int)ep_handle, req_handle, status,
					     actual);
		return 0;
	}
	__DWC_ERROR("%s(): enter \n", __func__);
	if (oreq == NULL) {
		return 0;
	}
	if (status != 0) {
		__DWC_ERROR("%s(): status not null: %d \n", __func__, status);
		return 0;
	}

	if (static_data->usb_class_handler(&oreq->out_req, &oreq->data.length,
					   &oreq->data.buf) != 0) {
		__DWC_ERROR("%s(): Error while processing packet\n", __func__);
	}

	return 0;
}
Ejemplo n.º 2
0
/* Allocates and init the pcd */
static int pcd_init(dwc_otg_device_t *otg_dev)
{
	int retval = 0;

	otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if,
					&static_data->static_pcd);
	if (!otg_dev->pcd) {
		__DWC_ERROR("dwc_otg_pcd_init failed\n");
		return -DWC_E_NO_MEMORY;
	}

	dwc_otg_pcd_start(otg_dev->pcd, &fops);

	return retval;
}
/* Basic sanity check of the descriptor
 * Copy the provided descriptor locally*/
static int init_descriptors(usb_device_descriptor_t * dev_desc,
			    usb_config_descriptor_t * conf_desc, uint32_t csize,
			    usb_string_descriptor_t *strings, int num_strings)
{
	static_data->usb_device_descriptor = (usb_device_descriptor_t *) dev_desc;
	if (static_data->usb_device_descriptor->bNumConfigurations != 1) {
		__DWC_ERROR("Only one USB config is supported\n");
		return -DWC_E_INVALID;
	}

	static_data->usb_config_descriptor = (usb_config_descriptor_t *) conf_desc;

	static_data->config_descriptor_size = csize;
	static_data->strings_desc = strings;
	static_data->usb_strings_count = num_strings;
	return 0;
}
Ejemplo n.º 4
0
/*
 * This function is called when an lm_device is bound to a
 * dwc_otg_driver. It creates the driver components required to
 * control the device (CIL, HCD, and PCD) and it initializes the
 * device. The driver components are stored in a dwc_otg_device
 * structure. A reference to the dwc_otg_device is saved in the
 * lm_device. This allows the driver to access the dwc_otg_device
 * structure on subsequent calls to driver methods for this device.
 *
 */
int usb_driver_init(uint32_t base_addr)
{
	int retval = 0;

	__DWC_WARN("dwc_otg_driver_init\n");

	dwc_otg_device = &static_data->dwc_otg_device_inst;

	DWC_MEMSET(dwc_otg_device, 0, sizeof(*dwc_otg_device));

	/*
	 * Initialize driver data to point to the global DWC_otg
	 * Device structure.
	 */
	__DWC_ERROR("dwc_otg_device addr = 0x%p\n", dwc_otg_device);

	dwc_otg_device->core_if = dwc_otg_cil_init(base_addr);
	if (!dwc_otg_device->core_if) {
		__DWC_ERROR("CIL initialization failed!\n");
		retval = -DWC_E_NO_MEMORY;
		goto fail;
	}

#ifdef USB_DEBUG
	/*
	 * Attempt to ensure this device is really a DWC_otg Controller.
	 * Read and verify the SNPSID register contents. The value should be
	 * 0x45F42XXX or 0x45F42XXX, which corresponds to either "OT2" or "OTG3",
	 * as in "OTG version 2.XX" or "OTG version 3.XX".
	 */

	if (((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) !=
	     0x4F542000)
	    && ((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) !=
		0x4F543000)) {
		__DWC_WARN("Bad value for SNPSID: 0x%x\n",
			   dwc_otg_get_gsnpsid(dwc_otg_device->core_if));
		retval = -DWC_E_INVALID;
		goto fail;
	}
#endif /* USB_DEBUG */

	/*
	 * Disable the global interrupt until all the interrupt
	 * handlers are installed.
	 */
	__DWC_WARN("USB INIT: Disabling global IRQ\n");
	dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);

	/*
	 * Initialize the DWC_otg core.
	 */
	__DWC_WARN("USB INIT: Initializing OTG core\n");
	dwc_otg_core_init(dwc_otg_device->core_if);

	/*
	 * Initialize the PCD
	 */
	__DWC_WARN("USB INIT: Initializing PCD\n");
	retval = pcd_init(dwc_otg_device);
	if (retval != 0) {
		__DWC_ERROR("pcd_init failed\n");
		dwc_otg_device->pcd = NULL;
		goto fail;
	}

	__DWC_WARN("USB INIT: Enabling global IRQ\n");
	dwc_otg_enable_global_interrupts(dwc_otg_device->core_if);

	__DWC_WARN("DWC core initialized! \n");
	return 0;

fail:
	__DWC_ERROR("ERROR! unable to init USB driver\n");
	return retval;
}
Ejemplo n.º 5
0
static int _setup(dwc_otg_pcd_t *pcd, uint8_t *bytes)
{
	DWC_DEBUGPL(DBG_PCD, "%s(): enter \n", __func__);

	const usb_device_request_t *ctrlReq = (usb_device_request_t *)bytes;

	int value = 0;
	int idx;
	usb_device_request_t ctrlConvertedReq;
	usb_device_request_t *ctrl = &ctrlConvertedReq;


	ctrl->bmRequestType = (uByte)(ctrlReq->bmRequestType);
	ctrl->bRequest = (uByte)(ctrlReq->bRequest);
	ctrl->wValue[0] = ctrlReq->wValue[0];
	ctrl->wValue[1] = ctrlReq->wValue[1];
	ctrl->wIndex[0] = ctrlReq->wIndex[0];
	ctrl->wIndex[1] = ctrlReq->wIndex[1];
	ctrl->wLength[0] = ctrlReq->wLength[0];
	ctrl->wLength[1] = ctrlReq->wLength[1];
	/*
	 * usually this stores reply data in the pre-allocated ep0 buffer,
	 * but config change events will reconfigure hardware.
	 */
	static_data->dl_req.data.zero = 0;
	if (UT_GET_RECIPIENT(ctrl->bmRequestType) == UT_INTERFACE) {
		if ((UT_GET_DIR(ctrl->bmRequestType) == UT_WRITE)
		    && (UGETW(ctrl->wLength) > 0)) {
			static_data->dl_req.data.buf =
				&static_data->ep0_buffer[0];
			DWC_MEMCPY(&static_data->dl_req.out_req, ctrl,
				   sizeof(*ctrl));
			static_data->dl_req.data.length =
				DWC_MIN(UGETW(
						ctrl->wLength),
					static_data->ep0_buffer_size);

			// either shorter than asked, or multiple of the MPS require ZLP.
			static_data->dl_req.data.zero =
				((value < UGETW(ctrl->wLength))
				 &&
				 ((value %
				   pcd->ep0.dwc_ep.
				   maxpacket) ==
				  0)) ? 1 : 0;

			// Elaborate when getting the time.
			dwc_otg_pcd_ep_queue(
				pcd, /*void* ep_hanlde */ NULL,
				static_data->dl_req.data.buf,
				(dwc_dma_t)static_data->dl_req.
				data.buf,
				static_data->dl_req.data.length,
				static_data->dl_req.data.zero,
			        /*void* req_handle */ &static_data
				->dl_req, 0);
		} else {
			static_data->dl_req.data.buf =
				&static_data->ep0_buffer[0];
			if (static_data->usb_class_handler(ctrl, &value,
							   &static_data->dl_req
							   .data.buf) != 0) {
				value = -DWC_E_NOT_SUPPORTED;
			}
		}
	} else {
		switch (ctrl->bRequest) {
		case UR_GET_DESCRIPTOR:
			__DWC_ERROR("ctrl->Length = %d \n",
				    UGETW(ctrl->wLength));

			switch (UGETW(ctrl->wValue) >> 8) {
			case UDESC_DEVICE:
				value =
					DWC_MIN(UGETW(ctrl->wLength),
						USB_DEVICE_DESCRIPTOR_SIZE);
				static_data->dl_req.data.buf = (uint8_t *)
							       static_data->
							       usb_device_descriptor;

				break;

			case UDESC_CONFIG:
				value = DWC_MIN(UGETW(ctrl->wLength),
						UGETW(static_data->
						      usb_config_descriptor->
						      wTotalLength));
				static_data->dl_req.data.buf = (uint8_t *)
							       static_data->
							       usb_config_descriptor;

				break;

			case UDESC_OTHER_SPEED_CONFIGURATION:
				__DWC_ERROR
				(
					"UDESC_OTHER_SPEED_CONFIGURATION NOT IMPLEMENTED! \n");
				break;

			case UDESC_STRING:
				/* wIndex == language code.
				 * this driver only handles one language, you can
				 * add string tables for other languages, using
				 * any UTF-8 characters
				 */
				idx = UGETW(ctrl->wValue) & 0xFF;
				__DWC_ERROR("UDESC_STRING idx = %d \n", idx);
				__DWC_ERROR(
					"UDESC_STRING bLength = %d \n",
					&static_data->strings_desc[idx].
					bLength);
				if (idx > static_data->usb_strings_count) {
					if (idx == 0xEE) {
						// Microsoft MTP extension
						value = 0;
					} else {
						value = -DWC_E_INVALID;
					}
				} else {
					value = DWC_MIN(UGETW(
								ctrl->wLength),
							static_data->
							strings_desc[idx].
							bLength);
					static_data->dl_req.data.buf =
						(uint8_t *)
						&
						static_data->strings_desc[idx];
				}

				break;
			}
			break;

		case UR_SET_CONFIG:
			static_data->usb_config = UGETW(ctrl->wValue);
			value = 0;

			{
				int i;
				for (i = 0;
				     i < static_data->usb_num_endpoints;
				     i++) {
					int ret = dwc_otg_pcd_ep_enable(
						pcd,
						(const
						 uint8_t
						 *)&static_data->usb_endpoints[
							i],
						static_data
						->usb_endpoints[i].
						bEndpointAddress);
					__DWC_WARN(
						"Enabled endpoint: %x ret: %d\n",
						static_data->usb_endpoints[i
						].bEndpointAddress,
						ret);
				}
				struct usb_event evt;
				evt.event = USB_EVENT_SET_CONFIG;
				evt.event_data.config = static_data->usb_config;
				if (static_data->usb_event_cb) {
					static_data->usb_event_cb(&evt);
				}
			}

			break;
		case UR_GET_CONFIG:
			static_data->dl_req.data.buf = &static_data->usb_config;
			value = DWC_MIN(UGETW(ctrl->wLength), 1);
			break;

		/* until we add altsetting support, or other interfaces,
		 * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
		 * and already killed pending endpoint I/O.
		 */
		case UR_SET_INTERFACE:
			value = 0;
			break;

		case UR_GET_INTERFACE:
			__DWC_ERROR("UR_GET_INTERFACE NOT IMPLEMENTED! \n");
			goto unknown;
			break;

		default:
unknown:

			value = -DWC_E_NOT_SUPPORTED;
			break;
		}
	}
	/* respond with data transfer before status phase? */
	__DWC_ERROR("%s(): req.length = %d - max length is 64 \n", __func__,
		    value);
	if (value >= 0) {
		static_data->dl_req.data.length = value;

		// either shorter than asked, or multiple of the MPS require ZLP.
		static_data->dl_req.data.zero = ((value < UGETW(ctrl->wLength))
						 && ((value %
						      pcd->ep0.dwc_ep.maxpacket)
						     ==
						     0)) ? 1 : 0;

		// Elaborate when getting the time.
		dwc_otg_pcd_ep_queue(pcd, /*void* ep_hanlde */ NULL,
				     static_data->dl_req.data.buf,
				     (dwc_dma_t)static_data->dl_req.data.buf,
				     static_data->dl_req.data.length,
				     static_data->dl_req.data.zero,
		                     /*void* req_handle */ NULL, 0);
	}

	/* device either stalls (value < 0) or reports success */
	return value;
}
Ejemplo n.º 6
0
/**
 * This function handles the OTG Interrupts. It reads the OTG
 * Interrupt Register (GOTGINT) to determine what interrupt has
 * occurred.
 *
 * @param core_if Programming view of DWC_otg controller.
 */
int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if)
{
	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
	gotgint_data_t gotgint;
	gotgctl_data_t gotgctl;
	gintmsk_data_t gintmsk;
	gpwrdn_data_t gpwrdn;

	gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint);
	gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
	DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
		    op_state_str(core_if));

	if (gotgint.b.sesenddet) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
			    "Session End Detected++ (%s)\n",
			    op_state_str(core_if));
		gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);

		if (core_if->op_state == B_HOST) {
			cil_pcd_start(core_if);
			core_if->op_state = B_PERIPHERAL;
		} else {
			/* If not B_HOST and Device HNP still set. HNP
			 * Did not succeed!*/
			if (gotgctl.b.devhnpen) {
				DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
				__DWC_ERROR("Device Not Connected/Responding!\n");
			}

			/* If Session End Detected the B-Cable has
			 * been disconnected. */
			/* Reset PCD and Gadget driver to a
			 * clean state. */
			core_if->lx_state = DWC_OTG_L0;
			DWC_SPINUNLOCK(core_if->lock);
			cil_pcd_stop(core_if);
			DWC_SPINLOCK(core_if->lock);

			if (core_if->adp_enable) {
				if (core_if->power_down == 2) {
					gpwrdn.d32 = 0;
					gpwrdn.b.pwrdnswtch = 1;
					DWC_MODIFY_REG32(&core_if->
							 core_global_regs->
							 gpwrdn, gpwrdn.d32, 0);
				}

				gpwrdn.d32 = 0;
				gpwrdn.b.pmuintsel = 1;
				gpwrdn.b.pmuactv = 1;
				DWC_MODIFY_REG32(&core_if->core_global_regs->
						 gpwrdn, 0, gpwrdn.d32);

				dwc_otg_adp_sense_start(core_if);
			}
		}

		gotgctl.d32 = 0;
		gotgctl.b.devhnpen = 1;
		DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
	}
	if (gotgint.b.sesreqsucstschng) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
			    "Session Reqeust Success Status Change++\n");
		gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
		if (gotgctl.b.sesreqscs) {

			if ((core_if->core_params->phy_type ==
			     DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) {
				core_if->srp_success = 1;
			} else {
				DWC_SPINUNLOCK(core_if->lock);
				cil_pcd_resume(core_if);
				DWC_SPINLOCK(core_if->lock);
				/* Clear Session Request */
				gotgctl.d32 = 0;
				gotgctl.b.sesreq = 1;
				DWC_MODIFY_REG32(&global_regs->gotgctl,
						 gotgctl.d32, 0);
			}
		}
	}
	if (gotgint.b.hstnegsucstschng) {
		/* Print statements during the HNP interrupt handling
		 * can cause it to fail.*/
		gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
		/* WA for 3.00a- HW is not setting cur_mode, even sometimes
		 * this does not help*/
		if (core_if->snpsid >= OTG_CORE_REV_3_00a)
			dwc_udelay(100);
		if (gotgctl.b.hstnegscs) {
			if (dwc_otg_is_host_mode(core_if)) {
				core_if->op_state = B_HOST;
				/*
				 * Need to disable SOF interrupt immediately.
				 * When switching from device to host, the PCD
				 * interrupt handler won't handle the
				 * interrupt if host mode is already set. The
				 * HCD interrupt handler won't get called if
				 * the HCD state is HALT. This means that the
				 * interrupt does not get handled and Linux
				 * complains loudly.
				 */
				gintmsk.d32 = 0;
				gintmsk.b.sofintr = 1;
				DWC_MODIFY_REG32(&global_regs->gintmsk,
						 gintmsk.d32, 0);
				/* Call callback function with spin lock released */
				DWC_SPINUNLOCK(core_if->lock);
				cil_pcd_stop(core_if);
				/*
				 * Initialize the Core for Host mode.
				 */
				cil_hcd_start(core_if);
				DWC_SPINLOCK(core_if->lock);
				core_if->op_state = B_HOST;
			}
		} else {
			gotgctl.d32 = 0;
			gotgctl.b.hnpreq = 1;
			gotgctl.b.devhnpen = 1;
			DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
			DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
			__DWC_ERROR("Device Not Connected/Responding\n");
		}
	}
	if (gotgint.b.hstnegdet) {
		/* The disconnect interrupt is set at the same time as
		 * Host Negotiation Detected.  During the mode
		 * switch all interrupts are cleared so the disconnect
		 * interrupt handler will not get executed.
		 */
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
			    "Host Negotiation Detected++ (%s)\n",
			    (dwc_otg_is_host_mode(core_if) ? "Host" :
			     "Device"));
		if (dwc_otg_is_device_mode(core_if)) {
			DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",
				    core_if->op_state);
			DWC_SPINUNLOCK(core_if->lock);
			cil_hcd_disconnect(core_if);
			cil_pcd_start(core_if);
			DWC_SPINLOCK(core_if->lock);
			core_if->op_state = A_PERIPHERAL;
		} else {
			/*
			 * Need to disable SOF interrupt immediately. When
			 * switching from device to host, the PCD interrupt
			 * handler won't handle the interrupt if host mode is
			 * already set. The HCD interrupt handler won't get
			 * called if the HCD state is HALT. This means that
			 * the interrupt does not get handled and Linux
			 * complains loudly.
			 */
			gintmsk.d32 = 0;
			gintmsk.b.sofintr = 1;
			DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0);
			DWC_SPINUNLOCK(core_if->lock);
			cil_pcd_stop(core_if);
			cil_hcd_start(core_if);
			DWC_SPINLOCK(core_if->lock);
			core_if->op_state = A_HOST;
		}
	}
	if (gotgint.b.adevtoutchng) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
			    "A-Device Timeout Change++\n");
	}
	if (gotgint.b.debdone) {
		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n");
	}

	/* Clear GOTGINT */
	DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32);

	return 1;
}