Hi Felipe, I'm sorry for the misunderstanding from our side. We will implement the glue layer for our Exynos SoC and reuse the dwc3 driver. Thank you. Felipe Balbi wrote: > Hi, > > On Mon, Feb 06, 2012 at 05:13:28PM +0900, Anton Tikhomirov wrote: > > Cc: Kukjin Kim <kgene.kim <at> samsung.com> > > Cc: Greg Kroah-Hartman <gregkh <at> suse.de> > > Cc: Felipe Balbi <balbi <at> ti.com> > > you can add the correct email, actually, but they should follow your > Signed-off-by line. > > > > > Adds Exynos USB3.0 device controller driver to support USB peripherals > > on Exynos5 chips. > > > > Signed-off-by: Anton Tikhomirov <av.tikhomirov@xxxxxxxxxxx> > > --- > > drivers/usb/gadget/Kconfig | 13 + > > drivers/usb/gadget/Makefile | 1 + > > drivers/usb/gadget/exynos_ss_udc.c | 2511 > > ++++++++++++++++++++++++++++++++++++ > > drivers/usb/gadget/exynos_ss_udc.h | 342 +++++ > > 4 files changed, 2867 insertions(+), 0 deletions(-) create mode > > 100644 drivers/usb/gadget/exynos_ss_udc.c > > create mode 100644 drivers/usb/gadget/exynos_ss_udc.h > > > > diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig > > index 7ecb68a..8d3bc6e 100644 > > --- a/drivers/usb/gadget/Kconfig > > +++ b/drivers/usb/gadget/Kconfig > > @@ -268,6 +268,19 @@ config USB_S3C_HSOTG > > The Samsung S3C64XX USB2.0 high-speed gadget controller > > integrated into the S3C64XX series SoC. > > > > +config USB_EXYNOS_SS_UDC > > + tristate "EXYNOS SuperSpeed USB 3.0 Device controller" > > + depends on EXYNOS_DEV_SS_UDC > > + select USB_GADGET_DUALSPEED > > + select USB_GADGET_SUPERSPEED > > + help > > + The Samsung Exynos SuperSpeed USB 3.0 device controller > > + integrated into the Exynos5 series SoC. > > + > > + Say "y" to link the driver statically, or "m" to build a > > + dynamically linked module called "exynos_ss_udc" and force all > > + gadget drivers to also be dynamically linked. > > + > > config USB_IMX > > tristate "Freescale i.MX1 USB Peripheral Controller" > > depends on ARCH_MXC > > diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile > > index b7f6eef..092bc76 100644 > > --- a/drivers/usb/gadget/Makefile > > +++ b/drivers/usb/gadget/Makefile > > @@ -25,6 +25,7 @@ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o > > obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o > > obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o > > obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o > > +obj-$(CONFIG_USB_EXYNOS_SS_UDC) += exynos_ss_udc.o > > obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o > > obj-$(CONFIG_USB_EG20T) += pch_udc.o > > obj-$(CONFIG_USB_MV_UDC) += mv_udc.o > > diff --git a/drivers/usb/gadget/exynos_ss_udc.c > > b/drivers/usb/gadget/exynos_ss_udc.c > > new file mode 100644 > > index 0000000..aaad33c > > --- /dev/null > > +++ b/drivers/usb/gadget/exynos_ss_udc.c > > @@ -0,0 +1,2511 @@ > > +/* linux/drivers/usb/gadget/exynos_ss_udc.c > > + * > > + * Copyright 2011 Samsung Electronics Co., Ltd. > > + * http://www.samsung.com/ > > + * > > + * EXYNOS SuperSpeed USB 3.0 Device Controlle driver > > + * > > + * This program is free software; you can redistribute it and/or > > +modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + */ > > + > > +#include <linux/kernel.h> > > +#include <linux/module.h> > > +#include <linux/spinlock.h> > > +#include <linux/interrupt.h> > > +#include <linux/platform_device.h> > > +#include <linux/dma-mapping.h> > > +#include <linux/delay.h> > > +#include <linux/io.h> > > +#include <linux/slab.h> > > +#include <linux/clk.h> > > + > > +#include <linux/usb/ch9.h> > > +#include <linux/usb/gadget.h> > > + > > +#include <asm/byteorder.h> > > + > > +#include <mach/map.h> > > + > > +#include <plat/regs-usb3-exynos-drd.h> #include <plat/udc-ss.h> > > +#include <plat/usb-phy.h> > > do _NOT_ depend on any arch-specific crap please. Registers can be defined > on the C source code and other stuff could sit on <linux/usb/> or > <linux/platform_data/> depending on what it is. I want to be able to > compile this with whatever (cross) compiler I have available. > > > +#include "exynos_ss_udc.h" > > + > > +static void exynos_ss_udc_kill_all_requests(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep, > > + int result); > > +static void exynos_ss_udc_complete_setup(struct usb_ep *ep, > > + struct usb_request *req); > > +static void exynos_ss_udc_complete_request(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep, > > + struct exynos_ss_udc_req *udc_req, > > + int result); > > +static void exynos_ss_udc_start_req(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep, > > + struct exynos_ss_udc_req *udc_req, > > + bool continuing); > > +static void exynos_ss_udc_ep_activate(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep); static > void > > +exynos_ss_udc_ep_deactivate(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep); static > int > > +exynos_ss_udc_start(struct usb_gadget *gadget, > > + struct usb_gadget_driver *driver); static int > > +exynos_ss_udc_stop(struct usb_gadget *gadget, > > + struct usb_gadget_driver *driver); > > if you reorder the driver, you will not need to prototype everything here. > > > + > > + > > one blank line is enough. > > > +/** > > + * on_list - check request is on the given endpoint > > + * @ep: The endpoint to check. > > + * @test: The request to test if it is on the endpoint. > > + */ > > +static bool on_list(struct exynos_ss_udc_ep *udc_ep, > > + struct exynos_ss_udc_req *test) { > > + struct exynos_ss_udc_req *udc_req, *treq; > > + > > + list_for_each_entry_safe(udc_req, treq, &udc_ep->queue, queue) { > > + if (udc_req == test) > > + return true; > > + } > > + > > + return false; > > +} > > why don't you just hold a pointer to the endpoint on your > exynos_ss_udc_req structure ? Set that pointer on usb_ep_alloc() time and > all you have to do is compare a pointer, rather than traversing the entire > ep list. > > > +/** > > + * exynos_ss_udc_map_dma - map the DMA memory being used for the > > +request > > + * @udc: The device state. > > + * @udc_ep: The endpoint the request is on. > > + * @req: The request being processed. > > + * > > + * We've been asked to queue a request, so ensure that the memory > > +buffer > > + * is correctly setup for DMA. If we've been passed an extant DMA > > +address > > + * then ensure the buffer has been synced to memory. If our buffer > > +has no > > + * DMA memory, then we map the memory and mark our request to allow > > +us to > > + * cleanup on completion. > > + */ > > +static int exynos_ss_udc_map_dma(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep, > > + struct usb_request *req) > > +{ > > + enum dma_data_direction dir; > > + struct exynos_ss_udc_req *udc_req = our_req(req); > > + > > + dir = udc_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; > > + > > + /* if the length is zero, ignore the DMA data */ > > + if (udc_req->req.length == 0) > > + return 0; > > + > > + if (req->dma == DMA_ADDR_INVALID) { > > NAK, we want to remove this. Gadget drivers should _never_ map buffer by > themselves. Just go ahead and map it. > > > + dma_addr_t dma; > > + > > + dma = dma_map_single(udc->dev, > > + req->buf, req->length, dir); > > + > > + if (unlikely(dma_mapping_error(udc->dev, dma))) > > + goto dma_error; > > + > > + udc_req->mapped = 1; > > + req->dma = dma; > > + } else > > + dma_sync_single_for_device(udc->dev, > > + req->dma, req->length, dir); > > if one branch has {}, both should have, but this will be dropped because > of reason above. > > > + > > + return 0; > > beware that we will be providing generic map/unmap routines soonish. > > > +dma_error: > > + dev_err(udc->dev, "%s: failed to map buffer %p, %d bytes\n", > > + __func__, req->buf, req->length); > > + > > + return -EIO; > > +} > > + > > +/** > > + * exynos_ss_udc_unmap_dma - unmap the DMA memory being used for the > > +request > > + * @udc: The device state. > > + * @udc_ep: The endpoint for the request. > > + * @udc_req: The request being processed. > > + * > > + * This is the reverse of exynos_ss_udc_map_dma(), called for the > > +completion > > + * of a request to ensure the buffer is ready for access by the caller. > > + */ > > +static void exynos_ss_udc_unmap_dma(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep *udc_ep, > > + struct exynos_ss_udc_req *udc_req) { > > + struct usb_request *req = &udc_req->req; > > + enum dma_data_direction dir; > > + > > + dir = udc_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; > > + > > + /* ignore this if we're not moving any data */ > > + if (udc_req->req.length == 0) > > + return; > > + > > + if (udc_req->mapped) { > > + /* we mapped this, so unmap and remove the dma */ > > + > > + dma_unmap_single(udc->dev, req->dma, req->length, dir); > > + > > + req->dma = DMA_ADDR_INVALID; > > + udc_req->mapped = 0; > > + } > > +} > > + > > +/** > > + * exynos_ss_udc_issue_epcmd - issue physical endpoint-specific > > +command > > + * @udc: The device state. > > + * @epcmd: The command to issue. > > + */ > > +static int exynos_ss_udc_issue_epcmd(struct exynos_ss_udc *udc, > > + struct exynos_ss_udc_ep_command *epcmd) { > > + int res; > > + u32 depcmd; > > + > > + /* If some of parameters are not in use, we will write it anyway > > + for simplification */ > > + writel(epcmd->param0, udc->regs + EXYNOS_USB3_DEPCMDPAR0(epcmd- > >ep)); > > + writel(epcmd->param1, udc->regs + EXYNOS_USB3_DEPCMDPAR1(epcmd- > >ep)); > > + writel(epcmd->param2, udc->regs + > > +EXYNOS_USB3_DEPCMDPAR2(epcmd->ep)); > > + > > + depcmd = epcmd->cmdtyp | epcmd->cmdflags; > > + writel(depcmd, udc->regs + EXYNOS_USB3_DEPCMD(epcmd->ep)); > > + > > + res = poll_bit_clear(udc->regs + EXYNOS_USB3_DEPCMD(epcmd->ep), > > + EXYNOS_USB3_DEPCMDx_CmdAct, > > + 1000); > > + return res; > > +} > > + > > +/** > > + * exynos_ss_udc_run_stop - start/stop the device controller > > +operation > > + * @udc: The device state. > > + * @is_on: The action to take (1 - start, 0 - stop). > > + */ > > +static void exynos_ss_udc_run_stop(struct exynos_ss_udc *udc, int > > +is_on) { > > + int res; > > + > > + if (is_on) { > > + __orr32(udc->regs + EXYNOS_USB3_DCTL, > > + EXYNOS_USB3_DCTL_Run_Stop); > > + res = poll_bit_clear(udc->regs + EXYNOS_USB3_DSTS, > > + EXYNOS_USB3_DSTS_DevCtrlHlt, > > + 1000); > > + } else { > > + __bic32(udc->regs + EXYNOS_USB3_DCTL, > > + EXYNOS_USB3_DCTL_Run_Stop); > > + res = poll_bit_set(udc->regs + EXYNOS_USB3_DSTS, > > + EXYNOS_USB3_DSTS_DevCtrlHlt, > > + 1000); > > + } > > + > > + if (res < 0) > > + dev_dbg(udc->dev, "Failed to %sconnect by software\n", > > + is_on ? "" : "dis"); > > +} > > + > > +/** > > + * exynos_ss_udc_pullup - software-controlled connect/disconnect to > > +USB host > > + * @gadget: The peripheral being connected/disconnected. > > + * @is_on: The action to take (1 - connect, 0 - disconnect). > > + */ > > +static int exynos_ss_udc_pullup(struct usb_gadget *gadget, int is_on) > > +{ > > + struct exynos_ss_udc *udc = our_udc(gadget); > > + > > + exynos_ss_udc_run_stop(udc, is_on); > > + > > + return 0; > > +} > > Ok, I will stop reviewing here. This looks VERY similar to Synopsys DWC3 > controller. Why are you trying to add yet another driver ? Before > reviewing anything else, I want confirmation that you're _NOT_ using > Synopsys' controller. > > Anton, please confirm the information. All commands are the same as > Synopsys' controller, I cannot let you add another driver for the same > controller. Just re-use the dwc3 controller. > > -- > balbi -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html