Hi Felipe, On Mon, Apr-14-2014 at 10:12:56 PM +0200, Apelete Seketeli wrote: > Document the process of writing an musb glue layer by taking the > Ingenic JZ4740 glue layer as an example, as it seems more simple than > most glue layers due to the basic feature set of the JZ4740 USB device > controller. > > Signed-off-by: Apelete Seketeli <apel...@seketeli.net>
This one wasn't part of the pull request for 3.15-rc3, so I was wondering if you are planning to pull it in the next round of fixes (if it qualifies as such) or just waiting for the 3.16 merge window ? Cheers. > --- > Documentation/DocBook/Makefile | 3 +- > Documentation/DocBook/writing_musb_glue_layer.tmpl | 873 > ++++++++++++++++++++ > 2 files changed, 875 insertions(+), 1 deletion(-) > create mode 100644 Documentation/DocBook/writing_musb_glue_layer.tmpl > > diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile > index b444f2e..bec0665 100644 > --- a/Documentation/DocBook/Makefile > +++ b/Documentation/DocBook/Makefile > @@ -14,7 +14,8 @@ DOCBOOKS := z8530book.xml device-drivers.xml \ > genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ > 80211.xml debugobjects.xml sh.xml regulator.xml \ > alsa-driver-api.xml writing-an-alsa-driver.xml \ > - tracepoint.xml drm.xml media_api.xml w1.xml > + tracepoint.xml drm.xml media_api.xml w1.xml \ > + writing_musb_glue_layer.xml > > include Documentation/DocBook/media/Makefile > > diff --git a/Documentation/DocBook/writing_musb_glue_layer.tmpl > b/Documentation/DocBook/writing_musb_glue_layer.tmpl > new file mode 100644 > index 0000000..837eca7 > --- /dev/null > +++ b/Documentation/DocBook/writing_musb_glue_layer.tmpl > @@ -0,0 +1,873 @@ > +<?xml version="1.0" encoding="UTF-8"?> > +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" > + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []> > + > +<book id="Writing-MUSB-Glue-Layer"> > + <bookinfo> > + <title>Writing an MUSB Glue Layer</title> > + > + <authorgroup> > + <author> > + <firstname>Apelete</firstname> > + <surname>Seketeli</surname> > + <affiliation> > + <address> > + <email>apelete at seketeli.net</email> > + </address> > + </affiliation> > + </author> > + </authorgroup> > + > + <copyright> > + <year>2014</year> > + <holder>Apelete Seketeli</holder> > + </copyright> > + > + <legalnotice> > + <para> > + This documentation is free software; you can redistribute it > + and/or modify it under the terms of the GNU General Public > + License as published by the Free Software Foundation; either > + version 2 of the License, or (at your option) any later version. > + </para> > + > + <para> > + This documentation is distributed in the hope that it will be > + useful, but WITHOUT ANY WARRANTY; without even the implied > + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. > + See the GNU General Public License for more details. > + </para> > + > + <para> > + You should have received a copy of the GNU General Public License > + along with this documentation; if not, write to the Free Software > + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA > + 02111-1307 USA > + </para> > + > + <para> > + For more details see the file COPYING in the Linux kernel source > + tree. > + </para> > + </legalnotice> > + </bookinfo> > + > +<toc></toc> > + > + <chapter id="introduction"> > + <title>Introduction</title> > + <para> > + The Linux MUSB subsystem is part of the larger Linux USB > + subsystem. It provides support for embedded USB Device Controllers > + (UDC) that do not use Universal Host Controller Interface (UHCI) > + or Open Host Controller Interface (OHCI). > + </para> > + <para> > + Instead, these embedded UDC rely on the USB On-the-Go (OTG) > + specification which they implement at least partially. The silicon > + reference design used in most cases is the Multipoint USB > + Highspeed Dual-Role Controller (MUSB HDRC) found in the Mentor > + Graphics Inventra™ design. > + </para> > + <para> > + As a self-taught exercise I have written an MUSB glue layer for > + the Ingenic JZ4740 SoC, modelled after the many MUSB glue layers > + in the kernel source tree. This layer can be found at > + drivers/usb/musb/jz4740.c. In this documentation I will walk > + through the basics of the jz4740.c glue layer, explaining the > + different pieces and what needs to be done in order to write your > + own device glue layer. > + </para> > + </chapter> > + > + <chapter id="linux-musb-basics"> > + <title>Linux MUSB Basics</title> > + <para> > + To get started on the topic, please read USB On-the-Go Basics (see > + Resources) which provides an introduction of USB OTG operation at > + the hardware level. A couple of wiki pages by Texas Instruments > + and Analog Devices also provide an overview of the Linux kernel > + MUSB configuration, albeit focused on some specific devices > + provided by these companies. Finally, getting acquainted with the > + USB specification at USB home page may come in handy, with > + practical instance provided through the Writing USB Device Drivers > + documentation (again, see Resources). > + </para> > + <para> > + Linux USB stack is a layered architecture in which the MUSB > + controller hardware sits at the lowest. The MUSB controller driver > + abstract the MUSB controller hardware to the Linux USB stack. > + </para> > + <programlisting> > + ------------------------ > + | | <------- drivers/usb/gadget > + | Linux USB Core Stack | <------- drivers/usb/host > + | | <------- drivers/usb/core > + ------------------------ > + ⬍ > + -------------------------- > + | | <------ drivers/usb/musb/musb_gadget.c > + | MUSB Controller driver | <------ drivers/usb/musb/musb_host.c > + | | <------ drivers/usb/musb/musb_core.c > + -------------------------- > + ⬍ > + --------------------------------- > + | MUSB Platform Specific Driver | > + | | <-- drivers/usb/musb/jz4740.c > + | aka "Glue Layer" | > + --------------------------------- > + ⬍ > + --------------------------------- > + | MUSB Controller Hardware | > + --------------------------------- > + </programlisting> > + <para> > + As outlined above, the glue layer is actually the platform > + specific code sitting in between the controller driver and the > + controller hardware. > + </para> > + <para> > + Just like a Linux USB driver needs to register itself with the > + Linux USB subsystem, the MUSB glue layer needs first to register > + itself with the MUSB controller driver. This will allow the > + controller driver to know about which device the glue layer > + supports and which functions to call when a supported device is > + detected or released; remember we are talking about an embedded > + controller chip here, so no insertion or removal at run-time. > + </para> > + <para> > + All of this information is passed to the MUSB controller driver > + through a platform_driver structure defined in the glue layer as: > + </para> > + <programlisting linenumbering="numbered"> > +static struct platform_driver jz4740_driver = { > + .probe = jz4740_probe, > + .remove = jz4740_remove, > + .driver = { > + .name = "musb-jz4740", > + }, > +}; > + </programlisting> > + <para> > + The probe and remove function pointers are called when a matching > + device is detected and, respectively, released. The name string > + describes the device supported by this glue layer. In the current > + case it matches a platform_device structure declared in > + arch/mips/jz4740/platform.c. Note that we are not using device > + tree bindings here. > + </para> > + <para> > + In order to register itself to the controller driver, the glue > + layer goes through a few steps, basically allocating the > + controller hardware resources and initialising a couple of > + circuits. To do so, it needs to keep track of the information used > + throughout these steps. This is done by defining a private > + jz4740_glue structure: > + </para> > + <programlisting linenumbering="numbered"> > +struct jz4740_glue { > + struct device *dev; > + struct platform_device *musb; > + struct clk *clk; > +}; > + </programlisting> > + <para> > + The dev and musb members are both device structure variables. The > + first one holds generic information about the device, since it's > + the basic device structure, and the latter holds information more > + closely related to the subsystem the device is registered to. The > + clk variable keeps information related to the device clock > + operation. > + </para> > + <para> > + Let's go through the steps of the probe function that leads the > + glue layer to register itself to the controller driver. > + </para> > + <para> > + N.B.: For the sake of readability each function will be split in > + logical parts, each part being shown as if it was independent from > + the others. > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_probe(struct platform_device *pdev) > +{ > + struct platform_device *musb; > + struct jz4740_glue *glue; > + struct clk *clk; > + int ret; > + > + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); > + if (!glue) > + return -ENOMEM; > + > + musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); > + if (!musb) { > + dev_err(&pdev->dev, "failed to allocate musb device\n"); > + return -ENOMEM; > + } > + > + clk = devm_clk_get(&pdev->dev, "udc"); > + if (IS_ERR(clk)) { > + dev_err(&pdev->dev, "failed to get clock\n"); > + ret = PTR_ERR(clk); > + goto err_platform_device_put; > + } > + > + ret = clk_prepare_enable(clk); > + if (ret) { > + dev_err(&pdev->dev, "failed to enable clock\n"); > + goto err_platform_device_put; > + } > + > + musb->dev.parent = &pdev->dev; > + > + glue->dev = &pdev->dev; > + glue->musb = musb; > + glue->clk = clk; > + > + return 0; > + > +err_platform_device_put: > + platform_device_put(musb); > + return ret; > +} > + </programlisting> > + <para> > + The first few lines of the probe function allocate and assign the > + glue, musb and clk variables. The GFP_KERNEL flag (line 8) allows > + the allocation process to sleep and wait for memory, thus being > + usable in a blocking situation. The PLATFORM_DEVID_AUTO flag (line > + 12) allows automatic allocation and management of device IDs in > + order to avoid device namespace collisions with explicit IDs. With > + devm_clk_get() (line 18) the glue layer allocates the clock -- the > + <literal>devm_</literal> prefix indicates that clk_get() is > + managed: it automatically frees the allocated clock resource data > + when the device is released -- and enable it. > + </para> > + <para> > + Then comes the registration steps: > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_probe(struct platform_device *pdev) > +{ > + struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data; > + > + pdata->platform_ops = &jz4740_musb_ops; > + > + platform_set_drvdata(pdev, glue); > + > + ret = platform_device_add_resources(musb, pdev->resource, > + pdev->num_resources); > + if (ret) { > + dev_err(&pdev->dev, "failed to add resources\n"); > + goto err_clk_disable; > + } > + > + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); > + if (ret) { > + dev_err(&pdev->dev, "failed to add platform_data\n"); > + goto err_clk_disable; > + } > + > + return 0; > + > +err_clk_disable: > + clk_disable_unprepare(clk); > +err_platform_device_put: > + platform_device_put(musb); > + return ret; > +} > + </programlisting> > + <para> > + The first step is to pass the device data privately held by the > + glue layer on to the controller driver through > + platform_set_drvdata() (line 7). Next is passing on the device > + resources information, also privately held at that point, through > + platform_device_add_resources() (line 9). > + </para> > + <para> > + Finally comes passing on the platform specific data to the > + controller driver (line 16). Platform data will be discussed in > + <link linkend="device-platform-data">Chapter 4</link>, but here > + we are looking at the platform_ops function pointer (line 5) in > + musb_hdrc_platform_data structure (line 3). This function > + pointer allows the MUSB controller driver to know which function > + to call for device operation: > + </para> > + <programlisting linenumbering="numbered"> > +static const struct musb_platform_ops jz4740_musb_ops = { > + .init = jz4740_musb_init, > + .exit = jz4740_musb_exit, > +}; > + </programlisting> > + <para> > + Here we have the minimal case where only init and exit functions > + are called by the controller driver when needed. Fact is the > + JZ4740 MUSB controller is a basic controller, lacking some > + features found in other controllers, otherwise we may also have > + pointers to a few other functions like a power management function > + or a function to switch between OTG and non-OTG modes, for > + instance. > + </para> > + <para> > + At that point of the registration process, the controller driver > + actually calls the init function: > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_musb_init(struct musb *musb) > +{ > + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); > + if (!musb->xceiv) { > + pr_err("HS UDC: no transceiver configured\n"); > + return -ENODEV; > + } > + > + /* Silicon does not implement ConfigData register. > + * Set dyn_fifo to avoid reading EP config from hardware. > + */ > + musb->dyn_fifo = true; > + > + musb->isr = jz4740_musb_interrupt; > + > + return 0; > +} > + </programlisting> > + <para> > + The goal of jz4740_musb_init() is to get hold of the transceiver > + driver data of the MUSB controller hardware and pass it on to the > + MUSB controller driver, as usual. The transceiver is the circuitry > + inside the controller hardware responsible for sending/receiving > + the USB data. Since it is an implementation of the physical layer > + of the OSI model, the transceiver is also referred to as PHY. > + </para> > + <para> > + Getting hold of the MUSB PHY driver data is done with > + usb_get_phy() which returns a pointer to the structure > + containing the driver instance data. The next couple of > + instructions (line 12 and 14) are used as a quirk and to setup > + IRQ handling respectively. Quirks and IRQ handling will be > + discussed later in <link linkend="device-quirks">Chapter > + 5</link> and <link linkend="handling-irqs">Chapter 3</link>. > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_musb_exit(struct musb *musb) > +{ > + usb_put_phy(musb->xceiv); > + > + return 0; > +} > + </programlisting> > + <para> > + Acting as the counterpart of init, the exit function releases the > + MUSB PHY driver when the controller hardware itself is about to be > + released. > + </para> > + <para> > + Again, note that init and exit are fairly simple in this case due > + to the basic set of features of the JZ4740 controller hardware. > + When writing an musb glue layer for a more complex controller > + hardware, you might need to take care of more processing in those > + two functions. > + </para> > + <para> > + Returning from the init function, the MUSB controller driver jumps > + back into the probe function: > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_probe(struct platform_device *pdev) > +{ > + ret = platform_device_add(musb); > + if (ret) { > + dev_err(&pdev->dev, "failed to register musb device\n"); > + goto err_clk_disable; > + } > + > + return 0; > + > +err_clk_disable: > + clk_disable_unprepare(clk); > +err_platform_device_put: > + platform_device_put(musb); > + return ret; > +} > + </programlisting> > + <para> > + This is the last part of the device registration process where the > + glue layer adds the controller hardware device to Linux kernel > + device hierarchy: at this stage, all known information about the > + device is passed on to the Linux USB core stack. > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_remove(struct platform_device *pdev) > +{ > + struct jz4740_glue *glue = platform_get_drvdata(pdev); > + > + platform_device_unregister(glue->musb); > + clk_disable_unprepare(glue->clk); > + > + return 0; > +} > + </programlisting> > + <para> > + Acting as the counterpart of probe, the remove function unregister > + the MUSB controller hardware (line 5) and disable the clock (line > + 6), allowing it to be gated. > + </para> > + </chapter> > + > + <chapter id="handling-irqs"> > + <title>Handling IRQs</title> > + <para> > + Additionally to the MUSB controller hardware basic setup and > + registration, the glue layer is also responsible for handling the > + IRQs: > + </para> > + <programlisting linenumbering="numbered"> > +static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) > +{ > + unsigned long flags; > + irqreturn_t retval = IRQ_NONE; > + struct musb *musb = __hci; > + > + spin_lock_irqsave(&musb->lock, flags); > + > + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); > + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); > + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); > + > + /* > + * The controller is gadget only, the state of the host mode IRQ bits is > + * undefined. Mask them to make sure that the musb driver core will > + * never see them set > + */ > + musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | > + MUSB_INTR_RESET | MUSB_INTR_SOF; > + > + if (musb->int_usb || musb->int_tx || musb->int_rx) > + retval = musb_interrupt(musb); > + > + spin_unlock_irqrestore(&musb->lock, flags); > + > + return retval; > +} > + </programlisting> > + <para> > + Here the glue layer mostly has to read the relevant hardware > + registers and pass their values on to the controller driver which > + will handle the actual event that triggered the IRQ. > + </para> > + <para> > + The interrupt handler critical section is protected by the > + spin_lock_irqsave() and counterpart spin_unlock_irqrestore() > + functions (line 7 and 24 respectively), which prevent the > + interrupt handler code to be run by two different threads at the > + same time. > + </para> > + <para> > + Then the relevant interrupt registers are read (line 9 to 11): > + </para> > + <itemizedlist> > + <listitem> > + <para> > + MUSB_INTRUSB: indicates which USB interrupts are currently > + active, > + </para> > + </listitem> > + <listitem> > + <para> > + MUSB_INTRTX: indicates which of the interrupts for TX > + endpoints are currently active, > + </para> > + </listitem> > + <listitem> > + <para> > + MUSB_INTRRX: indicates which of the interrupts for TX > + endpoints are currently active. > + </para> > + </listitem> > + </itemizedlist> > + <para> > + Note that musb_readb() is used to read 8-bit registers at most, > + while musb_readw() allows us to read at most 16-bit registers. > + There are other functions that can be used depending on the size > + of your device registers. See musb_io.h for more information. > + </para> > + <para> > + Instruction on line 18 is another quirk specific to the JZ4740 > + USB device controller, which will be discussed later in <link > + linkend="device-quirks">Chapter 5</link>. > + </para> > + <para> > + The glue layer still needs to register the IRQ handler though. > + Remember the instruction on line 14 of the init function: > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_musb_init(struct musb *musb) > +{ > + musb->isr = jz4740_musb_interrupt; > + > + return 0; > +} > + </programlisting> > + <para> > + This instruction sets a pointer to the glue layer IRQ handler > + function, in order for the controller hardware to call the handler > + back when an IRQ comes from the controller hardware. The interrupt > + handler is now implemented and registered. > + </para> > + </chapter> > + > + <chapter id="device-platform-data"> > + <title>Device Platform Data</title> > + <para> > + In order to write an MUSB glue layer, you need to have some data > + describing the hardware capabilities of your controller hardware, > + which is called the platform data. > + </para> > + <para> > + Platform data is specific to your hardware, though it may cover a > + broad range of devices, and is generally found somewhere in the > + arch/ directory, depending on your device architecture. > + </para> > + <para> > + For instance, platform data for the JZ4740 SoC is found in > + arch/mips/jz4740/platform.c. In the platform.c file each device of > + the JZ4740 SoC is described through a set of structures. > + </para> > + <para> > + Here is the part of arch/mips/jz4740/platform.c that covers the > + USB Device Controller (UDC): > + </para> > + <programlisting linenumbering="numbered"> > +/* USB Device Controller */ > +struct platform_device jz4740_udc_xceiv_device = { > + .name = "usb_phy_gen_xceiv", > + .id = 0, > +}; > + > +static struct resource jz4740_udc_resources[] = { > + [0] = { > + .start = JZ4740_UDC_BASE_ADDR, > + .end = JZ4740_UDC_BASE_ADDR + 0x10000 - 1, > + .flags = IORESOURCE_MEM, > + }, > + [1] = { > + .start = JZ4740_IRQ_UDC, > + .end = JZ4740_IRQ_UDC, > + .flags = IORESOURCE_IRQ, > + .name = "mc", > + }, > +}; > + > +struct platform_device jz4740_udc_device = { > + .name = "musb-jz4740", > + .id = -1, > + .dev = { > + .dma_mask = > &jz4740_udc_device.dev.coherent_dma_mask, > + .coherent_dma_mask = DMA_BIT_MASK(32), > + }, > + .num_resources = ARRAY_SIZE(jz4740_udc_resources), > + .resource = jz4740_udc_resources, > +}; > + </programlisting> > + <para> > + The jz4740_udc_xceiv_device platform device structure (line 2) > + describes the UDC transceiver with a name and id number. > + </para> > + <para> > + At the time of this writing, note that > + "usb_phy_gen_xceiv" is the specific name to be used for > + all transceivers that are either built-in with reference USB IP or > + autonomous and doesn't require any PHY programming. You will need > + to set CONFIG_NOP_USB_XCEIV=y in the kernel configuration to make > + use of the corresponding transceiver driver. The id field could be > + set to -1 (equivalent to PLATFORM_DEVID_NONE), -2 (equivalent to > + PLATFORM_DEVID_AUTO) or start with 0 for the first device of this > + kind if we want a specific id number. > + </para> > + <para> > + The jz4740_udc_resources resource structure (line 7) defines the > + UDC registers base addresses. > + </para> > + <para> > + The first array (line 9 to 11) defines the UDC registers base > + memory addresses: start points to the first register memory > + address, end points to the last register memory address and the > + flags member defines the type of resource we are dealing with. So > + IORESOURCE_MEM is used to define the registers memory addresses. > + The second array (line 14 to 17) defines the UDC IRQ registers > + addresses. Since there is only one IRQ register available for the > + JZ4740 UDC, start and end point at the same address. The > + IORESOURCE_IRQ flag tells that we are dealing with IRQ resources, > + and the name "mc" is in fact hard-coded in the MUSB core > + in order for the controller driver to retrieve this IRQ resource > + by querying it by its name. > + </para> > + <para> > + Finally, the jz4740_udc_device platform device structure (line 21) > + describes the UDC itself. > + </para> > + <para> > + The "musb-jz4740" name (line 22) defines the MUSB > + driver that is used for this device; remember this is in fact > + the name that we used in the jz4740_driver platform driver > + structure in <link linkend="linux-musb-basics">Chapter > + 2</link>. The id field (line 23) is set to -1 (equivalent to > + PLATFORM_DEVID_NONE) since we do not need an id for the device: > + the MUSB controller driver was already set to allocate an > + automatic id in <link linkend="linux-musb-basics">Chapter > + 2</link>. In the dev field we care for DMA related information > + here. The dma_mask field (line 25) defines the width of the DMA > + mask that is going to be used, and coherent_dma_mask (line 26) > + has the same purpose but for the alloc_coherent DMA mappings: in > + both cases we are using a 32 bits mask. Then the resource field > + (line 29) is simply a pointer to the resource structure defined > + before, while the num_resources field (line 28) keeps track of > + the number of arrays defined in the resource structure (in this > + case there were two resource arrays defined before). > + </para> > + <para> > + With this quick overview of the UDC platform data at the arch/ > + level now done, let's get back to the MUSB glue layer specific > + platform data in drivers/usb/musb/jz4740.c: > + </para> > + <programlisting linenumbering="numbered"> > +static struct musb_hdrc_config jz4740_musb_config = { > + /* Silicon does not implement USB OTG. */ > + .multipoint = 0, > + /* Max EPs scanned, driver will decide which EP can be used. */ > + .num_eps = 4, > + /* RAMbits needed to configure EPs from table */ > + .ram_bits = 9, > + .fifo_cfg = jz4740_musb_fifo_cfg, > + .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), > +}; > + > +static struct musb_hdrc_platform_data jz4740_musb_platform_data = { > + .mode = MUSB_PERIPHERAL, > + .config = &jz4740_musb_config, > +}; > + </programlisting> > + <para> > + First the glue layer configures some aspects of the controller > + driver operation related to the controller hardware specifics. > + This is done through the jz4740_musb_config musb_hdrc_config > + structure. > + </para> > + <para> > + Defining the OTG capability of the controller hardware, the > + multipoint member (line 3) is set to 0 (equivalent to false) > + since the JZ4740 UDC is not OTG compatible. Then num_eps (line > + 5) defines the number of USB endpoints of the controller > + hardware, including endpoint 0: here we have 3 endpoints + > + endpoint 0. Next is ram_bits (line 7) which is the width of the > + RAM address bus for the MUSB controller hardware. This > + information is needed when the controller driver cannot > + automatically configure endpoints by reading the relevant > + controller hardware registers. This issue will be discussed when > + we get to device quirks in <link linkend="device-quirks">Chapter > + 5</link>. Last two fields (line 8 and 9) are also about device > + quirks: fifo_cfg points to the USB endpoints configuration table > + and fifo_cfg_size keeps track of the size of the number of > + entries in that configuration table. More on that later in <link > + linkend="device-quirks">Chapter 5</link>. > + </para> > + <para> > + Then this configuration is embedded inside > + jz4740_musb_platform_data musb_hdrc_platform_data structure (line > + 11): config is a pointer to the configuration structure itself, > + and mode tells the controller driver if the controller hardware > + may be used as MUSB_HOST only, MUSB_PERIPHERAL only or MUSB_OTG > + which is a dual mode. > + </para> > + <para> > + Remember that jz4740_musb_platform_data is then used to convey > + platform data information as we have seen in the probe function > + in <link linkend="linux-musb-basics">Chapter 2</link> > + </para> > + </chapter> > + > + <chapter id="device-quirks"> > + <title>Device Quirks</title> > + <para> > + Completing the platform data specific to your device, you may also > + need to write some code in the glue layer to work around some > + device specific limitations. These quirks may be due to some > + hardware bugs, or simply be the result of an incomplete > + implementation of the USB On-the-Go specification. > + </para> > + <para> > + The JZ4740 UDC exhibits such quirks, some of which we will discuss > + here for the sake of insight even though these might not be found > + in the controller hardware you are working on. > + </para> > + <para> > + Let's get back to the init function first: > + </para> > + <programlisting linenumbering="numbered"> > +static int jz4740_musb_init(struct musb *musb) > +{ > + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); > + if (!musb->xceiv) { > + pr_err("HS UDC: no transceiver configured\n"); > + return -ENODEV; > + } > + > + /* Silicon does not implement ConfigData register. > + * Set dyn_fifo to avoid reading EP config from hardware. > + */ > + musb->dyn_fifo = true; > + > + musb->isr = jz4740_musb_interrupt; > + > + return 0; > +} > + </programlisting> > + <para> > + Instruction on line 12 helps the MUSB controller driver to work > + around the fact that the controller hardware is missing registers > + that are used for USB endpoints configuration. > + </para> > + <para> > + Without these registers, the controller driver is unable to read > + the endpoints configuration from the hardware, so we use line 12 > + instruction to bypass reading the configuration from silicon, and > + rely on a hard-coded table that describes the endpoints > + configuration instead: > + </para> > + <programlisting linenumbering="numbered"> > +static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { > +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, > +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, > +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, > +}; > + </programlisting> > + <para> > + Looking at the configuration table above, we see that each > + endpoints is described by three fields: hw_ep_num is the endpoint > + number, style is its direction (either FIFO_TX for the controller > + driver to send packets in the controller hardware, or FIFO_RX to > + receive packets from hardware), and maxpacket defines the maximum > + size of each data packet that can be transmitted over that > + endpoint. Reading from the table, the controller driver knows that > + endpoint 1 can be used to send and receive USB data packets of 512 > + bytes at once (this is in fact a bulk in/out endpoint), and > + endpoint 2 can be used to send data packets of 64 bytes at once > + (this is in fact an interrupt endpoint). > + </para> > + <para> > + Note that there is no information about endpoint 0 here: that one > + is implemented by default in every silicon design, with a > + predefined configuration according to the USB specification. For > + more examples of endpoint configuration tables, see musb_core.c. > + </para> > + <para> > + Let's now get back to the interrupt handler function: > + </para> > + <programlisting linenumbering="numbered"> > +static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) > +{ > + unsigned long flags; > + irqreturn_t retval = IRQ_NONE; > + struct musb *musb = __hci; > + > + spin_lock_irqsave(&musb->lock, flags); > + > + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); > + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); > + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); > + > + /* > + * The controller is gadget only, the state of the host mode IRQ bits is > + * undefined. Mask them to make sure that the musb driver core will > + * never see them set > + */ > + musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | > + MUSB_INTR_RESET | MUSB_INTR_SOF; > + > + if (musb->int_usb || musb->int_tx || musb->int_rx) > + retval = musb_interrupt(musb); > + > + spin_unlock_irqrestore(&musb->lock, flags); > + > + return retval; > +} > + </programlisting> > + <para> > + Instruction on line 18 above is a way for the controller driver to > + work around the fact that some interrupt bits used for USB host > + mode operation are missing in the MUSB_INTRUSB register, thus left > + in an undefined hardware state, since this MUSB controller > + hardware is used in peripheral mode only. As a consequence, the > + glue layer masks these missing bits out to avoid parasite > + interrupts by doing a logical AND operation between the value read > + from MUSB_INTRUSB and the bits that are actually implemented in > + the register. > + </para> > + <para> > + These are only a couple of the quirks found in the JZ4740 USB > + device controller. Some others were directly addressed in the MUSB > + core since the fixes were generic enough to provide a better > + handling of the issues for others controller hardware eventually. > + </para> > + </chapter> > + > + <chapter id="conclusion"> > + <title>Conclusion</title> > + <para> > + Writing a Linux MUSB glue layer should be a more accessible task, > + as this documentation tries to show the ins and outs of this > + exercise. > + </para> > + <para> > + The JZ4740 USB device controller being fairly simple, I hope its > + glue layer serves as a good example for the curious mind. Used > + with the current MUSB glue layers, this documentation should > + provide enough guidance to get started; should anything gets out > + of hand, the linux-usb mailing list archive is another helpful > + resource to browse through. > + </para> > + </chapter> > + > + <chapter id="acknowledgements"> > + <title>Acknowledgements</title> > + <para> > + Many thanks to Lars-Peter Clausen and Maarten ter Huurne for > + answering my questions while I was writing the JZ4740 glue layer > + and for helping me out getting the code in good shape. > + </para> > + <para> > + I would also like to thank the Qi-Hardware community at large for > + its cheerful guidance and support. > + </para> > + </chapter> > + > + <chapter id="resources"> > + <title>Resources</title> > + <para> > + USB Home Page: > + <ulink url="http://www.usb.org">http://www.usb.org</ulink> > + </para> > + <para> > + linux-usb Mailing List Archives: > + <ulink > url="http://marc.info/?l=linux-usb">http://marc.info/?l=linux-usb</ulink> > + </para> > + <para> > + USB On-the-Go Basics: > + <ulink > url="http://www.maximintegrated.com/app-notes/index.mvp/id/1822">http://www.maximintegrated.com/app-notes/index.mvp/id/1822</ulink> > + </para> > + <para> > + Writing USB Device Drivers: > + <ulink > url="https://www.kernel.org/doc/htmldocs/writing_usb_driver/index.html">https://www.kernel.org/doc/htmldocs/writing_usb_driver/index.html</ulink> > + </para> > + <para> > + Texas Instruments USB Configuration Wiki Page: > + <ulink > url="http://processors.wiki.ti.com/index.php/Usbgeneralpage">http://processors.wiki.ti.com/index.php/Usbgeneralpage</ulink> > + </para> > + <para> > + Analog Devices Blackfin MUSB Configuration: > + <ulink > url="http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:drivers:musb">http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:drivers:musb</ulink> > + </para> > + </chapter> > + > +</book> > -- > Apelete > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Apelete -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html