diff options
Diffstat (limited to 'drivers/usb/isp1760/isp1760-udc.c')
-rw-r--r-- | drivers/usb/isp1760/isp1760-udc.c | 251 |
1 files changed, 173 insertions, 78 deletions
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 1714b2258b54..a78da59d6417 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -2,10 +2,12 @@ /* * Driver for the NXP ISP1761 device controller * + * Copyright 2021 Linaro, Rui Miguel Silva * Copyright 2014 Ideas on Board Oy * * Contacts: * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * Rui Miguel Silva <rui.silva@linaro.org> */ #include <linux/interrupt.h> @@ -45,16 +47,62 @@ static inline struct isp1760_request *req_to_udc_req(struct usb_request *req) return container_of(req, struct isp1760_request, req); } -static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg) +static u32 isp1760_udc_read(struct isp1760_udc *udc, u16 field) { - return isp1760_read32(udc->regs, reg); + return isp1760_field_read(udc->fields, field); } -static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val) +static void isp1760_udc_write(struct isp1760_udc *udc, u16 field, u32 val) { - isp1760_write32(udc->regs, reg, val); + isp1760_field_write(udc->fields, field, val); } +static u32 isp1760_udc_read_raw(struct isp1760_udc *udc, u16 reg) +{ + __le32 val; + + regmap_raw_read(udc->regs, reg, &val, 4); + + return le32_to_cpu(val); +} + +static u16 isp1760_udc_read_raw16(struct isp1760_udc *udc, u16 reg) +{ + __le16 val; + + regmap_raw_read(udc->regs, reg, &val, 2); + + return le16_to_cpu(val); +} + +static void isp1760_udc_write_raw(struct isp1760_udc *udc, u16 reg, u32 val) +{ + __le32 val_le = cpu_to_le32(val); + + regmap_raw_write(udc->regs, reg, &val_le, 4); +} + +static void isp1760_udc_write_raw16(struct isp1760_udc *udc, u16 reg, u16 val) +{ + __le16 val_le = cpu_to_le16(val); + + regmap_raw_write(udc->regs, reg, &val_le, 2); +} + +static void isp1760_udc_set(struct isp1760_udc *udc, u32 field) +{ + isp1760_udc_write(udc, field, 0xFFFFFFFF); +} + +static void isp1760_udc_clear(struct isp1760_udc *udc, u32 field) +{ + isp1760_udc_write(udc, field, 0); +} + +static bool isp1760_udc_is_set(struct isp1760_udc *udc, u32 field) +{ + return !!isp1760_udc_read(udc, field); +} /* ----------------------------------------------------------------------------- * Endpoint Management */ @@ -75,16 +123,21 @@ static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc, return NULL; } -static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir) +static void __isp1760_udc_select_ep(struct isp1760_udc *udc, + struct isp1760_ep *ep, int dir) { - isp1760_udc_write(ep->udc, DC_EPINDEX, - DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) | - (dir == USB_DIR_IN ? DC_EPDIR : 0)); + isp1760_udc_write(udc, DC_ENDPIDX, ep->addr & USB_ENDPOINT_NUMBER_MASK); + + if (dir == USB_DIR_IN) + isp1760_udc_set(udc, DC_EPDIR); + else + isp1760_udc_clear(udc, DC_EPDIR); } /** * isp1760_udc_select_ep - Select an endpoint for register access * @ep: The endpoint + * @udc: Reference to the device controller * * The ISP1761 endpoint registers are banked. This function selects the target * endpoint for banked register access. The selection remains valid until the @@ -93,9 +146,10 @@ static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir) * * Called with the UDC spinlock held. */ -static void isp1760_udc_select_ep(struct isp1760_ep *ep) +static void isp1760_udc_select_ep(struct isp1760_udc *udc, + struct isp1760_ep *ep) { - __isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK); + __isp1760_udc_select_ep(udc, ep, ep->addr & USB_ENDPOINT_DIR_MASK); } /* Called with the UDC spinlock held. */ @@ -108,9 +162,13 @@ static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir) * the direction opposite to the data stage data packets, we thus need * to select the OUT/IN endpoint for IN/OUT transfers. */ - isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | - (dir == USB_DIR_IN ? 0 : DC_EPDIR)); - isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS); + if (dir == USB_DIR_IN) + isp1760_udc_clear(udc, DC_EPDIR); + else + isp1760_udc_set(udc, DC_EPDIR); + + isp1760_udc_write(udc, DC_ENDPIDX, 1); + isp1760_udc_set(udc, DC_STATUS); /* * The hardware will terminate the request automatically and go back to @@ -157,10 +215,10 @@ static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep) spin_lock_irqsave(&udc->lock, flags); /* Stall both the IN and OUT endpoints. */ - __isp1760_udc_select_ep(ep, USB_DIR_OUT); - isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL); - __isp1760_udc_select_ep(ep, USB_DIR_IN); - isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL); + __isp1760_udc_select_ep(udc, ep, USB_DIR_OUT); + isp1760_udc_set(udc, DC_STALL); + __isp1760_udc_select_ep(udc, ep, USB_DIR_IN); + isp1760_udc_set(udc, DC_STALL); /* A protocol stall completes the control transaction. */ udc->ep0_state = ISP1760_CTRL_SETUP; @@ -181,8 +239,8 @@ static bool isp1760_udc_receive(struct isp1760_ep *ep, u32 *buf; int i; - isp1760_udc_select_ep(ep); - len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK; + isp1760_udc_select_ep(udc, ep); + len = isp1760_udc_read(udc, DC_BUFLEN); dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n", __func__, len, req->req.actual, req->req.length); @@ -198,7 +256,7 @@ static bool isp1760_udc_receive(struct isp1760_ep *ep, * datasheet doesn't clearly document how this should be * handled. */ - isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); + isp1760_udc_set(udc, DC_CLBUF); return false; } @@ -209,9 +267,9 @@ static bool isp1760_udc_receive(struct isp1760_ep *ep, * the next packet might be removed from the FIFO. */ for (i = len; i > 2; i -= 4, ++buf) - *buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT)); + *buf = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT); if (i > 0) - *(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT)); + *(u16 *)buf = isp1760_udc_read_raw16(udc, ISP176x_DC_DATAPORT); req->req.actual += len; @@ -253,7 +311,7 @@ static void isp1760_udc_transmit(struct isp1760_ep *ep, __func__, req->packet_size, req->req.actual, req->req.length); - __isp1760_udc_select_ep(ep, USB_DIR_IN); + __isp1760_udc_select_ep(udc, ep, USB_DIR_IN); if (req->packet_size) isp1760_udc_write(udc, DC_BUFLEN, req->packet_size); @@ -265,14 +323,14 @@ static void isp1760_udc_transmit(struct isp1760_ep *ep, * the FIFO for this kind of conditions, but doesn't seem to work. */ for (i = req->packet_size; i > 2; i -= 4, ++buf) - isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf)); + isp1760_udc_write_raw(udc, ISP176x_DC_DATAPORT, *buf); if (i > 0) - writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT); + isp1760_udc_write_raw16(udc, ISP176x_DC_DATAPORT, *(u16 *)buf); if (ep->addr == 0) - isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); + isp1760_udc_set(udc, DC_DSEN); if (!req->packet_size) - isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP); + isp1760_udc_set(udc, DC_VENDP); } static void isp1760_ep_rx_ready(struct isp1760_ep *ep) @@ -408,19 +466,24 @@ static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt) return -EINVAL; } - isp1760_udc_select_ep(ep); - isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0); + isp1760_udc_select_ep(udc, ep); + + if (halt) + isp1760_udc_set(udc, DC_STALL); + else + isp1760_udc_clear(udc, DC_STALL); if (ep->addr == 0) { /* When halting the control endpoint, stall both IN and OUT. */ - __isp1760_udc_select_ep(ep, USB_DIR_IN); - isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0); + __isp1760_udc_select_ep(udc, ep, USB_DIR_IN); + if (halt) + isp1760_udc_set(udc, DC_STALL); + else + isp1760_udc_clear(udc, DC_STALL); } else if (!halt) { /* Reset the data PID by cycling the endpoint enable bit. */ - u16 eptype = isp1760_udc_read(udc, DC_EPTYPE); - - isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE); - isp1760_udc_write(udc, DC_EPTYPE, eptype); + isp1760_udc_clear(udc, DC_EPENABLE); + isp1760_udc_set(udc, DC_EPENABLE); /* * Disabling the endpoint emptied the transmit FIFO, fill it @@ -479,12 +542,14 @@ static int isp1760_udc_get_status(struct isp1760_udc *udc, return -EINVAL; } - isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR); + isp1760_udc_set(udc, DC_EPDIR); + isp1760_udc_write(udc, DC_ENDPIDX, 1); + isp1760_udc_write(udc, DC_BUFLEN, 2); - writew(cpu_to_le16(status), udc->regs + DC_DATAPORT); + isp1760_udc_write_raw16(udc, ISP176x_DC_DATAPORT, status); - isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); + isp1760_udc_set(udc, DC_DSEN); dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status); @@ -508,7 +573,8 @@ static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr) usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT); - isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr); + isp1760_udc_write(udc, DC_DEVADDR, addr); + isp1760_udc_set(udc, DC_DEVEN); spin_lock(&udc->lock); isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT); @@ -650,9 +716,9 @@ static void isp1760_ep0_setup(struct isp1760_udc *udc) spin_lock(&udc->lock); - isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP); + isp1760_udc_set(udc, DC_EP0SETUP); - count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK; + count = isp1760_udc_read(udc, DC_BUFLEN); if (count != sizeof(req)) { spin_unlock(&udc->lock); @@ -663,8 +729,8 @@ static void isp1760_ep0_setup(struct isp1760_udc *udc) return; } - req.data[0] = isp1760_udc_read(udc, DC_DATAPORT); - req.data[1] = isp1760_udc_read(udc, DC_DATAPORT); + req.data[0] = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT); + req.data[1] = isp1760_udc_read_raw(udc, ISP176x_DC_DATAPORT); if (udc->ep0_state != ISP1760_CTRL_SETUP) { spin_unlock(&udc->lock); @@ -732,13 +798,13 @@ static int isp1760_ep_enable(struct usb_ep *ep, switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_ISOC: - type = DC_ENDPTYP_ISOC; + type = ISP176x_DC_ENDPTYP_ISOC; break; case USB_ENDPOINT_XFER_BULK: - type = DC_ENDPTYP_BULK; + type = ISP176x_DC_ENDPTYP_BULK; break; case USB_ENDPOINT_XFER_INT: - type = DC_ENDPTYP_INTERRUPT; + type = ISP176x_DC_ENDPTYP_INTERRUPT; break; case USB_ENDPOINT_XFER_CONTROL: default: @@ -755,10 +821,13 @@ static int isp1760_ep_enable(struct usb_ep *ep, uep->halted = false; uep->wedged = false; - isp1760_udc_select_ep(uep); - isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket); + isp1760_udc_select_ep(udc, uep); + + isp1760_udc_write(udc, DC_FFOSZ, uep->maxpacket); isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket); - isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type); + + isp1760_udc_write(udc, DC_ENDPTYP, type); + isp1760_udc_set(udc, DC_EPENABLE); spin_unlock_irqrestore(&udc->lock, flags); @@ -786,8 +855,9 @@ static int isp1760_ep_disable(struct usb_ep *ep) uep->desc = NULL; uep->maxpacket = 0; - isp1760_udc_select_ep(uep); - isp1760_udc_write(udc, DC_EPTYPE, 0); + isp1760_udc_select_ep(udc, uep); + isp1760_udc_clear(udc, DC_EPENABLE); + isp1760_udc_clear(udc, DC_ENDPTYP); /* TODO Synchronize with the IRQ handler */ @@ -864,8 +934,8 @@ static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req, case ISP1760_CTRL_DATA_OUT: list_add_tail(&req->queue, &uep->queue); - __isp1760_udc_select_ep(uep, USB_DIR_OUT); - isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); + __isp1760_udc_select_ep(udc, uep, USB_DIR_OUT); + isp1760_udc_set(udc, DC_DSEN); break; case ISP1760_CTRL_STATUS: @@ -1025,14 +1095,14 @@ static void isp1760_ep_fifo_flush(struct usb_ep *ep) spin_lock_irqsave(&udc->lock, flags); - isp1760_udc_select_ep(uep); + isp1760_udc_select_ep(udc, uep); /* * Set the CLBUF bit twice to flush both buffers in case double * buffering is enabled. */ - isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); - isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); + isp1760_udc_set(udc, DC_CLBUF); + isp1760_udc_set(udc, DC_CLBUF); spin_unlock_irqrestore(&udc->lock, flags); } @@ -1082,6 +1152,10 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc) static void isp1760_udc_init_hw(struct isp1760_udc *udc) { + u32 intconf = udc->is_isp1763 ? ISP1763_DC_INTCONF : ISP176x_DC_INTCONF; + u32 intena = udc->is_isp1763 ? ISP1763_DC_INTENABLE : + ISP176x_DC_INTENABLE; + /* * The device controller currently shares its interrupt with the host * controller, the DC_IRQ polarity and signaling mode are ignored. Set @@ -1091,19 +1165,22 @@ static void isp1760_udc_init_hw(struct isp1760_udc *udc) * ACK tokens only (and NYET for the out pipe). The default * configuration also generates an interrupt on the first NACK token. */ - isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK | - DC_DDBGMODOUT_ACK_NYET); - - isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) | - DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) | - DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) | - DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP | - DC_IEHS_STA | DC_IEBRST); + isp1760_reg_write(udc->regs, intconf, + ISP176x_DC_CDBGMOD_ACK | ISP176x_DC_DDBGMODIN_ACK | + ISP176x_DC_DDBGMODOUT_ACK); + + isp1760_reg_write(udc->regs, intena, DC_IEPRXTX(7) | + DC_IEPRXTX(6) | DC_IEPRXTX(5) | DC_IEPRXTX(4) | + DC_IEPRXTX(3) | DC_IEPRXTX(2) | DC_IEPRXTX(1) | + DC_IEPRXTX(0) | ISP176x_DC_IEP0SETUP | + ISP176x_DC_IEVBUS | ISP176x_DC_IERESM | + ISP176x_DC_IESUSP | ISP176x_DC_IEHS_STA | + ISP176x_DC_IEBRST); if (udc->connected) isp1760_set_pullup(udc->isp, true); - isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN); + isp1760_udc_set(udc, DC_DEVEN); } static void isp1760_udc_reset(struct isp1760_udc *udc) @@ -1152,7 +1229,7 @@ static int isp1760_udc_get_frame(struct usb_gadget *gadget) { struct isp1760_udc *udc = gadget_to_udc(gadget); - return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1); + return isp1760_udc_read(udc, DC_FRAMENUM); } static int isp1760_udc_wakeup(struct usb_gadget *gadget) @@ -1219,7 +1296,7 @@ static int isp1760_udc_start(struct usb_gadget *gadget, usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); /* DMA isn't supported yet, don't enable the DMA clock. */ - isp1760_udc_write(udc, DC_MODE, DC_GLINTENA); + isp1760_udc_set(udc, DC_GLINTENA); isp1760_udc_init_hw(udc); @@ -1232,13 +1309,14 @@ static int isp1760_udc_start(struct usb_gadget *gadget, static int isp1760_udc_stop(struct usb_gadget *gadget) { struct isp1760_udc *udc = gadget_to_udc(gadget); + u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE; unsigned long flags; dev_dbg(udc->isp->dev, "%s\n", __func__); del_timer_sync(&udc->vbus_timer); - isp1760_udc_write(udc, DC_MODE, 0); + isp1760_reg_write(udc->regs, mode_reg, 0); spin_lock_irqsave(&udc->lock, flags); udc->driver = NULL; @@ -1260,15 +1338,30 @@ static const struct usb_gadget_ops isp1760_udc_ops = { * Interrupt Handling */ +static u32 isp1760_udc_irq_get_status(struct isp1760_udc *udc) +{ + u32 status; + + if (udc->is_isp1763) { + status = isp1760_reg_read(udc->regs, ISP1763_DC_INTERRUPT) + & isp1760_reg_read(udc->regs, ISP1763_DC_INTENABLE); + isp1760_reg_write(udc->regs, ISP1763_DC_INTERRUPT, status); + } else { + status = isp1760_reg_read(udc->regs, ISP176x_DC_INTERRUPT) + & isp1760_reg_read(udc->regs, ISP176x_DC_INTENABLE); + isp1760_reg_write(udc->regs, ISP176x_DC_INTERRUPT, status); + } + + return status; +} + static irqreturn_t isp1760_udc_irq(int irq, void *dev) { struct isp1760_udc *udc = dev; unsigned int i; u32 status; - status = isp1760_udc_read(udc, DC_INTERRUPT) - & isp1760_udc_read(udc, DC_INTENABLE); - isp1760_udc_write(udc, DC_INTERRUPT, status); + status = isp1760_udc_irq_get_status(udc); if (status & DC_IEVBUS) { dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__); @@ -1313,7 +1406,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev) dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__); spin_lock(&udc->lock); - if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT)) + if (!isp1760_udc_is_set(udc, DC_VBUSSTAT)) isp1760_udc_disconnect(udc); else isp1760_udc_suspend(udc); @@ -1335,7 +1428,7 @@ static void isp1760_udc_vbus_poll(struct timer_list *t) spin_lock_irqsave(&udc->lock, flags); - if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT)) + if (!(isp1760_udc_is_set(udc, DC_VBUSSTAT))) isp1760_udc_disconnect(udc); else if (udc->gadget.state >= USB_STATE_POWERED) mod_timer(&udc->vbus_timer, @@ -1403,6 +1496,7 @@ static void isp1760_udc_init_eps(struct isp1760_udc *udc) static int isp1760_udc_init(struct isp1760_udc *udc) { + u32 mode_reg = udc->is_isp1763 ? ISP1763_DC_MODE : ISP176x_DC_MODE; u16 scratch; u32 chipid; @@ -1413,7 +1507,8 @@ static int isp1760_udc_init(struct isp1760_udc *udc) * and scratch register contents must match the expected values. */ isp1760_udc_write(udc, DC_SCRATCH, 0xbabe); - chipid = isp1760_udc_read(udc, DC_CHIPID); + chipid = isp1760_udc_read(udc, DC_CHIP_ID_HIGH) << 16; + chipid |= isp1760_udc_read(udc, DC_CHIP_ID_LOW); scratch = isp1760_udc_read(udc, DC_SCRATCH); if (scratch != 0xbabe) { @@ -1423,15 +1518,16 @@ static int isp1760_udc_init(struct isp1760_udc *udc) return -ENODEV; } - if (chipid != 0x00011582 && chipid != 0x00158210) { + if (chipid != 0x00011582 && chipid != 0x00158210 && + chipid != 0x00176320) { dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); return -ENODEV; } /* Reset the device controller. */ - isp1760_udc_write(udc, DC_MODE, DC_SFRESET); + isp1760_udc_set(udc, DC_SFRESET); usleep_range(10000, 11000); - isp1760_udc_write(udc, DC_MODE, 0); + isp1760_reg_write(udc->regs, mode_reg, 0); usleep_range(10000, 11000); return 0; @@ -1445,7 +1541,6 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq, udc->irq = -1; udc->isp = isp; - udc->regs = isp->regs; spin_lock_init(&udc->lock); timer_setup(&udc->vbus_timer, isp1760_udc_vbus_poll, 0); |