mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-05 16:07:46 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
873
Documentation/DocBook/writing_musb_glue_layer.tmpl
Normal file
873
Documentation/DocBook/writing_musb_glue_layer.tmpl
Normal file
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue