15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 20316ca63SLaurent Pinchart /* 30316ca63SLaurent Pinchart * Driver for the NXP ISP1761 device controller 40316ca63SLaurent Pinchart * 560d789f3SRui Miguel Silva * Copyright 2021 Linaro, Rui Miguel Silva 60316ca63SLaurent Pinchart * Copyright 2014 Ideas on Board Oy 70316ca63SLaurent Pinchart * 80316ca63SLaurent Pinchart * Contacts: 90316ca63SLaurent Pinchart * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1060d789f3SRui Miguel Silva * Rui Miguel Silva <rui.silva@linaro.org> 110316ca63SLaurent Pinchart */ 120316ca63SLaurent Pinchart 130316ca63SLaurent Pinchart #include <linux/interrupt.h> 140316ca63SLaurent Pinchart #include <linux/io.h> 150316ca63SLaurent Pinchart #include <linux/kernel.h> 160316ca63SLaurent Pinchart #include <linux/list.h> 170316ca63SLaurent Pinchart #include <linux/module.h> 180316ca63SLaurent Pinchart #include <linux/slab.h> 190316ca63SLaurent Pinchart #include <linux/timer.h> 200316ca63SLaurent Pinchart #include <linux/usb.h> 210316ca63SLaurent Pinchart 220316ca63SLaurent Pinchart #include "isp1760-core.h" 230316ca63SLaurent Pinchart #include "isp1760-regs.h" 240316ca63SLaurent Pinchart #include "isp1760-udc.h" 250316ca63SLaurent Pinchart 260316ca63SLaurent Pinchart #define ISP1760_VBUS_POLL_INTERVAL msecs_to_jiffies(500) 270316ca63SLaurent Pinchart 280316ca63SLaurent Pinchart struct isp1760_request { 290316ca63SLaurent Pinchart struct usb_request req; 300316ca63SLaurent Pinchart struct list_head queue; 310316ca63SLaurent Pinchart struct isp1760_ep *ep; 320316ca63SLaurent Pinchart unsigned int packet_size; 330316ca63SLaurent Pinchart }; 340316ca63SLaurent Pinchart 350316ca63SLaurent Pinchart static inline struct isp1760_udc *gadget_to_udc(struct usb_gadget *gadget) 360316ca63SLaurent Pinchart { 370316ca63SLaurent Pinchart return container_of(gadget, struct isp1760_udc, gadget); 380316ca63SLaurent Pinchart } 390316ca63SLaurent Pinchart 400316ca63SLaurent Pinchart static inline struct isp1760_ep *ep_to_udc_ep(struct usb_ep *ep) 410316ca63SLaurent Pinchart { 420316ca63SLaurent Pinchart return container_of(ep, struct isp1760_ep, ep); 430316ca63SLaurent Pinchart } 440316ca63SLaurent Pinchart 450316ca63SLaurent Pinchart static inline struct isp1760_request *req_to_udc_req(struct usb_request *req) 460316ca63SLaurent Pinchart { 470316ca63SLaurent Pinchart return container_of(req, struct isp1760_request, req); 480316ca63SLaurent Pinchart } 490316ca63SLaurent Pinchart 501da9e1c0SRui Miguel Silva static u32 isp1760_udc_read(struct isp1760_udc *udc, u16 field) 510316ca63SLaurent Pinchart { 521da9e1c0SRui Miguel Silva return isp1760_field_read(udc->fields, field); 530316ca63SLaurent Pinchart } 540316ca63SLaurent Pinchart 551da9e1c0SRui Miguel Silva static void isp1760_udc_write(struct isp1760_udc *udc, u16 field, u32 val) 560316ca63SLaurent Pinchart { 571da9e1c0SRui Miguel Silva isp1760_field_write(udc->fields, field, val); 580316ca63SLaurent Pinchart } 590316ca63SLaurent Pinchart 601da9e1c0SRui Miguel Silva static u32 isp1760_udc_read_raw(struct isp1760_udc *udc, u16 reg) 611da9e1c0SRui Miguel Silva { 621da9e1c0SRui Miguel Silva __le32 val; 631da9e1c0SRui Miguel Silva 641da9e1c0SRui Miguel Silva regmap_raw_read(udc->regs, reg, &val, 4); 651da9e1c0SRui Miguel Silva 661da9e1c0SRui Miguel Silva return le32_to_cpu(val); 671da9e1c0SRui Miguel Silva } 681da9e1c0SRui Miguel Silva 691da9e1c0SRui Miguel Silva static u16 isp1760_udc_read_raw16(struct isp1760_udc *udc, u16 reg) 701da9e1c0SRui Miguel Silva { 711da9e1c0SRui Miguel Silva __le16 val; 721da9e1c0SRui Miguel Silva 731da9e1c0SRui Miguel Silva regmap_raw_read(udc->regs, reg, &val, 2); 741da9e1c0SRui Miguel Silva 751da9e1c0SRui Miguel Silva return le16_to_cpu(val); 761da9e1c0SRui Miguel Silva } 771da9e1c0SRui Miguel Silva 781da9e1c0SRui Miguel Silva static void isp1760_udc_write_raw(struct isp1760_udc *udc, u16 reg, u32 val) 791da9e1c0SRui Miguel Silva { 801da9e1c0SRui Miguel Silva __le32 val_le = cpu_to_le32(val); 811da9e1c0SRui Miguel Silva 821da9e1c0SRui Miguel Silva regmap_raw_write(udc->regs, reg, &val_le, 4); 831da9e1c0SRui Miguel Silva } 841da9e1c0SRui Miguel Silva 851da9e1c0SRui Miguel Silva static void isp1760_udc_write_raw16(struct isp1760_udc *udc, u16 reg, u16 val) 861da9e1c0SRui Miguel Silva { 871da9e1c0SRui Miguel Silva __le16 val_le = cpu_to_le16(val); 881da9e1c0SRui Miguel Silva 891da9e1c0SRui Miguel Silva regmap_raw_write(udc->regs, reg, &val_le, 2); 901da9e1c0SRui Miguel Silva } 911da9e1c0SRui Miguel Silva 921da9e1c0SRui Miguel Silva static void isp1760_udc_set(struct isp1760_udc *udc, u32 field) 931da9e1c0SRui Miguel Silva { 941da9e1c0SRui Miguel Silva isp1760_udc_write(udc, field, 0xFFFFFFFF); 951da9e1c0SRui Miguel Silva } 961da9e1c0SRui Miguel Silva 971da9e1c0SRui Miguel Silva static void isp1760_udc_clear(struct isp1760_udc *udc, u32 field) 981da9e1c0SRui Miguel Silva { 991da9e1c0SRui Miguel Silva isp1760_udc_write(udc, field, 0); 1001da9e1c0SRui Miguel Silva } 1011da9e1c0SRui Miguel Silva 1021da9e1c0SRui Miguel Silva static bool isp1760_udc_is_set(struct isp1760_udc *udc, u32 field) 1031da9e1c0SRui Miguel Silva { 1041da9e1c0SRui Miguel Silva return !!isp1760_udc_read(udc, field); 1051da9e1c0SRui Miguel Silva } 1060316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 1070316ca63SLaurent Pinchart * Endpoint Management 1080316ca63SLaurent Pinchart */ 1090316ca63SLaurent Pinchart 1100316ca63SLaurent Pinchart static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc, 1110316ca63SLaurent Pinchart u16 index) 1120316ca63SLaurent Pinchart { 1130316ca63SLaurent Pinchart unsigned int i; 1140316ca63SLaurent Pinchart 1150316ca63SLaurent Pinchart if (index == 0) 1160316ca63SLaurent Pinchart return &udc->ep[0]; 1170316ca63SLaurent Pinchart 1180316ca63SLaurent Pinchart for (i = 1; i < ARRAY_SIZE(udc->ep); ++i) { 1190316ca63SLaurent Pinchart if (udc->ep[i].addr == index) 1200316ca63SLaurent Pinchart return udc->ep[i].desc ? &udc->ep[i] : NULL; 1210316ca63SLaurent Pinchart } 1220316ca63SLaurent Pinchart 1230316ca63SLaurent Pinchart return NULL; 1240316ca63SLaurent Pinchart } 1250316ca63SLaurent Pinchart 1261da9e1c0SRui Miguel Silva static void __isp1760_udc_select_ep(struct isp1760_udc *udc, 1271da9e1c0SRui Miguel Silva struct isp1760_ep *ep, int dir) 1280316ca63SLaurent Pinchart { 1291da9e1c0SRui Miguel Silva isp1760_udc_write(udc, DC_ENDPIDX, ep->addr & USB_ENDPOINT_NUMBER_MASK); 1301da9e1c0SRui Miguel Silva 1311da9e1c0SRui Miguel Silva if (dir == USB_DIR_IN) 1321da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_EPDIR); 1331da9e1c0SRui Miguel Silva else 1341da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_EPDIR); 1350316ca63SLaurent Pinchart } 1360316ca63SLaurent Pinchart 1370316ca63SLaurent Pinchart /** 1380316ca63SLaurent Pinchart * isp1760_udc_select_ep - Select an endpoint for register access 1390316ca63SLaurent Pinchart * @ep: The endpoint 1408268acfeSLee Jones * @udc: Reference to the device controller 1410316ca63SLaurent Pinchart * 1420316ca63SLaurent Pinchart * The ISP1761 endpoint registers are banked. This function selects the target 1430316ca63SLaurent Pinchart * endpoint for banked register access. The selection remains valid until the 1440316ca63SLaurent Pinchart * next call to this function, the next direct access to the EPINDEX register 1450316ca63SLaurent Pinchart * or the next reset, whichever comes first. 1460316ca63SLaurent Pinchart * 1470316ca63SLaurent Pinchart * Called with the UDC spinlock held. 1480316ca63SLaurent Pinchart */ 1491da9e1c0SRui Miguel Silva static void isp1760_udc_select_ep(struct isp1760_udc *udc, 1501da9e1c0SRui Miguel Silva struct isp1760_ep *ep) 1510316ca63SLaurent Pinchart { 1521da9e1c0SRui Miguel Silva __isp1760_udc_select_ep(udc, ep, ep->addr & USB_ENDPOINT_DIR_MASK); 1530316ca63SLaurent Pinchart } 1540316ca63SLaurent Pinchart 1550316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 1560316ca63SLaurent Pinchart static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir) 1570316ca63SLaurent Pinchart { 1580316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 1590316ca63SLaurent Pinchart 1600316ca63SLaurent Pinchart /* 1610316ca63SLaurent Pinchart * Proceed to the status stage. The status stage data packet flows in 1620316ca63SLaurent Pinchart * the direction opposite to the data stage data packets, we thus need 1630316ca63SLaurent Pinchart * to select the OUT/IN endpoint for IN/OUT transfers. 1640316ca63SLaurent Pinchart */ 1651da9e1c0SRui Miguel Silva if (dir == USB_DIR_IN) 1661da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_EPDIR); 1671da9e1c0SRui Miguel Silva else 1681da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_EPDIR); 1691da9e1c0SRui Miguel Silva 1701da9e1c0SRui Miguel Silva isp1760_udc_write(udc, DC_ENDPIDX, 1); 1711da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_STATUS); 1720316ca63SLaurent Pinchart 1730316ca63SLaurent Pinchart /* 1740316ca63SLaurent Pinchart * The hardware will terminate the request automatically and go back to 1750316ca63SLaurent Pinchart * the setup stage without notifying us. 1760316ca63SLaurent Pinchart */ 1770316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 1780316ca63SLaurent Pinchart } 1790316ca63SLaurent Pinchart 1800316ca63SLaurent Pinchart /* Called without the UDC spinlock held. */ 1810316ca63SLaurent Pinchart static void isp1760_udc_request_complete(struct isp1760_ep *ep, 1820316ca63SLaurent Pinchart struct isp1760_request *req, 1830316ca63SLaurent Pinchart int status) 1840316ca63SLaurent Pinchart { 1850316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 1860316ca63SLaurent Pinchart unsigned long flags; 1870316ca63SLaurent Pinchart 1880316ca63SLaurent Pinchart dev_dbg(ep->udc->isp->dev, "completing request %p with status %d\n", 1890316ca63SLaurent Pinchart req, status); 1900316ca63SLaurent Pinchart 1910316ca63SLaurent Pinchart req->ep = NULL; 1920316ca63SLaurent Pinchart req->req.status = status; 1930316ca63SLaurent Pinchart req->req.complete(&ep->ep, &req->req); 1940316ca63SLaurent Pinchart 1950316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 1960316ca63SLaurent Pinchart 1970316ca63SLaurent Pinchart /* 1980316ca63SLaurent Pinchart * When completing control OUT requests, move to the status stage after 1990316ca63SLaurent Pinchart * calling the request complete callback. This gives the gadget an 2000316ca63SLaurent Pinchart * opportunity to stall the control transfer if needed. 2010316ca63SLaurent Pinchart */ 2020316ca63SLaurent Pinchart if (status == 0 && ep->addr == 0 && udc->ep0_dir == USB_DIR_OUT) 2030316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(ep, USB_DIR_OUT); 2040316ca63SLaurent Pinchart 2050316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 2060316ca63SLaurent Pinchart } 2070316ca63SLaurent Pinchart 2080316ca63SLaurent Pinchart static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep) 2090316ca63SLaurent Pinchart { 2100316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 2110316ca63SLaurent Pinchart unsigned long flags; 2120316ca63SLaurent Pinchart 2130316ca63SLaurent Pinchart dev_dbg(ep->udc->isp->dev, "%s(ep%02x)\n", __func__, ep->addr); 2140316ca63SLaurent Pinchart 2150316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 2160316ca63SLaurent Pinchart 2170316ca63SLaurent Pinchart /* Stall both the IN and OUT endpoints. */ 2181da9e1c0SRui Miguel Silva __isp1760_udc_select_ep(udc, ep, USB_DIR_OUT); 2191da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_STALL); 2201da9e1c0SRui Miguel Silva __isp1760_udc_select_ep(udc, ep, USB_DIR_IN); 2211da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_STALL); 2220316ca63SLaurent Pinchart 2230316ca63SLaurent Pinchart /* A protocol stall completes the control transaction. */ 2240316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 2250316ca63SLaurent Pinchart 2260316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 2270316ca63SLaurent Pinchart } 2280316ca63SLaurent Pinchart 2290316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 2300316ca63SLaurent Pinchart * Data Endpoints 2310316ca63SLaurent Pinchart */ 2320316ca63SLaurent Pinchart 2330316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 2340316ca63SLaurent Pinchart static bool isp1760_udc_receive(struct isp1760_ep *ep, 2350316ca63SLaurent Pinchart struct isp1760_request *req) 2360316ca63SLaurent Pinchart { 2370316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 2380316ca63SLaurent Pinchart unsigned int len; 2390316ca63SLaurent Pinchart u32 *buf; 2400316ca63SLaurent Pinchart int i; 2410316ca63SLaurent Pinchart 2421da9e1c0SRui Miguel Silva isp1760_udc_select_ep(udc, ep); 2431da9e1c0SRui Miguel Silva len = isp1760_udc_read(udc, DC_BUFLEN); 2440316ca63SLaurent Pinchart 2450316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n", 2460316ca63SLaurent Pinchart __func__, len, req->req.actual, req->req.length); 2470316ca63SLaurent Pinchart 2480316ca63SLaurent Pinchart len = min(len, req->req.length - req->req.actual); 2490316ca63SLaurent Pinchart 2500316ca63SLaurent Pinchart if (!len) { 2510316ca63SLaurent Pinchart /* 2520316ca63SLaurent Pinchart * There's no data to be read from the FIFO, acknowledge the RX 2530316ca63SLaurent Pinchart * interrupt by clearing the buffer. 2540316ca63SLaurent Pinchart * 2550316ca63SLaurent Pinchart * TODO: What if another packet arrives in the meantime ? The 2560316ca63SLaurent Pinchart * datasheet doesn't clearly document how this should be 2570316ca63SLaurent Pinchart * handled. 2580316ca63SLaurent Pinchart */ 2591da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_CLBUF); 2600316ca63SLaurent Pinchart return false; 2610316ca63SLaurent Pinchart } 2620316ca63SLaurent Pinchart 2630316ca63SLaurent Pinchart buf = req->req.buf + req->req.actual; 2640316ca63SLaurent Pinchart 2650316ca63SLaurent Pinchart /* 2660316ca63SLaurent Pinchart * Make sure not to read more than one extra byte, otherwise data from 2670316ca63SLaurent Pinchart * the next packet might be removed from the FIFO. 2680316ca63SLaurent Pinchart */ 2690316ca63SLaurent Pinchart for (i = len; i > 2; i -= 4, ++buf) 2701da9e1c0SRui Miguel Silva *buf = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT); 2710316ca63SLaurent Pinchart if (i > 0) 2721da9e1c0SRui Miguel Silva *(u16 *)buf = isp1760_udc_read_raw16(udc, ISP176x_DC_DATAPORT); 2730316ca63SLaurent Pinchart 2740316ca63SLaurent Pinchart req->req.actual += len; 2750316ca63SLaurent Pinchart 2760316ca63SLaurent Pinchart /* 2770316ca63SLaurent Pinchart * TODO: The short_not_ok flag isn't supported yet, but isn't used by 2780316ca63SLaurent Pinchart * any gadget driver either. 2790316ca63SLaurent Pinchart */ 2800316ca63SLaurent Pinchart 2810316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 2820316ca63SLaurent Pinchart "%s: req %p actual/length %u/%u maxpacket %u packet size %u\n", 2830316ca63SLaurent Pinchart __func__, req, req->req.actual, req->req.length, ep->maxpacket, 2840316ca63SLaurent Pinchart len); 2850316ca63SLaurent Pinchart 2860316ca63SLaurent Pinchart ep->rx_pending = false; 2870316ca63SLaurent Pinchart 2880316ca63SLaurent Pinchart /* 2890316ca63SLaurent Pinchart * Complete the request if all data has been received or if a short 2900316ca63SLaurent Pinchart * packet has been received. 2910316ca63SLaurent Pinchart */ 2920316ca63SLaurent Pinchart if (req->req.actual == req->req.length || len < ep->maxpacket) { 2930316ca63SLaurent Pinchart list_del(&req->queue); 2940316ca63SLaurent Pinchart return true; 2950316ca63SLaurent Pinchart } 2960316ca63SLaurent Pinchart 2970316ca63SLaurent Pinchart return false; 2980316ca63SLaurent Pinchart } 2990316ca63SLaurent Pinchart 3000316ca63SLaurent Pinchart static void isp1760_udc_transmit(struct isp1760_ep *ep, 3010316ca63SLaurent Pinchart struct isp1760_request *req) 3020316ca63SLaurent Pinchart { 3030316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 3040316ca63SLaurent Pinchart u32 *buf = req->req.buf + req->req.actual; 3050316ca63SLaurent Pinchart int i; 3060316ca63SLaurent Pinchart 3070316ca63SLaurent Pinchart req->packet_size = min(req->req.length - req->req.actual, 3080316ca63SLaurent Pinchart ep->maxpacket); 3090316ca63SLaurent Pinchart 3100316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: transferring %u bytes (%u/%u done)\n", 3110316ca63SLaurent Pinchart __func__, req->packet_size, req->req.actual, 3120316ca63SLaurent Pinchart req->req.length); 3130316ca63SLaurent Pinchart 3141da9e1c0SRui Miguel Silva __isp1760_udc_select_ep(udc, ep, USB_DIR_IN); 3150316ca63SLaurent Pinchart 3160316ca63SLaurent Pinchart if (req->packet_size) 3170316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_BUFLEN, req->packet_size); 3180316ca63SLaurent Pinchart 3190316ca63SLaurent Pinchart /* 3200316ca63SLaurent Pinchart * Make sure not to write more than one extra byte, otherwise extra data 3210316ca63SLaurent Pinchart * will stay in the FIFO and will be transmitted during the next control 3220316ca63SLaurent Pinchart * request. The endpoint control CLBUF bit is supposed to allow flushing 3230316ca63SLaurent Pinchart * the FIFO for this kind of conditions, but doesn't seem to work. 3240316ca63SLaurent Pinchart */ 3250316ca63SLaurent Pinchart for (i = req->packet_size; i > 2; i -= 4, ++buf) 3261da9e1c0SRui Miguel Silva isp1760_udc_write_raw(udc, ISP176x_DC_DATAPORT, *buf); 3270316ca63SLaurent Pinchart if (i > 0) 3281da9e1c0SRui Miguel Silva isp1760_udc_write_raw16(udc, ISP176x_DC_DATAPORT, *(u16 *)buf); 3290316ca63SLaurent Pinchart 3300316ca63SLaurent Pinchart if (ep->addr == 0) 3311da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_DSEN); 3320316ca63SLaurent Pinchart if (!req->packet_size) 3331da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_VENDP); 3340316ca63SLaurent Pinchart } 3350316ca63SLaurent Pinchart 3360316ca63SLaurent Pinchart static void isp1760_ep_rx_ready(struct isp1760_ep *ep) 3370316ca63SLaurent Pinchart { 3380316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 3390316ca63SLaurent Pinchart struct isp1760_request *req; 3400316ca63SLaurent Pinchart bool complete; 3410316ca63SLaurent Pinchart 3420316ca63SLaurent Pinchart spin_lock(&udc->lock); 3430316ca63SLaurent Pinchart 3440316ca63SLaurent Pinchart if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_OUT) { 3450316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3460316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: invalid ep0 state %u\n", __func__, 3470316ca63SLaurent Pinchart udc->ep0_state); 3480316ca63SLaurent Pinchart return; 3490316ca63SLaurent Pinchart } 3500316ca63SLaurent Pinchart 3510316ca63SLaurent Pinchart if (ep->addr != 0 && !ep->desc) { 3520316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3530316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__, 3540316ca63SLaurent Pinchart ep->addr); 3550316ca63SLaurent Pinchart return; 3560316ca63SLaurent Pinchart } 3570316ca63SLaurent Pinchart 3580316ca63SLaurent Pinchart if (list_empty(&ep->queue)) { 3590316ca63SLaurent Pinchart ep->rx_pending = true; 3600316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3610316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x (%p) has no request queued\n", 3620316ca63SLaurent Pinchart __func__, ep->addr, ep); 3630316ca63SLaurent Pinchart return; 3640316ca63SLaurent Pinchart } 3650316ca63SLaurent Pinchart 3660316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, struct isp1760_request, 3670316ca63SLaurent Pinchart queue); 3680316ca63SLaurent Pinchart complete = isp1760_udc_receive(ep, req); 3690316ca63SLaurent Pinchart 3700316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3710316ca63SLaurent Pinchart 3720316ca63SLaurent Pinchart if (complete) 3730316ca63SLaurent Pinchart isp1760_udc_request_complete(ep, req, 0); 3740316ca63SLaurent Pinchart } 3750316ca63SLaurent Pinchart 3760316ca63SLaurent Pinchart static void isp1760_ep_tx_complete(struct isp1760_ep *ep) 3770316ca63SLaurent Pinchart { 3780316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 3790316ca63SLaurent Pinchart struct isp1760_request *complete = NULL; 3800316ca63SLaurent Pinchart struct isp1760_request *req; 3810316ca63SLaurent Pinchart bool need_zlp; 3820316ca63SLaurent Pinchart 3830316ca63SLaurent Pinchart spin_lock(&udc->lock); 3840316ca63SLaurent Pinchart 3850316ca63SLaurent Pinchart if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_IN) { 3860316ca63SLaurent Pinchart spin_unlock(&udc->lock); 3870316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "TX IRQ: invalid endpoint state %u\n", 3880316ca63SLaurent Pinchart udc->ep0_state); 3890316ca63SLaurent Pinchart return; 3900316ca63SLaurent Pinchart } 3910316ca63SLaurent Pinchart 3920316ca63SLaurent Pinchart if (list_empty(&ep->queue)) { 3930316ca63SLaurent Pinchart /* 3940316ca63SLaurent Pinchart * This can happen for the control endpoint when the reply to 3950316ca63SLaurent Pinchart * the GET_STATUS IN control request is sent directly by the 3960316ca63SLaurent Pinchart * setup IRQ handler. Just proceed to the status stage. 3970316ca63SLaurent Pinchart */ 3980316ca63SLaurent Pinchart if (ep->addr == 0) { 3990316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(ep, USB_DIR_IN); 4000316ca63SLaurent Pinchart spin_unlock(&udc->lock); 4010316ca63SLaurent Pinchart return; 4020316ca63SLaurent Pinchart } 4030316ca63SLaurent Pinchart 4040316ca63SLaurent Pinchart spin_unlock(&udc->lock); 4050316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x has no request queued\n", 4060316ca63SLaurent Pinchart __func__, ep->addr); 4070316ca63SLaurent Pinchart return; 4080316ca63SLaurent Pinchart } 4090316ca63SLaurent Pinchart 4100316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, struct isp1760_request, 4110316ca63SLaurent Pinchart queue); 4120316ca63SLaurent Pinchart req->req.actual += req->packet_size; 4130316ca63SLaurent Pinchart 4140316ca63SLaurent Pinchart need_zlp = req->req.actual == req->req.length && 4150316ca63SLaurent Pinchart !(req->req.length % ep->maxpacket) && 4160316ca63SLaurent Pinchart req->packet_size && req->req.zero; 4170316ca63SLaurent Pinchart 4180316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 4190316ca63SLaurent Pinchart "TX IRQ: req %p actual/length %u/%u maxpacket %u packet size %u zero %u need zlp %u\n", 4200316ca63SLaurent Pinchart req, req->req.actual, req->req.length, ep->maxpacket, 4210316ca63SLaurent Pinchart req->packet_size, req->req.zero, need_zlp); 4220316ca63SLaurent Pinchart 4230316ca63SLaurent Pinchart /* 4240316ca63SLaurent Pinchart * Complete the request if all data has been sent and we don't need to 4250316ca63SLaurent Pinchart * transmit a zero length packet. 4260316ca63SLaurent Pinchart */ 4270316ca63SLaurent Pinchart if (req->req.actual == req->req.length && !need_zlp) { 4280316ca63SLaurent Pinchart complete = req; 4290316ca63SLaurent Pinchart list_del(&req->queue); 4300316ca63SLaurent Pinchart 4310316ca63SLaurent Pinchart if (ep->addr == 0) 4320316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(ep, USB_DIR_IN); 4330316ca63SLaurent Pinchart 4340316ca63SLaurent Pinchart if (!list_empty(&ep->queue)) 4350316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, 4360316ca63SLaurent Pinchart struct isp1760_request, queue); 4370316ca63SLaurent Pinchart else 4380316ca63SLaurent Pinchart req = NULL; 4390316ca63SLaurent Pinchart } 4400316ca63SLaurent Pinchart 4410316ca63SLaurent Pinchart /* 4420316ca63SLaurent Pinchart * Transmit the next packet or start the next request, if any. 4430316ca63SLaurent Pinchart * 4440316ca63SLaurent Pinchart * TODO: If the endpoint is stalled the next request shouldn't be 4450316ca63SLaurent Pinchart * started, but what about the next packet ? 4460316ca63SLaurent Pinchart */ 4470316ca63SLaurent Pinchart if (req) 4480316ca63SLaurent Pinchart isp1760_udc_transmit(ep, req); 4490316ca63SLaurent Pinchart 4500316ca63SLaurent Pinchart spin_unlock(&udc->lock); 4510316ca63SLaurent Pinchart 4520316ca63SLaurent Pinchart if (complete) 4530316ca63SLaurent Pinchart isp1760_udc_request_complete(ep, complete, 0); 4540316ca63SLaurent Pinchart } 4550316ca63SLaurent Pinchart 4560316ca63SLaurent Pinchart static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt) 4570316ca63SLaurent Pinchart { 4580316ca63SLaurent Pinchart struct isp1760_udc *udc = ep->udc; 4590316ca63SLaurent Pinchart 4600316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: %s halt on ep%02x\n", __func__, 4610316ca63SLaurent Pinchart halt ? "set" : "clear", ep->addr); 4620316ca63SLaurent Pinchart 4630316ca63SLaurent Pinchart if (ep->desc && usb_endpoint_xfer_isoc(ep->desc)) { 4640316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x is isochronous\n", __func__, 4650316ca63SLaurent Pinchart ep->addr); 4660316ca63SLaurent Pinchart return -EINVAL; 4670316ca63SLaurent Pinchart } 4680316ca63SLaurent Pinchart 4691da9e1c0SRui Miguel Silva isp1760_udc_select_ep(udc, ep); 4701da9e1c0SRui Miguel Silva 4711da9e1c0SRui Miguel Silva if (halt) 4721da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_STALL); 4731da9e1c0SRui Miguel Silva else 4741da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_STALL); 4750316ca63SLaurent Pinchart 4760316ca63SLaurent Pinchart if (ep->addr == 0) { 4770316ca63SLaurent Pinchart /* When halting the control endpoint, stall both IN and OUT. */ 4781da9e1c0SRui Miguel Silva __isp1760_udc_select_ep(udc, ep, USB_DIR_IN); 4791da9e1c0SRui Miguel Silva if (halt) 4801da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_STALL); 4811da9e1c0SRui Miguel Silva else 4821da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_STALL); 4830316ca63SLaurent Pinchart } else if (!halt) { 4840316ca63SLaurent Pinchart /* Reset the data PID by cycling the endpoint enable bit. */ 4851da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_EPENABLE); 4861da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_EPENABLE); 4870316ca63SLaurent Pinchart 4880316ca63SLaurent Pinchart /* 4890316ca63SLaurent Pinchart * Disabling the endpoint emptied the transmit FIFO, fill it 4900316ca63SLaurent Pinchart * again if a request is pending. 4910316ca63SLaurent Pinchart * 4920316ca63SLaurent Pinchart * TODO: Does the gadget framework require synchronizatino with 4930316ca63SLaurent Pinchart * the TX IRQ handler ? 4940316ca63SLaurent Pinchart */ 4950316ca63SLaurent Pinchart if ((ep->addr & USB_DIR_IN) && !list_empty(&ep->queue)) { 4960316ca63SLaurent Pinchart struct isp1760_request *req; 4970316ca63SLaurent Pinchart 4980316ca63SLaurent Pinchart req = list_first_entry(&ep->queue, 4990316ca63SLaurent Pinchart struct isp1760_request, queue); 5000316ca63SLaurent Pinchart isp1760_udc_transmit(ep, req); 5010316ca63SLaurent Pinchart } 5020316ca63SLaurent Pinchart } 5030316ca63SLaurent Pinchart 5040316ca63SLaurent Pinchart ep->halted = halt; 5050316ca63SLaurent Pinchart 5060316ca63SLaurent Pinchart return 0; 5070316ca63SLaurent Pinchart } 5080316ca63SLaurent Pinchart 5090316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 5100316ca63SLaurent Pinchart * Control Endpoint 5110316ca63SLaurent Pinchart */ 5120316ca63SLaurent Pinchart 5130316ca63SLaurent Pinchart static int isp1760_udc_get_status(struct isp1760_udc *udc, 5140316ca63SLaurent Pinchart const struct usb_ctrlrequest *req) 5150316ca63SLaurent Pinchart { 5160316ca63SLaurent Pinchart struct isp1760_ep *ep; 5170316ca63SLaurent Pinchart u16 status; 5180316ca63SLaurent Pinchart 5190316ca63SLaurent Pinchart if (req->wLength != cpu_to_le16(2) || req->wValue != cpu_to_le16(0)) 5200316ca63SLaurent Pinchart return -EINVAL; 5210316ca63SLaurent Pinchart 5220316ca63SLaurent Pinchart switch (req->bRequestType) { 5230316ca63SLaurent Pinchart case USB_DIR_IN | USB_RECIP_DEVICE: 5240316ca63SLaurent Pinchart status = udc->devstatus; 5250316ca63SLaurent Pinchart break; 5260316ca63SLaurent Pinchart 5270316ca63SLaurent Pinchart case USB_DIR_IN | USB_RECIP_INTERFACE: 5280316ca63SLaurent Pinchart status = 0; 5290316ca63SLaurent Pinchart break; 5300316ca63SLaurent Pinchart 5310316ca63SLaurent Pinchart case USB_DIR_IN | USB_RECIP_ENDPOINT: 5320316ca63SLaurent Pinchart ep = isp1760_udc_find_ep(udc, le16_to_cpu(req->wIndex)); 5330316ca63SLaurent Pinchart if (!ep) 5340316ca63SLaurent Pinchart return -EINVAL; 5350316ca63SLaurent Pinchart 5360316ca63SLaurent Pinchart status = 0; 5370316ca63SLaurent Pinchart if (ep->halted) 5380316ca63SLaurent Pinchart status |= 1 << USB_ENDPOINT_HALT; 5390316ca63SLaurent Pinchart break; 5400316ca63SLaurent Pinchart 5410316ca63SLaurent Pinchart default: 5420316ca63SLaurent Pinchart return -EINVAL; 5430316ca63SLaurent Pinchart } 5440316ca63SLaurent Pinchart 5451da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_EPDIR); 5461da9e1c0SRui Miguel Silva isp1760_udc_write(udc, DC_ENDPIDX, 1); 5471da9e1c0SRui Miguel Silva 5480316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_BUFLEN, 2); 5490316ca63SLaurent Pinchart 5501da9e1c0SRui Miguel Silva isp1760_udc_write_raw16(udc, ISP176x_DC_DATAPORT, status); 5510316ca63SLaurent Pinchart 5521da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_DSEN); 5530316ca63SLaurent Pinchart 5540316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status); 5550316ca63SLaurent Pinchart 5560316ca63SLaurent Pinchart return 0; 5570316ca63SLaurent Pinchart } 5580316ca63SLaurent Pinchart 5590316ca63SLaurent Pinchart static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr) 5600316ca63SLaurent Pinchart { 5610316ca63SLaurent Pinchart if (addr > 127) { 5620316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "invalid device address %u\n", addr); 5630316ca63SLaurent Pinchart return -EINVAL; 5640316ca63SLaurent Pinchart } 5650316ca63SLaurent Pinchart 5660316ca63SLaurent Pinchart if (udc->gadget.state != USB_STATE_DEFAULT && 5670316ca63SLaurent Pinchart udc->gadget.state != USB_STATE_ADDRESS) { 5680316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "can't set address in state %u\n", 5690316ca63SLaurent Pinchart udc->gadget.state); 5700316ca63SLaurent Pinchart return -EINVAL; 5710316ca63SLaurent Pinchart } 5720316ca63SLaurent Pinchart 5730316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS : 5740316ca63SLaurent Pinchart USB_STATE_DEFAULT); 5750316ca63SLaurent Pinchart 5761da9e1c0SRui Miguel Silva isp1760_udc_write(udc, DC_DEVADDR, addr); 5771da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_DEVEN); 5780316ca63SLaurent Pinchart 5790316ca63SLaurent Pinchart spin_lock(&udc->lock); 5800316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT); 5810316ca63SLaurent Pinchart spin_unlock(&udc->lock); 5820316ca63SLaurent Pinchart 5830316ca63SLaurent Pinchart return 0; 5840316ca63SLaurent Pinchart } 5850316ca63SLaurent Pinchart 5860316ca63SLaurent Pinchart static bool isp1760_ep0_setup_standard(struct isp1760_udc *udc, 5870316ca63SLaurent Pinchart struct usb_ctrlrequest *req) 5880316ca63SLaurent Pinchart { 5890316ca63SLaurent Pinchart bool stall; 5900316ca63SLaurent Pinchart 5910316ca63SLaurent Pinchart switch (req->bRequest) { 5920316ca63SLaurent Pinchart case USB_REQ_GET_STATUS: 5930316ca63SLaurent Pinchart return isp1760_udc_get_status(udc, req); 5940316ca63SLaurent Pinchart 5950316ca63SLaurent Pinchart case USB_REQ_CLEAR_FEATURE: 5960316ca63SLaurent Pinchart switch (req->bRequestType) { 5970316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_DEVICE: { 5980316ca63SLaurent Pinchart /* TODO: Handle remote wakeup feature. */ 5990316ca63SLaurent Pinchart return true; 6000316ca63SLaurent Pinchart } 6010316ca63SLaurent Pinchart 6020316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_ENDPOINT: { 6030316ca63SLaurent Pinchart u16 index = le16_to_cpu(req->wIndex); 6040316ca63SLaurent Pinchart struct isp1760_ep *ep; 6050316ca63SLaurent Pinchart 6060316ca63SLaurent Pinchart if (req->wLength != cpu_to_le16(0) || 6070316ca63SLaurent Pinchart req->wValue != cpu_to_le16(USB_ENDPOINT_HALT)) 6080316ca63SLaurent Pinchart return true; 6090316ca63SLaurent Pinchart 6100316ca63SLaurent Pinchart ep = isp1760_udc_find_ep(udc, index); 6110316ca63SLaurent Pinchart if (!ep) 6120316ca63SLaurent Pinchart return true; 6130316ca63SLaurent Pinchart 6140316ca63SLaurent Pinchart spin_lock(&udc->lock); 6150316ca63SLaurent Pinchart 6160316ca63SLaurent Pinchart /* 6170316ca63SLaurent Pinchart * If the endpoint is wedged only the gadget can clear 6180316ca63SLaurent Pinchart * the halt feature. Pretend success in that case, but 6190316ca63SLaurent Pinchart * keep the endpoint halted. 6200316ca63SLaurent Pinchart */ 6210316ca63SLaurent Pinchart if (!ep->wedged) 6220316ca63SLaurent Pinchart stall = __isp1760_udc_set_halt(ep, false); 6230316ca63SLaurent Pinchart else 6240316ca63SLaurent Pinchart stall = false; 6250316ca63SLaurent Pinchart 6260316ca63SLaurent Pinchart if (!stall) 6270316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(&udc->ep[0], 6280316ca63SLaurent Pinchart USB_DIR_OUT); 6290316ca63SLaurent Pinchart 6300316ca63SLaurent Pinchart spin_unlock(&udc->lock); 6310316ca63SLaurent Pinchart return stall; 6320316ca63SLaurent Pinchart } 6330316ca63SLaurent Pinchart 6340316ca63SLaurent Pinchart default: 6350316ca63SLaurent Pinchart return true; 6360316ca63SLaurent Pinchart } 6370316ca63SLaurent Pinchart break; 6380316ca63SLaurent Pinchart 6390316ca63SLaurent Pinchart case USB_REQ_SET_FEATURE: 6400316ca63SLaurent Pinchart switch (req->bRequestType) { 6410316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_DEVICE: { 6420316ca63SLaurent Pinchart /* TODO: Handle remote wakeup and test mode features */ 6430316ca63SLaurent Pinchart return true; 6440316ca63SLaurent Pinchart } 6450316ca63SLaurent Pinchart 6460316ca63SLaurent Pinchart case USB_DIR_OUT | USB_RECIP_ENDPOINT: { 6470316ca63SLaurent Pinchart u16 index = le16_to_cpu(req->wIndex); 6480316ca63SLaurent Pinchart struct isp1760_ep *ep; 6490316ca63SLaurent Pinchart 6500316ca63SLaurent Pinchart if (req->wLength != cpu_to_le16(0) || 6510316ca63SLaurent Pinchart req->wValue != cpu_to_le16(USB_ENDPOINT_HALT)) 6520316ca63SLaurent Pinchart return true; 6530316ca63SLaurent Pinchart 6540316ca63SLaurent Pinchart ep = isp1760_udc_find_ep(udc, index); 6550316ca63SLaurent Pinchart if (!ep) 6560316ca63SLaurent Pinchart return true; 6570316ca63SLaurent Pinchart 6580316ca63SLaurent Pinchart spin_lock(&udc->lock); 6590316ca63SLaurent Pinchart 6600316ca63SLaurent Pinchart stall = __isp1760_udc_set_halt(ep, true); 6610316ca63SLaurent Pinchart if (!stall) 6620316ca63SLaurent Pinchart isp1760_udc_ctrl_send_status(&udc->ep[0], 6630316ca63SLaurent Pinchart USB_DIR_OUT); 6640316ca63SLaurent Pinchart 6650316ca63SLaurent Pinchart spin_unlock(&udc->lock); 6660316ca63SLaurent Pinchart return stall; 6670316ca63SLaurent Pinchart } 6680316ca63SLaurent Pinchart 6690316ca63SLaurent Pinchart default: 6700316ca63SLaurent Pinchart return true; 6710316ca63SLaurent Pinchart } 6720316ca63SLaurent Pinchart break; 6730316ca63SLaurent Pinchart 6740316ca63SLaurent Pinchart case USB_REQ_SET_ADDRESS: 6750316ca63SLaurent Pinchart if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) 6760316ca63SLaurent Pinchart return true; 6770316ca63SLaurent Pinchart 6780316ca63SLaurent Pinchart return isp1760_udc_set_address(udc, le16_to_cpu(req->wValue)); 6790316ca63SLaurent Pinchart 6800316ca63SLaurent Pinchart case USB_REQ_SET_CONFIGURATION: 6810316ca63SLaurent Pinchart if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) 6820316ca63SLaurent Pinchart return true; 6830316ca63SLaurent Pinchart 6840316ca63SLaurent Pinchart if (udc->gadget.state != USB_STATE_ADDRESS && 6850316ca63SLaurent Pinchart udc->gadget.state != USB_STATE_CONFIGURED) 6860316ca63SLaurent Pinchart return true; 6870316ca63SLaurent Pinchart 6880316ca63SLaurent Pinchart stall = udc->driver->setup(&udc->gadget, req) < 0; 6890316ca63SLaurent Pinchart if (stall) 6900316ca63SLaurent Pinchart return true; 6910316ca63SLaurent Pinchart 6920316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, req->wValue ? 6930316ca63SLaurent Pinchart USB_STATE_CONFIGURED : USB_STATE_ADDRESS); 6940316ca63SLaurent Pinchart 6950316ca63SLaurent Pinchart /* 6960316ca63SLaurent Pinchart * SET_CONFIGURATION (and SET_INTERFACE) must reset the halt 6970316ca63SLaurent Pinchart * feature on all endpoints. There is however no need to do so 6980316ca63SLaurent Pinchart * explicitly here as the gadget driver will disable and 6990316ca63SLaurent Pinchart * reenable endpoints, clearing the halt feature. 7000316ca63SLaurent Pinchart */ 7010316ca63SLaurent Pinchart return false; 7020316ca63SLaurent Pinchart 7030316ca63SLaurent Pinchart default: 7040316ca63SLaurent Pinchart return udc->driver->setup(&udc->gadget, req) < 0; 7050316ca63SLaurent Pinchart } 7060316ca63SLaurent Pinchart } 7070316ca63SLaurent Pinchart 7080316ca63SLaurent Pinchart static void isp1760_ep0_setup(struct isp1760_udc *udc) 7090316ca63SLaurent Pinchart { 7100316ca63SLaurent Pinchart union { 7110316ca63SLaurent Pinchart struct usb_ctrlrequest r; 7120316ca63SLaurent Pinchart u32 data[2]; 7130316ca63SLaurent Pinchart } req; 7140316ca63SLaurent Pinchart unsigned int count; 7150316ca63SLaurent Pinchart bool stall = false; 7160316ca63SLaurent Pinchart 7170316ca63SLaurent Pinchart spin_lock(&udc->lock); 7180316ca63SLaurent Pinchart 7191da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_EP0SETUP); 7200316ca63SLaurent Pinchart 7211da9e1c0SRui Miguel Silva count = isp1760_udc_read(udc, DC_BUFLEN); 7220316ca63SLaurent Pinchart if (count != sizeof(req)) { 7230316ca63SLaurent Pinchart spin_unlock(&udc->lock); 7240316ca63SLaurent Pinchart 7250316ca63SLaurent Pinchart dev_err(udc->isp->dev, "invalid length %u for setup packet\n", 7260316ca63SLaurent Pinchart count); 7270316ca63SLaurent Pinchart 7280316ca63SLaurent Pinchart isp1760_udc_ctrl_send_stall(&udc->ep[0]); 7290316ca63SLaurent Pinchart return; 7300316ca63SLaurent Pinchart } 7310316ca63SLaurent Pinchart 7321da9e1c0SRui Miguel Silva req.data[0] = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT); 7331da9e1c0SRui Miguel Silva req.data[1] = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT); 7340316ca63SLaurent Pinchart 7350316ca63SLaurent Pinchart if (udc->ep0_state != ISP1760_CTRL_SETUP) { 7360316ca63SLaurent Pinchart spin_unlock(&udc->lock); 7370316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "unexpected SETUP packet\n"); 7380316ca63SLaurent Pinchart return; 7390316ca63SLaurent Pinchart } 7400316ca63SLaurent Pinchart 7410316ca63SLaurent Pinchart /* Move to the data stage. */ 7420316ca63SLaurent Pinchart if (!req.r.wLength) 7430316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_STATUS; 7440316ca63SLaurent Pinchart else if (req.r.bRequestType & USB_DIR_IN) 7450316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_DATA_IN; 7460316ca63SLaurent Pinchart else 7470316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_DATA_OUT; 7480316ca63SLaurent Pinchart 7490316ca63SLaurent Pinchart udc->ep0_dir = req.r.bRequestType & USB_DIR_IN; 7500316ca63SLaurent Pinchart udc->ep0_length = le16_to_cpu(req.r.wLength); 7510316ca63SLaurent Pinchart 7520316ca63SLaurent Pinchart spin_unlock(&udc->lock); 7530316ca63SLaurent Pinchart 7540316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 7550316ca63SLaurent Pinchart "%s: bRequestType 0x%02x bRequest 0x%02x wValue 0x%04x wIndex 0x%04x wLength 0x%04x\n", 7560316ca63SLaurent Pinchart __func__, req.r.bRequestType, req.r.bRequest, 7570316ca63SLaurent Pinchart le16_to_cpu(req.r.wValue), le16_to_cpu(req.r.wIndex), 7580316ca63SLaurent Pinchart le16_to_cpu(req.r.wLength)); 7590316ca63SLaurent Pinchart 7600316ca63SLaurent Pinchart if ((req.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 7610316ca63SLaurent Pinchart stall = isp1760_ep0_setup_standard(udc, &req.r); 7620316ca63SLaurent Pinchart else 7630316ca63SLaurent Pinchart stall = udc->driver->setup(&udc->gadget, &req.r) < 0; 7640316ca63SLaurent Pinchart 7650316ca63SLaurent Pinchart if (stall) 7660316ca63SLaurent Pinchart isp1760_udc_ctrl_send_stall(&udc->ep[0]); 7670316ca63SLaurent Pinchart } 7680316ca63SLaurent Pinchart 7690316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 7700316ca63SLaurent Pinchart * Gadget Endpoint Operations 7710316ca63SLaurent Pinchart */ 7720316ca63SLaurent Pinchart 7730316ca63SLaurent Pinchart static int isp1760_ep_enable(struct usb_ep *ep, 7740316ca63SLaurent Pinchart const struct usb_endpoint_descriptor *desc) 7750316ca63SLaurent Pinchart { 7760316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 7770316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 7780316ca63SLaurent Pinchart unsigned long flags; 7790316ca63SLaurent Pinchart unsigned int type; 7800316ca63SLaurent Pinchart 7810316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s\n", __func__); 7820316ca63SLaurent Pinchart 7830316ca63SLaurent Pinchart /* 7840316ca63SLaurent Pinchart * Validate the descriptor. The control endpoint can't be enabled 7850316ca63SLaurent Pinchart * manually. 7860316ca63SLaurent Pinchart */ 7870316ca63SLaurent Pinchart if (desc->bDescriptorType != USB_DT_ENDPOINT || 7880316ca63SLaurent Pinchart desc->bEndpointAddress == 0 || 7890316ca63SLaurent Pinchart desc->bEndpointAddress != uep->addr || 7900316ca63SLaurent Pinchart le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket) { 7910316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 7920316ca63SLaurent Pinchart "%s: invalid descriptor type %u addr %02x ep addr %02x max packet size %u/%u\n", 7930316ca63SLaurent Pinchart __func__, desc->bDescriptorType, 7940316ca63SLaurent Pinchart desc->bEndpointAddress, uep->addr, 7950316ca63SLaurent Pinchart le16_to_cpu(desc->wMaxPacketSize), ep->maxpacket); 7960316ca63SLaurent Pinchart return -EINVAL; 7970316ca63SLaurent Pinchart } 7980316ca63SLaurent Pinchart 7990316ca63SLaurent Pinchart switch (usb_endpoint_type(desc)) { 8000316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_ISOC: 8011da9e1c0SRui Miguel Silva type = ISP176x_DC_ENDPTYP_ISOC; 8020316ca63SLaurent Pinchart break; 8030316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_BULK: 8041da9e1c0SRui Miguel Silva type = ISP176x_DC_ENDPTYP_BULK; 8050316ca63SLaurent Pinchart break; 8060316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_INT: 8071da9e1c0SRui Miguel Silva type = ISP176x_DC_ENDPTYP_INTERRUPT; 8080316ca63SLaurent Pinchart break; 8090316ca63SLaurent Pinchart case USB_ENDPOINT_XFER_CONTROL: 8100316ca63SLaurent Pinchart default: 8110316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: control endpoints unsupported\n", 8120316ca63SLaurent Pinchart __func__); 8130316ca63SLaurent Pinchart return -EINVAL; 8140316ca63SLaurent Pinchart } 8150316ca63SLaurent Pinchart 8160316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 8170316ca63SLaurent Pinchart 8180316ca63SLaurent Pinchart uep->desc = desc; 8190316ca63SLaurent Pinchart uep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); 8200316ca63SLaurent Pinchart uep->rx_pending = false; 8210316ca63SLaurent Pinchart uep->halted = false; 8220316ca63SLaurent Pinchart uep->wedged = false; 8230316ca63SLaurent Pinchart 8241da9e1c0SRui Miguel Silva isp1760_udc_select_ep(udc, uep); 8251da9e1c0SRui Miguel Silva 8261da9e1c0SRui Miguel Silva isp1760_udc_write(udc, DC_FFOSZ, uep->maxpacket); 8270316ca63SLaurent Pinchart isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket); 8281da9e1c0SRui Miguel Silva 8291da9e1c0SRui Miguel Silva isp1760_udc_write(udc, DC_ENDPTYP, type); 8301da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_EPENABLE); 8310316ca63SLaurent Pinchart 8320316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 8330316ca63SLaurent Pinchart 8340316ca63SLaurent Pinchart return 0; 8350316ca63SLaurent Pinchart } 8360316ca63SLaurent Pinchart 8370316ca63SLaurent Pinchart static int isp1760_ep_disable(struct usb_ep *ep) 8380316ca63SLaurent Pinchart { 8390316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 8400316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 8410316ca63SLaurent Pinchart struct isp1760_request *req, *nreq; 8420316ca63SLaurent Pinchart LIST_HEAD(req_list); 8430316ca63SLaurent Pinchart unsigned long flags; 8440316ca63SLaurent Pinchart 8450316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s\n", __func__); 8460316ca63SLaurent Pinchart 8470316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 8480316ca63SLaurent Pinchart 8490316ca63SLaurent Pinchart if (!uep->desc) { 8500316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: endpoint not enabled\n", __func__); 8510316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 8520316ca63SLaurent Pinchart return -EINVAL; 8530316ca63SLaurent Pinchart } 8540316ca63SLaurent Pinchart 8550316ca63SLaurent Pinchart uep->desc = NULL; 8560316ca63SLaurent Pinchart uep->maxpacket = 0; 8570316ca63SLaurent Pinchart 8581da9e1c0SRui Miguel Silva isp1760_udc_select_ep(udc, uep); 8591da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_EPENABLE); 8601da9e1c0SRui Miguel Silva isp1760_udc_clear(udc, DC_ENDPTYP); 8610316ca63SLaurent Pinchart 8620316ca63SLaurent Pinchart /* TODO Synchronize with the IRQ handler */ 8630316ca63SLaurent Pinchart 8640316ca63SLaurent Pinchart list_splice_init(&uep->queue, &req_list); 8650316ca63SLaurent Pinchart 8660316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 8670316ca63SLaurent Pinchart 8680316ca63SLaurent Pinchart list_for_each_entry_safe(req, nreq, &req_list, queue) { 8690316ca63SLaurent Pinchart list_del(&req->queue); 8700316ca63SLaurent Pinchart isp1760_udc_request_complete(uep, req, -ESHUTDOWN); 8710316ca63SLaurent Pinchart } 8720316ca63SLaurent Pinchart 8730316ca63SLaurent Pinchart return 0; 8740316ca63SLaurent Pinchart } 8750316ca63SLaurent Pinchart 8760316ca63SLaurent Pinchart static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep, 8770316ca63SLaurent Pinchart gfp_t gfp_flags) 8780316ca63SLaurent Pinchart { 8790316ca63SLaurent Pinchart struct isp1760_request *req; 8800316ca63SLaurent Pinchart 8810316ca63SLaurent Pinchart req = kzalloc(sizeof(*req), gfp_flags); 882e3e64f3fSColin Ian King if (!req) 883e3e64f3fSColin Ian King return NULL; 8840316ca63SLaurent Pinchart 8850316ca63SLaurent Pinchart return &req->req; 8860316ca63SLaurent Pinchart } 8870316ca63SLaurent Pinchart 8880316ca63SLaurent Pinchart static void isp1760_ep_free_request(struct usb_ep *ep, struct usb_request *_req) 8890316ca63SLaurent Pinchart { 8900316ca63SLaurent Pinchart struct isp1760_request *req = req_to_udc_req(_req); 8910316ca63SLaurent Pinchart 8920316ca63SLaurent Pinchart kfree(req); 8930316ca63SLaurent Pinchart } 8940316ca63SLaurent Pinchart 8950316ca63SLaurent Pinchart static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req, 8960316ca63SLaurent Pinchart gfp_t gfp_flags) 8970316ca63SLaurent Pinchart { 8980316ca63SLaurent Pinchart struct isp1760_request *req = req_to_udc_req(_req); 8990316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 9000316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 9010316ca63SLaurent Pinchart bool complete = false; 9020316ca63SLaurent Pinchart unsigned long flags; 9030316ca63SLaurent Pinchart int ret = 0; 9040316ca63SLaurent Pinchart 9050316ca63SLaurent Pinchart _req->status = -EINPROGRESS; 9060316ca63SLaurent Pinchart _req->actual = 0; 9070316ca63SLaurent Pinchart 9080316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 9090316ca63SLaurent Pinchart 9100316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 9110316ca63SLaurent Pinchart "%s: req %p (%u bytes%s) ep %p(0x%02x)\n", __func__, _req, 9120316ca63SLaurent Pinchart _req->length, _req->zero ? " (zlp)" : "", uep, uep->addr); 9130316ca63SLaurent Pinchart 9140316ca63SLaurent Pinchart req->ep = uep; 9150316ca63SLaurent Pinchart 9160316ca63SLaurent Pinchart if (uep->addr == 0) { 9170316ca63SLaurent Pinchart if (_req->length != udc->ep0_length && 9180316ca63SLaurent Pinchart udc->ep0_state != ISP1760_CTRL_DATA_IN) { 9190316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 9200316ca63SLaurent Pinchart "%s: invalid length %u for req %p\n", 9210316ca63SLaurent Pinchart __func__, _req->length, req); 9220316ca63SLaurent Pinchart ret = -EINVAL; 9230316ca63SLaurent Pinchart goto done; 9240316ca63SLaurent Pinchart } 9250316ca63SLaurent Pinchart 9260316ca63SLaurent Pinchart switch (udc->ep0_state) { 9270316ca63SLaurent Pinchart case ISP1760_CTRL_DATA_IN: 9280316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: transmitting req %p\n", 9290316ca63SLaurent Pinchart __func__, req); 9300316ca63SLaurent Pinchart 9310316ca63SLaurent Pinchart list_add_tail(&req->queue, &uep->queue); 9320316ca63SLaurent Pinchart isp1760_udc_transmit(uep, req); 9330316ca63SLaurent Pinchart break; 9340316ca63SLaurent Pinchart 9350316ca63SLaurent Pinchart case ISP1760_CTRL_DATA_OUT: 9360316ca63SLaurent Pinchart list_add_tail(&req->queue, &uep->queue); 9371da9e1c0SRui Miguel Silva __isp1760_udc_select_ep(udc, uep, USB_DIR_OUT); 9381da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_DSEN); 9390316ca63SLaurent Pinchart break; 9400316ca63SLaurent Pinchart 9410316ca63SLaurent Pinchart case ISP1760_CTRL_STATUS: 9420316ca63SLaurent Pinchart complete = true; 9430316ca63SLaurent Pinchart break; 9440316ca63SLaurent Pinchart 9450316ca63SLaurent Pinchart default: 9460316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: invalid ep0 state\n", 9470316ca63SLaurent Pinchart __func__); 9480316ca63SLaurent Pinchart ret = -EINVAL; 9490316ca63SLaurent Pinchart break; 9500316ca63SLaurent Pinchart } 9510316ca63SLaurent Pinchart } else if (uep->desc) { 9520316ca63SLaurent Pinchart bool empty = list_empty(&uep->queue); 9530316ca63SLaurent Pinchart 9540316ca63SLaurent Pinchart list_add_tail(&req->queue, &uep->queue); 9550316ca63SLaurent Pinchart if ((uep->addr & USB_DIR_IN) && !uep->halted && empty) 9560316ca63SLaurent Pinchart isp1760_udc_transmit(uep, req); 9570316ca63SLaurent Pinchart else if (!(uep->addr & USB_DIR_IN) && uep->rx_pending) 9580316ca63SLaurent Pinchart complete = isp1760_udc_receive(uep, req); 9590316ca63SLaurent Pinchart } else { 9600316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 9610316ca63SLaurent Pinchart "%s: can't queue request to disabled ep%02x\n", 9620316ca63SLaurent Pinchart __func__, uep->addr); 9630316ca63SLaurent Pinchart ret = -ESHUTDOWN; 9640316ca63SLaurent Pinchart } 9650316ca63SLaurent Pinchart 9660316ca63SLaurent Pinchart done: 9670316ca63SLaurent Pinchart if (ret < 0) 9680316ca63SLaurent Pinchart req->ep = NULL; 9690316ca63SLaurent Pinchart 9700316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 9710316ca63SLaurent Pinchart 9720316ca63SLaurent Pinchart if (complete) 9730316ca63SLaurent Pinchart isp1760_udc_request_complete(uep, req, 0); 9740316ca63SLaurent Pinchart 9750316ca63SLaurent Pinchart return ret; 9760316ca63SLaurent Pinchart } 9770316ca63SLaurent Pinchart 9780316ca63SLaurent Pinchart static int isp1760_ep_dequeue(struct usb_ep *ep, struct usb_request *_req) 9790316ca63SLaurent Pinchart { 9800316ca63SLaurent Pinchart struct isp1760_request *req = req_to_udc_req(_req); 9810316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 9820316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 9830316ca63SLaurent Pinchart unsigned long flags; 9840316ca63SLaurent Pinchart 9850316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s(ep%02x)\n", __func__, uep->addr); 9860316ca63SLaurent Pinchart 9870316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 9880316ca63SLaurent Pinchart 9890316ca63SLaurent Pinchart if (req->ep != uep) 9900316ca63SLaurent Pinchart req = NULL; 9910316ca63SLaurent Pinchart else 9920316ca63SLaurent Pinchart list_del(&req->queue); 9930316ca63SLaurent Pinchart 9940316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 9950316ca63SLaurent Pinchart 9960316ca63SLaurent Pinchart if (!req) 9970316ca63SLaurent Pinchart return -EINVAL; 9980316ca63SLaurent Pinchart 9990316ca63SLaurent Pinchart isp1760_udc_request_complete(uep, req, -ECONNRESET); 10000316ca63SLaurent Pinchart return 0; 10010316ca63SLaurent Pinchart } 10020316ca63SLaurent Pinchart 10030316ca63SLaurent Pinchart static int __isp1760_ep_set_halt(struct isp1760_ep *uep, bool stall, bool wedge) 10040316ca63SLaurent Pinchart { 10050316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 10060316ca63SLaurent Pinchart int ret; 10070316ca63SLaurent Pinchart 10080316ca63SLaurent Pinchart if (!uep->addr) { 10090316ca63SLaurent Pinchart /* 10100316ca63SLaurent Pinchart * Halting the control endpoint is only valid as a delayed error 10110316ca63SLaurent Pinchart * response to a SETUP packet. Make sure EP0 is in the right 10120316ca63SLaurent Pinchart * stage and that the gadget isn't trying to clear the halt 10130316ca63SLaurent Pinchart * condition. 10140316ca63SLaurent Pinchart */ 10150316ca63SLaurent Pinchart if (WARN_ON(udc->ep0_state == ISP1760_CTRL_SETUP || !stall || 10160316ca63SLaurent Pinchart wedge)) { 10170316ca63SLaurent Pinchart return -EINVAL; 10180316ca63SLaurent Pinchart } 10190316ca63SLaurent Pinchart } 10200316ca63SLaurent Pinchart 10210316ca63SLaurent Pinchart if (uep->addr && !uep->desc) { 10220316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__, 10230316ca63SLaurent Pinchart uep->addr); 10240316ca63SLaurent Pinchart return -EINVAL; 10250316ca63SLaurent Pinchart } 10260316ca63SLaurent Pinchart 10270316ca63SLaurent Pinchart if (uep->addr & USB_DIR_IN) { 10280316ca63SLaurent Pinchart /* Refuse to halt IN endpoints with active transfers. */ 10290316ca63SLaurent Pinchart if (!list_empty(&uep->queue)) { 10300316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, 10310316ca63SLaurent Pinchart "%s: ep%02x has request pending\n", __func__, 10320316ca63SLaurent Pinchart uep->addr); 10330316ca63SLaurent Pinchart return -EAGAIN; 10340316ca63SLaurent Pinchart } 10350316ca63SLaurent Pinchart } 10360316ca63SLaurent Pinchart 10370316ca63SLaurent Pinchart ret = __isp1760_udc_set_halt(uep, stall); 10380316ca63SLaurent Pinchart if (ret < 0) 10390316ca63SLaurent Pinchart return ret; 10400316ca63SLaurent Pinchart 10410316ca63SLaurent Pinchart if (!uep->addr) { 10420316ca63SLaurent Pinchart /* 10430316ca63SLaurent Pinchart * Stalling EP0 completes the control transaction, move back to 10440316ca63SLaurent Pinchart * the SETUP state. 10450316ca63SLaurent Pinchart */ 10460316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 10470316ca63SLaurent Pinchart return 0; 10480316ca63SLaurent Pinchart } 10490316ca63SLaurent Pinchart 10500316ca63SLaurent Pinchart if (wedge) 10510316ca63SLaurent Pinchart uep->wedged = true; 10520316ca63SLaurent Pinchart else if (!stall) 10530316ca63SLaurent Pinchart uep->wedged = false; 10540316ca63SLaurent Pinchart 10550316ca63SLaurent Pinchart return 0; 10560316ca63SLaurent Pinchart } 10570316ca63SLaurent Pinchart 10580316ca63SLaurent Pinchart static int isp1760_ep_set_halt(struct usb_ep *ep, int value) 10590316ca63SLaurent Pinchart { 10600316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 10610316ca63SLaurent Pinchart unsigned long flags; 10620316ca63SLaurent Pinchart int ret; 10630316ca63SLaurent Pinchart 10640316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s: %s halt on ep%02x\n", __func__, 10650316ca63SLaurent Pinchart value ? "set" : "clear", uep->addr); 10660316ca63SLaurent Pinchart 10670316ca63SLaurent Pinchart spin_lock_irqsave(&uep->udc->lock, flags); 10680316ca63SLaurent Pinchart ret = __isp1760_ep_set_halt(uep, value, false); 10690316ca63SLaurent Pinchart spin_unlock_irqrestore(&uep->udc->lock, flags); 10700316ca63SLaurent Pinchart 10710316ca63SLaurent Pinchart return ret; 10720316ca63SLaurent Pinchart } 10730316ca63SLaurent Pinchart 10740316ca63SLaurent Pinchart static int isp1760_ep_set_wedge(struct usb_ep *ep) 10750316ca63SLaurent Pinchart { 10760316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 10770316ca63SLaurent Pinchart unsigned long flags; 10780316ca63SLaurent Pinchart int ret; 10790316ca63SLaurent Pinchart 10800316ca63SLaurent Pinchart dev_dbg(uep->udc->isp->dev, "%s: set wedge on ep%02x)\n", __func__, 10810316ca63SLaurent Pinchart uep->addr); 10820316ca63SLaurent Pinchart 10830316ca63SLaurent Pinchart spin_lock_irqsave(&uep->udc->lock, flags); 10840316ca63SLaurent Pinchart ret = __isp1760_ep_set_halt(uep, true, true); 10850316ca63SLaurent Pinchart spin_unlock_irqrestore(&uep->udc->lock, flags); 10860316ca63SLaurent Pinchart 10870316ca63SLaurent Pinchart return ret; 10880316ca63SLaurent Pinchart } 10890316ca63SLaurent Pinchart 10900316ca63SLaurent Pinchart static void isp1760_ep_fifo_flush(struct usb_ep *ep) 10910316ca63SLaurent Pinchart { 10920316ca63SLaurent Pinchart struct isp1760_ep *uep = ep_to_udc_ep(ep); 10930316ca63SLaurent Pinchart struct isp1760_udc *udc = uep->udc; 10940316ca63SLaurent Pinchart unsigned long flags; 10950316ca63SLaurent Pinchart 10960316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 10970316ca63SLaurent Pinchart 10981da9e1c0SRui Miguel Silva isp1760_udc_select_ep(udc, uep); 10990316ca63SLaurent Pinchart 11000316ca63SLaurent Pinchart /* 11010316ca63SLaurent Pinchart * Set the CLBUF bit twice to flush both buffers in case double 11020316ca63SLaurent Pinchart * buffering is enabled. 11030316ca63SLaurent Pinchart */ 11041da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_CLBUF); 11051da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_CLBUF); 11060316ca63SLaurent Pinchart 11070316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 11080316ca63SLaurent Pinchart } 11090316ca63SLaurent Pinchart 11100316ca63SLaurent Pinchart static const struct usb_ep_ops isp1760_ep_ops = { 11110316ca63SLaurent Pinchart .enable = isp1760_ep_enable, 11120316ca63SLaurent Pinchart .disable = isp1760_ep_disable, 11130316ca63SLaurent Pinchart .alloc_request = isp1760_ep_alloc_request, 11140316ca63SLaurent Pinchart .free_request = isp1760_ep_free_request, 11150316ca63SLaurent Pinchart .queue = isp1760_ep_queue, 11160316ca63SLaurent Pinchart .dequeue = isp1760_ep_dequeue, 11170316ca63SLaurent Pinchart .set_halt = isp1760_ep_set_halt, 11180316ca63SLaurent Pinchart .set_wedge = isp1760_ep_set_wedge, 11190316ca63SLaurent Pinchart .fifo_flush = isp1760_ep_fifo_flush, 11200316ca63SLaurent Pinchart }; 11210316ca63SLaurent Pinchart 11220316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 11230316ca63SLaurent Pinchart * Device States 11240316ca63SLaurent Pinchart */ 11250316ca63SLaurent Pinchart 11260316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 11270316ca63SLaurent Pinchart static void isp1760_udc_connect(struct isp1760_udc *udc) 11280316ca63SLaurent Pinchart { 11290316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED); 11300316ca63SLaurent Pinchart mod_timer(&udc->vbus_timer, jiffies + ISP1760_VBUS_POLL_INTERVAL); 11310316ca63SLaurent Pinchart } 11320316ca63SLaurent Pinchart 11330316ca63SLaurent Pinchart /* Called with the UDC spinlock held. */ 11340316ca63SLaurent Pinchart static void isp1760_udc_disconnect(struct isp1760_udc *udc) 11350316ca63SLaurent Pinchart { 11360316ca63SLaurent Pinchart if (udc->gadget.state < USB_STATE_POWERED) 11370316ca63SLaurent Pinchart return; 11380316ca63SLaurent Pinchart 11390316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "Device disconnected in state %u\n", 11400316ca63SLaurent Pinchart udc->gadget.state); 11410316ca63SLaurent Pinchart 11420316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_UNKNOWN; 11430316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); 11440316ca63SLaurent Pinchart 11450316ca63SLaurent Pinchart if (udc->driver->disconnect) 11460316ca63SLaurent Pinchart udc->driver->disconnect(&udc->gadget); 11470316ca63SLaurent Pinchart 11480316ca63SLaurent Pinchart del_timer(&udc->vbus_timer); 11490316ca63SLaurent Pinchart 11500316ca63SLaurent Pinchart /* TODO Reset all endpoints ? */ 11510316ca63SLaurent Pinchart } 11520316ca63SLaurent Pinchart 11530316ca63SLaurent Pinchart static void isp1760_udc_init_hw(struct isp1760_udc *udc) 11540316ca63SLaurent Pinchart { 1155d369c918SRui Miguel Silva u32 intconf = udc->is_isp1763 ? ISP1763_DC_INTCONF : ISP176x_DC_INTCONF; 1156d369c918SRui Miguel Silva u32 intena = udc->is_isp1763 ? ISP1763_DC_INTENABLE : 1157d369c918SRui Miguel Silva ISP176x_DC_INTENABLE; 1158d369c918SRui Miguel Silva 11590316ca63SLaurent Pinchart /* 11600316ca63SLaurent Pinchart * The device controller currently shares its interrupt with the host 11610316ca63SLaurent Pinchart * controller, the DC_IRQ polarity and signaling mode are ignored. Set 11620316ca63SLaurent Pinchart * the to active-low level-triggered. 11630316ca63SLaurent Pinchart * 11640316ca63SLaurent Pinchart * Configure the control, in and out pipes to generate interrupts on 11650316ca63SLaurent Pinchart * ACK tokens only (and NYET for the out pipe). The default 11660316ca63SLaurent Pinchart * configuration also generates an interrupt on the first NACK token. 11670316ca63SLaurent Pinchart */ 1168d369c918SRui Miguel Silva isp1760_reg_write(udc->regs, intconf, 11691da9e1c0SRui Miguel Silva ISP176x_DC_CDBGMOD_ACK | ISP176x_DC_DDBGMODIN_ACK | 11701da9e1c0SRui Miguel Silva ISP176x_DC_DDBGMODOUT_ACK); 11710316ca63SLaurent Pinchart 1172d369c918SRui Miguel Silva isp1760_reg_write(udc->regs, intena, DC_IEPRXTX(7) | 11731da9e1c0SRui Miguel Silva DC_IEPRXTX(6) | DC_IEPRXTX(5) | DC_IEPRXTX(4) | 11741da9e1c0SRui Miguel Silva DC_IEPRXTX(3) | DC_IEPRXTX(2) | DC_IEPRXTX(1) | 11751da9e1c0SRui Miguel Silva DC_IEPRXTX(0) | ISP176x_DC_IEP0SETUP | 11761da9e1c0SRui Miguel Silva ISP176x_DC_IEVBUS | ISP176x_DC_IERESM | 11771da9e1c0SRui Miguel Silva ISP176x_DC_IESUSP | ISP176x_DC_IEHS_STA | 11781da9e1c0SRui Miguel Silva ISP176x_DC_IEBRST); 11790316ca63SLaurent Pinchart 11800316ca63SLaurent Pinchart if (udc->connected) 11810316ca63SLaurent Pinchart isp1760_set_pullup(udc->isp, true); 11820316ca63SLaurent Pinchart 11831da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_DEVEN); 11840316ca63SLaurent Pinchart } 11850316ca63SLaurent Pinchart 11860316ca63SLaurent Pinchart static void isp1760_udc_reset(struct isp1760_udc *udc) 11870316ca63SLaurent Pinchart { 11880316ca63SLaurent Pinchart unsigned long flags; 11890316ca63SLaurent Pinchart 11900316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 11910316ca63SLaurent Pinchart 11920316ca63SLaurent Pinchart /* 11930316ca63SLaurent Pinchart * The bus reset has reset most registers to their default value, 11940316ca63SLaurent Pinchart * reinitialize the UDC hardware. 11950316ca63SLaurent Pinchart */ 11960316ca63SLaurent Pinchart isp1760_udc_init_hw(udc); 11970316ca63SLaurent Pinchart 11980316ca63SLaurent Pinchart udc->ep0_state = ISP1760_CTRL_SETUP; 11990316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_FULL; 12000316ca63SLaurent Pinchart 12010316ca63SLaurent Pinchart usb_gadget_udc_reset(&udc->gadget, udc->driver); 12020316ca63SLaurent Pinchart 12030316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 12040316ca63SLaurent Pinchart } 12050316ca63SLaurent Pinchart 12060316ca63SLaurent Pinchart static void isp1760_udc_suspend(struct isp1760_udc *udc) 12070316ca63SLaurent Pinchart { 12080316ca63SLaurent Pinchart if (udc->gadget.state < USB_STATE_DEFAULT) 12090316ca63SLaurent Pinchart return; 12100316ca63SLaurent Pinchart 12110316ca63SLaurent Pinchart if (udc->driver->suspend) 12120316ca63SLaurent Pinchart udc->driver->suspend(&udc->gadget); 12130316ca63SLaurent Pinchart } 12140316ca63SLaurent Pinchart 12150316ca63SLaurent Pinchart static void isp1760_udc_resume(struct isp1760_udc *udc) 12160316ca63SLaurent Pinchart { 12170316ca63SLaurent Pinchart if (udc->gadget.state < USB_STATE_DEFAULT) 12180316ca63SLaurent Pinchart return; 12190316ca63SLaurent Pinchart 12200316ca63SLaurent Pinchart if (udc->driver->resume) 12210316ca63SLaurent Pinchart udc->driver->resume(&udc->gadget); 12220316ca63SLaurent Pinchart } 12230316ca63SLaurent Pinchart 12240316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 12250316ca63SLaurent Pinchart * Gadget Operations 12260316ca63SLaurent Pinchart */ 12270316ca63SLaurent Pinchart 12280316ca63SLaurent Pinchart static int isp1760_udc_get_frame(struct usb_gadget *gadget) 12290316ca63SLaurent Pinchart { 12300316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 12310316ca63SLaurent Pinchart 12321da9e1c0SRui Miguel Silva return isp1760_udc_read(udc, DC_FRAMENUM); 12330316ca63SLaurent Pinchart } 12340316ca63SLaurent Pinchart 12350316ca63SLaurent Pinchart static int isp1760_udc_wakeup(struct usb_gadget *gadget) 12360316ca63SLaurent Pinchart { 12370316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 12380316ca63SLaurent Pinchart 12390316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s\n", __func__); 12400316ca63SLaurent Pinchart return -ENOTSUPP; 12410316ca63SLaurent Pinchart } 12420316ca63SLaurent Pinchart 12430316ca63SLaurent Pinchart static int isp1760_udc_set_selfpowered(struct usb_gadget *gadget, 12440316ca63SLaurent Pinchart int is_selfpowered) 12450316ca63SLaurent Pinchart { 12460316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 12470316ca63SLaurent Pinchart 12480316ca63SLaurent Pinchart if (is_selfpowered) 12490316ca63SLaurent Pinchart udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; 12500316ca63SLaurent Pinchart else 12510316ca63SLaurent Pinchart udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); 12520316ca63SLaurent Pinchart 12530316ca63SLaurent Pinchart return 0; 12540316ca63SLaurent Pinchart } 12550316ca63SLaurent Pinchart 12560316ca63SLaurent Pinchart static int isp1760_udc_pullup(struct usb_gadget *gadget, int is_on) 12570316ca63SLaurent Pinchart { 12580316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 12590316ca63SLaurent Pinchart 12600316ca63SLaurent Pinchart isp1760_set_pullup(udc->isp, is_on); 12610316ca63SLaurent Pinchart udc->connected = is_on; 12620316ca63SLaurent Pinchart 12630316ca63SLaurent Pinchart return 0; 12640316ca63SLaurent Pinchart } 12650316ca63SLaurent Pinchart 12660316ca63SLaurent Pinchart static int isp1760_udc_start(struct usb_gadget *gadget, 12670316ca63SLaurent Pinchart struct usb_gadget_driver *driver) 12680316ca63SLaurent Pinchart { 12690316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 127079852397SSudeep Holla unsigned long flags; 12710316ca63SLaurent Pinchart 12720316ca63SLaurent Pinchart /* The hardware doesn't support low speed. */ 12730316ca63SLaurent Pinchart if (driver->max_speed < USB_SPEED_FULL) { 12740316ca63SLaurent Pinchart dev_err(udc->isp->dev, "Invalid gadget driver\n"); 12750316ca63SLaurent Pinchart return -EINVAL; 12760316ca63SLaurent Pinchart } 12770316ca63SLaurent Pinchart 127879852397SSudeep Holla spin_lock_irqsave(&udc->lock, flags); 12790316ca63SLaurent Pinchart 12800316ca63SLaurent Pinchart if (udc->driver) { 12810316ca63SLaurent Pinchart dev_err(udc->isp->dev, "UDC already has a gadget driver\n"); 128265582a7fSSudeep Holla spin_unlock_irqrestore(&udc->lock, flags); 12830316ca63SLaurent Pinchart return -EBUSY; 12840316ca63SLaurent Pinchart } 12850316ca63SLaurent Pinchart 12860316ca63SLaurent Pinchart udc->driver = driver; 12870316ca63SLaurent Pinchart 128879852397SSudeep Holla spin_unlock_irqrestore(&udc->lock, flags); 12890316ca63SLaurent Pinchart 12900316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "starting UDC with driver %s\n", 12910316ca63SLaurent Pinchart driver->function); 12920316ca63SLaurent Pinchart 12930316ca63SLaurent Pinchart udc->devstatus = 0; 12940316ca63SLaurent Pinchart udc->connected = true; 12950316ca63SLaurent Pinchart 12960316ca63SLaurent Pinchart usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); 12970316ca63SLaurent Pinchart 12980316ca63SLaurent Pinchart /* DMA isn't supported yet, don't enable the DMA clock. */ 12991da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_GLINTENA); 13000316ca63SLaurent Pinchart 13010316ca63SLaurent Pinchart isp1760_udc_init_hw(udc); 13020316ca63SLaurent Pinchart 13030316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "UDC started with driver %s\n", 13040316ca63SLaurent Pinchart driver->function); 13050316ca63SLaurent Pinchart 13060316ca63SLaurent Pinchart return 0; 13070316ca63SLaurent Pinchart } 13080316ca63SLaurent Pinchart 13090316ca63SLaurent Pinchart static int isp1760_udc_stop(struct usb_gadget *gadget) 13100316ca63SLaurent Pinchart { 13110316ca63SLaurent Pinchart struct isp1760_udc *udc = gadget_to_udc(gadget); 1312d369c918SRui Miguel Silva u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE; 131379852397SSudeep Holla unsigned long flags; 13140316ca63SLaurent Pinchart 13150316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s\n", __func__); 13160316ca63SLaurent Pinchart 13170316ca63SLaurent Pinchart del_timer_sync(&udc->vbus_timer); 13180316ca63SLaurent Pinchart 1319d369c918SRui Miguel Silva isp1760_reg_write(udc->regs, mode_reg, 0); 13200316ca63SLaurent Pinchart 132179852397SSudeep Holla spin_lock_irqsave(&udc->lock, flags); 13220316ca63SLaurent Pinchart udc->driver = NULL; 132379852397SSudeep Holla spin_unlock_irqrestore(&udc->lock, flags); 13240316ca63SLaurent Pinchart 13250316ca63SLaurent Pinchart return 0; 13260316ca63SLaurent Pinchart } 13270316ca63SLaurent Pinchart 13283774e02cSBhumika Goyal static const struct usb_gadget_ops isp1760_udc_ops = { 13290316ca63SLaurent Pinchart .get_frame = isp1760_udc_get_frame, 13300316ca63SLaurent Pinchart .wakeup = isp1760_udc_wakeup, 13310316ca63SLaurent Pinchart .set_selfpowered = isp1760_udc_set_selfpowered, 13320316ca63SLaurent Pinchart .pullup = isp1760_udc_pullup, 13330316ca63SLaurent Pinchart .udc_start = isp1760_udc_start, 13340316ca63SLaurent Pinchart .udc_stop = isp1760_udc_stop, 13350316ca63SLaurent Pinchart }; 13360316ca63SLaurent Pinchart 13370316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 13380316ca63SLaurent Pinchart * Interrupt Handling 13390316ca63SLaurent Pinchart */ 13400316ca63SLaurent Pinchart 1341d369c918SRui Miguel Silva static u32 isp1760_udc_irq_get_status(struct isp1760_udc *udc) 1342d369c918SRui Miguel Silva { 1343d369c918SRui Miguel Silva u32 status; 1344d369c918SRui Miguel Silva 1345d369c918SRui Miguel Silva if (udc->is_isp1763) { 1346d369c918SRui Miguel Silva status = isp1760_reg_read(udc->regs, ISP1763_DC_INTERRUPT) 1347d369c918SRui Miguel Silva & isp1760_reg_read(udc->regs, ISP1763_DC_INTENABLE); 1348d369c918SRui Miguel Silva isp1760_reg_write(udc->regs, ISP1763_DC_INTERRUPT, status); 1349d369c918SRui Miguel Silva } else { 1350d369c918SRui Miguel Silva status = isp1760_reg_read(udc->regs, ISP176x_DC_INTERRUPT) 1351d369c918SRui Miguel Silva & isp1760_reg_read(udc->regs, ISP176x_DC_INTENABLE); 1352d369c918SRui Miguel Silva isp1760_reg_write(udc->regs, ISP176x_DC_INTERRUPT, status); 1353d369c918SRui Miguel Silva } 1354d369c918SRui Miguel Silva 1355d369c918SRui Miguel Silva return status; 1356d369c918SRui Miguel Silva } 1357d369c918SRui Miguel Silva 13580316ca63SLaurent Pinchart static irqreturn_t isp1760_udc_irq(int irq, void *dev) 13590316ca63SLaurent Pinchart { 13600316ca63SLaurent Pinchart struct isp1760_udc *udc = dev; 13610316ca63SLaurent Pinchart unsigned int i; 13620316ca63SLaurent Pinchart u32 status; 13630316ca63SLaurent Pinchart 1364d369c918SRui Miguel Silva status = isp1760_udc_irq_get_status(udc); 13650316ca63SLaurent Pinchart 1366*955d0fb5SRui Miguel Silva if (status & ISP176x_DC_IEVBUS) { 13670316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__); 13680316ca63SLaurent Pinchart /* The VBUS interrupt is only triggered when VBUS appears. */ 13690316ca63SLaurent Pinchart spin_lock(&udc->lock); 13700316ca63SLaurent Pinchart isp1760_udc_connect(udc); 13710316ca63SLaurent Pinchart spin_unlock(&udc->lock); 13720316ca63SLaurent Pinchart } 13730316ca63SLaurent Pinchart 1374*955d0fb5SRui Miguel Silva if (status & ISP176x_DC_IEBRST) { 13750316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__); 13760316ca63SLaurent Pinchart 13770316ca63SLaurent Pinchart isp1760_udc_reset(udc); 13780316ca63SLaurent Pinchart } 13790316ca63SLaurent Pinchart 13800316ca63SLaurent Pinchart for (i = 0; i <= 7; ++i) { 13810316ca63SLaurent Pinchart struct isp1760_ep *ep = &udc->ep[i*2]; 13820316ca63SLaurent Pinchart 13830316ca63SLaurent Pinchart if (status & DC_IEPTX(i)) { 13840316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(EPTX%u)\n", __func__, i); 13850316ca63SLaurent Pinchart isp1760_ep_tx_complete(ep); 13860316ca63SLaurent Pinchart } 13870316ca63SLaurent Pinchart 13880316ca63SLaurent Pinchart if (status & DC_IEPRX(i)) { 13890316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(EPRX%u)\n", __func__, i); 13900316ca63SLaurent Pinchart isp1760_ep_rx_ready(i ? ep - 1 : ep); 13910316ca63SLaurent Pinchart } 13920316ca63SLaurent Pinchart } 13930316ca63SLaurent Pinchart 1394*955d0fb5SRui Miguel Silva if (status & ISP176x_DC_IEP0SETUP) { 13950316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__); 13960316ca63SLaurent Pinchart 13970316ca63SLaurent Pinchart isp1760_ep0_setup(udc); 13980316ca63SLaurent Pinchart } 13990316ca63SLaurent Pinchart 1400*955d0fb5SRui Miguel Silva if (status & ISP176x_DC_IERESM) { 14010316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__); 14020316ca63SLaurent Pinchart isp1760_udc_resume(udc); 14030316ca63SLaurent Pinchart } 14040316ca63SLaurent Pinchart 1405*955d0fb5SRui Miguel Silva if (status & ISP176x_DC_IESUSP) { 14060316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__); 14070316ca63SLaurent Pinchart 14080316ca63SLaurent Pinchart spin_lock(&udc->lock); 14091da9e1c0SRui Miguel Silva if (!isp1760_udc_is_set(udc, DC_VBUSSTAT)) 14100316ca63SLaurent Pinchart isp1760_udc_disconnect(udc); 14110316ca63SLaurent Pinchart else 14120316ca63SLaurent Pinchart isp1760_udc_suspend(udc); 14130316ca63SLaurent Pinchart spin_unlock(&udc->lock); 14140316ca63SLaurent Pinchart } 14150316ca63SLaurent Pinchart 1416*955d0fb5SRui Miguel Silva if (status & ISP176x_DC_IEHS_STA) { 14170316ca63SLaurent Pinchart dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__); 14180316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_HIGH; 14190316ca63SLaurent Pinchart } 14200316ca63SLaurent Pinchart 14210316ca63SLaurent Pinchart return status ? IRQ_HANDLED : IRQ_NONE; 14220316ca63SLaurent Pinchart } 14230316ca63SLaurent Pinchart 14247e33da59SKees Cook static void isp1760_udc_vbus_poll(struct timer_list *t) 14250316ca63SLaurent Pinchart { 14267e33da59SKees Cook struct isp1760_udc *udc = from_timer(udc, t, vbus_timer); 14270316ca63SLaurent Pinchart unsigned long flags; 14280316ca63SLaurent Pinchart 14290316ca63SLaurent Pinchart spin_lock_irqsave(&udc->lock, flags); 14300316ca63SLaurent Pinchart 14311da9e1c0SRui Miguel Silva if (!(isp1760_udc_is_set(udc, DC_VBUSSTAT))) 14320316ca63SLaurent Pinchart isp1760_udc_disconnect(udc); 14330316ca63SLaurent Pinchart else if (udc->gadget.state >= USB_STATE_POWERED) 14340316ca63SLaurent Pinchart mod_timer(&udc->vbus_timer, 14350316ca63SLaurent Pinchart jiffies + ISP1760_VBUS_POLL_INTERVAL); 14360316ca63SLaurent Pinchart 14370316ca63SLaurent Pinchart spin_unlock_irqrestore(&udc->lock, flags); 14380316ca63SLaurent Pinchart } 14390316ca63SLaurent Pinchart 14400316ca63SLaurent Pinchart /* ----------------------------------------------------------------------------- 14410316ca63SLaurent Pinchart * Registration 14420316ca63SLaurent Pinchart */ 14430316ca63SLaurent Pinchart 14440316ca63SLaurent Pinchart static void isp1760_udc_init_eps(struct isp1760_udc *udc) 14450316ca63SLaurent Pinchart { 14460316ca63SLaurent Pinchart unsigned int i; 14470316ca63SLaurent Pinchart 14480316ca63SLaurent Pinchart INIT_LIST_HEAD(&udc->gadget.ep_list); 14490316ca63SLaurent Pinchart 14500316ca63SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(udc->ep); ++i) { 14510316ca63SLaurent Pinchart struct isp1760_ep *ep = &udc->ep[i]; 14520316ca63SLaurent Pinchart unsigned int ep_num = (i + 1) / 2; 14530316ca63SLaurent Pinchart bool is_in = !(i & 1); 14540316ca63SLaurent Pinchart 14550316ca63SLaurent Pinchart ep->udc = udc; 14560316ca63SLaurent Pinchart 14570316ca63SLaurent Pinchart INIT_LIST_HEAD(&ep->queue); 14580316ca63SLaurent Pinchart 14590316ca63SLaurent Pinchart ep->addr = (ep_num && is_in ? USB_DIR_IN : USB_DIR_OUT) 14600316ca63SLaurent Pinchart | ep_num; 14610316ca63SLaurent Pinchart ep->desc = NULL; 14620316ca63SLaurent Pinchart 14630316ca63SLaurent Pinchart sprintf(ep->name, "ep%u%s", ep_num, 14640316ca63SLaurent Pinchart ep_num ? (is_in ? "in" : "out") : ""); 14650316ca63SLaurent Pinchart 14660316ca63SLaurent Pinchart ep->ep.ops = &isp1760_ep_ops; 14670316ca63SLaurent Pinchart ep->ep.name = ep->name; 14680316ca63SLaurent Pinchart 14690316ca63SLaurent Pinchart /* 14700316ca63SLaurent Pinchart * Hardcode the maximum packet sizes for now, to 64 bytes for 14710316ca63SLaurent Pinchart * the control endpoint and 512 bytes for all other endpoints. 14720316ca63SLaurent Pinchart * This fits in the 8kB FIFO without double-buffering. 14730316ca63SLaurent Pinchart */ 14740316ca63SLaurent Pinchart if (ep_num == 0) { 14756fd82b69SRobert Baldyga usb_ep_set_maxpacket_limit(&ep->ep, 64); 1476eb4cbc19SRobert Baldyga ep->ep.caps.type_control = true; 1477eb4cbc19SRobert Baldyga ep->ep.caps.dir_in = true; 1478eb4cbc19SRobert Baldyga ep->ep.caps.dir_out = true; 14790316ca63SLaurent Pinchart ep->maxpacket = 64; 14800316ca63SLaurent Pinchart udc->gadget.ep0 = &ep->ep; 14810316ca63SLaurent Pinchart } else { 14826fd82b69SRobert Baldyga usb_ep_set_maxpacket_limit(&ep->ep, 512); 1483eb4cbc19SRobert Baldyga ep->ep.caps.type_iso = true; 1484eb4cbc19SRobert Baldyga ep->ep.caps.type_bulk = true; 1485eb4cbc19SRobert Baldyga ep->ep.caps.type_int = true; 14860316ca63SLaurent Pinchart ep->maxpacket = 0; 14870316ca63SLaurent Pinchart list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); 14880316ca63SLaurent Pinchart } 1489eb4cbc19SRobert Baldyga 1490eb4cbc19SRobert Baldyga if (is_in) 1491eb4cbc19SRobert Baldyga ep->ep.caps.dir_in = true; 1492eb4cbc19SRobert Baldyga else 1493eb4cbc19SRobert Baldyga ep->ep.caps.dir_out = true; 14940316ca63SLaurent Pinchart } 14950316ca63SLaurent Pinchart } 14960316ca63SLaurent Pinchart 14970316ca63SLaurent Pinchart static int isp1760_udc_init(struct isp1760_udc *udc) 14980316ca63SLaurent Pinchart { 1499d369c918SRui Miguel Silva u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE; 15000316ca63SLaurent Pinchart u16 scratch; 15010316ca63SLaurent Pinchart u32 chipid; 15020316ca63SLaurent Pinchart 15030316ca63SLaurent Pinchart /* 15040316ca63SLaurent Pinchart * Check that the controller is present by writing to the scratch 15050316ca63SLaurent Pinchart * register, modifying the bus pattern by reading from the chip ID 15060316ca63SLaurent Pinchart * register, and reading the scratch register value back. The chip ID 15070316ca63SLaurent Pinchart * and scratch register contents must match the expected values. 15080316ca63SLaurent Pinchart */ 1509d369c918SRui Miguel Silva isp1760_udc_write(udc, DC_SCRATCH, 0xbabe); 1510d369c918SRui Miguel Silva chipid = isp1760_udc_read(udc, DC_CHIP_ID_HIGH) << 16; 1511d369c918SRui Miguel Silva chipid |= isp1760_udc_read(udc, DC_CHIP_ID_LOW); 1512d369c918SRui Miguel Silva scratch = isp1760_udc_read(udc, DC_SCRATCH); 15130316ca63SLaurent Pinchart 15140316ca63SLaurent Pinchart if (scratch != 0xbabe) { 15150316ca63SLaurent Pinchart dev_err(udc->isp->dev, 15160316ca63SLaurent Pinchart "udc: scratch test failed (0x%04x/0x%08x)\n", 15170316ca63SLaurent Pinchart scratch, chipid); 15180316ca63SLaurent Pinchart return -ENODEV; 15190316ca63SLaurent Pinchart } 15200316ca63SLaurent Pinchart 1521d369c918SRui Miguel Silva if (chipid != 0x00011582 && chipid != 0x00158210 && 1522d369c918SRui Miguel Silva chipid != 0x00176320) { 15230316ca63SLaurent Pinchart dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); 15240316ca63SLaurent Pinchart return -ENODEV; 15250316ca63SLaurent Pinchart } 15260316ca63SLaurent Pinchart 15270316ca63SLaurent Pinchart /* Reset the device controller. */ 15281da9e1c0SRui Miguel Silva isp1760_udc_set(udc, DC_SFRESET); 15290316ca63SLaurent Pinchart usleep_range(10000, 11000); 1530d369c918SRui Miguel Silva isp1760_reg_write(udc->regs, mode_reg, 0); 15310316ca63SLaurent Pinchart usleep_range(10000, 11000); 15320316ca63SLaurent Pinchart 15330316ca63SLaurent Pinchart return 0; 15340316ca63SLaurent Pinchart } 15350316ca63SLaurent Pinchart 15360316ca63SLaurent Pinchart int isp1760_udc_register(struct isp1760_device *isp, int irq, 15370316ca63SLaurent Pinchart unsigned long irqflags) 15380316ca63SLaurent Pinchart { 15390316ca63SLaurent Pinchart struct isp1760_udc *udc = &isp->udc; 15400316ca63SLaurent Pinchart int ret; 15410316ca63SLaurent Pinchart 15420316ca63SLaurent Pinchart udc->irq = -1; 15430316ca63SLaurent Pinchart udc->isp = isp; 15440316ca63SLaurent Pinchart 15450316ca63SLaurent Pinchart spin_lock_init(&udc->lock); 15467e33da59SKees Cook timer_setup(&udc->vbus_timer, isp1760_udc_vbus_poll, 0); 15470316ca63SLaurent Pinchart 15480316ca63SLaurent Pinchart ret = isp1760_udc_init(udc); 15490316ca63SLaurent Pinchart if (ret < 0) 15500316ca63SLaurent Pinchart return ret; 15510316ca63SLaurent Pinchart 155201812ba3SHimanshu Jha udc->irqname = kasprintf(GFP_KERNEL, "%s (udc)", dev_name(isp->dev)); 15530316ca63SLaurent Pinchart if (!udc->irqname) 15540316ca63SLaurent Pinchart return -ENOMEM; 15550316ca63SLaurent Pinchart 155680b4a0f8SValentin Rothberg ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags, 155780b4a0f8SValentin Rothberg udc->irqname, udc); 15580316ca63SLaurent Pinchart if (ret < 0) 15590316ca63SLaurent Pinchart goto error; 15600316ca63SLaurent Pinchart 15610316ca63SLaurent Pinchart udc->irq = irq; 15620316ca63SLaurent Pinchart 15630316ca63SLaurent Pinchart /* 15640316ca63SLaurent Pinchart * Initialize the gadget static fields and register its device. Gadget 15650316ca63SLaurent Pinchart * fields that vary during the life time of the gadget are initialized 15660316ca63SLaurent Pinchart * by the UDC core. 15670316ca63SLaurent Pinchart */ 15680316ca63SLaurent Pinchart udc->gadget.ops = &isp1760_udc_ops; 15690316ca63SLaurent Pinchart udc->gadget.speed = USB_SPEED_UNKNOWN; 15700316ca63SLaurent Pinchart udc->gadget.max_speed = USB_SPEED_HIGH; 15710316ca63SLaurent Pinchart udc->gadget.name = "isp1761_udc"; 15720316ca63SLaurent Pinchart 15730316ca63SLaurent Pinchart isp1760_udc_init_eps(udc); 15740316ca63SLaurent Pinchart 15750316ca63SLaurent Pinchart ret = usb_add_gadget_udc(isp->dev, &udc->gadget); 15760316ca63SLaurent Pinchart if (ret < 0) 15770316ca63SLaurent Pinchart goto error; 15780316ca63SLaurent Pinchart 15790316ca63SLaurent Pinchart return 0; 15800316ca63SLaurent Pinchart 15810316ca63SLaurent Pinchart error: 15820316ca63SLaurent Pinchart if (udc->irq >= 0) 15830316ca63SLaurent Pinchart free_irq(udc->irq, udc); 15840316ca63SLaurent Pinchart kfree(udc->irqname); 15850316ca63SLaurent Pinchart 15860316ca63SLaurent Pinchart return ret; 15870316ca63SLaurent Pinchart } 15880316ca63SLaurent Pinchart 15890316ca63SLaurent Pinchart void isp1760_udc_unregister(struct isp1760_device *isp) 15900316ca63SLaurent Pinchart { 15910316ca63SLaurent Pinchart struct isp1760_udc *udc = &isp->udc; 15920316ca63SLaurent Pinchart 1593d21daf1eSLaurent Pinchart if (!udc->isp) 1594d21daf1eSLaurent Pinchart return; 1595d21daf1eSLaurent Pinchart 15960316ca63SLaurent Pinchart usb_del_gadget_udc(&udc->gadget); 15970316ca63SLaurent Pinchart 15980316ca63SLaurent Pinchart free_irq(udc->irq, udc); 15990316ca63SLaurent Pinchart kfree(udc->irqname); 16000316ca63SLaurent Pinchart } 1601