1*5fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 20316ca63SLaurent Pinchart /* 30316ca63SLaurent Pinchart * Driver for the NXP ISP1761 device controller 40316ca63SLaurent Pinchart * 50316ca63SLaurent Pinchart * Copyright 2014 Ideas on Board Oy 60316ca63SLaurent Pinchart * 70316ca63SLaurent Pinchart * Contacts: 80316ca63SLaurent Pinchart * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 90316ca63SLaurent Pinchart * 100316ca63SLaurent Pinchart * This program is free software; you can redistribute it and/or 110316ca63SLaurent Pinchart * modify it under the terms of the GNU General Public License 120316ca63SLaurent Pinchart * version 2 as published by the Free Software Foundation. 130316ca63SLaurent Pinchart */ 140316ca63SLaurent Pinchart 150316ca63SLaurent Pinchart #include <linux/interrupt.h> 160316ca63SLaurent Pinchart #include <linux/io.h> 170316ca63SLaurent Pinchart #include <linux/kernel.h> 180316ca63SLaurent Pinchart #include <linux/list.h> 190316ca63SLaurent Pinchart #include <linux/module.h> 200316ca63SLaurent Pinchart #include <linux/slab.h> 210316ca63SLaurent Pinchart #include <linux/timer.h> 220316ca63SLaurent Pinchart #include <linux/usb.h> 230316ca63SLaurent Pinchart 240316ca63SLaurent Pinchart #include "isp1760-core.h" 250316ca63SLaurent Pinchart #include "isp1760-regs.h" 260316ca63SLaurent Pinchart #include "isp1760-udc.h" 270316ca63SLaurent Pinchart 280316ca63SLaurent Pinchart #define ISP1760_VBUS_POLL_INTERVAL msecs_to_jiffies(500) 290316ca63SLaurent Pinchart 300316ca63SLaurent Pinchart struct isp1760_request { 310316ca63SLaurent Pinchart struct usb_request req; 320316ca63SLaurent Pinchart struct list_head queue; 330316ca63SLaurent Pinchart struct isp1760_ep *ep; 340316ca63SLaurent Pinchart unsigned int packet_size; 350316ca63SLaurent Pinchart }; 360316ca63SLaurent Pinchart 370316ca63SLaurent Pinchart static inline struct isp1760_udc *gadget_to_udc(struct usb_gadget *gadget) 380316ca63SLaurent Pinchart { 390316ca63SLaurent Pinchart return container_of(gadget, struct isp1760_udc, gadget); 400316ca63SLaurent Pinchart } 410316ca63SLaurent Pinchart 420316ca63SLaurent Pinchart static inline struct isp1760_ep *ep_to_udc_ep(struct usb_ep *ep) 430316ca63SLaurent Pinchart { 440316ca63SLaurent Pinchart return container_of(ep, struct isp1760_ep, ep); 450316ca63SLaurent Pinchart } 460316ca63SLaurent Pinchart 470316ca63SLaurent Pinchart static inline struct isp1760_request *req_to_udc_req(struct usb_request *req) 480316ca63SLaurent Pinchart { 490316ca63SLaurent Pinchart return container_of(req, struct isp1760_request, req); 500316ca63SLaurent Pinchart } 510316ca63SLaurent Pinchart 520316ca63SLaurent Pinchart static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg) 530316ca63SLaurent Pinchart { 540316ca63SLaurent Pinchart return isp1760_read32(udc->regs, reg); 550316ca63SLaurent Pinchart } 560316ca63SLaurent Pinchart 570316ca63SLaurent Pinchart static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val) 580316ca63SLaurent Pinchart { 590316ca63SLaurent Pinchart isp1760_write32(udc->regs, reg, val); 600316ca63SLaurent Pinchart } 610316ca63SLaurent Pinchart 620316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 630316ca63SLaurent Pinchart * Endpoint Management 640316ca63SLaurent Pinchart */ 650316ca63SLaurent Pinchart 660316ca63SLaurent Pinchart static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc, 670316ca63SLaurent Pinchart u16 index) 680316ca63SLaurent Pinchart { 690316ca63SLaurent Pinchart unsigned int i; 700316ca63SLaurent Pinchart 710316ca63SLaurent Pinchart if (index == 0) 720316ca63SLaurent Pinchart return &udc->ep[0]; 730316ca63SLaurent Pinchart 740316ca63SLaurent Pinchart for (i = 1; i < ARRAY_SIZE(udc->ep); ++i) { 750316ca63SLaurent Pinchart if (udc->ep[i].addr == index) 760316ca63SLaurent Pinchart return udc->ep[i].desc ? &udc->ep[i] : NULL; 770316ca63SLaurent Pinchart } 780316ca63SLaurent Pinchart 790316ca63SLaurent Pinchart return NULL; 800316ca63SLaurent Pinchart } 810316ca63SLaurent Pinchart 820316ca63SLaurent Pinchart static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir) 830316ca63SLaurent Pinchart { 840316ca63SLaurent Pinchart isp1760_udc_write(ep->udc, DC_EPINDEX, 850316ca63SLaurent Pinchart DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) | 860316ca63SLaurent Pinchart (dir == USB_DIR_IN ? DC_EPDIR : 0)); 870316ca63SLaurent Pinchart } 880316ca63SLaurent Pinchart 890316ca63SLaurent Pinchart /** 900316ca63SLaurent Pinchart * isp1760_udc_select_ep - Select an endpoint for register access 910316ca63SLaurent Pinchart * @ep: The endpoint 920316ca63SLaurent Pinchart * 930316ca63SLaurent Pinchart * The ISP1761 endpoint registers are banked. This function selects the target 940316ca63SLaurent Pinchart * endpoint for banked register access. The selection remains valid until the 950316ca63SLaurent Pinchart * next call to this function, the next direct access to the EPINDEX register 960316ca63SLaurent Pinchart * or the next reset, whichever comes first. 970316ca63SLaurent Pinchart * 980316ca63SLaurent Pinchart * Called with the UDC spinlock held. 990316ca63SLaurent Pinchart */ 1000316ca63SLaurent Pinchart static void isp1760_udc_select_ep(struct isp1760_ep *ep) 1010316ca63SLaurent Pinchart { 1020316ca63SLaurent Pinchart __isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK); 1030316ca63SLaurent Pinchart } 1040316ca63SLaurent Pinchart 1050316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 1060316ca63SLaurent Pinchart static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir) 1070316ca63SLaurent Pinchart { 1080316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 1090316ca63SLaurent Pinchart 1100316ca63SLaurent Pinchart /* 1110316ca63SLaurent Pinchart * Proceed to the status stage. The status stage data packet flows in 1120316ca63SLaurent Pinchart * the direction opposite to the data stage data packets, we thus need 1130316ca63SLaurent Pinchart * to select the OUT/IN endpoint for IN/OUT transfers. 1140316ca63SLaurent Pinchart */ 1150316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | 1160316ca63SLaurent Pinchart (dir == USB_DIR_IN ? 0 : DC_EPDIR)); 1170316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS); 1180316ca63SLaurent Pinchart 1190316ca63SLaurent Pinchart /* 1200316ca63SLaurent Pinchart * The hardware will terminate the request automatically and go back to 1210316ca63SLaurent Pinchart * the setup stage without notifying us. 1220316ca63SLaurent Pinchart */ 1230316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 1240316ca63SLaurent Pinchart } 1250316ca63SLaurent Pinchart 1260316ca63SLaurent Pinchart /* Called without the UDC spinlock held. */ 1270316ca63SLaurent Pinchart static void isp1760_udc_request_complete(struct isp1760_ep *ep, 1280316ca63SLaurent Pinchart struct isp1760_request *req, 1290316ca63SLaurent Pinchart int status) 1300316ca63SLaurent Pinchart { 1310316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 1320316ca63SLaurent Pinchart unsigned long flags; 1330316ca63SLaurent Pinchart 1340316ca63SLaurent Pinchart dev_dbg(ep->udc->isp->dev, "completing request %p with status %d\n", 1350316ca63SLaurent Pinchart req, status); 1360316ca63SLaurent Pinchart 1370316ca63SLaurent Pinchart req->ep = NULL; 1380316ca63SLaurent Pinchart req->req.status = status; 1390316ca63SLaurent Pinchart req->req.complete(&ep->ep, &req->req); 1400316ca63SLaurent Pinchart 1410316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 1420316ca63SLaurent Pinchart 1430316ca63SLaurent Pinchart /* 1440316ca63SLaurent Pinchart * When completing control OUT requests, move to the status stage after 1450316ca63SLaurent Pinchart * calling the request complete callback. This gives the gadget an 1460316ca63SLaurent Pinchart * opportunity to stall the control transfer if needed. 1470316ca63SLaurent Pinchart */ 1480316ca63SLaurent Pinchart if (status == 0 && ep->addr == 0 && udc->ep0_dir == USB_DIR_OUT) 1490316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(ep, USB_DIR_OUT); 1500316ca63SLaurent Pinchart 1510316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 1520316ca63SLaurent Pinchart } 1530316ca63SLaurent Pinchart 1540316ca63SLaurent Pinchart static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep) 1550316ca63SLaurent Pinchart { 1560316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 1570316ca63SLaurent Pinchart unsigned long flags; 1580316ca63SLaurent Pinchart 1590316ca63SLaurent Pinchart dev_dbg(ep->udc->isp->dev, "%s(ep%02x)\n", __func__, ep->addr); 1600316ca63SLaurent Pinchart 1610316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 1620316ca63SLaurent Pinchart 1630316ca63SLaurent Pinchart /* Stall both the IN and OUT endpoints. */ 1640316ca63SLaurent Pinchart __isp1760_udc_select_ep(ep, USB_DIR_OUT); 1650316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL); 1660316ca63SLaurent Pinchart __isp1760_udc_select_ep(ep, USB_DIR_IN); 1670316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL); 1680316ca63SLaurent Pinchart 1690316ca63SLaurent Pinchart /* A protocol stall completes the control transaction. */ 1700316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 1710316ca63SLaurent Pinchart 1720316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 1730316ca63SLaurent Pinchart } 1740316ca63SLaurent Pinchart 1750316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 1760316ca63SLaurent Pinchart * Data Endpoints 1770316ca63SLaurent Pinchart */ 1780316ca63SLaurent Pinchart 1790316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 1800316ca63SLaurent Pinchart static bool isp1760_udc_receive(struct isp1760_ep *ep, 1810316ca63SLaurent Pinchart struct isp1760_request *req) 1820316ca63SLaurent Pinchart { 1830316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 1840316ca63SLaurent Pinchart unsigned int len; 1850316ca63SLaurent Pinchart u32 *buf; 1860316ca63SLaurent Pinchart int i; 1870316ca63SLaurent Pinchart 1880316ca63SLaurent Pinchart isp1760_udc_select_ep(ep); 1890316ca63SLaurent Pinchart len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK; 1900316ca63SLaurent Pinchart 1910316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n", 1920316ca63SLaurent Pinchart __func__, len, req->req.actual, req->req.length); 1930316ca63SLaurent Pinchart 1940316ca63SLaurent Pinchart len = min(len, req->req.length - req->req.actual); 1950316ca63SLaurent Pinchart 1960316ca63SLaurent Pinchart if (!len) { 1970316ca63SLaurent Pinchart /* 1980316ca63SLaurent Pinchart * There's no data to be read from the FIFO, acknowledge the RX 1990316ca63SLaurent Pinchart * interrupt by clearing the buffer. 2000316ca63SLaurent Pinchart * 2010316ca63SLaurent Pinchart * TODO: What if another packet arrives in the meantime ? The 2020316ca63SLaurent Pinchart * datasheet doesn't clearly document how this should be 2030316ca63SLaurent Pinchart * handled. 2040316ca63SLaurent Pinchart */ 2050316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); 2060316ca63SLaurent Pinchart return false; 2070316ca63SLaurent Pinchart } 2080316ca63SLaurent Pinchart 2090316ca63SLaurent Pinchart buf = req->req.buf + req->req.actual; 2100316ca63SLaurent Pinchart 2110316ca63SLaurent Pinchart /* 2120316ca63SLaurent Pinchart * Make sure not to read more than one extra byte, otherwise data from 2130316ca63SLaurent Pinchart * the next packet might be removed from the FIFO. 2140316ca63SLaurent Pinchart */ 2150316ca63SLaurent Pinchart for (i = len; i > 2; i -= 4, ++buf) 2160316ca63SLaurent Pinchart *buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT)); 2170316ca63SLaurent Pinchart if (i > 0) 2180316ca63SLaurent Pinchart *(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT)); 2190316ca63SLaurent Pinchart 2200316ca63SLaurent Pinchart req->req.actual += len; 2210316ca63SLaurent Pinchart 2220316ca63SLaurent Pinchart /* 2230316ca63SLaurent Pinchart * TODO: The short_not_ok flag isn't supported yet, but isn't used by 2240316ca63SLaurent Pinchart * any gadget driver either. 2250316ca63SLaurent Pinchart */ 2260316ca63SLaurent Pinchart 2270316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 2280316ca63SLaurent Pinchart "%s: req %p actual/length %u/%u maxpacket %u packet size %u\n", 2290316ca63SLaurent Pinchart __func__, req, req->req.actual, req->req.length, ep->maxpacket, 2300316ca63SLaurent Pinchart len); 2310316ca63SLaurent Pinchart 2320316ca63SLaurent Pinchart ep->rx_pending = false; 2330316ca63SLaurent Pinchart 2340316ca63SLaurent Pinchart /* 2350316ca63SLaurent Pinchart * Complete the request if all data has been received or if a short 2360316ca63SLaurent Pinchart * packet has been received. 2370316ca63SLaurent Pinchart */ 2380316ca63SLaurent Pinchart if (req->req.actual == req->req.length || len < ep->maxpacket) { 2390316ca63SLaurent Pinchart list_del(&req->queue); 2400316ca63SLaurent Pinchart return true; 2410316ca63SLaurent Pinchart } 2420316ca63SLaurent Pinchart 2430316ca63SLaurent Pinchart return false; 2440316ca63SLaurent Pinchart } 2450316ca63SLaurent Pinchart 2460316ca63SLaurent Pinchart static void isp1760_udc_transmit(struct isp1760_ep *ep, 2470316ca63SLaurent Pinchart struct isp1760_request *req) 2480316ca63SLaurent Pinchart { 2490316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 2500316ca63SLaurent Pinchart u32 *buf = req->req.buf + req->req.actual; 2510316ca63SLaurent Pinchart int i; 2520316ca63SLaurent Pinchart 2530316ca63SLaurent Pinchart req->packet_size = min(req->req.length - req->req.actual, 2540316ca63SLaurent Pinchart ep->maxpacket); 2550316ca63SLaurent Pinchart 2560316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: transferring %u bytes (%u/%u done)\n", 2570316ca63SLaurent Pinchart __func__, req->packet_size, req->req.actual, 2580316ca63SLaurent Pinchart req->req.length); 2590316ca63SLaurent Pinchart 2600316ca63SLaurent Pinchart __isp1760_udc_select_ep(ep, USB_DIR_IN); 2610316ca63SLaurent Pinchart 2620316ca63SLaurent Pinchart if (req->packet_size) 2630316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_BUFLEN, req->packet_size); 2640316ca63SLaurent Pinchart 2650316ca63SLaurent Pinchart /* 2660316ca63SLaurent Pinchart * Make sure not to write more than one extra byte, otherwise extra data 2670316ca63SLaurent Pinchart * will stay in the FIFO and will be transmitted during the next control 2680316ca63SLaurent Pinchart * request. The endpoint control CLBUF bit is supposed to allow flushing 2690316ca63SLaurent Pinchart * the FIFO for this kind of conditions, but doesn't seem to work. 2700316ca63SLaurent Pinchart */ 2710316ca63SLaurent Pinchart for (i = req->packet_size; i > 2; i -= 4, ++buf) 2720316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf)); 2730316ca63SLaurent Pinchart if (i > 0) 2740316ca63SLaurent Pinchart writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT); 2750316ca63SLaurent Pinchart 2760316ca63SLaurent Pinchart if (ep->addr == 0) 2770316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); 2780316ca63SLaurent Pinchart if (!req->packet_size) 2790316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP); 2800316ca63SLaurent Pinchart } 2810316ca63SLaurent Pinchart 2820316ca63SLaurent Pinchart static void isp1760_ep_rx_ready(struct isp1760_ep *ep) 2830316ca63SLaurent Pinchart { 2840316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 2850316ca63SLaurent Pinchart struct isp1760_request *req; 2860316ca63SLaurent Pinchart bool complete; 2870316ca63SLaurent Pinchart 2880316ca63SLaurent Pinchart spin_lock(&udc->lock); 2890316ca63SLaurent Pinchart 2900316ca63SLaurent Pinchart if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_OUT) { 2910316ca63SLaurent Pinchart spin_unlock(&udc->lock); 2920316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: invalid ep0 state %u\n", __func__, 2930316ca63SLaurent Pinchart udc->ep0_state); 2940316ca63SLaurent Pinchart return; 2950316ca63SLaurent Pinchart } 2960316ca63SLaurent Pinchart 2970316ca63SLaurent Pinchart if (ep->addr != 0 && !ep->desc) { 2980316ca63SLaurent Pinchart spin_unlock(&udc->lock); 2990316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__, 3000316ca63SLaurent Pinchart ep->addr); 3010316ca63SLaurent Pinchart return; 3020316ca63SLaurent Pinchart } 3030316ca63SLaurent Pinchart 3040316ca63SLaurent Pinchart if (list_empty(&ep->queue)) { 3050316ca63SLaurent Pinchart ep->rx_pending = true; 3060316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3070316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x (%p) has no request queued\n", 3080316ca63SLaurent Pinchart __func__, ep->addr, ep); 3090316ca63SLaurent Pinchart return; 3100316ca63SLaurent Pinchart } 3110316ca63SLaurent Pinchart 3120316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, struct isp1760_request, 3130316ca63SLaurent Pinchart queue); 3140316ca63SLaurent Pinchart complete = isp1760_udc_receive(ep, req); 3150316ca63SLaurent Pinchart 3160316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3170316ca63SLaurent Pinchart 3180316ca63SLaurent Pinchart if (complete) 3190316ca63SLaurent Pinchart isp1760_udc_request_complete(ep, req, 0); 3200316ca63SLaurent Pinchart } 3210316ca63SLaurent Pinchart 3220316ca63SLaurent Pinchart static void isp1760_ep_tx_complete(struct isp1760_ep *ep) 3230316ca63SLaurent Pinchart { 3240316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 3250316ca63SLaurent Pinchart struct isp1760_request *complete = NULL; 3260316ca63SLaurent Pinchart struct isp1760_request *req; 3270316ca63SLaurent Pinchart bool need_zlp; 3280316ca63SLaurent Pinchart 3290316ca63SLaurent Pinchart spin_lock(&udc->lock); 3300316ca63SLaurent Pinchart 3310316ca63SLaurent Pinchart if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_IN) { 3320316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3330316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "TX IRQ: invalid endpoint state %u\n", 3340316ca63SLaurent Pinchart udc->ep0_state); 3350316ca63SLaurent Pinchart return; 3360316ca63SLaurent Pinchart } 3370316ca63SLaurent Pinchart 3380316ca63SLaurent Pinchart if (list_empty(&ep->queue)) { 3390316ca63SLaurent Pinchart /* 3400316ca63SLaurent Pinchart * This can happen for the control endpoint when the reply to 3410316ca63SLaurent Pinchart * the GET_STATUS IN control request is sent directly by the 3420316ca63SLaurent Pinchart * setup IRQ handler. Just proceed to the status stage. 3430316ca63SLaurent Pinchart */ 3440316ca63SLaurent Pinchart if (ep->addr == 0) { 3450316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(ep, USB_DIR_IN); 3460316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3470316ca63SLaurent Pinchart return; 3480316ca63SLaurent Pinchart } 3490316ca63SLaurent Pinchart 3500316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3510316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x has no request queued\n", 3520316ca63SLaurent Pinchart __func__, ep->addr); 3530316ca63SLaurent Pinchart return; 3540316ca63SLaurent Pinchart } 3550316ca63SLaurent Pinchart 3560316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, struct isp1760_request, 3570316ca63SLaurent Pinchart queue); 3580316ca63SLaurent Pinchart req->req.actual += req->packet_size; 3590316ca63SLaurent Pinchart 3600316ca63SLaurent Pinchart need_zlp = req->req.actual == req->req.length && 3610316ca63SLaurent Pinchart !(req->req.length % ep->maxpacket) && 3620316ca63SLaurent Pinchart req->packet_size && req->req.zero; 3630316ca63SLaurent Pinchart 3640316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 3650316ca63SLaurent Pinchart "TX IRQ: req %p actual/length %u/%u maxpacket %u packet size %u zero %u need zlp %u\n", 3660316ca63SLaurent Pinchart req, req->req.actual, req->req.length, ep->maxpacket, 3670316ca63SLaurent Pinchart req->packet_size, req->req.zero, need_zlp); 3680316ca63SLaurent Pinchart 3690316ca63SLaurent Pinchart /* 3700316ca63SLaurent Pinchart * Complete the request if all data has been sent and we don't need to 3710316ca63SLaurent Pinchart * transmit a zero length packet. 3720316ca63SLaurent Pinchart */ 3730316ca63SLaurent Pinchart if (req->req.actual == req->req.length && !need_zlp) { 3740316ca63SLaurent Pinchart complete = req; 3750316ca63SLaurent Pinchart list_del(&req->queue); 3760316ca63SLaurent Pinchart 3770316ca63SLaurent Pinchart if (ep->addr == 0) 3780316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(ep, USB_DIR_IN); 3790316ca63SLaurent Pinchart 3800316ca63SLaurent Pinchart if (!list_empty(&ep->queue)) 3810316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, 3820316ca63SLaurent Pinchart struct isp1760_request, queue); 3830316ca63SLaurent Pinchart else 3840316ca63SLaurent Pinchart req = NULL; 3850316ca63SLaurent Pinchart } 3860316ca63SLaurent Pinchart 3870316ca63SLaurent Pinchart /* 3880316ca63SLaurent Pinchart * Transmit the next packet or start the next request, if any. 3890316ca63SLaurent Pinchart * 3900316ca63SLaurent Pinchart * TODO: If the endpoint is stalled the next request shouldn't be 3910316ca63SLaurent Pinchart * started, but what about the next packet ? 3920316ca63SLaurent Pinchart */ 3930316ca63SLaurent Pinchart if (req) 3940316ca63SLaurent Pinchart isp1760_udc_transmit(ep, req); 3950316ca63SLaurent Pinchart 3960316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3970316ca63SLaurent Pinchart 3980316ca63SLaurent Pinchart if (complete) 3990316ca63SLaurent Pinchart isp1760_udc_request_complete(ep, complete, 0); 4000316ca63SLaurent Pinchart } 4010316ca63SLaurent Pinchart 4020316ca63SLaurent Pinchart static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt) 4030316ca63SLaurent Pinchart { 4040316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 4050316ca63SLaurent Pinchart 4060316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: %s halt on ep%02x\n", __func__, 4070316ca63SLaurent Pinchart halt ? "set" : "clear", ep->addr); 4080316ca63SLaurent Pinchart 4090316ca63SLaurent Pinchart if (ep->desc && usb_endpoint_xfer_isoc(ep->desc)) { 4100316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x is isochronous\n", __func__, 4110316ca63SLaurent Pinchart ep->addr); 4120316ca63SLaurent Pinchart return -EINVAL; 4130316ca63SLaurent Pinchart } 4140316ca63SLaurent Pinchart 4150316ca63SLaurent Pinchart isp1760_udc_select_ep(ep); 4160316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0); 4170316ca63SLaurent Pinchart 4180316ca63SLaurent Pinchart if (ep->addr == 0) { 4190316ca63SLaurent Pinchart /* When halting the control endpoint, stall both IN and OUT. */ 4200316ca63SLaurent Pinchart __isp1760_udc_select_ep(ep, USB_DIR_IN); 4210316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0); 4220316ca63SLaurent Pinchart } else if (!halt) { 4230316ca63SLaurent Pinchart /* Reset the data PID by cycling the endpoint enable bit. */ 4240316ca63SLaurent Pinchart u16 eptype = isp1760_udc_read(udc, DC_EPTYPE); 4250316ca63SLaurent Pinchart 4260316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE); 4270316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPTYPE, eptype); 4280316ca63SLaurent Pinchart 4290316ca63SLaurent Pinchart /* 4300316ca63SLaurent Pinchart * Disabling the endpoint emptied the transmit FIFO, fill it 4310316ca63SLaurent Pinchart * again if a request is pending. 4320316ca63SLaurent Pinchart * 4330316ca63SLaurent Pinchart * TODO: Does the gadget framework require synchronizatino with 4340316ca63SLaurent Pinchart * the TX IRQ handler ? 4350316ca63SLaurent Pinchart */ 4360316ca63SLaurent Pinchart if ((ep->addr & USB_DIR_IN) && !list_empty(&ep->queue)) { 4370316ca63SLaurent Pinchart struct isp1760_request *req; 4380316ca63SLaurent Pinchart 4390316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, 4400316ca63SLaurent Pinchart struct isp1760_request, queue); 4410316ca63SLaurent Pinchart isp1760_udc_transmit(ep, req); 4420316ca63SLaurent Pinchart } 4430316ca63SLaurent Pinchart } 4440316ca63SLaurent Pinchart 4450316ca63SLaurent Pinchart ep->halted = halt; 4460316ca63SLaurent Pinchart 4470316ca63SLaurent Pinchart return 0; 4480316ca63SLaurent Pinchart } 4490316ca63SLaurent Pinchart 4500316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 4510316ca63SLaurent Pinchart * Control Endpoint 4520316ca63SLaurent Pinchart */ 4530316ca63SLaurent Pinchart 4540316ca63SLaurent Pinchart static int isp1760_udc_get_status(struct isp1760_udc *udc, 4550316ca63SLaurent Pinchart const struct usb_ctrlrequest *req) 4560316ca63SLaurent Pinchart { 4570316ca63SLaurent Pinchart struct isp1760_ep *ep; 4580316ca63SLaurent Pinchart u16 status; 4590316ca63SLaurent Pinchart 4600316ca63SLaurent Pinchart if (req->wLength != cpu_to_le16(2) || req->wValue != cpu_to_le16(0)) 4610316ca63SLaurent Pinchart return -EINVAL; 4620316ca63SLaurent Pinchart 4630316ca63SLaurent Pinchart switch (req->bRequestType) { 4640316ca63SLaurent Pinchart case USB_DIR_IN | USB_RECIP_DEVICE: 4650316ca63SLaurent Pinchart status = udc->devstatus; 4660316ca63SLaurent Pinchart break; 4670316ca63SLaurent Pinchart 4680316ca63SLaurent Pinchart case USB_DIR_IN | USB_RECIP_INTERFACE: 4690316ca63SLaurent Pinchart status = 0; 4700316ca63SLaurent Pinchart break; 4710316ca63SLaurent Pinchart 4720316ca63SLaurent Pinchart case USB_DIR_IN | USB_RECIP_ENDPOINT: 4730316ca63SLaurent Pinchart ep = isp1760_udc_find_ep(udc, le16_to_cpu(req->wIndex)); 4740316ca63SLaurent Pinchart if (!ep) 4750316ca63SLaurent Pinchart return -EINVAL; 4760316ca63SLaurent Pinchart 4770316ca63SLaurent Pinchart status = 0; 4780316ca63SLaurent Pinchart if (ep->halted) 4790316ca63SLaurent Pinchart status |= 1 << USB_ENDPOINT_HALT; 4800316ca63SLaurent Pinchart break; 4810316ca63SLaurent Pinchart 4820316ca63SLaurent Pinchart default: 4830316ca63SLaurent Pinchart return -EINVAL; 4840316ca63SLaurent Pinchart } 4850316ca63SLaurent Pinchart 4860316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR); 4870316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_BUFLEN, 2); 4880316ca63SLaurent Pinchart 4890316ca63SLaurent Pinchart writew(cpu_to_le16(status), udc->regs + DC_DATAPORT); 4900316ca63SLaurent Pinchart 4910316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); 4920316ca63SLaurent Pinchart 4930316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status); 4940316ca63SLaurent Pinchart 4950316ca63SLaurent Pinchart return 0; 4960316ca63SLaurent Pinchart } 4970316ca63SLaurent Pinchart 4980316ca63SLaurent Pinchart static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr) 4990316ca63SLaurent Pinchart { 5000316ca63SLaurent Pinchart if (addr > 127) { 5010316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "invalid device address %u\n", addr); 5020316ca63SLaurent Pinchart return -EINVAL; 5030316ca63SLaurent Pinchart } 5040316ca63SLaurent Pinchart 5050316ca63SLaurent Pinchart if (udc->gadget.state != USB_STATE_DEFAULT && 5060316ca63SLaurent Pinchart udc->gadget.state != USB_STATE_ADDRESS) { 5070316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "can't set address in state %u\n", 5080316ca63SLaurent Pinchart udc->gadget.state); 5090316ca63SLaurent Pinchart return -EINVAL; 5100316ca63SLaurent Pinchart } 5110316ca63SLaurent Pinchart 5120316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS : 5130316ca63SLaurent Pinchart USB_STATE_DEFAULT); 5140316ca63SLaurent Pinchart 5150316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr); 5160316ca63SLaurent Pinchart 5170316ca63SLaurent Pinchart spin_lock(&udc->lock); 5180316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT); 5190316ca63SLaurent Pinchart spin_unlock(&udc->lock); 5200316ca63SLaurent Pinchart 5210316ca63SLaurent Pinchart return 0; 5220316ca63SLaurent Pinchart } 5230316ca63SLaurent Pinchart 5240316ca63SLaurent Pinchart static bool isp1760_ep0_setup_standard(struct isp1760_udc *udc, 5250316ca63SLaurent Pinchart struct usb_ctrlrequest *req) 5260316ca63SLaurent Pinchart { 5270316ca63SLaurent Pinchart bool stall; 5280316ca63SLaurent Pinchart 5290316ca63SLaurent Pinchart switch (req->bRequest) { 5300316ca63SLaurent Pinchart case USB_REQ_GET_STATUS: 5310316ca63SLaurent Pinchart return isp1760_udc_get_status(udc, req); 5320316ca63SLaurent Pinchart 5330316ca63SLaurent Pinchart case USB_REQ_CLEAR_FEATURE: 5340316ca63SLaurent Pinchart switch (req->bRequestType) { 5350316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_DEVICE: { 5360316ca63SLaurent Pinchart /* TODO: Handle remote wakeup feature. */ 5370316ca63SLaurent Pinchart return true; 5380316ca63SLaurent Pinchart } 5390316ca63SLaurent Pinchart 5400316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_ENDPOINT: { 5410316ca63SLaurent Pinchart u16 index = le16_to_cpu(req->wIndex); 5420316ca63SLaurent Pinchart struct isp1760_ep *ep; 5430316ca63SLaurent Pinchart 5440316ca63SLaurent Pinchart if (req->wLength != cpu_to_le16(0) || 5450316ca63SLaurent Pinchart req->wValue != cpu_to_le16(USB_ENDPOINT_HALT)) 5460316ca63SLaurent Pinchart return true; 5470316ca63SLaurent Pinchart 5480316ca63SLaurent Pinchart ep = isp1760_udc_find_ep(udc, index); 5490316ca63SLaurent Pinchart if (!ep) 5500316ca63SLaurent Pinchart return true; 5510316ca63SLaurent Pinchart 5520316ca63SLaurent Pinchart spin_lock(&udc->lock); 5530316ca63SLaurent Pinchart 5540316ca63SLaurent Pinchart /* 5550316ca63SLaurent Pinchart * If the endpoint is wedged only the gadget can clear 5560316ca63SLaurent Pinchart * the halt feature. Pretend success in that case, but 5570316ca63SLaurent Pinchart * keep the endpoint halted. 5580316ca63SLaurent Pinchart */ 5590316ca63SLaurent Pinchart if (!ep->wedged) 5600316ca63SLaurent Pinchart stall = __isp1760_udc_set_halt(ep, false); 5610316ca63SLaurent Pinchart else 5620316ca63SLaurent Pinchart stall = false; 5630316ca63SLaurent Pinchart 5640316ca63SLaurent Pinchart if (!stall) 5650316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(&udc->ep[0], 5660316ca63SLaurent Pinchart USB_DIR_OUT); 5670316ca63SLaurent Pinchart 5680316ca63SLaurent Pinchart spin_unlock(&udc->lock); 5690316ca63SLaurent Pinchart return stall; 5700316ca63SLaurent Pinchart } 5710316ca63SLaurent Pinchart 5720316ca63SLaurent Pinchart default: 5730316ca63SLaurent Pinchart return true; 5740316ca63SLaurent Pinchart } 5750316ca63SLaurent Pinchart break; 5760316ca63SLaurent Pinchart 5770316ca63SLaurent Pinchart case USB_REQ_SET_FEATURE: 5780316ca63SLaurent Pinchart switch (req->bRequestType) { 5790316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_DEVICE: { 5800316ca63SLaurent Pinchart /* TODO: Handle remote wakeup and test mode features */ 5810316ca63SLaurent Pinchart return true; 5820316ca63SLaurent Pinchart } 5830316ca63SLaurent Pinchart 5840316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_ENDPOINT: { 5850316ca63SLaurent Pinchart u16 index = le16_to_cpu(req->wIndex); 5860316ca63SLaurent Pinchart struct isp1760_ep *ep; 5870316ca63SLaurent Pinchart 5880316ca63SLaurent Pinchart if (req->wLength != cpu_to_le16(0) || 5890316ca63SLaurent Pinchart req->wValue != cpu_to_le16(USB_ENDPOINT_HALT)) 5900316ca63SLaurent Pinchart return true; 5910316ca63SLaurent Pinchart 5920316ca63SLaurent Pinchart ep = isp1760_udc_find_ep(udc, index); 5930316ca63SLaurent Pinchart if (!ep) 5940316ca63SLaurent Pinchart return true; 5950316ca63SLaurent Pinchart 5960316ca63SLaurent Pinchart spin_lock(&udc->lock); 5970316ca63SLaurent Pinchart 5980316ca63SLaurent Pinchart stall = __isp1760_udc_set_halt(ep, true); 5990316ca63SLaurent Pinchart if (!stall) 6000316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(&udc->ep[0], 6010316ca63SLaurent Pinchart USB_DIR_OUT); 6020316ca63SLaurent Pinchart 6030316ca63SLaurent Pinchart spin_unlock(&udc->lock); 6040316ca63SLaurent Pinchart return stall; 6050316ca63SLaurent Pinchart } 6060316ca63SLaurent Pinchart 6070316ca63SLaurent Pinchart default: 6080316ca63SLaurent Pinchart return true; 6090316ca63SLaurent Pinchart } 6100316ca63SLaurent Pinchart break; 6110316ca63SLaurent Pinchart 6120316ca63SLaurent Pinchart case USB_REQ_SET_ADDRESS: 6130316ca63SLaurent Pinchart if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) 6140316ca63SLaurent Pinchart return true; 6150316ca63SLaurent Pinchart 6160316ca63SLaurent Pinchart return isp1760_udc_set_address(udc, le16_to_cpu(req->wValue)); 6170316ca63SLaurent Pinchart 6180316ca63SLaurent Pinchart case USB_REQ_SET_CONFIGURATION: 6190316ca63SLaurent Pinchart if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) 6200316ca63SLaurent Pinchart return true; 6210316ca63SLaurent Pinchart 6220316ca63SLaurent Pinchart if (udc->gadget.state != USB_STATE_ADDRESS && 6230316ca63SLaurent Pinchart udc->gadget.state != USB_STATE_CONFIGURED) 6240316ca63SLaurent Pinchart return true; 6250316ca63SLaurent Pinchart 6260316ca63SLaurent Pinchart stall = udc->driver->setup(&udc->gadget, req) < 0; 6270316ca63SLaurent Pinchart if (stall) 6280316ca63SLaurent Pinchart return true; 6290316ca63SLaurent Pinchart 6300316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, req->wValue ? 6310316ca63SLaurent Pinchart USB_STATE_CONFIGURED : USB_STATE_ADDRESS); 6320316ca63SLaurent Pinchart 6330316ca63SLaurent Pinchart /* 6340316ca63SLaurent Pinchart * SET_CONFIGURATION (and SET_INTERFACE) must reset the halt 6350316ca63SLaurent Pinchart * feature on all endpoints. There is however no need to do so 6360316ca63SLaurent Pinchart * explicitly here as the gadget driver will disable and 6370316ca63SLaurent Pinchart * reenable endpoints, clearing the halt feature. 6380316ca63SLaurent Pinchart */ 6390316ca63SLaurent Pinchart return false; 6400316ca63SLaurent Pinchart 6410316ca63SLaurent Pinchart default: 6420316ca63SLaurent Pinchart return udc->driver->setup(&udc->gadget, req) < 0; 6430316ca63SLaurent Pinchart } 6440316ca63SLaurent Pinchart } 6450316ca63SLaurent Pinchart 6460316ca63SLaurent Pinchart static void isp1760_ep0_setup(struct isp1760_udc *udc) 6470316ca63SLaurent Pinchart { 6480316ca63SLaurent Pinchart union { 6490316ca63SLaurent Pinchart struct usb_ctrlrequest r; 6500316ca63SLaurent Pinchart u32 data[2]; 6510316ca63SLaurent Pinchart } req; 6520316ca63SLaurent Pinchart unsigned int count; 6530316ca63SLaurent Pinchart bool stall = false; 6540316ca63SLaurent Pinchart 6550316ca63SLaurent Pinchart spin_lock(&udc->lock); 6560316ca63SLaurent Pinchart 6570316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP); 6580316ca63SLaurent Pinchart 6590316ca63SLaurent Pinchart count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK; 6600316ca63SLaurent Pinchart if (count != sizeof(req)) { 6610316ca63SLaurent Pinchart spin_unlock(&udc->lock); 6620316ca63SLaurent Pinchart 6630316ca63SLaurent Pinchart dev_err(udc->isp->dev, "invalid length %u for setup packet\n", 6640316ca63SLaurent Pinchart count); 6650316ca63SLaurent Pinchart 6660316ca63SLaurent Pinchart isp1760_udc_ctrl_send_stall(&udc->ep[0]); 6670316ca63SLaurent Pinchart return; 6680316ca63SLaurent Pinchart } 6690316ca63SLaurent Pinchart 6700316ca63SLaurent Pinchart req.data[0] = isp1760_udc_read(udc, DC_DATAPORT); 6710316ca63SLaurent Pinchart req.data[1] = isp1760_udc_read(udc, DC_DATAPORT); 6720316ca63SLaurent Pinchart 6730316ca63SLaurent Pinchart if (udc->ep0_state != ISP1760_CTRL_SETUP) { 6740316ca63SLaurent Pinchart spin_unlock(&udc->lock); 6750316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "unexpected SETUP packet\n"); 6760316ca63SLaurent Pinchart return; 6770316ca63SLaurent Pinchart } 6780316ca63SLaurent Pinchart 6790316ca63SLaurent Pinchart /* Move to the data stage. */ 6800316ca63SLaurent Pinchart if (!req.r.wLength) 6810316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_STATUS; 6820316ca63SLaurent Pinchart else if (req.r.bRequestType & USB_DIR_IN) 6830316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_DATA_IN; 6840316ca63SLaurent Pinchart else 6850316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_DATA_OUT; 6860316ca63SLaurent Pinchart 6870316ca63SLaurent Pinchart udc->ep0_dir = req.r.bRequestType & USB_DIR_IN; 6880316ca63SLaurent Pinchart udc->ep0_length = le16_to_cpu(req.r.wLength); 6890316ca63SLaurent Pinchart 6900316ca63SLaurent Pinchart spin_unlock(&udc->lock); 6910316ca63SLaurent Pinchart 6920316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 6930316ca63SLaurent Pinchart "%s: bRequestType 0x%02x bRequest 0x%02x wValue 0x%04x wIndex 0x%04x wLength 0x%04x\n", 6940316ca63SLaurent Pinchart __func__, req.r.bRequestType, req.r.bRequest, 6950316ca63SLaurent Pinchart le16_to_cpu(req.r.wValue), le16_to_cpu(req.r.wIndex), 6960316ca63SLaurent Pinchart le16_to_cpu(req.r.wLength)); 6970316ca63SLaurent Pinchart 6980316ca63SLaurent Pinchart if ((req.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 6990316ca63SLaurent Pinchart stall = isp1760_ep0_setup_standard(udc, &req.r); 7000316ca63SLaurent Pinchart else 7010316ca63SLaurent Pinchart stall = udc->driver->setup(&udc->gadget, &req.r) < 0; 7020316ca63SLaurent Pinchart 7030316ca63SLaurent Pinchart if (stall) 7040316ca63SLaurent Pinchart isp1760_udc_ctrl_send_stall(&udc->ep[0]); 7050316ca63SLaurent Pinchart } 7060316ca63SLaurent Pinchart 7070316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 7080316ca63SLaurent Pinchart * Gadget Endpoint Operations 7090316ca63SLaurent Pinchart */ 7100316ca63SLaurent Pinchart 7110316ca63SLaurent Pinchart static int isp1760_ep_enable(struct usb_ep *ep, 7120316ca63SLaurent Pinchart const struct usb_endpoint_descriptor *desc) 7130316ca63SLaurent Pinchart { 7140316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 7150316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 7160316ca63SLaurent Pinchart unsigned long flags; 7170316ca63SLaurent Pinchart unsigned int type; 7180316ca63SLaurent Pinchart 7190316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s\n", __func__); 7200316ca63SLaurent Pinchart 7210316ca63SLaurent Pinchart /* 7220316ca63SLaurent Pinchart * Validate the descriptor. The control endpoint can't be enabled 7230316ca63SLaurent Pinchart * manually. 7240316ca63SLaurent Pinchart */ 7250316ca63SLaurent Pinchart if (desc->bDescriptorType != USB_DT_ENDPOINT || 7260316ca63SLaurent Pinchart desc->bEndpointAddress == 0 || 7270316ca63SLaurent Pinchart desc->bEndpointAddress != uep->addr || 7280316ca63SLaurent Pinchart le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket) { 7290316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 7300316ca63SLaurent Pinchart "%s: invalid descriptor type %u addr %02x ep addr %02x max packet size %u/%u\n", 7310316ca63SLaurent Pinchart __func__, desc->bDescriptorType, 7320316ca63SLaurent Pinchart desc->bEndpointAddress, uep->addr, 7330316ca63SLaurent Pinchart le16_to_cpu(desc->wMaxPacketSize), ep->maxpacket); 7340316ca63SLaurent Pinchart return -EINVAL; 7350316ca63SLaurent Pinchart } 7360316ca63SLaurent Pinchart 7370316ca63SLaurent Pinchart switch (usb_endpoint_type(desc)) { 7380316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_ISOC: 7390316ca63SLaurent Pinchart type = DC_ENDPTYP_ISOC; 7400316ca63SLaurent Pinchart break; 7410316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_BULK: 7420316ca63SLaurent Pinchart type = DC_ENDPTYP_BULK; 7430316ca63SLaurent Pinchart break; 7440316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_INT: 7450316ca63SLaurent Pinchart type = DC_ENDPTYP_INTERRUPT; 7460316ca63SLaurent Pinchart break; 7470316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_CONTROL: 7480316ca63SLaurent Pinchart default: 7490316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: control endpoints unsupported\n", 7500316ca63SLaurent Pinchart __func__); 7510316ca63SLaurent Pinchart return -EINVAL; 7520316ca63SLaurent Pinchart } 7530316ca63SLaurent Pinchart 7540316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 7550316ca63SLaurent Pinchart 7560316ca63SLaurent Pinchart uep->desc = desc; 7570316ca63SLaurent Pinchart uep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); 7580316ca63SLaurent Pinchart uep->rx_pending = false; 7590316ca63SLaurent Pinchart uep->halted = false; 7600316ca63SLaurent Pinchart uep->wedged = false; 7610316ca63SLaurent Pinchart 7620316ca63SLaurent Pinchart isp1760_udc_select_ep(uep); 7630316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket); 7640316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket); 7650316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type); 7660316ca63SLaurent Pinchart 7670316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 7680316ca63SLaurent Pinchart 7690316ca63SLaurent Pinchart return 0; 7700316ca63SLaurent Pinchart } 7710316ca63SLaurent Pinchart 7720316ca63SLaurent Pinchart static int isp1760_ep_disable(struct usb_ep *ep) 7730316ca63SLaurent Pinchart { 7740316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 7750316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 7760316ca63SLaurent Pinchart struct isp1760_request *req, *nreq; 7770316ca63SLaurent Pinchart LIST_HEAD(req_list); 7780316ca63SLaurent Pinchart unsigned long flags; 7790316ca63SLaurent Pinchart 7800316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s\n", __func__); 7810316ca63SLaurent Pinchart 7820316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 7830316ca63SLaurent Pinchart 7840316ca63SLaurent Pinchart if (!uep->desc) { 7850316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: endpoint not enabled\n", __func__); 7860316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 7870316ca63SLaurent Pinchart return -EINVAL; 7880316ca63SLaurent Pinchart } 7890316ca63SLaurent Pinchart 7900316ca63SLaurent Pinchart uep->desc = NULL; 7910316ca63SLaurent Pinchart uep->maxpacket = 0; 7920316ca63SLaurent Pinchart 7930316ca63SLaurent Pinchart isp1760_udc_select_ep(uep); 7940316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_EPTYPE, 0); 7950316ca63SLaurent Pinchart 7960316ca63SLaurent Pinchart /* TODO Synchronize with the IRQ handler */ 7970316ca63SLaurent Pinchart 7980316ca63SLaurent Pinchart list_splice_init(&uep->queue, &req_list); 7990316ca63SLaurent Pinchart 8000316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 8010316ca63SLaurent Pinchart 8020316ca63SLaurent Pinchart list_for_each_entry_safe(req, nreq, &req_list, queue) { 8030316ca63SLaurent Pinchart list_del(&req->queue); 8040316ca63SLaurent Pinchart isp1760_udc_request_complete(uep, req, -ESHUTDOWN); 8050316ca63SLaurent Pinchart } 8060316ca63SLaurent Pinchart 8070316ca63SLaurent Pinchart return 0; 8080316ca63SLaurent Pinchart } 8090316ca63SLaurent Pinchart 8100316ca63SLaurent Pinchart static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep, 8110316ca63SLaurent Pinchart gfp_t gfp_flags) 8120316ca63SLaurent Pinchart { 8130316ca63SLaurent Pinchart struct isp1760_request *req; 8140316ca63SLaurent Pinchart 8150316ca63SLaurent Pinchart req = kzalloc(sizeof(*req), gfp_flags); 816e3e64f3fSColin Ian King if (!req) 817e3e64f3fSColin Ian King return NULL; 8180316ca63SLaurent Pinchart 8190316ca63SLaurent Pinchart return &req->req; 8200316ca63SLaurent Pinchart } 8210316ca63SLaurent Pinchart 8220316ca63SLaurent Pinchart static void isp1760_ep_free_request(struct usb_ep *ep, struct usb_request *_req) 8230316ca63SLaurent Pinchart { 8240316ca63SLaurent Pinchart struct isp1760_request *req = req_to_udc_req(_req); 8250316ca63SLaurent Pinchart 8260316ca63SLaurent Pinchart kfree(req); 8270316ca63SLaurent Pinchart } 8280316ca63SLaurent Pinchart 8290316ca63SLaurent Pinchart static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req, 8300316ca63SLaurent Pinchart gfp_t gfp_flags) 8310316ca63SLaurent Pinchart { 8320316ca63SLaurent Pinchart struct isp1760_request *req = req_to_udc_req(_req); 8330316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 8340316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 8350316ca63SLaurent Pinchart bool complete = false; 8360316ca63SLaurent Pinchart unsigned long flags; 8370316ca63SLaurent Pinchart int ret = 0; 8380316ca63SLaurent Pinchart 8390316ca63SLaurent Pinchart _req->status = -EINPROGRESS; 8400316ca63SLaurent Pinchart _req->actual = 0; 8410316ca63SLaurent Pinchart 8420316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 8430316ca63SLaurent Pinchart 8440316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 8450316ca63SLaurent Pinchart "%s: req %p (%u bytes%s) ep %p(0x%02x)\n", __func__, _req, 8460316ca63SLaurent Pinchart _req->length, _req->zero ? " (zlp)" : "", uep, uep->addr); 8470316ca63SLaurent Pinchart 8480316ca63SLaurent Pinchart req->ep = uep; 8490316ca63SLaurent Pinchart 8500316ca63SLaurent Pinchart if (uep->addr == 0) { 8510316ca63SLaurent Pinchart if (_req->length != udc->ep0_length && 8520316ca63SLaurent Pinchart udc->ep0_state != ISP1760_CTRL_DATA_IN) { 8530316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 8540316ca63SLaurent Pinchart "%s: invalid length %u for req %p\n", 8550316ca63SLaurent Pinchart __func__, _req->length, req); 8560316ca63SLaurent Pinchart ret = -EINVAL; 8570316ca63SLaurent Pinchart goto done; 8580316ca63SLaurent Pinchart } 8590316ca63SLaurent Pinchart 8600316ca63SLaurent Pinchart switch (udc->ep0_state) { 8610316ca63SLaurent Pinchart case ISP1760_CTRL_DATA_IN: 8620316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: transmitting req %p\n", 8630316ca63SLaurent Pinchart __func__, req); 8640316ca63SLaurent Pinchart 8650316ca63SLaurent Pinchart list_add_tail(&req->queue, &uep->queue); 8660316ca63SLaurent Pinchart isp1760_udc_transmit(uep, req); 8670316ca63SLaurent Pinchart break; 8680316ca63SLaurent Pinchart 8690316ca63SLaurent Pinchart case ISP1760_CTRL_DATA_OUT: 8700316ca63SLaurent Pinchart list_add_tail(&req->queue, &uep->queue); 8710316ca63SLaurent Pinchart __isp1760_udc_select_ep(uep, USB_DIR_OUT); 8720316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); 8730316ca63SLaurent Pinchart break; 8740316ca63SLaurent Pinchart 8750316ca63SLaurent Pinchart case ISP1760_CTRL_STATUS: 8760316ca63SLaurent Pinchart complete = true; 8770316ca63SLaurent Pinchart break; 8780316ca63SLaurent Pinchart 8790316ca63SLaurent Pinchart default: 8800316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: invalid ep0 state\n", 8810316ca63SLaurent Pinchart __func__); 8820316ca63SLaurent Pinchart ret = -EINVAL; 8830316ca63SLaurent Pinchart break; 8840316ca63SLaurent Pinchart } 8850316ca63SLaurent Pinchart } else if (uep->desc) { 8860316ca63SLaurent Pinchart bool empty = list_empty(&uep->queue); 8870316ca63SLaurent Pinchart 8880316ca63SLaurent Pinchart list_add_tail(&req->queue, &uep->queue); 8890316ca63SLaurent Pinchart if ((uep->addr & USB_DIR_IN) && !uep->halted && empty) 8900316ca63SLaurent Pinchart isp1760_udc_transmit(uep, req); 8910316ca63SLaurent Pinchart else if (!(uep->addr & USB_DIR_IN) && uep->rx_pending) 8920316ca63SLaurent Pinchart complete = isp1760_udc_receive(uep, req); 8930316ca63SLaurent Pinchart } else { 8940316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 8950316ca63SLaurent Pinchart "%s: can't queue request to disabled ep%02x\n", 8960316ca63SLaurent Pinchart __func__, uep->addr); 8970316ca63SLaurent Pinchart ret = -ESHUTDOWN; 8980316ca63SLaurent Pinchart } 8990316ca63SLaurent Pinchart 9000316ca63SLaurent Pinchart done: 9010316ca63SLaurent Pinchart if (ret < 0) 9020316ca63SLaurent Pinchart req->ep = NULL; 9030316ca63SLaurent Pinchart 9040316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 9050316ca63SLaurent Pinchart 9060316ca63SLaurent Pinchart if (complete) 9070316ca63SLaurent Pinchart isp1760_udc_request_complete(uep, req, 0); 9080316ca63SLaurent Pinchart 9090316ca63SLaurent Pinchart return ret; 9100316ca63SLaurent Pinchart } 9110316ca63SLaurent Pinchart 9120316ca63SLaurent Pinchart static int isp1760_ep_dequeue(struct usb_ep *ep, struct usb_request *_req) 9130316ca63SLaurent Pinchart { 9140316ca63SLaurent Pinchart struct isp1760_request *req = req_to_udc_req(_req); 9150316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 9160316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 9170316ca63SLaurent Pinchart unsigned long flags; 9180316ca63SLaurent Pinchart 9190316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s(ep%02x)\n", __func__, uep->addr); 9200316ca63SLaurent Pinchart 9210316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 9220316ca63SLaurent Pinchart 9230316ca63SLaurent Pinchart if (req->ep != uep) 9240316ca63SLaurent Pinchart req = NULL; 9250316ca63SLaurent Pinchart else 9260316ca63SLaurent Pinchart list_del(&req->queue); 9270316ca63SLaurent Pinchart 9280316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 9290316ca63SLaurent Pinchart 9300316ca63SLaurent Pinchart if (!req) 9310316ca63SLaurent Pinchart return -EINVAL; 9320316ca63SLaurent Pinchart 9330316ca63SLaurent Pinchart isp1760_udc_request_complete(uep, req, -ECONNRESET); 9340316ca63SLaurent Pinchart return 0; 9350316ca63SLaurent Pinchart } 9360316ca63SLaurent Pinchart 9370316ca63SLaurent Pinchart static int __isp1760_ep_set_halt(struct isp1760_ep *uep, bool stall, bool wedge) 9380316ca63SLaurent Pinchart { 9390316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 9400316ca63SLaurent Pinchart int ret; 9410316ca63SLaurent Pinchart 9420316ca63SLaurent Pinchart if (!uep->addr) { 9430316ca63SLaurent Pinchart /* 9440316ca63SLaurent Pinchart * Halting the control endpoint is only valid as a delayed error 9450316ca63SLaurent Pinchart * response to a SETUP packet. Make sure EP0 is in the right 9460316ca63SLaurent Pinchart * stage and that the gadget isn't trying to clear the halt 9470316ca63SLaurent Pinchart * condition. 9480316ca63SLaurent Pinchart */ 9490316ca63SLaurent Pinchart if (WARN_ON(udc->ep0_state == ISP1760_CTRL_SETUP || !stall || 9500316ca63SLaurent Pinchart wedge)) { 9510316ca63SLaurent Pinchart return -EINVAL; 9520316ca63SLaurent Pinchart } 9530316ca63SLaurent Pinchart } 9540316ca63SLaurent Pinchart 9550316ca63SLaurent Pinchart if (uep->addr && !uep->desc) { 9560316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__, 9570316ca63SLaurent Pinchart uep->addr); 9580316ca63SLaurent Pinchart return -EINVAL; 9590316ca63SLaurent Pinchart } 9600316ca63SLaurent Pinchart 9610316ca63SLaurent Pinchart if (uep->addr & USB_DIR_IN) { 9620316ca63SLaurent Pinchart /* Refuse to halt IN endpoints with active transfers. */ 9630316ca63SLaurent Pinchart if (!list_empty(&uep->queue)) { 9640316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 9650316ca63SLaurent Pinchart "%s: ep%02x has request pending\n", __func__, 9660316ca63SLaurent Pinchart uep->addr); 9670316ca63SLaurent Pinchart return -EAGAIN; 9680316ca63SLaurent Pinchart } 9690316ca63SLaurent Pinchart } 9700316ca63SLaurent Pinchart 9710316ca63SLaurent Pinchart ret = __isp1760_udc_set_halt(uep, stall); 9720316ca63SLaurent Pinchart if (ret < 0) 9730316ca63SLaurent Pinchart return ret; 9740316ca63SLaurent Pinchart 9750316ca63SLaurent Pinchart if (!uep->addr) { 9760316ca63SLaurent Pinchart /* 9770316ca63SLaurent Pinchart * Stalling EP0 completes the control transaction, move back to 9780316ca63SLaurent Pinchart * the SETUP state. 9790316ca63SLaurent Pinchart */ 9800316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 9810316ca63SLaurent Pinchart return 0; 9820316ca63SLaurent Pinchart } 9830316ca63SLaurent Pinchart 9840316ca63SLaurent Pinchart if (wedge) 9850316ca63SLaurent Pinchart uep->wedged = true; 9860316ca63SLaurent Pinchart else if (!stall) 9870316ca63SLaurent Pinchart uep->wedged = false; 9880316ca63SLaurent Pinchart 9890316ca63SLaurent Pinchart return 0; 9900316ca63SLaurent Pinchart } 9910316ca63SLaurent Pinchart 9920316ca63SLaurent Pinchart static int isp1760_ep_set_halt(struct usb_ep *ep, int value) 9930316ca63SLaurent Pinchart { 9940316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 9950316ca63SLaurent Pinchart unsigned long flags; 9960316ca63SLaurent Pinchart int ret; 9970316ca63SLaurent Pinchart 9980316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s: %s halt on ep%02x\n", __func__, 9990316ca63SLaurent Pinchart value ? "set" : "clear", uep->addr); 10000316ca63SLaurent Pinchart 10010316ca63SLaurent Pinchart spin_lock_irqsave(&uep->udc->lock, flags); 10020316ca63SLaurent Pinchart ret = __isp1760_ep_set_halt(uep, value, false); 10030316ca63SLaurent Pinchart spin_unlock_irqrestore(&uep->udc->lock, flags); 10040316ca63SLaurent Pinchart 10050316ca63SLaurent Pinchart return ret; 10060316ca63SLaurent Pinchart } 10070316ca63SLaurent Pinchart 10080316ca63SLaurent Pinchart static int isp1760_ep_set_wedge(struct usb_ep *ep) 10090316ca63SLaurent Pinchart { 10100316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 10110316ca63SLaurent Pinchart unsigned long flags; 10120316ca63SLaurent Pinchart int ret; 10130316ca63SLaurent Pinchart 10140316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s: set wedge on ep%02x)\n", __func__, 10150316ca63SLaurent Pinchart uep->addr); 10160316ca63SLaurent Pinchart 10170316ca63SLaurent Pinchart spin_lock_irqsave(&uep->udc->lock, flags); 10180316ca63SLaurent Pinchart ret = __isp1760_ep_set_halt(uep, true, true); 10190316ca63SLaurent Pinchart spin_unlock_irqrestore(&uep->udc->lock, flags); 10200316ca63SLaurent Pinchart 10210316ca63SLaurent Pinchart return ret; 10220316ca63SLaurent Pinchart } 10230316ca63SLaurent Pinchart 10240316ca63SLaurent Pinchart static void isp1760_ep_fifo_flush(struct usb_ep *ep) 10250316ca63SLaurent Pinchart { 10260316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 10270316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 10280316ca63SLaurent Pinchart unsigned long flags; 10290316ca63SLaurent Pinchart 10300316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 10310316ca63SLaurent Pinchart 10320316ca63SLaurent Pinchart isp1760_udc_select_ep(uep); 10330316ca63SLaurent Pinchart 10340316ca63SLaurent Pinchart /* 10350316ca63SLaurent Pinchart * Set the CLBUF bit twice to flush both buffers in case double 10360316ca63SLaurent Pinchart * buffering is enabled. 10370316ca63SLaurent Pinchart */ 10380316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); 10390316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); 10400316ca63SLaurent Pinchart 10410316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 10420316ca63SLaurent Pinchart } 10430316ca63SLaurent Pinchart 10440316ca63SLaurent Pinchart static const struct usb_ep_ops isp1760_ep_ops = { 10450316ca63SLaurent Pinchart .enable = isp1760_ep_enable, 10460316ca63SLaurent Pinchart .disable = isp1760_ep_disable, 10470316ca63SLaurent Pinchart .alloc_request = isp1760_ep_alloc_request, 10480316ca63SLaurent Pinchart .free_request = isp1760_ep_free_request, 10490316ca63SLaurent Pinchart .queue = isp1760_ep_queue, 10500316ca63SLaurent Pinchart .dequeue = isp1760_ep_dequeue, 10510316ca63SLaurent Pinchart .set_halt = isp1760_ep_set_halt, 10520316ca63SLaurent Pinchart .set_wedge = isp1760_ep_set_wedge, 10530316ca63SLaurent Pinchart .fifo_flush = isp1760_ep_fifo_flush, 10540316ca63SLaurent Pinchart }; 10550316ca63SLaurent Pinchart 10560316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 10570316ca63SLaurent Pinchart * Device States 10580316ca63SLaurent Pinchart */ 10590316ca63SLaurent Pinchart 10600316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 10610316ca63SLaurent Pinchart static void isp1760_udc_connect(struct isp1760_udc *udc) 10620316ca63SLaurent Pinchart { 10630316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED); 10640316ca63SLaurent Pinchart mod_timer(&udc->vbus_timer, jiffies + ISP1760_VBUS_POLL_INTERVAL); 10650316ca63SLaurent Pinchart } 10660316ca63SLaurent Pinchart 10670316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 10680316ca63SLaurent Pinchart static void isp1760_udc_disconnect(struct isp1760_udc *udc) 10690316ca63SLaurent Pinchart { 10700316ca63SLaurent Pinchart if (udc->gadget.state < USB_STATE_POWERED) 10710316ca63SLaurent Pinchart return; 10720316ca63SLaurent Pinchart 10730316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "Device disconnected in state %u\n", 10740316ca63SLaurent Pinchart udc->gadget.state); 10750316ca63SLaurent Pinchart 10760316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_UNKNOWN; 10770316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); 10780316ca63SLaurent Pinchart 10790316ca63SLaurent Pinchart if (udc->driver->disconnect) 10800316ca63SLaurent Pinchart udc->driver->disconnect(&udc->gadget); 10810316ca63SLaurent Pinchart 10820316ca63SLaurent Pinchart del_timer(&udc->vbus_timer); 10830316ca63SLaurent Pinchart 10840316ca63SLaurent Pinchart /* TODO Reset all endpoints ? */ 10850316ca63SLaurent Pinchart } 10860316ca63SLaurent Pinchart 10870316ca63SLaurent Pinchart static void isp1760_udc_init_hw(struct isp1760_udc *udc) 10880316ca63SLaurent Pinchart { 10890316ca63SLaurent Pinchart /* 10900316ca63SLaurent Pinchart * The device controller currently shares its interrupt with the host 10910316ca63SLaurent Pinchart * controller, the DC_IRQ polarity and signaling mode are ignored. Set 10920316ca63SLaurent Pinchart * the to active-low level-triggered. 10930316ca63SLaurent Pinchart * 10940316ca63SLaurent Pinchart * Configure the control, in and out pipes to generate interrupts on 10950316ca63SLaurent Pinchart * ACK tokens only (and NYET for the out pipe). The default 10960316ca63SLaurent Pinchart * configuration also generates an interrupt on the first NACK token. 10970316ca63SLaurent Pinchart */ 10980316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK | 10990316ca63SLaurent Pinchart DC_DDBGMODOUT_ACK_NYET); 11000316ca63SLaurent Pinchart 11010316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) | 11020316ca63SLaurent Pinchart DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) | 11030316ca63SLaurent Pinchart DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) | 11040316ca63SLaurent Pinchart DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP | 11050316ca63SLaurent Pinchart DC_IEHS_STA | DC_IEBRST); 11060316ca63SLaurent Pinchart 11070316ca63SLaurent Pinchart if (udc->connected) 11080316ca63SLaurent Pinchart isp1760_set_pullup(udc->isp, true); 11090316ca63SLaurent Pinchart 11100316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN); 11110316ca63SLaurent Pinchart } 11120316ca63SLaurent Pinchart 11130316ca63SLaurent Pinchart static void isp1760_udc_reset(struct isp1760_udc *udc) 11140316ca63SLaurent Pinchart { 11150316ca63SLaurent Pinchart unsigned long flags; 11160316ca63SLaurent Pinchart 11170316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 11180316ca63SLaurent Pinchart 11190316ca63SLaurent Pinchart /* 11200316ca63SLaurent Pinchart * The bus reset has reset most registers to their default value, 11210316ca63SLaurent Pinchart * reinitialize the UDC hardware. 11220316ca63SLaurent Pinchart */ 11230316ca63SLaurent Pinchart isp1760_udc_init_hw(udc); 11240316ca63SLaurent Pinchart 11250316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 11260316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_FULL; 11270316ca63SLaurent Pinchart 11280316ca63SLaurent Pinchart usb_gadget_udc_reset(&udc->gadget, udc->driver); 11290316ca63SLaurent Pinchart 11300316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 11310316ca63SLaurent Pinchart } 11320316ca63SLaurent Pinchart 11330316ca63SLaurent Pinchart static void isp1760_udc_suspend(struct isp1760_udc *udc) 11340316ca63SLaurent Pinchart { 11350316ca63SLaurent Pinchart if (udc->gadget.state < USB_STATE_DEFAULT) 11360316ca63SLaurent Pinchart return; 11370316ca63SLaurent Pinchart 11380316ca63SLaurent Pinchart if (udc->driver->suspend) 11390316ca63SLaurent Pinchart udc->driver->suspend(&udc->gadget); 11400316ca63SLaurent Pinchart } 11410316ca63SLaurent Pinchart 11420316ca63SLaurent Pinchart static void isp1760_udc_resume(struct isp1760_udc *udc) 11430316ca63SLaurent Pinchart { 11440316ca63SLaurent Pinchart if (udc->gadget.state < USB_STATE_DEFAULT) 11450316ca63SLaurent Pinchart return; 11460316ca63SLaurent Pinchart 11470316ca63SLaurent Pinchart if (udc->driver->resume) 11480316ca63SLaurent Pinchart udc->driver->resume(&udc->gadget); 11490316ca63SLaurent Pinchart } 11500316ca63SLaurent Pinchart 11510316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 11520316ca63SLaurent Pinchart * Gadget Operations 11530316ca63SLaurent Pinchart */ 11540316ca63SLaurent Pinchart 11550316ca63SLaurent Pinchart static int isp1760_udc_get_frame(struct usb_gadget *gadget) 11560316ca63SLaurent Pinchart { 11570316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 11580316ca63SLaurent Pinchart 11590316ca63SLaurent Pinchart return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1); 11600316ca63SLaurent Pinchart } 11610316ca63SLaurent Pinchart 11620316ca63SLaurent Pinchart static int isp1760_udc_wakeup(struct usb_gadget *gadget) 11630316ca63SLaurent Pinchart { 11640316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 11650316ca63SLaurent Pinchart 11660316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s\n", __func__); 11670316ca63SLaurent Pinchart return -ENOTSUPP; 11680316ca63SLaurent Pinchart } 11690316ca63SLaurent Pinchart 11700316ca63SLaurent Pinchart static int isp1760_udc_set_selfpowered(struct usb_gadget *gadget, 11710316ca63SLaurent Pinchart int is_selfpowered) 11720316ca63SLaurent Pinchart { 11730316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 11740316ca63SLaurent Pinchart 11750316ca63SLaurent Pinchart if (is_selfpowered) 11760316ca63SLaurent Pinchart udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; 11770316ca63SLaurent Pinchart else 11780316ca63SLaurent Pinchart udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); 11790316ca63SLaurent Pinchart 11800316ca63SLaurent Pinchart return 0; 11810316ca63SLaurent Pinchart } 11820316ca63SLaurent Pinchart 11830316ca63SLaurent Pinchart static int isp1760_udc_pullup(struct usb_gadget *gadget, int is_on) 11840316ca63SLaurent Pinchart { 11850316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 11860316ca63SLaurent Pinchart 11870316ca63SLaurent Pinchart isp1760_set_pullup(udc->isp, is_on); 11880316ca63SLaurent Pinchart udc->connected = is_on; 11890316ca63SLaurent Pinchart 11900316ca63SLaurent Pinchart return 0; 11910316ca63SLaurent Pinchart } 11920316ca63SLaurent Pinchart 11930316ca63SLaurent Pinchart static int isp1760_udc_start(struct usb_gadget *gadget, 11940316ca63SLaurent Pinchart struct usb_gadget_driver *driver) 11950316ca63SLaurent Pinchart { 11960316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 119779852397SSudeep Holla unsigned long flags; 11980316ca63SLaurent Pinchart 11990316ca63SLaurent Pinchart /* The hardware doesn't support low speed. */ 12000316ca63SLaurent Pinchart if (driver->max_speed < USB_SPEED_FULL) { 12010316ca63SLaurent Pinchart dev_err(udc->isp->dev, "Invalid gadget driver\n"); 12020316ca63SLaurent Pinchart return -EINVAL; 12030316ca63SLaurent Pinchart } 12040316ca63SLaurent Pinchart 120579852397SSudeep Holla spin_lock_irqsave(&udc->lock, flags); 12060316ca63SLaurent Pinchart 12070316ca63SLaurent Pinchart if (udc->driver) { 12080316ca63SLaurent Pinchart dev_err(udc->isp->dev, "UDC already has a gadget driver\n"); 120965582a7fSSudeep Holla spin_unlock_irqrestore(&udc->lock, flags); 12100316ca63SLaurent Pinchart return -EBUSY; 12110316ca63SLaurent Pinchart } 12120316ca63SLaurent Pinchart 12130316ca63SLaurent Pinchart udc->driver = driver; 12140316ca63SLaurent Pinchart 121579852397SSudeep Holla spin_unlock_irqrestore(&udc->lock, flags); 12160316ca63SLaurent Pinchart 12170316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "starting UDC with driver %s\n", 12180316ca63SLaurent Pinchart driver->function); 12190316ca63SLaurent Pinchart 12200316ca63SLaurent Pinchart udc->devstatus = 0; 12210316ca63SLaurent Pinchart udc->connected = true; 12220316ca63SLaurent Pinchart 12230316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); 12240316ca63SLaurent Pinchart 12250316ca63SLaurent Pinchart /* DMA isn't supported yet, don't enable the DMA clock. */ 12260316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_MODE, DC_GLINTENA); 12270316ca63SLaurent Pinchart 12280316ca63SLaurent Pinchart isp1760_udc_init_hw(udc); 12290316ca63SLaurent Pinchart 12300316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "UDC started with driver %s\n", 12310316ca63SLaurent Pinchart driver->function); 12320316ca63SLaurent Pinchart 12330316ca63SLaurent Pinchart return 0; 12340316ca63SLaurent Pinchart } 12350316ca63SLaurent Pinchart 12360316ca63SLaurent Pinchart static int isp1760_udc_stop(struct usb_gadget *gadget) 12370316ca63SLaurent Pinchart { 12380316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 123979852397SSudeep Holla unsigned long flags; 12400316ca63SLaurent Pinchart 12410316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s\n", __func__); 12420316ca63SLaurent Pinchart 12430316ca63SLaurent Pinchart del_timer_sync(&udc->vbus_timer); 12440316ca63SLaurent Pinchart 12450316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_MODE, 0); 12460316ca63SLaurent Pinchart 124779852397SSudeep Holla spin_lock_irqsave(&udc->lock, flags); 12480316ca63SLaurent Pinchart udc->driver = NULL; 124979852397SSudeep Holla spin_unlock_irqrestore(&udc->lock, flags); 12500316ca63SLaurent Pinchart 12510316ca63SLaurent Pinchart return 0; 12520316ca63SLaurent Pinchart } 12530316ca63SLaurent Pinchart 12543774e02cSBhumika Goyal static const struct usb_gadget_ops isp1760_udc_ops = { 12550316ca63SLaurent Pinchart .get_frame = isp1760_udc_get_frame, 12560316ca63SLaurent Pinchart .wakeup = isp1760_udc_wakeup, 12570316ca63SLaurent Pinchart .set_selfpowered = isp1760_udc_set_selfpowered, 12580316ca63SLaurent Pinchart .pullup = isp1760_udc_pullup, 12590316ca63SLaurent Pinchart .udc_start = isp1760_udc_start, 12600316ca63SLaurent Pinchart .udc_stop = isp1760_udc_stop, 12610316ca63SLaurent Pinchart }; 12620316ca63SLaurent Pinchart 12630316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 12640316ca63SLaurent Pinchart * Interrupt Handling 12650316ca63SLaurent Pinchart */ 12660316ca63SLaurent Pinchart 12670316ca63SLaurent Pinchart static irqreturn_t isp1760_udc_irq(int irq, void *dev) 12680316ca63SLaurent Pinchart { 12690316ca63SLaurent Pinchart struct isp1760_udc *udc = dev; 12700316ca63SLaurent Pinchart unsigned int i; 12710316ca63SLaurent Pinchart u32 status; 12720316ca63SLaurent Pinchart 12730316ca63SLaurent Pinchart status = isp1760_udc_read(udc, DC_INTERRUPT) 12740316ca63SLaurent Pinchart & isp1760_udc_read(udc, DC_INTENABLE); 12750316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_INTERRUPT, status); 12760316ca63SLaurent Pinchart 12770316ca63SLaurent Pinchart if (status & DC_IEVBUS) { 12780316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__); 12790316ca63SLaurent Pinchart /* The VBUS interrupt is only triggered when VBUS appears. */ 12800316ca63SLaurent Pinchart spin_lock(&udc->lock); 12810316ca63SLaurent Pinchart isp1760_udc_connect(udc); 12820316ca63SLaurent Pinchart spin_unlock(&udc->lock); 12830316ca63SLaurent Pinchart } 12840316ca63SLaurent Pinchart 12850316ca63SLaurent Pinchart if (status & DC_IEBRST) { 12860316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__); 12870316ca63SLaurent Pinchart 12880316ca63SLaurent Pinchart isp1760_udc_reset(udc); 12890316ca63SLaurent Pinchart } 12900316ca63SLaurent Pinchart 12910316ca63SLaurent Pinchart for (i = 0; i <= 7; ++i) { 12920316ca63SLaurent Pinchart struct isp1760_ep *ep = &udc->ep[i*2]; 12930316ca63SLaurent Pinchart 12940316ca63SLaurent Pinchart if (status & DC_IEPTX(i)) { 12950316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(EPTX%u)\n", __func__, i); 12960316ca63SLaurent Pinchart isp1760_ep_tx_complete(ep); 12970316ca63SLaurent Pinchart } 12980316ca63SLaurent Pinchart 12990316ca63SLaurent Pinchart if (status & DC_IEPRX(i)) { 13000316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(EPRX%u)\n", __func__, i); 13010316ca63SLaurent Pinchart isp1760_ep_rx_ready(i ? ep - 1 : ep); 13020316ca63SLaurent Pinchart } 13030316ca63SLaurent Pinchart } 13040316ca63SLaurent Pinchart 13050316ca63SLaurent Pinchart if (status & DC_IEP0SETUP) { 13060316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__); 13070316ca63SLaurent Pinchart 13080316ca63SLaurent Pinchart isp1760_ep0_setup(udc); 13090316ca63SLaurent Pinchart } 13100316ca63SLaurent Pinchart 13110316ca63SLaurent Pinchart if (status & DC_IERESM) { 13120316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__); 13130316ca63SLaurent Pinchart isp1760_udc_resume(udc); 13140316ca63SLaurent Pinchart } 13150316ca63SLaurent Pinchart 13160316ca63SLaurent Pinchart if (status & DC_IESUSP) { 13170316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__); 13180316ca63SLaurent Pinchart 13190316ca63SLaurent Pinchart spin_lock(&udc->lock); 13200316ca63SLaurent Pinchart if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT)) 13210316ca63SLaurent Pinchart isp1760_udc_disconnect(udc); 13220316ca63SLaurent Pinchart else 13230316ca63SLaurent Pinchart isp1760_udc_suspend(udc); 13240316ca63SLaurent Pinchart spin_unlock(&udc->lock); 13250316ca63SLaurent Pinchart } 13260316ca63SLaurent Pinchart 13270316ca63SLaurent Pinchart if (status & DC_IEHS_STA) { 13280316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__); 13290316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_HIGH; 13300316ca63SLaurent Pinchart } 13310316ca63SLaurent Pinchart 13320316ca63SLaurent Pinchart return status ? IRQ_HANDLED : IRQ_NONE; 13330316ca63SLaurent Pinchart } 13340316ca63SLaurent Pinchart 13357e33da59SKees Cook static void isp1760_udc_vbus_poll(struct timer_list *t) 13360316ca63SLaurent Pinchart { 13377e33da59SKees Cook struct isp1760_udc *udc = from_timer(udc, t, vbus_timer); 13380316ca63SLaurent Pinchart unsigned long flags; 13390316ca63SLaurent Pinchart 13400316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 13410316ca63SLaurent Pinchart 13420316ca63SLaurent Pinchart if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT)) 13430316ca63SLaurent Pinchart isp1760_udc_disconnect(udc); 13440316ca63SLaurent Pinchart else if (udc->gadget.state >= USB_STATE_POWERED) 13450316ca63SLaurent Pinchart mod_timer(&udc->vbus_timer, 13460316ca63SLaurent Pinchart jiffies + ISP1760_VBUS_POLL_INTERVAL); 13470316ca63SLaurent Pinchart 13480316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 13490316ca63SLaurent Pinchart } 13500316ca63SLaurent Pinchart 13510316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 13520316ca63SLaurent Pinchart * Registration 13530316ca63SLaurent Pinchart */ 13540316ca63SLaurent Pinchart 13550316ca63SLaurent Pinchart static void isp1760_udc_init_eps(struct isp1760_udc *udc) 13560316ca63SLaurent Pinchart { 13570316ca63SLaurent Pinchart unsigned int i; 13580316ca63SLaurent Pinchart 13590316ca63SLaurent Pinchart INIT_LIST_HEAD(&udc->gadget.ep_list); 13600316ca63SLaurent Pinchart 13610316ca63SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(udc->ep); ++i) { 13620316ca63SLaurent Pinchart struct isp1760_ep *ep = &udc->ep[i]; 13630316ca63SLaurent Pinchart unsigned int ep_num = (i + 1) / 2; 13640316ca63SLaurent Pinchart bool is_in = !(i & 1); 13650316ca63SLaurent Pinchart 13660316ca63SLaurent Pinchart ep->udc = udc; 13670316ca63SLaurent Pinchart 13680316ca63SLaurent Pinchart INIT_LIST_HEAD(&ep->queue); 13690316ca63SLaurent Pinchart 13700316ca63SLaurent Pinchart ep->addr = (ep_num && is_in ? USB_DIR_IN : USB_DIR_OUT) 13710316ca63SLaurent Pinchart | ep_num; 13720316ca63SLaurent Pinchart ep->desc = NULL; 13730316ca63SLaurent Pinchart 13740316ca63SLaurent Pinchart sprintf(ep->name, "ep%u%s", ep_num, 13750316ca63SLaurent Pinchart ep_num ? (is_in ? "in" : "out") : ""); 13760316ca63SLaurent Pinchart 13770316ca63SLaurent Pinchart ep->ep.ops = &isp1760_ep_ops; 13780316ca63SLaurent Pinchart ep->ep.name = ep->name; 13790316ca63SLaurent Pinchart 13800316ca63SLaurent Pinchart /* 13810316ca63SLaurent Pinchart * Hardcode the maximum packet sizes for now, to 64 bytes for 13820316ca63SLaurent Pinchart * the control endpoint and 512 bytes for all other endpoints. 13830316ca63SLaurent Pinchart * This fits in the 8kB FIFO without double-buffering. 13840316ca63SLaurent Pinchart */ 13850316ca63SLaurent Pinchart if (ep_num == 0) { 13866fd82b69SRobert Baldyga usb_ep_set_maxpacket_limit(&ep->ep, 64); 1387eb4cbc19SRobert Baldyga ep->ep.caps.type_control = true; 1388eb4cbc19SRobert Baldyga ep->ep.caps.dir_in = true; 1389eb4cbc19SRobert Baldyga ep->ep.caps.dir_out = true; 13900316ca63SLaurent Pinchart ep->maxpacket = 64; 13910316ca63SLaurent Pinchart udc->gadget.ep0 = &ep->ep; 13920316ca63SLaurent Pinchart } else { 13936fd82b69SRobert Baldyga usb_ep_set_maxpacket_limit(&ep->ep, 512); 1394eb4cbc19SRobert Baldyga ep->ep.caps.type_iso = true; 1395eb4cbc19SRobert Baldyga ep->ep.caps.type_bulk = true; 1396eb4cbc19SRobert Baldyga ep->ep.caps.type_int = true; 13970316ca63SLaurent Pinchart ep->maxpacket = 0; 13980316ca63SLaurent Pinchart list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); 13990316ca63SLaurent Pinchart } 1400eb4cbc19SRobert Baldyga 1401eb4cbc19SRobert Baldyga if (is_in) 1402eb4cbc19SRobert Baldyga ep->ep.caps.dir_in = true; 1403eb4cbc19SRobert Baldyga else 1404eb4cbc19SRobert Baldyga ep->ep.caps.dir_out = true; 14050316ca63SLaurent Pinchart } 14060316ca63SLaurent Pinchart } 14070316ca63SLaurent Pinchart 14080316ca63SLaurent Pinchart static int isp1760_udc_init(struct isp1760_udc *udc) 14090316ca63SLaurent Pinchart { 14100316ca63SLaurent Pinchart u16 scratch; 14110316ca63SLaurent Pinchart u32 chipid; 14120316ca63SLaurent Pinchart 14130316ca63SLaurent Pinchart /* 14140316ca63SLaurent Pinchart * Check that the controller is present by writing to the scratch 14150316ca63SLaurent Pinchart * register, modifying the bus pattern by reading from the chip ID 14160316ca63SLaurent Pinchart * register, and reading the scratch register value back. The chip ID 14170316ca63SLaurent Pinchart * and scratch register contents must match the expected values. 14180316ca63SLaurent Pinchart */ 14190316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_SCRATCH, 0xbabe); 14200316ca63SLaurent Pinchart chipid = isp1760_udc_read(udc, DC_CHIPID); 14210316ca63SLaurent Pinchart scratch = isp1760_udc_read(udc, DC_SCRATCH); 14220316ca63SLaurent Pinchart 14230316ca63SLaurent Pinchart if (scratch != 0xbabe) { 14240316ca63SLaurent Pinchart dev_err(udc->isp->dev, 14250316ca63SLaurent Pinchart "udc: scratch test failed (0x%04x/0x%08x)\n", 14260316ca63SLaurent Pinchart scratch, chipid); 14270316ca63SLaurent Pinchart return -ENODEV; 14280316ca63SLaurent Pinchart } 14290316ca63SLaurent Pinchart 14301998adabSSudeep Holla if (chipid != 0x00011582 && chipid != 0x00158210) { 14310316ca63SLaurent Pinchart dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); 14320316ca63SLaurent Pinchart return -ENODEV; 14330316ca63SLaurent Pinchart } 14340316ca63SLaurent Pinchart 14350316ca63SLaurent Pinchart /* Reset the device controller. */ 14360316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_MODE, DC_SFRESET); 14370316ca63SLaurent Pinchart usleep_range(10000, 11000); 14380316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_MODE, 0); 14390316ca63SLaurent Pinchart usleep_range(10000, 11000); 14400316ca63SLaurent Pinchart 14410316ca63SLaurent Pinchart return 0; 14420316ca63SLaurent Pinchart } 14430316ca63SLaurent Pinchart 14440316ca63SLaurent Pinchart int isp1760_udc_register(struct isp1760_device *isp, int irq, 14450316ca63SLaurent Pinchart unsigned long irqflags) 14460316ca63SLaurent Pinchart { 14470316ca63SLaurent Pinchart struct isp1760_udc *udc = &isp->udc; 14480316ca63SLaurent Pinchart const char *devname; 14490316ca63SLaurent Pinchart int ret; 14500316ca63SLaurent Pinchart 14510316ca63SLaurent Pinchart udc->irq = -1; 14520316ca63SLaurent Pinchart udc->isp = isp; 14530316ca63SLaurent Pinchart udc->regs = isp->regs; 14540316ca63SLaurent Pinchart 14550316ca63SLaurent Pinchart spin_lock_init(&udc->lock); 14567e33da59SKees Cook timer_setup(&udc->vbus_timer, isp1760_udc_vbus_poll, 0); 14570316ca63SLaurent Pinchart 14580316ca63SLaurent Pinchart ret = isp1760_udc_init(udc); 14590316ca63SLaurent Pinchart if (ret < 0) 14600316ca63SLaurent Pinchart return ret; 14610316ca63SLaurent Pinchart 14620316ca63SLaurent Pinchart devname = dev_name(isp->dev); 14630316ca63SLaurent Pinchart udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL); 14640316ca63SLaurent Pinchart if (!udc->irqname) 14650316ca63SLaurent Pinchart return -ENOMEM; 14660316ca63SLaurent Pinchart 14670316ca63SLaurent Pinchart sprintf(udc->irqname, "%s (udc)", devname); 14680316ca63SLaurent Pinchart 146980b4a0f8SValentin Rothberg ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags, 147080b4a0f8SValentin Rothberg udc->irqname, udc); 14710316ca63SLaurent Pinchart if (ret < 0) 14720316ca63SLaurent Pinchart goto error; 14730316ca63SLaurent Pinchart 14740316ca63SLaurent Pinchart udc->irq = irq; 14750316ca63SLaurent Pinchart 14760316ca63SLaurent Pinchart /* 14770316ca63SLaurent Pinchart * Initialize the gadget static fields and register its device. Gadget 14780316ca63SLaurent Pinchart * fields that vary during the life time of the gadget are initialized 14790316ca63SLaurent Pinchart * by the UDC core. 14800316ca63SLaurent Pinchart */ 14810316ca63SLaurent Pinchart udc->gadget.ops = &isp1760_udc_ops; 14820316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_UNKNOWN; 14830316ca63SLaurent Pinchart udc->gadget.max_speed = USB_SPEED_HIGH; 14840316ca63SLaurent Pinchart udc->gadget.name = "isp1761_udc"; 14850316ca63SLaurent Pinchart 14860316ca63SLaurent Pinchart isp1760_udc_init_eps(udc); 14870316ca63SLaurent Pinchart 14880316ca63SLaurent Pinchart ret = usb_add_gadget_udc(isp->dev, &udc->gadget); 14890316ca63SLaurent Pinchart if (ret < 0) 14900316ca63SLaurent Pinchart goto error; 14910316ca63SLaurent Pinchart 14920316ca63SLaurent Pinchart return 0; 14930316ca63SLaurent Pinchart 14940316ca63SLaurent Pinchart error: 14950316ca63SLaurent Pinchart if (udc->irq >= 0) 14960316ca63SLaurent Pinchart free_irq(udc->irq, udc); 14970316ca63SLaurent Pinchart kfree(udc->irqname); 14980316ca63SLaurent Pinchart 14990316ca63SLaurent Pinchart return ret; 15000316ca63SLaurent Pinchart } 15010316ca63SLaurent Pinchart 15020316ca63SLaurent Pinchart void isp1760_udc_unregister(struct isp1760_device *isp) 15030316ca63SLaurent Pinchart { 15040316ca63SLaurent Pinchart struct isp1760_udc *udc = &isp->udc; 15050316ca63SLaurent Pinchart 1506d21daf1eSLaurent Pinchart if (!udc->isp) 1507d21daf1eSLaurent Pinchart return; 1508d21daf1eSLaurent Pinchart 15090316ca63SLaurent Pinchart usb_del_gadget_udc(&udc->gadget); 15100316ca63SLaurent Pinchart 15110316ca63SLaurent Pinchart free_irq(udc->irq, udc); 15120316ca63SLaurent Pinchart kfree(udc->irqname); 15130316ca63SLaurent Pinchart } 1514