[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <534322B9.30605@infradead.org>
Date: Mon, 07 Apr 2014 15:12:09 -0700
From: Randy Dunlap <rdunlap@...radead.org>
To: Apelete Seketeli <apelete@...eteli.net>, linux-doc@...r.kernel.org
CC: Rob Landley <rob@...dley.net>, Jiri Kosina <jkosina@...e.cz>,
David Fries <David@...es.net>,
"Robert P. J. Day" <rpjday@...shcourse.ca>,
Ben Hutchings <ben@...adent.org.uk>,
Lars-Peter Clausen <lars@...afoo.de>,
linux-kernel@...r.kernel.org, Felipe Balbi <balbi@...com>,
USB list <linux-usb@...r.kernel.org>
Subject: Re: [RESEND PATCH v2] documentation: docbook: document process of
writing an musb glue layer
On 04/05/2014 11:42 AM, 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 <apelete@...eteli.net>
Hi,
You should be cc-ing the musb maintainer and the USB mailing list (so now done).
Felipe should probably be the person to merge this unless he wants me
to do it...
Thanks.
> ---
> 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 8d96ebf..e274743 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 $(srctree)/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>
>
--
~Randy
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists