Message ID | 20200607205859.55341-1-jassisinghbrar@gmail.com |
---|---|
State | Superseded |
Headers | show |
Series | [RESEND] usb: max3420: add the gadget driver | expand |
... a polite ping, Lukasz. On Sun, Jun 7, 2020 at 3:59 PM <jassisinghbrar at gmail.com> wrote: > > From: Jassi Brar <jaswinder.singh at linaro.org> > > MAX3420 implements FullSpeed USB Device over SPI. > Another version MAX3421, also implements USB Host mode. > This driver should be good for the device mode of max3421 as well. > > Signed-off-by: Jassi Brar <jaswinder.singh at linaro.org> > --- > drivers/usb/gadget/Kconfig | 6 + > drivers/usb/gadget/Makefile | 1 + > drivers/usb/gadget/gadget_chips.h | 8 + > drivers/usb/gadget/max3420_udc.c | 875 ++++++++++++++++++++++++++++++ > 4 files changed, 890 insertions(+) > create mode 100644 drivers/usb/gadget/max3420_udc.c > > diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig > index 46aa3fe954..7c0df5c264 100644 > --- a/drivers/usb/gadget/Kconfig > +++ b/drivers/usb/gadget/Kconfig > @@ -105,6 +105,12 @@ config CI_UDC > Say Y here to enable device controller functionality of the > ChipIdea driver. > > +config USB_GADGET_MAX3420 > + bool "MAX3420 USB Over SPI" > + depends on DM_SPI > + help > + MAX3420, from MAXIM, implements USB-over-SPI Full-Speed device controller. > + > config USB_GADGET_VBUS_DRAW > int "Maximum VBUS Power usage (2-500 mA)" > range 2 500 > diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile > index 70f3bf43e7..f560068b41 100644 > --- a/drivers/usb/gadget/Makefile > +++ b/drivers/usb/gadget/Makefile > @@ -20,6 +20,7 @@ obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += bcm_udc_otg_phy.o > obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o > obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o > obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o > +obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o > obj-$(CONFIG_CI_UDC) += ci_udc.o > ifndef CONFIG_SPL_BUILD > obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o > diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h > index 91b0285244..587204cfb7 100644 > --- a/drivers/usb/gadget/gadget_chips.h > +++ b/drivers/usb/gadget/gadget_chips.h > @@ -155,6 +155,12 @@ > #define gadget_is_cdns3(g) 0 > #endif > > +#ifdef CONFIG_USB_GADGET_MAX3420 > +#define gadget_is_max3420(g) (!strcmp("max3420-udc", (g)->name)) > +#else > +#define gadget_is_max3420(g) 0 > +#endif > + > /** > * usb_gadget_controller_number - support bcdDevice id convention > * @gadget: the controller being driven > @@ -216,5 +222,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) > return 0x23; > else if (gadget_is_cdns3(gadget)) > return 0x24; > + else if (gadget_is_max3420(gadget)) > + return 0x25; > return -ENOENT; > } > diff --git a/drivers/usb/gadget/max3420_udc.c b/drivers/usb/gadget/max3420_udc.c > new file mode 100644 > index 0000000000..7267d482b8 > --- /dev/null > +++ b/drivers/usb/gadget/max3420_udc.c > @@ -0,0 +1,875 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +#include <common.h> > +#include <linux/errno.h> > +#include <asm/gpio.h> > +#include <linux/list.h> > +#include <linux/usb/ch9.h> > +#include <linux/usb/gadget.h> > +#include <malloc.h> > +#include <spi.h> > +#include <dm.h> > +#include <g_dnl.h> > + > +#define MAX3420_MAX_EPS 4 > +#define EP_MAX_PACKET 64 /* Same for all Endpoints */ > +#define EPNAME_SIZE 16 /* Buffer size for endpoint name */ > + > +#define ACKSTAT BIT(0) > + > +#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */ > +#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */ > + > +/* SPI commands: */ > +#define MAX3420_SPI_DIR_SHIFT 1 > +#define MAX3420_SPI_REG_SHIFT 3 > + > +#define MAX3420_REG_EP0FIFO 0 > +#define MAX3420_REG_EP1FIFO 1 > +#define MAX3420_REG_EP2FIFO 2 > +#define MAX3420_REG_EP3FIFO 3 > +#define MAX3420_REG_SUDFIFO 4 > +#define MAX3420_REG_EP0BC 5 > +#define MAX3420_REG_EP1BC 6 > +#define MAX3420_REG_EP2BC 7 > +#define MAX3420_REG_EP3BC 8 > + > +#define MAX3420_REG_EPSTALLS 9 > + #define bACKSTAT BIT(6) > + #define bSTLSTAT BIT(5) > + #define bSTLEP3IN BIT(4) > + #define bSTLEP2IN BIT(3) > + #define bSTLEP1OUT BIT(2) > + #define bSTLEP0OUT BIT(1) > + #define bSTLEP0IN BIT(0) > + > +#define MAX3420_REG_CLRTOGS 10 > + #define bEP3DISAB BIT(7) > + #define bEP2DISAB BIT(6) > + #define bEP1DISAB BIT(5) > + #define bCTGEP3IN BIT(4) > + #define bCTGEP2IN BIT(3) > + #define bCTGEP1OUT BIT(2) > + > +#define MAX3420_REG_EPIRQ 11 > +#define MAX3420_REG_EPIEN 12 > + #define bSUDAVIRQ BIT(5) > + #define bIN3BAVIRQ BIT(4) > + #define bIN2BAVIRQ BIT(3) > + #define bOUT1DAVIRQ BIT(2) > + #define bOUT0DAVIRQ BIT(1) > + #define bIN0BAVIRQ BIT(0) > + > +#define MAX3420_REG_USBIRQ 13 > +#define MAX3420_REG_USBIEN 14 > + #define bOSCOKIRQ BIT(0) > + #define bRWUDNIRQ BIT(1) > + #define bBUSACTIRQ BIT(2) > + #define bURESIRQ BIT(3) > + #define bSUSPIRQ BIT(4) > + #define bNOVBUSIRQ BIT(5) > + #define bVBUSIRQ BIT(6) > + #define bURESDNIRQ BIT(7) > + > +#define MAX3420_REG_USBCTL 15 > + #define bHOSCSTEN BIT(7) > + #define bVBGATE BIT(6) > + #define bCHIPRES BIT(5) > + #define bPWRDOWN BIT(4) > + #define bCONNECT BIT(3) > + #define bSIGRWU BIT(2) > + > +#define MAX3420_REG_CPUCTL 16 > + #define bIE BIT(0) > + > +#define MAX3420_REG_PINCTL 17 > + #define bEP3INAK BIT(7) > + #define bEP2INAK BIT(6) > + #define bEP0INAK BIT(5) > + #define bFDUPSPI BIT(4) > + #define bINTLEVEL BIT(3) > + #define bPOSINT BIT(2) > + #define bGPXB BIT(1) > + #define bGPXA BIT(0) > + > +#define MAX3420_REG_REVISION 18 > + > +#define MAX3420_REG_FNADDR 19 > + #define FNADDR_MASK 0x7f > + > +#define MAX3420_REG_IOPINS 20 > +#define MAX3420_REG_IOPINS2 21 > +#define MAX3420_REG_GPINIRQ 22 > +#define MAX3420_REG_GPINIEN 23 > +#define MAX3420_REG_GPINPOL 24 > +#define MAX3420_REG_HIRQ 25 > +#define MAX3420_REG_HIEN 26 > +#define MAX3420_REG_MODE 27 > +#define MAX3420_REG_PERADDR 28 > +#define MAX3420_REG_HCTL 29 > +#define MAX3420_REG_HXFR 30 > +#define MAX3420_REG_HRSL 31 > + > +#define field(val, bit) ((val) << (bit)) > + > +#define msleep(a) udelay((a) * 1000) > + > +struct max3420_req { > + struct usb_request usb_req; > + struct list_head queue; > + struct max3420_ep *ep; > +}; > + > +struct max3420_ep { > + struct max3420_udc *udc; > + struct list_head queue; > + char name[EPNAME_SIZE]; > + unsigned int maxpacket; > + struct usb_ep ep_usb; > + int halted; > + int id; > +}; > + > +struct max3420_udc { > + struct max3420_ep ep[MAX3420_MAX_EPS]; > + struct usb_gadget_driver *driver; > + bool vbus_active, softconnect; > + struct usb_ctrlrequest setup; > + struct max3420_req ep0req; > + struct usb_gadget gadget; > + struct spi_slave *slave; > + struct udevice *dev; > + u8 ep0buf[64]; > + int remote_wkp; > + bool suspended; > +}; > + > +#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req) > +#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb) > +#define to_udc(g) container_of((g), struct max3420_udc, gadget) > + > +static void spi_ack_ctrl(struct max3420_udc *udc) > +{ > + struct spi_slave *slave = udc->slave; > + u8 txdata[1]; > + > + txdata[0] = ACKSTAT; > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_ONCE); > +} > + > +static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat) > +{ > + struct spi_slave *slave = udc->slave; > + u8 txdata[2], rxdata[2]; > + > + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | > + field(MAX3420_SPI_DIR_RD, MAX3420_SPI_DIR_SHIFT) | > + (actstat ? ACKSTAT : 0); > + > + rxdata[0] = 0; > + rxdata[1] = 0; > + spi_xfer(slave, 2 * 8, txdata, rxdata, SPI_XFER_ONCE); > + > + return rxdata[1]; > +} > + > +static u8 spi_rd8(struct max3420_udc *udc, u8 reg) > +{ > + return spi_rd8_ack(udc, reg, 0); > +} > + > +static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int actstat) > +{ > + struct spi_slave *slave = udc->slave; > + u8 txdata[2]; > + > + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | > + field(MAX3420_SPI_DIR_WR, MAX3420_SPI_DIR_SHIFT) | > + (actstat ? ACKSTAT : 0); > + txdata[1] = val; > + > + spi_xfer(slave, 2 * 8, txdata, NULL, SPI_XFER_ONCE); > +} > + > +static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) > +{ > + spi_wr8_ack(udc, reg, val, 0); > +} > + > +static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) > +{ > + struct spi_slave *slave = udc->slave; > + u8 txdata[1]; > + > + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | > + field(MAX3420_SPI_DIR_RD, MAX3420_SPI_DIR_SHIFT)); > + > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); > + spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END); > +} > + > +static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) > +{ > + struct spi_slave *slave = udc->slave; > + u8 txdata[1]; > + > + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | > + field(MAX3420_SPI_DIR_WR, MAX3420_SPI_DIR_SHIFT)); > + > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); > + spi_xfer(slave, len * 8, buf, NULL, SPI_XFER_END); > +} > + > +/* 0 if not-connected */ > +int g_dnl_board_usb_cable_connected(void) > +{ > + return 1; > +} > + > +static void spi_max3420_enable(struct max3420_ep *ep, int enable) > +{ > + struct max3420_udc *udc = ep->udc; > + u8 epdis, epien; > + > + if (ep->id == 0) > + return; > + > + epien = spi_rd8(udc, MAX3420_REG_EPIEN); > + epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS); > + > + if (enable) { > + epdis &= ~BIT(ep->id + 4); > + epien |= BIT(ep->id + 1); > + } else { > + epdis |= BIT(ep->id + 4); > + epien &= ~BIT(ep->id + 1); > + } > + > + spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis); > + spi_wr8(udc, MAX3420_REG_EPIEN, epien); > +} > + > +static int > +max3420_ep_enable(struct usb_ep *_ep, > + const struct usb_endpoint_descriptor *desc) > +{ > + struct max3420_ep *ep = to_max3420_ep(_ep); > + > + _ep->desc = desc; > + _ep->maxpacket = usb_endpoint_maxp(desc) & 0x7ff; > + > + spi_max3420_enable(ep, 1); > + > + return 0; > +} > + > +static void max3420_req_done(struct max3420_req *req, int status) > +{ > + struct max3420_ep *ep = req->ep; > + > + if (req->usb_req.status == -EINPROGRESS) > + req->usb_req.status = status; > + else > + status = req->usb_req.status; > + > + if (status && status != -ESHUTDOWN) > + dev_err(ep->udc->dev, "%s done %p, status %d\n", > + ep->ep_usb.name, req, status); > + > + if (req->usb_req.complete) > + req->usb_req.complete(&ep->ep_usb, &req->usb_req); > +} > + > +static void max3420_ep_nuke(struct max3420_ep *ep, int status) > +{ > + struct max3420_req *req, *r; > + > + list_for_each_entry_safe(req, r, &ep->queue, queue) { > + list_del_init(&req->queue); > + max3420_req_done(req, status); > + } > +} > + > +static int max3420_ep_disable(struct usb_ep *_ep) > +{ > + struct max3420_ep *ep = to_max3420_ep(_ep); > + > + _ep->desc = NULL; > + max3420_ep_nuke(ep, -ESHUTDOWN); > + spi_max3420_enable(ep, 0); > + > + return 0; > +} > + > +static struct usb_request * > +max3420_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) > +{ > + struct max3420_ep *ep = to_max3420_ep(_ep); > + struct max3420_req *req = kzalloc(sizeof(*req), gfp_flags); > + > + if (!req) > + return NULL; > + > + req->ep = ep; > + INIT_LIST_HEAD(&req->queue); > + > + return &req->usb_req; > +} > + > +static void > +max3420_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) > +{ > + kfree(to_max3420_req(_req)); > +} > + > +static int > +max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) > +{ > + struct max3420_req *req = to_max3420_req(_req); > + struct max3420_ep *ep = to_max3420_ep(_ep); > + > + _req->status = -EINPROGRESS; > + _req->actual = 0; > + list_add_tail(&req->queue, &ep->queue); > + > + return 0; > +} > + > +static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) > +{ > + struct max3420_req *req = to_max3420_req(_req); > + > + list_del_init(&req->queue); > + max3420_req_done(req, -ECONNRESET); > + > + return 0; > +} > + > +static int max3420_ep_set_halt(struct usb_ep *_ep, int halt) > +{ > + struct max3420_ep *ep = to_max3420_ep(_ep); > + struct max3420_udc *udc = ep->udc; > + u8 epstalls; > + > + if (ep->id == 0) /* can't stall EP0 */ > + return 0; > + > + epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS); > + if (halt) { > + ep->halted = 1; > + epstalls |= BIT(ep->id + 1); > + } else { > + u8 clrtogs; > + > + ep->halted = 0; > + epstalls &= ~BIT(ep->id + 1); > + clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS); > + clrtogs |= BIT(ep->id + 1); > + spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs); > + } > + spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | bACKSTAT); > + > + return 0; > +} > + > +static const struct usb_ep_ops max3420_ep_ops = { > + .enable = max3420_ep_enable, > + .disable = max3420_ep_disable, > + .alloc_request = max3420_ep_alloc_request, > + .free_request = max3420_ep_free_request, > + .queue = max3420_ep_queue, > + .dequeue = max3420_ep_dequeue, > + .set_halt = max3420_ep_set_halt, > +}; > + > +static void __max3420_stop(struct max3420_udc *udc) > +{ > + u8 val; > + > + /* Disable IRQ to CPU */ > + spi_wr8(udc, MAX3420_REG_CPUCTL, 0); > + > + val = spi_rd8(udc, MAX3420_REG_USBCTL); > + val |= bPWRDOWN; > + val |= bHOSCSTEN; > + spi_wr8(udc, MAX3420_REG_USBCTL, val); > +} > + > +static void __max3420_start(struct max3420_udc *udc) > +{ > + u8 val; > + > + /* configure SPI */ > + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); > + > + /* Chip Reset */ > + spi_wr8(udc, MAX3420_REG_USBCTL, bCHIPRES); > + msleep(5); > + spi_wr8(udc, MAX3420_REG_USBCTL, 0); > + > + /* Poll for OSC to stabilize */ > + while (1) { > + val = spi_rd8(udc, MAX3420_REG_USBIRQ); > + if (val & bOSCOKIRQ) > + break; > + cond_resched(); > + } > + > + /* Enable PULL-UP only when Vbus detected */ > + val = spi_rd8(udc, MAX3420_REG_USBCTL); > + val |= bVBGATE | bCONNECT; > + spi_wr8(udc, MAX3420_REG_USBCTL, val); > + > + val = bURESDNIRQ | bURESIRQ; > + spi_wr8(udc, MAX3420_REG_USBIEN, val); > + > + /* Enable only EP0 interrupts */ > + val = bIN0BAVIRQ | bOUT0DAVIRQ | bSUDAVIRQ; > + spi_wr8(udc, MAX3420_REG_EPIEN, val); > + > + /* Enable IRQ to CPU */ > + spi_wr8(udc, MAX3420_REG_CPUCTL, bIE); > +} > + > +static int max3420_udc_start(struct usb_gadget *gadget, > + struct usb_gadget_driver *driver) > +{ > + struct max3420_udc *udc = to_udc(gadget); > + unsigned long flags; > + > + udc->driver = driver; > + udc->remote_wkp = 0; > + udc->softconnect = true; > + > + //if (udc->vbus_active) > + __max3420_start(udc); > + > + return 0; > +} > + > +static int max3420_udc_stop(struct usb_gadget *gadget) > +{ > + struct max3420_udc *udc = to_udc(gadget); > + > + udc->driver = NULL; > + udc->softconnect = false; > + > + __max3420_stop(udc); > + > + return 0; > +} > + > +static int max3420_wakeup(struct usb_gadget *gadget) > +{ > + struct max3420_udc *udc = to_udc(gadget); > + u8 usbctl; > + > + /* Only if wakeup allowed by host */ > + if (!udc->remote_wkp || !udc->suspended) > + return 0; > + > + /* Set Remote-WkUp Signal*/ > + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); > + usbctl |= bSIGRWU; > + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); > + > + msleep(5); > + > + /* Clear Remote-WkUp Signal*/ > + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); > + usbctl &= ~bSIGRWU; > + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); > + > + udc->suspended = false; > + > + return 0; > +} > + > +static const struct usb_gadget_ops max3420_udc_ops = { > + .udc_start = max3420_udc_start, > + .udc_stop = max3420_udc_stop, > + .wakeup = max3420_wakeup, > +}; > + > +static struct usb_endpoint_descriptor ep0_desc = { > + .bLength = USB_DT_ENDPOINT_SIZE, > + .bDescriptorType = USB_DT_ENDPOINT, > + .bEndpointAddress = USB_DIR_OUT, > + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, > + .wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET), > +}; > + > +static void max3420_getstatus(struct max3420_udc *udc) > +{ > + struct max3420_ep *ep; > + u16 status = 0; > + > + switch (udc->setup.bRequestType & USB_RECIP_MASK) { > + case USB_RECIP_DEVICE: > + /* Get device status */ > + status = 0 << USB_DEVICE_SELF_POWERED; > + status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP); > + break; > + case USB_RECIP_INTERFACE: > + if (udc->driver->setup(&udc->gadget, &udc->setup) < 0) > + goto stall; > + break; > + case USB_RECIP_ENDPOINT: > + ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK]; > + if (ep->halted) > + status = 1 << USB_ENDPOINT_HALT; > + break; > + default: > + goto stall; > + } > + > + status = cpu_to_le16(status); > + spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2); > + spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1); > + return; > +stall: > + dev_err(udc->dev, "Can't respond to getstatus request\n"); > + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); > +} > + > +static void max3420_set_clear_feature(struct max3420_udc *udc) > +{ > + int set = udc->setup.bRequest == USB_REQ_SET_FEATURE; > + struct max3420_ep *ep; > + int id; > + > + switch (udc->setup.bRequestType) { > + case USB_RECIP_DEVICE: > + if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP) > + break; > + > + if (udc->setup.bRequest == USB_REQ_SET_FEATURE) > + udc->remote_wkp = 1; > + else > + udc->remote_wkp = 0; > + > + return spi_ack_ctrl(udc); > + > + case USB_RECIP_ENDPOINT: > + if (udc->setup.wValue != USB_ENDPOINT_HALT) > + break; > + > + id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; > + ep = &udc->ep[id]; > + > + max3420_ep_set_halt(&ep->ep_usb, set); > + return; > + default: > + break; > + } > + > + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); > + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); > +} > + > +static void max3420_handle_setup(struct max3420_udc *udc) > +{ > + struct usb_ctrlrequest setup; > + u8 addr; > + > + spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8); > + > + udc->setup = setup; > + udc->setup.wValue = cpu_to_le16(setup.wValue); > + udc->setup.wIndex = cpu_to_le16(setup.wIndex); > + udc->setup.wLength = cpu_to_le16(setup.wLength); > + > + switch (udc->setup.bRequest) { > + case USB_REQ_GET_STATUS: > + /* Data+Status phase form udc */ > + if ((udc->setup.bRequestType & > + (USB_DIR_IN | USB_TYPE_MASK)) != > + (USB_DIR_IN | USB_TYPE_STANDARD)) { > + break; > + } > + return max3420_getstatus(udc); > + case USB_REQ_SET_ADDRESS: > + /* Status phase from udc */ > + if (udc->setup.bRequestType != (USB_DIR_OUT | > + USB_TYPE_STANDARD | USB_RECIP_DEVICE)) > + break; > + addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1); > + dev_dbg(udc->dev, "Assigned Address=%d/%d\n", > + udc->setup.wValue, addr); > + return; > + case USB_REQ_CLEAR_FEATURE: > + case USB_REQ_SET_FEATURE: > + /* Requests with no data phase, status phase from udc */ > + if ((udc->setup.bRequestType & USB_TYPE_MASK) > + != USB_TYPE_STANDARD) > + break; > + return max3420_set_clear_feature(udc); > + default: > + break; > + } > + > + if (udc->driver->setup(&udc->gadget, &setup) < 0) { > + /* Stall EP0 */ > + spi_wr8(udc, MAX3420_REG_EPSTALLS, > + bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); > + } > +} > + > +static int do_data(struct max3420_udc *udc, int ep_id, int in) > +{ > + struct max3420_ep *ep = &udc->ep[ep_id]; > + struct max3420_req *req; > + int done, length, psz; > + void *buf; > + > + if (list_empty(&ep->queue)) > + return 0; > + > + req = list_first_entry(&ep->queue, struct max3420_req, queue); > + buf = req->usb_req.buf + req->usb_req.actual; > + > + psz = ep->ep_usb.maxpacket; > + length = req->usb_req.length - req->usb_req.actual; > + length = min(length, psz); > + > + if (length == 0) { > + done = 1; > + goto xfer_done; > + } > + > + done = 0; > + if (in) { > + spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); > + spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length); > + if (length < psz) > + done = 1; > + } else { > + psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id); > + length = min(length, psz); > + spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); > + if (length < ep->ep_usb.maxpacket) > + done = 1; > + } > + > + req->usb_req.actual += length; > + > + if (req->usb_req.actual == req->usb_req.length) > + done = 1; > + > +xfer_done: > + if (done) { > + unsigned long flags; > + > + list_del_init(&req->queue); > + > + if (ep_id == 0) > + spi_ack_ctrl(udc); > + > + max3420_req_done(req, 0); > + } > + > + return 1; > +} > + > +static int max3420_handle_irqs(struct max3420_udc *udc) > +{ > + u8 epien, epirq, usbirq, usbien, reg[4]; > + int ret = 0; > + > + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4); > + epirq = reg[0]; > + epien = reg[1]; > + usbirq = reg[2]; > + usbien = reg[3]; > + > + usbirq &= usbien; > + epirq &= epien; > + > + if (epirq & bSUDAVIRQ) { > + spi_wr8(udc, MAX3420_REG_EPIRQ, bSUDAVIRQ); > + max3420_handle_setup(udc); > + return 1; > + } > + > + if (usbirq & bVBUSIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bVBUSIRQ); > + dev_dbg(udc->dev, "Cable plugged in\n"); > + g_dnl_clear_detach(); > + return 1; > + } > + > + if (usbirq & bNOVBUSIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bNOVBUSIRQ); > + dev_dbg(udc->dev, "Cable pulled out\n"); > + g_dnl_trigger_detach(); > + return 1; > + } > + > + if (usbirq & bURESIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESIRQ); > + return 1; > + } > + > + if (usbirq & bURESDNIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESDNIRQ); > + spi_wr8(udc, MAX3420_REG_USBIEN, bURESDNIRQ | bURESIRQ); > + spi_wr8(udc, MAX3420_REG_EPIEN, bSUDAVIRQ > + | bIN0BAVIRQ | bOUT0DAVIRQ); > + return 1; > + } > + > + if (usbirq & bSUSPIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bSUSPIRQ); > + dev_dbg(udc->dev, "USB Suspend - Enter\n"); > + udc->suspended = true; > + return 1; > + } > + > + if (usbirq & bBUSACTIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bBUSACTIRQ); > + dev_dbg(udc->dev, "USB Suspend - Exit\n"); > + udc->suspended = false; > + return 1; > + } > + > + if (usbirq & bRWUDNIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bRWUDNIRQ); > + dev_dbg(udc->dev, "Asked Host to wakeup\n"); > + return 1; > + } > + > + if (usbirq & bOSCOKIRQ) { > + spi_wr8(udc, MAX3420_REG_USBIRQ, bOSCOKIRQ); > + dev_dbg(udc->dev, "Osc stabilized, start work\n"); > + return 1; > + } > + > + if (epirq & bOUT0DAVIRQ && do_data(udc, 0, 0)) { > + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT0DAVIRQ, 1); > + ret = 1; > + } > + > + if (epirq & bIN0BAVIRQ && do_data(udc, 0, 1)) > + ret = 1; > + > + if (epirq & bOUT1DAVIRQ && do_data(udc, 1, 0)) { > + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT1DAVIRQ, 1); > + ret = 1; > + } > + > + if (epirq & bIN2BAVIRQ && do_data(udc, 2, 1)) > + ret = 1; > + > + if (epirq & bIN3BAVIRQ && do_data(udc, 3, 1)) > + ret = 1; > + > + return ret; > +} > + > +static int max3420_irq(struct max3420_udc *udc) > +{ > + /* needed ? */ > + do_data(udc, 0, 1); /* get done with the EP0 ZLP */ > + > + return max3420_handle_irqs(udc); > +} > + > +static void max3420_setup_eps(struct max3420_udc *udc) > +{ > + int i; > + > + INIT_LIST_HEAD(&udc->gadget.ep_list); > + INIT_LIST_HEAD(&udc->ep[0].ep_usb.ep_list); > + > + for (i = 0; i < MAX3420_MAX_EPS; i++) { > + struct max3420_ep *ep = &udc->ep[i]; > + > + INIT_LIST_HEAD(&ep->queue); > + > + ep->id = i; > + ep->udc = udc; > + ep->ep_usb.ops = &max3420_ep_ops; > + ep->ep_usb.name = ep->name; > + ep->ep_usb.maxpacket = EP_MAX_PACKET; > + > + if (i == 0) { > + ep->ep_usb.desc = &ep0_desc; > + snprintf(ep->name, EPNAME_SIZE, "ep0"); > + continue; > + } > + > + list_add_tail(&ep->ep_usb.ep_list, &udc->gadget.ep_list); > + > + if (i == 1) > + snprintf(ep->name, EPNAME_SIZE, "ep1out-bulk"); > + else > + snprintf(ep->name, EPNAME_SIZE, "ep%din-bulk", i); > + }; > +} > + > +static void max3420_setup_spi(struct max3420_udc *udc) > +{ > + u8 reg[8]; > + > + spi_claim_bus(udc->slave); > + /* necessary? */ > + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); > + /* configure SPI */ > + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); > +} > + > +int dm_usb_gadget_handle_interrupts(struct udevice *dev) > +{ > + struct max3420_udc *udc = dev_get_priv(dev); > + > + return max3420_irq(udc); > +} > + > +static int max3420_udc_probe(struct udevice *dev) > +{ > + struct max3420_udc *udc = dev_get_priv(dev); > + struct udevice *spid; > + > + spi_get_bus_and_cs(3, 0, 10000000, SPI_MODE_3, "spi_generic_drv", > + NULL, &spid, &udc->slave); > + > + udc->dev = dev; > + udc->gadget.ep0 = &udc->ep[0].ep_usb; > + udc->gadget.max_speed = USB_SPEED_FULL; > + udc->gadget.speed = USB_SPEED_FULL; > + udc->gadget.is_dualspeed = 0; > + udc->gadget.ops = &max3420_udc_ops; > + udc->gadget.name = "max3420-udc"; > + > + max3420_setup_eps(udc); > + max3420_setup_spi(udc); > + > + usb_add_gadget_udc((struct device *)dev, &udc->gadget); > + > + return 0; > +} > + > +static int max3420_udc_remove(struct udevice *dev) > +{ > + struct max3420_udc *udc = dev_get_priv(dev); > + > + usb_del_gadget_udc(&udc->gadget); > + > + spi_release_bus(udc->slave); > + > + return 0; > +} > + > +static const struct udevice_id max3420_ids[] = { > + { .compatible = "maxim,max3421-udc" }, > + { } > +}; > + > +U_BOOT_DRIVER(max3420_generic_udc) = { > + .name = "max3420-udc", > + .id = UCLASS_USB_GADGET_GENERIC, > + .of_match = max3420_ids, > + .probe = max3420_udc_probe, > + .remove = max3420_udc_remove, > + .priv_auto_alloc_size = sizeof(struct max3420_udc), > +}; > -- > 2.25.1 >
On 6/16/20 1:25 AM, Jassi Brar wrote: > ... a polite ping, Lukasz. so while I don't know much about the gadget subsystem, a few nits below. [...] >> +#define MAX3420_REG_MODE 27 >> +#define MAX3420_REG_PERADDR 28 >> +#define MAX3420_REG_HCTL 29 >> +#define MAX3420_REG_HXFR 30 >> +#define MAX3420_REG_HRSL 31 >> + >> +#define field(val, bit) ((val) << (bit)) Is this GENMASK() ? >> +#define msleep(a) udelay((a) * 1000) Please use mdelay() . [...] >> +static int max3420_udc_start(struct usb_gadget *gadget, >> + struct usb_gadget_driver *driver) >> +{ >> + struct max3420_udc *udc = to_udc(gadget); >> + unsigned long flags; >> + >> + udc->driver = driver; >> + udc->remote_wkp = 0; >> + udc->softconnect = true; >> + >> + //if (udc->vbus_active) Is this intended to be commented out ? >> + __max3420_start(udc); >> + >> + return 0; >> +} The rest looks good, thanks! And sorry for the inactivity.
Hi Jassi, > ... a polite ping, Lukasz. The only excuse for so long lack of my response are my personal issues caused by the covid-19. Sorry for that.. > > On Sun, Jun 7, 2020 at 3:59 PM <jassisinghbrar at gmail.com> wrote: > > > > From: Jassi Brar <jaswinder.singh at linaro.org> > > > > MAX3420 implements FullSpeed USB Device over SPI. > > Another version MAX3421, also implements USB Host mode. > > This driver should be good for the device mode of max3421 as well. > > > > Signed-off-by: Jassi Brar <jaswinder.singh at linaro.org> > > --- > > drivers/usb/gadget/Kconfig | 6 + > > drivers/usb/gadget/Makefile | 1 + > > drivers/usb/gadget/gadget_chips.h | 8 + > > drivers/usb/gadget/max3420_udc.c | 875 > > ++++++++++++++++++++++++++++++ 4 files changed, 890 insertions(+) > > create mode 100644 drivers/usb/gadget/max3420_udc.c > > > > diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig > > index 46aa3fe954..7c0df5c264 100644 > > --- a/drivers/usb/gadget/Kconfig > > +++ b/drivers/usb/gadget/Kconfig > > @@ -105,6 +105,12 @@ config CI_UDC > > Say Y here to enable device controller functionality of > > the ChipIdea driver. > > > > +config USB_GADGET_MAX3420 > > + bool "MAX3420 USB Over SPI" > > + depends on DM_SPI > > + help > > + MAX3420, from MAXIM, implements USB-over-SPI Full-Speed > > device controller. + > > config USB_GADGET_VBUS_DRAW > > int "Maximum VBUS Power usage (2-500 mA)" > > range 2 500 > > diff --git a/drivers/usb/gadget/Makefile > > b/drivers/usb/gadget/Makefile index 70f3bf43e7..f560068b41 100644 > > --- a/drivers/usb/gadget/Makefile > > +++ b/drivers/usb/gadget/Makefile > > @@ -20,6 +20,7 @@ obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += > > bcm_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_DWC2_OTG) += > > dwc2_udc_otg.o obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += > > dwc2_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o > > +obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o > > obj-$(CONFIG_CI_UDC) += ci_udc.o > > ifndef CONFIG_SPL_BUILD > > obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o > > diff --git a/drivers/usb/gadget/gadget_chips.h > > b/drivers/usb/gadget/gadget_chips.h index 91b0285244..587204cfb7 > > 100644 --- a/drivers/usb/gadget/gadget_chips.h > > +++ b/drivers/usb/gadget/gadget_chips.h > > @@ -155,6 +155,12 @@ > > #define gadget_is_cdns3(g) 0 > > #endif > > > > +#ifdef CONFIG_USB_GADGET_MAX3420 > > +#define gadget_is_max3420(g) (!strcmp("max3420-udc", > > (g)->name)) +#else > > +#define gadget_is_max3420(g) 0 > > +#endif > > + Ok. > > /** > > * usb_gadget_controller_number - support bcdDevice id convention > > * @gadget: the controller being driven > > @@ -216,5 +222,7 @@ static inline int > > usb_gadget_controller_number(struct usb_gadget *gadget) return 0x23; > > else if (gadget_is_cdns3(gadget)) > > return 0x24; > > + else if (gadget_is_max3420(gadget)) > > + return 0x25; > > return -ENOENT; > > } > > diff --git a/drivers/usb/gadget/max3420_udc.c > > b/drivers/usb/gadget/max3420_udc.c new file mode 100644 > > index 0000000000..7267d482b8 > > --- /dev/null > > +++ b/drivers/usb/gadget/max3420_udc.c > > @@ -0,0 +1,875 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > + > > +#include <common.h> > > +#include <linux/errno.h> > > +#include <asm/gpio.h> > > +#include <linux/list.h> > > +#include <linux/usb/ch9.h> > > +#include <linux/usb/gadget.h> > > +#include <malloc.h> > > +#include <spi.h> > > +#include <dm.h> > > +#include <g_dnl.h> > > + > > +#define MAX3420_MAX_EPS 4 > > +#define EP_MAX_PACKET 64 /* Same for all Endpoints */ > > +#define EPNAME_SIZE 16 /* Buffer size for endpoint > > name */ + > > +#define ACKSTAT BIT(0) > > + > > +#define MAX3420_SPI_DIR_RD 0 /* read register from > > MAX3420 */ +#define MAX3420_SPI_DIR_WR 1 /* write > > register to MAX3420 */ + > > +/* SPI commands: */ > > +#define MAX3420_SPI_DIR_SHIFT 1 > > +#define MAX3420_SPI_REG_SHIFT 3 > > + > > +#define MAX3420_REG_EP0FIFO 0 > > +#define MAX3420_REG_EP1FIFO 1 > > +#define MAX3420_REG_EP2FIFO 2 > > +#define MAX3420_REG_EP3FIFO 3 > > +#define MAX3420_REG_SUDFIFO 4 > > +#define MAX3420_REG_EP0BC 5 > > +#define MAX3420_REG_EP1BC 6 > > +#define MAX3420_REG_EP2BC 7 > > +#define MAX3420_REG_EP3BC 8 > > + > > +#define MAX3420_REG_EPSTALLS 9 > > + #define bACKSTAT BIT(6) > > + #define bSTLSTAT BIT(5) > > + #define bSTLEP3IN BIT(4) > > + #define bSTLEP2IN BIT(3) > > + #define bSTLEP1OUT BIT(2) > > + #define bSTLEP0OUT BIT(1) > > + #define bSTLEP0IN BIT(0) > > + > > +#define MAX3420_REG_CLRTOGS 10 > > + #define bEP3DISAB BIT(7) > > + #define bEP2DISAB BIT(6) > > + #define bEP1DISAB BIT(5) > > + #define bCTGEP3IN BIT(4) > > + #define bCTGEP2IN BIT(3) > > + #define bCTGEP1OUT BIT(2) > > + > > +#define MAX3420_REG_EPIRQ 11 > > +#define MAX3420_REG_EPIEN 12 > > + #define bSUDAVIRQ BIT(5) > > + #define bIN3BAVIRQ BIT(4) > > + #define bIN2BAVIRQ BIT(3) > > + #define bOUT1DAVIRQ BIT(2) > > + #define bOUT0DAVIRQ BIT(1) > > + #define bIN0BAVIRQ BIT(0) > > + > > +#define MAX3420_REG_USBIRQ 13 > > +#define MAX3420_REG_USBIEN 14 > > + #define bOSCOKIRQ BIT(0) > > + #define bRWUDNIRQ BIT(1) > > + #define bBUSACTIRQ BIT(2) > > + #define bURESIRQ BIT(3) > > + #define bSUSPIRQ BIT(4) > > + #define bNOVBUSIRQ BIT(5) > > + #define bVBUSIRQ BIT(6) > > + #define bURESDNIRQ BIT(7) > > + > > +#define MAX3420_REG_USBCTL 15 > > + #define bHOSCSTEN BIT(7) > > + #define bVBGATE BIT(6) > > + #define bCHIPRES BIT(5) > > + #define bPWRDOWN BIT(4) > > + #define bCONNECT BIT(3) > > + #define bSIGRWU BIT(2) > > + > > +#define MAX3420_REG_CPUCTL 16 > > + #define bIE BIT(0) > > + > > +#define MAX3420_REG_PINCTL 17 > > + #define bEP3INAK BIT(7) > > + #define bEP2INAK BIT(6) > > + #define bEP0INAK BIT(5) > > + #define bFDUPSPI BIT(4) > > + #define bINTLEVEL BIT(3) > > + #define bPOSINT BIT(2) > > + #define bGPXB BIT(1) > > + #define bGPXA BIT(0) > > + > > +#define MAX3420_REG_REVISION 18 > > + > > +#define MAX3420_REG_FNADDR 19 > > + #define FNADDR_MASK 0x7f > > + > > +#define MAX3420_REG_IOPINS 20 > > +#define MAX3420_REG_IOPINS2 21 > > +#define MAX3420_REG_GPINIRQ 22 > > +#define MAX3420_REG_GPINIEN 23 > > +#define MAX3420_REG_GPINPOL 24 > > +#define MAX3420_REG_HIRQ 25 > > +#define MAX3420_REG_HIEN 26 > > +#define MAX3420_REG_MODE 27 > > +#define MAX3420_REG_PERADDR 28 > > +#define MAX3420_REG_HCTL 29 > > +#define MAX3420_REG_HXFR 30 > > +#define MAX3420_REG_HRSL 31 > > + When I do look into drivers/usb/gadget/f_dfu.* the defines are placed in the f_dfu.h file. > > +#define field(val, bit) ((val) << (bit)) > > + > > +#define msleep(a) udelay((a) * 1000) > > + Aren't those two above already defined in some *.h files? > > +struct max3420_req { > > + struct usb_request usb_req; > > + struct list_head queue; > > + struct max3420_ep *ep; > > +}; > > + > > +struct max3420_ep { > > + struct max3420_udc *udc; > > + struct list_head queue; > > + char name[EPNAME_SIZE]; > > + unsigned int maxpacket; > > + struct usb_ep ep_usb; > > + int halted; > > + int id; > > +}; > > + > > +struct max3420_udc { > > + struct max3420_ep ep[MAX3420_MAX_EPS]; > > + struct usb_gadget_driver *driver; > > + bool vbus_active, softconnect; > > + struct usb_ctrlrequest setup; > > + struct max3420_req ep0req; > > + struct usb_gadget gadget; > > + struct spi_slave *slave; > > + struct udevice *dev; > > + u8 ep0buf[64]; > > + int remote_wkp; > > + bool suspended; > > +}; > > + > > +#define to_max3420_req(r) container_of((r), struct > > max3420_req, usb_req) +#define to_max3420_ep(e) > > container_of((e), struct max3420_ep, ep_usb) +#define to_udc(g) > > container_of((g), struct max3420_udc, gadget) + > > +static void spi_ack_ctrl(struct max3420_udc *udc) > > +{ > > + struct spi_slave *slave = udc->slave; > > + u8 txdata[1]; > > + > > + txdata[0] = ACKSTAT; > > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_ONCE); > > +} > > + > > +static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat) > > +{ > > + struct spi_slave *slave = udc->slave; > > + u8 txdata[2], rxdata[2]; > > + > > + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | > > + field(MAX3420_SPI_DIR_RD, > > MAX3420_SPI_DIR_SHIFT) | > > + (actstat ? ACKSTAT : 0); > > + > > + rxdata[0] = 0; > > + rxdata[1] = 0; > > + spi_xfer(slave, 2 * 8, txdata, rxdata, SPI_XFER_ONCE); > > + > > + return rxdata[1]; > > +} > > + > > +static u8 spi_rd8(struct max3420_udc *udc, u8 reg) > > +{ > > + return spi_rd8_ack(udc, reg, 0); > > +} > > + > > +static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, > > int actstat) +{ > > + struct spi_slave *slave = udc->slave; > > + u8 txdata[2]; > > + > > + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | > > + field(MAX3420_SPI_DIR_WR, > > MAX3420_SPI_DIR_SHIFT) | > > + (actstat ? ACKSTAT : 0); > > + txdata[1] = val; > > + > > + spi_xfer(slave, 2 * 8, txdata, NULL, SPI_XFER_ONCE); > > +} 2 * 8 -> sizeof(txdata) ? > > + > > +static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) > > +{ > > + spi_wr8_ack(udc, reg, val, 0); > > +} > > + > > +static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, > > u8 len) +{ > > + struct spi_slave *slave = udc->slave; > > + u8 txdata[1]; > > + > > + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | > > + field(MAX3420_SPI_DIR_RD, > > MAX3420_SPI_DIR_SHIFT)); + > > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); I think that 1 * 8 shall be replaced with just 8. > > + spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END); > > +} > > + > > +static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, > > u8 len) +{ > > + struct spi_slave *slave = udc->slave; > > + u8 txdata[1]; > > + > > + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | > > + field(MAX3420_SPI_DIR_WR, > > MAX3420_SPI_DIR_SHIFT)); + > > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); > > + spi_xfer(slave, len * 8, buf, NULL, SPI_XFER_END); > > +} > > + > > +/* 0 if not-connected */ > > +int g_dnl_board_usb_cable_connected(void) > > +{ > > + return 1; > > +} > > + > > +static void spi_max3420_enable(struct max3420_ep *ep, int enable) > > +{ > > + struct max3420_udc *udc = ep->udc; > > + u8 epdis, epien; > > + > > + if (ep->id == 0) > > + return; > > + > > + epien = spi_rd8(udc, MAX3420_REG_EPIEN); > > + epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS); > > + > > + if (enable) { > > + epdis &= ~BIT(ep->id + 4); > > + epien |= BIT(ep->id + 1); > > + } else { > > + epdis |= BIT(ep->id + 4); > > + epien &= ~BIT(ep->id + 1); > > + } > > + > > + spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis); > > + spi_wr8(udc, MAX3420_REG_EPIEN, epien); > > +} > > + > > +static int > > +max3420_ep_enable(struct usb_ep *_ep, > > + const struct usb_endpoint_descriptor *desc) > > +{ > > + struct max3420_ep *ep = to_max3420_ep(_ep); > > + > > + _ep->desc = desc; > > + _ep->maxpacket = usb_endpoint_maxp(desc) & 0x7ff; > > + > > + spi_max3420_enable(ep, 1); > > + > > + return 0; > > +} > > + > > +static void max3420_req_done(struct max3420_req *req, int status) > > +{ > > + struct max3420_ep *ep = req->ep; > > + > > + if (req->usb_req.status == -EINPROGRESS) > > + req->usb_req.status = status; > > + else > > + status = req->usb_req.status; > > + > > + if (status && status != -ESHUTDOWN) > > + dev_err(ep->udc->dev, "%s done %p, status %d\n", > > + ep->ep_usb.name, req, status); > > + > > + if (req->usb_req.complete) > > + req->usb_req.complete(&ep->ep_usb, &req->usb_req); > > +} > > + > > +static void max3420_ep_nuke(struct max3420_ep *ep, int status) > > +{ > > + struct max3420_req *req, *r; > > + > > + list_for_each_entry_safe(req, r, &ep->queue, queue) { > > + list_del_init(&req->queue); > > + max3420_req_done(req, status); > > + } > > +} > > + > > +static int max3420_ep_disable(struct usb_ep *_ep) > > +{ > > + struct max3420_ep *ep = to_max3420_ep(_ep); > > + > > + _ep->desc = NULL; > > + max3420_ep_nuke(ep, -ESHUTDOWN); > > + spi_max3420_enable(ep, 0); > > + > > + return 0; > > +} > > + > > +static struct usb_request * > > +max3420_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) > > +{ > > + struct max3420_ep *ep = to_max3420_ep(_ep); > > + struct max3420_req *req = kzalloc(sizeof(*req), gfp_flags); > > + > > + if (!req) > > + return NULL; > > + > > + req->ep = ep; > > + INIT_LIST_HEAD(&req->queue); > > + > > + return &req->usb_req; > > +} > > + > > +static void > > +max3420_ep_free_request(struct usb_ep *_ep, struct usb_request > > *_req) +{ > > + kfree(to_max3420_req(_req)); > > +} > > + > > +static int > > +max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, > > gfp_t gfp_flags) +{ > > + struct max3420_req *req = to_max3420_req(_req); > > + struct max3420_ep *ep = to_max3420_ep(_ep); > > + > > + _req->status = -EINPROGRESS; > > + _req->actual = 0; > > + list_add_tail(&req->queue, &ep->queue); > > + > > + return 0; > > +} > > + > > +static int max3420_ep_dequeue(struct usb_ep *_ep, struct > > usb_request *_req) +{ > > + struct max3420_req *req = to_max3420_req(_req); > > + > > + list_del_init(&req->queue); > > + max3420_req_done(req, -ECONNRESET); > > + > > + return 0; > > +} > > + > > +static int max3420_ep_set_halt(struct usb_ep *_ep, int halt) > > +{ > > + struct max3420_ep *ep = to_max3420_ep(_ep); > > + struct max3420_udc *udc = ep->udc; > > + u8 epstalls; > > + > > + if (ep->id == 0) /* can't stall EP0 */ > > + return 0; > > + > > + epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS); > > + if (halt) { > > + ep->halted = 1; > > + epstalls |= BIT(ep->id + 1); > > + } else { > > + u8 clrtogs; > > + > > + ep->halted = 0; > > + epstalls &= ~BIT(ep->id + 1); > > + clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS); > > + clrtogs |= BIT(ep->id + 1); > > + spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs); > > + } > > + spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | bACKSTAT); > > + > > + return 0; > > +} > > + > > +static const struct usb_ep_ops max3420_ep_ops = { > > + .enable = max3420_ep_enable, > > + .disable = max3420_ep_disable, > > + .alloc_request = max3420_ep_alloc_request, > > + .free_request = max3420_ep_free_request, > > + .queue = max3420_ep_queue, > > + .dequeue = max3420_ep_dequeue, > > + .set_halt = max3420_ep_set_halt, > > +}; > > + > > +static void __max3420_stop(struct max3420_udc *udc) > > +{ > > + u8 val; > > + > > + /* Disable IRQ to CPU */ > > + spi_wr8(udc, MAX3420_REG_CPUCTL, 0); > > + > > + val = spi_rd8(udc, MAX3420_REG_USBCTL); > > + val |= bPWRDOWN; > > + val |= bHOSCSTEN; > > + spi_wr8(udc, MAX3420_REG_USBCTL, val); > > +} > > + > > +static void __max3420_start(struct max3420_udc *udc) > > +{ > > + u8 val; > > + > > + /* configure SPI */ > > + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); > > + > > + /* Chip Reset */ > > + spi_wr8(udc, MAX3420_REG_USBCTL, bCHIPRES); > > + msleep(5); > > + spi_wr8(udc, MAX3420_REG_USBCTL, 0); > > + > > + /* Poll for OSC to stabilize */ > > + while (1) { > > + val = spi_rd8(udc, MAX3420_REG_USBIRQ); > > + if (val & bOSCOKIRQ) > > + break; > > + cond_resched(); > > + } > > + > > + /* Enable PULL-UP only when Vbus detected */ > > + val = spi_rd8(udc, MAX3420_REG_USBCTL); > > + val |= bVBGATE | bCONNECT; > > + spi_wr8(udc, MAX3420_REG_USBCTL, val); > > + > > + val = bURESDNIRQ | bURESIRQ; > > + spi_wr8(udc, MAX3420_REG_USBIEN, val); > > + > > + /* Enable only EP0 interrupts */ > > + val = bIN0BAVIRQ | bOUT0DAVIRQ | bSUDAVIRQ; > > + spi_wr8(udc, MAX3420_REG_EPIEN, val); > > + > > + /* Enable IRQ to CPU */ > > + spi_wr8(udc, MAX3420_REG_CPUCTL, bIE); > > +} > > + > > +static int max3420_udc_start(struct usb_gadget *gadget, > > + struct usb_gadget_driver *driver) > > +{ > > + struct max3420_udc *udc = to_udc(gadget); > > + unsigned long flags; > > + > > + udc->driver = driver; > > + udc->remote_wkp = 0; > > + udc->softconnect = true; > > + > > + //if (udc->vbus_active) > > + __max3420_start(udc); > > + Please refactor the code and remove any lines started with //. > > + return 0; > > +} > > + > > +static int max3420_udc_stop(struct usb_gadget *gadget) > > +{ > > + struct max3420_udc *udc = to_udc(gadget); > > + > > + udc->driver = NULL; > > + udc->softconnect = false; > > + > > + __max3420_stop(udc); > > + > > + return 0; > > +} > > + > > +static int max3420_wakeup(struct usb_gadget *gadget) > > +{ > > + struct max3420_udc *udc = to_udc(gadget); > > + u8 usbctl; > > + > > + /* Only if wakeup allowed by host */ > > + if (!udc->remote_wkp || !udc->suspended) > > + return 0; > > + > > + /* Set Remote-WkUp Signal*/ WkUp? -> Wakeup? > > + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); > > + usbctl |= bSIGRWU; > > + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); > > + > > + msleep(5); > > + > > + /* Clear Remote-WkUp Signal*/ > > + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); > > + usbctl &= ~bSIGRWU; > > + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); > > + > > + udc->suspended = false; > > + > > + return 0; > > +} > > + > > +static const struct usb_gadget_ops max3420_udc_ops = { > > + .udc_start = max3420_udc_start, > > + .udc_stop = max3420_udc_stop, > > + .wakeup = max3420_wakeup, > > +}; > > + > > +static struct usb_endpoint_descriptor ep0_desc = { > > + .bLength = USB_DT_ENDPOINT_SIZE, > > + .bDescriptorType = USB_DT_ENDPOINT, > > + .bEndpointAddress = USB_DIR_OUT, > > + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, > > + .wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET), > > +}; > > + > > +static void max3420_getstatus(struct max3420_udc *udc) > > +{ > > + struct max3420_ep *ep; > > + u16 status = 0; > > + > > + switch (udc->setup.bRequestType & USB_RECIP_MASK) { > > + case USB_RECIP_DEVICE: > > + /* Get device status */ > > + status = 0 << USB_DEVICE_SELF_POWERED; > > + status |= (udc->remote_wkp << > > USB_DEVICE_REMOTE_WAKEUP); > > + break; > > + case USB_RECIP_INTERFACE: > > + if (udc->driver->setup(&udc->gadget, &udc->setup) < > > 0) > > + goto stall; > > + break; > > + case USB_RECIP_ENDPOINT: > > + ep = &udc->ep[udc->setup.wIndex & > > USB_ENDPOINT_NUMBER_MASK]; > > + if (ep->halted) > > + status = 1 << USB_ENDPOINT_HALT; > > + break; > > + default: > > + goto stall; > > + } > > + > > + status = cpu_to_le16(status); > > + spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2); > > + spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1); > > + return; > > +stall: > > + dev_err(udc->dev, "Can't respond to getstatus request\n"); > > + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | > > bSTLSTAT); +} > > + > > +static void max3420_set_clear_feature(struct max3420_udc *udc) > > +{ > > + int set = udc->setup.bRequest == USB_REQ_SET_FEATURE; > > + struct max3420_ep *ep; > > + int id; > > + > > + switch (udc->setup.bRequestType) { > > + case USB_RECIP_DEVICE: > > + if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP) > > + break; > > + > > + if (udc->setup.bRequest == USB_REQ_SET_FEATURE) > > + udc->remote_wkp = 1; > > + else > > + udc->remote_wkp = 0; > > + > > + return spi_ack_ctrl(udc); > > + > > + case USB_RECIP_ENDPOINT: > > + if (udc->setup.wValue != USB_ENDPOINT_HALT) > > + break; > > + > > + id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; > > + ep = &udc->ep[id]; > > + > > + max3420_ep_set_halt(&ep->ep_usb, set); > > + return; > > + default: > > + break; > > + } > > + > > + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); > > + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | > > bSTLSTAT); +} > > + > > +static void max3420_handle_setup(struct max3420_udc *udc) > > +{ > > + struct usb_ctrlrequest setup; > > + u8 addr; > > + > > + spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8); > > + > > + udc->setup = setup; > > + udc->setup.wValue = cpu_to_le16(setup.wValue); > > + udc->setup.wIndex = cpu_to_le16(setup.wIndex); > > + udc->setup.wLength = cpu_to_le16(setup.wLength); > > + > > + switch (udc->setup.bRequest) { > > + case USB_REQ_GET_STATUS: > > + /* Data+Status phase form udc */ > > + if ((udc->setup.bRequestType & > > + (USB_DIR_IN | USB_TYPE_MASK)) != > > + (USB_DIR_IN | USB_TYPE_STANDARD)) { > > + break; > > + } > > + return max3420_getstatus(udc); > > + case USB_REQ_SET_ADDRESS: > > + /* Status phase from udc */ > > + if (udc->setup.bRequestType != (USB_DIR_OUT | > > + USB_TYPE_STANDARD | > > USB_RECIP_DEVICE)) > > + break; > > + addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1); > > + dev_dbg(udc->dev, "Assigned Address=%d/%d\n", > > + udc->setup.wValue, addr); > > + return; > > + case USB_REQ_CLEAR_FEATURE: > > + case USB_REQ_SET_FEATURE: > > + /* Requests with no data phase, status phase from > > udc */ > > + if ((udc->setup.bRequestType & USB_TYPE_MASK) > > + != USB_TYPE_STANDARD) > > + break; > > + return max3420_set_clear_feature(udc); > > + default: > > + break; > > + } > > + > > + if (udc->driver->setup(&udc->gadget, &setup) < 0) { > > + /* Stall EP0 */ > > + spi_wr8(udc, MAX3420_REG_EPSTALLS, > > + bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); > > + } > > +} > > + > > +static int do_data(struct max3420_udc *udc, int ep_id, int in) > > +{ > > + struct max3420_ep *ep = &udc->ep[ep_id]; > > + struct max3420_req *req; > > + int done, length, psz; > > + void *buf; > > + > > + if (list_empty(&ep->queue)) > > + return 0; > > + > > + req = list_first_entry(&ep->queue, struct max3420_req, > > queue); > > + buf = req->usb_req.buf + req->usb_req.actual; > > + > > + psz = ep->ep_usb.maxpacket; > > + length = req->usb_req.length - req->usb_req.actual; > > + length = min(length, psz); > > + > > + if (length == 0) { > > + done = 1; > > + goto xfer_done; > > + } > > + > > + done = 0; > > + if (in) { > > + spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, > > length); > > + spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length); > > + if (length < psz) > > + done = 1; > > + } else { > > + psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id); > > + length = min(length, psz); > > + spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, > > length); > > + if (length < ep->ep_usb.maxpacket) > > + done = 1; > > + } > > + > > + req->usb_req.actual += length; > > + > > + if (req->usb_req.actual == req->usb_req.length) > > + done = 1; > > + > > +xfer_done: > > + if (done) { > > + unsigned long flags; > > + > > + list_del_init(&req->queue); > > + > > + if (ep_id == 0) > > + spi_ack_ctrl(udc); > > + > > + max3420_req_done(req, 0); > > + } > > + > > + return 1; > > +} > > + > > +static int max3420_handle_irqs(struct max3420_udc *udc) > > +{ > > + u8 epien, epirq, usbirq, usbien, reg[4]; > > + int ret = 0; > > + > > + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4); > > + epirq = reg[0]; > > + epien = reg[1]; > > + usbirq = reg[2]; > > + usbien = reg[3]; > > + > > + usbirq &= usbien; > > + epirq &= epien; > > + > > + if (epirq & bSUDAVIRQ) { > > + spi_wr8(udc, MAX3420_REG_EPIRQ, bSUDAVIRQ); > > + max3420_handle_setup(udc); > > + return 1; > > + } > > + > > + if (usbirq & bVBUSIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bVBUSIRQ); > > + dev_dbg(udc->dev, "Cable plugged in\n"); > > + g_dnl_clear_detach(); > > + return 1; > > + } > > + > > + if (usbirq & bNOVBUSIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bNOVBUSIRQ); > > + dev_dbg(udc->dev, "Cable pulled out\n"); > > + g_dnl_trigger_detach(); > > + return 1; > > + } > > + > > + if (usbirq & bURESIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESIRQ); > > + return 1; > > + } > > + > > + if (usbirq & bURESDNIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESDNIRQ); > > + spi_wr8(udc, MAX3420_REG_USBIEN, bURESDNIRQ | > > bURESIRQ); > > + spi_wr8(udc, MAX3420_REG_EPIEN, bSUDAVIRQ > > + | bIN0BAVIRQ | bOUT0DAVIRQ); > > + return 1; > > + } > > + > > + if (usbirq & bSUSPIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bSUSPIRQ); > > + dev_dbg(udc->dev, "USB Suspend - Enter\n"); > > + udc->suspended = true; > > + return 1; > > + } > > + > > + if (usbirq & bBUSACTIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bBUSACTIRQ); > > + dev_dbg(udc->dev, "USB Suspend - Exit\n"); > > + udc->suspended = false; > > + return 1; > > + } > > + > > + if (usbirq & bRWUDNIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bRWUDNIRQ); > > + dev_dbg(udc->dev, "Asked Host to wakeup\n"); > > + return 1; > > + } > > + > > + if (usbirq & bOSCOKIRQ) { > > + spi_wr8(udc, MAX3420_REG_USBIRQ, bOSCOKIRQ); > > + dev_dbg(udc->dev, "Osc stabilized, start work\n"); > > + return 1; > > + } > > + > > + if (epirq & bOUT0DAVIRQ && do_data(udc, 0, 0)) { > > + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT0DAVIRQ, 1); > > + ret = 1; > > + } > > + > > + if (epirq & bIN0BAVIRQ && do_data(udc, 0, 1)) > > + ret = 1; > > + > > + if (epirq & bOUT1DAVIRQ && do_data(udc, 1, 0)) { > > + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT1DAVIRQ, 1); > > + ret = 1; > > + } > > + > > + if (epirq & bIN2BAVIRQ && do_data(udc, 2, 1)) > > + ret = 1; > > + > > + if (epirq & bIN3BAVIRQ && do_data(udc, 3, 1)) > > + ret = 1; > > + > > + return ret; > > +} > > + > > +static int max3420_irq(struct max3420_udc *udc) > > +{ > > + /* needed ? */ Please make sure if the code is needed or not. > > + do_data(udc, 0, 1); /* get done with the EP0 ZLP */ > > + > > + return max3420_handle_irqs(udc); > > +} > > + > > +static void max3420_setup_eps(struct max3420_udc *udc) > > +{ > > + int i; > > + > > + INIT_LIST_HEAD(&udc->gadget.ep_list); > > + INIT_LIST_HEAD(&udc->ep[0].ep_usb.ep_list); > > + > > + for (i = 0; i < MAX3420_MAX_EPS; i++) { > > + struct max3420_ep *ep = &udc->ep[i]; > > + > > + INIT_LIST_HEAD(&ep->queue); > > + > > + ep->id = i; > > + ep->udc = udc; > > + ep->ep_usb.ops = &max3420_ep_ops; > > + ep->ep_usb.name = ep->name; > > + ep->ep_usb.maxpacket = EP_MAX_PACKET; > > + > > + if (i == 0) { > > + ep->ep_usb.desc = &ep0_desc; > > + snprintf(ep->name, EPNAME_SIZE, "ep0"); > > + continue; > > + } > > + > > + list_add_tail(&ep->ep_usb.ep_list, > > &udc->gadget.ep_list); + > > + if (i == 1) > > + snprintf(ep->name, EPNAME_SIZE, > > "ep1out-bulk"); > > + else > > + snprintf(ep->name, EPNAME_SIZE, > > "ep%din-bulk", i); > > + }; > > +} > > + > > +static void max3420_setup_spi(struct max3420_udc *udc) > > +{ > > + u8 reg[8]; > > + > > + spi_claim_bus(udc->slave); > > + /* necessary? */ The same as above. If there are any quirks or doubts - please state them in the verbose comment. > > + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); > > + /* configure SPI */ > > + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); > > +} > > + > > +int dm_usb_gadget_handle_interrupts(struct udevice *dev) > > +{ > > + struct max3420_udc *udc = dev_get_priv(dev); > > + > > + return max3420_irq(udc); > > +} > > + > > +static int max3420_udc_probe(struct udevice *dev) > > +{ > > + struct max3420_udc *udc = dev_get_priv(dev); > > + struct udevice *spid; > > + > > + spi_get_bus_and_cs(3, 0, 10000000, SPI_MODE_3, > > "spi_generic_drv", > > + NULL, &spid, &udc->slave); IMHO the bus number (3?), CS (0), speed (10000000) and mode (SPI_MODE_3) shall be read from device tree. Some other setups, which would use this IC, could reuse the code when it is connected to other bus/CS/mode/speed. > > + > > + udc->dev = dev; > > + udc->gadget.ep0 = &udc->ep[0].ep_usb; > > + udc->gadget.max_speed = USB_SPEED_FULL; > > + udc->gadget.speed = USB_SPEED_FULL; > > + udc->gadget.is_dualspeed = 0; > > + udc->gadget.ops = &max3420_udc_ops; > > + udc->gadget.name = "max3420-udc"; > > + > > + max3420_setup_eps(udc); > > + max3420_setup_spi(udc); > > + > > + usb_add_gadget_udc((struct device *)dev, &udc->gadget); > > + > > + return 0; > > +} > > + > > +static int max3420_udc_remove(struct udevice *dev) > > +{ > > + struct max3420_udc *udc = dev_get_priv(dev); > > + > > + usb_del_gadget_udc(&udc->gadget); > > + > > + spi_release_bus(udc->slave); > > + > > + return 0; > > +} > > + > > +static const struct udevice_id max3420_ids[] = { > > + { .compatible = "maxim,max3421-udc" }, > > + { } > > +}; > > + > > +U_BOOT_DRIVER(max3420_generic_udc) = { > > + .name = "max3420-udc", > > + .id = UCLASS_USB_GADGET_GENERIC, > > + .of_match = max3420_ids, > > + .probe = max3420_udc_probe, > > + .remove = max3420_udc_remove, > > + .priv_auto_alloc_size = sizeof(struct max3420_udc), > > +}; > > -- > > 2.25.1 > > A few more remarks: 1. Is this code ported from the Linux kernel? If yes, please explicitly state SHA1 and version from which it was ported. 2. Does this particular driver require any extra explanation or description, which shall be put into ./doc/? 3. Please also follow comments from Marek, which have been sent in the other mail. Best regards, Lukasz Majewski -- DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma at denx.de -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: <https://lists.denx.de/pipermail/u-boot/attachments/20200616/5a83be9f/attachment.sig>
Hi Marek, On Mon, Jun 15, 2020 at 6:49 PM Marek Vasut <marex at denx.de> wrote: > >> +#define MAX3420_REG_MODE 27 > >> +#define MAX3420_REG_PERADDR 28 > >> +#define MAX3420_REG_HCTL 29 > >> +#define MAX3420_REG_HXFR 30 > >> +#define MAX3420_REG_HRSL 31 > >> + > >> +#define field(val, bit) ((val) << (bit)) > > Is this GENMASK() ? > The 'val' is not a mask, otherwise we could use genmask. > >> +#define msleep(a) udelay((a) * 1000) > > Please use mdelay() . > Ok. > >> +static int max3420_udc_start(struct usb_gadget *gadget, > >> + struct usb_gadget_driver *driver) > >> +{ > >> + struct max3420_udc *udc = to_udc(gadget); > >> + unsigned long flags; > >> + > >> + udc->driver = driver; > >> + udc->remote_wkp = 0; > >> + udc->softconnect = true; > >> + > >> + //if (udc->vbus_active) > > Is this intended to be commented out ? > Yes. Thank you!
On Tue, Jun 16, 2020 at 2:29 AM Lukasz Majewski <lukma at denx.de> wrote: > > Hi Jassi, > > > ... a polite ping, Lukasz. > > The only excuse for so long lack of my response are my personal issues > caused by the covid-19. > Sorry if I came across as pestering you. I hope all is well now. ..... > > > + > > > +#define MAX3420_REG_IOPINS 20 > > > +#define MAX3420_REG_IOPINS2 21 > > > +#define MAX3420_REG_GPINIRQ 22 > > > +#define MAX3420_REG_GPINIEN 23 > > > +#define MAX3420_REG_GPINPOL 24 > > > +#define MAX3420_REG_HIRQ 25 > > > +#define MAX3420_REG_HIEN 26 > > > +#define MAX3420_REG_MODE 27 > > > +#define MAX3420_REG_PERADDR 28 > > > +#define MAX3420_REG_HCTL 29 > > > +#define MAX3420_REG_HXFR 30 > > > +#define MAX3420_REG_HRSL 31 > > > + > > When I do look into drivers/usb/gadget/f_dfu.* the defines are placed > in the f_dfu.h file. > One school of thought is to contain all code in one file, especially when no other file should access it -- these defines are very max3420 specific and none else should need these. But I am fine if you want them in a separate file. > > > +#define field(val, bit) ((val) << (bit)) > > > + > > > +#define msleep(a) udelay((a) * 1000) > > > + > > Aren't those two above already defined in some *.h files? > I replaced msleep with mdelay as Marek suggested. I couldn't find the simple shift op define as field. > > > + > > > +static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, > > > int actstat) +{ > > > + struct spi_slave *slave = udc->slave; > > > + u8 txdata[2]; > > > + > > > + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | > > > + field(MAX3420_SPI_DIR_WR, > > > MAX3420_SPI_DIR_SHIFT) | > > > + (actstat ? ACKSTAT : 0); > > > + txdata[1] = val; > > > + > > > + spi_xfer(slave, 2 * 8, txdata, NULL, SPI_XFER_ONCE); > > > +} > > 2 * 8 -> sizeof(txdata) ? > Sure. > > > + > > > +static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) > > > +{ > > > + spi_wr8_ack(udc, reg, val, 0); > > > +} > > > + > > > +static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, > > > u8 len) +{ > > > + struct spi_slave *slave = udc->slave; > > > + u8 txdata[1]; > > > + > > > + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | > > > + field(MAX3420_SPI_DIR_RD, > > > MAX3420_SPI_DIR_SHIFT)); + > > > + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); > > I think that 1 * 8 shall be replaced with just 8. > sizeof(txdata) is more consistent > > > + > > > +static int max3420_udc_start(struct usb_gadget *gadget, > > > + struct usb_gadget_driver *driver) > > > +{ > > > + struct max3420_udc *udc = to_udc(gadget); > > > + unsigned long flags; > > > + > > > + udc->driver = driver; > > > + udc->remote_wkp = 0; > > > + udc->softconnect = true; > > > + > > > + //if (udc->vbus_active) > > > + __max3420_start(udc); > > > + > > Please refactor the code and remove any lines started with //. > ok. > > > +static int max3420_wakeup(struct usb_gadget *gadget) > > > +{ > > > + struct max3420_udc *udc = to_udc(gadget); > > > + u8 usbctl; > > > + > > > + /* Only if wakeup allowed by host */ > > > + if (!udc->remote_wkp || !udc->suspended) > > > + return 0; > > > + > > > + /* Set Remote-WkUp Signal*/ > > WkUp? -> Wakeup? > Ok. > > > + > > > +static int max3420_irq(struct max3420_udc *udc) > > > +{ > > > + /* needed ? */ > > Please make sure if the code is needed or not. > Yup. > > > +static void max3420_setup_spi(struct max3420_udc *udc) > > > +{ > > > + u8 reg[8]; > > > + > > > + spi_claim_bus(udc->slave); > > > + /* necessary? */ > > The same as above. If there are any quirks or doubts - please state > them in the verbose comment. > The spec doesn't say explicitly but my platform needed these. So I'll keep them until someone comes with a reason not to. > > > + > > > +static int max3420_udc_probe(struct udevice *dev) > > > +{ > > > + struct max3420_udc *udc = dev_get_priv(dev); > > > + struct udevice *spid; > > > + > > > + spi_get_bus_and_cs(3, 0, 10000000, SPI_MODE_3, > > > "spi_generic_drv", > > > + NULL, &spid, &udc->slave); > > IMHO the bus number (3?), CS (0), speed (10000000) and mode > (SPI_MODE_3) shall be read from device tree. > > Some other setups, which would use this IC, could reuse the code when > it is connected to other bus/CS/mode/speed. > Yes, of course. > > A few more remarks: > > 1. Is this code ported from the Linux kernel? If yes, please explicitly > state SHA1 and version from which it was ported. > This code has no roots in the kernel. > 2. Does this particular driver require any extra explanation or > description, which shall be put into ./doc/? > We don't implement any platform specific feature, so it is generic udc driver. > 3. Please also follow comments from Marek, which have been sent in the > other mail. > Sure. Thank you!
On 6/24/20 6:53 AM, Jassi Brar wrote: > Hi Marek, Hi, > On Mon, Jun 15, 2020 at 6:49 PM Marek Vasut <marex at denx.de> wrote: > >>>> +#define MAX3420_REG_MODE 27 >>>> +#define MAX3420_REG_PERADDR 28 >>>> +#define MAX3420_REG_HCTL 29 >>>> +#define MAX3420_REG_HXFR 30 >>>> +#define MAX3420_REG_HRSL 31 >>>> + >>>> +#define field(val, bit) ((val) << (bit)) >> >> Is this GENMASK() ? >> > The 'val' is not a mask, otherwise we could use genmask. Ah, right. Isn't there already some such macro in include/linux/ ? [...] >>>> +static int max3420_udc_start(struct usb_gadget *gadget, >>>> + struct usb_gadget_driver *driver) >>>> +{ >>>> + struct max3420_udc *udc = to_udc(gadget); >>>> + unsigned long flags; >>>> + >>>> + udc->driver = driver; >>>> + udc->remote_wkp = 0; >>>> + udc->softconnect = true; >>>> + >>>> + //if (udc->vbus_active) >> >> Is this intended to be commented out ? >> > Yes. Why ?
On Wed, Jun 24, 2020 at 4:24 AM Marek Vasut <marex at denx.de> wrote: > > On 6/24/20 6:53 AM, Jassi Brar wrote: > > Hi Marek, > > Hi, > > > On Mon, Jun 15, 2020 at 6:49 PM Marek Vasut <marex at denx.de> wrote: > > > >>>> +#define MAX3420_REG_MODE 27 > >>>> +#define MAX3420_REG_PERADDR 28 > >>>> +#define MAX3420_REG_HCTL 29 > >>>> +#define MAX3420_REG_HXFR 30 > >>>> +#define MAX3420_REG_HRSL 31 > >>>> + > >>>> +#define field(val, bit) ((val) << (bit)) > >> > >> Is this GENMASK() ? > >> > > The 'val' is not a mask, otherwise we could use genmask. > > Ah, right. Isn't there already some such macro in include/linux/ ? > I am not aware if there is. > [...] > > >>>> +static int max3420_udc_start(struct usb_gadget *gadget, > >>>> + struct usb_gadget_driver *driver) > >>>> +{ > >>>> + struct max3420_udc *udc = to_udc(gadget); > >>>> + unsigned long flags; > >>>> + > >>>> + udc->driver = driver; > >>>> + udc->remote_wkp = 0; > >>>> + udc->softconnect = true; > >>>> + > >>>> + //if (udc->vbus_active) > >> > >> Is this intended to be commented out ? > >> > > Yes. > > Why ? > That was remnant of debugging. The enumeration fails randomly if we don't always reset the chip upon connection and the spec isn't clear about it. Thanks.
On 6/24/20 1:09 AM, Jassi Brar wrote: > On Tue, Jun 16, 2020 at 2:29 AM Lukasz Majewski <lukma@denx.de> wrote: >> >> Hi Jassi, >> >>> ... a polite ping, Lukasz. >> >> The only excuse for so long lack of my response are my personal issues >> caused by the covid-19. >> > Sorry if I came across as pestering you. I hope all is well now. > > ..... >>>> + >>>> +#define MAX3420_REG_IOPINS 20 >>>> +#define MAX3420_REG_IOPINS2 21 >>>> +#define MAX3420_REG_GPINIRQ 22 >>>> +#define MAX3420_REG_GPINIEN 23 >>>> +#define MAX3420_REG_GPINPOL 24 >>>> +#define MAX3420_REG_HIRQ 25 >>>> +#define MAX3420_REG_HIEN 26 >>>> +#define MAX3420_REG_MODE 27 >>>> +#define MAX3420_REG_PERADDR 28 >>>> +#define MAX3420_REG_HCTL 29 >>>> +#define MAX3420_REG_HXFR 30 >>>> +#define MAX3420_REG_HRSL 31 >>>> + >> >> When I do look into drivers/usb/gadget/f_dfu.* the defines are placed >> in the f_dfu.h file. >> > One school of thought is to contain all code in one file, especially > when no other file should access it -- these defines are very max3420 > specific and none else should need these. > But I am fine if you want them in a separate file. > >>>> +#define field(val, bit) ((val) << (bit)) >>>> + >>>> +#define msleep(a) udelay((a) * 1000) >>>> + >> >> Aren't those two above already defined in some *.h files? >> > I replaced msleep with mdelay as Marek suggested. > I couldn't find the simple shift op define as field. Perhaps check out <linux/bitfield.h>? I believe FIELD_PREP is similar to what you have there. --Sean
On Wed, Jul 29, 2020 at 5:04 AM Sean Anderson <seanga2@gmail.com> wrote: > > On 6/24/20 1:09 AM, Jassi Brar wrote: > > On Tue, Jun 16, 2020 at 2:29 AM Lukasz Majewski <lukma@denx.de> wrote: > >> > >> Hi Jassi, > >> > >>> ... a polite ping, Lukasz. > >> > >> The only excuse for so long lack of my response are my personal issues > >> caused by the covid-19. > >> > > Sorry if I came across as pestering you. I hope all is well now. > > > > ..... > >>>> + > >>>> +#define MAX3420_REG_IOPINS 20 > >>>> +#define MAX3420_REG_IOPINS2 21 > >>>> +#define MAX3420_REG_GPINIRQ 22 > >>>> +#define MAX3420_REG_GPINIEN 23 > >>>> +#define MAX3420_REG_GPINPOL 24 > >>>> +#define MAX3420_REG_HIRQ 25 > >>>> +#define MAX3420_REG_HIEN 26 > >>>> +#define MAX3420_REG_MODE 27 > >>>> +#define MAX3420_REG_PERADDR 28 > >>>> +#define MAX3420_REG_HCTL 29 > >>>> +#define MAX3420_REG_HXFR 30 > >>>> +#define MAX3420_REG_HRSL 31 > >>>> + > >> > >> When I do look into drivers/usb/gadget/f_dfu.* the defines are placed > >> in the f_dfu.h file. > >> > > One school of thought is to contain all code in one file, especially > > when no other file should access it -- these defines are very max3420 > > specific and none else should need these. > > But I am fine if you want them in a separate file. > > > >>>> +#define field(val, bit) ((val) << (bit)) > >>>> + > >>>> +#define msleep(a) udelay((a) * 1000) > >>>> + > >> > >> Aren't those two above already defined in some *.h files? > >> > > I replaced msleep with mdelay as Marek suggested. > > I couldn't find the simple shift op define as field. > > Perhaps check out <linux/bitfield.h>? I believe FIELD_PREP is similar to > what you have there. > One has to go two levels deep to see what FIELD_PREP, as compared to the simple (value << shift) needed in the code. But I am ok doing that, whatever floats the boat. Cheers!
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 46aa3fe954..7c0df5c264 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -105,6 +105,12 @@ config CI_UDC Say Y here to enable device controller functionality of the ChipIdea driver. +config USB_GADGET_MAX3420 + bool "MAX3420 USB Over SPI" + depends on DM_SPI + help + MAX3420, from MAXIM, implements USB-over-SPI Full-Speed device controller. + config USB_GADGET_VBUS_DRAW int "Maximum VBUS Power usage (2-500 mA)" range 2 500 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 70f3bf43e7..f560068b41 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += bcm_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o obj-$(CONFIG_CI_UDC) += ci_udc.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 91b0285244..587204cfb7 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -155,6 +155,12 @@ #define gadget_is_cdns3(g) 0 #endif +#ifdef CONFIG_USB_GADGET_MAX3420 +#define gadget_is_max3420(g) (!strcmp("max3420-udc", (g)->name)) +#else +#define gadget_is_max3420(g) 0 +#endif + /** * usb_gadget_controller_number - support bcdDevice id convention * @gadget: the controller being driven @@ -216,5 +222,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x23; else if (gadget_is_cdns3(gadget)) return 0x24; + else if (gadget_is_max3420(gadget)) + return 0x25; return -ENOENT; } diff --git a/drivers/usb/gadget/max3420_udc.c b/drivers/usb/gadget/max3420_udc.c new file mode 100644 index 0000000000..7267d482b8 --- /dev/null +++ b/drivers/usb/gadget/max3420_udc.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <linux/errno.h> +#include <asm/gpio.h> +#include <linux/list.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <malloc.h> +#include <spi.h> +#include <dm.h> +#include <g_dnl.h> + +#define MAX3420_MAX_EPS 4 +#define EP_MAX_PACKET 64 /* Same for all Endpoints */ +#define EPNAME_SIZE 16 /* Buffer size for endpoint name */ + +#define ACKSTAT BIT(0) + +#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */ +#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */ + +/* SPI commands: */ +#define MAX3420_SPI_DIR_SHIFT 1 +#define MAX3420_SPI_REG_SHIFT 3 + +#define MAX3420_REG_EP0FIFO 0 +#define MAX3420_REG_EP1FIFO 1 +#define MAX3420_REG_EP2FIFO 2 +#define MAX3420_REG_EP3FIFO 3 +#define MAX3420_REG_SUDFIFO 4 +#define MAX3420_REG_EP0BC 5 +#define MAX3420_REG_EP1BC 6 +#define MAX3420_REG_EP2BC 7 +#define MAX3420_REG_EP3BC 8 + +#define MAX3420_REG_EPSTALLS 9 + #define bACKSTAT BIT(6) + #define bSTLSTAT BIT(5) + #define bSTLEP3IN BIT(4) + #define bSTLEP2IN BIT(3) + #define bSTLEP1OUT BIT(2) + #define bSTLEP0OUT BIT(1) + #define bSTLEP0IN BIT(0) + +#define MAX3420_REG_CLRTOGS 10 + #define bEP3DISAB BIT(7) + #define bEP2DISAB BIT(6) + #define bEP1DISAB BIT(5) + #define bCTGEP3IN BIT(4) + #define bCTGEP2IN BIT(3) + #define bCTGEP1OUT BIT(2) + +#define MAX3420_REG_EPIRQ 11 +#define MAX3420_REG_EPIEN 12 + #define bSUDAVIRQ BIT(5) + #define bIN3BAVIRQ BIT(4) + #define bIN2BAVIRQ BIT(3) + #define bOUT1DAVIRQ BIT(2) + #define bOUT0DAVIRQ BIT(1) + #define bIN0BAVIRQ BIT(0) + +#define MAX3420_REG_USBIRQ 13 +#define MAX3420_REG_USBIEN 14 + #define bOSCOKIRQ BIT(0) + #define bRWUDNIRQ BIT(1) + #define bBUSACTIRQ BIT(2) + #define bURESIRQ BIT(3) + #define bSUSPIRQ BIT(4) + #define bNOVBUSIRQ BIT(5) + #define bVBUSIRQ BIT(6) + #define bURESDNIRQ BIT(7) + +#define MAX3420_REG_USBCTL 15 + #define bHOSCSTEN BIT(7) + #define bVBGATE BIT(6) + #define bCHIPRES BIT(5) + #define bPWRDOWN BIT(4) + #define bCONNECT BIT(3) + #define bSIGRWU BIT(2) + +#define MAX3420_REG_CPUCTL 16 + #define bIE BIT(0) + +#define MAX3420_REG_PINCTL 17 + #define bEP3INAK BIT(7) + #define bEP2INAK BIT(6) + #define bEP0INAK BIT(5) + #define bFDUPSPI BIT(4) + #define bINTLEVEL BIT(3) + #define bPOSINT BIT(2) + #define bGPXB BIT(1) + #define bGPXA BIT(0) + +#define MAX3420_REG_REVISION 18 + +#define MAX3420_REG_FNADDR 19 + #define FNADDR_MASK 0x7f + +#define MAX3420_REG_IOPINS 20 +#define MAX3420_REG_IOPINS2 21 +#define MAX3420_REG_GPINIRQ 22 +#define MAX3420_REG_GPINIEN 23 +#define MAX3420_REG_GPINPOL 24 +#define MAX3420_REG_HIRQ 25 +#define MAX3420_REG_HIEN 26 +#define MAX3420_REG_MODE 27 +#define MAX3420_REG_PERADDR 28 +#define MAX3420_REG_HCTL 29 +#define MAX3420_REG_HXFR 30 +#define MAX3420_REG_HRSL 31 + +#define field(val, bit) ((val) << (bit)) + +#define msleep(a) udelay((a) * 1000) + +struct max3420_req { + struct usb_request usb_req; + struct list_head queue; + struct max3420_ep *ep; +}; + +struct max3420_ep { + struct max3420_udc *udc; + struct list_head queue; + char name[EPNAME_SIZE]; + unsigned int maxpacket; + struct usb_ep ep_usb; + int halted; + int id; +}; + +struct max3420_udc { + struct max3420_ep ep[MAX3420_MAX_EPS]; + struct usb_gadget_driver *driver; + bool vbus_active, softconnect; + struct usb_ctrlrequest setup; + struct max3420_req ep0req; + struct usb_gadget gadget; + struct spi_slave *slave; + struct udevice *dev; + u8 ep0buf[64]; + int remote_wkp; + bool suspended; +}; + +#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req) +#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb) +#define to_udc(g) container_of((g), struct max3420_udc, gadget) + +static void spi_ack_ctrl(struct max3420_udc *udc) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[1]; + + txdata[0] = ACKSTAT; + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_ONCE); +} + +static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[2], rxdata[2]; + + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | + field(MAX3420_SPI_DIR_RD, MAX3420_SPI_DIR_SHIFT) | + (actstat ? ACKSTAT : 0); + + rxdata[0] = 0; + rxdata[1] = 0; + spi_xfer(slave, 2 * 8, txdata, rxdata, SPI_XFER_ONCE); + + return rxdata[1]; +} + +static u8 spi_rd8(struct max3420_udc *udc, u8 reg) +{ + return spi_rd8_ack(udc, reg, 0); +} + +static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int actstat) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[2]; + + txdata[0] = field(reg, MAX3420_SPI_REG_SHIFT) | + field(MAX3420_SPI_DIR_WR, MAX3420_SPI_DIR_SHIFT) | + (actstat ? ACKSTAT : 0); + txdata[1] = val; + + spi_xfer(slave, 2 * 8, txdata, NULL, SPI_XFER_ONCE); +} + +static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) +{ + spi_wr8_ack(udc, reg, val, 0); +} + +static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[1]; + + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | + field(MAX3420_SPI_DIR_RD, MAX3420_SPI_DIR_SHIFT)); + + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); + spi_xfer(slave, len * 8, NULL, buf, SPI_XFER_END); +} + +static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) +{ + struct spi_slave *slave = udc->slave; + u8 txdata[1]; + + txdata[0] = (field(reg, MAX3420_SPI_REG_SHIFT) | + field(MAX3420_SPI_DIR_WR, MAX3420_SPI_DIR_SHIFT)); + + spi_xfer(slave, 1 * 8, txdata, NULL, SPI_XFER_BEGIN); + spi_xfer(slave, len * 8, buf, NULL, SPI_XFER_END); +} + +/* 0 if not-connected */ +int g_dnl_board_usb_cable_connected(void) +{ + return 1; +} + +static void spi_max3420_enable(struct max3420_ep *ep, int enable) +{ + struct max3420_udc *udc = ep->udc; + u8 epdis, epien; + + if (ep->id == 0) + return; + + epien = spi_rd8(udc, MAX3420_REG_EPIEN); + epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS); + + if (enable) { + epdis &= ~BIT(ep->id + 4); + epien |= BIT(ep->id + 1); + } else { + epdis |= BIT(ep->id + 4); + epien &= ~BIT(ep->id + 1); + } + + spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis); + spi_wr8(udc, MAX3420_REG_EPIEN, epien); +} + +static int +max3420_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + + _ep->desc = desc; + _ep->maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + + spi_max3420_enable(ep, 1); + + return 0; +} + +static void max3420_req_done(struct max3420_req *req, int status) +{ + struct max3420_ep *ep = req->ep; + + if (req->usb_req.status == -EINPROGRESS) + req->usb_req.status = status; + else + status = req->usb_req.status; + + if (status && status != -ESHUTDOWN) + dev_err(ep->udc->dev, "%s done %p, status %d\n", + ep->ep_usb.name, req, status); + + if (req->usb_req.complete) + req->usb_req.complete(&ep->ep_usb, &req->usb_req); +} + +static void max3420_ep_nuke(struct max3420_ep *ep, int status) +{ + struct max3420_req *req, *r; + + list_for_each_entry_safe(req, r, &ep->queue, queue) { + list_del_init(&req->queue); + max3420_req_done(req, status); + } +} + +static int max3420_ep_disable(struct usb_ep *_ep) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + + _ep->desc = NULL; + max3420_ep_nuke(ep, -ESHUTDOWN); + spi_max3420_enable(ep, 0); + + return 0; +} + +static struct usb_request * +max3420_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + struct max3420_req *req = kzalloc(sizeof(*req), gfp_flags); + + if (!req) + return NULL; + + req->ep = ep; + INIT_LIST_HEAD(&req->queue); + + return &req->usb_req; +} + +static void +max3420_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + kfree(to_max3420_req(_req)); +} + +static int +max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct max3420_req *req = to_max3420_req(_req); + struct max3420_ep *ep = to_max3420_ep(_ep); + + _req->status = -EINPROGRESS; + _req->actual = 0; + list_add_tail(&req->queue, &ep->queue); + + return 0; +} + +static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct max3420_req *req = to_max3420_req(_req); + + list_del_init(&req->queue); + max3420_req_done(req, -ECONNRESET); + + return 0; +} + +static int max3420_ep_set_halt(struct usb_ep *_ep, int halt) +{ + struct max3420_ep *ep = to_max3420_ep(_ep); + struct max3420_udc *udc = ep->udc; + u8 epstalls; + + if (ep->id == 0) /* can't stall EP0 */ + return 0; + + epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS); + if (halt) { + ep->halted = 1; + epstalls |= BIT(ep->id + 1); + } else { + u8 clrtogs; + + ep->halted = 0; + epstalls &= ~BIT(ep->id + 1); + clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS); + clrtogs |= BIT(ep->id + 1); + spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs); + } + spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | bACKSTAT); + + return 0; +} + +static const struct usb_ep_ops max3420_ep_ops = { + .enable = max3420_ep_enable, + .disable = max3420_ep_disable, + .alloc_request = max3420_ep_alloc_request, + .free_request = max3420_ep_free_request, + .queue = max3420_ep_queue, + .dequeue = max3420_ep_dequeue, + .set_halt = max3420_ep_set_halt, +}; + +static void __max3420_stop(struct max3420_udc *udc) +{ + u8 val; + + /* Disable IRQ to CPU */ + spi_wr8(udc, MAX3420_REG_CPUCTL, 0); + + val = spi_rd8(udc, MAX3420_REG_USBCTL); + val |= bPWRDOWN; + val |= bHOSCSTEN; + spi_wr8(udc, MAX3420_REG_USBCTL, val); +} + +static void __max3420_start(struct max3420_udc *udc) +{ + u8 val; + + /* configure SPI */ + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); + + /* Chip Reset */ + spi_wr8(udc, MAX3420_REG_USBCTL, bCHIPRES); + msleep(5); + spi_wr8(udc, MAX3420_REG_USBCTL, 0); + + /* Poll for OSC to stabilize */ + while (1) { + val = spi_rd8(udc, MAX3420_REG_USBIRQ); + if (val & bOSCOKIRQ) + break; + cond_resched(); + } + + /* Enable PULL-UP only when Vbus detected */ + val = spi_rd8(udc, MAX3420_REG_USBCTL); + val |= bVBGATE | bCONNECT; + spi_wr8(udc, MAX3420_REG_USBCTL, val); + + val = bURESDNIRQ | bURESIRQ; + spi_wr8(udc, MAX3420_REG_USBIEN, val); + + /* Enable only EP0 interrupts */ + val = bIN0BAVIRQ | bOUT0DAVIRQ | bSUDAVIRQ; + spi_wr8(udc, MAX3420_REG_EPIEN, val); + + /* Enable IRQ to CPU */ + spi_wr8(udc, MAX3420_REG_CPUCTL, bIE); +} + +static int max3420_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct max3420_udc *udc = to_udc(gadget); + unsigned long flags; + + udc->driver = driver; + udc->remote_wkp = 0; + udc->softconnect = true; + + //if (udc->vbus_active) + __max3420_start(udc); + + return 0; +} + +static int max3420_udc_stop(struct usb_gadget *gadget) +{ + struct max3420_udc *udc = to_udc(gadget); + + udc->driver = NULL; + udc->softconnect = false; + + __max3420_stop(udc); + + return 0; +} + +static int max3420_wakeup(struct usb_gadget *gadget) +{ + struct max3420_udc *udc = to_udc(gadget); + u8 usbctl; + + /* Only if wakeup allowed by host */ + if (!udc->remote_wkp || !udc->suspended) + return 0; + + /* Set Remote-WkUp Signal*/ + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); + usbctl |= bSIGRWU; + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); + + msleep(5); + + /* Clear Remote-WkUp Signal*/ + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); + usbctl &= ~bSIGRWU; + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); + + udc->suspended = false; + + return 0; +} + +static const struct usb_gadget_ops max3420_udc_ops = { + .udc_start = max3420_udc_start, + .udc_stop = max3420_udc_stop, + .wakeup = max3420_wakeup, +}; + +static struct usb_endpoint_descriptor ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET), +}; + +static void max3420_getstatus(struct max3420_udc *udc) +{ + struct max3420_ep *ep; + u16 status = 0; + + switch (udc->setup.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + /* Get device status */ + status = 0 << USB_DEVICE_SELF_POWERED; + status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP); + break; + case USB_RECIP_INTERFACE: + if (udc->driver->setup(&udc->gadget, &udc->setup) < 0) + goto stall; + break; + case USB_RECIP_ENDPOINT: + ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK]; + if (ep->halted) + status = 1 << USB_ENDPOINT_HALT; + break; + default: + goto stall; + } + + status = cpu_to_le16(status); + spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2); + spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1); + return; +stall: + dev_err(udc->dev, "Can't respond to getstatus request\n"); + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); +} + +static void max3420_set_clear_feature(struct max3420_udc *udc) +{ + int set = udc->setup.bRequest == USB_REQ_SET_FEATURE; + struct max3420_ep *ep; + int id; + + switch (udc->setup.bRequestType) { + case USB_RECIP_DEVICE: + if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP) + break; + + if (udc->setup.bRequest == USB_REQ_SET_FEATURE) + udc->remote_wkp = 1; + else + udc->remote_wkp = 0; + + return spi_ack_ctrl(udc); + + case USB_RECIP_ENDPOINT: + if (udc->setup.wValue != USB_ENDPOINT_HALT) + break; + + id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + ep = &udc->ep[id]; + + max3420_ep_set_halt(&ep->ep_usb, set); + return; + default: + break; + } + + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); + spi_wr8(udc, MAX3420_REG_EPSTALLS, bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); +} + +static void max3420_handle_setup(struct max3420_udc *udc) +{ + struct usb_ctrlrequest setup; + u8 addr; + + spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8); + + udc->setup = setup; + udc->setup.wValue = cpu_to_le16(setup.wValue); + udc->setup.wIndex = cpu_to_le16(setup.wIndex); + udc->setup.wLength = cpu_to_le16(setup.wLength); + + switch (udc->setup.bRequest) { + case USB_REQ_GET_STATUS: + /* Data+Status phase form udc */ + if ((udc->setup.bRequestType & + (USB_DIR_IN | USB_TYPE_MASK)) != + (USB_DIR_IN | USB_TYPE_STANDARD)) { + break; + } + return max3420_getstatus(udc); + case USB_REQ_SET_ADDRESS: + /* Status phase from udc */ + if (udc->setup.bRequestType != (USB_DIR_OUT | + USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1); + dev_dbg(udc->dev, "Assigned Address=%d/%d\n", + udc->setup.wValue, addr); + return; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* Requests with no data phase, status phase from udc */ + if ((udc->setup.bRequestType & USB_TYPE_MASK) + != USB_TYPE_STANDARD) + break; + return max3420_set_clear_feature(udc); + default: + break; + } + + if (udc->driver->setup(&udc->gadget, &setup) < 0) { + /* Stall EP0 */ + spi_wr8(udc, MAX3420_REG_EPSTALLS, + bSTLEP0IN | bSTLEP0OUT | bSTLSTAT); + } +} + +static int do_data(struct max3420_udc *udc, int ep_id, int in) +{ + struct max3420_ep *ep = &udc->ep[ep_id]; + struct max3420_req *req; + int done, length, psz; + void *buf; + + if (list_empty(&ep->queue)) + return 0; + + req = list_first_entry(&ep->queue, struct max3420_req, queue); + buf = req->usb_req.buf + req->usb_req.actual; + + psz = ep->ep_usb.maxpacket; + length = req->usb_req.length - req->usb_req.actual; + length = min(length, psz); + + if (length == 0) { + done = 1; + goto xfer_done; + } + + done = 0; + if (in) { + spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); + spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length); + if (length < psz) + done = 1; + } else { + psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id); + length = min(length, psz); + spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); + if (length < ep->ep_usb.maxpacket) + done = 1; + } + + req->usb_req.actual += length; + + if (req->usb_req.actual == req->usb_req.length) + done = 1; + +xfer_done: + if (done) { + unsigned long flags; + + list_del_init(&req->queue); + + if (ep_id == 0) + spi_ack_ctrl(udc); + + max3420_req_done(req, 0); + } + + return 1; +} + +static int max3420_handle_irqs(struct max3420_udc *udc) +{ + u8 epien, epirq, usbirq, usbien, reg[4]; + int ret = 0; + + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4); + epirq = reg[0]; + epien = reg[1]; + usbirq = reg[2]; + usbien = reg[3]; + + usbirq &= usbien; + epirq &= epien; + + if (epirq & bSUDAVIRQ) { + spi_wr8(udc, MAX3420_REG_EPIRQ, bSUDAVIRQ); + max3420_handle_setup(udc); + return 1; + } + + if (usbirq & bVBUSIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bVBUSIRQ); + dev_dbg(udc->dev, "Cable plugged in\n"); + g_dnl_clear_detach(); + return 1; + } + + if (usbirq & bNOVBUSIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bNOVBUSIRQ); + dev_dbg(udc->dev, "Cable pulled out\n"); + g_dnl_trigger_detach(); + return 1; + } + + if (usbirq & bURESIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESIRQ); + return 1; + } + + if (usbirq & bURESDNIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bURESDNIRQ); + spi_wr8(udc, MAX3420_REG_USBIEN, bURESDNIRQ | bURESIRQ); + spi_wr8(udc, MAX3420_REG_EPIEN, bSUDAVIRQ + | bIN0BAVIRQ | bOUT0DAVIRQ); + return 1; + } + + if (usbirq & bSUSPIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bSUSPIRQ); + dev_dbg(udc->dev, "USB Suspend - Enter\n"); + udc->suspended = true; + return 1; + } + + if (usbirq & bBUSACTIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bBUSACTIRQ); + dev_dbg(udc->dev, "USB Suspend - Exit\n"); + udc->suspended = false; + return 1; + } + + if (usbirq & bRWUDNIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bRWUDNIRQ); + dev_dbg(udc->dev, "Asked Host to wakeup\n"); + return 1; + } + + if (usbirq & bOSCOKIRQ) { + spi_wr8(udc, MAX3420_REG_USBIRQ, bOSCOKIRQ); + dev_dbg(udc->dev, "Osc stabilized, start work\n"); + return 1; + } + + if (epirq & bOUT0DAVIRQ && do_data(udc, 0, 0)) { + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT0DAVIRQ, 1); + ret = 1; + } + + if (epirq & bIN0BAVIRQ && do_data(udc, 0, 1)) + ret = 1; + + if (epirq & bOUT1DAVIRQ && do_data(udc, 1, 0)) { + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, bOUT1DAVIRQ, 1); + ret = 1; + } + + if (epirq & bIN2BAVIRQ && do_data(udc, 2, 1)) + ret = 1; + + if (epirq & bIN3BAVIRQ && do_data(udc, 3, 1)) + ret = 1; + + return ret; +} + +static int max3420_irq(struct max3420_udc *udc) +{ + /* needed ? */ + do_data(udc, 0, 1); /* get done with the EP0 ZLP */ + + return max3420_handle_irqs(udc); +} + +static void max3420_setup_eps(struct max3420_udc *udc) +{ + int i; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->ep[0].ep_usb.ep_list); + + for (i = 0; i < MAX3420_MAX_EPS; i++) { + struct max3420_ep *ep = &udc->ep[i]; + + INIT_LIST_HEAD(&ep->queue); + + ep->id = i; + ep->udc = udc; + ep->ep_usb.ops = &max3420_ep_ops; + ep->ep_usb.name = ep->name; + ep->ep_usb.maxpacket = EP_MAX_PACKET; + + if (i == 0) { + ep->ep_usb.desc = &ep0_desc; + snprintf(ep->name, EPNAME_SIZE, "ep0"); + continue; + } + + list_add_tail(&ep->ep_usb.ep_list, &udc->gadget.ep_list); + + if (i == 1) + snprintf(ep->name, EPNAME_SIZE, "ep1out-bulk"); + else + snprintf(ep->name, EPNAME_SIZE, "ep%din-bulk", i); + }; +} + +static void max3420_setup_spi(struct max3420_udc *udc) +{ + u8 reg[8]; + + spi_claim_bus(udc->slave); + /* necessary? */ + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); + /* configure SPI */ + spi_wr8(udc, MAX3420_REG_PINCTL, bFDUPSPI); +} + +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + struct max3420_udc *udc = dev_get_priv(dev); + + return max3420_irq(udc); +} + +static int max3420_udc_probe(struct udevice *dev) +{ + struct max3420_udc *udc = dev_get_priv(dev); + struct udevice *spid; + + spi_get_bus_and_cs(3, 0, 10000000, SPI_MODE_3, "spi_generic_drv", + NULL, &spid, &udc->slave); + + udc->dev = dev; + udc->gadget.ep0 = &udc->ep[0].ep_usb; + udc->gadget.max_speed = USB_SPEED_FULL; + udc->gadget.speed = USB_SPEED_FULL; + udc->gadget.is_dualspeed = 0; + udc->gadget.ops = &max3420_udc_ops; + udc->gadget.name = "max3420-udc"; + + max3420_setup_eps(udc); + max3420_setup_spi(udc); + + usb_add_gadget_udc((struct device *)dev, &udc->gadget); + + return 0; +} + +static int max3420_udc_remove(struct udevice *dev) +{ + struct max3420_udc *udc = dev_get_priv(dev); + + usb_del_gadget_udc(&udc->gadget); + + spi_release_bus(udc->slave); + + return 0; +} + +static const struct udevice_id max3420_ids[] = { + { .compatible = "maxim,max3421-udc" }, + { } +}; + +U_BOOT_DRIVER(max3420_generic_udc) = { + .name = "max3420-udc", + .id = UCLASS_USB_GADGET_GENERIC, + .of_match = max3420_ids, + .probe = max3420_udc_probe, + .remove = max3420_udc_remove, + .priv_auto_alloc_size = sizeof(struct max3420_udc), +};