⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 atmel_usba_udc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
			if (crq.crq.wLength != __constant_cpu_to_le16(0))				ep->state = DATA_STAGE_OUT;			else				ep->state = STATUS_STAGE_IN;		}		ret = -1;		if (ep->index == 0)			ret = handle_ep0_setup(udc, ep, &crq.crq);		else {			spin_unlock(&udc->lock);			ret = udc->driver->setup(&udc->gadget, &crq.crq);			spin_lock(&udc->lock);		}		DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n",			crq.crq.bRequestType, crq.crq.bRequest,			le16_to_cpu(crq.crq.wLength), ep->state, ret);		if (ret < 0) {			/* Let the host know that we failed */			set_protocol_stall(udc, ep);		}	}}static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep){	struct usba_request *req;	u32 epstatus;	u32 epctrl;	epstatus = usba_ep_readl(ep, STA);	epctrl = usba_ep_readl(ep, CTL);	DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus);	while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) {		DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name);		if (list_empty(&ep->queue)) {			dev_warn(&udc->pdev->dev, "ep_irq: queue empty\n");			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);			return;		}		req = list_entry(ep->queue.next, struct usba_request, queue);		if (req->using_dma) {			/* Send a zero-length packet */			usba_ep_writel(ep, SET_STA,					USBA_TX_PK_RDY);			usba_ep_writel(ep, CTL_DIS,					USBA_TX_PK_RDY);			list_del_init(&req->queue);			submit_next_request(ep);			request_complete(ep, req, 0);		} else {			if (req->submitted)				next_fifo_transaction(ep, req);			else				submit_request(ep, req);			if (req->last_transaction) {				list_del_init(&req->queue);				submit_next_request(ep);				request_complete(ep, req, 0);			}		}		epstatus = usba_ep_readl(ep, STA);		epctrl = usba_ep_readl(ep, CTL);	}	if ((epstatus & epctrl) & USBA_RX_BK_RDY) {		DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name);		receive_data(ep);		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);	}}static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep){	struct usba_request *req;	u32 status, control, pending;	status = usba_dma_readl(ep, STATUS);	control = usba_dma_readl(ep, CONTROL);#ifdef CONFIG_USB_GADGET_DEBUG_FS	ep->last_dma_status = status;#endif	pending = status & control;	DBG(DBG_INT | DBG_DMA, "dma irq, s/%#08x, c/%#08x\n", status, control);	if (status & USBA_DMA_CH_EN) {		dev_err(&udc->pdev->dev,			"DMA_CH_EN is set after transfer is finished!\n");		dev_err(&udc->pdev->dev,			"status=%#08x, pending=%#08x, control=%#08x\n",			status, pending, control);		/*		 * try to pretend nothing happened. We might have to		 * do something here...		 */	}	if (list_empty(&ep->queue))		/* Might happen if a reset comes along at the right moment */		return;	if (pending & (USBA_DMA_END_TR_ST | USBA_DMA_END_BUF_ST)) {		req = list_entry(ep->queue.next, struct usba_request, queue);		usba_update_req(ep, req, status);		list_del_init(&req->queue);		submit_next_request(ep);		request_complete(ep, req, 0);	}}static irqreturn_t usba_udc_irq(int irq, void *devid){	struct usba_udc *udc = devid;	u32 status;	u32 dma_status;	u32 ep_status;	spin_lock(&udc->lock);	status = usba_readl(udc, INT_STA);	DBG(DBG_INT, "irq, status=%#08x\n", status);	if (status & USBA_DET_SUSPEND) {		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);		DBG(DBG_BUS, "Suspend detected\n");		if (udc->gadget.speed != USB_SPEED_UNKNOWN				&& udc->driver && udc->driver->suspend) {			spin_unlock(&udc->lock);			udc->driver->suspend(&udc->gadget);			spin_lock(&udc->lock);		}	}	if (status & USBA_WAKE_UP) {		usba_writel(udc, INT_CLR, USBA_WAKE_UP);		DBG(DBG_BUS, "Wake Up CPU detected\n");	}	if (status & USBA_END_OF_RESUME) {		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);		DBG(DBG_BUS, "Resume detected\n");		if (udc->gadget.speed != USB_SPEED_UNKNOWN				&& udc->driver && udc->driver->resume) {			spin_unlock(&udc->lock);			udc->driver->resume(&udc->gadget);			spin_lock(&udc->lock);		}	}	dma_status = USBA_BFEXT(DMA_INT, status);	if (dma_status) {		int i;		for (i = 1; i < USBA_NR_ENDPOINTS; i++)			if (dma_status & (1 << i))				usba_dma_irq(udc, &usba_ep[i]);	}	ep_status = USBA_BFEXT(EPT_INT, status);	if (ep_status) {		int i;		for (i = 0; i < USBA_NR_ENDPOINTS; i++)			if (ep_status & (1 << i)) {				if (ep_is_control(&usba_ep[i]))					usba_control_irq(udc, &usba_ep[i]);				else					usba_ep_irq(udc, &usba_ep[i]);			}	}	if (status & USBA_END_OF_RESET) {		struct usba_ep *ep0;		usba_writel(udc, INT_CLR, USBA_END_OF_RESET);		reset_all_endpoints(udc);		if (status & USBA_HIGH_SPEED) {			DBG(DBG_BUS, "High-speed bus reset detected\n");			udc->gadget.speed = USB_SPEED_HIGH;		} else {			DBG(DBG_BUS, "Full-speed bus reset detected\n");			udc->gadget.speed = USB_SPEED_FULL;		}		ep0 = &usba_ep[0];		ep0->desc = &usba_ep0_desc;		ep0->state = WAIT_FOR_SETUP;		usba_ep_writel(ep0, CFG,				(USBA_BF(EPT_SIZE, EP0_EPT_SIZE)				| USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL)				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));		usba_ep_writel(ep0, CTL_ENB,				USBA_EPT_ENABLE | USBA_RX_SETUP);		usba_writel(udc, INT_ENB,				(usba_readl(udc, INT_ENB)				| USBA_BF(EPT_INT, 1)				| USBA_DET_SUSPEND				| USBA_END_OF_RESUME));		if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED))			dev_warn(&udc->pdev->dev,				 "WARNING: EP0 configuration is invalid!\n");	}	spin_unlock(&udc->lock);	return IRQ_HANDLED;}static irqreturn_t usba_vbus_irq(int irq, void *devid){	struct usba_udc *udc = devid;	int vbus;	/* debounce */	udelay(10);	spin_lock(&udc->lock);	/* May happen if Vbus pin toggles during probe() */	if (!udc->driver)		goto out;	vbus = gpio_get_value(udc->vbus_pin);	if (vbus != udc->vbus_prev) {		if (vbus) {			usba_writel(udc, CTRL, USBA_EN_USBA);			usba_writel(udc, INT_ENB, USBA_END_OF_RESET);		} else {			udc->gadget.speed = USB_SPEED_UNKNOWN;			reset_all_endpoints(udc);			usba_writel(udc, CTRL, 0);			spin_unlock(&udc->lock);			udc->driver->disconnect(&udc->gadget);			spin_lock(&udc->lock);		}		udc->vbus_prev = vbus;	}out:	spin_unlock(&udc->lock);	return IRQ_HANDLED;}int usb_gadget_register_driver(struct usb_gadget_driver *driver){	struct usba_udc *udc = &the_udc;	unsigned long flags;	int ret;	if (!udc->pdev)		return -ENODEV;	spin_lock_irqsave(&udc->lock, flags);	if (udc->driver) {		spin_unlock_irqrestore(&udc->lock, flags);		return -EBUSY;	}	udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;	udc->driver = driver;	udc->gadget.dev.driver = &driver->driver;	spin_unlock_irqrestore(&udc->lock, flags);	clk_enable(udc->pclk);	clk_enable(udc->hclk);	ret = driver->bind(&udc->gadget);	if (ret) {		DBG(DBG_ERR, "Could not bind to driver %s: error %d\n",			driver->driver.name, ret);		goto err_driver_bind;	}	DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name);	udc->vbus_prev = 0;	if (udc->vbus_pin != -1)		enable_irq(gpio_to_irq(udc->vbus_pin));	/* If Vbus is present, enable the controller and wait for reset */	spin_lock_irqsave(&udc->lock, flags);	if (vbus_is_present(udc) && udc->vbus_prev == 0) {		usba_writel(udc, CTRL, USBA_EN_USBA);		usba_writel(udc, INT_ENB, USBA_END_OF_RESET);	}	spin_unlock_irqrestore(&udc->lock, flags);	return 0;err_driver_bind:	udc->driver = NULL;	udc->gadget.dev.driver = NULL;	return ret;}EXPORT_SYMBOL(usb_gadget_register_driver);int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){	struct usba_udc *udc = &the_udc;	unsigned long flags;	if (!udc->pdev)		return -ENODEV;	if (driver != udc->driver)		return -EINVAL;	if (udc->vbus_pin != -1)		disable_irq(gpio_to_irq(udc->vbus_pin));	spin_lock_irqsave(&udc->lock, flags);	udc->gadget.speed = USB_SPEED_UNKNOWN;	reset_all_endpoints(udc);	spin_unlock_irqrestore(&udc->lock, flags);	/* This will also disable the DP pullup */	usba_writel(udc, CTRL, 0);	driver->unbind(&udc->gadget);	udc->gadget.dev.driver = NULL;	udc->driver = NULL;	clk_disable(udc->hclk);	clk_disable(udc->pclk);	DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name);	return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);static int __init usba_udc_probe(struct platform_device *pdev){	struct usba_platform_data *pdata = pdev->dev.platform_data;	struct resource *regs, *fifo;	struct clk *pclk, *hclk;	struct usba_udc *udc = &the_udc;	int irq, ret, i;	regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);	fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);	if (!regs || !fifo)		return -ENXIO;	irq = platform_get_irq(pdev, 0);	if (irq < 0)		return irq;	pclk = clk_get(&pdev->dev, "pclk");	if (IS_ERR(pclk))		return PTR_ERR(pclk);	hclk = clk_get(&pdev->dev, "hclk");	if (IS_ERR(hclk)) {		ret = PTR_ERR(hclk);		goto err_get_hclk;	}	udc->pdev = pdev;	udc->pclk = pclk;	udc->hclk = hclk;	udc->vbus_pin = -1;	ret = -ENOMEM;	udc->regs = ioremap(regs->start, regs->end - regs->start + 1);	if (!udc->regs) {		dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n");		goto err_map_regs;	}	dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n",		 (unsigned long)regs->start, udc->regs);	udc->fifo = ioremap(fifo->start, fifo->end - fifo->start + 1);	if (!udc->fifo) {		dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n");		goto err_map_fifo;	}	dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n",		 (unsigned long)fifo->start, udc->fifo);	device_initialize(&udc->gadget.dev);	udc->gadget.dev.parent = &pdev->dev;	udc->gadget.dev.dma_mask = pdev->dev.dma_mask;	platform_set_drvdata(pdev, udc);	/* Make sure we start from a clean slate */	clk_enable(pclk);	usba_writel(udc, CTRL, 0);	clk_disable(pclk);	INIT_LIST_HEAD(&usba_ep[0].ep.ep_list);	usba_ep[0].ep_regs = udc->regs + USBA_EPT_BASE(0);	usba_ep[0].dma_regs = udc->regs + USBA_DMA_BASE(0);	usba_ep[0].fifo = udc->fifo + USBA_FIFO_BASE(0);	for (i = 1; i < ARRAY_SIZE(usba_ep); i++) {		struct usba_ep *ep = &usba_ep[i];		ep->ep_regs = udc->regs + USBA_EPT_BASE(i);		ep->dma_regs = udc->regs + USBA_DMA_BASE(i);		ep->fifo = udc->fifo + USBA_FIFO_BASE(i);		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);	}	ret = request_irq(irq, usba_udc_irq, 0, "atmel_usba_udc", udc);	if (ret) {		dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n",			irq, ret);		goto err_request_irq;	}	udc->irq = irq;	ret = device_add(&udc->gadget.dev);	if (ret) {		dev_dbg(&pdev->dev, "Could not add gadget: %d\n", ret);		goto err_device_add;	}	if (pdata && pdata->vbus_pin != GPIO_PIN_NONE) {		if (!gpio_request(pdata->vbus_pin, "atmel_usba_udc")) {			udc->vbus_pin = pdata->vbus_pin;			ret = request_irq(gpio_to_irq(udc->vbus_pin),					usba_vbus_irq, 0,					"atmel_usba_udc", udc);			if (ret) {				gpio_free(udc->vbus_pin);				udc->vbus_pin = -1;				dev_warn(&udc->pdev->dev,					 "failed to request vbus irq; "					 "assuming always on\n");			} else {				disable_irq(gpio_to_irq(udc->vbus_pin));			}		}	}	usba_init_debugfs(udc);	for (i = 1; i < ARRAY_SIZE(usba_ep); i++)		usba_ep_init_debugfs(udc, &usba_ep[i]);	return 0;err_device_add:	free_irq(irq, udc);err_request_irq:	iounmap(udc->fifo);err_map_fifo:	iounmap(udc->regs);err_map_regs:	clk_put(hclk);err_get_hclk:	clk_put(pclk);	platform_set_drvdata(pdev, NULL);	return ret;}static int __exit usba_udc_remove(struct platform_device *pdev){	struct usba_udc *udc;	int i;	udc = platform_get_drvdata(pdev);	for (i = 1; i < ARRAY_SIZE(usba_ep); i++)		usba_ep_cleanup_debugfs(&usba_ep[i]);	usba_cleanup_debugfs(udc);	if (udc->vbus_pin != -1)		gpio_free(udc->vbus_pin);	free_irq(udc->irq, udc);	iounmap(udc->fifo);	iounmap(udc->regs);	clk_put(udc->hclk);	clk_put(udc->pclk);	device_unregister(&udc->gadget.dev);	return 0;}static struct platform_driver udc_driver = {	.remove		= __exit_p(usba_udc_remove),	.driver		= {		.name		= "atmel_usba_udc",	},};static int __init udc_init(void){	return platform_driver_probe(&udc_driver, usba_udc_probe);}module_init(udc_init);static void __exit udc_exit(void){	platform_driver_unregister(&udc_driver);}module_exit(udc_exit);MODULE_DESCRIPTION("Atmel USBA UDC driver");MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -