mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
merge linus into release branch
Conflicts: drivers/acpi/acpi_memhotplug.c
This commit is contained in:
commit
d120cfb544
4
CREDITS
4
CREDITS
@ -3401,10 +3401,10 @@ S: Czech Republic
|
||||
|
||||
N: Thibaut Varene
|
||||
E: T-Bone@parisc-linux.org
|
||||
W: http://www.parisc-linux.org/
|
||||
W: http://www.parisc-linux.org/~varenet/
|
||||
P: 1024D/B7D2F063 E67C 0D43 A75E 12A5 BB1C FA2F 1E32 C3DA B7D2 F063
|
||||
D: PA-RISC port minion, PDC and GSCPS2 drivers, debuglocks and other bits
|
||||
D: Some bits in an ARM port, S1D13XXX FB driver, random patches here and there
|
||||
D: Some ARM at91rm9200 bits, S1D13XXX FB driver, random patches here and there
|
||||
D: AD1889 sound driver
|
||||
S: Paris, France
|
||||
|
||||
|
@ -181,8 +181,8 @@ Intel IA32 microcode
|
||||
--------------------
|
||||
|
||||
A driver has been added to allow updating of Intel IA32 microcode,
|
||||
accessible as both a devfs regular file and as a normal (misc)
|
||||
character device. If you are not using devfs you may need to:
|
||||
accessible as a normal (misc) character device. If you are not using
|
||||
udev you may need to:
|
||||
|
||||
mkdir /dev/cpu
|
||||
mknod /dev/cpu/microcode c 10 184
|
||||
@ -201,7 +201,9 @@ with programs using shared memory.
|
||||
udev
|
||||
----
|
||||
udev is a userspace application for populating /dev dynamically with
|
||||
only entries for devices actually present. udev replaces devfs.
|
||||
only entries for devices actually present. udev replaces the basic
|
||||
functionality of devfs, while allowing persistant device naming for
|
||||
devices.
|
||||
|
||||
FUSE
|
||||
----
|
||||
@ -231,18 +233,13 @@ The PPP driver has been restructured to support multilink and to
|
||||
enable it to operate over diverse media layers. If you use PPP,
|
||||
upgrade pppd to at least 2.4.0.
|
||||
|
||||
If you are not using devfs, you must have the device file /dev/ppp
|
||||
If you are not using udev, you must have the device file /dev/ppp
|
||||
which can be made by:
|
||||
|
||||
mknod /dev/ppp c 108 0
|
||||
|
||||
as root.
|
||||
|
||||
If you use devfsd and build ppp support as modules, you will need
|
||||
the following in your /etc/devfsd.conf file:
|
||||
|
||||
LOOKUP PPP MODLOAD
|
||||
|
||||
Isdn4k-utils
|
||||
------------
|
||||
|
||||
|
@ -10,7 +10,8 @@ DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \
|
||||
kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
|
||||
procfs-guide.xml writing_usb_driver.xml \
|
||||
kernel-api.xml journal-api.xml lsm.xml usb.xml \
|
||||
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml
|
||||
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
|
||||
genericirq.xml
|
||||
|
||||
###
|
||||
# The build process is as follows (targets):
|
||||
|
474
Documentation/DocBook/genericirq.tmpl
Normal file
474
Documentation/DocBook/genericirq.tmpl
Normal file
@ -0,0 +1,474 @@
|
||||
<?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="Generic-IRQ-Guide">
|
||||
<bookinfo>
|
||||
<title>Linux generic IRQ handling</title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Thomas</firstname>
|
||||
<surname>Gleixner</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
<email>tglx@linutronix.de</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Ingo</firstname>
|
||||
<surname>Molnar</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
<email>mingo@elte.hu</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<copyright>
|
||||
<year>2005-2006</year>
|
||||
<holder>Thomas Gleixner</holder>
|
||||
</copyright>
|
||||
<copyright>
|
||||
<year>2005-2006</year>
|
||||
<holder>Ingo Molnar</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 version 2 as published by the Free Software Foundation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This program 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 program; 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 source
|
||||
distribution of Linux.
|
||||
</para>
|
||||
</legalnotice>
|
||||
</bookinfo>
|
||||
|
||||
<toc></toc>
|
||||
|
||||
<chapter id="intro">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
The generic interrupt handling layer is designed to provide a
|
||||
complete abstraction of interrupt handling for device drivers.
|
||||
It is able to handle all the different types of interrupt controller
|
||||
hardware. Device drivers use generic API functions to request, enable,
|
||||
disable and free interrupts. The drivers do not have to know anything
|
||||
about interrupt hardware details, so they can be used on different
|
||||
platforms without code changes.
|
||||
</para>
|
||||
<para>
|
||||
This documentation is provided to developers who want to implement
|
||||
an interrupt subsystem based for their architecture, with the help
|
||||
of the generic IRQ handling layer.
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="rationale">
|
||||
<title>Rationale</title>
|
||||
<para>
|
||||
The original implementation of interrupt handling in Linux is using
|
||||
the __do_IRQ() super-handler, which is able to deal with every
|
||||
type of interrupt logic.
|
||||
</para>
|
||||
<para>
|
||||
Originally, Russell King identified different types of handlers to
|
||||
build a quite universal set for the ARM interrupt handler
|
||||
implementation in Linux 2.5/2.6. He distinguished between:
|
||||
<itemizedlist>
|
||||
<listitem><para>Level type</para></listitem>
|
||||
<listitem><para>Edge type</para></listitem>
|
||||
<listitem><para>Simple type</para></listitem>
|
||||
</itemizedlist>
|
||||
In the SMP world of the __do_IRQ() super-handler another type
|
||||
was identified:
|
||||
<itemizedlist>
|
||||
<listitem><para>Per CPU type</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
This split implementation of highlevel IRQ handlers allows us to
|
||||
optimize the flow of the interrupt handling for each specific
|
||||
interrupt type. This reduces complexity in that particular codepath
|
||||
and allows the optimized handling of a given type.
|
||||
</para>
|
||||
<para>
|
||||
The original general IRQ implementation used hw_interrupt_type
|
||||
structures and their ->ack(), ->end() [etc.] callbacks to
|
||||
differentiate the flow control in the super-handler. This leads to
|
||||
a mix of flow logic and lowlevel hardware logic, and it also leads
|
||||
to unnecessary code duplication: for example in i386, there is a
|
||||
ioapic_level_irq and a ioapic_edge_irq irq-type which share many
|
||||
of the lowlevel details but have different flow handling.
|
||||
</para>
|
||||
<para>
|
||||
A more natural abstraction is the clean separation of the
|
||||
'irq flow' and the 'chip details'.
|
||||
</para>
|
||||
<para>
|
||||
Analysing a couple of architecture's IRQ subsystem implementations
|
||||
reveals that most of them can use a generic set of 'irq flow'
|
||||
methods and only need to add the chip level specific code.
|
||||
The separation is also valuable for (sub)architectures
|
||||
which need specific quirks in the irq flow itself but not in the
|
||||
chip-details - and thus provides a more transparent IRQ subsystem
|
||||
design.
|
||||
</para>
|
||||
<para>
|
||||
Each interrupt descriptor is assigned its own highlevel flow
|
||||
handler, which is normally one of the generic
|
||||
implementations. (This highlevel flow handler implementation also
|
||||
makes it simple to provide demultiplexing handlers which can be
|
||||
found in embedded platforms on various architectures.)
|
||||
</para>
|
||||
<para>
|
||||
The separation makes the generic interrupt handling layer more
|
||||
flexible and extensible. For example, an (sub)architecture can
|
||||
use a generic irq-flow implementation for 'level type' interrupts
|
||||
and add a (sub)architecture specific 'edge type' implementation.
|
||||
</para>
|
||||
<para>
|
||||
To make the transition to the new model easier and prevent the
|
||||
breakage of existing implementations, the __do_IRQ() super-handler
|
||||
is still available. This leads to a kind of duality for the time
|
||||
being. Over time the new model should be used in more and more
|
||||
architectures, as it enables smaller and cleaner IRQ subsystems.
|
||||
</para>
|
||||
</chapter>
|
||||
<chapter id="bugs">
|
||||
<title>Known Bugs And Assumptions</title>
|
||||
<para>
|
||||
None (knock on wood).
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="Abstraction">
|
||||
<title>Abstraction layers</title>
|
||||
<para>
|
||||
There are three main levels of abstraction in the interrupt code:
|
||||
<orderedlist>
|
||||
<listitem><para>Highlevel driver API</para></listitem>
|
||||
<listitem><para>Highlevel IRQ flow handlers</para></listitem>
|
||||
<listitem><para>Chiplevel hardware encapsulation</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
<sect1>
|
||||
<title>Interrupt control flow</title>
|
||||
<para>
|
||||
Each interrupt is described by an interrupt descriptor structure
|
||||
irq_desc. The interrupt is referenced by an 'unsigned int' numeric
|
||||
value which selects the corresponding interrupt decription structure
|
||||
in the descriptor structures array.
|
||||
The descriptor structure contains status information and pointers
|
||||
to the interrupt flow method and the interrupt chip structure
|
||||
which are assigned to this interrupt.
|
||||
</para>
|
||||
<para>
|
||||
Whenever an interrupt triggers, the lowlevel arch code calls into
|
||||
the generic interrupt code by calling desc->handle_irq().
|
||||
This highlevel IRQ handling function only uses desc->chip primitives
|
||||
referenced by the assigned chip descriptor structure.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>Highlevel Driver API</title>
|
||||
<para>
|
||||
The highlevel Driver API consists of following functions:
|
||||
<itemizedlist>
|
||||
<listitem><para>request_irq()</para></listitem>
|
||||
<listitem><para>free_irq()</para></listitem>
|
||||
<listitem><para>disable_irq()</para></listitem>
|
||||
<listitem><para>enable_irq()</para></listitem>
|
||||
<listitem><para>disable_irq_nosync() (SMP only)</para></listitem>
|
||||
<listitem><para>synchronize_irq() (SMP only)</para></listitem>
|
||||
<listitem><para>set_irq_type()</para></listitem>
|
||||
<listitem><para>set_irq_wake()</para></listitem>
|
||||
<listitem><para>set_irq_data()</para></listitem>
|
||||
<listitem><para>set_irq_chip()</para></listitem>
|
||||
<listitem><para>set_irq_chip_data()</para></listitem>
|
||||
</itemizedlist>
|
||||
See the autogenerated function documentation for details.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>Highlevel IRQ flow handlers</title>
|
||||
<para>
|
||||
The generic layer provides a set of pre-defined irq-flow methods:
|
||||
<itemizedlist>
|
||||
<listitem><para>handle_level_irq</para></listitem>
|
||||
<listitem><para>handle_edge_irq</para></listitem>
|
||||
<listitem><para>handle_simple_irq</para></listitem>
|
||||
<listitem><para>handle_percpu_irq</para></listitem>
|
||||
</itemizedlist>
|
||||
The interrupt flow handlers (either predefined or architecture
|
||||
specific) are assigned to specific interrupts by the architecture
|
||||
either during bootup or during device initialization.
|
||||
</para>
|
||||
<sect2>
|
||||
<title>Default flow implementations</title>
|
||||
<sect3>
|
||||
<title>Helper functions</title>
|
||||
<para>
|
||||
The helper functions call the chip primitives and
|
||||
are used by the default flow implementations.
|
||||
The following helper functions are implemented (simplified excerpt):
|
||||
<programlisting>
|
||||
default_enable(irq)
|
||||
{
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
|
||||
default_disable(irq)
|
||||
{
|
||||
if (!delay_disable(irq))
|
||||
desc->chip->mask(irq);
|
||||
}
|
||||
|
||||
default_ack(irq)
|
||||
{
|
||||
chip->ack(irq);
|
||||
}
|
||||
|
||||
default_mask_ack(irq)
|
||||
{
|
||||
if (chip->mask_ack) {
|
||||
chip->mask_ack(irq);
|
||||
} else {
|
||||
chip->mask(irq);
|
||||
chip->ack(irq);
|
||||
}
|
||||
}
|
||||
|
||||
noop(irq)
|
||||
{
|
||||
}
|
||||
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Default flow handler implementations</title>
|
||||
<sect3>
|
||||
<title>Default Level IRQ flow handler</title>
|
||||
<para>
|
||||
handle_level_irq provides a generic implementation
|
||||
for level-triggered interrupts.
|
||||
</para>
|
||||
<para>
|
||||
The following control flow is implemented (simplified excerpt):
|
||||
<programlisting>
|
||||
desc->chip->start();
|
||||
handle_IRQ_event(desc->action);
|
||||
desc->chip->end();
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3>
|
||||
<title>Default Edge IRQ flow handler</title>
|
||||
<para>
|
||||
handle_edge_irq provides a generic implementation
|
||||
for edge-triggered interrupts.
|
||||
</para>
|
||||
<para>
|
||||
The following control flow is implemented (simplified excerpt):
|
||||
<programlisting>
|
||||
if (desc->status & running) {
|
||||
desc->chip->hold();
|
||||
desc->status |= pending | masked;
|
||||
return;
|
||||
}
|
||||
desc->chip->start();
|
||||
desc->status |= running;
|
||||
do {
|
||||
if (desc->status & masked)
|
||||
desc->chip->enable();
|
||||
desc-status &= ~pending;
|
||||
handle_IRQ_event(desc->action);
|
||||
} while (status & pending);
|
||||
desc-status &= ~running;
|
||||
desc->chip->end();
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3>
|
||||
<title>Default simple IRQ flow handler</title>
|
||||
<para>
|
||||
handle_simple_irq provides a generic implementation
|
||||
for simple interrupts.
|
||||
</para>
|
||||
<para>
|
||||
Note: The simple flow handler does not call any
|
||||
handler/chip primitives.
|
||||
</para>
|
||||
<para>
|
||||
The following control flow is implemented (simplified excerpt):
|
||||
<programlisting>
|
||||
handle_IRQ_event(desc->action);
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3>
|
||||
<title>Default per CPU flow handler</title>
|
||||
<para>
|
||||
handle_percpu_irq provides a generic implementation
|
||||
for per CPU interrupts.
|
||||
</para>
|
||||
<para>
|
||||
Per CPU interrupts are only available on SMP and
|
||||
the handler provides a simplified version without
|
||||
locking.
|
||||
</para>
|
||||
<para>
|
||||
The following control flow is implemented (simplified excerpt):
|
||||
<programlisting>
|
||||
desc->chip->start();
|
||||
handle_IRQ_event(desc->action);
|
||||
desc->chip->end();
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Quirks and optimizations</title>
|
||||
<para>
|
||||
The generic functions are intended for 'clean' architectures and chips,
|
||||
which have no platform-specific IRQ handling quirks. If an architecture
|
||||
needs to implement quirks on the 'flow' level then it can do so by
|
||||
overriding the highlevel irq-flow handler.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Delayed interrupt disable</title>
|
||||
<para>
|
||||
This per interrupt selectable feature, which was introduced by Russell
|
||||
King in the ARM interrupt implementation, does not mask an interrupt
|
||||
at the hardware level when disable_irq() is called. The interrupt is
|
||||
kept enabled and is masked in the flow handler when an interrupt event
|
||||
happens. This prevents losing edge interrupts on hardware which does
|
||||
not store an edge interrupt event while the interrupt is disabled at
|
||||
the hardware level. When an interrupt arrives while the IRQ_DISABLED
|
||||
flag is set, then the interrupt is masked at the hardware level and
|
||||
the IRQ_PENDING bit is set. When the interrupt is re-enabled by
|
||||
enable_irq() the pending bit is checked and if it is set, the
|
||||
interrupt is resent either via hardware or by a software resend
|
||||
mechanism. (It's necessary to enable CONFIG_HARDIRQS_SW_RESEND when
|
||||
you want to use the delayed interrupt disable feature and your
|
||||
hardware is not capable of retriggering an interrupt.)
|
||||
The delayed interrupt disable can be runtime enabled, per interrupt,
|
||||
by setting the IRQ_DELAYED_DISABLE flag in the irq_desc status field.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>Chiplevel hardware encapsulation</title>
|
||||
<para>
|
||||
The chip level hardware descriptor structure irq_chip
|
||||
contains all the direct chip relevant functions, which
|
||||
can be utilized by the irq flow implementations.
|
||||
<itemizedlist>
|
||||
<listitem><para>ack()</para></listitem>
|
||||
<listitem><para>mask_ack() - Optional, recommended for performance</para></listitem>
|
||||
<listitem><para>mask()</para></listitem>
|
||||
<listitem><para>unmask()</para></listitem>
|
||||
<listitem><para>retrigger() - Optional</para></listitem>
|
||||
<listitem><para>set_type() - Optional</para></listitem>
|
||||
<listitem><para>set_wake() - Optional</para></listitem>
|
||||
</itemizedlist>
|
||||
These primitives are strictly intended to mean what they say: ack means
|
||||
ACK, masking means masking of an IRQ line, etc. It is up to the flow
|
||||
handler(s) to use these basic units of lowlevel functionality.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<chapter id="doirq">
|
||||
<title>__do_IRQ entry point</title>
|
||||
<para>
|
||||
The original implementation __do_IRQ() is an alternative entry
|
||||
point for all types of interrupts.
|
||||
</para>
|
||||
<para>
|
||||
This handler turned out to be not suitable for all
|
||||
interrupt hardware and was therefore reimplemented with split
|
||||
functionality for egde/level/simple/percpu interrupts. This is not
|
||||
only a functional optimization. It also shortens code paths for
|
||||
interrupts.
|
||||
</para>
|
||||
<para>
|
||||
To make use of the split implementation, replace the call to
|
||||
__do_IRQ by a call to desc->chip->handle_irq() and associate
|
||||
the appropriate handler function to desc->chip->handle_irq().
|
||||
In most cases the generic handler implementations should
|
||||
be sufficient.
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="locking">
|
||||
<title>Locking on SMP</title>
|
||||
<para>
|
||||
The locking of chip registers is up to the architecture that
|
||||
defines the chip primitives. There is a chip->lock field that can be used
|
||||
for serialization, but the generic layer does not touch it. The per-irq
|
||||
structure is protected via desc->lock, by the generic layer.
|
||||
</para>
|
||||
</chapter>
|
||||
<chapter id="structs">
|
||||
<title>Structures</title>
|
||||
<para>
|
||||
This chapter contains the autogenerated documentation of the structures which are
|
||||
used in the generic IRQ layer.
|
||||
</para>
|
||||
!Iinclude/linux/irq.h
|
||||
</chapter>
|
||||
|
||||
<chapter id="pubfunctions">
|
||||
<title>Public Functions Provided</title>
|
||||
<para>
|
||||
This chapter contains the autogenerated documentation of the kernel API functions
|
||||
which are exported.
|
||||
</para>
|
||||
!Ekernel/irq/manage.c
|
||||
!Ekernel/irq/chip.c
|
||||
</chapter>
|
||||
|
||||
<chapter id="intfunctions">
|
||||
<title>Internal Functions Provided</title>
|
||||
<para>
|
||||
This chapter contains the autogenerated documentation of the internal functions.
|
||||
</para>
|
||||
!Ikernel/irq/handle.c
|
||||
!Ikernel/irq/chip.c
|
||||
</chapter>
|
||||
|
||||
<chapter id="credits">
|
||||
<title>Credits</title>
|
||||
<para>
|
||||
The following people have contributed to this document:
|
||||
<orderedlist>
|
||||
<listitem><para>Thomas Gleixner<email>tglx@linutronix.de</email></para></listitem>
|
||||
<listitem><para>Ingo Molnar<email>mingo@elte.hu</email></para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
</chapter>
|
||||
</book>
|
@ -348,11 +348,6 @@ X!Earch/i386/kernel/mca.c
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<chapter id="devfs">
|
||||
<title>The Device File System</title>
|
||||
!Efs/devfs/base.c
|
||||
</chapter>
|
||||
|
||||
<chapter id="sysfs">
|
||||
<title>The Filesystem for Exporting Kernel Objects</title>
|
||||
!Efs/sysfs/file.c
|
||||
|
22
Documentation/IRQ.txt
Normal file
22
Documentation/IRQ.txt
Normal file
@ -0,0 +1,22 @@
|
||||
What is an IRQ?
|
||||
|
||||
An IRQ is an interrupt request from a device.
|
||||
Currently they can come in over a pin, or over a packet.
|
||||
Several devices may be connected to the same pin thus
|
||||
sharing an IRQ.
|
||||
|
||||
An IRQ number is a kernel identifier used to talk about a hardware
|
||||
interrupt source. Typically this is an index into the global irq_desc
|
||||
array, but except for what linux/interrupt.h implements the details
|
||||
are architecture specific.
|
||||
|
||||
An IRQ number is an enumeration of the possible interrupt sources on a
|
||||
machine. Typically what is enumerated is the number of input pins on
|
||||
all of the interrupt controller in the system. In the case of ISA
|
||||
what is enumerated are the 16 input pins on the two i8259 interrupt
|
||||
controllers.
|
||||
|
||||
Architectures can assign additional meaning to the IRQ numbers, and
|
||||
are encouraged to in the case where there is any manual configuration
|
||||
of the hardware involved. The ISA IRQs are a classic example of
|
||||
assigning this kind of additional meaning.
|
@ -7,7 +7,7 @@ The CONFIG_RCU_TORTURE_TEST config option is available for all RCU
|
||||
implementations. It creates an rcutorture kernel module that can
|
||||
be loaded to run a torture test. The test periodically outputs
|
||||
status messages via printk(), which can be examined via the dmesg
|
||||
command (perhaps grepping for "rcutorture"). The test is started
|
||||
command (perhaps grepping for "torture"). The test is started
|
||||
when the module is loaded, and stops when the module is unloaded.
|
||||
|
||||
However, actually setting this config option to "y" results in the system
|
||||
@ -35,6 +35,19 @@ stat_interval The number of seconds between output of torture
|
||||
be printed -only- when the module is unloaded, and this
|
||||
is the default.
|
||||
|
||||
shuffle_interval
|
||||
The number of seconds to keep the test threads affinitied
|
||||
to a particular subset of the CPUs. Used in conjunction
|
||||
with test_no_idle_hz.
|
||||
|
||||
test_no_idle_hz Whether or not to test the ability of RCU to operate in
|
||||
a kernel that disables the scheduling-clock interrupt to
|
||||
idle CPUs. Boolean parameter, "1" to test, "0" otherwise.
|
||||
|
||||
torture_type The type of RCU to test: "rcu" for the rcu_read_lock()
|
||||
API, "rcu_bh" for the rcu_read_lock_bh() API, and "srcu"
|
||||
for the "srcu_read_lock()" API.
|
||||
|
||||
verbose Enable debug printk()s. Default is disabled.
|
||||
|
||||
|
||||
@ -42,14 +55,14 @@ OUTPUT
|
||||
|
||||
The statistics output is as follows:
|
||||
|
||||
rcutorture: --- Start of test: nreaders=16 stat_interval=0 verbose=0
|
||||
rcutorture: rtc: 0000000000000000 ver: 1916 tfle: 0 rta: 1916 rtaf: 0 rtf: 1915
|
||||
rcutorture: Reader Pipe: 1466408 9747 0 0 0 0 0 0 0 0 0
|
||||
rcutorture: Reader Batch: 1464477 11678 0 0 0 0 0 0 0 0
|
||||
rcutorture: Free-Block Circulation: 1915 1915 1915 1915 1915 1915 1915 1915 1915 1915 0
|
||||
rcutorture: --- End of test
|
||||
rcu-torture: --- Start of test: nreaders=16 stat_interval=0 verbose=0
|
||||
rcu-torture: rtc: 0000000000000000 ver: 1916 tfle: 0 rta: 1916 rtaf: 0 rtf: 1915
|
||||
rcu-torture: Reader Pipe: 1466408 9747 0 0 0 0 0 0 0 0 0
|
||||
rcu-torture: Reader Batch: 1464477 11678 0 0 0 0 0 0 0 0
|
||||
rcu-torture: Free-Block Circulation: 1915 1915 1915 1915 1915 1915 1915 1915 1915 1915 0
|
||||
rcu-torture: --- End of test
|
||||
|
||||
The command "dmesg | grep rcutorture:" will extract this information on
|
||||
The command "dmesg | grep torture:" will extract this information on
|
||||
most systems. On more esoteric configurations, it may be necessary to
|
||||
use other commands to access the output of the printk()s used by
|
||||
the RCU torture test. The printk()s use KERN_ALERT, so they should
|
||||
@ -115,8 +128,9 @@ The following script may be used to torture RCU:
|
||||
modprobe rcutorture
|
||||
sleep 100
|
||||
rmmod rcutorture
|
||||
dmesg | grep rcutorture:
|
||||
dmesg | grep torture:
|
||||
|
||||
The output can be manually inspected for the error flag of "!!!".
|
||||
One could of course create a more elaborate script that automatically
|
||||
checked for such errors.
|
||||
checked for such errors. The "rmmod" command forces a "SUCCESS" or
|
||||
"FAILURE" indication to be printk()ed.
|
||||
|
@ -78,9 +78,9 @@ also known as "System Drives", and Drive Groups are also called "Packs". Both
|
||||
terms are in use in the Mylex documentation; I have chosen to standardize on
|
||||
the more generic "Logical Drive" and "Drive Group".
|
||||
|
||||
DAC960 RAID disk devices are named in the style of the Device File System
|
||||
(DEVFS). The device corresponding to Logical Drive D on Controller C is
|
||||
referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1
|
||||
DAC960 RAID disk devices are named in the style of the obsolete Device File
|
||||
System (DEVFS). The device corresponding to Logical Drive D on Controller C
|
||||
is referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1
|
||||
through /dev/rd/cCdDp7. For example, partition 3 of Logical Drive 5 on
|
||||
Controller 2 is referred to as /dev/rd/c2d5p3. Note that unlike with SCSI
|
||||
disks the device names will not change in the event of a disk drive failure.
|
||||
|
@ -6,17 +6,6 @@ be removed from this file.
|
||||
|
||||
---------------------------
|
||||
|
||||
What: devfs
|
||||
When: July 2005
|
||||
Files: fs/devfs/*, include/linux/devfs_fs*.h and assorted devfs
|
||||
function calls throughout the kernel tree
|
||||
Why: It has been unmaintained for a number of years, has unfixable
|
||||
races, contains a naming policy within the kernel that is
|
||||
against the LSB, and can be replaced by using udev.
|
||||
Who: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: RAW driver (CONFIG_RAW_DRIVER)
|
||||
When: December 2005
|
||||
Why: declared obsolete since kernel 2.6.3
|
||||
@ -132,16 +121,6 @@ Who: NeilBrown <neilb@suse.de>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: au1x00_uart driver
|
||||
When: January 2006
|
||||
Why: The 8250 serial driver now has the ability to deal with the differences
|
||||
between the standard 8250 family of UARTs and their slightly strange
|
||||
brother on Alchemy SOCs. The loss of features is not considered an
|
||||
issue.
|
||||
Who: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: eepro100 network driver
|
||||
When: January 2007
|
||||
Why: replaced by the e100 driver
|
||||
@ -177,6 +156,16 @@ Who: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports
|
||||
(temporary transition config option provided until then)
|
||||
The transition config option will also be removed at the same time.
|
||||
When: before 2.6.19
|
||||
Why: Unused symbols are both increasing the size of the kernel binary
|
||||
and are often a sign of "wrong API"
|
||||
Who: Arjan van de Ven <arjan@linux.intel.com>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: remove EXPORT_SYMBOL(tasklist_lock)
|
||||
When: August 2006
|
||||
Files: kernel/fork.c
|
||||
@ -224,3 +213,47 @@ Why: The interface no longer has any callers left in the kernel. It
|
||||
Who: Nick Piggin <npiggin@suse.de>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Support for the MIPS EV96100 evaluation board
|
||||
When: September 2006
|
||||
Why: Does no longer build since at least November 15, 2003, apparently
|
||||
no userbase left.
|
||||
Who: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Support for the Momentum / PMC-Sierra Jaguar ATX evaluation board
|
||||
When: September 2006
|
||||
Why: Does no longer build since quite some time, and was never popular,
|
||||
due to the platform being replaced by successor models. Apparently
|
||||
no user base left. It also is one of the last users of
|
||||
WANT_PAGE_VIRTUAL.
|
||||
Who: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Support for the Momentum Ocelot, Ocelot 3, Ocelot C and Ocelot G
|
||||
When: September 2006
|
||||
Why: Some do no longer build and apparently there is no user base left
|
||||
for these platforms.
|
||||
Who: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Support for MIPS Technologies' Altas and SEAD evaluation board
|
||||
When: September 2006
|
||||
Why: Some do no longer build and apparently there is no user base left
|
||||
for these platforms. Hardware out of production since several years.
|
||||
Who: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Support for the IT8172-based platforms, ITE 8172G and Globespan IVR
|
||||
When: September 2006
|
||||
Why: Code does no longer build since at least 2.6.0, apparently there is
|
||||
no user base left for these platforms. Hardware out of production
|
||||
since several years and hardly a trace of the manufacturer left on
|
||||
the net.
|
||||
Who: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
||||
---------------------------
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,40 +0,0 @@
|
||||
Device File System (devfs) ToDo List
|
||||
|
||||
Richard Gooch <rgooch@atnf.csiro.au>
|
||||
|
||||
3-JUL-2000
|
||||
|
||||
This is a list of things to be done for better devfs support in the
|
||||
Linux kernel. If you'd like to contribute to the devfs, please have a
|
||||
look at this list for anything that is unallocated. Also, if there are
|
||||
items missing (surely), please contact me so I can add them to the
|
||||
list (preferably with your name attached to them:-).
|
||||
|
||||
|
||||
- >256 ptys
|
||||
Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
|
||||
|
||||
- Amiga floppy driver (drivers/block/amiflop.c)
|
||||
|
||||
- Atari floppy driver (drivers/block/ataflop.c)
|
||||
|
||||
- SWIM3 (Super Woz Integrated Machine 3) floppy driver (drivers/block/swim3.c)
|
||||
|
||||
- Amiga ZorroII ramdisc driver (drivers/block/z2ram.c)
|
||||
|
||||
- Parallel port ATAPI CD-ROM (drivers/block/paride/pcd.c)
|
||||
|
||||
- Parallel port ATAPI floppy (drivers/block/paride/pf.c)
|
||||
|
||||
- AP1000 block driver (drivers/ap1000/ap.c, drivers/ap1000/ddv.c)
|
||||
|
||||
- Archimedes floppy (drivers/acorn/block/fd1772.c)
|
||||
|
||||
- MFM hard drive (drivers/acorn/block/mfmhd.c)
|
||||
|
||||
- I2O block device (drivers/message/i2o/i2o_block.c)
|
||||
|
||||
- ST-RAM device (arch/m68k/atari/stram.c)
|
||||
|
||||
- Raw devices
|
||||
|
@ -1,65 +0,0 @@
|
||||
/* -*- auto-fill -*- */
|
||||
|
||||
Device File System (devfs) Boot Options
|
||||
|
||||
Richard Gooch <rgooch@atnf.csiro.au>
|
||||
|
||||
18-AUG-2001
|
||||
|
||||
|
||||
When CONFIG_DEVFS_DEBUG is enabled, you can pass several boot options
|
||||
to the kernel to debug devfs. The boot options are prefixed by
|
||||
"devfs=", and are separated by commas. Spaces are not allowed. The
|
||||
syntax looks like this:
|
||||
|
||||
devfs=<option1>,<option2>,<option3>
|
||||
|
||||
and so on. For example, if you wanted to turn on debugging for module
|
||||
load requests and device registration, you would do:
|
||||
|
||||
devfs=dmod,dreg
|
||||
|
||||
You may prefix "no" to any option. This will invert the option.
|
||||
|
||||
|
||||
Debugging Options
|
||||
=================
|
||||
|
||||
These requires CONFIG_DEVFS_DEBUG to be enabled.
|
||||
Note that all debugging options have 'd' as the first character. By
|
||||
default all options are off. All debugging output is sent to the
|
||||
kernel logs. The debugging options do not take effect until the devfs
|
||||
version message appears (just prior to the root filesystem being
|
||||
mounted).
|
||||
|
||||
These are the options:
|
||||
|
||||
dmod print module load requests to <request_module>
|
||||
|
||||
dreg print device register requests to <devfs_register>
|
||||
|
||||
dunreg print device unregister requests to <devfs_unregister>
|
||||
|
||||
dchange print device change requests to <devfs_set_flags>
|
||||
|
||||
dilookup print inode lookup requests
|
||||
|
||||
diget print VFS inode allocations
|
||||
|
||||
diunlink print inode unlinks
|
||||
|
||||
dichange print inode changes
|
||||
|
||||
dimknod print calls to mknod(2)
|
||||
|
||||
dall some debugging turned on
|
||||
|
||||
|
||||
Other Options
|
||||
=============
|
||||
|
||||
These control the default behaviour of devfs. The options are:
|
||||
|
||||
mount mount devfs onto /dev at boot time
|
||||
|
||||
only disable non-devfs device nodes for devfs-capable drivers
|
@ -67,8 +67,7 @@ initrd adds the following new options:
|
||||
as the last process has closed it, all data is freed and /dev/initrd
|
||||
can't be opened anymore.
|
||||
|
||||
root=/dev/ram0 (without devfs)
|
||||
root=/dev/rd/0 (with devfs)
|
||||
root=/dev/ram0
|
||||
|
||||
initrd is mounted as root, and the normal boot procedure is followed,
|
||||
with the RAM disk still mounted as root.
|
||||
@ -90,8 +89,7 @@ you're building an install floppy), the root file system creation
|
||||
procedure should create the /initrd directory.
|
||||
|
||||
If initrd will not be mounted in some cases, its content is still
|
||||
accessible if the following device has been created (note that this
|
||||
does not work if using devfs):
|
||||
accessible if the following device has been created:
|
||||
|
||||
# mknod /dev/initrd b 1 250
|
||||
# chmod 400 /dev/initrd
|
||||
@ -119,8 +117,7 @@ We'll describe the loopback device method:
|
||||
(if space is critical, you may want to use the Minix FS instead of Ext2)
|
||||
3) mount the file system, e.g.
|
||||
# mount -t ext2 -o loop initrd /mnt
|
||||
4) create the console device (not necessary if using devfs, but it can't
|
||||
hurt to do it anyway):
|
||||
4) create the console device:
|
||||
# mkdir /mnt/dev
|
||||
# mknod /mnt/dev/console c 5 1
|
||||
5) copy all the files that are needed to properly use the initrd
|
||||
@ -152,12 +149,7 @@ have to be given:
|
||||
|
||||
root=/dev/ram0 init=/linuxrc rw
|
||||
|
||||
if not using devfs, or
|
||||
|
||||
root=/dev/rd/0 init=/linuxrc rw
|
||||
|
||||
if using devfs. (rw is only necessary if writing to the initrd file
|
||||
system.)
|
||||
(rw is only necessary if writing to the initrd file system.)
|
||||
|
||||
With LOADLIN, you simply execute
|
||||
|
||||
@ -217,9 +209,9 @@ following command:
|
||||
# exec chroot . what-follows <dev/console >dev/console 2>&1
|
||||
|
||||
Where what-follows is a program under the new root, e.g. /sbin/init
|
||||
If the new root file system will be used with devfs and has no valid
|
||||
/dev directory, devfs must be mounted before invoking chroot in order to
|
||||
provide /dev/console.
|
||||
If the new root file system will be used with udev and has no valid
|
||||
/dev directory, udev must be initialized before invoking chroot in order
|
||||
to provide /dev/console.
|
||||
|
||||
Note: implementation details of pivot_root may change with time. In order
|
||||
to ensure compatibility, the following points should be observed:
|
||||
@ -236,7 +228,7 @@ Now, the initrd can be unmounted and the memory allocated by the RAM
|
||||
disk can be freed:
|
||||
|
||||
# umount /initrd
|
||||
# blockdev --flushbufs /dev/ram0 # /dev/rd/0 if using devfs
|
||||
# blockdev --flushbufs /dev/ram0
|
||||
|
||||
It is also possible to use initrd with an NFS-mounted root, see the
|
||||
pivot_root(8) man page for details.
|
||||
|
@ -119,7 +119,6 @@ Code Seq# Include File Comments
|
||||
'c' 00-7F linux/comstats.h conflict!
|
||||
'c' 00-7F linux/coda.h conflict!
|
||||
'd' 00-FF linux/char/drm/drm/h conflict!
|
||||
'd' 00-1F linux/devfs_fs.h conflict!
|
||||
'd' 00-DF linux/video_decoder.h conflict!
|
||||
'd' F0-FF linux/digi1.h
|
||||
'e' all linux/digi1.h conflict!
|
||||
|
@ -35,7 +35,6 @@ parameter is applicable:
|
||||
APM Advanced Power Management support is enabled.
|
||||
AX25 Appropriate AX.25 support is enabled.
|
||||
CD Appropriate CD support is enabled.
|
||||
DEVFS devfs support is enabled.
|
||||
DRM Direct Rendering Management support is enabled.
|
||||
EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
|
||||
EFI EFI Partitioning (GPT) is enabled
|
||||
@ -440,9 +439,6 @@ running once the system is up.
|
||||
Format: <area>[,<node>]
|
||||
See also Documentation/networking/decnet.txt.
|
||||
|
||||
devfs= [DEVFS]
|
||||
See Documentation/filesystems/devfs/boot-options.
|
||||
|
||||
dhash_entries= [KNL]
|
||||
Set number of hash buckets for dentry cache.
|
||||
|
||||
@ -1669,6 +1665,10 @@ running once the system is up.
|
||||
usbhid.mousepoll=
|
||||
[USBHID] The interval which mice are to be polled at.
|
||||
|
||||
vdso= [IA-32]
|
||||
vdso=1: enable VDSO (default)
|
||||
vdso=0: disable VDSO mapping
|
||||
|
||||
video= [FB] Frame buffer configuration
|
||||
See Documentation/fb/modedb.txt.
|
||||
|
||||
@ -1685,9 +1685,14 @@ running once the system is up.
|
||||
decrease the size and leave more room for directly
|
||||
mapped kernel RAM.
|
||||
|
||||
vmhalt= [KNL,S390]
|
||||
vmhalt= [KNL,S390] Perform z/VM CP command after system halt.
|
||||
Format: <command>
|
||||
|
||||
vmpoff= [KNL,S390]
|
||||
vmpanic= [KNL,S390] Perform z/VM CP command after kernel panic.
|
||||
Format: <command>
|
||||
|
||||
vmpoff= [KNL,S390] Perform z/VM CP command after power off.
|
||||
Format: <command>
|
||||
|
||||
waveartist= [HW,OSS]
|
||||
Format: <io>,<irq>,<dma>,<dma2>
|
||||
|
@ -3,16 +3,23 @@
|
||||
===================
|
||||
|
||||
The key request service is part of the key retention service (refer to
|
||||
Documentation/keys.txt). This document explains more fully how that the
|
||||
requesting algorithm works.
|
||||
Documentation/keys.txt). This document explains more fully how the requesting
|
||||
algorithm works.
|
||||
|
||||
The process starts by either the kernel requesting a service by calling
|
||||
request_key():
|
||||
request_key*():
|
||||
|
||||
struct key *request_key(const struct key_type *type,
|
||||
const char *description,
|
||||
const char *callout_string);
|
||||
|
||||
or:
|
||||
|
||||
struct key *request_key_with_auxdata(const struct key_type *type,
|
||||
const char *description,
|
||||
const char *callout_string,
|
||||
void *aux);
|
||||
|
||||
Or by userspace invoking the request_key system call:
|
||||
|
||||
key_serial_t request_key(const char *type,
|
||||
@ -20,16 +27,26 @@ Or by userspace invoking the request_key system call:
|
||||
const char *callout_info,
|
||||
key_serial_t dest_keyring);
|
||||
|
||||
The main difference between the two access points is that the in-kernel
|
||||
interface does not need to link the key to a keyring to prevent it from being
|
||||
immediately destroyed. The kernel interface returns a pointer directly to the
|
||||
key, and it's up to the caller to destroy the key.
|
||||
The main difference between the access points is that the in-kernel interface
|
||||
does not need to link the key to a keyring to prevent it from being immediately
|
||||
destroyed. The kernel interface returns a pointer directly to the key, and
|
||||
it's up to the caller to destroy the key.
|
||||
|
||||
The request_key_with_auxdata() call is like the in-kernel request_key() call,
|
||||
except that it permits auxiliary data to be passed to the upcaller (the default
|
||||
is NULL). This is only useful for those key types that define their own upcall
|
||||
mechanism rather than using /sbin/request-key.
|
||||
|
||||
The userspace interface links the key to a keyring associated with the process
|
||||
to prevent the key from going away, and returns the serial number of the key to
|
||||
the caller.
|
||||
|
||||
|
||||
The following example assumes that the key types involved don't define their
|
||||
own upcall mechanisms. If they do, then those should be substituted for the
|
||||
forking and execution of /sbin/request-key.
|
||||
|
||||
|
||||
===========
|
||||
THE PROCESS
|
||||
===========
|
||||
@ -40,8 +57,8 @@ A request proceeds in the following manner:
|
||||
interface].
|
||||
|
||||
(2) request_key() searches the process's subscribed keyrings to see if there's
|
||||
a suitable key there. If there is, it returns the key. If there isn't, and
|
||||
callout_info is not set, an error is returned. Otherwise the process
|
||||
a suitable key there. If there is, it returns the key. If there isn't,
|
||||
and callout_info is not set, an error is returned. Otherwise the process
|
||||
proceeds to the next step.
|
||||
|
||||
(3) request_key() sees that A doesn't have the desired key yet, so it creates
|
||||
@ -62,7 +79,7 @@ A request proceeds in the following manner:
|
||||
instantiation.
|
||||
|
||||
(7) The program may want to access another key from A's context (say a
|
||||
Kerberos TGT key). It just requests the appropriate key, and the keyring
|
||||
Kerberos TGT key). It just requests the appropriate key, and the keyring
|
||||
search notes that the session keyring has auth key V in its bottom level.
|
||||
|
||||
This will permit it to then search the keyrings of process A with the
|
||||
@ -79,10 +96,11 @@ A request proceeds in the following manner:
|
||||
(10) The program then exits 0 and request_key() deletes key V and returns key
|
||||
U to the caller.
|
||||
|
||||
This also extends further. If key W (step 7 above) didn't exist, key W would be
|
||||
created uninstantiated, another auth key (X) would be created (as per step 3)
|
||||
and another copy of /sbin/request-key spawned (as per step 4); but the context
|
||||
specified by auth key X will still be process A, as it was in auth key V.
|
||||
This also extends further. If key W (step 7 above) didn't exist, key W would
|
||||
be created uninstantiated, another auth key (X) would be created (as per step
|
||||
3) and another copy of /sbin/request-key spawned (as per step 4); but the
|
||||
context specified by auth key X will still be process A, as it was in auth key
|
||||
V.
|
||||
|
||||
This is because process A's keyrings can't simply be attached to
|
||||
/sbin/request-key at the appropriate places because (a) execve will discard two
|
||||
@ -118,17 +136,17 @@ A search of any particular keyring proceeds in the following fashion:
|
||||
|
||||
(2) It considers all the non-keyring keys within that keyring and, if any key
|
||||
matches the criteria specified, calls key_permission(SEARCH) on it to see
|
||||
if the key is allowed to be found. If it is, that key is returned; if
|
||||
if the key is allowed to be found. If it is, that key is returned; if
|
||||
not, the search continues, and the error code is retained if of higher
|
||||
priority than the one currently set.
|
||||
|
||||
(3) It then considers all the keyring-type keys in the keyring it's currently
|
||||
searching. It calls key_permission(SEARCH) on each keyring, and if this
|
||||
searching. It calls key_permission(SEARCH) on each keyring, and if this
|
||||
grants permission, it recurses, executing steps (2) and (3) on that
|
||||
keyring.
|
||||
|
||||
The process stops immediately a valid key is found with permission granted to
|
||||
use it. Any error from a previous match attempt is discarded and the key is
|
||||
use it. Any error from a previous match attempt is discarded and the key is
|
||||
returned.
|
||||
|
||||
When search_process_keyrings() is invoked, it performs the following searches
|
||||
@ -153,7 +171,7 @@ The moment one succeeds, all pending errors are discarded and the found key is
|
||||
returned.
|
||||
|
||||
Only if all these fail does the whole thing fail with the highest priority
|
||||
error. Note that several errors may have come from LSM.
|
||||
error. Note that several errors may have come from LSM.
|
||||
|
||||
The error priority is:
|
||||
|
||||
|
@ -780,6 +780,17 @@ payload contents" for more information.
|
||||
See also Documentation/keys-request-key.txt.
|
||||
|
||||
|
||||
(*) To search for a key, passing auxiliary data to the upcaller, call:
|
||||
|
||||
struct key *request_key_with_auxdata(const struct key_type *type,
|
||||
const char *description,
|
||||
const char *callout_string,
|
||||
void *aux);
|
||||
|
||||
This is identical to request_key(), except that the auxiliary data is
|
||||
passed to the key_type->request_key() op if it exists.
|
||||
|
||||
|
||||
(*) When it is no longer required, the key should be released using:
|
||||
|
||||
void key_put(struct key *key);
|
||||
@ -1031,6 +1042,24 @@ The structure has a number of fields, some of which are mandatory:
|
||||
as might happen when the userspace buffer is accessed.
|
||||
|
||||
|
||||
(*) int (*request_key)(struct key *key, struct key *authkey, const char *op,
|
||||
void *aux);
|
||||
|
||||
This method is optional. If provided, request_key() and
|
||||
request_key_with_auxdata() will invoke this function rather than
|
||||
upcalling to /sbin/request-key to operate upon a key of this type.
|
||||
|
||||
The aux parameter is as passed to request_key_with_auxdata() or is NULL
|
||||
otherwise. Also passed are the key to be operated upon, the
|
||||
authorisation key for this operation and the operation type (currently
|
||||
only "create").
|
||||
|
||||
This function should return only when the upcall is complete. Upon return
|
||||
the authorisation key will be revoked, and the target key will be
|
||||
negatively instantiated if it is still uninstantiated. The error will be
|
||||
returned to the caller of request_key*().
|
||||
|
||||
|
||||
============================
|
||||
REQUEST-KEY CALLBACK SERVICE
|
||||
============================
|
||||
|
121
Documentation/pi-futex.txt
Normal file
121
Documentation/pi-futex.txt
Normal file
@ -0,0 +1,121 @@
|
||||
Lightweight PI-futexes
|
||||
----------------------
|
||||
|
||||
We are calling them lightweight for 3 reasons:
|
||||
|
||||
- in the user-space fastpath a PI-enabled futex involves no kernel work
|
||||
(or any other PI complexity) at all. No registration, no extra kernel
|
||||
calls - just pure fast atomic ops in userspace.
|
||||
|
||||
- even in the slowpath, the system call and scheduling pattern is very
|
||||
similar to normal futexes.
|
||||
|
||||
- the in-kernel PI implementation is streamlined around the mutex
|
||||
abstraction, with strict rules that keep the implementation
|
||||
relatively simple: only a single owner may own a lock (i.e. no
|
||||
read-write lock support), only the owner may unlock a lock, no
|
||||
recursive locking, etc.
|
||||
|
||||
Priority Inheritance - why?
|
||||
---------------------------
|
||||
|
||||
The short reply: user-space PI helps achieving/improving determinism for
|
||||
user-space applications. In the best-case, it can help achieve
|
||||
determinism and well-bound latencies. Even in the worst-case, PI will
|
||||
improve the statistical distribution of locking related application
|
||||
delays.
|
||||
|
||||
The longer reply:
|
||||
-----------------
|
||||
|
||||
Firstly, sharing locks between multiple tasks is a common programming
|
||||
technique that often cannot be replaced with lockless algorithms. As we
|
||||
can see it in the kernel [which is a quite complex program in itself],
|
||||
lockless structures are rather the exception than the norm - the current
|
||||
ratio of lockless vs. locky code for shared data structures is somewhere
|
||||
between 1:10 and 1:100. Lockless is hard, and the complexity of lockless
|
||||
algorithms often endangers to ability to do robust reviews of said code.
|
||||
I.e. critical RT apps often choose lock structures to protect critical
|
||||
data structures, instead of lockless algorithms. Furthermore, there are
|
||||
cases (like shared hardware, or other resource limits) where lockless
|
||||
access is mathematically impossible.
|
||||
|
||||
Media players (such as Jack) are an example of reasonable application
|
||||
design with multiple tasks (with multiple priority levels) sharing
|
||||
short-held locks: for example, a highprio audio playback thread is
|
||||
combined with medium-prio construct-audio-data threads and low-prio
|
||||
display-colory-stuff threads. Add video and decoding to the mix and
|
||||
we've got even more priority levels.
|
||||
|
||||
So once we accept that synchronization objects (locks) are an
|
||||
unavoidable fact of life, and once we accept that multi-task userspace
|
||||
apps have a very fair expectation of being able to use locks, we've got
|
||||
to think about how to offer the option of a deterministic locking
|
||||
implementation to user-space.
|
||||
|
||||
Most of the technical counter-arguments against doing priority
|
||||
inheritance only apply to kernel-space locks. But user-space locks are
|
||||
different, there we cannot disable interrupts or make the task
|
||||
non-preemptible in a critical section, so the 'use spinlocks' argument
|
||||
does not apply (user-space spinlocks have the same priority inversion
|
||||
problems as other user-space locking constructs). Fact is, pretty much
|
||||
the only technique that currently enables good determinism for userspace
|
||||
locks (such as futex-based pthread mutexes) is priority inheritance:
|
||||
|
||||
Currently (without PI), if a high-prio and a low-prio task shares a lock
|
||||
[this is a quite common scenario for most non-trivial RT applications],
|
||||
even if all critical sections are coded carefully to be deterministic
|
||||
(i.e. all critical sections are short in duration and only execute a
|
||||
limited number of instructions), the kernel cannot guarantee any
|
||||
deterministic execution of the high-prio task: any medium-priority task
|
||||
could preempt the low-prio task while it holds the shared lock and
|
||||
executes the critical section, and could delay it indefinitely.
|
||||
|
||||
Implementation:
|
||||
---------------
|
||||
|
||||
As mentioned before, the userspace fastpath of PI-enabled pthread
|
||||
mutexes involves no kernel work at all - they behave quite similarly to
|
||||
normal futex-based locks: a 0 value means unlocked, and a value==TID
|
||||
means locked. (This is the same method as used by list-based robust
|
||||
futexes.) Userspace uses atomic ops to lock/unlock these mutexes without
|
||||
entering the kernel.
|
||||
|
||||
To handle the slowpath, we have added two new futex ops:
|
||||
|
||||
FUTEX_LOCK_PI
|
||||
FUTEX_UNLOCK_PI
|
||||
|
||||
If the lock-acquire fastpath fails, [i.e. an atomic transition from 0 to
|
||||
TID fails], then FUTEX_LOCK_PI is called. The kernel does all the
|
||||
remaining work: if there is no futex-queue attached to the futex address
|
||||
yet then the code looks up the task that owns the futex [it has put its
|
||||
own TID into the futex value], and attaches a 'PI state' structure to
|
||||
the futex-queue. The pi_state includes an rt-mutex, which is a PI-aware,
|
||||
kernel-based synchronization object. The 'other' task is made the owner
|
||||
of the rt-mutex, and the FUTEX_WAITERS bit is atomically set in the
|
||||
futex value. Then this task tries to lock the rt-mutex, on which it
|
||||
blocks. Once it returns, it has the mutex acquired, and it sets the
|
||||
futex value to its own TID and returns. Userspace has no other work to
|
||||
perform - it now owns the lock, and futex value contains
|
||||
FUTEX_WAITERS|TID.
|
||||
|
||||
If the unlock side fastpath succeeds, [i.e. userspace manages to do a
|
||||
TID -> 0 atomic transition of the futex value], then no kernel work is
|
||||
triggered.
|
||||
|
||||
If the unlock fastpath fails (because the FUTEX_WAITERS bit is set),
|
||||
then FUTEX_UNLOCK_PI is called, and the kernel unlocks the futex on the
|
||||
behalf of userspace - and it also unlocks the attached
|
||||
pi_state->rt_mutex and thus wakes up any potential waiters.
|
||||
|
||||
Note that under this approach, contrary to previous PI-futex approaches,
|
||||
there is no prior 'registration' of a PI-futex. [which is not quite
|
||||
possible anyway, due to existing ABI properties of pthread mutexes.]
|
||||
|
||||
Also, under this scheme, 'robustness' and 'PI' are two orthogonal
|
||||
properties of futexes, and all four combinations are possible: futex,
|
||||
robust-futex, PI-futex, robust+PI-futex.
|
||||
|
||||
More details about priority inheritance can be found in
|
||||
Documentation/rtmutex.txt.
|
@ -95,7 +95,7 @@ comparison. If the thread has registered a list, then normally the list
|
||||
is empty. If the thread/process crashed or terminated in some incorrect
|
||||
way then the list might be non-empty: in this case the kernel carefully
|
||||
walks the list [not trusting it], and marks all locks that are owned by
|
||||
this thread with the FUTEX_OWNER_DEAD bit, and wakes up one waiter (if
|
||||
this thread with the FUTEX_OWNER_DIED bit, and wakes up one waiter (if
|
||||
any).
|
||||
|
||||
The list is guaranteed to be private and per-thread at do_exit() time,
|
||||
|
781
Documentation/rt-mutex-design.txt
Normal file
781
Documentation/rt-mutex-design.txt
Normal file
@ -0,0 +1,781 @@
|
||||
#
|
||||
# Copyright (c) 2006 Steven Rostedt
|
||||
# Licensed under the GNU Free Documentation License, Version 1.2
|
||||
#
|
||||
|
||||
RT-mutex implementation design
|
||||
------------------------------
|
||||
|
||||
This document tries to describe the design of the rtmutex.c implementation.
|
||||
It doesn't describe the reasons why rtmutex.c exists. For that please see
|
||||
Documentation/rt-mutex.txt. Although this document does explain problems
|
||||
that happen without this code, but that is in the concept to understand
|
||||
what the code actually is doing.
|
||||
|
||||
The goal of this document is to help others understand the priority
|
||||
inheritance (PI) algorithm that is used, as well as reasons for the
|
||||
decisions that were made to implement PI in the manner that was done.
|
||||
|
||||
|
||||
Unbounded Priority Inversion
|
||||
----------------------------
|
||||
|
||||
Priority inversion is when a lower priority process executes while a higher
|
||||
priority process wants to run. This happens for several reasons, and
|
||||
most of the time it can't be helped. Anytime a high priority process wants
|
||||
to use a resource that a lower priority process has (a mutex for example),
|
||||
the high priority process must wait until the lower priority process is done
|
||||
with the resource. This is a priority inversion. What we want to prevent
|
||||
is something called unbounded priority inversion. That is when the high
|
||||
priority process is prevented from running by a lower priority process for
|
||||
an undetermined amount of time.
|
||||
|
||||
The classic example of unbounded priority inversion is were you have three
|
||||
processes, let's call them processes A, B, and C, where A is the highest
|
||||
priority process, C is the lowest, and B is in between. A tries to grab a lock
|
||||
that C owns and must wait and lets C run to release the lock. But in the
|
||||
meantime, B executes, and since B is of a higher priority than C, it preempts C,
|
||||
but by doing so, it is in fact preempting A which is a higher priority process.
|
||||
Now there's no way of knowing how long A will be sleeping waiting for C
|
||||
to release the lock, because for all we know, B is a CPU hog and will
|
||||
never give C a chance to release the lock. This is called unbounded priority
|
||||
inversion.
|
||||
|
||||
Here's a little ASCII art to show the problem.
|
||||
|
||||
grab lock L1 (owned by C)
|
||||
|
|
||||
A ---+
|
||||
C preempted by B
|
||||
|
|
||||
C +----+
|
||||
|
||||
B +-------->
|
||||
B now keeps A from running.
|
||||
|
||||
|
||||
Priority Inheritance (PI)
|
||||
-------------------------
|
||||
|
||||
There are several ways to solve this issue, but other ways are out of scope
|
||||
for this document. Here we only discuss PI.
|
||||
|
||||
PI is where a process inherits the priority of another process if the other
|
||||
process blocks on a lock owned by the current process. To make this easier
|
||||
to understand, let's use the previous example, with processes A, B, and C again.
|
||||
|
||||
This time, when A blocks on the lock owned by C, C would inherit the priority
|
||||
of A. So now if B becomes runnable, it would not preempt C, since C now has
|
||||
the high priority of A. As soon as C releases the lock, it loses its
|
||||
inherited priority, and A then can continue with the resource that C had.
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
Here I explain some terminology that is used in this document to help describe
|
||||
the design that is used to implement PI.
|
||||
|
||||
PI chain - The PI chain is an ordered series of locks and processes that cause
|
||||
processes to inherit priorities from a previous process that is
|
||||
blocked on one of its locks. This is described in more detail
|
||||
later in this document.
|
||||
|
||||
mutex - In this document, to differentiate from locks that implement
|
||||
PI and spin locks that are used in the PI code, from now on
|
||||
the PI locks will be called a mutex.
|
||||
|
||||
lock - In this document from now on, I will use the term lock when
|
||||
referring to spin locks that are used to protect parts of the PI
|
||||
algorithm. These locks disable preemption for UP (when
|
||||
CONFIG_PREEMPT is enabled) and on SMP prevents multiple CPUs from
|
||||
entering critical sections simultaneously.
|
||||
|
||||
spin lock - Same as lock above.
|
||||
|
||||
waiter - A waiter is a struct that is stored on the stack of a blocked
|
||||
process. Since the scope of the waiter is within the code for
|
||||
a process being blocked on the mutex, it is fine to allocate
|
||||
the waiter on the process's stack (local variable). This
|
||||
structure holds a pointer to the task, as well as the mutex that
|
||||
the task is blocked on. It also has the plist node structures to
|
||||
place the task in the waiter_list of a mutex as well as the
|
||||
pi_list of a mutex owner task (described below).
|
||||
|
||||
waiter is sometimes used in reference to the task that is waiting
|
||||
on a mutex. This is the same as waiter->task.
|
||||
|
||||
waiters - A list of processes that are blocked on a mutex.
|
||||
|
||||
top waiter - The highest priority process waiting on a specific mutex.
|
||||
|
||||
top pi waiter - The highest priority process waiting on one of the mutexes
|
||||
that a specific process owns.
|
||||
|
||||
Note: task and process are used interchangeably in this document, mostly to
|
||||
differentiate between two processes that are being described together.
|
||||
|
||||
|
||||
PI chain
|
||||
--------
|
||||
|
||||
The PI chain is a list of processes and mutexes that may cause priority
|
||||
inheritance to take place. Multiple chains may converge, but a chain
|
||||
would never diverge, since a process can't be blocked on more than one
|
||||
mutex at a time.
|
||||
|
||||
Example:
|
||||
|
||||
Process: A, B, C, D, E
|
||||
Mutexes: L1, L2, L3, L4
|
||||
|
||||
A owns: L1
|
||||
B blocked on L1
|
||||
B owns L2
|
||||
C blocked on L2
|
||||
C owns L3
|
||||
D blocked on L3
|
||||
D owns L4
|
||||
E blocked on L4
|
||||
|
||||
The chain would be:
|
||||
|
||||
E->L4->D->L3->C->L2->B->L1->A
|
||||
|
||||
To show where two chains merge, we could add another process F and
|
||||
another mutex L5 where B owns L5 and F is blocked on mutex L5.
|
||||
|
||||
The chain for F would be:
|
||||
|
||||
F->L5->B->L1->A
|
||||
|
||||
Since a process may own more than one mutex, but never be blocked on more than
|
||||
one, the chains merge.
|
||||
|
||||
Here we show both chains:
|
||||
|
||||
E->L4->D->L3->C->L2-+
|
||||
|
|
||||
+->B->L1->A
|
||||
|
|
||||
F->L5-+
|
||||
|
||||
For PI to work, the processes at the right end of these chains (or we may
|
||||
also call it the Top of the chain) must be equal to or higher in priority
|
||||
than the processes to the left or below in the chain.
|
||||
|
||||
Also since a mutex may have more than one process blocked on it, we can
|
||||
have multiple chains merge at mutexes. If we add another process G that is
|
||||
blocked on mutex L2:
|
||||
|
||||
G->L2->B->L1->A
|
||||
|
||||
And once again, to show how this can grow I will show the merging chains
|
||||
again.
|
||||
|
||||
E->L4->D->L3->C-+
|
||||
+->L2-+
|
||||
| |
|
||||
G-+ +->B->L1->A
|
||||
|
|
||||
F->L5-+
|
||||
|
||||
|
||||
Plist
|
||||
-----
|
||||
|
||||
Before I go further and talk about how the PI chain is stored through lists
|
||||
on both mutexes and processes, I'll explain the plist. This is similar to
|
||||
the struct list_head functionality that is already in the kernel.
|
||||
The implementation of plist is out of scope for this document, but it is
|
||||
very important to understand what it does.
|
||||
|
||||
There are a few differences between plist and list, the most important one
|
||||
being that plist is a priority sorted linked list. This means that the
|
||||
priorities of the plist are sorted, such that it takes O(1) to retrieve the
|
||||
highest priority item in the list. Obviously this is useful to store processes
|
||||
based on their priorities.
|
||||
|
||||
Another difference, which is important for implementation, is that, unlike
|
||||
list, the head of the list is a different element than the nodes of a list.
|
||||
So the head of the list is declared as struct plist_head and nodes that will
|
||||
be added to the list are declared as struct plist_node.
|
||||
|
||||
|
||||
Mutex Waiter List
|
||||
-----------------
|
||||
|
||||
Every mutex keeps track of all the waiters that are blocked on itself. The mutex
|
||||
has a plist to store these waiters by priority. This list is protected by
|
||||
a spin lock that is located in the struct of the mutex. This lock is called
|
||||
wait_lock. Since the modification of the waiter list is never done in
|
||||
interrupt context, the wait_lock can be taken without disabling interrupts.
|
||||
|
||||
|
||||
Task PI List
|
||||
------------
|
||||
|
||||
To keep track of the PI chains, each process has its own PI list. This is
|
||||
a list of all top waiters of the mutexes that are owned by the process.
|
||||
Note that this list only holds the top waiters and not all waiters that are
|
||||
blocked on mutexes owned by the process.
|
||||
|
||||
The top of the task's PI list is always the highest priority task that
|
||||
is waiting on a mutex that is owned by the task. So if the task has
|
||||
inherited a priority, it will always be the priority of the task that is
|
||||
at the top of this list.
|
||||
|
||||
This list is stored in the task structure of a process as a plist called
|
||||
pi_list. This list is protected by a spin lock also in the task structure,
|
||||
called pi_lock. This lock may also be taken in interrupt context, so when
|
||||
locking the pi_lock, interrupts must be disabled.
|
||||
|
||||
|
||||
Depth of the PI Chain
|
||||
---------------------
|
||||
|
||||
The maximum depth of the PI chain is not dynamic, and could actually be
|
||||
defined. But is very complex to figure it out, since it depends on all
|
||||
the nesting of mutexes. Let's look at the example where we have 3 mutexes,
|
||||
L1, L2, and L3, and four separate functions func1, func2, func3 and func4.
|
||||
The following shows a locking order of L1->L2->L3, but may not actually
|
||||
be directly nested that way.
|
||||
|
||||
void func1(void)
|
||||
{
|
||||
mutex_lock(L1);
|
||||
|
||||
/* do anything */
|
||||
|
||||
mutex_unlock(L1);
|
||||
}
|
||||
|
||||
void func2(void)
|
||||
{
|
||||
mutex_lock(L1);
|
||||
mutex_lock(L2);
|
||||
|
||||
/* do something */
|
||||
|
||||
mutex_unlock(L2);
|
||||
mutex_unlock(L1);
|
||||
}
|
||||
|
||||
void func3(void)
|
||||
{
|
||||
mutex_lock(L2);
|
||||
mutex_lock(L3);
|
||||
|
||||
/* do something else */
|
||||
|
||||
mutex_unlock(L3);
|
||||
mutex_unlock(L2);
|
||||
}
|
||||
|
||||
void func4(void)
|
||||
{
|
||||
mutex_lock(L3);
|
||||
|
||||
/* do something again */
|
||||
|
||||
mutex_unlock(L3);
|
||||
}
|
||||
|
||||
Now we add 4 processes that run each of these functions separately.
|
||||
Processes A, B, C, and D which run functions func1, func2, func3 and func4
|
||||
respectively, and such that D runs first and A last. With D being preempted
|
||||
in func4 in the "do something again" area, we have a locking that follows:
|
||||
|
||||
D owns L3
|
||||
C blocked on L3
|
||||
C owns L2
|
||||
B blocked on L2
|
||||
B owns L1
|
||||
A blocked on L1
|
||||
|
||||
And thus we have the chain A->L1->B->L2->C->L3->D.
|
||||
|
||||
This gives us a PI depth of 4 (four processes), but looking at any of the
|
||||
functions individually, it seems as though they only have at most a locking
|
||||
depth of two. So, although the locking depth is defined at compile time,
|
||||
it still is very difficult to find the possibilities of that depth.
|
||||
|
||||
Now since mutexes can be defined by user-land applications, we don't want a DOS
|
||||
type of application that nests large amounts of mutexes to create a large
|
||||
PI chain, and have the code holding spin locks while looking at a large
|
||||
amount of data. So to prevent this, the implementation not only implements
|
||||
a maximum lock depth, but also only holds at most two different locks at a
|
||||
time, as it walks the PI chain. More about this below.
|
||||
|
||||
|
||||
Mutex owner and flags
|
||||
---------------------
|
||||
|
||||
The mutex structure contains a pointer to the owner of the mutex. If the
|
||||
mutex is not owned, this owner is set to NULL. Since all architectures
|
||||
have the task structure on at least a four byte alignment (and if this is
|
||||
not true, the rtmutex.c code will be broken!), this allows for the two
|
||||
least significant bits to be used as flags. This part is also described
|
||||
in Documentation/rt-mutex.txt, but will also be briefly described here.
|
||||
|
||||
Bit 0 is used as the "Pending Owner" flag. This is described later.
|
||||
Bit 1 is used as the "Has Waiters" flags. This is also described later
|
||||
in more detail, but is set whenever there are waiters on a mutex.
|
||||
|
||||
|
||||
cmpxchg Tricks
|
||||
--------------
|
||||
|
||||
Some architectures implement an atomic cmpxchg (Compare and Exchange). This
|
||||
is used (when applicable) to keep the fast path of grabbing and releasing
|
||||
mutexes short.
|
||||
|
||||
cmpxchg is basically the following function performed atomically:
|
||||
|
||||
unsigned long _cmpxchg(unsigned long *A, unsigned long *B, unsigned long *C)
|
||||
{
|
||||
unsigned long T = *A;
|
||||
if (*A == *B) {
|
||||
*A = *C;
|
||||
}
|
||||
return T;
|
||||
}
|
||||
#define cmpxchg(a,b,c) _cmpxchg(&a,&b,&c)
|
||||
|
||||
This is really nice to have, since it allows you to only update a variable
|
||||
if the variable is what you expect it to be. You know if it succeeded if
|
||||
the return value (the old value of A) is equal to B.
|
||||
|
||||
The macro rt_mutex_cmpxchg is used to try to lock and unlock mutexes. If
|
||||
the architecture does not support CMPXCHG, then this macro is simply set
|
||||
to fail every time. But if CMPXCHG is supported, then this will
|
||||
help out extremely to keep the fast path short.
|
||||
|
||||
The use of rt_mutex_cmpxchg with the flags in the owner field help optimize
|
||||
the system for architectures that support it. This will also be explained
|
||||
later in this document.
|
||||
|
||||
|
||||
Priority adjustments
|
||||
--------------------
|
||||
|
||||
The implementation of the PI code in rtmutex.c has several places that a
|
||||
process must adjust its priority. With the help of the pi_list of a
|
||||
process this is rather easy to know what needs to be adjusted.
|
||||
|
||||
The functions implementing the task adjustments are rt_mutex_adjust_prio,
|
||||
__rt_mutex_adjust_prio (same as the former, but expects the task pi_lock
|
||||
to already be taken), rt_mutex_get_prio, and rt_mutex_setprio.
|
||||
|
||||
rt_mutex_getprio and rt_mutex_setprio are only used in __rt_mutex_adjust_prio.
|
||||
|
||||
rt_mutex_getprio returns the priority that the task should have. Either the
|
||||
task's own normal priority, or if a process of a higher priority is waiting on
|
||||
a mutex owned by the task, then that higher priority should be returned.
|
||||
Since the pi_list of a task holds an order by priority list of all the top
|
||||
waiters of all the mutexes that the task owns, rt_mutex_getprio simply needs
|
||||
to compare the top pi waiter to its own normal priority, and return the higher
|
||||
priority back.
|
||||
|
||||
(Note: if looking at the code, you will notice that the lower number of
|
||||
prio is returned. This is because the prio field in the task structure
|
||||
is an inverse order of the actual priority. So a "prio" of 5 is
|
||||
of higher priority than a "prio" of 10.)
|
||||
|
||||
__rt_mutex_adjust_prio examines the result of rt_mutex_getprio, and if the
|
||||
result does not equal the task's current priority, then rt_mutex_setprio
|
||||
is called to adjust the priority of the task to the new priority.
|
||||
Note that rt_mutex_setprio is defined in kernel/sched.c to implement the
|
||||
actual change in priority.
|
||||
|
||||
It is interesting to note that __rt_mutex_adjust_prio can either increase
|
||||
or decrease the priority of the task. In the case that a higher priority
|
||||
process has just blocked on a mutex owned by the task, __rt_mutex_adjust_prio
|
||||
would increase/boost the task's priority. But if a higher priority task
|
||||
were for some reason to leave the mutex (timeout or signal), this same function
|
||||
would decrease/unboost the priority of the task. That is because the pi_list
|
||||
always contains the highest priority task that is waiting on a mutex owned
|
||||
by the task, so we only need to compare the priority of that top pi waiter
|
||||
to the normal priority of the given task.
|
||||
|
||||
|
||||
High level overview of the PI chain walk
|
||||
----------------------------------------
|
||||
|
||||
The PI chain walk is implemented by the function rt_mutex_adjust_prio_chain.
|
||||
|
||||
The implementation has gone through several iterations, and has ended up
|
||||
with what we believe is the best. It walks the PI chain by only grabbing
|
||||
at most two locks at a time, and is very efficient.
|
||||
|
||||
The rt_mutex_adjust_prio_chain can be used either to boost or lower process
|
||||
priorities.
|
||||
|
||||
rt_mutex_adjust_prio_chain is called with a task to be checked for PI
|
||||
(de)boosting (the owner of a mutex that a process is blocking on), a flag to
|
||||
check for deadlocking, the mutex that the task owns, and a pointer to a waiter
|
||||
that is the process's waiter struct that is blocked on the mutex (although this
|
||||
parameter may be NULL for deboosting).
|
||||
|
||||
For this explanation, I will not mention deadlock detection. This explanation
|
||||
will try to stay at a high level.
|
||||
|
||||
When this function is called, there are no locks held. That also means
|
||||
that the state of the owner and lock can change when entered into this function.
|
||||
|
||||
Before this function is called, the task has already had rt_mutex_adjust_prio
|
||||
performed on it. This means that the task is set to the priority that it
|
||||
should be at, but the plist nodes of the task's waiter have not been updated
|
||||
with the new priorities, and that this task may not be in the proper locations
|
||||
in the pi_lists and wait_lists that the task is blocked on. This function
|
||||
solves all that.
|
||||
|
||||
A loop is entered, where task is the owner to be checked for PI changes that
|
||||
was passed by parameter (for the first iteration). The pi_lock of this task is
|
||||
taken to prevent any more changes to the pi_list of the task. This also
|
||||
prevents new tasks from completing the blocking on a mutex that is owned by this
|
||||
task.
|
||||
|
||||
If the task is not blocked on a mutex then the loop is exited. We are at
|
||||
the top of the PI chain.
|
||||
|
||||
A check is now done to see if the original waiter (the process that is blocked
|
||||
on the current mutex) is the top pi waiter of the task. That is, is this
|
||||
waiter on the top of the task's pi_list. If it is not, it either means that
|
||||
there is another process higher in priority that is blocked on one of the
|
||||
mutexes that the task owns, or that the waiter has just woken up via a signal
|
||||
or timeout and has left the PI chain. In either case, the loop is exited, since
|
||||
we don't need to do any more changes to the priority of the current task, or any
|
||||
task that owns a mutex that this current task is waiting on. A priority chain
|
||||
walk is only needed when a new top pi waiter is made to a task.
|
||||
|
||||
The next check sees if the task's waiter plist node has the priority equal to
|
||||
the priority the task is set at. If they are equal, then we are done with
|
||||
the loop. Remember that the function started with the priority of the
|
||||
task adjusted, but the plist nodes that hold the task in other processes
|
||||
pi_lists have not been adjusted.
|
||||
|
||||
Next, we look at the mutex that the task is blocked on. The mutex's wait_lock
|
||||
is taken. This is done by a spin_trylock, because the locking order of the
|
||||
pi_lock and wait_lock goes in the opposite direction. If we fail to grab the
|
||||
lock, the pi_lock is released, and we restart the loop.
|
||||
|
||||
Now that we have both the pi_lock of the task as well as the wait_lock of
|
||||
the mutex the task is blocked on, we update the task's waiter's plist node
|
||||
that is located on the mutex's wait_list.
|
||||
|
||||
Now we release the pi_lock of the task.
|
||||
|
||||
Next the owner of the mutex has its pi_lock taken, so we can update the
|
||||
task's entry in the owner's pi_list. If the task is the highest priority
|
||||
process on the mutex's wait_list, then we remove the previous top waiter
|
||||
from the owner's pi_list, and replace it with the task.
|
||||
|
||||
Note: It is possible that the task was the current top waiter on the mutex,
|
||||
in which case the task is not yet on the pi_list of the waiter. This
|
||||
is OK, since plist_del does nothing if the plist node is not on any
|
||||
list.
|
||||
|
||||
If the task was not the top waiter of the mutex, but it was before we
|
||||
did the priority updates, that means we are deboosting/lowering the
|
||||
task. In this case, the task is removed from the pi_list of the owner,
|
||||
and the new top waiter is added.
|
||||
|
||||
Lastly, we unlock both the pi_lock of the task, as well as the mutex's
|
||||
wait_lock, and continue the loop again. On the next iteration of the
|
||||
loop, the previous owner of the mutex will be the task that will be
|
||||
processed.
|
||||
|
||||
Note: One might think that the owner of this mutex might have changed
|
||||
since we just grab the mutex's wait_lock. And one could be right.
|
||||
The important thing to remember is that the owner could not have
|
||||
become the task that is being processed in the PI chain, since
|
||||
we have taken that task's pi_lock at the beginning of the loop.
|
||||
So as long as there is an owner of this mutex that is not the same
|
||||
process as the tasked being worked on, we are OK.
|
||||
|
||||
Looking closely at the code, one might be confused. The check for the
|
||||
end of the PI chain is when the task isn't blocked on anything or the
|
||||
task's waiter structure "task" element is NULL. This check is
|
||||
protected only by the task's pi_lock. But the code to unlock the mutex
|
||||
sets the task's waiter structure "task" element to NULL with only
|
||||
the protection of the mutex's wait_lock, which was not taken yet.
|
||||
Isn't this a race condition if the task becomes the new owner?
|
||||
|
||||
The answer is No! The trick is the spin_trylock of the mutex's
|
||||
wait_lock. If we fail that lock, we release the pi_lock of the
|
||||
task and continue the loop, doing the end of PI chain check again.
|
||||
|
||||
In the code to release the lock, the wait_lock of the mutex is held
|
||||
the entire time, and it is not let go when we grab the pi_lock of the
|
||||
new owner of the mutex. So if the switch of a new owner were to happen
|
||||
after the check for end of the PI chain and the grabbing of the
|
||||
wait_lock, the unlocking code would spin on the new owner's pi_lock
|
||||
but never give up the wait_lock. So the PI chain loop is guaranteed to
|
||||
fail the spin_trylock on the wait_lock, release the pi_lock, and
|
||||
try again.
|
||||
|
||||
If you don't quite understand the above, that's OK. You don't have to,
|
||||
unless you really want to make a proof out of it ;)
|
||||
|
||||
|
||||
Pending Owners and Lock stealing
|
||||
--------------------------------
|
||||
|
||||
One of the flags in the owner field of the mutex structure is "Pending Owner".
|
||||
What this means is that an owner was chosen by the process releasing the
|
||||
mutex, but that owner has yet to wake up and actually take the mutex.
|
||||
|
||||
Why is this important? Why can't we just give the mutex to another process
|
||||
and be done with it?
|
||||
|
||||
The PI code is to help with real-time processes, and to let the highest
|
||||
priority process run as long as possible with little latencies and delays.
|
||||
If a high priority process owns a mutex that a lower priority process is
|
||||
blocked on, when the mutex is released it would be given to the lower priority
|
||||
process. What if the higher priority process wants to take that mutex again.
|
||||
The high priority process would fail to take that mutex that it just gave up
|
||||
and it would need to boost the lower priority process to run with full
|
||||
latency of that critical section (since the low priority process just entered
|
||||
it).
|
||||
|
||||
There's no reason a high priority process that gives up a mutex should be
|
||||
penalized if it tries to take that mutex again. If the new owner of the
|
||||
mutex has not woken up yet, there's no reason that the higher priority process
|
||||
could not take that mutex away.
|
||||
|
||||
To solve this, we introduced Pending Ownership and Lock Stealing. When a
|
||||
new process is given a mutex that it was blocked on, it is only given
|
||||
pending ownership. This means that it's the new owner, unless a higher
|
||||
priority process comes in and tries to grab that mutex. If a higher priority
|
||||
process does come along and wants that mutex, we let the higher priority
|
||||
process "steal" the mutex from the pending owner (only if it is still pending)
|
||||
and continue with the mutex.
|
||||
|
||||
|
||||
Taking of a mutex (The walk through)
|
||||
------------------------------------
|
||||
|
||||
OK, now let's take a look at the detailed walk through of what happens when
|
||||
taking a mutex.
|
||||
|
||||
The first thing that is tried is the fast taking of the mutex. This is
|
||||
done when we have CMPXCHG enabled (otherwise the fast taking automatically
|
||||
fails). Only when the owner field of the mutex is NULL can the lock be
|
||||
taken with the CMPXCHG and nothing else needs to be done.
|
||||
|
||||
If there is contention on the lock, whether it is owned or pending owner
|
||||
we go about the slow path (rt_mutex_slowlock).
|
||||
|
||||
The slow path function is where the task's waiter structure is created on
|
||||
the stack. This is because the waiter structure is only needed for the
|
||||
scope of this function. The waiter structure holds the nodes to store
|
||||
the task on the wait_list of the mutex, and if need be, the pi_list of
|
||||
the owner.
|
||||
|
||||
The wait_lock of the mutex is taken since the slow path of unlocking the
|
||||
mutex also takes this lock.
|
||||
|
||||
We then call try_to_take_rt_mutex. This is where the architecture that
|
||||
does not implement CMPXCHG would always grab the lock (if there's no
|
||||
contention).
|
||||
|
||||
try_to_take_rt_mutex is used every time the task tries to grab a mutex in the
|
||||
slow path. The first thing that is done here is an atomic setting of
|
||||
the "Has Waiters" flag of the mutex's owner field. Yes, this could really
|
||||
be false, because if the the mutex has no owner, there are no waiters and
|
||||
the current task also won't have any waiters. But we don't have the lock
|
||||
yet, so we assume we are going to be a waiter. The reason for this is to
|
||||
play nice for those architectures that do have CMPXCHG. By setting this flag
|
||||
now, the owner of the mutex can't release the mutex without going into the
|
||||
slow unlock path, and it would then need to grab the wait_lock, which this
|
||||
code currently holds. So setting the "Has Waiters" flag forces the owner
|
||||
to synchronize with this code.
|
||||
|
||||
Now that we know that we can't have any races with the owner releasing the
|
||||
mutex, we check to see if we can take the ownership. This is done if the
|
||||
mutex doesn't have a owner, or if we can steal the mutex from a pending
|
||||
owner. Let's look at the situations we have here.
|
||||
|
||||
1) Has owner that is pending
|
||||
----------------------------
|
||||
|
||||
The mutex has a owner, but it hasn't woken up and the mutex flag
|
||||
"Pending Owner" is set. The first check is to see if the owner isn't the
|
||||
current task. This is because this function is also used for the pending
|
||||
owner to grab the mutex. When a pending owner wakes up, it checks to see
|
||||
if it can take the mutex, and this is done if the owner is already set to
|
||||
itself. If so, we succeed and leave the function, clearing the "Pending
|
||||
Owner" bit.
|
||||
|
||||
If the pending owner is not current, we check to see if the current priority is
|
||||
higher than the pending owner. If not, we fail the function and return.
|
||||
|
||||
There's also something special about a pending owner. That is a pending owner
|
||||
is never blocked on a mutex. So there is no PI chain to worry about. It also
|
||||
means that if the mutex doesn't have any waiters, there's no accounting needed
|
||||
to update the pending owner's pi_list, since we only worry about processes
|
||||
blocked on the current mutex.
|
||||
|
||||
If there are waiters on this mutex, and we just stole the ownership, we need
|
||||
to take the top waiter, remove it from the pi_list of the pending owner, and
|
||||
add it to the current pi_list. Note that at this moment, the pending owner
|
||||
is no longer on the list of waiters. This is fine, since the pending owner
|
||||
would add itself back when it realizes that it had the ownership stolen
|
||||
from itself. When the pending owner tries to grab the mutex, it will fail
|
||||
in try_to_take_rt_mutex if the owner field points to another process.
|
||||
|
||||
2) No owner
|
||||
-----------
|
||||
|
||||
If there is no owner (or we successfully stole the lock), we set the owner
|
||||
of the mutex to current, and set the flag of "Has Waiters" if the current
|
||||
mutex actually has waiters, or we clear the flag if it doesn't. See, it was
|
||||
OK that we set that flag early, since now it is cleared.
|
||||
|
||||
3) Failed to grab ownership
|
||||
---------------------------
|
||||
|
||||
The most interesting case is when we fail to take ownership. This means that
|
||||
there exists an owner, or there's a pending owner with equal or higher
|
||||
priority than the current task.
|
||||
|
||||
We'll continue on the failed case.
|
||||
|
||||
If the mutex has a timeout, we set up a timer to go off to break us out
|
||||
of this mutex if we failed to get it after a specified amount of time.
|
||||
|
||||
Now we enter a loop that will continue to try to take ownership of the mutex, or
|
||||
fail from a timeout or signal.
|
||||
|
||||
Once again we try to take the mutex. This will usually fail the first time
|
||||
in the loop, since it had just failed to get the mutex. But the second time
|
||||
in the loop, this would likely succeed, since the task would likely be
|
||||
the pending owner.
|
||||
|
||||
If the mutex is TASK_INTERRUPTIBLE a check for signals and timeout is done
|
||||
here.
|
||||
|
||||
The waiter structure has a "task" field that points to the task that is blocked
|
||||
on the mutex. This field can be NULL the first time it goes through the loop
|
||||
or if the task is a pending owner and had it's mutex stolen. If the "task"
|
||||
field is NULL then we need to set up the accounting for it.
|
||||
|
||||
Task blocks on mutex
|
||||
--------------------
|
||||
|
||||
The accounting of a mutex and process is done with the waiter structure of
|
||||
the process. The "task" field is set to the process, and the "lock" field
|
||||
to the mutex. The plist nodes are initialized to the processes current
|
||||
priority.
|
||||
|
||||
Since the wait_lock was taken at the entry of the slow lock, we can safely
|
||||
add the waiter to the wait_list. If the current process is the highest
|
||||
priority process currently waiting on this mutex, then we remove the
|
||||
previous top waiter process (if it exists) from the pi_list of the owner,
|
||||
and add the current process to that list. Since the pi_list of the owner
|
||||
has changed, we call rt_mutex_adjust_prio on the owner to see if the owner
|
||||
should adjust its priority accordingly.
|
||||
|
||||
If the owner is also blocked on a lock, and had its pi_list changed
|
||||
(or deadlock checking is on), we unlock the wait_lock of the mutex and go ahead
|
||||
and run rt_mutex_adjust_prio_chain on the owner, as described earlier.
|
||||
|
||||
Now all locks are released, and if the current process is still blocked on a
|
||||
mutex (waiter "task" field is not NULL), then we go to sleep (call schedule).
|
||||
|
||||
Waking up in the loop
|
||||
---------------------
|
||||
|
||||
The schedule can then wake up for a few reasons.
|
||||
1) we were given pending ownership of the mutex.
|
||||
2) we received a signal and was TASK_INTERRUPTIBLE
|
||||
3) we had a timeout and was TASK_INTERRUPTIBLE
|
||||
|
||||
In any of these cases, we continue the loop and once again try to grab the
|
||||
ownership of the mutex. If we succeed, we exit the loop, otherwise we continue
|
||||
and on signal and timeout, will exit the loop, or if we had the mutex stolen
|
||||
we just simply add ourselves back on the lists and go back to sleep.
|
||||
|
||||
Note: For various reasons, because of timeout and signals, the steal mutex
|
||||
algorithm needs to be careful. This is because the current process is
|
||||
still on the wait_list. And because of dynamic changing of priorities,
|
||||
especially on SCHED_OTHER tasks, the current process can be the
|
||||
highest priority task on the wait_list.
|
||||
|
||||
Failed to get mutex on Timeout or Signal
|
||||
----------------------------------------
|
||||
|
||||
If a timeout or signal occurred, the waiter's "task" field would not be
|
||||
NULL and the task needs to be taken off the wait_list of the mutex and perhaps
|
||||
pi_list of the owner. If this process was a high priority process, then
|
||||
the rt_mutex_adjust_prio_chain needs to be executed again on the owner,
|
||||
but this time it will be lowering the priorities.
|
||||
|
||||
|
||||
Unlocking the Mutex
|
||||
-------------------
|
||||
|
||||
The unlocking of a mutex also has a fast path for those architectures with
|
||||
CMPXCHG. Since the taking of a mutex on contention always sets the
|
||||
"Has Waiters" flag of the mutex's owner, we use this to know if we need to
|
||||
take the slow path when unlocking the mutex. If the mutex doesn't have any
|
||||
waiters, the owner field of the mutex would equal the current process and
|
||||
the mutex can be unlocked by just replacing the owner field with NULL.
|
||||
|
||||
If the owner field has the "Has Waiters" bit set (or CMPXCHG is not available),
|
||||
the slow unlock path is taken.
|
||||
|
||||
The first thing done in the slow unlock path is to take the wait_lock of the
|
||||
mutex. This synchronizes the locking and unlocking of the mutex.
|
||||
|
||||
A check is made to see if the mutex has waiters or not. On architectures that
|
||||
do not have CMPXCHG, this is the location that the owner of the mutex will
|
||||
determine if a waiter needs to be awoken or not. On architectures that
|
||||
do have CMPXCHG, that check is done in the fast path, but it is still needed
|
||||
in the slow path too. If a waiter of a mutex woke up because of a signal
|
||||
or timeout between the time the owner failed the fast path CMPXCHG check and
|
||||
the grabbing of the wait_lock, the mutex may not have any waiters, thus the
|
||||
owner still needs to make this check. If there are no waiters than the mutex
|
||||
owner field is set to NULL, the wait_lock is released and nothing more is
|
||||
needed.
|
||||
|
||||
If there are waiters, then we need to wake one up and give that waiter
|
||||
pending ownership.
|
||||
|
||||
On the wake up code, the pi_lock of the current owner is taken. The top
|
||||
waiter of the lock is found and removed from the wait_list of the mutex
|
||||
as well as the pi_list of the current owner. The task field of the new
|
||||
pending owner's waiter structure is set to NULL, and the owner field of the
|
||||
mutex is set to the new owner with the "Pending Owner" bit set, as well
|
||||
as the "Has Waiters" bit if there still are other processes blocked on the
|
||||
mutex.
|
||||
|
||||
The pi_lock of the previous owner is released, and the new pending owner's
|
||||
pi_lock is taken. Remember that this is the trick to prevent the race
|
||||
condition in rt_mutex_adjust_prio_chain from adding itself as a waiter
|
||||
on the mutex.
|
||||
|
||||
We now clear the "pi_blocked_on" field of the new pending owner, and if
|
||||
the mutex still has waiters pending, we add the new top waiter to the pi_list
|
||||
of the pending owner.
|
||||
|
||||
Finally we unlock the pi_lock of the pending owner and wake it up.
|
||||
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
For updates on this document, please email Steven Rostedt <rostedt@goodmis.org>
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Author: Steven Rostedt <rostedt@goodmis.org>
|
||||
|
||||
Reviewers: Ingo Molnar, Thomas Gleixner, Thomas Duetsch, and Randy Dunlap
|
||||
|
||||
Updates
|
||||
-------
|
||||
|
||||
This document was originally written for 2.6.17-rc3-mm1
|
79
Documentation/rt-mutex.txt
Normal file
79
Documentation/rt-mutex.txt
Normal file
@ -0,0 +1,79 @@
|
||||
RT-mutex subsystem with PI support
|
||||
----------------------------------
|
||||
|
||||
RT-mutexes with priority inheritance are used to support PI-futexes,
|
||||
which enable pthread_mutex_t priority inheritance attributes
|
||||
(PTHREAD_PRIO_INHERIT). [See Documentation/pi-futex.txt for more details
|
||||
about PI-futexes.]
|
||||
|
||||
This technology was developed in the -rt tree and streamlined for
|
||||
pthread_mutex support.
|
||||
|
||||
Basic principles:
|
||||
-----------------
|
||||
|
||||
RT-mutexes extend the semantics of simple mutexes by the priority
|
||||
inheritance protocol.
|
||||
|
||||
A low priority owner of a rt-mutex inherits the priority of a higher
|
||||
priority waiter until the rt-mutex is released. If the temporarily
|
||||
boosted owner blocks on a rt-mutex itself it propagates the priority
|
||||
boosting to the owner of the other rt_mutex it gets blocked on. The
|
||||
priority boosting is immediately removed once the rt_mutex has been
|
||||
unlocked.
|
||||
|
||||
This approach allows us to shorten the block of high-prio tasks on
|
||||
mutexes which protect shared resources. Priority inheritance is not a
|
||||
magic bullet for poorly designed applications, but it allows
|
||||
well-designed applications to use userspace locks in critical parts of
|
||||
an high priority thread, without losing determinism.
|
||||
|
||||
The enqueueing of the waiters into the rtmutex waiter list is done in
|
||||
priority order. For same priorities FIFO order is chosen. For each
|
||||
rtmutex, only the top priority waiter is enqueued into the owner's
|
||||
priority waiters list. This list too queues in priority order. Whenever
|
||||
the top priority waiter of a task changes (for example it timed out or
|
||||
got a signal), the priority of the owner task is readjusted. [The
|
||||
priority enqueueing is handled by "plists", see include/linux/plist.h
|
||||
for more details.]
|
||||
|
||||
RT-mutexes are optimized for fastpath operations and have no internal
|
||||
locking overhead when locking an uncontended mutex or unlocking a mutex
|
||||
without waiters. The optimized fastpath operations require cmpxchg
|
||||
support. [If that is not available then the rt-mutex internal spinlock
|
||||
is used]
|
||||
|
||||
The state of the rt-mutex is tracked via the owner field of the rt-mutex
|
||||
structure:
|
||||
|
||||
rt_mutex->owner holds the task_struct pointer of the owner. Bit 0 and 1
|
||||
are used to keep track of the "owner is pending" and "rtmutex has
|
||||
waiters" state.
|
||||
|
||||
owner bit1 bit0
|
||||
NULL 0 0 mutex is free (fast acquire possible)
|
||||
NULL 0 1 invalid state
|
||||
NULL 1 0 Transitional state*
|
||||
NULL 1 1 invalid state
|
||||
taskpointer 0 0 mutex is held (fast release possible)
|
||||
taskpointer 0 1 task is pending owner
|
||||
taskpointer 1 0 mutex is held and has waiters
|
||||
taskpointer 1 1 task is pending owner and mutex has waiters
|
||||
|
||||
Pending-ownership handling is a performance optimization:
|
||||
pending-ownership is assigned to the first (highest priority) waiter of
|
||||
the mutex, when the mutex is released. The thread is woken up and once
|
||||
it starts executing it can acquire the mutex. Until the mutex is taken
|
||||
by it (bit 0 is cleared) a competing higher priority thread can "steal"
|
||||
the mutex which puts the woken up thread back on the waiters list.
|
||||
|
||||
The pending-ownership optimization is especially important for the
|
||||
uninterrupted workflow of high-prio tasks which repeatedly
|
||||
takes/releases locks that have lower-prio waiters. Without this
|
||||
optimization the higher-prio thread would ping-pong to the lower-prio
|
||||
task [because at unlock time we always assign a new owner].
|
||||
|
||||
(*) The "mutex has waiters" bit gets set to take the lock. If the lock
|
||||
doesn't already have an owner, this bit is quickly cleared if there are
|
||||
no waiters. So this is a transitional state to synchronize with looking
|
||||
at the owner field of the mutex and the mutex owner releasing the lock.
|
@ -472,6 +472,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-darla20
|
||||
------------------
|
||||
|
||||
Module for Echoaudio Darla20
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-darla24
|
||||
------------------
|
||||
|
||||
Module for Echoaudio Darla24
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-dt019x
|
||||
-----------------
|
||||
|
||||
@ -499,6 +515,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-echo3g
|
||||
-----------------
|
||||
|
||||
Module for Echoaudio 3G cards (Gina3G/Layla3G)
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-emu10k1
|
||||
------------------
|
||||
|
||||
@ -657,6 +681,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-gina20
|
||||
-----------------
|
||||
|
||||
Module for Echoaudio Gina20
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-gina24
|
||||
-----------------
|
||||
|
||||
Module for Echoaudio Gina24
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-gusclassic
|
||||
---------------------
|
||||
|
||||
@ -760,12 +800,18 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
basic fixed pin assignment w/o SPDIF
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC882/883/885
|
||||
ALC882/885
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stck-dig 6-jack digital with SPDIF I/O
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861
|
||||
ALC883/888
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
6stack-dig-demo 6-stack digital for Intel demo board
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861/660
|
||||
3stack 3-jack
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack with SPDIF I/O
|
||||
@ -937,6 +983,30 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
driver isn't configured properly or you want to try another
|
||||
type for testing.
|
||||
|
||||
Module snd-indigo
|
||||
-----------------
|
||||
|
||||
Module for Echoaudio Indigo
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-indigodj
|
||||
-------------------
|
||||
|
||||
Module for Echoaudio Indigo DJ
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-indigoio
|
||||
-------------------
|
||||
|
||||
Module for Echoaudio Indigo IO
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-intel8x0
|
||||
-------------------
|
||||
|
||||
@ -1036,6 +1106,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
This module supports multiple cards.
|
||||
|
||||
Module snd-layla20
|
||||
------------------
|
||||
|
||||
Module for Echoaudio Layla20
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-layla24
|
||||
------------------
|
||||
|
||||
Module for Echoaudio Layla24
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-maestro3
|
||||
-------------------
|
||||
|
||||
@ -1056,6 +1142,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-mia
|
||||
---------------
|
||||
|
||||
Module for Echoaudio Mia
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-miro
|
||||
---------------
|
||||
|
||||
@ -1088,6 +1182,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
When no hotplug fw loader is available, you need to load the
|
||||
firmware via mixartloader utility in alsa-tools package.
|
||||
|
||||
Module snd-mona
|
||||
---------------
|
||||
|
||||
Module for Echoaudio Mona
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-mpu401
|
||||
-----------------
|
||||
|
||||
|
212
Documentation/video4linux/README.pvrusb2
Normal file
212
Documentation/video4linux/README.pvrusb2
Normal file
@ -0,0 +1,212 @@
|
||||
|
||||
$Id$
|
||||
Mike Isely <isely@pobox.com>
|
||||
|
||||
pvrusb2 driver
|
||||
|
||||
Background:
|
||||
|
||||
This driver is intended for the "Hauppauge WinTV PVR USB 2.0", which
|
||||
is a USB 2.0 hosted TV Tuner. This driver is a work in progress.
|
||||
Its history started with the reverse-engineering effort by Björn
|
||||
Danielsson <pvrusb2@dax.nu> whose web page can be found here:
|
||||
|
||||
http://pvrusb2.dax.nu/
|
||||
|
||||
From there Aurelien Alleaume <slts@free.fr> began an effort to
|
||||
create a video4linux compatible driver. I began with Aurelien's
|
||||
last known snapshot and evolved the driver to the state it is in
|
||||
here.
|
||||
|
||||
More information on this driver can be found at:
|
||||
|
||||
http://www.isely.net/pvrusb2.html
|
||||
|
||||
|
||||
This driver has a strong separation of layers. They are very
|
||||
roughly:
|
||||
|
||||
1a. Low level wire-protocol implementation with the device.
|
||||
|
||||
1b. I2C adaptor implementation and corresponding I2C client drivers
|
||||
implemented elsewhere in V4L.
|
||||
|
||||
1c. High level hardware driver implementation which coordinates all
|
||||
activities that ensure correct operation of the device.
|
||||
|
||||
2. A "context" layer which manages instancing of driver, setup,
|
||||
tear-down, arbitration, and interaction with high level
|
||||
interfaces appropriately as devices are hotplugged in the
|
||||
system.
|
||||
|
||||
3. High level interfaces which glue the driver to various published
|
||||
Linux APIs (V4L, sysfs, maybe DVB in the future).
|
||||
|
||||
The most important shearing layer is between the top 2 layers. A
|
||||
lot of work went into the driver to ensure that any kind of
|
||||
conceivable API can be laid on top of the core driver. (Yes, the
|
||||
driver internally leverages V4L to do its work but that really has
|
||||
nothing to do with the API published by the driver to the outside
|
||||
world.) The architecture allows for different APIs to
|
||||
simultaneously access the driver. I have a strong sense of fairness
|
||||
about APIs and also feel that it is a good design principle to keep
|
||||
implementation and interface isolated from each other. Thus while
|
||||
right now the V4L high level interface is the most complete, the
|
||||
sysfs high level interface will work equally well for similar
|
||||
functions, and there's no reason I see right now why it shouldn't be
|
||||
possible to produce a DVB high level interface that can sit right
|
||||
alongside V4L.
|
||||
|
||||
NOTE: Complete documentation on the pvrusb2 driver is contained in
|
||||
the html files within the doc directory; these are exactly the same
|
||||
as what is on the web site at the time. Browse those files
|
||||
(especially the FAQ) before asking questions.
|
||||
|
||||
|
||||
Building
|
||||
|
||||
To build these modules essentially amounts to just running "Make",
|
||||
but you need the kernel source tree nearby and you will likely also
|
||||
want to set a few controlling environment variables first in order
|
||||
to link things up with that source tree. Please see the Makefile
|
||||
here for comments that explain how to do that.
|
||||
|
||||
|
||||
Source file list / functional overview:
|
||||
|
||||
(Note: The term "module" used below generally refers to loosely
|
||||
defined functional units within the pvrusb2 driver and bears no
|
||||
relation to the Linux kernel's concept of a loadable module.)
|
||||
|
||||
pvrusb2-audio.[ch] - This is glue logic that resides between this
|
||||
driver and the msp3400.ko I2C client driver (which is found
|
||||
elsewhere in V4L).
|
||||
|
||||
pvrusb2-context.[ch] - This module implements the context for an
|
||||
instance of the driver. Everything else eventually ties back to
|
||||
or is otherwise instanced within the data structures implemented
|
||||
here. Hotplugging is ultimately coordinated here. All high level
|
||||
interfaces tie into the driver through this module. This module
|
||||
helps arbitrate each interface's access to the actual driver core,
|
||||
and is designed to allow concurrent access through multiple
|
||||
instances of multiple interfaces (thus you can for example change
|
||||
the tuner's frequency through sysfs while simultaneously streaming
|
||||
video through V4L out to an instance of mplayer).
|
||||
|
||||
pvrusb2-debug.h - This header defines a printk() wrapper and a mask
|
||||
of debugging bit definitions for the various kinds of debug
|
||||
messages that can be enabled within the driver.
|
||||
|
||||
pvrusb2-debugifc.[ch] - This module implements a crude command line
|
||||
oriented debug interface into the driver. Aside from being part
|
||||
of the process for implementing manual firmware extraction (see
|
||||
the pvrusb2 web site mentioned earlier), probably I'm the only one
|
||||
who has ever used this. It is mainly a debugging aid.
|
||||
|
||||
pvrusb2-eeprom.[ch] - This is glue logic that resides between this
|
||||
driver the tveeprom.ko module, which is itself implemented
|
||||
elsewhere in V4L.
|
||||
|
||||
pvrusb2-encoder.[ch] - This module implements all protocol needed to
|
||||
interact with the Conexant mpeg2 encoder chip within the pvrusb2
|
||||
device. It is a crude echo of corresponding logic in ivtv,
|
||||
however the design goals (strict isolation) and physical layer
|
||||
(proxy through USB instead of PCI) are enough different that this
|
||||
implementation had to be completely different.
|
||||
|
||||
pvrusb2-hdw-internal.h - This header defines the core data structure
|
||||
in the driver used to track ALL internal state related to control
|
||||
of the hardware. Nobody outside of the core hardware-handling
|
||||
modules should have any business using this header. All external
|
||||
access to the driver should be through one of the high level
|
||||
interfaces (e.g. V4L, sysfs, etc), and in fact even those high
|
||||
level interfaces are restricted to the API defined in
|
||||
pvrusb2-hdw.h and NOT this header.
|
||||
|
||||
pvrusb2-hdw.h - This header defines the full internal API for
|
||||
controlling the hardware. High level interfaces (e.g. V4L, sysfs)
|
||||
will work through here.
|
||||
|
||||
pvrusb2-hdw.c - This module implements all the various bits of logic
|
||||
that handle overall control of a specific pvrusb2 device.
|
||||
(Policy, instantiation, and arbitration of pvrusb2 devices fall
|
||||
within the jurisdiction of pvrusb-context not here).
|
||||
|
||||
pvrusb2-i2c-chips-*.c - These modules implement the glue logic to
|
||||
tie together and configure various I2C modules as they attach to
|
||||
the I2C bus. There are two versions of this file. The "v4l2"
|
||||
version is intended to be used in-tree alongside V4L, where we
|
||||
implement just the logic that makes sense for a pure V4L
|
||||
environment. The "all" version is intended for use outside of
|
||||
V4L, where we might encounter other possibly "challenging" modules
|
||||
from ivtv or older kernel snapshots (or even the support modules
|
||||
in the standalone snapshot).
|
||||
|
||||
pvrusb2-i2c-cmd-v4l1.[ch] - This module implements generic V4L1
|
||||
compatible commands to the I2C modules. It is here where state
|
||||
changes inside the pvrusb2 driver are translated into V4L1
|
||||
commands that are in turn send to the various I2C modules.
|
||||
|
||||
pvrusb2-i2c-cmd-v4l2.[ch] - This module implements generic V4L2
|
||||
compatible commands to the I2C modules. It is here where state
|
||||
changes inside the pvrusb2 driver are translated into V4L2
|
||||
commands that are in turn send to the various I2C modules.
|
||||
|
||||
pvrusb2-i2c-core.[ch] - This module provides an implementation of a
|
||||
kernel-friendly I2C adaptor driver, through which other external
|
||||
I2C client drivers (e.g. msp3400, tuner, lirc) may connect and
|
||||
operate corresponding chips within the the pvrusb2 device. It is
|
||||
through here that other V4L modules can reach into this driver to
|
||||
operate specific pieces (and those modules are in turn driven by
|
||||
glue logic which is coordinated by pvrusb2-hdw, doled out by
|
||||
pvrusb2-context, and then ultimately made available to users
|
||||
through one of the high level interfaces).
|
||||
|
||||
pvrusb2-io.[ch] - This module implements a very low level ring of
|
||||
transfer buffers, required in order to stream data from the
|
||||
device. This module is *very* low level. It only operates the
|
||||
buffers and makes no attempt to define any policy or mechanism for
|
||||
how such buffers might be used.
|
||||
|
||||
pvrusb2-ioread.[ch] - This module layers on top of pvrusb2-io.[ch]
|
||||
to provide a streaming API usable by a read() system call style of
|
||||
I/O. Right now this is the only layer on top of pvrusb2-io.[ch],
|
||||
however the underlying architecture here was intended to allow for
|
||||
other styles of I/O to be implemented with additonal modules, like
|
||||
mmap()'ed buffers or something even more exotic.
|
||||
|
||||
pvrusb2-main.c - This is the top level of the driver. Module level
|
||||
and USB core entry points are here. This is our "main".
|
||||
|
||||
pvrusb2-sysfs.[ch] - This is the high level interface which ties the
|
||||
pvrusb2 driver into sysfs. Through this interface you can do
|
||||
everything with the driver except actually stream data.
|
||||
|
||||
pvrusb2-tuner.[ch] - This is glue logic that resides between this
|
||||
driver and the tuner.ko I2C client driver (which is found
|
||||
elsewhere in V4L).
|
||||
|
||||
pvrusb2-util.h - This header defines some common macros used
|
||||
throughout the driver. These macros are not really specific to
|
||||
the driver, but they had to go somewhere.
|
||||
|
||||
pvrusb2-v4l2.[ch] - This is the high level interface which ties the
|
||||
pvrusb2 driver into video4linux. It is through here that V4L
|
||||
applications can open and operate the driver in the usual V4L
|
||||
ways. Note that **ALL** V4L functionality is published only
|
||||
through here and nowhere else.
|
||||
|
||||
pvrusb2-video-*.[ch] - This is glue logic that resides between this
|
||||
driver and the saa711x.ko I2C client driver (which is found
|
||||
elsewhere in V4L). Note that saa711x.ko used to be known as
|
||||
saa7115.ko in ivtv. There are two versions of this; one is
|
||||
selected depending on the particular saa711[5x].ko that is found.
|
||||
|
||||
pvrusb2.h - This header contains compile time tunable parameters
|
||||
(and at the moment the driver has very little that needs to be
|
||||
tuned).
|
||||
|
||||
|
||||
-Mike Isely
|
||||
isely@pobox.com
|
||||
|
@ -22,78 +22,9 @@
|
||||
to run the program with an "&" to run it in the background!)
|
||||
|
||||
If you want to write a program to be compatible with the PC Watchdog
|
||||
driver, simply do the following:
|
||||
driver, simply use of modify the watchdog test program:
|
||||
Documentation/watchdog/src/watchdog-test.c
|
||||
|
||||
-- Snippet of code --
|
||||
/*
|
||||
* Watchdog Driver Test Program
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* This function simply sends an IOCTL to the driver, which in turn ticks
|
||||
* the PC Watchdog card to reset its internal timer so it doesn't trigger
|
||||
* a computer reset.
|
||||
*/
|
||||
void keep_alive(void)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main program. Run the program with "-d" to disable the card,
|
||||
* or "-e" to enable the card.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
fd = open("/dev/watchdog", O_WRONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Watchdog device not enabled.\n");
|
||||
fflush(stderr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strncasecmp(argv[1], "-d", 2)) {
|
||||
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_DISABLECARD);
|
||||
fprintf(stderr, "Watchdog card disabled.\n");
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
} else if (!strncasecmp(argv[1], "-e", 2)) {
|
||||
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD);
|
||||
fprintf(stderr, "Watchdog card enabled.\n");
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
} else {
|
||||
fprintf(stderr, "-d to disable, -e to enable.\n");
|
||||
fprintf(stderr, "run by itself to tick the card.\n");
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Watchdog Ticking Away!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
while(1) {
|
||||
keep_alive();
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
-- End snippet --
|
||||
|
||||
Other IOCTL functions include:
|
||||
|
||||
|
15
Documentation/watchdog/src/watchdog-simple.c
Normal file
15
Documentation/watchdog/src/watchdog-simple.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
int fd = open("/dev/watchdog", O_WRONLY);
|
||||
if (fd == -1) {
|
||||
perror("watchdog");
|
||||
exit(1);
|
||||
}
|
||||
while (1) {
|
||||
write(fd, "\0", 1);
|
||||
fsync(fd);
|
||||
sleep(10);
|
||||
}
|
||||
}
|
68
Documentation/watchdog/src/watchdog-test.c
Normal file
68
Documentation/watchdog/src/watchdog-test.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Watchdog Driver Test Program
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* This function simply sends an IOCTL to the driver, which in turn ticks
|
||||
* the PC Watchdog card to reset its internal timer so it doesn't trigger
|
||||
* a computer reset.
|
||||
*/
|
||||
void keep_alive(void)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main program. Run the program with "-d" to disable the card,
|
||||
* or "-e" to enable the card.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
fd = open("/dev/watchdog", O_WRONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Watchdog device not enabled.\n");
|
||||
fflush(stderr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strncasecmp(argv[1], "-d", 2)) {
|
||||
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_DISABLECARD);
|
||||
fprintf(stderr, "Watchdog card disabled.\n");
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
} else if (!strncasecmp(argv[1], "-e", 2)) {
|
||||
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD);
|
||||
fprintf(stderr, "Watchdog card enabled.\n");
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
} else {
|
||||
fprintf(stderr, "-d to disable, -e to enable.\n");
|
||||
fprintf(stderr, "run by itself to tick the card.\n");
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Watchdog Ticking Away!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
while(1) {
|
||||
keep_alive();
|
||||
sleep(1);
|
||||
}
|
||||
}
|
@ -34,22 +34,7 @@ activates as soon as /dev/watchdog is opened and will reboot unless
|
||||
the watchdog is pinged within a certain time, this time is called the
|
||||
timeout or margin. The simplest way to ping the watchdog is to write
|
||||
some data to the device. So a very simple watchdog daemon would look
|
||||
like this:
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
int fd=open("/dev/watchdog",O_WRONLY);
|
||||
if (fd==-1) {
|
||||
perror("watchdog");
|
||||
exit(1);
|
||||
}
|
||||
while(1) {
|
||||
write(fd, "\0", 1);
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
like this source file: see Documentation/watchdog/src/watchdog-simple.c
|
||||
|
||||
A more advanced driver could for example check that a HTTP server is
|
||||
still responding before doing the write call to ping the watchdog.
|
||||
@ -110,7 +95,40 @@ current timeout using the GETTIMEOUT ioctl.
|
||||
ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
|
||||
printf("The timeout was is %d seconds\n", timeout);
|
||||
|
||||
Envinronmental monitoring:
|
||||
Pretimeouts:
|
||||
|
||||
Some watchdog timers can be set to have a trigger go off before the
|
||||
actual time they will reset the system. This can be done with an NMI,
|
||||
interrupt, or other mechanism. This allows Linux to record useful
|
||||
information (like panic information and kernel coredumps) before it
|
||||
resets.
|
||||
|
||||
pretimeout = 10;
|
||||
ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
|
||||
|
||||
Note that the pretimeout is the number of seconds before the time
|
||||
when the timeout will go off. It is not the number of seconds until
|
||||
the pretimeout. So, for instance, if you set the timeout to 60 seconds
|
||||
and the pretimeout to 10 seconds, the pretimout will go of in 50
|
||||
seconds. Setting a pretimeout to zero disables it.
|
||||
|
||||
There is also a get function for getting the pretimeout:
|
||||
|
||||
ioctl(fd, WDIOC_GETPRETIMEOUT, &timeout);
|
||||
printf("The pretimeout was is %d seconds\n", timeout);
|
||||
|
||||
Not all watchdog drivers will support a pretimeout.
|
||||
|
||||
Get the number of seconds before reboot:
|
||||
|
||||
Some watchdog drivers have the ability to report the remaining time
|
||||
before the system will reboot. The WDIOC_GETTIMELEFT is the ioctl
|
||||
that returns the number of seconds before reboot.
|
||||
|
||||
ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);
|
||||
printf("The timeout was is %d seconds\n", timeleft);
|
||||
|
||||
Environmental monitoring:
|
||||
|
||||
All watchdog drivers are required return more information about the system,
|
||||
some do temperature, fan and power level monitoring, some can tell you
|
||||
@ -169,6 +187,10 @@ The watchdog saw a keepalive ping since it was last queried.
|
||||
|
||||
WDIOF_SETTIMEOUT Can set/get the timeout
|
||||
|
||||
The watchdog can do pretimeouts.
|
||||
|
||||
WDIOF_PRETIMEOUT Pretimeout (in seconds), get/set
|
||||
|
||||
|
||||
For those drivers that return any bits set in the option field, the
|
||||
GETSTATUS and GETBOOTSTATUS ioctls can be used to ask for the current
|
||||
|
@ -65,28 +65,7 @@ The external event interfaces on the WDT boards are not currently supported.
|
||||
Minor numbers are however allocated for it.
|
||||
|
||||
|
||||
Example Watchdog Driver
|
||||
-----------------------
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int fd=open("/dev/watchdog",O_WRONLY);
|
||||
if(fd==-1)
|
||||
{
|
||||
perror("watchdog");
|
||||
exit(1);
|
||||
}
|
||||
while(1)
|
||||
{
|
||||
write(fd,"\0",1);
|
||||
fsync(fd);
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
Example Watchdog Driver: see Documentation/watchdog/src/watchdog-simple.c
|
||||
|
||||
|
||||
Contact Information
|
||||
|
@ -49,15 +49,15 @@ select_smp_affinity(unsigned int irq)
|
||||
static int last_cpu;
|
||||
int cpu = last_cpu + 1;
|
||||
|
||||
if (!irq_desc[irq].handler->set_affinity || irq_user_affinity[irq])
|
||||
if (!irq_desc[irq].chip->set_affinity || irq_user_affinity[irq])
|
||||
return 1;
|
||||
|
||||
while (!cpu_possible(cpu))
|
||||
cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0);
|
||||
last_cpu = cpu;
|
||||
|
||||
irq_affinity[irq] = cpumask_of_cpu(cpu);
|
||||
irq_desc[irq].handler->set_affinity(irq, cpumask_of_cpu(cpu));
|
||||
irq_desc[irq].affinity = cpumask_of_cpu(cpu);
|
||||
irq_desc[irq].chip->set_affinity(irq, cpumask_of_cpu(cpu));
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
@ -93,7 +93,7 @@ show_interrupts(struct seq_file *p, void *v)
|
||||
for_each_online_cpu(j)
|
||||
seq_printf(p, "%10u ", kstat_cpu(j).irqs[irq]);
|
||||
#endif
|
||||
seq_printf(p, " %14s", irq_desc[irq].handler->typename);
|
||||
seq_printf(p, " %14s", irq_desc[irq].chip->typename);
|
||||
seq_printf(p, " %c%s",
|
||||
(action->flags & SA_INTERRUPT)?'+':' ',
|
||||
action->name);
|
||||
|
@ -233,7 +233,7 @@ void __init
|
||||
init_rtc_irq(void)
|
||||
{
|
||||
irq_desc[RTC_IRQ].status = IRQ_DISABLED;
|
||||
irq_desc[RTC_IRQ].handler = &rtc_irq_type;
|
||||
irq_desc[RTC_IRQ].chip = &rtc_irq_type;
|
||||
setup_irq(RTC_IRQ, &timer_irqaction);
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ init_i8259a_irqs(void)
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
irq_desc[i].status = IRQ_DISABLED;
|
||||
irq_desc[i].handler = &i8259a_irq_type;
|
||||
irq_desc[i].chip = &i8259a_irq_type;
|
||||
}
|
||||
|
||||
setup_irq(2, &cascade);
|
||||
|
@ -120,7 +120,7 @@ init_pyxis_irqs(unsigned long ignore_mask)
|
||||
if ((ignore_mask >> i) & 1)
|
||||
continue;
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &pyxis_irq_type;
|
||||
irq_desc[i].chip = &pyxis_irq_type;
|
||||
}
|
||||
|
||||
setup_irq(16+7, &isa_cascade_irqaction);
|
||||
|
@ -67,7 +67,7 @@ init_srm_irqs(long max, unsigned long ignore_mask)
|
||||
if (i < 64 && ((ignore_mask >> i) & 1))
|
||||
continue;
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &srm_irq_type;
|
||||
irq_desc[i].chip = &srm_irq_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,12 +124,12 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final);
|
||||
|
||||
void
|
||||
pcibios_align_resource(void *data, struct resource *res,
|
||||
unsigned long size, unsigned long align)
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
struct pci_dev *dev = data;
|
||||
struct pci_controller *hose = dev->sysdata;
|
||||
unsigned long alignto;
|
||||
unsigned long start = res->start;
|
||||
resource_size_t start = res->start;
|
||||
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
/* Make sure we start at our min on all hoses */
|
||||
|
@ -481,7 +481,7 @@ register_cpus(void)
|
||||
struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
register_cpu(p, i, NULL);
|
||||
register_cpu(p, i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ alcor_init_irq(void)
|
||||
if (i >= 16+20 && i <= 16+30)
|
||||
continue;
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &alcor_irq_type;
|
||||
irq_desc[i].chip = &alcor_irq_type;
|
||||
}
|
||||
i8259a_irq_type.ack = alcor_isa_mask_and_ack_irq;
|
||||
|
||||
|
@ -124,7 +124,7 @@ common_init_irq(void (*srm_dev_int)(unsigned long v, struct pt_regs *r))
|
||||
|
||||
for (i = 16; i < 35; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &cabriolet_irq_type;
|
||||
irq_desc[i].chip = &cabriolet_irq_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,7 +300,7 @@ init_tsunami_irqs(struct hw_interrupt_type * ops, int imin, int imax)
|
||||
long i;
|
||||
for (i = imin; i <= imax; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = ops;
|
||||
irq_desc[i].chip = ops;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ eb64p_init_irq(void)
|
||||
|
||||
for (i = 16; i < 32; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &eb64p_irq_type;
|
||||
irq_desc[i].chip = &eb64p_irq_type;
|
||||
}
|
||||
|
||||
common_init_isa_dma();
|
||||
|
@ -154,7 +154,7 @@ eiger_init_irq(void)
|
||||
|
||||
for (i = 16; i < 128; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &eiger_irq_type;
|
||||
irq_desc[i].chip = &eiger_irq_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,11 +206,11 @@ jensen_init_irq(void)
|
||||
{
|
||||
init_i8259a_irqs();
|
||||
|
||||
irq_desc[1].handler = &jensen_local_irq_type;
|
||||
irq_desc[4].handler = &jensen_local_irq_type;
|
||||
irq_desc[3].handler = &jensen_local_irq_type;
|
||||
irq_desc[7].handler = &jensen_local_irq_type;
|
||||
irq_desc[9].handler = &jensen_local_irq_type;
|
||||
irq_desc[1].chip = &jensen_local_irq_type;
|
||||
irq_desc[4].chip = &jensen_local_irq_type;
|
||||
irq_desc[3].chip = &jensen_local_irq_type;
|
||||
irq_desc[7].chip = &jensen_local_irq_type;
|
||||
irq_desc[9].chip = &jensen_local_irq_type;
|
||||
|
||||
common_init_isa_dma();
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ init_io7_irqs(struct io7 *io7,
|
||||
/* Set up the lsi irqs. */
|
||||
for (i = 0; i < 128; ++i) {
|
||||
irq_desc[base + i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[base + i].handler = lsi_ops;
|
||||
irq_desc[base + i].chip = lsi_ops;
|
||||
}
|
||||
|
||||
/* Disable the implemented irqs in hardware. */
|
||||
@ -317,7 +317,7 @@ init_io7_irqs(struct io7 *io7,
|
||||
/* Set up the msi irqs. */
|
||||
for (i = 128; i < (128 + 512); ++i) {
|
||||
irq_desc[base + i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[base + i].handler = msi_ops;
|
||||
irq_desc[base + i].chip = msi_ops;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; ++i)
|
||||
@ -335,7 +335,7 @@ marvel_init_irq(void)
|
||||
/* Reserve the legacy irqs. */
|
||||
for (i = 0; i < 16; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED;
|
||||
irq_desc[i].handler = &marvel_legacy_irq_type;
|
||||
irq_desc[i].chip = &marvel_legacy_irq_type;
|
||||
}
|
||||
|
||||
/* Init the io7 irqs. */
|
||||
|
@ -117,7 +117,7 @@ mikasa_init_irq(void)
|
||||
|
||||
for (i = 16; i < 32; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &mikasa_irq_type;
|
||||
irq_desc[i].chip = &mikasa_irq_type;
|
||||
}
|
||||
|
||||
init_i8259a_irqs();
|
||||
|
@ -139,7 +139,7 @@ noritake_init_irq(void)
|
||||
|
||||
for (i = 16; i < 48; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &noritake_irq_type;
|
||||
irq_desc[i].chip = &noritake_irq_type;
|
||||
}
|
||||
|
||||
init_i8259a_irqs();
|
||||
|
@ -180,7 +180,7 @@ rawhide_init_irq(void)
|
||||
|
||||
for (i = 16; i < 128; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &rawhide_irq_type;
|
||||
irq_desc[i].chip = &rawhide_irq_type;
|
||||
}
|
||||
|
||||
init_i8259a_irqs();
|
||||
|
@ -117,7 +117,7 @@ rx164_init_irq(void)
|
||||
rx164_update_irq_hw(0);
|
||||
for (i = 16; i < 40; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &rx164_irq_type;
|
||||
irq_desc[i].chip = &rx164_irq_type;
|
||||
}
|
||||
|
||||
init_i8259a_irqs();
|
||||
|
@ -537,7 +537,7 @@ sable_lynx_init_irq(int nr_irqs)
|
||||
|
||||
for (i = 0; i < nr_irqs; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &sable_lynx_irq_type;
|
||||
irq_desc[i].chip = &sable_lynx_irq_type;
|
||||
}
|
||||
|
||||
common_init_isa_dma();
|
||||
|
@ -154,7 +154,7 @@ takara_init_irq(void)
|
||||
|
||||
for (i = 16; i < 128; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = &takara_irq_type;
|
||||
irq_desc[i].chip = &takara_irq_type;
|
||||
}
|
||||
|
||||
common_init_isa_dma();
|
||||
|
@ -189,7 +189,7 @@ init_titan_irqs(struct hw_interrupt_type * ops, int imin, int imax)
|
||||
long i;
|
||||
for (i = imin; i <= imax; ++i) {
|
||||
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i].handler = ops;
|
||||
irq_desc[i].chip = ops;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,14 +199,14 @@ wildfire_init_irq_per_pca(int qbbno, int pcano)
|
||||
if (i == 2)
|
||||
continue;
|
||||
irq_desc[i+irq_bias].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i+irq_bias].handler = &wildfire_irq_type;
|
||||
irq_desc[i+irq_bias].chip = &wildfire_irq_type;
|
||||
}
|
||||
|
||||
irq_desc[36+irq_bias].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[36+irq_bias].handler = &wildfire_irq_type;
|
||||
irq_desc[36+irq_bias].chip = &wildfire_irq_type;
|
||||
for (i = 40; i < 64; ++i) {
|
||||
irq_desc[i+irq_bias].status = IRQ_DISABLED | IRQ_LEVEL;
|
||||
irq_desc[i+irq_bias].handler = &wildfire_irq_type;
|
||||
irq_desc[i+irq_bias].chip = &wildfire_irq_type;
|
||||
}
|
||||
|
||||
setup_irq(32+irq_bias, &isa_enable);
|
||||
|
@ -188,23 +188,27 @@ config ARCH_IMX
|
||||
|
||||
config ARCH_IOP3XX
|
||||
bool "IOP3xx-based"
|
||||
depends on MMU
|
||||
select PCI
|
||||
help
|
||||
Support for Intel's IOP3XX (XScale) family of processors.
|
||||
|
||||
config ARCH_IXP4XX
|
||||
bool "IXP4xx-based"
|
||||
depends on MMU
|
||||
help
|
||||
Support for Intel's IXP4XX (XScale) family of processors.
|
||||
|
||||
config ARCH_IXP2000
|
||||
bool "IXP2400/2800-based"
|
||||
depends on MMU
|
||||
select PCI
|
||||
help
|
||||
Support for Intel's IXP2400/2800 (XScale) family of processors.
|
||||
|
||||
config ARCH_IXP23XX
|
||||
bool "IXP23XX-based"
|
||||
depends on MMU
|
||||
select PCI
|
||||
help
|
||||
Support for Intel's IXP23xx (XScale) family of processors.
|
||||
@ -229,6 +233,7 @@ config ARCH_PNX4008
|
||||
|
||||
config ARCH_PXA
|
||||
bool "PXA2xx-based"
|
||||
depends on MMU
|
||||
select ARCH_MTD_XIP
|
||||
help
|
||||
Support for Intel's PXA2XX processor line.
|
||||
@ -339,6 +344,10 @@ config XSCALE_PMU
|
||||
depends on CPU_XSCALE && !XSCALE_PMU_TIMER
|
||||
default y
|
||||
|
||||
if !MMU
|
||||
source "arch/arm/Kconfig-nommu"
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
source "arch/arm/common/Kconfig"
|
||||
|
@ -22,6 +22,9 @@ obj-$(CONFIG_PCI) += bios32.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
|
||||
|
||||
obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
|
||||
AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
|
||||
|
||||
obj-$(CONFIG_IWMMXT) += iwmmxt.o
|
||||
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
|
||||
|
||||
|
@ -109,11 +109,13 @@ EXPORT_SYMBOL(memchr);
|
||||
EXPORT_SYMBOL(__memzero);
|
||||
|
||||
/* user mem (segment) */
|
||||
EXPORT_SYMBOL(__arch_copy_from_user);
|
||||
EXPORT_SYMBOL(__arch_copy_to_user);
|
||||
EXPORT_SYMBOL(__arch_clear_user);
|
||||
EXPORT_SYMBOL(__arch_strnlen_user);
|
||||
EXPORT_SYMBOL(__arch_strncpy_from_user);
|
||||
EXPORT_SYMBOL(__strnlen_user);
|
||||
EXPORT_SYMBOL(__strncpy_from_user);
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
EXPORT_SYMBOL(__copy_from_user);
|
||||
EXPORT_SYMBOL(__copy_to_user);
|
||||
EXPORT_SYMBOL(__clear_user);
|
||||
|
||||
EXPORT_SYMBOL(__get_user_1);
|
||||
EXPORT_SYMBOL(__get_user_2);
|
||||
@ -123,6 +125,7 @@ EXPORT_SYMBOL(__put_user_1);
|
||||
EXPORT_SYMBOL(__put_user_2);
|
||||
EXPORT_SYMBOL(__put_user_4);
|
||||
EXPORT_SYMBOL(__put_user_8);
|
||||
#endif
|
||||
|
||||
/* crypto hash */
|
||||
EXPORT_SYMBOL(sha_transform);
|
||||
|
@ -59,6 +59,9 @@ int main(void)
|
||||
DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
|
||||
#ifdef CONFIG_IWMMXT
|
||||
DEFINE(TI_IWMMXT_STATE, offsetof(struct thread_info, fpstate.iwmmxt));
|
||||
#endif
|
||||
#ifdef CONFIG_CRUNCH
|
||||
DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate));
|
||||
#endif
|
||||
BLANK();
|
||||
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
|
||||
|
@ -304,7 +304,7 @@ static inline int pdev_bad_for_parity(struct pci_dev *dev)
|
||||
static void __devinit
|
||||
pdev_fixup_device_resources(struct pci_sys_data *root, struct pci_dev *dev)
|
||||
{
|
||||
unsigned long offset;
|
||||
resource_size_t offset;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
@ -634,9 +634,9 @@ char * __init pcibios_setup(char *str)
|
||||
* which might be mirrored at 0x0100-0x03ff..
|
||||
*/
|
||||
void pcibios_align_resource(void *data, struct resource *res,
|
||||
unsigned long size, unsigned long align)
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
unsigned long start = res->start;
|
||||
resource_size_t start = res->start;
|
||||
|
||||
if (res->flags & IORESOURCE_IO && start & 0x300)
|
||||
start = (start + 0x3ff) & ~0x3ff;
|
||||
|
305
arch/arm/kernel/crunch-bits.S
Normal file
305
arch/arm/kernel/crunch-bits.S
Normal file
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* arch/arm/kernel/crunch-bits.S
|
||||
* Cirrus MaverickCrunch context switching and handling
|
||||
*
|
||||
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
*
|
||||
* Shamelessly stolen from the iWMMXt code by Nicolas Pitre, which is
|
||||
* Copyright (c) 2003-2004, MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/arch/ep93xx-regs.h>
|
||||
|
||||
/*
|
||||
* We can't use hex constants here due to a bug in gas.
|
||||
*/
|
||||
#define CRUNCH_MVDX0 0
|
||||
#define CRUNCH_MVDX1 8
|
||||
#define CRUNCH_MVDX2 16
|
||||
#define CRUNCH_MVDX3 24
|
||||
#define CRUNCH_MVDX4 32
|
||||
#define CRUNCH_MVDX5 40
|
||||
#define CRUNCH_MVDX6 48
|
||||
#define CRUNCH_MVDX7 56
|
||||
#define CRUNCH_MVDX8 64
|
||||
#define CRUNCH_MVDX9 72
|
||||
#define CRUNCH_MVDX10 80
|
||||
#define CRUNCH_MVDX11 88
|
||||
#define CRUNCH_MVDX12 96
|
||||
#define CRUNCH_MVDX13 104
|
||||
#define CRUNCH_MVDX14 112
|
||||
#define CRUNCH_MVDX15 120
|
||||
#define CRUNCH_MVAX0L 128
|
||||
#define CRUNCH_MVAX0M 132
|
||||
#define CRUNCH_MVAX0H 136
|
||||
#define CRUNCH_MVAX1L 140
|
||||
#define CRUNCH_MVAX1M 144
|
||||
#define CRUNCH_MVAX1H 148
|
||||
#define CRUNCH_MVAX2L 152
|
||||
#define CRUNCH_MVAX2M 156
|
||||
#define CRUNCH_MVAX2H 160
|
||||
#define CRUNCH_MVAX3L 164
|
||||
#define CRUNCH_MVAX3M 168
|
||||
#define CRUNCH_MVAX3H 172
|
||||
#define CRUNCH_DSPSC 176
|
||||
|
||||
#define CRUNCH_SIZE 184
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* Lazy switching of crunch coprocessor context
|
||||
*
|
||||
* r10 = struct thread_info pointer
|
||||
* r9 = ret_from_exception
|
||||
* lr = undefined instr exit
|
||||
*
|
||||
* called from prefetch exception handler with interrupts disabled
|
||||
*/
|
||||
ENTRY(crunch_task_enable)
|
||||
ldr r8, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr
|
||||
|
||||
ldr r1, [r8, #0x80]
|
||||
tst r1, #0x00800000 @ access to crunch enabled?
|
||||
movne pc, lr @ if so no business here
|
||||
mov r3, #0xaa @ unlock syscon swlock
|
||||
str r3, [r8, #0xc0]
|
||||
orr r1, r1, #0x00800000 @ enable access to crunch
|
||||
str r1, [r8, #0x80]
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r0, r10, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r2, [sp, #60] @ current task pc value
|
||||
ldr r1, [r3] @ get current crunch owner
|
||||
str r0, [r3] @ this task now owns crunch
|
||||
sub r2, r2, #4 @ adjust pc back
|
||||
str r2, [sp, #60]
|
||||
|
||||
ldr r2, [r8, #0x80]
|
||||
mov r2, r2 @ flush out enable (@@@)
|
||||
|
||||
teq r1, #0 @ test for last ownership
|
||||
mov lr, r9 @ normal exit from exception
|
||||
beq crunch_load @ no owner, skip save
|
||||
|
||||
crunch_save:
|
||||
cfstr64 mvdx0, [r1, #CRUNCH_MVDX0] @ save 64b registers
|
||||
cfstr64 mvdx1, [r1, #CRUNCH_MVDX1]
|
||||
cfstr64 mvdx2, [r1, #CRUNCH_MVDX2]
|
||||
cfstr64 mvdx3, [r1, #CRUNCH_MVDX3]
|
||||
cfstr64 mvdx4, [r1, #CRUNCH_MVDX4]
|
||||
cfstr64 mvdx5, [r1, #CRUNCH_MVDX5]
|
||||
cfstr64 mvdx6, [r1, #CRUNCH_MVDX6]
|
||||
cfstr64 mvdx7, [r1, #CRUNCH_MVDX7]
|
||||
cfstr64 mvdx8, [r1, #CRUNCH_MVDX8]
|
||||
cfstr64 mvdx9, [r1, #CRUNCH_MVDX9]
|
||||
cfstr64 mvdx10, [r1, #CRUNCH_MVDX10]
|
||||
cfstr64 mvdx11, [r1, #CRUNCH_MVDX11]
|
||||
cfstr64 mvdx12, [r1, #CRUNCH_MVDX12]
|
||||
cfstr64 mvdx13, [r1, #CRUNCH_MVDX13]
|
||||
cfstr64 mvdx14, [r1, #CRUNCH_MVDX14]
|
||||
cfstr64 mvdx15, [r1, #CRUNCH_MVDX15]
|
||||
|
||||
#ifdef __ARMEB__
|
||||
#error fix me for ARMEB
|
||||
#endif
|
||||
|
||||
cfmv32al mvfx0, mvax0 @ save 72b accumulators
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX0L]
|
||||
cfmv32am mvfx0, mvax0
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX0M]
|
||||
cfmv32ah mvfx0, mvax0
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX0H]
|
||||
cfmv32al mvfx0, mvax1
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX1L]
|
||||
cfmv32am mvfx0, mvax1
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX1M]
|
||||
cfmv32ah mvfx0, mvax1
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX1H]
|
||||
cfmv32al mvfx0, mvax2
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX2L]
|
||||
cfmv32am mvfx0, mvax2
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX2M]
|
||||
cfmv32ah mvfx0, mvax2
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX2H]
|
||||
cfmv32al mvfx0, mvax3
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX3L]
|
||||
cfmv32am mvfx0, mvax3
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX3M]
|
||||
cfmv32ah mvfx0, mvax3
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX3H]
|
||||
|
||||
cfmv32sc mvdx0, dspsc @ save status word
|
||||
cfstr64 mvdx0, [r1, #CRUNCH_DSPSC]
|
||||
|
||||
teq r0, #0 @ anything to load?
|
||||
cfldr64eq mvdx0, [r1, #CRUNCH_MVDX0] @ mvdx0 was clobbered
|
||||
moveq pc, lr
|
||||
|
||||
crunch_load:
|
||||
cfldr64 mvdx0, [r0, #CRUNCH_DSPSC] @ load status word
|
||||
cfmvsc32 dspsc, mvdx0
|
||||
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX0L] @ load 72b accumulators
|
||||
cfmval32 mvax0, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX0M]
|
||||
cfmvam32 mvax0, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX0H]
|
||||
cfmvah32 mvax0, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX1L]
|
||||
cfmval32 mvax1, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX1M]
|
||||
cfmvam32 mvax1, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX1H]
|
||||
cfmvah32 mvax1, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX2L]
|
||||
cfmval32 mvax2, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX2M]
|
||||
cfmvam32 mvax2, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX2H]
|
||||
cfmvah32 mvax2, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX3L]
|
||||
cfmval32 mvax3, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX3M]
|
||||
cfmvam32 mvax3, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX3H]
|
||||
cfmvah32 mvax3, mvfx0
|
||||
|
||||
cfldr64 mvdx0, [r0, #CRUNCH_MVDX0] @ load 64b registers
|
||||
cfldr64 mvdx1, [r0, #CRUNCH_MVDX1]
|
||||
cfldr64 mvdx2, [r0, #CRUNCH_MVDX2]
|
||||
cfldr64 mvdx3, [r0, #CRUNCH_MVDX3]
|
||||
cfldr64 mvdx4, [r0, #CRUNCH_MVDX4]
|
||||
cfldr64 mvdx5, [r0, #CRUNCH_MVDX5]
|
||||
cfldr64 mvdx6, [r0, #CRUNCH_MVDX6]
|
||||
cfldr64 mvdx7, [r0, #CRUNCH_MVDX7]
|
||||
cfldr64 mvdx8, [r0, #CRUNCH_MVDX8]
|
||||
cfldr64 mvdx9, [r0, #CRUNCH_MVDX9]
|
||||
cfldr64 mvdx10, [r0, #CRUNCH_MVDX10]
|
||||
cfldr64 mvdx11, [r0, #CRUNCH_MVDX11]
|
||||
cfldr64 mvdx12, [r0, #CRUNCH_MVDX12]
|
||||
cfldr64 mvdx13, [r0, #CRUNCH_MVDX13]
|
||||
cfldr64 mvdx14, [r0, #CRUNCH_MVDX14]
|
||||
cfldr64 mvdx15, [r0, #CRUNCH_MVDX15]
|
||||
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Back up crunch regs to save area and disable access to them
|
||||
* (mainly for gdb or sleep mode usage)
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task or NULL for any
|
||||
*/
|
||||
ENTRY(crunch_task_disable)
|
||||
stmfd sp!, {r4, r5, lr}
|
||||
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r4, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r1, [r3] @ get current crunch owner
|
||||
teq r1, #0 @ any current owner?
|
||||
beq 1f @ no: quit
|
||||
teq r0, #0 @ any owner?
|
||||
teqne r1, r2 @ or specified one?
|
||||
bne 1f @ no: quit
|
||||
|
||||
ldr r5, [r4, #0x80] @ enable access to crunch
|
||||
mov r2, #0xaa
|
||||
str r2, [r4, #0xc0]
|
||||
orr r5, r5, #0x00800000
|
||||
str r5, [r4, #0x80]
|
||||
|
||||
mov r0, #0 @ nothing to load
|
||||
str r0, [r3] @ no more current owner
|
||||
ldr r2, [r4, #0x80] @ flush out enable (@@@)
|
||||
mov r2, r2
|
||||
bl crunch_save
|
||||
|
||||
mov r2, #0xaa @ disable access to crunch
|
||||
str r2, [r4, #0xc0]
|
||||
bic r5, r5, #0x00800000
|
||||
str r5, [r4, #0x80]
|
||||
ldr r5, [r4, #0x80] @ flush out enable (@@@)
|
||||
mov r5, r5
|
||||
|
||||
1: msr cpsr_c, ip @ restore interrupt mode
|
||||
ldmfd sp!, {r4, r5, pc}
|
||||
|
||||
/*
|
||||
* Copy crunch state to given memory address
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task
|
||||
* r1 = memory address where to store crunch state
|
||||
*
|
||||
* this is called mainly in the creation of signal stack frames
|
||||
*/
|
||||
ENTRY(crunch_task_copy)
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r3, [r3] @ get current crunch owner
|
||||
teq r2, r3 @ does this task own it...
|
||||
beq 1f
|
||||
|
||||
@ current crunch values are in the task save area
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov r0, r1
|
||||
mov r1, r2
|
||||
mov r2, #CRUNCH_SIZE
|
||||
b memcpy
|
||||
|
||||
1: @ this task owns crunch regs -- grab a copy from there
|
||||
mov r0, #0 @ nothing to load
|
||||
mov r3, lr @ preserve return address
|
||||
bl crunch_save
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov pc, r3
|
||||
|
||||
/*
|
||||
* Restore crunch state from given memory address
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task
|
||||
* r1 = memory address where to get crunch state from
|
||||
*
|
||||
* this is used to restore crunch state when unwinding a signal stack frame
|
||||
*/
|
||||
ENTRY(crunch_task_restore)
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r3, [r3] @ get current crunch owner
|
||||
teq r2, r3 @ does this task own it...
|
||||
beq 1f
|
||||
|
||||
@ this task doesn't own crunch regs -- use its save area
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov r0, r2
|
||||
mov r2, #CRUNCH_SIZE
|
||||
b memcpy
|
||||
|
||||
1: @ this task owns crunch regs -- load them directly
|
||||
mov r0, r1
|
||||
mov r1, #0 @ nothing to save
|
||||
mov r3, lr @ preserve return address
|
||||
bl crunch_load
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov pc, r3
|
83
arch/arm/kernel/crunch.c
Normal file
83
arch/arm/kernel/crunch.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* arch/arm/kernel/crunch.c
|
||||
* Cirrus MaverickCrunch context switching and handling
|
||||
*
|
||||
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/arch/ep93xx-regs.h>
|
||||
#include <asm/thread_notify.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct crunch_state *crunch_owner;
|
||||
|
||||
void crunch_task_release(struct thread_info *thread)
|
||||
{
|
||||
local_irq_disable();
|
||||
if (crunch_owner == &thread->crunchstate)
|
||||
crunch_owner = NULL;
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static int crunch_enabled(u32 devcfg)
|
||||
{
|
||||
return !!(devcfg & EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE);
|
||||
}
|
||||
|
||||
static int crunch_do(struct notifier_block *self, unsigned long cmd, void *t)
|
||||
{
|
||||
struct thread_info *thread = (struct thread_info *)t;
|
||||
struct crunch_state *crunch_state;
|
||||
u32 devcfg;
|
||||
|
||||
crunch_state = &thread->crunchstate;
|
||||
|
||||
switch (cmd) {
|
||||
case THREAD_NOTIFY_FLUSH:
|
||||
memset(crunch_state, 0, sizeof(*crunch_state));
|
||||
|
||||
/*
|
||||
* FALLTHROUGH: Ensure we don't try to overwrite our newly
|
||||
* initialised state information on the first fault.
|
||||
*/
|
||||
|
||||
case THREAD_NOTIFY_RELEASE:
|
||||
crunch_task_release(thread);
|
||||
break;
|
||||
|
||||
case THREAD_NOTIFY_SWITCH:
|
||||
devcfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
|
||||
if (crunch_enabled(devcfg) || crunch_owner == crunch_state) {
|
||||
devcfg ^= EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE;
|
||||
__raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
|
||||
__raw_writel(devcfg, EP93XX_SYSCON_DEVICE_CONFIG);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block crunch_notifier_block = {
|
||||
.notifier_call = crunch_do,
|
||||
};
|
||||
|
||||
static int __init crunch_init(void)
|
||||
{
|
||||
thread_register_notifier(&crunch_notifier_block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(crunch_init);
|
@ -492,9 +492,15 @@ call_fpe:
|
||||
b do_fpe @ CP#1 (FPE)
|
||||
b do_fpe @ CP#2 (FPE)
|
||||
mov pc, lr @ CP#3
|
||||
#ifdef CONFIG_CRUNCH
|
||||
b crunch_task_enable @ CP#4 (MaverickCrunch)
|
||||
b crunch_task_enable @ CP#5 (MaverickCrunch)
|
||||
b crunch_task_enable @ CP#6 (MaverickCrunch)
|
||||
#else
|
||||
mov pc, lr @ CP#4
|
||||
mov pc, lr @ CP#5
|
||||
mov pc, lr @ CP#6
|
||||
#endif
|
||||
mov pc, lr @ CP#7
|
||||
mov pc, lr @ CP#8
|
||||
mov pc, lr @ CP#9
|
||||
|
@ -634,6 +634,32 @@ static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CRUNCH
|
||||
/*
|
||||
* Get the child Crunch state.
|
||||
*/
|
||||
static int ptrace_getcrunchregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
|
||||
crunch_task_disable(thread); /* force it to ram */
|
||||
return copy_to_user(ufp, &thread->crunchstate, CRUNCH_SIZE)
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the child Crunch state.
|
||||
*/
|
||||
static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
|
||||
crunch_task_release(thread); /* force a reload */
|
||||
return copy_from_user(&thread->crunchstate, ufp, CRUNCH_SIZE)
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||
{
|
||||
unsigned long tmp;
|
||||
@ -765,6 +791,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||
child->ptrace_message = data;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_CRUNCH
|
||||
case PTRACE_GETCRUNCHREGS:
|
||||
ret = ptrace_getcrunchregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETCRUNCHREGS:
|
||||
ret = ptrace_setcrunchregs(child, (void __user *)data);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
|
@ -119,9 +119,24 @@ DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
|
||||
* Standard memory resources
|
||||
*/
|
||||
static struct resource mem_res[] = {
|
||||
{ "Video RAM", 0, 0, IORESOURCE_MEM },
|
||||
{ "Kernel text", 0, 0, IORESOURCE_MEM },
|
||||
{ "Kernel data", 0, 0, IORESOURCE_MEM }
|
||||
{
|
||||
.name = "Video RAM",
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.flags = IORESOURCE_MEM
|
||||
},
|
||||
{
|
||||
.name = "Kernel text",
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.flags = IORESOURCE_MEM
|
||||
},
|
||||
{
|
||||
.name = "Kernel data",
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.flags = IORESOURCE_MEM
|
||||
}
|
||||
};
|
||||
|
||||
#define video_ram mem_res[0]
|
||||
@ -129,9 +144,24 @@ static struct resource mem_res[] = {
|
||||
#define kernel_data mem_res[2]
|
||||
|
||||
static struct resource io_res[] = {
|
||||
{ "reserved", 0x3bc, 0x3be, IORESOURCE_IO | IORESOURCE_BUSY },
|
||||
{ "reserved", 0x378, 0x37f, IORESOURCE_IO | IORESOURCE_BUSY },
|
||||
{ "reserved", 0x278, 0x27f, IORESOURCE_IO | IORESOURCE_BUSY }
|
||||
{
|
||||
.name = "reserved",
|
||||
.start = 0x3bc,
|
||||
.end = 0x3be,
|
||||
.flags = IORESOURCE_IO | IORESOURCE_BUSY
|
||||
},
|
||||
{
|
||||
.name = "reserved",
|
||||
.start = 0x378,
|
||||
.end = 0x37f,
|
||||
.flags = IORESOURCE_IO | IORESOURCE_BUSY
|
||||
},
|
||||
{
|
||||
.name = "reserved",
|
||||
.start = 0x278,
|
||||
.end = 0x27f,
|
||||
.flags = IORESOURCE_IO | IORESOURCE_BUSY
|
||||
}
|
||||
};
|
||||
|
||||
#define lp0 io_res[0]
|
||||
@ -808,7 +838,7 @@ static int __init topology_init(void)
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
register_cpu(&per_cpu(cpu_data, cpu).cpu, cpu, NULL);
|
||||
register_cpu(&per_cpu(cpu_data, cpu).cpu, cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -132,6 +132,37 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRUNCH
|
||||
static int preserve_crunch_context(struct crunch_sigframe *frame)
|
||||
{
|
||||
char kbuf[sizeof(*frame) + 8];
|
||||
struct crunch_sigframe *kframe;
|
||||
|
||||
/* the crunch context must be 64 bit aligned */
|
||||
kframe = (struct crunch_sigframe *)((unsigned long)(kbuf + 8) & ~7);
|
||||
kframe->magic = CRUNCH_MAGIC;
|
||||
kframe->size = CRUNCH_STORAGE_SIZE;
|
||||
crunch_task_copy(current_thread_info(), &kframe->storage);
|
||||
return __copy_to_user(frame, kframe, sizeof(*frame));
|
||||
}
|
||||
|
||||
static int restore_crunch_context(struct crunch_sigframe *frame)
|
||||
{
|
||||
char kbuf[sizeof(*frame) + 8];
|
||||
struct crunch_sigframe *kframe;
|
||||
|
||||
/* the crunch context must be 64 bit aligned */
|
||||
kframe = (struct crunch_sigframe *)((unsigned long)(kbuf + 8) & ~7);
|
||||
if (__copy_from_user(kframe, frame, sizeof(*frame)))
|
||||
return -1;
|
||||
if (kframe->magic != CRUNCH_MAGIC ||
|
||||
kframe->size != CRUNCH_STORAGE_SIZE)
|
||||
return -1;
|
||||
crunch_task_restore(current_thread_info(), &kframe->storage);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IWMMXT
|
||||
|
||||
static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame)
|
||||
@ -214,6 +245,10 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
|
||||
err |= !valid_user_regs(regs);
|
||||
|
||||
aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
|
||||
#ifdef CONFIG_CRUNCH
|
||||
if (err == 0)
|
||||
err |= restore_crunch_context(&aux->crunch);
|
||||
#endif
|
||||
#ifdef CONFIG_IWMMXT
|
||||
if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
|
||||
err |= restore_iwmmxt_context(&aux->iwmmxt);
|
||||
@ -333,6 +368,10 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set)
|
||||
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
|
||||
|
||||
aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
|
||||
#ifdef CONFIG_CRUNCH
|
||||
if (err == 0)
|
||||
err |= preserve_crunch_context(&aux->crunch);
|
||||
#endif
|
||||
#ifdef CONFIG_IWMMXT
|
||||
if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
|
||||
err |= preserve_iwmmxt_context(&aux->iwmmxt);
|
||||
|
@ -80,6 +80,10 @@ SECTIONS
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
#ifndef CONFIG_MMU
|
||||
*(.fixup)
|
||||
*(__ex_table)
|
||||
#endif
|
||||
}
|
||||
|
||||
.text : { /* Real text segment */
|
||||
@ -87,7 +91,9 @@ SECTIONS
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
#ifdef CONFIG_MMU
|
||||
*(.fixup)
|
||||
#endif
|
||||
*(.gnu.warning)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
@ -142,7 +148,9 @@ SECTIONS
|
||||
*/
|
||||
. = ALIGN(32);
|
||||
__start___ex_table = .;
|
||||
#ifdef CONFIG_MMU
|
||||
*(__ex_table)
|
||||
#endif
|
||||
__stop___ex_table = .;
|
||||
|
||||
/*
|
||||
|
@ -6,28 +6,31 @@
|
||||
|
||||
lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \
|
||||
csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
|
||||
copy_page.o delay.o findbit.o memchr.o memcpy.o \
|
||||
delay.o findbit.o memchr.o memcpy.o \
|
||||
memmove.o memset.o memzero.o setbit.o \
|
||||
strncpy_from_user.o strnlen_user.o \
|
||||
strchr.o strrchr.o \
|
||||
testchangebit.o testclearbit.o testsetbit.o \
|
||||
getuser.o putuser.o clear_user.o \
|
||||
ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \
|
||||
ucmpdi2.o lib1funcs.o div64.o sha1.o \
|
||||
io-readsb.o io-writesb.o io-readsl.o io-writesl.o
|
||||
|
||||
mmu-y := clear_user.o copy_page.o getuser.o putuser.o
|
||||
|
||||
# the code in uaccess.S is not preemption safe and
|
||||
# probably faster on ARMv3 only
|
||||
ifeq ($(CONFIG_PREEMPT),y)
|
||||
lib-y += copy_from_user.o copy_to_user.o
|
||||
mmu-y += copy_from_user.o copy_to_user.o
|
||||
else
|
||||
ifneq ($(CONFIG_CPU_32v3),y)
|
||||
lib-y += copy_from_user.o copy_to_user.o
|
||||
mmu-y += copy_from_user.o copy_to_user.o
|
||||
else
|
||||
lib-y += uaccess.o
|
||||
mmu-y += uaccess.o
|
||||
endif
|
||||
endif
|
||||
|
||||
lib-$(CONFIG_MMU) += $(mmu-y)
|
||||
|
||||
ifeq ($(CONFIG_CPU_32v3),y)
|
||||
lib-y += io-readsw-armv3.o io-writesw-armv3.o
|
||||
else
|
||||
|
@ -97,16 +97,13 @@ ENTRY(c_backtrace)
|
||||
b 1007f
|
||||
|
||||
/*
|
||||
* Fixup for LDMDB
|
||||
* Fixup for LDMDB. Note that this must not be in the fixup section.
|
||||
*/
|
||||
.section .fixup,"ax"
|
||||
.align 0
|
||||
1007: ldr r0, =.Lbad
|
||||
mov r1, frame
|
||||
bl printk
|
||||
ldmfd sp!, {r4 - r8, pc}
|
||||
.ltorg
|
||||
.previous
|
||||
|
||||
.section __ex_table,"a"
|
||||
.align 3
|
||||
|
@ -12,13 +12,13 @@
|
||||
|
||||
.text
|
||||
|
||||
/* Prototype: int __arch_clear_user(void *addr, size_t sz)
|
||||
/* Prototype: int __clear_user(void *addr, size_t sz)
|
||||
* Purpose : clear some user memory
|
||||
* Params : addr - user memory address to clear
|
||||
* : sz - number of bytes to clear
|
||||
* Returns : number of bytes NOT cleared
|
||||
*/
|
||||
ENTRY(__arch_clear_user)
|
||||
ENTRY(__clear_user)
|
||||
stmfd sp!, {r1, lr}
|
||||
mov r2, #0
|
||||
cmp r1, #4
|
||||
|
@ -16,7 +16,7 @@
|
||||
/*
|
||||
* Prototype:
|
||||
*
|
||||
* size_t __arch_copy_from_user(void *to, const void *from, size_t n)
|
||||
* size_t __copy_from_user(void *to, const void *from, size_t n)
|
||||
*
|
||||
* Purpose:
|
||||
*
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
.text
|
||||
|
||||
ENTRY(__arch_copy_from_user)
|
||||
ENTRY(__copy_from_user)
|
||||
|
||||
#include "copy_template.S"
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
/*
|
||||
* Prototype:
|
||||
*
|
||||
* size_t __arch_copy_to_user(void *to, const void *from, size_t n)
|
||||
* size_t __copy_to_user(void *to, const void *from, size_t n)
|
||||
*
|
||||
* Purpose:
|
||||
*
|
||||
@ -86,7 +86,7 @@
|
||||
|
||||
.text
|
||||
|
||||
ENTRY(__arch_copy_to_user)
|
||||
ENTRY(__copy_to_user)
|
||||
|
||||
#include "copy_template.S"
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
* returns the number of characters copied (strlen of copied string),
|
||||
* -EFAULT on exception, or "len" if we fill the whole buffer
|
||||
*/
|
||||
ENTRY(__arch_strncpy_from_user)
|
||||
ENTRY(__strncpy_from_user)
|
||||
mov ip, r1
|
||||
1: subs r2, r2, #1
|
||||
USER( ldrplbt r3, [r1], #1)
|
||||
|
@ -14,13 +14,13 @@
|
||||
.text
|
||||
.align 5
|
||||
|
||||
/* Prototype: unsigned long __arch_strnlen_user(const char *str, long n)
|
||||
/* Prototype: unsigned long __strnlen_user(const char *str, long n)
|
||||
* Purpose : get length of a string in user memory
|
||||
* Params : str - address of string in user memory
|
||||
* Returns : length of string *including terminator*
|
||||
* or zero on exception, or n + 1 if too long
|
||||
*/
|
||||
ENTRY(__arch_strnlen_user)
|
||||
ENTRY(__strnlen_user)
|
||||
mov r2, r0
|
||||
1:
|
||||
USER( ldrbt r3, [r0], #1)
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#define PAGE_SHIFT 12
|
||||
|
||||
/* Prototype: int __arch_copy_to_user(void *to, const char *from, size_t n)
|
||||
/* Prototype: int __copy_to_user(void *to, const char *from, size_t n)
|
||||
* Purpose : copy a block to user memory from kernel memory
|
||||
* Params : to - user memory
|
||||
* : from - kernel memory
|
||||
@ -39,7 +39,7 @@ USER( strgtbt r3, [r0], #1) @ May fault
|
||||
sub r2, r2, ip
|
||||
b .Lc2u_dest_aligned
|
||||
|
||||
ENTRY(__arch_copy_to_user)
|
||||
ENTRY(__copy_to_user)
|
||||
stmfd sp!, {r2, r4 - r7, lr}
|
||||
cmp r2, #4
|
||||
blt .Lc2u_not_enough
|
||||
@ -283,7 +283,7 @@ USER( strgtbt r3, [r0], #1) @ May fault
|
||||
9001: ldmfd sp!, {r0, r4 - r7, pc}
|
||||
.previous
|
||||
|
||||
/* Prototype: unsigned long __arch_copy_from_user(void *to,const void *from,unsigned long n);
|
||||
/* Prototype: unsigned long __copy_from_user(void *to,const void *from,unsigned long n);
|
||||
* Purpose : copy a block from user memory to kernel memory
|
||||
* Params : to - kernel memory
|
||||
* : from - user memory
|
||||
@ -302,7 +302,7 @@ USER( ldrgtbt r3, [r1], #1) @ May fault
|
||||
sub r2, r2, ip
|
||||
b .Lcfu_dest_aligned
|
||||
|
||||
ENTRY(__arch_copy_from_user)
|
||||
ENTRY(__copy_from_user)
|
||||
stmfd sp!, {r0, r2, r4 - r7, lr}
|
||||
cmp r2, #4
|
||||
blt .Lcfu_not_enough
|
||||
|
@ -2,8 +2,19 @@ if ARCH_EP93XX
|
||||
|
||||
menu "Cirrus EP93xx Implementation Options"
|
||||
|
||||
config CRUNCH
|
||||
bool "Support for MaverickCrunch"
|
||||
help
|
||||
Enable kernel support for MaverickCrunch.
|
||||
|
||||
comment "EP93xx Platforms"
|
||||
|
||||
config MACH_EDB9315
|
||||
bool "Support Cirrus Logic EDB9315"
|
||||
help
|
||||
Say 'Y' here if you want your kernel to support the Cirrus
|
||||
Logic EDB9315 Evaluation Board.
|
||||
|
||||
config MACH_GESBC9312
|
||||
bool "Support Glomation GESBC-9312-sx"
|
||||
help
|
||||
|
@ -6,5 +6,6 @@ obj-m :=
|
||||
obj-n :=
|
||||
obj- :=
|
||||
|
||||
obj-$(CONFIG_MACH_EDB9315) += edb9315.o
|
||||
obj-$(CONFIG_MACH_GESBC9312) += gesbc9312.o
|
||||
obj-$(CONFIG_MACH_TS72XX) += ts72xx.o
|
||||
|
62
arch/arm/mach-ep93xx/edb9315.c
Normal file
62
arch/arm/mach-ep93xx/edb9315.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* arch/arm/mach-ep93xx/edb9315.c
|
||||
* Cirrus Logic EDB9315 support.
|
||||
*
|
||||
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
*
|
||||
* This program 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.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
static struct physmap_flash_data edb9315_flash_data = {
|
||||
.width = 4,
|
||||
};
|
||||
|
||||
static struct resource edb9315_flash_resource = {
|
||||
.start = 0x60000000,
|
||||
.end = 0x61ffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_device edb9315_flash = {
|
||||
.name = "physmap-flash",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &edb9315_flash_data,
|
||||
},
|
||||
.num_resources = 1,
|
||||
.resource = &edb9315_flash_resource,
|
||||
};
|
||||
|
||||
static void __init edb9315_init_machine(void)
|
||||
{
|
||||
ep93xx_init_devices();
|
||||
platform_device_register(&edb9315_flash);
|
||||
}
|
||||
|
||||
MACHINE_START(EDB9315, "Cirrus Logic EDB9315 Evaluation Board")
|
||||
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
|
||||
.phys_io = EP93XX_APB_PHYS_BASE,
|
||||
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
|
||||
.boot_params = 0x00000100,
|
||||
.map_io = ep93xx_map_io,
|
||||
.init_irq = ep93xx_init_irq,
|
||||
.timer = &ep93xx_timer,
|
||||
.init_machine = edb9315_init_machine,
|
||||
MACHINE_END
|
@ -30,7 +30,7 @@ static struct physmap_flash_data gesbc9312_flash_data = {
|
||||
|
||||
static struct resource gesbc9312_flash_resource = {
|
||||
.start = 0x60000000,
|
||||
.end = 0x60800000,
|
||||
.end = 0x607fffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
|
@ -118,7 +118,7 @@ static struct physmap_flash_data ts72xx_flash_data = {
|
||||
|
||||
static struct resource ts72xx_flash_resource = {
|
||||
.start = TS72XX_NOR_PHYS_BASE,
|
||||
.end = TS72XX_NOR_PHYS_BASE + 0x01000000,
|
||||
.end = TS72XX_NOR_PHYS_BASE + 0x00ffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ static struct physmap_flash_data espresso_flash_data = {
|
||||
|
||||
static struct resource espresso_flash_resource = {
|
||||
.start = 0x90000000,
|
||||
.end = 0x92000000,
|
||||
.end = 0x91ffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
|
@ -304,7 +304,7 @@ static struct physmap_flash_data ixdp2351_flash_data = {
|
||||
|
||||
static struct resource ixdp2351_flash_resource = {
|
||||
.start = 0x90000000,
|
||||
.end = 0x94000000,
|
||||
.end = 0x93ffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
|
@ -143,7 +143,7 @@ static struct physmap_flash_data roadrunner_flash_data = {
|
||||
|
||||
static struct resource roadrunner_flash_resource = {
|
||||
.start = 0x90000000,
|
||||
.end = 0x94000000,
|
||||
.end = 0x93ffffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
|
@ -88,8 +88,8 @@ static int pxa_gpio_irq_type(unsigned int irq, unsigned int type)
|
||||
|
||||
if (type == IRQT_PROBE) {
|
||||
/* Don't mess with enabled GPIOs using preconfigured edges or
|
||||
GPIOs set to alternate function during probe */
|
||||
if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx]) &
|
||||
GPIOs set to alternate function or to output during probe */
|
||||
if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx] | GPDR(gpio)) &
|
||||
GPIO_bit(gpio))
|
||||
return 0;
|
||||
if (GAFR(gpio) & (0x3 << (((gpio) & 0xf)*2)))
|
||||
|
@ -69,6 +69,7 @@ void __init s3c244x_map_io(struct map_desc *mach_desc, int size)
|
||||
|
||||
s3c_device_i2c.name = "s3c2440-i2c";
|
||||
s3c_device_nand.name = "s3c2440-nand";
|
||||
s3c_device_usbgadget.name = "s3c2440-usbgadget";
|
||||
}
|
||||
|
||||
void __init s3c244x_init_clocks(int xtal)
|
||||
|
@ -15,8 +15,8 @@ config CPU_ARM610
|
||||
select CPU_32v3
|
||||
select CPU_CACHE_V3
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V3
|
||||
select CPU_TLB_V3
|
||||
select CPU_COPY_V3 if MMU
|
||||
select CPU_TLB_V3 if MMU
|
||||
help
|
||||
The ARM610 is the successor to the ARM3 processor
|
||||
and was produced by VLSI Technology Inc.
|
||||
@ -31,8 +31,8 @@ config CPU_ARM710
|
||||
select CPU_32v3
|
||||
select CPU_CACHE_V3
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V3
|
||||
select CPU_TLB_V3
|
||||
select CPU_COPY_V3 if MMU
|
||||
select CPU_TLB_V3 if MMU
|
||||
help
|
||||
A 32-bit RISC microprocessor based on the ARM7 processor core
|
||||
designed by Advanced RISC Machines Ltd. The ARM710 is the
|
||||
@ -50,8 +50,8 @@ config CPU_ARM720T
|
||||
select CPU_ABRT_LV4T
|
||||
select CPU_CACHE_V4
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WT
|
||||
select CPU_TLB_V4WT
|
||||
select CPU_COPY_V4WT if MMU
|
||||
select CPU_TLB_V4WT if MMU
|
||||
help
|
||||
A 32-bit RISC processor with 8kByte Cache, Write Buffer and
|
||||
MMU built around an ARM7TDMI core.
|
||||
@ -68,8 +68,8 @@ config CPU_ARM920T
|
||||
select CPU_ABRT_EV4T
|
||||
select CPU_CACHE_V4WT
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
The ARM920T is licensed to be produced by numerous vendors,
|
||||
and is used in the Maverick EP9312 and the Samsung S3C2410.
|
||||
@ -89,8 +89,8 @@ config CPU_ARM922T
|
||||
select CPU_ABRT_EV4T
|
||||
select CPU_CACHE_V4WT
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
The ARM922T is a version of the ARM920T, but with smaller
|
||||
instruction and data caches. It is used in Altera's
|
||||
@ -108,8 +108,8 @@ config CPU_ARM925T
|
||||
select CPU_ABRT_EV4T
|
||||
select CPU_CACHE_V4WT
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
The ARM925T is a mix between the ARM920T and ARM926T, but with
|
||||
different instruction and data caches. It is used in TI's OMAP
|
||||
@ -126,8 +126,8 @@ config CPU_ARM926T
|
||||
select CPU_32v5
|
||||
select CPU_ABRT_EV5TJ
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
This is a variant of the ARM920. It has slightly different
|
||||
instruction sequences for cache and TLB operations. Curiously,
|
||||
@ -144,8 +144,8 @@ config CPU_ARM1020
|
||||
select CPU_ABRT_EV4T
|
||||
select CPU_CACHE_V4WT
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
The ARM1020 is the 32K cached version of the ARM10 processor,
|
||||
with an addition of a floating-point unit.
|
||||
@ -161,8 +161,8 @@ config CPU_ARM1020E
|
||||
select CPU_ABRT_EV4T
|
||||
select CPU_CACHE_V4WT
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
depends on n
|
||||
|
||||
# ARM1022E
|
||||
@ -172,8 +172,8 @@ config CPU_ARM1022
|
||||
select CPU_32v5
|
||||
select CPU_ABRT_EV4T
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB # can probably do better
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU # can probably do better
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
The ARM1022E is an implementation of the ARMv5TE architecture
|
||||
based upon the ARM10 integer core with a 16KiB L1 Harvard cache,
|
||||
@ -189,8 +189,8 @@ config CPU_ARM1026
|
||||
select CPU_32v5
|
||||
select CPU_ABRT_EV5T # But need Jazelle, but EV5TJ ignores bit 10
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB # can probably do better
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_COPY_V4WB if MMU # can probably do better
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
help
|
||||
The ARM1026EJ-S is an implementation of the ARMv5TEJ architecture
|
||||
based upon the ARM10 integer core.
|
||||
@ -207,8 +207,8 @@ config CPU_SA110
|
||||
select CPU_ABRT_EV4
|
||||
select CPU_CACHE_V4WB
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_COPY_V4WB
|
||||
select CPU_TLB_V4WB
|
||||
select CPU_COPY_V4WB if MMU
|
||||
select CPU_TLB_V4WB if MMU
|
||||
help
|
||||
The Intel StrongARM(R) SA-110 is a 32-bit microprocessor and
|
||||
is available at five speeds ranging from 100 MHz to 233 MHz.
|
||||
@ -227,7 +227,7 @@ config CPU_SA1100
|
||||
select CPU_ABRT_EV4
|
||||
select CPU_CACHE_V4WB
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_TLB_V4WB
|
||||
select CPU_TLB_V4WB if MMU
|
||||
|
||||
# XScale
|
||||
config CPU_XSCALE
|
||||
@ -237,7 +237,7 @@ config CPU_XSCALE
|
||||
select CPU_32v5
|
||||
select CPU_ABRT_EV5T
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
|
||||
# XScale Core Version 3
|
||||
config CPU_XSC3
|
||||
@ -247,7 +247,7 @@ config CPU_XSC3
|
||||
select CPU_32v5
|
||||
select CPU_ABRT_EV5T
|
||||
select CPU_CACHE_VIVT
|
||||
select CPU_TLB_V4WBI
|
||||
select CPU_TLB_V4WBI if MMU
|
||||
select IO_36
|
||||
|
||||
# ARMv6
|
||||
@ -258,8 +258,8 @@ config CPU_V6
|
||||
select CPU_ABRT_EV6
|
||||
select CPU_CACHE_V6
|
||||
select CPU_CACHE_VIPT
|
||||
select CPU_COPY_V6
|
||||
select CPU_TLB_V6
|
||||
select CPU_COPY_V6 if MMU
|
||||
select CPU_TLB_V6 if MMU
|
||||
|
||||
# ARMv6k
|
||||
config CPU_32v6K
|
||||
@ -277,17 +277,17 @@ config CPU_32v6K
|
||||
# This defines the compiler instruction set which depends on the machine type.
|
||||
config CPU_32v3
|
||||
bool
|
||||
select TLS_REG_EMUL if SMP
|
||||
select TLS_REG_EMUL if SMP || !MMU
|
||||
select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
|
||||
|
||||
config CPU_32v4
|
||||
bool
|
||||
select TLS_REG_EMUL if SMP
|
||||
select TLS_REG_EMUL if SMP || !MMU
|
||||
select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
|
||||
|
||||
config CPU_32v5
|
||||
bool
|
||||
select TLS_REG_EMUL if SMP
|
||||
select TLS_REG_EMUL if SMP || !MMU
|
||||
select NEEDS_SYSCALL_FOR_CMPXCHG if SMP
|
||||
|
||||
config CPU_32v6
|
||||
@ -334,6 +334,7 @@ config CPU_CACHE_VIVT
|
||||
config CPU_CACHE_VIPT
|
||||
bool
|
||||
|
||||
if MMU
|
||||
# The copy-page model
|
||||
config CPU_COPY_V3
|
||||
bool
|
||||
@ -372,6 +373,8 @@ config CPU_TLB_V4WBI
|
||||
config CPU_TLB_V6
|
||||
bool
|
||||
|
||||
endif
|
||||
|
||||
#
|
||||
# CPU supports 36-bit I/O
|
||||
#
|
||||
|
@ -2,10 +2,16 @@
|
||||
# Makefile for the linux arm-specific parts of the memory manager.
|
||||
#
|
||||
|
||||
obj-y := consistent.o extable.o fault-armv.o \
|
||||
fault.o flush.o init.o ioremap.o mmap.o \
|
||||
obj-y := consistent.o extable.o fault.o init.o \
|
||||
iomap.o
|
||||
|
||||
obj-$(CONFIG_MMU) += fault-armv.o flush.o ioremap.o mmap.o \
|
||||
mm-armv.o
|
||||
|
||||
ifneq ($(CONFIG_MMU),y)
|
||||
obj-y += nommu.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MODULES) += proc-syms.o
|
||||
|
||||
obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#define TABLE_SIZE (2 * PTRS_PER_PTE * sizeof(pte_t))
|
||||
|
||||
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
|
||||
|
||||
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
|
||||
|
55
arch/arm/mm/iomap.c
Normal file
55
arch/arm/mm/iomap.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* linux/arch/arm/mm/iomap.c
|
||||
*
|
||||
* Map IO port and PCI memory spaces so that {read,write}[bwl] can
|
||||
* be used to access this memory.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef __io
|
||||
void __iomem *ioport_map(unsigned long port, unsigned int nr)
|
||||
{
|
||||
return __io(port);
|
||||
}
|
||||
EXPORT_SYMBOL(ioport_map);
|
||||
|
||||
void ioport_unmap(void __iomem *addr)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(ioport_unmap);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
|
||||
{
|
||||
unsigned long start = pci_resource_start(dev, bar);
|
||||
unsigned long len = pci_resource_len(dev, bar);
|
||||
unsigned long flags = pci_resource_flags(dev, bar);
|
||||
|
||||
if (!len || !start)
|
||||
return NULL;
|
||||
if (maxlen && len > maxlen)
|
||||
len = maxlen;
|
||||
if (flags & IORESOURCE_IO)
|
||||
return ioport_map(start, len);
|
||||
if (flags & IORESOURCE_MEM) {
|
||||
if (flags & IORESOURCE_CACHEABLE)
|
||||
return ioremap(start, len);
|
||||
return ioremap_nocache(start, len);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_iomap);
|
||||
|
||||
void pci_iounmap(struct pci_dev *dev, void __iomem *addr)
|
||||
{
|
||||
if ((unsigned long)addr >= VMALLOC_START &&
|
||||
(unsigned long)addr < VMALLOC_END)
|
||||
iounmap(addr);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_iounmap);
|
||||
#endif
|
@ -176,50 +176,3 @@ void __iounmap(void __iomem *addr)
|
||||
vunmap((void *)(PAGE_MASK & (unsigned long)addr));
|
||||
}
|
||||
EXPORT_SYMBOL(__iounmap);
|
||||
|
||||
#ifdef __io
|
||||
void __iomem *ioport_map(unsigned long port, unsigned int nr)
|
||||
{
|
||||
return __io(port);
|
||||
}
|
||||
EXPORT_SYMBOL(ioport_map);
|
||||
|
||||
void ioport_unmap(void __iomem *addr)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(ioport_unmap);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
|
||||
{
|
||||
unsigned long start = pci_resource_start(dev, bar);
|
||||
unsigned long len = pci_resource_len(dev, bar);
|
||||
unsigned long flags = pci_resource_flags(dev, bar);
|
||||
|
||||
if (!len || !start)
|
||||
return NULL;
|
||||
if (maxlen && len > maxlen)
|
||||
len = maxlen;
|
||||
if (flags & IORESOURCE_IO)
|
||||
return ioport_map(start, len);
|
||||
if (flags & IORESOURCE_MEM) {
|
||||
if (flags & IORESOURCE_CACHEABLE)
|
||||
return ioremap(start, len);
|
||||
return ioremap_nocache(start, len);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_iomap);
|
||||
|
||||
void pci_iounmap(struct pci_dev *dev, void __iomem *addr)
|
||||
{
|
||||
if ((unsigned long)addr >= VMALLOC_START &&
|
||||
(unsigned long)addr < VMALLOC_END)
|
||||
iounmap(addr);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_iounmap);
|
||||
#endif
|
||||
|
39
arch/arm/mm/nommu.c
Normal file
39
arch/arm/mm/nommu.c
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* linux/arch/arm/mm/nommu.c
|
||||
*
|
||||
* ARM uCLinux supporting functions.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
void flush_dcache_page(struct page *page)
|
||||
{
|
||||
__cpuc_flush_dcache_page(page_address(page));
|
||||
}
|
||||
EXPORT_SYMBOL(flush_dcache_page);
|
||||
|
||||
void __iomem *__ioremap_pfn(unsigned long pfn, unsigned long offset,
|
||||
size_t size, unsigned long flags)
|
||||
{
|
||||
if (pfn >= (0x100000000ULL >> PAGE_SHIFT))
|
||||
return NULL;
|
||||
return (void __iomem *) (offset + (pfn << PAGE_SHIFT));
|
||||
}
|
||||
EXPORT_SYMBOL(__ioremap_pfn);
|
||||
|
||||
void __iomem *__ioremap(unsigned long phys_addr, size_t size,
|
||||
unsigned long flags)
|
||||
{
|
||||
return (void __iomem *)phys_addr;
|
||||
}
|
||||
EXPORT_SYMBOL(__ioremap);
|
||||
|
||||
void __iounmap(void __iomem *addr)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(__iounmap);
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2000 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -101,7 +102,9 @@ ENTRY(cpu_arm1020_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -359,6 +362,7 @@ ENTRY(cpu_arm1020_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1020_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
mcr p15, 0, r3, c7, c10, 4
|
||||
mov r1, #0xF @ 16 segments
|
||||
@ -383,6 +387,7 @@ ENTRY(cpu_arm1020_switch_mm)
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -392,6 +397,7 @@ ENTRY(cpu_arm1020_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1020_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -421,6 +427,7 @@ ENTRY(cpu_arm1020_set_pte)
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
#endif
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -430,7 +437,9 @@ __arm1020_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, arm1020_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2000 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -101,7 +102,9 @@ ENTRY(cpu_arm1020e_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -344,6 +347,7 @@ ENTRY(cpu_arm1020e_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1020e_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
mcr p15, 0, r3, c7, c10, 4
|
||||
mov r1, #0xF @ 16 segments
|
||||
@ -367,6 +371,7 @@ ENTRY(cpu_arm1020e_switch_mm)
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -376,6 +381,7 @@ ENTRY(cpu_arm1020e_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1020e_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -403,6 +409,7 @@ ENTRY(cpu_arm1020e_set_pte)
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
#endif
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -412,7 +419,9 @@ __arm1020e_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, arm1020e_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2000 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -90,7 +91,9 @@ ENTRY(cpu_arm1022_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -333,6 +336,7 @@ ENTRY(cpu_arm1022_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1022_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 16 segments
|
||||
1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries
|
||||
@ -349,6 +353,7 @@ ENTRY(cpu_arm1022_switch_mm)
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -358,6 +363,7 @@ ENTRY(cpu_arm1022_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1022_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -385,6 +391,7 @@ ENTRY(cpu_arm1022_set_pte)
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
#endif
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -394,7 +401,9 @@ __arm1022_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, arm1022_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2000 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -90,7 +91,9 @@ ENTRY(cpu_arm1026_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -327,6 +330,7 @@ ENTRY(cpu_arm1026_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1026_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov r1, #0
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
1: mrc p15, 0, r15, c7, c14, 3 @ test, clean, invalidate
|
||||
@ -338,6 +342,7 @@ ENTRY(cpu_arm1026_switch_mm)
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -347,6 +352,7 @@ ENTRY(cpu_arm1026_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm1026_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -374,6 +380,7 @@ ENTRY(cpu_arm1026_set_pte)
|
||||
#ifndef CONFIG_CPU_DCACHE_DISABLE
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
#endif
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
|
||||
@ -384,8 +391,10 @@ __arm1026_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
mcr p15, 0, r4, c2, c0 @ load page table pointer
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
mov r0, #4 @ explicitly disable writeback
|
||||
mcr p15, 7, r0, c15, c0, 0
|
||||
|
@ -2,6 +2,7 @@
|
||||
* linux/arch/arm/mm/proc-arm6,7.S
|
||||
*
|
||||
* Copyright (C) 1997-2000 Russell King
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@ -199,10 +200,12 @@ ENTRY(cpu_arm7_do_idle)
|
||||
*/
|
||||
ENTRY(cpu_arm6_switch_mm)
|
||||
ENTRY(cpu_arm7_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov r1, #0
|
||||
mcr p15, 0, r1, c7, c0, 0 @ flush cache
|
||||
mcr p15, 0, r0, c2, c0, 0 @ update page table ptr
|
||||
mcr p15, 0, r1, c5, c0, 0 @ flush TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -214,6 +217,7 @@ ENTRY(cpu_arm7_switch_mm)
|
||||
.align 5
|
||||
ENTRY(cpu_arm6_set_pte)
|
||||
ENTRY(cpu_arm7_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -232,6 +236,7 @@ ENTRY(cpu_arm7_set_pte)
|
||||
movne r2, #0
|
||||
|
||||
str r2, [r0] @ hardware version
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -243,7 +248,9 @@ ENTRY(cpu_arm6_reset)
|
||||
ENTRY(cpu_arm7_reset)
|
||||
mov r1, #0
|
||||
mcr p15, 0, r1, c7, c0, 0 @ flush cache
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r1, c5, c0, 0 @ flush TLB
|
||||
#endif
|
||||
mov r1, #0x30
|
||||
mcr p15, 0, r1, c1, c0, 0 @ turn off MMU etc
|
||||
mov pc, r0
|
||||
@ -253,19 +260,27 @@ ENTRY(cpu_arm7_reset)
|
||||
.type __arm6_setup, #function
|
||||
__arm6_setup: mov r0, #0
|
||||
mcr p15, 0, r0, c7, c0 @ flush caches on v3
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c5, c0 @ flush TLBs on v3
|
||||
mov r0, #0x3d @ . ..RS BLDP WCAM
|
||||
orr r0, r0, #0x100 @ . ..01 0011 1101
|
||||
#else
|
||||
mov r0, #0x3c @ . ..RS BLDP WCA.
|
||||
#endif
|
||||
mov pc, lr
|
||||
.size __arm6_setup, . - __arm6_setup
|
||||
|
||||
.type __arm7_setup, #function
|
||||
__arm7_setup: mov r0, #0
|
||||
mcr p15, 0, r0, c7, c0 @ flush caches on v3
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c5, c0 @ flush TLBs on v3
|
||||
mcr p15, 0, r0, c3, c0 @ load domain access register
|
||||
mov r0, #0x7d @ . ..RS BLDP WCAM
|
||||
orr r0, r0, #0x100 @ . ..01 0111 1101
|
||||
#else
|
||||
mov r0, #0x7c @ . ..RS BLDP WCA.
|
||||
#endif
|
||||
mov pc, lr
|
||||
.size __arm7_setup, . - __arm7_setup
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2000 Steve Hill (sjhill@cotw.com)
|
||||
* Rob Scott (rscott@mtrob.fdns.net)
|
||||
* Copyright (C) 2000 ARM Limited, Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2004.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -29,6 +30,7 @@
|
||||
* out of 'proc-arm6,7.S' per RMK discussion
|
||||
* 07-25-2000 SJH Added idle function.
|
||||
* 08-25-2000 DBS Updated for integration of ARM Ltd version.
|
||||
* 04-20-2004 HSC modified for non-paged memory management mode.
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
@ -75,10 +77,12 @@ ENTRY(cpu_arm720_do_idle)
|
||||
* the new.
|
||||
*/
|
||||
ENTRY(cpu_arm720_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov r1, #0
|
||||
mcr p15, 0, r1, c7, c7, 0 @ invalidate cache
|
||||
mcr p15, 0, r0, c2, c0, 0 @ update page table ptr
|
||||
mcr p15, 0, r1, c8, c7, 0 @ flush TLB (v4)
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -89,6 +93,7 @@ ENTRY(cpu_arm720_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm720_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -107,6 +112,7 @@ ENTRY(cpu_arm720_set_pte)
|
||||
movne r2, #0
|
||||
|
||||
str r2, [r0] @ hardware version
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -117,7 +123,9 @@ ENTRY(cpu_arm720_set_pte)
|
||||
ENTRY(cpu_arm720_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate cache
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ flush TLB (v4)
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ get ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x2100 @ ..v....s........
|
||||
@ -130,7 +138,9 @@ ENTRY(cpu_arm720_reset)
|
||||
__arm710_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7, 0 @ invalidate caches
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7, 0 @ flush TLB (v4)
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register
|
||||
ldr r5, arm710_cr1_clear
|
||||
bic r0, r0, r5
|
||||
@ -156,7 +166,9 @@ arm710_cr1_set:
|
||||
__arm720_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7, 0 @ invalidate caches
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7, 0 @ flush TLB (v4)
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register
|
||||
ldr r5, arm720_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1999,2000 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -97,7 +98,9 @@ ENTRY(cpu_arm920_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -317,6 +320,7 @@ ENTRY(cpu_arm920_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm920_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov ip, #0
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
|
||||
@ -337,6 +341,7 @@ ENTRY(cpu_arm920_switch_mm)
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -346,6 +351,7 @@ ENTRY(cpu_arm920_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm920_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -372,6 +378,7 @@ ENTRY(cpu_arm920_set_pte)
|
||||
mov r0, r0
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -381,7 +388,9 @@ __arm920_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, arm920_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (C) 1999,2000 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* Copyright (C) 2001 Altera Corporation
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -99,7 +100,9 @@ ENTRY(cpu_arm922_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -321,6 +324,7 @@ ENTRY(cpu_arm922_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm922_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov ip, #0
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
|
||||
@ -341,6 +345,7 @@ ENTRY(cpu_arm922_switch_mm)
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -350,6 +355,7 @@ ENTRY(cpu_arm922_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm922_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -376,6 +382,7 @@ ENTRY(cpu_arm922_set_pte)
|
||||
mov r0, r0
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -385,7 +392,9 @@ __arm922_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, arm922_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -9,6 +9,8 @@
|
||||
* Update for Linux-2.6 and cache flush improvements
|
||||
* Copyright (C) 2004 Nokia Corporation by Tony Lindgren <tony@atomide.com>
|
||||
*
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2004.
|
||||
*
|
||||
* This program 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
|
||||
@ -122,7 +124,9 @@ ENTRY(cpu_arm925_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -369,6 +373,7 @@ ENTRY(cpu_arm925_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm925_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov ip, #0
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
|
||||
@ -383,6 +388,7 @@ ENTRY(cpu_arm925_switch_mm)
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -392,6 +398,7 @@ ENTRY(cpu_arm925_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm925_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -420,6 +427,7 @@ ENTRY(cpu_arm925_set_pte)
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
#endif
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif /* CONFIG_MMU */
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -438,7 +446,9 @@ __arm925_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
mov r0, #4 @ disable write-back on caches explicitly
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 1999-2001 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd.
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -85,7 +86,9 @@ ENTRY(cpu_arm926_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -329,6 +332,7 @@ ENTRY(cpu_arm926_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm926_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov ip, #0
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
|
||||
@ -341,6 +345,7 @@ ENTRY(cpu_arm926_switch_mm)
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -350,6 +355,7 @@ ENTRY(cpu_arm926_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_arm926_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -378,6 +384,7 @@ ENTRY(cpu_arm926_set_pte)
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
#endif
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -387,7 +394,9 @@ __arm926_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
|
||||
|
@ -2,6 +2,7 @@
|
||||
* linux/arch/arm/mm/proc-sa110.S
|
||||
*
|
||||
* Copyright (C) 1997-2002 Russell King
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@ -67,7 +68,9 @@ ENTRY(cpu_sa110_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -130,11 +133,15 @@ ENTRY(cpu_sa110_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_sa110_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
str lr, [sp, #-4]!
|
||||
bl v4wb_flush_kern_cache_all @ clears IP
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
ldr pc, [sp], #4
|
||||
#else
|
||||
mov pc, lr
|
||||
#endif
|
||||
|
||||
/*
|
||||
* cpu_sa110_set_pte(ptep, pte)
|
||||
@ -143,6 +150,7 @@ ENTRY(cpu_sa110_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_sa110_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -164,6 +172,7 @@ ENTRY(cpu_sa110_set_pte)
|
||||
mov r0, r0
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -173,7 +182,9 @@ __sa110_setup:
|
||||
mov r10, #0
|
||||
mcr p15, 0, r10, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r10, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r10, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, sa110_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -2,6 +2,7 @@
|
||||
* linux/arch/arm/mm/proc-sa1100.S
|
||||
*
|
||||
* Copyright (C) 1997-2002 Russell King
|
||||
* hacked for non-paged-MM by Hyok S. Choi, 2003.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@ -77,7 +78,9 @@ ENTRY(cpu_sa1100_reset)
|
||||
mov ip, #0
|
||||
mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches
|
||||
mcr p15, 0, ip, c7, c10, 4 @ drain WB
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
#endif
|
||||
mrc p15, 0, ip, c1, c0, 0 @ ctrl register
|
||||
bic ip, ip, #0x000f @ ............wcam
|
||||
bic ip, ip, #0x1100 @ ...i...s........
|
||||
@ -142,12 +145,16 @@ ENTRY(cpu_sa1100_dcache_clean_area)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_sa1100_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
str lr, [sp, #-4]!
|
||||
bl v4wb_flush_kern_cache_all @ clears IP
|
||||
mcr p15, 0, ip, c9, c0, 0 @ invalidate RB
|
||||
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer
|
||||
mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs
|
||||
ldr pc, [sp], #4
|
||||
#else
|
||||
mov pc, lr
|
||||
#endif
|
||||
|
||||
/*
|
||||
* cpu_sa1100_set_pte(ptep, pte)
|
||||
@ -156,6 +163,7 @@ ENTRY(cpu_sa1100_switch_mm)
|
||||
*/
|
||||
.align 5
|
||||
ENTRY(cpu_sa1100_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
|
||||
@ -177,6 +185,7 @@ ENTRY(cpu_sa1100_set_pte)
|
||||
mov r0, r0
|
||||
mcr p15, 0, r0, c7, c10, 1 @ clean D entry
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain WB
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
__INIT
|
||||
@ -186,7 +195,9 @@ __sa1100_setup:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
|
||||
#endif
|
||||
mrc p15, 0, r0, c1, c0 @ get control register v4
|
||||
ldr r5, sa1100_cr1_clear
|
||||
bic r0, r0, r5
|
||||
|
@ -2,6 +2,7 @@
|
||||
* linux/arch/arm/mm/proc-v6.S
|
||||
*
|
||||
* Copyright (C) 2001 Deep Blue Solutions Ltd.
|
||||
* Modified by Catalin Marinas for noMMU support
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@ -88,6 +89,7 @@ ENTRY(cpu_v6_dcache_clean_area)
|
||||
* - we are not using split page tables
|
||||
*/
|
||||
ENTRY(cpu_v6_switch_mm)
|
||||
#ifdef CONFIG_MMU
|
||||
mov r2, #0
|
||||
ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id
|
||||
#ifdef CONFIG_SMP
|
||||
@ -97,6 +99,7 @@ ENTRY(cpu_v6_switch_mm)
|
||||
mcr p15, 0, r2, c7, c10, 4 @ drain write buffer
|
||||
mcr p15, 0, r0, c2, c0, 0 @ set TTB 0
|
||||
mcr p15, 0, r1, c13, c0, 1 @ set context ID
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
@ -119,6 +122,7 @@ ENTRY(cpu_v6_switch_mm)
|
||||
* 1111 0 1 1 r/w r/w
|
||||
*/
|
||||
ENTRY(cpu_v6_set_pte)
|
||||
#ifdef CONFIG_MMU
|
||||
str r1, [r0], #-2048 @ linux version
|
||||
|
||||
bic r2, r1, #0x000003f0
|
||||
@ -145,6 +149,7 @@ ENTRY(cpu_v6_set_pte)
|
||||
|
||||
str r2, [r0]
|
||||
mcr p15, 0, r0, c7, c10, 1 @ flush_pte
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
|
||||
@ -194,12 +199,14 @@ __v6_setup:
|
||||
mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
|
||||
mcr p15, 0, r0, c7, c15, 0 @ clean+invalidate cache
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||
#ifdef CONFIG_MMU
|
||||
mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs
|
||||
mcr p15, 0, r0, c2, c0, 2 @ TTB control register
|
||||
#ifdef CONFIG_SMP
|
||||
orr r4, r4, #TTB_RGN_WBWA|TTB_S @ mark PTWs shared, outer cacheable
|
||||
#endif
|
||||
mcr p15, 0, r4, c2, c0, 1 @ load TTB1
|
||||
#endif /* CONFIG_MMU */
|
||||
#ifdef CONFIG_VFP
|
||||
mrc p15, 0, r0, c1, c0, 2
|
||||
orr r0, r0, #(0xf << 20)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user