diff options
Diffstat (limited to 'Documentation/DocBook/writing_musb_glue_layer.tmpl')
-rw-r--r-- | Documentation/DocBook/writing_musb_glue_layer.tmpl | 873 |
1 files changed, 0 insertions, 873 deletions
diff --git a/Documentation/DocBook/writing_musb_glue_layer.tmpl b/Documentation/DocBook/writing_musb_glue_layer.tmpl deleted file mode 100644 index 837eca77f274..000000000000 --- a/Documentation/DocBook/writing_musb_glue_layer.tmpl +++ /dev/null @@ -1,873 +0,0 @@ -<?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> |