162306a36Sopenharmony_ci========================= 262306a36Sopenharmony_ciWriting a MUSB Glue Layer 362306a36Sopenharmony_ci========================= 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci:Author: Apelete Seketeli 662306a36Sopenharmony_ci 762306a36Sopenharmony_ciIntroduction 862306a36Sopenharmony_ci============ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciThe Linux MUSB subsystem is part of the larger Linux USB subsystem. It 1162306a36Sopenharmony_ciprovides support for embedded USB Device Controllers (UDC) that do not 1262306a36Sopenharmony_ciuse Universal Host Controller Interface (UHCI) or Open Host Controller 1362306a36Sopenharmony_ciInterface (OHCI). 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciInstead, these embedded UDC rely on the USB On-the-Go (OTG) 1662306a36Sopenharmony_cispecification which they implement at least partially. The silicon 1762306a36Sopenharmony_cireference design used in most cases is the Multipoint USB Highspeed 1862306a36Sopenharmony_ciDual-Role Controller (MUSB HDRC) found in the Mentor Graphics Inventra™ 1962306a36Sopenharmony_cidesign. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciAs a self-taught exercise I have written an MUSB glue layer for the 2262306a36Sopenharmony_ciIngenic JZ4740 SoC, modelled after the many MUSB glue layers in the 2362306a36Sopenharmony_cikernel source tree. This layer can be found at 2462306a36Sopenharmony_ci``drivers/usb/musb/jz4740.c``. In this documentation I will walk through the 2562306a36Sopenharmony_cibasics of the ``jz4740.c`` glue layer, explaining the different pieces and 2662306a36Sopenharmony_ciwhat needs to be done in order to write your own device glue layer. 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci.. _musb-basics: 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciLinux MUSB Basics 3162306a36Sopenharmony_ci================= 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciTo get started on the topic, please read USB On-the-Go Basics (see 3462306a36Sopenharmony_ciResources) which provides an introduction of USB OTG operation at the 3562306a36Sopenharmony_cihardware level. A couple of wiki pages by Texas Instruments and Analog 3662306a36Sopenharmony_ciDevices also provide an overview of the Linux kernel MUSB configuration, 3762306a36Sopenharmony_cialbeit focused on some specific devices provided by these companies. 3862306a36Sopenharmony_ciFinally, getting acquainted with the USB specification at USB home page 3962306a36Sopenharmony_cimay come in handy, with practical instance provided through the Writing 4062306a36Sopenharmony_ciUSB Device Drivers documentation (again, see Resources). 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciLinux USB stack is a layered architecture in which the MUSB controller 4362306a36Sopenharmony_cihardware sits at the lowest. The MUSB controller driver abstract the 4462306a36Sopenharmony_ciMUSB controller hardware to the Linux USB stack:: 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ------------------------ 4762306a36Sopenharmony_ci | | <------- drivers/usb/gadget 4862306a36Sopenharmony_ci | Linux USB Core Stack | <------- drivers/usb/host 4962306a36Sopenharmony_ci | | <------- drivers/usb/core 5062306a36Sopenharmony_ci ------------------------ 5162306a36Sopenharmony_ci ⬍ 5262306a36Sopenharmony_ci -------------------------- 5362306a36Sopenharmony_ci | | <------ drivers/usb/musb/musb_gadget.c 5462306a36Sopenharmony_ci | MUSB Controller driver | <------ drivers/usb/musb/musb_host.c 5562306a36Sopenharmony_ci | | <------ drivers/usb/musb/musb_core.c 5662306a36Sopenharmony_ci -------------------------- 5762306a36Sopenharmony_ci ⬍ 5862306a36Sopenharmony_ci --------------------------------- 5962306a36Sopenharmony_ci | MUSB Platform Specific Driver | 6062306a36Sopenharmony_ci | | <-- drivers/usb/musb/jz4740.c 6162306a36Sopenharmony_ci | aka "Glue Layer" | 6262306a36Sopenharmony_ci --------------------------------- 6362306a36Sopenharmony_ci ⬍ 6462306a36Sopenharmony_ci --------------------------------- 6562306a36Sopenharmony_ci | MUSB Controller Hardware | 6662306a36Sopenharmony_ci --------------------------------- 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciAs outlined above, the glue layer is actually the platform specific code 6962306a36Sopenharmony_cisitting in between the controller driver and the controller hardware. 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciJust like a Linux USB driver needs to register itself with the Linux USB 7262306a36Sopenharmony_cisubsystem, the MUSB glue layer needs first to register itself with the 7362306a36Sopenharmony_ciMUSB controller driver. This will allow the controller driver to know 7462306a36Sopenharmony_ciabout which device the glue layer supports and which functions to call 7562306a36Sopenharmony_ciwhen a supported device is detected or released; remember we are talking 7662306a36Sopenharmony_ciabout an embedded controller chip here, so no insertion or removal at 7762306a36Sopenharmony_cirun-time. 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciAll of this information is passed to the MUSB controller driver through 8062306a36Sopenharmony_cia :c:type:`platform_driver` structure defined in the glue layer as:: 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci static struct platform_driver jz4740_driver = { 8362306a36Sopenharmony_ci .probe = jz4740_probe, 8462306a36Sopenharmony_ci .remove = jz4740_remove, 8562306a36Sopenharmony_ci .driver = { 8662306a36Sopenharmony_ci .name = "musb-jz4740", 8762306a36Sopenharmony_ci }, 8862306a36Sopenharmony_ci }; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciThe probe and remove function pointers are called when a matching device 9162306a36Sopenharmony_ciis detected and, respectively, released. The name string describes the 9262306a36Sopenharmony_cidevice supported by this glue layer. In the current case it matches a 9362306a36Sopenharmony_ciplatform_device structure declared in ``arch/mips/jz4740/platform.c``. Note 9462306a36Sopenharmony_cithat we are not using device tree bindings here. 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciIn order to register itself to the controller driver, the glue layer 9762306a36Sopenharmony_cigoes through a few steps, basically allocating the controller hardware 9862306a36Sopenharmony_ciresources and initialising a couple of circuits. To do so, it needs to 9962306a36Sopenharmony_cikeep track of the information used throughout these steps. This is done 10062306a36Sopenharmony_ciby defining a private ``jz4740_glue`` structure:: 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci struct jz4740_glue { 10362306a36Sopenharmony_ci struct device *dev; 10462306a36Sopenharmony_ci struct platform_device *musb; 10562306a36Sopenharmony_ci struct clk *clk; 10662306a36Sopenharmony_ci }; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciThe dev and musb members are both device structure variables. The first 11062306a36Sopenharmony_cione holds generic information about the device, since it's the basic 11162306a36Sopenharmony_cidevice structure, and the latter holds information more closely related 11262306a36Sopenharmony_cito the subsystem the device is registered to. The clk variable keeps 11362306a36Sopenharmony_ciinformation related to the device clock operation. 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciLet's go through the steps of the probe function that leads the glue 11662306a36Sopenharmony_cilayer to register itself to the controller driver. 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci.. note:: 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci For the sake of readability each function will be split in logical 12162306a36Sopenharmony_ci parts, each part being shown as if it was independent from the others. 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci.. code-block:: c 12462306a36Sopenharmony_ci :emphasize-lines: 8,12,18 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci static int jz4740_probe(struct platform_device *pdev) 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci struct platform_device *musb; 12962306a36Sopenharmony_ci struct jz4740_glue *glue; 13062306a36Sopenharmony_ci struct clk *clk; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); 13462306a36Sopenharmony_ci if (!glue) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); 13862306a36Sopenharmony_ci if (!musb) { 13962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate musb device\n"); 14062306a36Sopenharmony_ci return -ENOMEM; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "udc"); 14462306a36Sopenharmony_ci if (IS_ERR(clk)) { 14562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock\n"); 14662306a36Sopenharmony_ci ret = PTR_ERR(clk); 14762306a36Sopenharmony_ci goto err_platform_device_put; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = clk_prepare_enable(clk); 15162306a36Sopenharmony_ci if (ret) { 15262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock\n"); 15362306a36Sopenharmony_ci goto err_platform_device_put; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci musb->dev.parent = &pdev->dev; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci glue->dev = &pdev->dev; 15962306a36Sopenharmony_ci glue->musb = musb; 16062306a36Sopenharmony_ci glue->clk = clk; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err_platform_device_put: 16562306a36Sopenharmony_ci platform_device_put(musb); 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciThe first few lines of the probe function allocate and assign the glue, 17062306a36Sopenharmony_cimusb and clk variables. The ``GFP_KERNEL`` flag (line 8) allows the 17162306a36Sopenharmony_ciallocation process to sleep and wait for memory, thus being usable in a 17262306a36Sopenharmony_cilocking situation. The ``PLATFORM_DEVID_AUTO`` flag (line 12) allows 17362306a36Sopenharmony_ciautomatic allocation and management of device IDs in order to avoid 17462306a36Sopenharmony_cidevice namespace collisions with explicit IDs. With :c:func:`devm_clk_get` 17562306a36Sopenharmony_ci(line 18) the glue layer allocates the clock -- the ``devm_`` prefix 17662306a36Sopenharmony_ciindicates that :c:func:`clk_get` is managed: it automatically frees the 17762306a36Sopenharmony_ciallocated clock resource data when the device is released -- and enable 17862306a36Sopenharmony_ciit. 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciThen comes the registration steps: 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci.. code-block:: c 18562306a36Sopenharmony_ci :emphasize-lines: 3,5,7,9,16 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci static int jz4740_probe(struct platform_device *pdev) 18862306a36Sopenharmony_ci { 18962306a36Sopenharmony_ci struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci pdata->platform_ops = &jz4740_musb_ops; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci platform_set_drvdata(pdev, glue); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ret = platform_device_add_resources(musb, pdev->resource, 19662306a36Sopenharmony_ci pdev->num_resources); 19762306a36Sopenharmony_ci if (ret) { 19862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to add resources\n"); 19962306a36Sopenharmony_ci goto err_clk_disable; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); 20362306a36Sopenharmony_ci if (ret) { 20462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to add platform_data\n"); 20562306a36Sopenharmony_ci goto err_clk_disable; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci err_clk_disable: 21162306a36Sopenharmony_ci clk_disable_unprepare(clk); 21262306a36Sopenharmony_ci err_platform_device_put: 21362306a36Sopenharmony_ci platform_device_put(musb); 21462306a36Sopenharmony_ci return ret; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciThe first step is to pass the device data privately held by the glue 21862306a36Sopenharmony_cilayer on to the controller driver through :c:func:`platform_set_drvdata` 21962306a36Sopenharmony_ci(line 7). Next is passing on the device resources information, also privately 22062306a36Sopenharmony_ciheld at that point, through :c:func:`platform_device_add_resources` (line 9). 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciFinally comes passing on the platform specific data to the controller 22362306a36Sopenharmony_cidriver (line 16). Platform data will be discussed in 22462306a36Sopenharmony_ci:ref:`musb-dev-platform-data`, but here we are looking at the 22562306a36Sopenharmony_ci``platform_ops`` function pointer (line 5) in ``musb_hdrc_platform_data`` 22662306a36Sopenharmony_cistructure (line 3). This function pointer allows the MUSB controller 22762306a36Sopenharmony_cidriver to know which function to call for device operation:: 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci static const struct musb_platform_ops jz4740_musb_ops = { 23062306a36Sopenharmony_ci .init = jz4740_musb_init, 23162306a36Sopenharmony_ci .exit = jz4740_musb_exit, 23262306a36Sopenharmony_ci }; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciHere we have the minimal case where only init and exit functions are 23562306a36Sopenharmony_cicalled by the controller driver when needed. Fact is the JZ4740 MUSB 23662306a36Sopenharmony_cicontroller is a basic controller, lacking some features found in other 23762306a36Sopenharmony_cicontrollers, otherwise we may also have pointers to a few other 23862306a36Sopenharmony_cifunctions like a power management function or a function to switch 23962306a36Sopenharmony_cibetween OTG and non-OTG modes, for instance. 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciAt that point of the registration process, the controller driver 24262306a36Sopenharmony_ciactually calls the init function: 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci .. code-block:: c 24562306a36Sopenharmony_ci :emphasize-lines: 12,14 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci static int jz4740_musb_init(struct musb *musb) 24862306a36Sopenharmony_ci { 24962306a36Sopenharmony_ci musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); 25062306a36Sopenharmony_ci if (!musb->xceiv) { 25162306a36Sopenharmony_ci pr_err("HS UDC: no transceiver configured\n"); 25262306a36Sopenharmony_ci return -ENODEV; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Silicon does not implement ConfigData register. 25662306a36Sopenharmony_ci * Set dyn_fifo to avoid reading EP config from hardware. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci musb->dyn_fifo = true; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci musb->isr = jz4740_musb_interrupt; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciThe goal of ``jz4740_musb_init()`` is to get hold of the transceiver 26662306a36Sopenharmony_cidriver data of the MUSB controller hardware and pass it on to the MUSB 26762306a36Sopenharmony_cicontroller driver, as usual. The transceiver is the circuitry inside the 26862306a36Sopenharmony_cicontroller hardware responsible for sending/receiving the USB data. 26962306a36Sopenharmony_ciSince it is an implementation of the physical layer of the OSI model, 27062306a36Sopenharmony_cithe transceiver is also referred to as PHY. 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciGetting hold of the ``MUSB PHY`` driver data is done with ``usb_get_phy()`` 27362306a36Sopenharmony_ciwhich returns a pointer to the structure containing the driver instance 27462306a36Sopenharmony_cidata. The next couple of instructions (line 12 and 14) are used as a 27562306a36Sopenharmony_ciquirk and to setup IRQ handling respectively. Quirks and IRQ handling 27662306a36Sopenharmony_ciwill be discussed later in :ref:`musb-dev-quirks` and 27762306a36Sopenharmony_ci:ref:`musb-handling-irqs`\ :: 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci static int jz4740_musb_exit(struct musb *musb) 28062306a36Sopenharmony_ci { 28162306a36Sopenharmony_ci usb_put_phy(musb->xceiv); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciActing as the counterpart of init, the exit function releases the MUSB 28762306a36Sopenharmony_ciPHY driver when the controller hardware itself is about to be released. 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciAgain, note that init and exit are fairly simple in this case due to the 29062306a36Sopenharmony_cibasic set of features of the JZ4740 controller hardware. When writing an 29162306a36Sopenharmony_cimusb glue layer for a more complex controller hardware, you might need 29262306a36Sopenharmony_cito take care of more processing in those two functions. 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciReturning from the init function, the MUSB controller driver jumps back 29562306a36Sopenharmony_ciinto the probe function:: 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci static int jz4740_probe(struct platform_device *pdev) 29862306a36Sopenharmony_ci { 29962306a36Sopenharmony_ci ret = platform_device_add(musb); 30062306a36Sopenharmony_ci if (ret) { 30162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register musb device\n"); 30262306a36Sopenharmony_ci goto err_clk_disable; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci err_clk_disable: 30862306a36Sopenharmony_ci clk_disable_unprepare(clk); 30962306a36Sopenharmony_ci err_platform_device_put: 31062306a36Sopenharmony_ci platform_device_put(musb); 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ciThis is the last part of the device registration process where the glue 31562306a36Sopenharmony_cilayer adds the controller hardware device to Linux kernel device 31662306a36Sopenharmony_cihierarchy: at this stage, all known information about the device is 31762306a36Sopenharmony_cipassed on to the Linux USB core stack: 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci .. code-block:: c 32062306a36Sopenharmony_ci :emphasize-lines: 5,6 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci static int jz4740_remove(struct platform_device *pdev) 32362306a36Sopenharmony_ci { 32462306a36Sopenharmony_ci struct jz4740_glue *glue = platform_get_drvdata(pdev); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci platform_device_unregister(glue->musb); 32762306a36Sopenharmony_ci clk_disable_unprepare(glue->clk); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ciActing as the counterpart of probe, the remove function unregister the 33362306a36Sopenharmony_ciMUSB controller hardware (line 5) and disable the clock (line 6), 33462306a36Sopenharmony_ciallowing it to be gated. 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci.. _musb-handling-irqs: 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ciHandling IRQs 33962306a36Sopenharmony_ci============= 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ciAdditionally to the MUSB controller hardware basic setup and 34262306a36Sopenharmony_ciregistration, the glue layer is also responsible for handling the IRQs: 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci .. code-block:: c 34562306a36Sopenharmony_ci :emphasize-lines: 7,9-11,14,24 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) 34862306a36Sopenharmony_ci { 34962306a36Sopenharmony_ci unsigned long flags; 35062306a36Sopenharmony_ci irqreturn_t retval = IRQ_NONE; 35162306a36Sopenharmony_ci struct musb *musb = __hci; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 35662306a36Sopenharmony_ci musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 35762306a36Sopenharmony_ci musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * The controller is gadget only, the state of the host mode IRQ bits is 36162306a36Sopenharmony_ci * undefined. Mask them to make sure that the musb driver core will 36262306a36Sopenharmony_ci * never see them set 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | 36562306a36Sopenharmony_ci MUSB_INTR_RESET | MUSB_INTR_SOF; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (musb->int_usb || musb->int_tx || musb->int_rx) 36862306a36Sopenharmony_ci retval = musb_interrupt(musb); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return retval; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciHere the glue layer mostly has to read the relevant hardware registers 37662306a36Sopenharmony_ciand pass their values on to the controller driver which will handle the 37762306a36Sopenharmony_ciactual event that triggered the IRQ. 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciThe interrupt handler critical section is protected by the 38062306a36Sopenharmony_ci:c:func:`spin_lock_irqsave` and counterpart :c:func:`spin_unlock_irqrestore` 38162306a36Sopenharmony_cifunctions (line 7 and 24 respectively), which prevent the interrupt 38262306a36Sopenharmony_cihandler code to be run by two different threads at the same time. 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ciThen the relevant interrupt registers are read (line 9 to 11): 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci- ``MUSB_INTRUSB``: indicates which USB interrupts are currently active, 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci- ``MUSB_INTRTX``: indicates which of the interrupts for TX endpoints are 38962306a36Sopenharmony_ci currently active, 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci- ``MUSB_INTRRX``: indicates which of the interrupts for TX endpoints are 39262306a36Sopenharmony_ci currently active. 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ciNote that :c:func:`musb_readb` is used to read 8-bit registers at most, while 39562306a36Sopenharmony_ci:c:func:`musb_readw` allows us to read at most 16-bit registers. There are 39662306a36Sopenharmony_ciother functions that can be used depending on the size of your device 39762306a36Sopenharmony_ciregisters. See ``musb_io.h`` for more information. 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ciInstruction on line 18 is another quirk specific to the JZ4740 USB 40062306a36Sopenharmony_cidevice controller, which will be discussed later in :ref:`musb-dev-quirks`. 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciThe glue layer still needs to register the IRQ handler though. Remember 40362306a36Sopenharmony_cithe instruction on line 14 of the init function:: 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci static int jz4740_musb_init(struct musb *musb) 40662306a36Sopenharmony_ci { 40762306a36Sopenharmony_ci musb->isr = jz4740_musb_interrupt; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciThis instruction sets a pointer to the glue layer IRQ handler function, 41362306a36Sopenharmony_ciin order for the controller hardware to call the handler back when an 41462306a36Sopenharmony_ciIRQ comes from the controller hardware. The interrupt handler is now 41562306a36Sopenharmony_ciimplemented and registered. 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci.. _musb-dev-platform-data: 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciDevice Platform Data 42062306a36Sopenharmony_ci==================== 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ciIn order to write an MUSB glue layer, you need to have some data 42362306a36Sopenharmony_cidescribing the hardware capabilities of your controller hardware, which 42462306a36Sopenharmony_ciis called the platform data. 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ciPlatform data is specific to your hardware, though it may cover a broad 42762306a36Sopenharmony_cirange of devices, and is generally found somewhere in the ``arch/`` 42862306a36Sopenharmony_cidirectory, depending on your device architecture. 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciFor instance, platform data for the JZ4740 SoC is found in 43162306a36Sopenharmony_ci``arch/mips/jz4740/platform.c``. In the ``platform.c`` file each device of the 43262306a36Sopenharmony_ciJZ4740 SoC is described through a set of structures. 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ciHere is the part of ``arch/mips/jz4740/platform.c`` that covers the USB 43562306a36Sopenharmony_ciDevice Controller (UDC): 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci .. code-block:: c 43862306a36Sopenharmony_ci :emphasize-lines: 2,7,14-17,21,22,25,26,28,29 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* USB Device Controller */ 44162306a36Sopenharmony_ci struct platform_device jz4740_udc_xceiv_device = { 44262306a36Sopenharmony_ci .name = "usb_phy_gen_xceiv", 44362306a36Sopenharmony_ci .id = 0, 44462306a36Sopenharmony_ci }; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci static struct resource jz4740_udc_resources[] = { 44762306a36Sopenharmony_ci [0] = { 44862306a36Sopenharmony_ci .start = JZ4740_UDC_BASE_ADDR, 44962306a36Sopenharmony_ci .end = JZ4740_UDC_BASE_ADDR + 0x10000 - 1, 45062306a36Sopenharmony_ci .flags = IORESOURCE_MEM, 45162306a36Sopenharmony_ci }, 45262306a36Sopenharmony_ci [1] = { 45362306a36Sopenharmony_ci .start = JZ4740_IRQ_UDC, 45462306a36Sopenharmony_ci .end = JZ4740_IRQ_UDC, 45562306a36Sopenharmony_ci .flags = IORESOURCE_IRQ, 45662306a36Sopenharmony_ci .name = "mc", 45762306a36Sopenharmony_ci }, 45862306a36Sopenharmony_ci }; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci struct platform_device jz4740_udc_device = { 46162306a36Sopenharmony_ci .name = "musb-jz4740", 46262306a36Sopenharmony_ci .id = -1, 46362306a36Sopenharmony_ci .dev = { 46462306a36Sopenharmony_ci .dma_mask = &jz4740_udc_device.dev.coherent_dma_mask, 46562306a36Sopenharmony_ci .coherent_dma_mask = DMA_BIT_MASK(32), 46662306a36Sopenharmony_ci }, 46762306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(jz4740_udc_resources), 46862306a36Sopenharmony_ci .resource = jz4740_udc_resources, 46962306a36Sopenharmony_ci }; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciThe ``jz4740_udc_xceiv_device`` platform device structure (line 2) 47262306a36Sopenharmony_cidescribes the UDC transceiver with a name and id number. 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciAt the time of this writing, note that ``usb_phy_gen_xceiv`` is the 47562306a36Sopenharmony_cispecific name to be used for all transceivers that are either built-in 47662306a36Sopenharmony_ciwith reference USB IP or autonomous and doesn't require any PHY 47762306a36Sopenharmony_ciprogramming. You will need to set ``CONFIG_NOP_USB_XCEIV=y`` in the 47862306a36Sopenharmony_cikernel configuration to make use of the corresponding transceiver 47962306a36Sopenharmony_cidriver. The id field could be set to -1 (equivalent to 48062306a36Sopenharmony_ci``PLATFORM_DEVID_NONE``), -2 (equivalent to ``PLATFORM_DEVID_AUTO``) or 48162306a36Sopenharmony_cistart with 0 for the first device of this kind if we want a specific id 48262306a36Sopenharmony_cinumber. 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciThe ``jz4740_udc_resources`` resource structure (line 7) defines the UDC 48562306a36Sopenharmony_ciregisters base addresses. 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ciThe first array (line 9 to 11) defines the UDC registers base memory 48862306a36Sopenharmony_ciaddresses: start points to the first register memory address, end points 48962306a36Sopenharmony_cito the last register memory address and the flags member defines the 49062306a36Sopenharmony_citype of resource we are dealing with. So ``IORESOURCE_MEM`` is used to 49162306a36Sopenharmony_cidefine the registers memory addresses. The second array (line 14 to 17) 49262306a36Sopenharmony_cidefines the UDC IRQ registers addresses. Since there is only one IRQ 49362306a36Sopenharmony_ciregister available for the JZ4740 UDC, start and end point at the same 49462306a36Sopenharmony_ciaddress. The ``IORESOURCE_IRQ`` flag tells that we are dealing with IRQ 49562306a36Sopenharmony_ciresources, and the name ``mc`` is in fact hard-coded in the MUSB core in 49662306a36Sopenharmony_ciorder for the controller driver to retrieve this IRQ resource by 49762306a36Sopenharmony_ciquerying it by its name. 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciFinally, the ``jz4740_udc_device`` platform device structure (line 21) 50062306a36Sopenharmony_cidescribes the UDC itself. 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciThe ``musb-jz4740`` name (line 22) defines the MUSB driver that is used 50362306a36Sopenharmony_cifor this device; remember this is in fact the name that we used in the 50462306a36Sopenharmony_ci``jz4740_driver`` platform driver structure in :ref:`musb-basics`. 50562306a36Sopenharmony_ciThe id field (line 23) is set to -1 (equivalent to ``PLATFORM_DEVID_NONE``) 50662306a36Sopenharmony_cisince we do not need an id for the device: the MUSB controller driver was 50762306a36Sopenharmony_cialready set to allocate an automatic id in :ref:`musb-basics`. In the dev field 50862306a36Sopenharmony_ciwe care for DMA related information here. The ``dma_mask`` field (line 25) 50962306a36Sopenharmony_cidefines the width of the DMA mask that is going to be used, and 51062306a36Sopenharmony_ci``coherent_dma_mask`` (line 26) has the same purpose but for the 51162306a36Sopenharmony_ci``alloc_coherent`` DMA mappings: in both cases we are using a 32 bits mask. 51262306a36Sopenharmony_ciThen the resource field (line 29) is simply a pointer to the resource 51362306a36Sopenharmony_cistructure defined before, while the ``num_resources`` field (line 28) keeps 51462306a36Sopenharmony_citrack of the number of arrays defined in the resource structure (in this 51562306a36Sopenharmony_cicase there were two resource arrays defined before). 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ciWith this quick overview of the UDC platform data at the ``arch/`` level now 51862306a36Sopenharmony_cidone, let's get back to the MUSB glue layer specific platform data in 51962306a36Sopenharmony_ci``drivers/usb/musb/jz4740.c``: 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci .. code-block:: c 52262306a36Sopenharmony_ci :emphasize-lines: 3,5,7-9,11 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci static struct musb_hdrc_config jz4740_musb_config = { 52562306a36Sopenharmony_ci /* Silicon does not implement USB OTG. */ 52662306a36Sopenharmony_ci .multipoint = 0, 52762306a36Sopenharmony_ci /* Max EPs scanned, driver will decide which EP can be used. */ 52862306a36Sopenharmony_ci .num_eps = 4, 52962306a36Sopenharmony_ci /* RAMbits needed to configure EPs from table */ 53062306a36Sopenharmony_ci .ram_bits = 9, 53162306a36Sopenharmony_ci .fifo_cfg = jz4740_musb_fifo_cfg, 53262306a36Sopenharmony_ci .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), 53362306a36Sopenharmony_ci }; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci static struct musb_hdrc_platform_data jz4740_musb_platform_data = { 53662306a36Sopenharmony_ci .mode = MUSB_PERIPHERAL, 53762306a36Sopenharmony_ci .config = &jz4740_musb_config, 53862306a36Sopenharmony_ci }; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciFirst the glue layer configures some aspects of the controller driver 54162306a36Sopenharmony_cioperation related to the controller hardware specifics. This is done 54262306a36Sopenharmony_cithrough the ``jz4740_musb_config`` :c:type:`musb_hdrc_config` structure. 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ciDefining the OTG capability of the controller hardware, the multipoint 54562306a36Sopenharmony_cimember (line 3) is set to 0 (equivalent to false) since the JZ4740 UDC 54662306a36Sopenharmony_ciis not OTG compatible. Then ``num_eps`` (line 5) defines the number of USB 54762306a36Sopenharmony_ciendpoints of the controller hardware, including endpoint 0: here we have 54862306a36Sopenharmony_ci3 endpoints + endpoint 0. Next is ``ram_bits`` (line 7) which is the width 54962306a36Sopenharmony_ciof the RAM address bus for the MUSB controller hardware. This 55062306a36Sopenharmony_ciinformation is needed when the controller driver cannot automatically 55162306a36Sopenharmony_ciconfigure endpoints by reading the relevant controller hardware 55262306a36Sopenharmony_ciregisters. This issue will be discussed when we get to device quirks in 55362306a36Sopenharmony_ci:ref:`musb-dev-quirks`. Last two fields (line 8 and 9) are also 55462306a36Sopenharmony_ciabout device quirks: ``fifo_cfg`` points to the USB endpoints configuration 55562306a36Sopenharmony_citable and ``fifo_cfg_size`` keeps track of the size of the number of 55662306a36Sopenharmony_cientries in that configuration table. More on that later in 55762306a36Sopenharmony_ci:ref:`musb-dev-quirks`. 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ciThen this configuration is embedded inside ``jz4740_musb_platform_data`` 56062306a36Sopenharmony_ci:c:type:`musb_hdrc_platform_data` structure (line 11): config is a pointer to 56162306a36Sopenharmony_cithe configuration structure itself, and mode tells the controller driver 56262306a36Sopenharmony_ciif the controller hardware may be used as ``MUSB_HOST`` only, 56362306a36Sopenharmony_ci``MUSB_PERIPHERAL`` only or ``MUSB_OTG`` which is a dual mode. 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ciRemember that ``jz4740_musb_platform_data`` is then used to convey 56662306a36Sopenharmony_ciplatform data information as we have seen in the probe function in 56762306a36Sopenharmony_ci:ref:`musb-basics`. 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci.. _musb-dev-quirks: 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciDevice Quirks 57262306a36Sopenharmony_ci============= 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciCompleting the platform data specific to your device, you may also need 57562306a36Sopenharmony_cito write some code in the glue layer to work around some device specific 57662306a36Sopenharmony_cilimitations. These quirks may be due to some hardware bugs, or simply be 57762306a36Sopenharmony_cithe result of an incomplete implementation of the USB On-the-Go 57862306a36Sopenharmony_cispecification. 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ciThe JZ4740 UDC exhibits such quirks, some of which we will discuss here 58162306a36Sopenharmony_cifor the sake of insight even though these might not be found in the 58262306a36Sopenharmony_cicontroller hardware you are working on. 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ciLet's get back to the init function first: 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci .. code-block:: c 58762306a36Sopenharmony_ci :emphasize-lines: 12 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci static int jz4740_musb_init(struct musb *musb) 59062306a36Sopenharmony_ci { 59162306a36Sopenharmony_ci musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); 59262306a36Sopenharmony_ci if (!musb->xceiv) { 59362306a36Sopenharmony_ci pr_err("HS UDC: no transceiver configured\n"); 59462306a36Sopenharmony_ci return -ENODEV; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Silicon does not implement ConfigData register. 59862306a36Sopenharmony_ci * Set dyn_fifo to avoid reading EP config from hardware. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci musb->dyn_fifo = true; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci musb->isr = jz4740_musb_interrupt; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciInstruction on line 12 helps the MUSB controller driver to work around 60862306a36Sopenharmony_cithe fact that the controller hardware is missing registers that are used 60962306a36Sopenharmony_cifor USB endpoints configuration. 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ciWithout these registers, the controller driver is unable to read the 61262306a36Sopenharmony_ciendpoints configuration from the hardware, so we use line 12 instruction 61362306a36Sopenharmony_cito bypass reading the configuration from silicon, and rely on a 61462306a36Sopenharmony_cihard-coded table that describes the endpoints configuration instead:: 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { 61762306a36Sopenharmony_ci { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, 61862306a36Sopenharmony_ci { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, 61962306a36Sopenharmony_ci { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, 62062306a36Sopenharmony_ci }; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ciLooking at the configuration table above, we see that each endpoints is 62362306a36Sopenharmony_cidescribed by three fields: ``hw_ep_num`` is the endpoint number, style is 62462306a36Sopenharmony_ciits direction (either ``FIFO_TX`` for the controller driver to send packets 62562306a36Sopenharmony_ciin the controller hardware, or ``FIFO_RX`` to receive packets from 62662306a36Sopenharmony_cihardware), and maxpacket defines the maximum size of each data packet 62762306a36Sopenharmony_cithat can be transmitted over that endpoint. Reading from the table, the 62862306a36Sopenharmony_cicontroller driver knows that endpoint 1 can be used to send and receive 62962306a36Sopenharmony_ciUSB data packets of 512 bytes at once (this is in fact a bulk in/out 63062306a36Sopenharmony_ciendpoint), and endpoint 2 can be used to send data packets of 64 bytes 63162306a36Sopenharmony_ciat once (this is in fact an interrupt endpoint). 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ciNote that there is no information about endpoint 0 here: that one is 63462306a36Sopenharmony_ciimplemented by default in every silicon design, with a predefined 63562306a36Sopenharmony_ciconfiguration according to the USB specification. For more examples of 63662306a36Sopenharmony_ciendpoint configuration tables, see ``musb_core.c``. 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ciLet's now get back to the interrupt handler function: 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci .. code-block:: c 64162306a36Sopenharmony_ci :emphasize-lines: 18-19 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) 64462306a36Sopenharmony_ci { 64562306a36Sopenharmony_ci unsigned long flags; 64662306a36Sopenharmony_ci irqreturn_t retval = IRQ_NONE; 64762306a36Sopenharmony_ci struct musb *musb = __hci; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 65262306a36Sopenharmony_ci musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 65362306a36Sopenharmony_ci musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* 65662306a36Sopenharmony_ci * The controller is gadget only, the state of the host mode IRQ bits is 65762306a36Sopenharmony_ci * undefined. Mask them to make sure that the musb driver core will 65862306a36Sopenharmony_ci * never see them set 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | 66162306a36Sopenharmony_ci MUSB_INTR_RESET | MUSB_INTR_SOF; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (musb->int_usb || musb->int_tx || musb->int_rx) 66462306a36Sopenharmony_ci retval = musb_interrupt(musb); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci return retval; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ciInstruction on line 18 above is a way for the controller driver to work 67262306a36Sopenharmony_ciaround the fact that some interrupt bits used for USB host mode 67362306a36Sopenharmony_cioperation are missing in the ``MUSB_INTRUSB`` register, thus left in an 67462306a36Sopenharmony_ciundefined hardware state, since this MUSB controller hardware is used in 67562306a36Sopenharmony_ciperipheral mode only. As a consequence, the glue layer masks these 67662306a36Sopenharmony_cimissing bits out to avoid parasite interrupts by doing a logical AND 67762306a36Sopenharmony_cioperation between the value read from ``MUSB_INTRUSB`` and the bits that 67862306a36Sopenharmony_ciare actually implemented in the register. 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ciThese are only a couple of the quirks found in the JZ4740 USB device 68162306a36Sopenharmony_cicontroller. Some others were directly addressed in the MUSB core since 68262306a36Sopenharmony_cithe fixes were generic enough to provide a better handling of the issues 68362306a36Sopenharmony_cifor others controller hardware eventually. 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ciConclusion 68662306a36Sopenharmony_ci========== 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ciWriting a Linux MUSB glue layer should be a more accessible task, as 68962306a36Sopenharmony_cithis documentation tries to show the ins and outs of this exercise. 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ciThe JZ4740 USB device controller being fairly simple, I hope its glue 69262306a36Sopenharmony_cilayer serves as a good example for the curious mind. Used with the 69362306a36Sopenharmony_cicurrent MUSB glue layers, this documentation should provide enough 69462306a36Sopenharmony_ciguidance to get started; should anything gets out of hand, the linux-usb 69562306a36Sopenharmony_cimailing list archive is another helpful resource to browse through. 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ciAcknowledgements 69862306a36Sopenharmony_ci================ 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ciMany thanks to Lars-Peter Clausen and Maarten ter Huurne for answering 70162306a36Sopenharmony_cimy questions while I was writing the JZ4740 glue layer and for helping 70262306a36Sopenharmony_cime out getting the code in good shape. 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciI would also like to thank the Qi-Hardware community at large for its 70562306a36Sopenharmony_cicheerful guidance and support. 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciResources 70862306a36Sopenharmony_ci========= 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciUSB Home Page: https://www.usb.org 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cilinux-usb Mailing List Archives: https://marc.info/?l=linux-usb 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciUSB On-the-Go Basics: 71562306a36Sopenharmony_cihttps://www.maximintegrated.com/app-notes/index.mvp/id/1822 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci:ref:`Writing USB Device Drivers <writing-usb-driver>` 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciTexas Instruments USB Configuration Wiki Page: 72062306a36Sopenharmony_cihttp://processors.wiki.ti.com/index.php/Usbgeneralpage 721