/** * This function initialized the PCD portion of the driver. * */ int pcd_init( struct dwc_otg_device *_dev ) { //dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); dwc_otg_device_t *otg_dev = _dev; int retval = 0; int irq; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if); if (!otg_dev->pcd) { DWC_ERROR("dwc_otg_pcd_init failed\n"); return -ENOMEM; } gadget_wrapper = alloc_wrapper(_dev); /* * Initialize EP structures */ gadget_add_eps(gadget_wrapper); /* * Setup interupt handler */ #if 0 irq = platform_get_irq(_dev, 0); DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq); retval = request_irq(irq, dwc_otg_pcd_irq, 0, gadget_wrapper->gadget.name, otg_dev->pcd); //SA_SHIRQ, gadget_wrapper->gadget.name, if (retval != 0) { DWC_ERROR("request of irq%d failed\n", irq); free_wrapper(gadget_wrapper); return -EBUSY; } #endif sprd_pcd = otg_dev->pcd; dwc_otg_pcd_start(gadget_wrapper->pcd, &fops); return retval; }
/** * This function initialized the PCD portion of the driver. * */ int pcd_init(struct platform_device *_dev) { dwc_otg_device_t *otg_dev = platform_get_otgdata(_dev); int irq; int retval = 0; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if); pdev = otg_dev->core_if; if (!otg_dev->pcd) { DWC_ERROR("dwc_otg_pcd_init failed\n"); return -ENOMEM; } gadget_wrapper = alloc_wrapper(_dev); gadget_wrapper->p = otg_dev; /* * Initialize EP structures */ gadget_add_eps(gadget_wrapper); /* * Setup interupt handler */ irq = platform_get_irq(_dev, 0); if(irq < 0) { DWC_ERROR("no irq? (irq=%d)\n", irq); kfree(otg_dev->pcd); return -ENODEV; } DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq); retval = request_irq(irq, dwc_otg_pcd_irq, IRQF_SHARED, gadget_wrapper->gadget.name, otg_dev->pcd); if (retval != 0) { DWC_ERROR("request of irq%d failed\n", irq); free_wrapper(gadget_wrapper); return -EBUSY; } dwc_otg_pcd_start(gadget_wrapper->pcd, &fops); return retval; }
/** * dwc_otg_device_init * * Initialise the dwc_otg_device structure, and the hardware * so that it can run in device mode. */ int dwc_otg_device_init(dwc_otg_device_t *_dev, dwc_otg_core_t *_core) { int ret = 0; if(!_dev) { DWC_ERROR("%s passed a null device structure pointer!\n", __func__); return -EINVAL; } // Clear device structure. // (This should have been by the driver, // but better safe than sorry.) memset(_dev, 0, sizeof(dwc_otg_device_t)); INIT_WORK(&_dev->reset_work, &dwc_otg_device_usb_reset_work); INIT_WORK(&_dev->suspend_work, &dwc_otg_device_usb_suspend_work); INIT_WORK(&_dev->disconnect_work, &dwc_otg_device_disconnect_work); // Initialise members _dev->remote_wakeup = 1; if(!_core) { DWC_ERROR("%s passed a null core structure pointer!\n", __func__); return -EINVAL; } else _dev->core = _core; // Register IRQ. ret = request_irq(_core->irq, dwc_otg_device_irq, IRQF_SHARED, DWC_OTG_DRIVER_NAME, _dev); if(ret) { DWC_ERROR("Failed to register IRQ (%#x).\n", ret); return ret; } else _dev->irq = _core->irq; return 0; }
dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data) { dwc_timer_t *t = DWC_ALLOC(sizeof(*t)); /*OS_ERR_TYPE err;*/ if (t == NULL) { DWC_ERROR("Cannot allocate memory for timer"); return NULL; } t->lock = DWC_SPINLOCK_ALLOC(); if (!t->lock) { DWC_ERROR("Cannot allocate memory for lock"); goto no_lock; } t->scheduled = 0; t->cb = cb; t->data = data; /*t->t = timer_create(timer_callback, (void *)t, 0, 0, 0, &err); if (err == E_OS_ERR) { DWC_ERROR("Cannot allocate memory for timer->t"); goto no_timer; }*/ return t; /* no_timer: */ DWC_SPINLOCK_FREE(t->lock); no_lock: DWC_FREE(t); return NULL; }
/** * This function registers a gadget driver with the PCD. * * When a driver is successfully registered, it will receive control * requests including set_configuration(), which enables non-control * requests. then usb traffic follows until a disconnect is reported. * then a host may connect again, or the driver might get unbound. * * @param driver The driver being registered */ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { int retval; pr_info("%s\n", __func__); if (!driver || driver->speed == USB_SPEED_UNKNOWN || !driver->disconnect || !driver->setup) { //!driver->unbind || !driver->disconnect || !driver->setup) { DWC_DEBUGPL(DBG_PCDV, "EINVAL\n"); return -EINVAL; } DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n", driver->driver.name); if (gadget_wrapper == 0) { DWC_DEBUGPL(DBG_PCDV, "ENODEV\n"); return -ENODEV; } if (gadget_wrapper->driver != 0) { DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver); return -EBUSY; } /* hook up the driver */ gadget_wrapper->driver = driver; gadget_wrapper->gadget.dev.driver = &driver->driver; DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name); retval = bind(&gadget_wrapper->gadget); gadget_wrapper->enabled = 1; if (retval) { DWC_ERROR("bind to driver %s --> error %d\n", driver->driver.name, retval); gadget_wrapper->driver = 0; gadget_wrapper->gadget.dev.driver = 0; gadget_wrapper->enabled = 0; return retval; } DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", driver->driver.name); return 0; }
static struct gadget_wrapper *alloc_wrapper( struct dwc_otg_device *_dev ) { static char pcd_name[] = "dwc_otg"; //dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); dwc_otg_device_t *otg_dev = _dev; struct gadget_wrapper *d; int retval; d = dwc_alloc(sizeof(*d)); if (d == NULL) { return NULL; } memset(d, 0, sizeof(*d)); d->gadget.name = pcd_name; d->pcd = otg_dev->pcd; //sword /* strcpy(d->gadget.dev.bus_id, "gadget"); */ #if 0 dev_set_name(&d->gadget.dev, "gadget"); d->gadget.dev.parent = &_dev->dev; d->gadget.dev.release = dwc_otg_pcd_gadget_release; #endif d->gadget.ops = &dwc_otg_pcd_ops; d->gadget.is_dualspeed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd); d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd); d->driver = 0; /* Register the gadget device */ #if 0 retval = device_register(&d->gadget.dev); if (retval != 0) { DWC_ERROR("device_register failed\n"); dwc_free(d); return NULL; } #endif return d; }
static struct gadget_wrapper *alloc_wrapper( struct platform_device *_dev ) { static char pcd_name[] = "dwc_otg"; dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); struct gadget_wrapper *d; int retval; d = dwc_alloc(sizeof(*d)); if (d == NULL) { return NULL; } memset(d, 0, sizeof(*d)); d->gadget.name = pcd_name; d->pcd = otg_dev->pcd; //sword /* strcpy(d->gadget.dev.bus_id, "gadget"); */ dev_set_name(&d->gadget.dev, "gadget"); d->gadget.dev.parent = &_dev->dev; d->gadget.dev.release = dwc_otg_pcd_gadget_release; d->gadget.ops = &dwc_otg_pcd_ops; d->gadget.max_speed = USB_SPEED_HIGH; d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd); if (d->pcd->core_if->dma_enable && d->pcd->core_if->dma_desc_enable) d->gadget.sg_supported = 1; d->driver = 0; /* Register the gadget device */ #if 0 retval = device_register(&d->gadget.dev); if (retval != 0) { DWC_ERROR("device_register failed\n"); dwc_free(d); return NULL; } #endif return d; }
/** * This function registers a gadget driver with the PCD. * * When a driver is successfully registered, it will receive control * requests including set_configuration(), which enables non-control * requests. then usb traffic follows until a disconnect is reported. * then a host may connect again, or the driver might get unbound. * * @param driver The driver being registered */ int usb_gadget_register_driver(struct usb_gadget_driver *driver) { int retval; DWC_DEBUGPL(DBG_PCD, "registering gadget driver \n"); if (!driver || driver->speed == USB_SPEED_UNKNOWN || !driver->bind || !driver->disconnect || !driver->setup) { //!driver->unbind || !driver->disconnect || !driver->setup) { DWC_DEBUGPL(DBG_PCDV, "EINVAL\n"); return -EINVAL; } if (gadget_wrapper == 0) { DWC_DEBUGPL(DBG_PCDV, "ENODEV\n"); return -ENODEV; } if (gadget_wrapper->driver != 0) { DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver); return -EBUSY; } /* hook up the driver */ gadget_wrapper->driver = driver; //gadget_wrapper->gadget.dev.driver = &driver->driver; DWC_DEBUGPL(DBG_PCD, "bind to driver \n"); retval = driver->bind(&gadget_wrapper->gadget); if (retval) { DWC_ERROR("bind to driver --> error %d\n", retval); gadget_wrapper->driver = 0; //gadget_wrapper->gadget.dev.driver = 0; return retval; } /* DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", driver->driver.name); */ return 0; }
/** * This function is used to submit an I/O Request to an EP. * * - When the request completes the request's completion callback * is called to return the request to the driver. * - An EP, except control EPs, may have multiple requests * pending. * - Once submitted the request cannot be examined or modified. * - Each request is turned into one or more packets. * - A BULK EP can queue any amount of data; the transfer is * packetized. * - Zero length Packets are specified with the request 'zero' * flag. */ static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int _gfp_flags) { int prevented = 0; dwc_otg_pcd_request_t *req; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd; unsigned long flags = 0; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%d)\n", __func__, _ep, _req, _gfp_flags); req = container_of(_req, dwc_otg_pcd_request_t, req); if (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) { if( !_req ) printk("bad _req\n"); if( !_req->complete ) printk("bad _req->complete\n"); if( !_req->buf ) printk("bad _req->buf\n"); if( !list_empty(&req->queue) ) printk("bad list_empty\n"); DWC_WARN("%s, bad params\n", __func__); return -EINVAL; } ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) { DWC_WARN("%s, bad ep\n", __func__); return -EINVAL; } pcd = ep->pcd; //cathy, if suspended, drop request if ( (GET_CORE_IF(pcd)->dev_if->suspended == 1) && (ep->dwc_ep.num != 0) ) { DWC_DEBUGPL(DBG_PCDV,"%s, epnum = %d, drop request\n", __func__, ep->dwc_ep.num); return -ESHUTDOWN; } if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed); DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); if (!GET_CORE_IF(pcd)->core_params->opt) { if (ep->dwc_ep.num != 0) { DWC_ERROR("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); } } SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); #if defined(DEBUG) & defined(VERBOSE) dump_msg(_req->buf, _req->length); #endif _req->status = -EINPROGRESS; _req->actual = 0; /* * For EP0 IN without premature status, zlp is required? */ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { DWC_DEBUGPL(DBG_PCDV, "%s-OUT ZLP\n", _ep->name); //_req->zero = 1; } /* Start the transfer */ if (list_empty(&ep->queue) && !ep->stopped) { /* EP0 Transfer? */ if (ep->dwc_ep.num == 0) { switch (pcd->ep0state) { case EP0_IN_DATA_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_IN_DATA_PHASE\n", __func__); break; case EP0_OUT_DATA_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_OUT_DATA_PHASE\n", __func__); if (pcd->request_config) { /* Complete STATUS PHASE */ ep->dwc_ep.is_in = 1; pcd->ep0state = EP0_STATUS; } break; default: DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", pcd->ep0state); SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return -EL2HLT; } ep->dwc_ep.dma_addr = (u32)_req->buf & ~Uncache_Mask; //_req->dma; //cathy ep->dwc_ep.start_xfer_buff = _req->buf; ep->dwc_ep.xfer_buff = _req->buf; ep->dwc_ep.xfer_len = _req->length; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; dwc_otg_ep0_start_transfer( GET_CORE_IF(pcd), &ep->dwc_ep ); } else { /* Setup and start the Transfer */ ep->dwc_ep.dma_addr = (u32)_req->buf & ~Uncache_Mask; //_req->dma; //cathy //ep->dwc_ep.dma_addr = _req->dma; ep->dwc_ep.start_xfer_buff = _req->buf; ep->dwc_ep.xfer_buff = _req->buf; ep->dwc_ep.xfer_len = _req->length; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; dwc_otg_ep_start_transfer( GET_CORE_IF(pcd), &ep->dwc_ep ); } } if ((req != 0) || prevented) { ++pcd->request_pending; list_add_tail(&req->queue, &ep->queue); //cathy #if 0 if (ep->dwc_ep.is_in && ep->stopped && !(GET_CORE_IF(pcd)->dma_enable)) { /** @todo NGS Create a function for this. */ diepmsk_data_t diepmsk = { .d32 = 0}; diepmsk.b.intktxfemp = 1; dwc_modify_reg32( &GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32 ); } #endif }
/** * This function initialized the PCD portion of the driver. * */ int pcd_init( struct platform_device *_dev ) { dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); int retval = 0; int irq; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); wake_lock_init(&usb_wake_lock, WAKE_LOCK_SUSPEND, "usb_work"); otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if); if (!otg_dev->pcd) { DWC_ERROR("dwc_otg_pcd_init failed\n"); return -ENOMEM; } gadget_wrapper = alloc_wrapper(_dev); /* * Initialize EP structures */ gadget_add_eps(gadget_wrapper); /* * Setup interupt handler */ irq = platform_get_irq(_dev, 0); DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq); retval = request_irq(irq, dwc_otg_pcd_irq, 0, gadget_wrapper->gadget.name, otg_dev->pcd); //SA_SHIRQ, gadget_wrapper->gadget.name, if (retval != 0) { DWC_ERROR("request of irq%d failed\n", irq); free_wrapper(gadget_wrapper); return -EBUSY; } /* * initialize a timer for checking cable type. */ { setup_timer(&setup_transfer_timer,setup_transfer_timer_fun,(unsigned long)gadget_wrapper); setup_transfer_timer_start = 0; } setup_timer(&gadget_wrapper->cable_timer, cable_detect_handler, (unsigned long)gadget_wrapper); /* * setup usb cable detect interupt */ { int plug_irq; plug_irq = usb_alloc_vbus_irq(); if (plug_irq < 0) { pr_warning("cannot alloc vbus irq\n"); return -EBUSY; } usb_set_vbus_irq_type(plug_irq, VBUS_PLUG_IN); gadget_wrapper->vbus = usb_get_vbus_state(); pr_info("now usb vbus is :%d\n", gadget_wrapper->vbus); retval = request_irq(plug_irq, usb_detect_handler, IRQF_SHARED, "usb detect", otg_dev->pcd); } spin_lock_init(&gadget_wrapper->lock); INIT_WORK(&gadget_wrapper->detect_work, usb_detect_works); gadget_wrapper->detect_wq = create_singlethread_workqueue("usb detect wq"); /* * register a switch device for sending pnp message, * for the user app need be notified immediately * when plug in & plug out happen; */ gadget_wrapper->sdev.name = "charger_cable"; retval = switch_dev_register(&gadget_wrapper->sdev); if (retval){ pr_warning("register switch dev error:%s\n", __func__); } dwc_otg_pcd_start(gadget_wrapper->pcd, &fops); /* * dwc driver is ok, check if the cable is insert, if no, * shutdown udc for saving power. */ if (!gadget_wrapper->vbus){ pr_debug("vbus is not power now \n"); gadget_wrapper->udc_startup = 1; __udc_shutdown(); } gadget_wrapper->udc_startup = gadget_wrapper->vbus; gadget_wrapper->enabled = 0; return retval; }
/** * This function is called during module intialization to verify that * the module parameters are in a valid state. */ static int check_parameters(dwc_otg_core_if_t *core_if) { int i; int retval = 0; /* Checks if the parameter is outside of its valid range of values */ #define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \ ((dwc_otg_module_params._param_ < (_low_)) || \ (dwc_otg_module_params._param_ > (_high_))) /* If the parameter has been set by the user, check that the parameter value is * within the value range of values. If not, report a module error. */ #define DWC_OTG_PARAM_ERR(_param_, _low_, _high_, _string_) \ do { \ if (dwc_otg_module_params._param_ != -1) { \ if (DWC_OTG_PARAM_TEST(_param_, (_low_), (_high_))) { \ DWC_ERROR("`%d' invalid for parameter `%s'\n", \ dwc_otg_module_params._param_, _string_); \ dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \ retval++; \ } \ } \ } while (0) DWC_OTG_PARAM_ERR(opt,0,1,"opt"); DWC_OTG_PARAM_ERR(otg_cap,0,2,"otg_cap"); DWC_OTG_PARAM_ERR(dma_enable,0,1,"dma_enable"); DWC_OTG_PARAM_ERR(dma_desc_enable,0,1,"dma_desc_enable"); DWC_OTG_PARAM_ERR(speed,0,1,"speed"); DWC_OTG_PARAM_ERR(host_support_fs_ls_low_power,0,1,"host_support_fs_ls_low_power"); DWC_OTG_PARAM_ERR(host_ls_low_power_phy_clk,0,1,"host_ls_low_power_phy_clk"); DWC_OTG_PARAM_ERR(enable_dynamic_fifo,0,1,"enable_dynamic_fifo"); DWC_OTG_PARAM_ERR(data_fifo_size,32,32768,"data_fifo_size"); DWC_OTG_PARAM_ERR(dev_rx_fifo_size,16,32768,"dev_rx_fifo_size"); DWC_OTG_PARAM_ERR(dev_nperio_tx_fifo_size,16,32768,"dev_nperio_tx_fifo_size"); DWC_OTG_PARAM_ERR(host_rx_fifo_size,16,32768,"host_rx_fifo_size"); DWC_OTG_PARAM_ERR(host_nperio_tx_fifo_size,16,32768,"host_nperio_tx_fifo_size"); DWC_OTG_PARAM_ERR(host_perio_tx_fifo_size,16,32768,"host_perio_tx_fifo_size"); DWC_OTG_PARAM_ERR(max_transfer_size,2047,524288,"max_transfer_size"); DWC_OTG_PARAM_ERR(max_packet_count,15,511,"max_packet_count"); DWC_OTG_PARAM_ERR(host_channels,1,16,"host_channels"); DWC_OTG_PARAM_ERR(dev_endpoints,1,15,"dev_endpoints"); DWC_OTG_PARAM_ERR(phy_type,0,2,"phy_type"); DWC_OTG_PARAM_ERR(phy_ulpi_ddr,0,1,"phy_ulpi_ddr"); DWC_OTG_PARAM_ERR(phy_ulpi_ext_vbus,0,1,"phy_ulpi_ext_vbus"); DWC_OTG_PARAM_ERR(i2c_enable,0,1,"i2c_enable"); DWC_OTG_PARAM_ERR(ulpi_fs_ls,0,1,"ulpi_fs_ls"); DWC_OTG_PARAM_ERR(ts_dline,0,1,"ts_dline"); if (dwc_otg_module_params.dma_burst_size != -1) { if (DWC_OTG_PARAM_TEST(dma_burst_size,1,1) && DWC_OTG_PARAM_TEST(dma_burst_size,4,4) && DWC_OTG_PARAM_TEST(dma_burst_size,8,8) && DWC_OTG_PARAM_TEST(dma_burst_size,16,16) && DWC_OTG_PARAM_TEST(dma_burst_size,32,32) && DWC_OTG_PARAM_TEST(dma_burst_size,64,64) && DWC_OTG_PARAM_TEST(dma_burst_size,128,128) && DWC_OTG_PARAM_TEST(dma_burst_size,256,256)) { DWC_ERROR("`%d' invalid for parameter `dma_burst_size'\n", dwc_otg_module_params.dma_burst_size); dwc_otg_module_params.dma_burst_size = 32; retval++; } { uint8_t brst_sz = 0; while(dwc_otg_module_params.dma_burst_size > 1) { brst_sz ++; dwc_otg_module_params.dma_burst_size >>= 1; } dwc_otg_module_params.dma_burst_size = brst_sz; } } if (dwc_otg_module_params.phy_utmi_width != -1) { if (DWC_OTG_PARAM_TEST(phy_utmi_width, 8, 8) && DWC_OTG_PARAM_TEST(phy_utmi_width, 16, 16)) { DWC_ERROR("`%d' invalid for parameter `phy_utmi_width'\n", dwc_otg_module_params.phy_utmi_width); dwc_otg_module_params.phy_utmi_width = 16; retval++; } } for (i = 0; i < 15; i++) { /** @todo should be like above */ //DWC_OTG_PARAM_ERR(dev_perio_tx_fifo_size[i], 4, 768, "dev_perio_tx_fifo_size"); if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) { if (DWC_OTG_PARAM_TEST(dev_perio_tx_fifo_size[i], 4, 768)) { DWC_ERROR("`%d' invalid for parameter `%s_%d'\n", dwc_otg_module_params.dev_perio_tx_fifo_size[i], "dev_perio_tx_fifo_size", i); dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default; retval++; } } } DWC_OTG_PARAM_ERR(en_multiple_tx_fifo, 0, 1, "en_multiple_tx_fifo"); for (i = 0; i < 15; i++) { /** @todo should be like above */ //DWC_OTG_PARAM_ERR(dev_tx_fifo_size[i], 4, 768, "dev_tx_fifo_size"); if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) { if (DWC_OTG_PARAM_TEST(dev_tx_fifo_size[i], 4, 768)) { DWC_ERROR("`%d' invalid for parameter `%s_%d'\n", dwc_otg_module_params.dev_tx_fifo_size[i], "dev_tx_fifo_size", i); dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default; retval++; } } } DWC_OTG_PARAM_ERR(thr_ctl, 0, 7, "thr_ctl"); DWC_OTG_PARAM_ERR(tx_thr_length, 8, 128, "tx_thr_length"); DWC_OTG_PARAM_ERR(rx_thr_length, 8, 128, "rx_thr_length"); DWC_OTG_PARAM_ERR(pti_enable,0,1,"pti_enable"); DWC_OTG_PARAM_ERR(mpi_enable,0,1,"mpi_enable"); /* At this point, all module parameters that have been set by the user * are valid, and those that have not are left unset. Now set their * default values and/or check the parameters against the hardware * configurations of the OTG core. */ /* This sets the parameter to the default value if it has not been set by the * user */ #define DWC_OTG_PARAM_SET_DEFAULT(_param_) \ ({ \ int changed = 1; \ if (dwc_otg_module_params._param_ == -1) { \ changed = 0; \ dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \ } \ changed; \ }) /* This checks the macro agains the hardware configuration to see if it is * valid. It is possible that the default value could be invalid. In this * case, it will report a module error if the user touched the parameter. * Otherwise it will adjust the value without any error. */ #define DWC_OTG_PARAM_CHECK_VALID(_param_, _str_, _is_valid_, _set_valid_) \ ({ \ int changed = DWC_OTG_PARAM_SET_DEFAULT(_param_); \ int error = 0; \ if (!(_is_valid_)) { \ if (changed) { \ DWC_ERROR("`%d' invalid for parameter `%s'. Check HW configuration.\n", dwc_otg_module_params._param_, _str_); \ error = 1; \ } \ dwc_otg_module_params._param_ = (_set_valid_); \ } \ error; \ }) /* OTG Cap */ retval += DWC_OTG_PARAM_CHECK_VALID(otg_cap, "otg_cap", ({ int valid; valid = 1; switch (dwc_otg_module_params.otg_cap) { case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE: if (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) valid = 0; break; case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE: if ((core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) && (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) && (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) && (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) { valid = 0; } break; case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE: /* always valid */ break; } valid; }),
int hcd_init(struct platform_device *_dev) { struct usb_hcd *hcd = NULL; dwc_otg_hcd_t *dwc_otg_hcd = NULL; dwc_otg_device_t *otg_dev = platform_get_otgdata(_dev); int irq; int retval = 0; DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n"); /* Set device flags indicating whether the HCD supports DMA. */ if (dwc_otg_is_dma_enable(otg_dev->core_if)) { _dev->dev.dma_mask = &dwc_otg_dmamask/*(void *)~0*/; _dev->dev.coherent_dma_mask = ~0; } else { _dev->dev.dma_mask = (void *)0; _dev->dev.coherent_dma_mask = 0; } /* * Allocate memory for the base HCD plus the DWC OTG HCD. * Initialize the base HCD. */ hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev)); if (!hcd) { retval = -ENOMEM; goto error1; } hcd->regs = otg_dev->base; hcd->has_tt = 1; /* Initialize the DWC OTG HCD. */ dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); if (!dwc_otg_hcd) { goto error2; } ((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd = dwc_otg_hcd; otg_dev->hcd = dwc_otg_hcd; if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) { goto error2; } hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the * IRQ line, and calls hcd_start method. */ irq = platform_get_irq(_dev, 0); if(irq < 0) { DWC_ERROR("no irq? (irq=%d)\n", irq); retval = -ENODEV; goto error2; } retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval < 0) { goto error2; } dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd); return 0; error2: usb_put_hcd(hcd); error1: return retval; }
/** * 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; 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)); //DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32 ); 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) { 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. */ pcd_stop(_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 { pcd_resume(_core_if); /* 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); 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); pcd_stop(_core_if); /* * Initialize the Core for Host mode. */ hcd_start(_core_if); _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); hcd_disconnect(_core_if); pcd_start(_core_if); _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); pcd_stop(_core_if); hcd_start(_core_if); _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; }
/** * dwc_otg_device_start * * Start device mode on the chip. */ int dwc_otg_device_start(dwc_otg_device_t *_dev) { int ret = 0, i; diepmsk_data_t diepmsk = { .d32 = 0 }; doepmsk_data_t doepmsk = { .d32 = 0 }; if(!_dev) { DWC_ERROR("%s passed a null device structure pointer!\n", __func__); return -EINVAL; } DWC_VERBOSE("%s(%p)\n", __func__, _dev); // Enable Interrupts if(dwc_otg_device_enable_interrupts(_dev)) DWC_WARNING("Failed to enable device interrupts.\n"); // Clear D(I|O)PCTLs. for(i = 0; i < _dev->core->num_eps; i++) { dwc_otg_write_reg32(&_dev->core->in_ep_registers[i]->diepctl, 0); dwc_otg_write_reg32(&_dev->core->out_ep_registers[i]->doepctl, 0); } // Enable EP Interrupts diepmsk.b.xfercompl = 1; diepmsk.b.ahberr = 1; diepmsk.b.timeout = 1; diepmsk.b.epdisabled = 1; diepmsk.b.inepnakeff = 1; dwc_otg_write_reg32(&_dev->core->device_registers->diepmsk, diepmsk.d32); doepmsk.b.xfercompl = 1; doepmsk.b.setup = 1; doepmsk.b.back2backsetup = 1; doepmsk.b.epdisabled = 1; dwc_otg_write_reg32(&_dev->core->device_registers->doepmsk, doepmsk.d32); // Reset the device. dwc_otg_device_usb_reset(_dev); // We're all set up, tell the usb_gadget framework // that we exist. ret = usb_gadget_register_controller(_dev); if(ret) { DWC_ERROR("Failed to register controller.\n"); return ret; } return 0; } /** * dwc_otg_device_stop * * Stop device mode on the chip. */ void dwc_otg_device_stop(dwc_otg_device_t *_dev) { if(!_dev) { DWC_WARNING("%s passed a null device structure pointer!\n", __func__); return; } if(dwc_otg_device_disable_interrupts(_dev)) DWC_WARNING("Failed to disable device interrupts.\n"); } /** * dwc_otg_device_enable_interrupts * * enables interrupts. */ int dwc_otg_device_enable_interrupts(dwc_otg_device_t *_dev) { gintmsk_data_t gintmsk = { .d32 = 0 }; DWC_VERBOSE("%s(%p)\n", __func__, _dev); gintmsk.b.usbreset = 1; gintmsk.b.usbsuspend = 1; gintmsk.b.disconnect = 1; gintmsk.b.inepintr = 1; gintmsk.b.outepintr = 1; gintmsk.b.enumdone = 1; gintmsk.b.otgintr = 1; gintmsk.b.epmismatch = 1; dwc_otg_modify_reg32(&_dev->core->registers->gintmsk, 0, gintmsk.d32); return 0; } /** * dwc_otg_device_disable_interrupts * * disables interrupts. */ int dwc_otg_device_disable_interrupts(dwc_otg_device_t *_dev) { gintmsk_data_t gintmsk = { .d32 = 0 }; DWC_VERBOSE("%s(%p)\n", __func__, _dev); gintmsk.b.usbreset = 1; gintmsk.b.usbsuspend = 1; gintmsk.b.disconnect = 1; gintmsk.b.inepintr = 1; gintmsk.b.outepintr = 1; gintmsk.b.enumdone = 1; gintmsk.b.otgintr = 1; gintmsk.b.epmismatch = 1; dwc_otg_modify_reg32(&_dev->core->registers->gintmsk, gintmsk.d32, 0); return 0; } /** * dwc_otg_device_usb_reset * * Resets the USB connection and re-enables EP0. */ int dwc_otg_device_usb_reset(dwc_otg_device_t *_dev) { diepmsk_data_t diepmsk = { .d32 = 0 }; doepmsk_data_t doepmsk = { .d32 = 0 }; dcfg_data_t dcfg = { .d32 = 0 }; daint_data_t daint = { .d32 = 0 }; unsigned long flags; DWC_VERBOSE("%s(%p)\n", __func__, _dev); spin_lock_irqsave(&_dev->core->lock, flags); // Clear Device Address dcfg.d32 = dwc_otg_read_reg32(&_dev->core->device_registers->dcfg); dcfg.b.devaddr = 0; dcfg.b.epmscnt = 1; // TODO: if shared fifo dwc_otg_write_reg32(&_dev->core->device_registers->dcfg, dcfg.d32); // Reset global NAK registers. _dev->core->global_in_nak_count = 0; _dev->core->global_out_nak_count = 0; dwc_otg_core_clear_global_in_nak(_dev->core); dwc_otg_core_clear_global_out_nak(_dev->core); // Reset EPs dwc_otg_core_ep_reset(_dev->core); // Enable interrupts on EP0 daint.b.inep0 = 1; daint.b.outep0 = 1; dwc_otg_write_reg32(&_dev->core->device_registers->daintmsk, daint.d32); // Clear EP0 interrupts dwc_otg_write_reg32(&_dev->core->in_ep_registers[0]->diepint, 0xffffffff); dwc_otg_write_reg32(&_dev->core->out_ep_registers[0]->doepint, 0xffffffff); // Enable EP Interrupts diepmsk.b.xfercompl = 1; diepmsk.b.ahberr = 1; diepmsk.b.timeout = 1; diepmsk.b.epdisabled = 1; diepmsk.b.inepnakeff = 1; dwc_otg_write_reg32(&_dev->core->device_registers->diepmsk, diepmsk.d32); doepmsk.b.xfercompl = 1; doepmsk.b.setup = 1; doepmsk.b.back2backsetup = 1; doepmsk.b.epdisabled = 1; dwc_otg_write_reg32(&_dev->core->device_registers->doepmsk, doepmsk.d32); spin_unlock_irqrestore(&_dev->core->lock, flags); // Notify gadget driver if(dwc_otg_gadget_driver && dwc_otg_gadget_driver->resume) dwc_otg_gadget_driver->resume(&dwc_otg_gadget); return 0; } /** The EP0 USB descriptor! */ static struct usb_endpoint_descriptor ep0_descriptor = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0, .bmAttributes = 0, }; /** The OUT request for EP0. */ static dwc_otg_core_request_t ep0_out_request = { .request_type = DWC_EP_TYPE_CONTROL, .direction = DWC_OTG_REQUEST_OUT, .completed_handler = &dwc_otg_device_complete_ep0, .dont_free = 1, .buffer_length = 64, .dma_buffer = NULL, }; /** The IN request for EP0. */ static dwc_otg_core_request_t ep0_in_request = { .request_type = DWC_EP_TYPE_CONTROL, .direction = DWC_OTG_REQUEST_IN, .dont_free = 1, .buffer_length = 64, .dma_buffer = NULL, }; /** * The device USB IRQ handler. */ irqreturn_t dwc_otg_device_irq(int _irq, void *_dev) { dwc_otg_device_t *dev = (dwc_otg_device_t*)_dev; dwc_otg_core_t *core = dev->core; gintsts_data_t gintsts = { .d32 = 0 }; gintsts_data_t gintclr = { .d32 = 0 }; gintsts.d32 = dwc_otg_read_reg32(&core->registers->gintsts) & dwc_otg_read_reg32(&core->registers->gintmsk); DWC_VERBOSE("%s(%d, %p) gintsts=0x%08x\n", __func__, _irq, _dev, gintsts.d32); if(gintsts.b.otgintr) { gotgint_data_t gotgint; gotgint.d32 = dwc_otg_read_reg32(&core->registers->gotgint); DWC_DEBUG("otgintr 0x%08x\n", gotgint.d32); if(gotgint.b.sesenddet) { dcfg_data_t dcfg = { .d32 = dwc_otg_read_reg32(&core->device_registers->dcfg) }; DWC_DEBUG("session end detected\n"); dcfg.b.nzstsouthshk = 1; dwc_otg_write_reg32(&core->device_registers->dcfg, dcfg.d32); DWC_DEBUG("DCFG=0x%08x\n", dcfg.d32); schedule_work(&dev->disconnect_work); } // Clear OTG interrupts dwc_otg_write_reg32(&core->registers->gotgint, 0xffffffff); gintclr.b.otgintr = 1; } if(gintsts.b.enumdone) { int dwcSpeed; int i; dsts_data_t dsts; DWC_DEBUG("enumdone\n"); // Enable EP0. dwc_otg_core_enable_ep(core, &core->endpoints[0], &ep0_descriptor); //ep0_in_request.queued = 0; //ep0_out_request.queued = 0; // Listen for setup packets on EP0. dwc_otg_device_receive_ep0(_dev); // Tell gadget driver our top speed. dsts.d32 = dwc_otg_read_reg32(&core->device_registers->dsts); switch (dsts.b.enumspd) { case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: dwc_otg_gadget.speed = USB_SPEED_HIGH; dwcSpeed = DWC_OTG_HIGH_SPEED; break; case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: dwc_otg_gadget.speed = USB_SPEED_FULL; dwcSpeed = DWC_OTG_FULL_SPEED; break; case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: dwc_otg_gadget.speed = USB_SPEED_LOW; dwcSpeed = DWC_OTG_LOW_SPEED; break; } // Update the endpoint speeds. for(i = 1; i < core->num_eps; i++) core->endpoints[i].speed = dwcSpeed; gintclr.b.enumdone = 1; } if(gintsts.b.usbreset) { DWC_DEBUG("usbreset\n"); //schedule_work(&dev->reset_work); dwc_otg_device_usb_reset(dev); gintclr.b.usbreset = 1; } if(gintsts.b.usbsuspend) { DWC_DEBUG("usbsuspend\n"); schedule_work(&dev->suspend_work); gintclr.b.usbsuspend = 1; } if(gintsts.b.disconnect) { DWC_DEBUG("disconnect\n"); schedule_work(&dev->disconnect_work); gintclr.b.disconnect = 1; } if(gintsts.b.epmismatch) { dtknq1_data_t t1; int t2, t3, t4; int i; int num_tokens; int doneEPs = 0; dwc_otg_core_request_t *req; DWC_ERROR("EP Mismatch.\n"); dwc_otg_core_set_global_in_nak(dev->core); for(i = 0; i < dev->core->num_eps; i++) { dwc_otg_core_ep_t *ep = &core->endpoints[i]; req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); if(req && req->direction == DWC_OTG_REQUEST_IN) { dwc_otg_core_cancel_ep(dev->core, ep); } } dwc_otg_core_clear_global_in_nak(dev->core); t1.d32 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr1); t2 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr2); t3 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr3_dthrctl); t4 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr4_fifoemptymsk); num_tokens = min((int)dev->core->hwcfg2.b.dev_token_q_depth, (int)t1.b.intknwptr); DWC_PRINT("Requeing %d requests.\n", num_tokens); for(i = 0; i < num_tokens; i++) { int ep; #define GET_BITS(x, start, len) (((x) << ((sizeof(x)*8)-(len)-(start))) >> ((sizeof(x)*8)-(len))) if(i < 6) ep = GET_BITS((int)t1.b.epnums0_5, 8 + (i*4), 4); else if(i < 14) ep = GET_BITS(t2, (i-6)*4, 4); else if(i < 21) ep = GET_BITS(t3, (i-14)*4, 4); else if(i < 30) ep = GET_BITS(t4, (i-21)*4, 4); else break; if(doneEPs & (1 << ep)) break; DWC_ERROR("Requeing %d.\n", ep); req = list_first_entry(&core->endpoints[i].transfer_queue, dwc_otg_core_request_t, queue_pointer); if(req && req->direction == DWC_OTG_REQUEST_IN) dwc_otg_core_start_request(core, req); else DWC_ERROR("Tried to requeue invalid request %p.\n", req); doneEPs |= (1 << ep); } for(i = 0; i < dev->core->num_eps; i++) { if(doneEPs & (1 << i)) continue; req = list_first_entry(&core->endpoints[i].transfer_queue, dwc_otg_core_request_t, queue_pointer); if(req && req->direction == DWC_OTG_REQUEST_IN) dwc_otg_core_start_request(core, req); //else // DWC_ERROR("Tried to requeue invalid request %p.\n", req); } gintclr.b.epmismatch = 1; } if(gintsts.b.inepintr || gintsts.b.outepintr) { daint_data_t daint = { .d32 = 0 }; int i; DWC_DEBUG("epint\n"); daint.d32 = dwc_otg_read_reg32(&core->device_registers->daint) & dwc_otg_read_reg32(&core->device_registers->daintmsk); for(i = 0; i < core->num_eps; i++) { if(daint.ep.in & 1) dwc_otg_device_handle_in_interrupt(dev, i); if(daint.ep.out & 1) dwc_otg_device_handle_out_interrupt(dev, i); daint.ep.out >>= 1; daint.ep.in >>= 1; } } // Clear the interrupts gintsts.d32 &= ~gintclr.d32; dwc_otg_write_reg32(&core->registers->gintsts, gintclr.d32); return gintsts.d32 != 0 ? IRQ_NONE : IRQ_HANDLED; } /** * The in endpoint interrupt handler. */ irqreturn_t dwc_otg_device_handle_in_interrupt(dwc_otg_device_t *_dev, int _ep) { dwc_otg_core_ep_t *ep = &_dev->core->endpoints[_ep]; diepint_data_t depint = { .d32 = 0 }; diepint_data_t depclr = { .d32 = 0 }; DWC_VERBOSE("%s(%p, %d)\n", __func__, _dev, _ep); depint.d32 = dwc_otg_read_reg32(&ep->in_registers->diepint) & dwc_otg_read_reg32(&_dev->core->device_registers->diepmsk); if(!ep->exists) { DWC_ERROR("%s called on non-existant in endpoint %d.\n", __func__, _ep); return -ENXIO; } if(depint.b.inepnakeff) { DWC_DEBUG("in nak eff %s\n", ep->name); complete_all(&ep->nakeff_completion); init_completion(&ep->nakeff_completion); depclr.b.inepnakeff = 1; } if(depint.b.epdisabled) { DWC_DEBUG("in epdisabled %s\n", ep->name); complete_all(&ep->disabled_completion); init_completion(&ep->disabled_completion); depclr.b.epdisabled = 1; } /*if(depint.b.intknepmis) { gintsts_data_t gintsts = { .d32 = 0 }; DWC_DEBUG("in tn mis\n"); gintsts.b.epmismatch = 1; dwc_otg_modify_reg32(&_dev->core->registers->gintsts, gintsts.d32, 0); }*/ if(depint.b.intktxfemp) { DWC_DEBUG("in tx f emp\n"); // Do nothing... ;_; depclr.b.intktxfemp = 1; } if(depint.b.timeout) { DWC_DEBUG("in timeout %s\n", ep->name); // Do something!? // Cancel sending?! depclr.b.timeout = 1; } if(depint.b.ahberr) { DWC_DEBUG("in ahberr\n"); // Do nothing again... :'( depclr.b.ahberr = 1; } if(depint.b.xfercompl) { dwc_otg_core_request_t *req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); DWC_DEBUG("in xfercompl %s\n", ep->name); if(list_empty(&ep->transfer_queue) || req->direction != DWC_OTG_REQUEST_IN) { DWC_ERROR("XferCompl received when we weren't transferring anything!\n"); } else dwc_otg_core_complete_request(_dev->core, req); depclr.b.xfercompl = 1; } // Clear interrupts dwc_otg_write_reg32(&ep->in_registers->diepint, depclr.d32); return 0; } /** * The out endpoint interrupt handler. */ irqreturn_t dwc_otg_device_handle_out_interrupt(dwc_otg_device_t *_dev, int _ep) { dwc_otg_core_ep_t *ep = &_dev->core->endpoints[_ep]; doepint_data_t depint = { .d32 = 0 }; doepint_data_t depclr = { .d32 = 0 }; DWC_VERBOSE("%s(%p, %d)\n", __func__, _dev, _ep); depint.d32 = dwc_otg_read_reg32(&ep->out_registers->doepint) & dwc_otg_read_reg32(&_dev->core->device_registers->doepmsk); if(!ep->exists) { DWC_ERROR("%s called on non-existant in endpoint %d.\n", __func__, _ep); return -ENXIO; } if(depint.b.setup) { dwc_otg_core_request_t *req; // We received a setup packet, yaaay! DWC_VERBOSE("setup\n"); // If we had a setup packet, set the flag on the // request that says it was prepended by a setup // token. req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); if(!list_empty(&ep->transfer_queue) && req->direction == DWC_OTG_REQUEST_OUT) { req->setup = 1; dwc_otg_core_complete_request(_dev->core, req); } else DWC_ERROR("Setup Packet Received without us initiating a transfer!\n"); depclr.b.setup = 1; } if(depint.b.back2backsetup) { // Do nothing DWC_DEBUG("back2backsetup\n"); depclr.b.back2backsetup = 1; } if(depint.b.epdisabled) { DWC_DEBUG("out epdisabled\n"); complete_all(&ep->disabled_completion); init_completion(&ep->disabled_completion); depclr.b.epdisabled = 1; } if(depint.b.ahberr) { // Do nothing again... :'( DWC_DEBUG("out ahberr\n"); depclr.b.ahberr = 1; } if(depint.b.outtknepdis) { // Nothing to do again! Wah... :''( DWC_DEBUG("out tkn epdis\n"); depclr.b.outtknepdis = 1; } if(depint.b.xfercompl) { dwc_otg_core_request_t *req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); DWC_DEBUG("out xfercompl\n"); // Yay! :D if(list_empty(&ep->transfer_queue) || req->direction != DWC_OTG_REQUEST_OUT) { DWC_ERROR("XferCompl received when we weren't transferring anything!\n"); } else dwc_otg_core_complete_request(_dev->core, req); depclr.b.xfercompl = 1; } // Clear interrupts dwc_otg_write_reg32(&ep->out_registers->doepint, depclr.d32); return 0; } /** * dwc_otg_device_receive_ep0 */ void dwc_otg_device_receive_ep0(dwc_otg_device_t *_dev) { ep0_out_request.data = _dev; ep0_out_request.length = 8; //ep0_out_request.buffer_length; if(ep0_out_request.dma_buffer == NULL) { ep0_out_request.dma_buffer = dma_alloc_coherent(NULL, ep0_out_request.buffer_length, &ep0_out_request.dma_address, GFP_KERNEL); if(!ep0_out_request.dma_buffer) { DWC_ERROR("Failed to allocate setup buffer for EP0!\n"); return; } // As we've allocated a DMA buffer, we don't // need another. :D -- Ricky26 ep0_out_request.buffer = ep0_out_request.dma_buffer; // Setup the in request, as we might need it as a response. ep0_in_request.length = ep0_in_request.buffer_length; ep0_in_request.dma_buffer = dma_alloc_coherent(NULL, ep0_in_request.buffer_length, &ep0_in_request.dma_address, GFP_KERNEL); if(!ep0_in_request.dma_buffer) { DWC_ERROR("Failed to allocate in buffer for EP0!\n"); return; } ep0_in_request.buffer = ep0_in_request.dma_buffer; } DWC_VERBOSE("%s: enqueue (%p):%d\n", __func__, &ep0_out_request, ep0_out_request.length); dwc_otg_core_enqueue_request(_dev->core, &_dev->core->endpoints[0], &ep0_out_request); } /** * dwc_otg_device_send_ep0 */ void dwc_otg_device_send_ep0(dwc_otg_device_t *_dev) { ep0_in_request.data = _dev; dwc_otg_core_enqueue_request(_dev->core, &_dev->core->endpoints[0], &ep0_in_request); } /** * dwc_otg_device_complete_ep0 * * This is called to complete an interrupt on EP0. */ void dwc_otg_device_complete_ep0(dwc_otg_core_request_t *_req) { dwc_otg_core_t *core = _req->core; dwc_otg_device_t *dev = (dwc_otg_device_t*)_req->data; if(_req->cancelled) { DWC_VERBOSE("Shutting down EP0 control channel.\n"); return; } if(_req->setup) { struct usb_ctrlrequest *packet = (struct usb_ctrlrequest*)_req->buffer; #if defined(VERBOSE)&&defined(DEBUG) uint8_t *b = (uint8_t*)packet; DWC_PRINT("EP0 Sent us a setup packet! :3\n"); DWC_PRINT("%02x %02x %02x %02x %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); DWC_PRINT("RT %d, R %d, w %d\n", packet->bRequestType & 0x7F, packet->bRequest, packet->wIndex); #endif if((packet->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { int set = 0; switch(packet->bRequest) { case USB_REQ_GET_STATUS: { uint16_t *result = (uint16_t*)ep0_in_request.buffer; switch (packet->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: *result = 0x1 | (1 << dev->remote_wakeup); // Self powered, remote wakeup enabled. break; case USB_RECIP_INTERFACE: *result = 0; break; case USB_RECIP_ENDPOINT: { // TODO: check this! int epnum = packet->wIndex & 0xf; if(epnum < core->num_eps && list_empty(&core->endpoints[epnum].transfer_queue)) { *result = 0; break; } *result = 1; } break; } ep0_in_request.length = 2; dwc_otg_device_send_ep0(dev); } goto exit; case USB_REQ_SET_FEATURE: set = 1; // fall through case USB_REQ_CLEAR_FEATURE: switch (packet->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: switch(packet->wValue) { case USB_DEVICE_REMOTE_WAKEUP: dev->remote_wakeup = set; break; } break; case USB_RECIP_ENDPOINT: { int ep = packet->wValue; if(ep == 0 || ep > core->num_eps) goto stall; dwc_otg_core_stall_ep(core, &core->endpoints[ep], set); } break; } goto send_zlp; case USB_REQ_SET_ADDRESS: { dcfg_data_t dcfg = { .d32 = 0 }; dcfg.d32 = dwc_otg_read_reg32(&core->device_registers->dcfg); dcfg.b.devaddr = packet->wValue; dwc_otg_write_reg32(&core->device_registers->dcfg, dcfg.d32); DWC_VERBOSE("Received address %d.\n", dcfg.b.devaddr); } goto send_zlp; } } // If we got here we don't know how to handle // the setup packet, so let's ask the gadget driver to // do it, and then blame it if it doesn't work... DWC_VERBOSE("Passing setup packet to gadget driver.\n"); if(dwc_otg_gadget_driver == NULL) { DWC_WARNING("No gadget driver yet, stalling.\n"); goto stall; } if(dwc_otg_gadget_driver->setup(&dwc_otg_gadget, packet)) { DWC_ERROR("Got a setup packet we don't handle properly yet.\n"); goto stall; } goto exit; } else if(ep0_out_request.length == 0) { DWC_VERBOSE("Received a ZLP from host.\n"); } else { DWC_ERROR("EP0 sent us some shit we don't know how to handle.\n"); } goto exit; stall: //dwc_otg_core_stall_ep(core, &core->endpoints[0], 1); { depctl_data_t depctl = { .d32 = dwc_otg_read_reg32(&core->in_ep_registers[0]->diepctl) }; depctl.b.stall = 1; depctl.b.cnak = 1; dwc_otg_write_reg32(&core->in_ep_registers[0]->diepctl, depctl.d32); } goto exit; send_zlp: ep0_in_request.length = 0; dwc_otg_device_send_ep0(dev); exit: dwc_otg_device_receive_ep0(dev); }
void w_conn_id_status_change(struct work_struct *p) { dwc_otg_core_if_t *core_if = container_of(p, dwc_otg_core_if_t, w_conn_id); uint32_t count = 0; gotgctl_data_t gotgctl = { .d32 = 0 }; gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); /* B-Device connector (Device Mode) */ if (gotgctl.b.conidsts) { dwc_otg_pcd_t *pcd; /* Wait for switch to device mode. */ while (!dwc_otg_is_device_mode(core_if)){ DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n", (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral")); MDELAY(100); if (++count > 10000) *(uint32_t*)NULL=0; } core_if->op_state = B_PERIPHERAL; dwc_otg_core_init(core_if); dwc_otg_enable_global_interrupts(core_if); pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p; if(unlikely(!pcd)) { DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__); BUG(); } SPIN_LOCK(&pcd->lock); pcd_start(core_if); SPIN_UNLOCK(&pcd->lock); } else { /* A-Device connector (Host Mode) */ while (!dwc_otg_is_host_mode(core_if)) { DWC_PRINT("Waiting for Host Mode, Mode=%s\n", (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral")); MDELAY(100); if (++count > 10000) *(uint32_t*)NULL=0; } core_if->op_state = A_HOST; /* * Initialize the Core for Host mode. */ dwc_otg_core_init(core_if); dwc_otg_enable_global_interrupts(core_if); hcd_start(core_if); } } /** * This function handles the Connector ID Status Change Interrupt. It * reads the OTG Interrupt Register (GOTCTL) to determine whether this * is a Device to Host Mode transition or a Host Mode to Device * Transition. * * This only occurs when the cable is connected/removed from the PHY * connector. * * @param core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *core_if) { /* * Need to disable SOF interrupt immediately. If 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_data_t gintmsk = { .d32 = 0 }; gintsts_data_t gintsts = { .d32 = 0 }; gintmsk.b.sofintr = 1; dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", (dwc_otg_is_host_mode(core_if)?"Host":"Device")); /* * Need to schedule a work, as there are possible DELAY function calls */ queue_work(core_if->wq_otg, &core_if->w_conn_id); /* Set flag and clear interrupt */ gintsts.b.conidstschng = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that a device is initiating the Session * Request Protocol to request the host to turn on bus power so a new * session can begin. The handler responds by turning on bus power. If * the DWC_otg controller is in low power mode, the handler brings the * controller out of low power mode before turning on bus power. * * @param core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t *core_if) { hprt0_data_t hprt0; gintsts_data_t gintsts; #ifndef DWC_HOST_ONLY DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); if (dwc_otg_is_device_mode(core_if)) { DWC_PRINT("SRP: Device mode\n"); } else { DWC_PRINT("SRP: Host mode\n"); /* Turn on the port power bit. */ hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtpwr = 1; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); /* Start the Connection timer. So a message can be displayed * if connect does not occur within 10 seconds. */ hcd_session_start(core_if); } #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.sessreqintr = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } void w_wakeup_detected(struct work_struct *p) { struct delayed_work *dw = container_of(p, struct delayed_work, work); dwc_otg_core_if_t *core_if = container_of(dw, dwc_otg_core_if_t, w_wkp); /* * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms * so that OPT tests pass with all PHYs). */ hprt0_data_t hprt0 = {.d32=0}; hprt0.d32 = dwc_otg_read_hprt0(core_if); DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); // MDELAY(70); hprt0.b.prtres = 0; /* Resume */ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(core_if->host_if->hprt0)); } /** * This interrupt indicates that the DWC_otg controller has detected a * resume or remote wakeup sequence. If the DWC_otg controller is in * low power mode, the handler must brings the controller out of low * power mode. The controller automatically begins resume * signaling. The handler schedules a time to stop resume signaling. */ int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t *core_if) { gintsts_data_t gintsts; DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); if (dwc_otg_is_device_mode(core_if)) { dctl_data_t dctl = {.d32=0}; DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts)); #ifdef PARTIAL_POWER_DOWN if (core_if->hwcfg4.b.power_optimiz) { pcgcctl_data_t power = {.d32=0}; power.d32 = dwc_read_reg32(core_if->pcgcctl); DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32); power.b.stoppclk = 0; dwc_write_reg32(core_if->pcgcctl, power.d32); power.b.pwrclmp = 0; dwc_write_reg32(core_if->pcgcctl, power.d32); power.b.rstpdwnmodule = 0; dwc_write_reg32(core_if->pcgcctl, power.d32); } #endif /* Clear the Remote Wakeup Signalling */ dctl.b.rmtwkupsig = 1; dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0); if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); } } else { pcgcctl_data_t pcgcctl = {.d32=0}; /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0); queue_delayed_work(core_if->wq_otg, &core_if->w_wkp, ((70 * HZ / 1000) + 1)); } /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.wkupintr = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that a device has been disconnected from * the root port. */ int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t *core_if) { gintsts_data_t gintsts; DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", (dwc_otg_is_host_mode(core_if)?"Host":"Device"), op_state_str(core_if)); /** @todo Consolidate this if statement. */ #ifndef DWC_HOST_ONLY if (core_if->op_state == B_HOST) { dwc_otg_pcd_t *pcd; /* If in device mode Disconnect and stop the HCD, then * start the PCD. */ hcd_disconnect(core_if); pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p; if(unlikely(!pcd)) { DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__); BUG(); } SPIN_LOCK(&pcd->lock); pcd_start(core_if); SPIN_UNLOCK(&pcd->lock); core_if->op_state = B_PERIPHERAL; } else if (dwc_otg_is_device_mode(core_if)) { gotgctl_data_t gotgctl = { .d32 = 0 }; gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); if (gotgctl.b.hstsethnpen==1) { /* Do nothing, if HNP in process the OTG * interrupt "Host Negotiation Detected" * interrupt will do the mode switch. */ } else if (gotgctl.b.devhnpen == 0) { dwc_otg_pcd_t *pcd; /* If in device mode Disconnect and stop the HCD, then * start the PCD. */ hcd_disconnect(core_if); pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p; if(unlikely(!pcd)) { DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__); BUG(); } SPIN_LOCK(&pcd->lock); pcd_start(core_if); SPIN_UNLOCK(&pcd->lock); core_if->op_state = B_PERIPHERAL; } else { DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n"); } } else { if (core_if->op_state == A_HOST) { /* A-Cable still connected but device disconnected. */ hcd_disconnect(core_if); } } #endif gintsts.d32 = 0; gintsts.b.disconnect = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that SUSPEND state has been detected on * the USB. * * For HNP the USB Suspend interrupt signals the change from * "a_peripheral" to "a_host". * * When power management is enabled the core will be put in low power * mode. */ int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *core_if) { dsts_data_t dsts; gintsts_data_t gintsts; DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n"); if (dwc_otg_is_device_mode(core_if)) { dwc_otg_pcd_t *pcd; /* Check the Device status register to determine if the Suspend * state is active. */ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " "HWCFG4.power Optimize=%d\n", dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz); #ifdef PARTIAL_POWER_DOWN /** @todo Add a module parameter for power management. */ if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) { pcgcctl_data_t power = {.d32=0}; DWC_DEBUGPL(DBG_CIL, "suspend\n"); power.b.pwrclmp = 1; dwc_write_reg32(core_if->pcgcctl, power.d32); power.b.rstpdwnmodule = 1; dwc_modify_reg32(core_if->pcgcctl, 0, power.d32); power.b.stoppclk = 1; dwc_modify_reg32(core_if->pcgcctl, 0, power.d32); } else { DWC_DEBUGPL(DBG_ANY,"disconnect?\n"); } #endif /* PCD callback for suspend. */ pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p; if(unlikely(!pcd)) { DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__); BUG(); } SPIN_LOCK(&pcd->lock); pcd_suspend(core_if); SPIN_UNLOCK(&pcd->lock); } else {
/** * This function initialized the PCD portion of the driver. * */ int pcd_init( struct platform_device *_dev ) { dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); struct sprd_usb_platform_data *pdata= _dev->dev.platform_data; int retval = 0; int irq; int plug_irq; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); device_dwc_otg = &(_dev->dev); if (dwc_otg_is_dma_enable(otg_dev->core_if)) { _dev->dev.dma_mask = &dwc_otg_pcd_dmamask; _dev->dev.coherent_dma_mask = dwc_otg_pcd_dmamask; } else { _dev->dev.dma_mask = (void *)0; _dev->dev.coherent_dma_mask = 0; } wake_lock_init(&usb_wake_lock, WAKE_LOCK_SUSPEND, "usb_work"); // wake_lock(&usb_wake_lock); otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if); if (!otg_dev->pcd) { DWC_ERROR("dwc_otg_pcd_init failed\n"); return -ENOMEM; } gadget_wrapper = alloc_wrapper(_dev); /* * Initialize EP structures */ gadget_add_eps(gadget_wrapper); /* * Setup interupt handler */ irq = platform_get_irq(_dev, 0); DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irq); retval = request_irq(irq, dwc_otg_pcd_irq, IRQF_SHARED, gadget_wrapper->gadget.name, otg_dev->pcd); //SA_SHIRQ, gadget_wrapper->gadget.name, if (retval != 0) { DWC_ERROR("request of irq%d failed\n", irq); free_wrapper(gadget_wrapper); return -EBUSY; } /* * initialize a timer for checking cable type. */ #ifdef USB_SETUP_TIMEOUT_RESTART { setup_timer(&setup_transfer_timer, setup_transfer_timer_fun, (unsigned long)gadget_wrapper); setup_transfer_timer_start = 0; } #endif INIT_DELAYED_WORK(&gadget_wrapper->cable2pc, cable2pc_detect_works); gadget_wrapper->cable2pc_wq = create_singlethread_workqueue("usb 2 pc wq"); #ifdef CONFIG_USB_EXTERNAL_DETECT register_otg_func(NULL, dwc_peripheral_start, otg_dev); #else /* * setup usb cable detect interupt */ #ifndef CONFIG_MFD_SM5504 { plug_irq = usb_alloc_vbus_irq(pdata->gpio_chgdet); if (plug_irq < 0) { pr_warning("cannot alloc vbus irq\n"); return -EBUSY; } usb_set_vbus_irq_type(plug_irq, VBUS_PLUG_IN); #ifdef CONFIG_SC_FPGA gadget_wrapper->vbus = 1; #else gadget_wrapper->vbus = usb_get_vbus_state(); #endif pr_info("now usb vbus is :%d\n", gadget_wrapper->vbus); retval = request_irq(plug_irq, usb_detect_handler, IRQF_SHARED | IRQF_NO_SUSPEND, "usb detect", otg_dev->pcd); #ifndef CONFIG_MUIC_CABLE_DETECT disable_irq(plug_irq); #endif } //gadget_wrapper->vbus = 1;//used when debug in FPGA, which doesn't have vbus operation #endif spin_lock_init(&gadget_wrapper->lock); #ifdef CONFIG_SC_FPGA gadget_wrapper->vbus = 1; #endif INIT_WORK(&gadget_wrapper->detect_work, usb_detect_works); gadget_wrapper->detect_wq = create_singlethread_workqueue("usb detect wq"); #endif /* * register a switch device for sending pnp message, * for the user app need be notified immediately * when plug in & plug out happen; */ gadget_wrapper->sdev.name = "charger_cable"; retval = switch_dev_register(&gadget_wrapper->sdev); if (retval){ pr_warning("register switch dev error:%s\n", __func__); } dwc_otg_pcd_start(gadget_wrapper->pcd, &fops); /* * dwc driver is ok, check if the cable is insert, if no, * shutdown udc for saving power. */ if (!gadget_wrapper->vbus){ pr_debug("vbus is not power now \n"); gadget_wrapper->udc_startup = 1; __udc_shutdown(); } gadget_wrapper->udc_startup = gadget_wrapper->vbus; gadget_wrapper->enabled = 0; retval = usb_add_gadget_udc(&_dev->dev, &gadget_wrapper->gadget); if (!retval) return retval; return retval; }