Merge branch 'master' into gfs2
This commit is contained in:
Коммит
185a257f2f
|
@ -1,13 +1,12 @@
|
|||
What: devfs
|
||||
Date: July 2005
|
||||
Date: July 2005 (scheduled), finally removed in kernel v2.6.18
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Description:
|
||||
devfs 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.
|
||||
The files fs/devfs/*, include/linux/devfs_fs*.h will be removed,
|
||||
The files fs/devfs/*, include/linux/devfs_fs*.h were removed,
|
||||
along with the the assorted devfs function calls throughout the
|
||||
kernel tree.
|
||||
|
||||
Users:
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
What: /sys/power/
|
||||
Date: August 2006
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/power directory will contain files that will
|
||||
provide a unified interface to the power management
|
||||
subsystem.
|
||||
|
||||
What: /sys/power/state
|
||||
Date: August 2006
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/power/state file controls the system power state.
|
||||
Reading from this file returns what states are supported,
|
||||
which is hard-coded to 'standby' (Power-On Suspend), 'mem'
|
||||
(Suspend-to-RAM), and 'disk' (Suspend-to-Disk).
|
||||
|
||||
Writing to this file one of these strings causes the system to
|
||||
transition into that state. Please see the file
|
||||
Documentation/power/states.txt for a description of each of
|
||||
these states.
|
||||
|
||||
What: /sys/power/disk
|
||||
Date: August 2006
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/power/disk file controls the operating mode of the
|
||||
suspend-to-disk mechanism. Reading from this file returns
|
||||
the name of the method by which the system will be put to
|
||||
sleep on the next suspend. There are four methods supported:
|
||||
'firmware' - means that the memory image will be saved to disk
|
||||
by some firmware, in which case we also assume that the
|
||||
firmware will handle the system suspend.
|
||||
'platform' - the memory image will be saved by the kernel and
|
||||
the system will be put to sleep by the platform driver (e.g.
|
||||
ACPI or other PM registers).
|
||||
'shutdown' - the memory image will be saved by the kernel and
|
||||
the system will be powered off.
|
||||
'reboot' - the memory image will be saved by the kernel and
|
||||
the system will be rebooted.
|
||||
|
||||
The suspend-to-disk method may be chosen by writing to this
|
||||
file one of the accepted strings:
|
||||
|
||||
'firmware'
|
||||
'platform'
|
||||
'shutdown'
|
||||
'reboot'
|
||||
|
||||
It will only change to 'firmware' or 'platform' if the system
|
||||
supports that.
|
||||
|
||||
What: /sys/power/image_size
|
||||
Date: August 2006
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/power/image_size file controls the size of the image
|
||||
created by the suspend-to-disk mechanism. It can be written a
|
||||
string representing a non-negative integer that will be used
|
||||
as an upper limit of the image size, in bytes. The kernel's
|
||||
suspend-to-disk code will do its best to ensure the image size
|
||||
will not exceed this number. However, if it turns out to be
|
||||
impossible, the kernel will try to suspend anyway using the
|
||||
smallest image possible. In particular, if "0" is written to
|
||||
this file, the suspend image will be as small as possible.
|
||||
|
||||
Reading from this file will display the current image size
|
||||
limit, which is set to 500 MB by default.
|
||||
|
||||
What: /sys/power/pm_trace
|
||||
Date: August 2006
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/power/pm_trace file controls the code which saves the
|
||||
last PM event point in the RTC across reboots, so that you can
|
||||
debug a machine that just hangs during suspend (or more
|
||||
commonly, during resume). Namely, the RTC is only used to save
|
||||
the last PM event point if this file contains '1'. Initially
|
||||
it contains '0' which may be changed to '1' by writing a
|
||||
string representing a nonzero integer into it.
|
||||
|
||||
To use this debugging feature you should attempt to suspend
|
||||
the machine, then reboot it and run
|
||||
|
||||
dmesg -s 1000000 | grep 'hash matches'
|
||||
|
||||
CAUTION: Using it will cause your machine's real-time (CMOS)
|
||||
clock to be set to a random invalid time after a resume.
|
|
@ -43,59 +43,52 @@
|
|||
|
||||
<para>A Universal Serial Bus (USB) is used to connect a host,
|
||||
such as a PC or workstation, to a number of peripheral
|
||||
devices. USB uses a tree structure, with the host at the
|
||||
devices. USB uses a tree structure, with the host as the
|
||||
root (the system's master), hubs as interior nodes, and
|
||||
peripheral devices as leaves (and slaves).
|
||||
peripherals as leaves (and slaves).
|
||||
Modern PCs support several such trees of USB devices, usually
|
||||
one USB 2.0 tree (480 Mbit/sec each) with
|
||||
a few USB 1.1 trees (12 Mbit/sec each) that are used when you
|
||||
connect a USB 1.1 device directly to the machine's "root hub".
|
||||
</para>
|
||||
|
||||
<para>That master/slave asymmetry was designed in part for
|
||||
ease of use. It is not physically possible to assemble
|
||||
(legal) USB cables incorrectly: all upstream "to-the-host"
|
||||
connectors are the rectangular type, matching the sockets on
|
||||
root hubs, and the downstream type are the squarish type
|
||||
(or they are built in to the peripheral).
|
||||
Software doesn't need to deal with distributed autoconfiguration
|
||||
since the pre-designated master node manages all that.
|
||||
At the electrical level, bus protocol overhead is reduced by
|
||||
eliminating arbitration and moving scheduling into host software.
|
||||
<para>That master/slave asymmetry was designed-in for a number of
|
||||
reasons, one being ease of use. It is not physically possible to
|
||||
assemble (legal) USB cables incorrectly: all upstream "to the host"
|
||||
connectors are the rectangular type (matching the sockets on
|
||||
root hubs), and all downstream connectors are the squarish type
|
||||
(or they are built into the peripheral).
|
||||
Also, the host software doesn't need to deal with distributed
|
||||
auto-configuration since the pre-designated master node manages all that.
|
||||
And finally, at the electrical level, bus protocol overhead is reduced by
|
||||
eliminating arbitration and moving scheduling into the host software.
|
||||
</para>
|
||||
|
||||
<para>USB 1.0 was announced in January 1996, and was revised
|
||||
<para>USB 1.0 was announced in January 1996 and was revised
|
||||
as USB 1.1 (with improvements in hub specification and
|
||||
support for interrupt-out transfers) in September 1998.
|
||||
USB 2.0 was released in April 2000, including high speed
|
||||
transfers and transaction translating hubs (used for USB 1.1
|
||||
USB 2.0 was released in April 2000, adding high-speed
|
||||
transfers and transaction-translating hubs (used for USB 1.1
|
||||
and 1.0 backward compatibility).
|
||||
</para>
|
||||
|
||||
<para>USB support was added to Linux early in the 2.2 kernel series
|
||||
shortly before the 2.3 development forked off. Updates
|
||||
from 2.3 were regularly folded back into 2.2 releases, bringing
|
||||
new features such as <filename>/sbin/hotplug</filename> support,
|
||||
more drivers, and more robustness.
|
||||
The 2.5 kernel series continued such improvements, and also
|
||||
worked on USB 2.0 support,
|
||||
higher performance,
|
||||
better consistency between host controller drivers,
|
||||
API simplification (to make bugs less likely),
|
||||
and providing internal "kerneldoc" documentation.
|
||||
<para>Kernel developers added USB support to Linux early in the 2.2 kernel
|
||||
series, shortly before 2.3 development forked. Updates from 2.3 were
|
||||
regularly folded back into 2.2 releases, which improved reliability and
|
||||
brought <filename>/sbin/hotplug</filename> support as well more drivers.
|
||||
Such improvements were continued in the 2.5 kernel series, where they added
|
||||
USB 2.0 support, improved performance, and made the host controller drivers
|
||||
(HCDs) more consistent. They also simplified the API (to make bugs less
|
||||
likely) and added internal "kerneldoc" documentation.
|
||||
</para>
|
||||
|
||||
<para>Linux can run inside USB devices as well as on
|
||||
the hosts that control the devices.
|
||||
Because the Linux 2.x USB support evolved to support mass market
|
||||
platforms such as Apple Macintosh or PC-compatible systems,
|
||||
it didn't address design concerns for those types of USB systems.
|
||||
So it can't be used inside mass-market PDAs, or other peripherals.
|
||||
USB device drivers running inside those Linux peripherals
|
||||
But USB device drivers running inside those peripherals
|
||||
don't do the same things as the ones running inside hosts,
|
||||
and so they've been given a different name:
|
||||
they're called <emphasis>gadget drivers</emphasis>.
|
||||
This document does not present gadget drivers.
|
||||
so they've been given a different name:
|
||||
<emphasis>gadget drivers</emphasis>.
|
||||
This document does not cover gadget drivers.
|
||||
</para>
|
||||
|
||||
</chapter>
|
||||
|
@ -103,17 +96,14 @@
|
|||
<chapter id="host">
|
||||
<title>USB Host-Side API Model</title>
|
||||
|
||||
<para>Within the kernel,
|
||||
host-side drivers for USB devices talk to the "usbcore" APIs.
|
||||
There are two types of public "usbcore" APIs, targetted at two different
|
||||
layers of USB driver. Those are
|
||||
<emphasis>general purpose</emphasis> drivers, exposed through
|
||||
driver frameworks such as block, character, or network devices;
|
||||
and drivers that are <emphasis>part of the core</emphasis>,
|
||||
which are involved in managing a USB bus.
|
||||
Such core drivers include the <emphasis>hub</emphasis> driver,
|
||||
which manages trees of USB devices, and several different kinds
|
||||
of <emphasis>host controller driver (HCD)</emphasis>,
|
||||
<para>Host-side drivers for USB devices talk to the "usbcore" APIs.
|
||||
There are two. One is intended for
|
||||
<emphasis>general-purpose</emphasis> drivers (exposed through
|
||||
driver frameworks), and the other is for drivers that are
|
||||
<emphasis>part of the core</emphasis>.
|
||||
Such core drivers include the <emphasis>hub</emphasis> driver
|
||||
(which manages trees of USB devices) and several different kinds
|
||||
of <emphasis>host controller drivers</emphasis>,
|
||||
which control individual busses.
|
||||
</para>
|
||||
|
||||
|
@ -122,21 +112,21 @@
|
|||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>USB supports four kinds of data transfer
|
||||
(control, bulk, interrupt, and isochronous). Two transfer
|
||||
types use bandwidth as it's available (control and bulk),
|
||||
while the other two types of transfer (interrupt and isochronous)
|
||||
<listitem><para>USB supports four kinds of data transfers
|
||||
(control, bulk, interrupt, and isochronous). Two of them (control
|
||||
and bulk) use bandwidth as it's available,
|
||||
while the other two (interrupt and isochronous)
|
||||
are scheduled to provide guaranteed bandwidth.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>The device description model includes one or more
|
||||
"configurations" per device, only one of which is active at a time.
|
||||
Devices that are capable of high speed operation must also support
|
||||
full speed configurations, along with a way to ask about the
|
||||
"other speed" configurations that might be used.
|
||||
Devices that are capable of high-speed operation must also support
|
||||
full-speed configurations, along with a way to ask about the
|
||||
"other speed" configurations which might be used.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Configurations have one or more "interface", each
|
||||
<listitem><para>Configurations have one or more "interfaces", each
|
||||
of which may have "alternate settings". Interfaces may be
|
||||
standardized by USB "Class" specifications, or may be specific to
|
||||
a vendor or device.</para>
|
||||
|
@ -162,7 +152,7 @@
|
|||
</para></listitem>
|
||||
|
||||
<listitem><para>The Linux USB API supports synchronous calls for
|
||||
control and bulk messaging.
|
||||
control and bulk messages.
|
||||
It also supports asynchnous calls for all kinds of data transfer,
|
||||
using request structures called "URBs" (USB Request Blocks).
|
||||
</para></listitem>
|
||||
|
@ -463,14 +453,25 @@
|
|||
file in your Linux kernel sources.
|
||||
</para>
|
||||
|
||||
<para>Otherwise the main use for this file from programs
|
||||
is to poll() it to get notifications of usb devices
|
||||
as they're plugged or unplugged.
|
||||
To see what changed, you'd need to read the file and
|
||||
compare "before" and "after" contents, scan the filesystem,
|
||||
or see its hotplug event.
|
||||
</para>
|
||||
<para>This file, in combination with the poll() system call, can
|
||||
also be used to detect when devices are added or removed:
|
||||
<programlisting>int fd;
|
||||
struct pollfd pfd;
|
||||
|
||||
fd = open("/proc/bus/usb/devices", O_RDONLY);
|
||||
pfd = { fd, POLLIN, 0 };
|
||||
for (;;) {
|
||||
/* The first time through, this call will return immediately. */
|
||||
poll(&pfd, 1, -1);
|
||||
|
||||
/* To see what's changed, compare the file's previous and current
|
||||
contents or scan the filesystem. (Scanning is more precise.) */
|
||||
}</programlisting>
|
||||
Note that this behavior is intended to be used for informational
|
||||
and debug purposes. It would be more appropriate to use programs
|
||||
such as udev or HAL to initialize a device or start a user-mode
|
||||
helper program, for instance.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
|
|
|
@ -358,7 +358,8 @@ Here is a list of some of the different kernel trees available:
|
|||
quilt trees:
|
||||
- USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman <gregkh@suse.de>
|
||||
kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
|
||||
|
||||
- x86-64, partly i386, Andi Kleen <ak@suse.de>
|
||||
ftp.firstfloor.org:/pub/ak/x86_64/quilt/
|
||||
|
||||
Bug Reporting
|
||||
-------------
|
||||
|
|
|
@ -2543,6 +2543,9 @@ Your cooperation is appreciated.
|
|||
64 = /dev/usb/rio500 Diamond Rio 500
|
||||
65 = /dev/usb/usblcd USBLCD Interface (info@usblcd.de)
|
||||
66 = /dev/usb/cpad0 Synaptics cPad (mouse/LCD)
|
||||
67 = /dev/usb/adutux0 1st Ontrak ADU device
|
||||
...
|
||||
76 = /dev/usb/adutux10 10th Ontrak ADU device
|
||||
96 = /dev/usb/hiddev0 1st USB HID device
|
||||
...
|
||||
111 = /dev/usb/hiddev15 16th USB HID device
|
||||
|
|
|
@ -6,6 +6,21 @@ be removed from this file.
|
|||
|
||||
---------------------------
|
||||
|
||||
What: /sys/devices/.../power/state
|
||||
dev->power.power_state
|
||||
dpm_runtime_{suspend,resume)()
|
||||
When: July 2007
|
||||
Why: Broken design for runtime control over driver power states, confusing
|
||||
driver-internal runtime power management with: mechanisms to support
|
||||
system-wide sleep state transitions; event codes that distinguish
|
||||
different phases of swsusp "sleep" transitions; and userspace policy
|
||||
inputs. This framework was never widely used, and most attempts to
|
||||
use it were broken. Drivers should instead be exposing domain-specific
|
||||
interfaces either to kernel or to userspace.
|
||||
Who: Pavel Machek <pavel@suse.cz>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: RAW driver (CONFIG_RAW_DRIVER)
|
||||
When: December 2005
|
||||
Why: declared obsolete since kernel 2.6.3
|
||||
|
@ -55,6 +70,18 @@ Who: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
|
|||
|
||||
---------------------------
|
||||
|
||||
What: sys_sysctl
|
||||
When: January 2007
|
||||
Why: The same information is available through /proc/sys and that is the
|
||||
interface user space prefers to use. And there do not appear to be
|
||||
any existing user in user space of sys_sysctl. The additional
|
||||
maintenance overhead of keeping a set of binary names gets
|
||||
in the way of doing a good job of maintaining this interface.
|
||||
|
||||
Who: Eric Biederman <ebiederm@xmission.com>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: PCMCIA control ioctl (needed for pcmcia-cs [cardmgr, cardctl])
|
||||
When: November 2005
|
||||
Files: drivers/pcmcia/: pcmcia_ioctl.c
|
||||
|
@ -202,14 +229,6 @@ 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,
|
||||
|
@ -294,3 +313,24 @@ Why: The frame diverter is included in most distribution kernels, but is
|
|||
It is not clear if anyone is still using it.
|
||||
Who: Stephen Hemminger <shemminger@osdl.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
|
||||
What: PHYSDEVPATH, PHYSDEVBUS, PHYSDEVDRIVER in the uevent environment
|
||||
When: Oktober 2008
|
||||
Why: The stacking of class devices makes these values misleading and
|
||||
inconsistent.
|
||||
Class devices should not carry any of these properties, and bus
|
||||
devices have SUBSYTEM and DRIVER as a replacement.
|
||||
Who: Kay Sievers <kay.sievers@suse.de>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: i2c-isa
|
||||
When: December 2006
|
||||
Why: i2c-isa is a non-sense and doesn't fit in the device driver
|
||||
model. Drivers relying on it are better implemented as platform
|
||||
drivers.
|
||||
Who: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
---------------------------
|
||||
|
|
|
@ -1124,11 +1124,15 @@ debugging information is displayed on console.
|
|||
NMI switch that most IA32 servers have fires unknown NMI up, for example.
|
||||
If a system hangs up, try pressing the NMI switch.
|
||||
|
||||
[NOTE]
|
||||
This function and oprofile share a NMI callback. Therefore this function
|
||||
cannot be enabled when oprofile is activated.
|
||||
And NMI watchdog will be disabled when the value in this file is set to
|
||||
non-zero.
|
||||
nmi_watchdog
|
||||
------------
|
||||
|
||||
Enables/Disables the NMI watchdog on x86 systems. When the value is non-zero
|
||||
the NMI watchdog is enabled and will continuously test all online cpus to
|
||||
determine whether or not they are still functioning properly.
|
||||
|
||||
Because the NMI watchdog shares registers with oprofile, by disabling the NMI
|
||||
watchdog, oprofile may have more registers to utilize.
|
||||
|
||||
|
||||
2.4 /proc/sys/vm - The virtual memory subsystem
|
||||
|
|
|
@ -7,9 +7,12 @@ Supported adapters:
|
|||
* VIA Technologies, Inc. VT82C686A/B
|
||||
Datasheet: Sometimes available at the VIA website
|
||||
|
||||
* VIA Technologies, Inc. VT8231, VT8233, VT8233A, VT8235, VT8237R
|
||||
* VIA Technologies, Inc. VT8231, VT8233, VT8233A
|
||||
Datasheet: available on request from VIA
|
||||
|
||||
* VIA Technologies, Inc. VT8235, VT8237R, VT8237A, VT8251
|
||||
Datasheet: available on request and under NDA from VIA
|
||||
|
||||
Authors:
|
||||
Kyösti Mälkki <kmalkki@cc.hut.fi>,
|
||||
Mark D. Studebaker <mdsxyz123@yahoo.com>,
|
||||
|
@ -39,6 +42,8 @@ Your lspci -n listing must show one of these :
|
|||
device 1106:8235 (VT8231 function 4)
|
||||
device 1106:3177 (VT8235)
|
||||
device 1106:3227 (VT8237R)
|
||||
device 1106:3337 (VT8237A)
|
||||
device 1106:3287 (VT8251)
|
||||
|
||||
If none of these show up, you should look in the BIOS for settings like
|
||||
enable ACPI / SMBus or even USB.
|
||||
|
|
|
@ -6,9 +6,12 @@ This module is a very simple fake I2C/SMBus driver. It implements four
|
|||
types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, and
|
||||
(r/w) word data.
|
||||
|
||||
You need to provide a chip address as a module parameter when loading
|
||||
this driver, which will then only react to SMBus commands to this address.
|
||||
|
||||
No hardware is needed nor associated with this module. It will accept write
|
||||
quick commands to all addresses; it will respond to the other commands (also
|
||||
to all addresses) by reading from or writing to an array in memory. It will
|
||||
quick commands to one address; it will respond to the other commands (also
|
||||
to one address) by reading from or writing to an array in memory. It will
|
||||
also spam the kernel logs for every command it handles.
|
||||
|
||||
A pointer register with auto-increment is implemented for all byte
|
||||
|
@ -21,6 +24,11 @@ The typical use-case is like this:
|
|||
3. load the target sensors chip driver module
|
||||
4. observe its behavior in the kernel log
|
||||
|
||||
PARAMETERS:
|
||||
|
||||
int chip_addr:
|
||||
The SMBus address to emulate a chip at.
|
||||
|
||||
CAVEATS:
|
||||
|
||||
There are independent arrays for byte/data and word/data commands. Depending
|
||||
|
@ -33,6 +41,9 @@ If the hardware for your driver has banked registers (e.g. Winbond sensors
|
|||
chips) this module will not work well - although it could be extended to
|
||||
support that pretty easily.
|
||||
|
||||
Only one chip address is supported - although this module could be
|
||||
extended to support more.
|
||||
|
||||
If you spam it hard enough, printk can be lossy. This module really wants
|
||||
something like relayfs.
|
||||
|
||||
|
|
|
@ -421,6 +421,11 @@ more details, with real examples.
|
|||
The second argument is optional, and if supplied will be used
|
||||
if first argument is not supported.
|
||||
|
||||
as-instr
|
||||
as-instr checks if the assembler reports a specific instruction
|
||||
and then outputs either option1 or option2
|
||||
C escapes are supported in the test instruction
|
||||
|
||||
cc-option
|
||||
cc-option is used to check if $(CC) supports a given option, and not
|
||||
supported to use an optional second option.
|
||||
|
|
|
@ -573,8 +573,6 @@ running once the system is up.
|
|||
gscd= [HW,CD]
|
||||
Format: <io>
|
||||
|
||||
gt96100eth= [NET] MIPS GT96100 Advanced Communication Controller
|
||||
|
||||
gus= [HW,OSS]
|
||||
Format: <io>,<irq>,<dma>,<dma16>
|
||||
|
||||
|
@ -1240,7 +1238,11 @@ running once the system is up.
|
|||
bootloader. This is currently used on
|
||||
IXP2000 systems where the bus has to be
|
||||
configured a certain way for adjunct CPUs.
|
||||
|
||||
noearly [X86] Don't do any early type 1 scanning.
|
||||
This might help on some broken boards which
|
||||
machine check when some devices' config space
|
||||
is read. But various workarounds are disabled
|
||||
and some IOMMU drivers will not work.
|
||||
pcmv= [HW,PCMCIA] BadgePAD 4
|
||||
|
||||
pd. [PARIDE]
|
||||
|
@ -1363,6 +1365,14 @@ running once the system is up.
|
|||
|
||||
reserve= [KNL,BUGS] Force the kernel to ignore some iomem area
|
||||
|
||||
reservetop= [IA-32]
|
||||
Format: nn[KMG]
|
||||
Reserves a hole at the top of the kernel virtual
|
||||
address space.
|
||||
|
||||
reset_devices [KNL] Force drivers to reset the underlying device
|
||||
during initialization.
|
||||
|
||||
resume= [SWSUSP]
|
||||
Specify the partition device for software suspend
|
||||
|
||||
|
|
|
@ -192,6 +192,17 @@ or, for backwards compatibility, the option value. E.g.,
|
|||
arp_interval
|
||||
|
||||
Specifies the ARP link monitoring frequency in milliseconds.
|
||||
|
||||
The ARP monitor works by periodically checking the slave
|
||||
devices to determine whether they have sent or received
|
||||
traffic recently (the precise criteria depends upon the
|
||||
bonding mode, and the state of the slave). Regular traffic is
|
||||
generated via ARP probes issued for the addresses specified by
|
||||
the arp_ip_target option.
|
||||
|
||||
This behavior can be modified by the arp_validate option,
|
||||
below.
|
||||
|
||||
If ARP monitoring is used in an etherchannel compatible mode
|
||||
(modes 0 and 2), the switch should be configured in a mode
|
||||
that evenly distributes packets across all links. If the
|
||||
|
@ -213,6 +224,54 @@ arp_ip_target
|
|||
maximum number of targets that can be specified is 16. The
|
||||
default value is no IP addresses.
|
||||
|
||||
arp_validate
|
||||
|
||||
Specifies whether or not ARP probes and replies should be
|
||||
validated in the active-backup mode. This causes the ARP
|
||||
monitor to examine the incoming ARP requests and replies, and
|
||||
only consider a slave to be up if it is receiving the
|
||||
appropriate ARP traffic.
|
||||
|
||||
Possible values are:
|
||||
|
||||
none or 0
|
||||
|
||||
No validation is performed. This is the default.
|
||||
|
||||
active or 1
|
||||
|
||||
Validation is performed only for the active slave.
|
||||
|
||||
backup or 2
|
||||
|
||||
Validation is performed only for backup slaves.
|
||||
|
||||
all or 3
|
||||
|
||||
Validation is performed for all slaves.
|
||||
|
||||
For the active slave, the validation checks ARP replies to
|
||||
confirm that they were generated by an arp_ip_target. Since
|
||||
backup slaves do not typically receive these replies, the
|
||||
validation performed for backup slaves is on the ARP request
|
||||
sent out via the active slave. It is possible that some
|
||||
switch or network configurations may result in situations
|
||||
wherein the backup slaves do not receive the ARP requests; in
|
||||
such a situation, validation of backup slaves must be
|
||||
disabled.
|
||||
|
||||
This option is useful in network configurations in which
|
||||
multiple bonding hosts are concurrently issuing ARPs to one or
|
||||
more targets beyond a common switch. Should the link between
|
||||
the switch and target fail (but not the switch itself), the
|
||||
probe traffic generated by the multiple bonding instances will
|
||||
fool the standard ARP monitor into considering the links as
|
||||
still up. Use of the arp_validate option can resolve this, as
|
||||
the ARP monitor will only consider ARP requests and replies
|
||||
associated with its own instance of bonding.
|
||||
|
||||
This option was added in bonding version 3.1.0.
|
||||
|
||||
downdelay
|
||||
|
||||
Specifies the time, in milliseconds, to wait before disabling
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
DCCP protocol
|
||||
============
|
||||
|
||||
Last updated: 10 November 2005
|
||||
|
||||
Contents
|
||||
========
|
||||
|
@ -42,8 +41,11 @@ Socket options
|
|||
DCCP_SOCKOPT_PACKET_SIZE is used for CCID3 to set default packet size for
|
||||
calculations.
|
||||
|
||||
DCCP_SOCKOPT_SERVICE sets the service. This is compulsory as per the
|
||||
specification. If you don't set it you will get EPROTO.
|
||||
DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of
|
||||
service codes (RFC 4340, sec. 8.1.2); if this socket option is not set,
|
||||
the socket will fall back to 0 (which means that no meaningful service code
|
||||
is present). Connecting sockets set at most one service option; for
|
||||
listening sockets, multiple service codes can be specified.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
|
|
@ -116,6 +116,9 @@ FURTHER NOTES ON NO-MMU MMAP
|
|||
(*) A list of all the mappings on the system is visible through /proc/maps in
|
||||
no-MMU mode.
|
||||
|
||||
(*) A list of all the mappings in use by a process is visible through
|
||||
/proc/<pid>/maps in no-MMU mode.
|
||||
|
||||
(*) Supplying MAP_FIXED or a requesting a particular mapping address will
|
||||
result in an error.
|
||||
|
||||
|
@ -125,6 +128,49 @@ FURTHER NOTES ON NO-MMU MMAP
|
|||
error will result if they don't. This is most likely to be encountered
|
||||
with character device files, pipes, fifos and sockets.
|
||||
|
||||
|
||||
==========================
|
||||
INTERPROCESS SHARED MEMORY
|
||||
==========================
|
||||
|
||||
Both SYSV IPC SHM shared memory and POSIX shared memory is supported in NOMMU
|
||||
mode. The former through the usual mechanism, the latter through files created
|
||||
on ramfs or tmpfs mounts.
|
||||
|
||||
|
||||
=======
|
||||
FUTEXES
|
||||
=======
|
||||
|
||||
Futexes are supported in NOMMU mode if the arch supports them. An error will
|
||||
be given if an address passed to the futex system call lies outside the
|
||||
mappings made by a process or if the mapping in which the address lies does not
|
||||
support futexes (such as an I/O chardev mapping).
|
||||
|
||||
|
||||
=============
|
||||
NO-MMU MREMAP
|
||||
=============
|
||||
|
||||
The mremap() function is partially supported. It may change the size of a
|
||||
mapping, and may move it[*] if MREMAP_MAYMOVE is specified and if the new size
|
||||
of the mapping exceeds the size of the slab object currently occupied by the
|
||||
memory to which the mapping refers, or if a smaller slab object could be used.
|
||||
|
||||
MREMAP_FIXED is not supported, though it is ignored if there's no change of
|
||||
address and the object does not need to be moved.
|
||||
|
||||
Shared mappings may not be moved. Shareable mappings may not be moved either,
|
||||
even if they are not currently shared.
|
||||
|
||||
The mremap() function must be given an exact match for base address and size of
|
||||
a previously mapped object. It may not be used to create holes in existing
|
||||
mappings, move parts of existing mappings or resize parts of mappings. It must
|
||||
act on a complete mapping.
|
||||
|
||||
[*] Not currently supported.
|
||||
|
||||
|
||||
============================================
|
||||
PROVIDING SHAREABLE CHARACTER DEVICE SUPPORT
|
||||
============================================
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
The PCI Express Advanced Error Reporting Driver Guide HOWTO
|
||||
T. Long Nguyen <tom.l.nguyen@intel.com>
|
||||
Yanmin Zhang <yanmin.zhang@intel.com>
|
||||
07/29/2006
|
||||
|
||||
|
||||
1. Overview
|
||||
|
||||
1.1 About this guide
|
||||
|
||||
This guide describes the basics of the PCI Express Advanced Error
|
||||
Reporting (AER) driver and provides information on how to use it, as
|
||||
well as how to enable the drivers of endpoint devices to conform with
|
||||
PCI Express AER driver.
|
||||
|
||||
1.2 Copyright © Intel Corporation 2006.
|
||||
|
||||
1.3 What is the PCI Express AER Driver?
|
||||
|
||||
PCI Express error signaling can occur on the PCI Express link itself
|
||||
or on behalf of transactions initiated on the link. PCI Express
|
||||
defines two error reporting paradigms: the baseline capability and
|
||||
the Advanced Error Reporting capability. The baseline capability is
|
||||
required of all PCI Express components providing a minimum defined
|
||||
set of error reporting requirements. Advanced Error Reporting
|
||||
capability is implemented with a PCI Express advanced error reporting
|
||||
extended capability structure providing more robust error reporting.
|
||||
|
||||
The PCI Express AER driver provides the infrastructure to support PCI
|
||||
Express Advanced Error Reporting capability. The PCI Express AER
|
||||
driver provides three basic functions:
|
||||
|
||||
- Gathers the comprehensive error information if errors occurred.
|
||||
- Reports error to the users.
|
||||
- Performs error recovery actions.
|
||||
|
||||
AER driver only attaches root ports which support PCI-Express AER
|
||||
capability.
|
||||
|
||||
|
||||
2. User Guide
|
||||
|
||||
2.1 Include the PCI Express AER Root Driver into the Linux Kernel
|
||||
|
||||
The PCI Express AER Root driver is a Root Port service driver attached
|
||||
to the PCI Express Port Bus driver. If a user wants to use it, the driver
|
||||
has to be compiled. Option CONFIG_PCIEAER supports this capability. It
|
||||
depends on CONFIG_PCIEPORTBUS, so pls. set CONFIG_PCIEPORTBUS=y and
|
||||
CONFIG_PCIEAER = y.
|
||||
|
||||
2.2 Load PCI Express AER Root Driver
|
||||
There is a case where a system has AER support in BIOS. Enabling the AER
|
||||
Root driver and having AER support in BIOS may result unpredictable
|
||||
behavior. To avoid this conflict, a successful load of the AER Root driver
|
||||
requires ACPI _OSC support in the BIOS to allow the AER Root driver to
|
||||
request for native control of AER. See the PCI FW 3.0 Specification for
|
||||
details regarding OSC usage. Currently, lots of firmwares don't provide
|
||||
_OSC support while they use PCI Express. To support such firmwares,
|
||||
forceload, a parameter of type bool, could enable AER to continue to
|
||||
be initiated although firmwares have no _OSC support. To enable the
|
||||
walkaround, pls. add aerdriver.forceload=y to kernel boot parameter line
|
||||
when booting kernel. Note that forceload=n by default.
|
||||
|
||||
2.3 AER error output
|
||||
When a PCI-E AER error is captured, an error message will be outputed to
|
||||
console. If it's a correctable error, it is outputed as a warning.
|
||||
Otherwise, it is printed as an error. So users could choose different
|
||||
log level to filter out correctable error messages.
|
||||
|
||||
Below shows an example.
|
||||
+------ PCI-Express Device Error -----+
|
||||
Error Severity : Uncorrected (Fatal)
|
||||
PCIE Bus Error type : Transaction Layer
|
||||
Unsupported Request : First
|
||||
Requester ID : 0500
|
||||
VendorID=8086h, DeviceID=0329h, Bus=05h, Device=00h, Function=00h
|
||||
TLB Header:
|
||||
04000001 00200a03 05010000 00050100
|
||||
|
||||
In the example, 'Requester ID' means the ID of the device who sends
|
||||
the error message to root port. Pls. refer to pci express specs for
|
||||
other fields.
|
||||
|
||||
|
||||
3. Developer Guide
|
||||
|
||||
To enable AER aware support requires a software driver to configure
|
||||
the AER capability structure within its device and to provide callbacks.
|
||||
|
||||
To support AER better, developers need understand how AER does work
|
||||
firstly.
|
||||
|
||||
PCI Express errors are classified into two types: correctable errors
|
||||
and uncorrectable errors. This classification is based on the impacts
|
||||
of those errors, which may result in degraded performance or function
|
||||
failure.
|
||||
|
||||
Correctable errors pose no impacts on the functionality of the
|
||||
interface. The PCI Express protocol can recover without any software
|
||||
intervention or any loss of data. These errors are detected and
|
||||
corrected by hardware. Unlike correctable errors, uncorrectable
|
||||
errors impact functionality of the interface. Uncorrectable errors
|
||||
can cause a particular transaction or a particular PCI Express link
|
||||
to be unreliable. Depending on those error conditions, uncorrectable
|
||||
errors are further classified into non-fatal errors and fatal errors.
|
||||
Non-fatal errors cause the particular transaction to be unreliable,
|
||||
but the PCI Express link itself is fully functional. Fatal errors, on
|
||||
the other hand, cause the link to be unreliable.
|
||||
|
||||
When AER is enabled, a PCI Express device will automatically send an
|
||||
error message to the PCIE root port above it when the device captures
|
||||
an error. The Root Port, upon receiving an error reporting message,
|
||||
internally processes and logs the error message in its PCI Express
|
||||
capability structure. Error information being logged includes storing
|
||||
the error reporting agent's requestor ID into the Error Source
|
||||
Identification Registers and setting the error bits of the Root Error
|
||||
Status Register accordingly. If AER error reporting is enabled in Root
|
||||
Error Command Register, the Root Port generates an interrupt if an
|
||||
error is detected.
|
||||
|
||||
Note that the errors as described above are related to the PCI Express
|
||||
hierarchy and links. These errors do not include any device specific
|
||||
errors because device specific errors will still get sent directly to
|
||||
the device driver.
|
||||
|
||||
3.1 Configure the AER capability structure
|
||||
|
||||
AER aware drivers of PCI Express component need change the device
|
||||
control registers to enable AER. They also could change AER registers,
|
||||
including mask and severity registers. Helper function
|
||||
pci_enable_pcie_error_reporting could be used to enable AER. See
|
||||
section 3.3.
|
||||
|
||||
3.2. Provide callbacks
|
||||
|
||||
3.2.1 callback reset_link to reset pci express link
|
||||
|
||||
This callback is used to reset the pci express physical link when a
|
||||
fatal error happens. The root port aer service driver provides a
|
||||
default reset_link function, but different upstream ports might
|
||||
have different specifications to reset pci express link, so all
|
||||
upstream ports should provide their own reset_link functions.
|
||||
|
||||
In struct pcie_port_service_driver, a new pointer, reset_link, is
|
||||
added.
|
||||
|
||||
pci_ers_result_t (*reset_link) (struct pci_dev *dev);
|
||||
|
||||
Section 3.2.2.2 provides more detailed info on when to call
|
||||
reset_link.
|
||||
|
||||
3.2.2 PCI error-recovery callbacks
|
||||
|
||||
The PCI Express AER Root driver uses error callbacks to coordinate
|
||||
with downstream device drivers associated with a hierarchy in question
|
||||
when performing error recovery actions.
|
||||
|
||||
Data struct pci_driver has a pointer, err_handler, to point to
|
||||
pci_error_handlers who consists of a couple of callback function
|
||||
pointers. AER driver follows the rules defined in
|
||||
pci-error-recovery.txt except pci express specific parts (e.g.
|
||||
reset_link). Pls. refer to pci-error-recovery.txt for detailed
|
||||
definitions of the callbacks.
|
||||
|
||||
Below sections specify when to call the error callback functions.
|
||||
|
||||
3.2.2.1 Correctable errors
|
||||
|
||||
Correctable errors pose no impacts on the functionality of
|
||||
the interface. The PCI Express protocol can recover without any
|
||||
software intervention or any loss of data. These errors do not
|
||||
require any recovery actions. The AER driver clears the device's
|
||||
correctable error status register accordingly and logs these errors.
|
||||
|
||||
3.2.2.2 Non-correctable (non-fatal and fatal) errors
|
||||
|
||||
If an error message indicates a non-fatal error, performing link reset
|
||||
at upstream is not required. The AER driver calls error_detected(dev,
|
||||
pci_channel_io_normal) to all drivers associated within a hierarchy in
|
||||
question. for example,
|
||||
EndPoint<==>DownstreamPort B<==>UpstreamPort A<==>RootPort.
|
||||
If Upstream port A captures an AER error, the hierarchy consists of
|
||||
Downstream port B and EndPoint.
|
||||
|
||||
A driver may return PCI_ERS_RESULT_CAN_RECOVER,
|
||||
PCI_ERS_RESULT_DISCONNECT, or PCI_ERS_RESULT_NEED_RESET, depending on
|
||||
whether it can recover or the AER driver calls mmio_enabled as next.
|
||||
|
||||
If an error message indicates a fatal error, kernel will broadcast
|
||||
error_detected(dev, pci_channel_io_frozen) to all drivers within
|
||||
a hierarchy in question. Then, performing link reset at upstream is
|
||||
necessary. As different kinds of devices might use different approaches
|
||||
to reset link, AER port service driver is required to provide the
|
||||
function to reset link. Firstly, kernel looks for if the upstream
|
||||
component has an aer driver. If it has, kernel uses the reset_link
|
||||
callback of the aer driver. If the upstream component has no aer driver
|
||||
and the port is downstream port, we will use the aer driver of the
|
||||
root port who reports the AER error. As for upstream ports,
|
||||
they should provide their own aer service drivers with reset_link
|
||||
function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
|
||||
reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes
|
||||
to mmio_enabled.
|
||||
|
||||
3.3 helper functions
|
||||
|
||||
3.3.1 int pci_find_aer_capability(struct pci_dev *dev);
|
||||
pci_find_aer_capability locates the PCI Express AER capability
|
||||
in the device configuration space. If the device doesn't support
|
||||
PCI-Express AER, the function returns 0.
|
||||
|
||||
3.3.2 int pci_enable_pcie_error_reporting(struct pci_dev *dev);
|
||||
pci_enable_pcie_error_reporting enables the device to send error
|
||||
messages to root port when an error is detected. Note that devices
|
||||
don't enable the error reporting by default, so device drivers need
|
||||
call this function to enable it.
|
||||
|
||||
3.3.3 int pci_disable_pcie_error_reporting(struct pci_dev *dev);
|
||||
pci_disable_pcie_error_reporting disables the device to send error
|
||||
messages to root port when an error is detected.
|
||||
|
||||
3.3.4 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev);
|
||||
pci_cleanup_aer_uncorrect_error_status cleanups the uncorrectable
|
||||
error status register.
|
||||
|
||||
3.4 Frequent Asked Questions
|
||||
|
||||
Q: What happens if a PCI Express device driver does not provide an
|
||||
error recovery handler (pci_driver->err_handler is equal to NULL)?
|
||||
|
||||
A: The devices attached with the driver won't be recovered. If the
|
||||
error is fatal, kernel will print out warning messages. Please refer
|
||||
to section 3 for more information.
|
||||
|
||||
Q: What happens if an upstream port service driver does not provide
|
||||
callback reset_link?
|
||||
|
||||
A: Fatal error recovery will fail if the errors are reported by the
|
||||
upstream ports who are attached by the service driver.
|
||||
|
||||
Q: How does this infrastructure deal with driver that is not PCI
|
||||
Express aware?
|
||||
|
||||
A: This infrastructure calls the error callback functions of the
|
||||
driver when an error happens. But if the driver is not aware of
|
||||
PCI Express, the device might not report its own errors to root
|
||||
port.
|
||||
|
||||
Q: What modifications will that driver need to make it compatible
|
||||
with the PCI Express AER Root driver?
|
||||
|
||||
A: It could call the helper functions to enable AER in devices and
|
||||
cleanup uncorrectable status register. Pls. refer to section 3.3.
|
||||
|
|
@ -1,208 +1,553 @@
|
|||
Most of the code in Linux is device drivers, so most of the Linux power
|
||||
management code is also driver-specific. Most drivers will do very little;
|
||||
others, especially for platforms with small batteries (like cell phones),
|
||||
will do a lot.
|
||||
|
||||
Device Power Management
|
||||
This writeup gives an overview of how drivers interact with system-wide
|
||||
power management goals, emphasizing the models and interfaces that are
|
||||
shared by everything that hooks up to the driver model core. Read it as
|
||||
background for the domain-specific work you'd do with any specific driver.
|
||||
|
||||
|
||||
Device power management encompasses two areas - the ability to save
|
||||
state and transition a device to a low-power state when the system is
|
||||
entering a low-power state; and the ability to transition a device to
|
||||
a low-power state while the system is running (and independently of
|
||||
any other power management activity).
|
||||
Two Models for Device Power Management
|
||||
======================================
|
||||
Drivers will use one or both of these models to put devices into low-power
|
||||
states:
|
||||
|
||||
System Sleep model:
|
||||
Drivers can enter low power states as part of entering system-wide
|
||||
low-power states like "suspend-to-ram", or (mostly for systems with
|
||||
disks) "hibernate" (suspend-to-disk).
|
||||
|
||||
This is something that device, bus, and class drivers collaborate on
|
||||
by implementing various role-specific suspend and resume methods to
|
||||
cleanly power down hardware and software subsystems, then reactivate
|
||||
them without loss of data.
|
||||
|
||||
Some drivers can manage hardware wakeup events, which make the system
|
||||
leave that low-power state. This feature may be disabled using the
|
||||
relevant /sys/devices/.../power/wakeup file; enabling it may cost some
|
||||
power usage, but let the whole system enter low power states more often.
|
||||
|
||||
Runtime Power Management model:
|
||||
Drivers may also enter low power states while the system is running,
|
||||
independently of other power management activity. Upstream drivers
|
||||
will normally not know (or care) if the device is in some low power
|
||||
state when issuing requests; the driver will auto-resume anything
|
||||
that's needed when it gets a request.
|
||||
|
||||
This doesn't have, or need much infrastructure; it's just something you
|
||||
should do when writing your drivers. For example, clk_disable() unused
|
||||
clocks as part of minimizing power drain for currently-unused hardware.
|
||||
Of course, sometimes clusters of drivers will collaborate with each
|
||||
other, which could involve task-specific power management.
|
||||
|
||||
There's not a lot to be said about those low power states except that they
|
||||
are very system-specific, and often device-specific. Also, that if enough
|
||||
drivers put themselves into low power states (at "runtime"), the effect may be
|
||||
the same as entering some system-wide low-power state (system sleep) ... and
|
||||
that synergies exist, so that several drivers using runtime pm might put the
|
||||
system into a state where even deeper power saving options are available.
|
||||
|
||||
Most suspended devices will have quiesced all I/O: no more DMA or irqs, no
|
||||
more data read or written, and requests from upstream drivers are no longer
|
||||
accepted. A given bus or platform may have different requirements though.
|
||||
|
||||
Examples of hardware wakeup events include an alarm from a real time clock,
|
||||
network wake-on-LAN packets, keyboard or mouse activity, and media insertion
|
||||
or removal (for PCMCIA, MMC/SD, USB, and so on).
|
||||
|
||||
|
||||
Methods
|
||||
Interfaces for Entering System Sleep States
|
||||
===========================================
|
||||
Most of the programming interfaces a device driver needs to know about
|
||||
relate to that first model: entering a system-wide low power state,
|
||||
rather than just minimizing power consumption by one device.
|
||||
|
||||
The methods to suspend and resume devices reside in struct bus_type:
|
||||
|
||||
Bus Driver Methods
|
||||
------------------
|
||||
The core methods to suspend and resume devices reside in struct bus_type.
|
||||
These are mostly of interest to people writing infrastructure for busses
|
||||
like PCI or USB, or because they define the primitives that device drivers
|
||||
may need to apply in domain-specific ways to their devices:
|
||||
|
||||
struct bus_type {
|
||||
...
|
||||
int (*suspend)(struct device * dev, pm_message_t state);
|
||||
int (*resume)(struct device * dev);
|
||||
...
|
||||
int (*suspend)(struct device *dev, pm_message_t state);
|
||||
int (*suspend_late)(struct device *dev, pm_message_t state);
|
||||
|
||||
int (*resume_early)(struct device *dev);
|
||||
int (*resume)(struct device *dev);
|
||||
};
|
||||
|
||||
Each bus driver is responsible implementing these methods, translating
|
||||
the call into a bus-specific request and forwarding the call to the
|
||||
bus-specific drivers. For example, PCI drivers implement suspend() and
|
||||
resume() methods in struct pci_driver. The PCI core is simply
|
||||
responsible for translating the pointers to PCI-specific ones and
|
||||
calling the low-level driver.
|
||||
Bus drivers implement those methods as appropriate for the hardware and
|
||||
the drivers using it; PCI works differently from USB, and so on. Not many
|
||||
people write bus drivers; most driver code is a "device driver" that
|
||||
builds on top of bus-specific framework code.
|
||||
|
||||
This is done to a) ease transition to the new power management methods
|
||||
and leverage the existing PM code in various bus drivers; b) allow
|
||||
buses to implement generic and default PM routines for devices, and c)
|
||||
make the flow of execution obvious to the reader.
|
||||
For more information on these driver calls, see the description later;
|
||||
they are called in phases for every device, respecting the parent-child
|
||||
sequencing in the driver model tree. Note that as this is being written,
|
||||
only the suspend() and resume() are widely available; not many bus drivers
|
||||
leverage all of those phases, or pass them down to lower driver levels.
|
||||
|
||||
|
||||
System Power Management
|
||||
/sys/devices/.../power/wakeup files
|
||||
-----------------------------------
|
||||
All devices in the driver model have two flags to control handling of
|
||||
wakeup events, which are hardware signals that can force the device and/or
|
||||
system out of a low power state. These are initialized by bus or device
|
||||
driver code using device_init_wakeup(dev,can_wakeup).
|
||||
|
||||
When the system enters a low-power state, the device tree is walked in
|
||||
a depth-first fashion to transition each device into a low-power
|
||||
state. The ordering of the device tree is guaranteed by the order in
|
||||
which devices get registered - children are never registered before
|
||||
their ancestors, and devices are placed at the back of the list when
|
||||
registered. By walking the list in reverse order, we are guaranteed to
|
||||
suspend devices in the proper order.
|
||||
The "can_wakeup" flag just records whether the device (and its driver) can
|
||||
physically support wakeup events. When that flag is clear, the sysfs
|
||||
"wakeup" file is empty, and device_may_wakeup() returns false.
|
||||
|
||||
Devices are suspended once with interrupts enabled. Drivers are
|
||||
expected to stop I/O transactions, save device state, and place the
|
||||
device into a low-power state. Drivers may sleep, allocate memory,
|
||||
etc. at will.
|
||||
For devices that can issue wakeup events, a separate flag controls whether
|
||||
that device should try to use its wakeup mechanism. The initial value of
|
||||
device_may_wakeup() will be true, so that the device's "wakeup" file holds
|
||||
the value "enabled". Userspace can change that to "disabled" so that
|
||||
device_may_wakeup() returns false; or change it back to "enabled" (so that
|
||||
it returns true again).
|
||||
|
||||
Some devices are broken and will inevitably have problems powering
|
||||
down or disabling themselves with interrupts enabled. For these
|
||||
special cases, they may return -EAGAIN. This will put the device on a
|
||||
list to be taken care of later. When interrupts are disabled, before
|
||||
we enter the low-power state, their drivers are called again to put
|
||||
their device to sleep.
|
||||
|
||||
On resume, the devices that returned -EAGAIN will be called to power
|
||||
themselves back on with interrupts disabled. Once interrupts have been
|
||||
re-enabled, the rest of the drivers will be called to resume their
|
||||
devices. On resume, a driver is responsible for powering back on each
|
||||
device, restoring state, and re-enabling I/O transactions for that
|
||||
device.
|
||||
EXAMPLE: PCI Device Driver Methods
|
||||
-----------------------------------
|
||||
PCI framework software calls these methods when the PCI device driver bound
|
||||
to a device device has provided them:
|
||||
|
||||
struct pci_driver {
|
||||
...
|
||||
int (*suspend)(struct pci_device *pdev, pm_message_t state);
|
||||
int (*suspend_late)(struct pci_device *pdev, pm_message_t state);
|
||||
|
||||
int (*resume_early)(struct pci_device *pdev);
|
||||
int (*resume)(struct pci_device *pdev);
|
||||
};
|
||||
|
||||
Drivers will implement those methods, and call PCI-specific procedures
|
||||
like pci_set_power_state(), pci_enable_wake(), pci_save_state(), and
|
||||
pci_restore_state() to manage PCI-specific mechanisms. (PCI config space
|
||||
could be saved during driver probe, if it weren't for the fact that some
|
||||
systems rely on userspace tweaking using setpci.) Devices are suspended
|
||||
before their bridges enter low power states, and likewise bridges resume
|
||||
before their devices.
|
||||
|
||||
|
||||
Upper Layers of Driver Stacks
|
||||
-----------------------------
|
||||
Device drivers generally have at least two interfaces, and the methods
|
||||
sketched above are the ones which apply to the lower level (nearer PCI, USB,
|
||||
or other bus hardware). The network and block layers are examples of upper
|
||||
level interfaces, as is a character device talking to userspace.
|
||||
|
||||
Power management requests normally need to flow through those upper levels,
|
||||
which often use domain-oriented requests like "blank that screen". In
|
||||
some cases those upper levels will have power management intelligence that
|
||||
relates to end-user activity, or other devices that work in cooperation.
|
||||
|
||||
When those interfaces are structured using class interfaces, there is a
|
||||
standard way to have the upper layer stop issuing requests to a given
|
||||
class device (and restart later):
|
||||
|
||||
struct class {
|
||||
...
|
||||
int (*suspend)(struct device *dev, pm_message_t state);
|
||||
int (*resume)(struct device *dev);
|
||||
};
|
||||
|
||||
Those calls are issued in specific phases of the process by which the
|
||||
system enters a low power "suspend" state, or resumes from it.
|
||||
|
||||
|
||||
Calling Drivers to Enter System Sleep States
|
||||
============================================
|
||||
When the system enters a low power state, each device's driver is asked
|
||||
to suspend the device by putting it into state compatible with the target
|
||||
system state. That's usually some version of "off", but the details are
|
||||
system-specific. Also, wakeup-enabled devices will usually stay partly
|
||||
functional in order to wake the system.
|
||||
|
||||
When the system leaves that low power state, the device's driver is asked
|
||||
to resume it. The suspend and resume operations always go together, and
|
||||
both are multi-phase operations.
|
||||
|
||||
For simple drivers, suspend might quiesce the device using the class code
|
||||
and then turn its hardware as "off" as possible with late_suspend. The
|
||||
matching resume calls would then completely reinitialize the hardware
|
||||
before reactivating its class I/O queues.
|
||||
|
||||
More power-aware drivers drivers will use more than one device low power
|
||||
state, either at runtime or during system sleep states, and might trigger
|
||||
system wakeup events.
|
||||
|
||||
|
||||
Call Sequence Guarantees
|
||||
------------------------
|
||||
To ensure that bridges and similar links needed to talk to a device are
|
||||
available when the device is suspended or resumed, the device tree is
|
||||
walked in a bottom-up order to suspend devices. A top-down order is
|
||||
used to resume those devices.
|
||||
|
||||
The ordering of the device tree is defined by the order in which devices
|
||||
get registered: a child can never be registered, probed or resumed before
|
||||
its parent; and can't be removed or suspended after that parent.
|
||||
|
||||
The policy is that the device tree should match hardware bus topology.
|
||||
(Or at least the control bus, for devices which use multiple busses.)
|
||||
|
||||
|
||||
Suspending Devices
|
||||
------------------
|
||||
Suspending a given device is done in several phases. Suspending the
|
||||
system always includes every phase, executing calls for every device
|
||||
before the next phase begins. Not all busses or classes support all
|
||||
these callbacks; and not all drivers use all the callbacks.
|
||||
|
||||
The phases are seen by driver notifications issued in this order:
|
||||
|
||||
1 class.suspend(dev, message) is called after tasks are frozen, for
|
||||
devices associated with a class that has such a method. This
|
||||
method may sleep.
|
||||
|
||||
Since I/O activity usually comes from such higher layers, this is
|
||||
a good place to quiesce all drivers of a given type (and keep such
|
||||
code out of those drivers).
|
||||
|
||||
2 bus.suspend(dev, message) is called next. This method may sleep,
|
||||
and is often morphed into a device driver call with bus-specific
|
||||
parameters and/or rules.
|
||||
|
||||
This call should handle parts of device suspend logic that require
|
||||
sleeping. It probably does work to quiesce the device which hasn't
|
||||
been abstracted into class.suspend() or bus.suspend_late().
|
||||
|
||||
3 bus.suspend_late(dev, message) is called with IRQs disabled, and
|
||||
with only one CPU active. Until the bus.resume_early() phase
|
||||
completes (see later), IRQs are not enabled again. This method
|
||||
won't be exposed by all busses; for message based busses like USB,
|
||||
I2C, or SPI, device interactions normally require IRQs. This bus
|
||||
call may be morphed into a driver call with bus-specific parameters.
|
||||
|
||||
This call might save low level hardware state that might otherwise
|
||||
be lost in the upcoming low power state, and actually put the
|
||||
device into a low power state ... so that in some cases the device
|
||||
may stay partly usable until this late. This "late" call may also
|
||||
help when coping with hardware that behaves badly.
|
||||
|
||||
The pm_message_t parameter is currently used to refine those semantics
|
||||
(described later).
|
||||
|
||||
At the end of those phases, drivers should normally have stopped all I/O
|
||||
transactions (DMA, IRQs), saved enough state that they can re-initialize
|
||||
or restore previous state (as needed by the hardware), and placed the
|
||||
device into a low-power state. On many platforms they will also use
|
||||
clk_disable() to gate off one or more clock sources; sometimes they will
|
||||
also switch off power supplies, or reduce voltages. Drivers which have
|
||||
runtime PM support may already have performed some or all of the steps
|
||||
needed to prepare for the upcoming system sleep state.
|
||||
|
||||
When any driver sees that its device_can_wakeup(dev), it should make sure
|
||||
to use the relevant hardware signals to trigger a system wakeup event.
|
||||
For example, enable_irq_wake() might identify GPIO signals hooked up to
|
||||
a switch or other external hardware, and pci_enable_wake() does something
|
||||
similar for PCI's PME# signal.
|
||||
|
||||
If a driver (or bus, or class) fails it suspend method, the system won't
|
||||
enter the desired low power state; it will resume all the devices it's
|
||||
suspended so far.
|
||||
|
||||
Note that drivers may need to perform different actions based on the target
|
||||
system lowpower/sleep state. At this writing, there are only platform
|
||||
specific APIs through which drivers could determine those target states.
|
||||
|
||||
|
||||
Device Low Power (suspend) States
|
||||
---------------------------------
|
||||
Device low-power states aren't very standard. One device might only handle
|
||||
"on" and "off, while another might support a dozen different versions of
|
||||
"on" (how many engines are active?), plus a state that gets back to "on"
|
||||
faster than from a full "off".
|
||||
|
||||
Some busses define rules about what different suspend states mean. PCI
|
||||
gives one example: after the suspend sequence completes, a non-legacy
|
||||
PCI device may not perform DMA or issue IRQs, and any wakeup events it
|
||||
issues would be issued through the PME# bus signal. Plus, there are
|
||||
several PCI-standard device states, some of which are optional.
|
||||
|
||||
In contrast, integrated system-on-chip processors often use irqs as the
|
||||
wakeup event sources (so drivers would call enable_irq_wake) and might
|
||||
be able to treat DMA completion as a wakeup event (sometimes DMA can stay
|
||||
active too, it'd only be the CPU and some peripherals that sleep).
|
||||
|
||||
Some details here may be platform-specific. Systems may have devices that
|
||||
can be fully active in certain sleep states, such as an LCD display that's
|
||||
refreshed using DMA while most of the system is sleeping lightly ... and
|
||||
its frame buffer might even be updated by a DSP or other non-Linux CPU while
|
||||
the Linux control processor stays idle.
|
||||
|
||||
Moreover, the specific actions taken may depend on the target system state.
|
||||
One target system state might allow a given device to be very operational;
|
||||
another might require a hard shut down with re-initialization on resume.
|
||||
And two different target systems might use the same device in different
|
||||
ways; the aforementioned LCD might be active in one product's "standby",
|
||||
but a different product using the same SOC might work differently.
|
||||
|
||||
|
||||
Meaning of pm_message_t.event
|
||||
-----------------------------
|
||||
Parameters to suspend calls include the device affected and a message of
|
||||
type pm_message_t, which has one field: the event. If driver does not
|
||||
recognize the event code, suspend calls may abort the request and return
|
||||
a negative errno. However, most drivers will be fine if they implement
|
||||
PM_EVENT_SUSPEND semantics for all messages.
|
||||
|
||||
The event codes are used to refine the goal of suspending the device, and
|
||||
mostly matter when creating or resuming system memory image snapshots, as
|
||||
used with suspend-to-disk:
|
||||
|
||||
PM_EVENT_SUSPEND -- quiesce the driver and put hardware into a low-power
|
||||
state. When used with system sleep states like "suspend-to-RAM" or
|
||||
"standby", the upcoming resume() call will often be able to rely on
|
||||
state kept in hardware, or issue system wakeup events. When used
|
||||
instead with suspend-to-disk, few devices support this capability;
|
||||
most are completely powered off.
|
||||
|
||||
PM_EVENT_FREEZE -- quiesce the driver, but don't necessarily change into
|
||||
any low power mode. A system snapshot is about to be taken, often
|
||||
followed by a call to the driver's resume() method. Neither wakeup
|
||||
events nor DMA are allowed.
|
||||
|
||||
PM_EVENT_PRETHAW -- quiesce the driver, knowing that the upcoming resume()
|
||||
will restore a suspend-to-disk snapshot from a different kernel image.
|
||||
Drivers that are smart enough to look at their hardware state during
|
||||
resume() processing need that state to be correct ... a PRETHAW could
|
||||
be used to invalidate that state (by resetting the device), like a
|
||||
shutdown() invocation would before a kexec() or system halt. Other
|
||||
drivers might handle this the same way as PM_EVENT_FREEZE. Neither
|
||||
wakeup events nor DMA are allowed.
|
||||
|
||||
To enter "standby" (ACPI S1) or "Suspend to RAM" (STR, ACPI S3) states, or
|
||||
the similarly named APM states, only PM_EVENT_SUSPEND is used; for "Suspend
|
||||
to Disk" (STD, hibernate, ACPI S4), all of those event codes are used.
|
||||
|
||||
There's also PM_EVENT_ON, a value which never appears as a suspend event
|
||||
but is sometimes used to record the "not suspended" device state.
|
||||
|
||||
|
||||
Resuming Devices
|
||||
----------------
|
||||
Resuming is done in multiple phases, much like suspending, with all
|
||||
devices processing each phase's calls before the next phase begins.
|
||||
|
||||
The phases are seen by driver notifications issued in this order:
|
||||
|
||||
1 bus.resume_early(dev) is called with IRQs disabled, and with
|
||||
only one CPU active. As with bus.suspend_late(), this method
|
||||
won't be supported on busses that require IRQs in order to
|
||||
interact with devices.
|
||||
|
||||
This reverses the effects of bus.suspend_late().
|
||||
|
||||
2 bus.resume(dev) is called next. This may be morphed into a device
|
||||
driver call with bus-specific parameters; implementations may sleep.
|
||||
|
||||
This reverses the effects of bus.suspend().
|
||||
|
||||
3 class.resume(dev) is called for devices associated with a class
|
||||
that has such a method. Implementations may sleep.
|
||||
|
||||
This reverses the effects of class.suspend(), and would usually
|
||||
reactivate the device's I/O queue.
|
||||
|
||||
At the end of those phases, drivers should normally be as functional as
|
||||
they were before suspending: I/O can be performed using DMA and IRQs, and
|
||||
the relevant clocks are gated on. The device need not be "fully on"; it
|
||||
might be in a runtime lowpower/suspend state that acts as if it were.
|
||||
|
||||
However, the details here may again be platform-specific. For example,
|
||||
some systems support multiple "run" states, and the mode in effect at
|
||||
the end of resume() might not be the one which preceded suspension.
|
||||
That means availability of certain clocks or power supplies changed,
|
||||
which could easily affect how a driver works.
|
||||
|
||||
|
||||
Drivers need to be able to handle hardware which has been reset since the
|
||||
suspend methods were called, for example by complete reinitialization.
|
||||
This may be the hardest part, and the one most protected by NDA'd documents
|
||||
and chip errata. It's simplest if the hardware state hasn't changed since
|
||||
the suspend() was called, but that can't always be guaranteed.
|
||||
|
||||
Drivers must also be prepared to notice that the device has been removed
|
||||
while the system was powered off, whenever that's physically possible.
|
||||
PCMCIA, MMC, USB, Firewire, SCSI, and even IDE are common examples of busses
|
||||
where common Linux platforms will see such removal. Details of how drivers
|
||||
will notice and handle such removals are currently bus-specific, and often
|
||||
involve a separate thread.
|
||||
|
||||
|
||||
Note that the bus-specific runtime PM wakeup mechanism can exist, and might
|
||||
be defined to share some of the same driver code as for system wakeup. For
|
||||
example, a bus-specific device driver's resume() method might be used there,
|
||||
so it wouldn't only be called from bus.resume() during system-wide wakeup.
|
||||
See bus-specific information about how runtime wakeup events are handled.
|
||||
|
||||
|
||||
System Devices
|
||||
--------------
|
||||
System devices follow a slightly different API, which can be found in
|
||||
|
||||
include/linux/sysdev.h
|
||||
drivers/base/sys.c
|
||||
|
||||
System devices will only be suspended with interrupts disabled, and
|
||||
after all other devices have been suspended. On resume, they will be
|
||||
resumed before any other devices, and also with interrupts disabled.
|
||||
System devices will only be suspended with interrupts disabled, and after
|
||||
all other devices have been suspended. On resume, they will be resumed
|
||||
before any other devices, and also with interrupts disabled.
|
||||
|
||||
That is, IRQs are disabled, the suspend_late() phase begins, then the
|
||||
sysdev_driver.suspend() phase, and the system enters a sleep state. Then
|
||||
the sysdev_driver.resume() phase begins, followed by the resume_early()
|
||||
phase, after which IRQs are enabled.
|
||||
|
||||
Code to actually enter and exit the system-wide low power state sometimes
|
||||
involves hardware details that are only known to the boot firmware, and
|
||||
may leave a CPU running software (from SRAM or flash memory) that monitors
|
||||
the system and manages its wakeup sequence.
|
||||
|
||||
|
||||
Runtime Power Management
|
||||
========================
|
||||
Many devices are able to dynamically power down while the system is still
|
||||
running. This feature is useful for devices that are not being used, and
|
||||
can offer significant power savings on a running system. These devices
|
||||
often support a range of runtime power states, which might use names such
|
||||
as "off", "sleep", "idle", "active", and so on. Those states will in some
|
||||
cases (like PCI) be partially constrained by a bus the device uses, and will
|
||||
usually include hardware states that are also used in system sleep states.
|
||||
|
||||
Many devices are able to dynamically power down while the system is
|
||||
still running. This feature is useful for devices that are not being
|
||||
used, and can offer significant power savings on a running system.
|
||||
However, note that if a driver puts a device into a runtime low power state
|
||||
and the system then goes into a system-wide sleep state, it normally ought
|
||||
to resume into that runtime low power state rather than "full on". Such
|
||||
distinctions would be part of the driver-internal state machine for that
|
||||
hardware; the whole point of runtime power management is to be sure that
|
||||
drivers are decoupled in that way from the state machine governing phases
|
||||
of the system-wide power/sleep state transitions.
|
||||
|
||||
In each device's directory, there is a 'power' directory, which
|
||||
contains at least a 'state' file. Reading from this file displays what
|
||||
power state the device is currently in. Writing to this file initiates
|
||||
a transition to the specified power state, which must be a decimal in
|
||||
the range 1-3, inclusive; or 0 for 'On'.
|
||||
|
||||
The PM core will call the ->suspend() method in the bus_type object
|
||||
that the device belongs to if the specified state is not 0, or
|
||||
->resume() if it is.
|
||||
Power Saving Techniques
|
||||
-----------------------
|
||||
Normally runtime power management is handled by the drivers without specific
|
||||
userspace or kernel intervention, by device-aware use of techniques like:
|
||||
|
||||
Nothing will happen if the specified state is the same state the
|
||||
device is currently in.
|
||||
Using information provided by other system layers
|
||||
- stay deeply "off" except between open() and close()
|
||||
- if transceiver/PHY indicates "nobody connected", stay "off"
|
||||
- application protocols may include power commands or hints
|
||||
|
||||
If the device is already in a low-power state, and the specified state
|
||||
is another, but different, low-power state, the ->resume() method will
|
||||
first be called to power the device back on, then ->suspend() will be
|
||||
called again with the new state.
|
||||
Using fewer CPU cycles
|
||||
- using DMA instead of PIO
|
||||
- removing timers, or making them lower frequency
|
||||
- shortening "hot" code paths
|
||||
- eliminating cache misses
|
||||
- (sometimes) offloading work to device firmware
|
||||
|
||||
The driver is responsible for saving the working state of the device
|
||||
and putting it into the low-power state specified. If this was
|
||||
successful, it returns 0, and the device's power_state field is
|
||||
updated.
|
||||
Reducing other resource costs
|
||||
- gating off unused clocks in software (or hardware)
|
||||
- switching off unused power supplies
|
||||
- eliminating (or delaying/merging) IRQs
|
||||
- tuning DMA to use word and/or burst modes
|
||||
|
||||
The driver must take care to know whether or not it is able to
|
||||
properly resume the device, including all step of reinitialization
|
||||
necessary. (This is the hardest part, and the one most protected by
|
||||
NDA'd documents).
|
||||
Using device-specific low power states
|
||||
- using lower voltages
|
||||
- avoiding needless DMA transfers
|
||||
|
||||
The driver must also take care not to suspend a device that is
|
||||
currently in use. It is their responsibility to provide their own
|
||||
exclusion mechanisms.
|
||||
Read your hardware documentation carefully to see the opportunities that
|
||||
may be available. If you can, measure the actual power usage and check
|
||||
it against the budget established for your project.
|
||||
|
||||
The runtime power transition happens with interrupts enabled. If a
|
||||
device cannot support being powered down with interrupts, it may
|
||||
return -EAGAIN (as it would during a system power management
|
||||
transition), but it will _not_ be called again, and the transaction
|
||||
will fail.
|
||||
|
||||
There is currently no way to know what states a device or driver
|
||||
supports a priori. This will change in the future.
|
||||
Examples: USB hosts, system timer, system CPU
|
||||
----------------------------------------------
|
||||
USB host controllers make interesting, if complex, examples. In many cases
|
||||
these have no work to do: no USB devices are connected, or all of them are
|
||||
in the USB "suspend" state. Linux host controller drivers can then disable
|
||||
periodic DMA transfers that would otherwise be a constant power drain on the
|
||||
memory subsystem, and enter a suspend state. In power-aware controllers,
|
||||
entering that suspend state may disable the clock used with USB signaling,
|
||||
saving a certain amount of power.
|
||||
|
||||
pm_message_t meaning
|
||||
The controller will be woken from that state (with an IRQ) by changes to the
|
||||
signal state on the data lines of a given port, for example by an existing
|
||||
peripheral requesting "remote wakeup" or by plugging a new peripheral. The
|
||||
same wakeup mechanism usually works from "standby" sleep states, and on some
|
||||
systems also from "suspend to RAM" (or even "suspend to disk") states.
|
||||
(Except that ACPI may be involved instead of normal IRQs, on some hardware.)
|
||||
|
||||
pm_message_t has two fields. event ("major"), and flags. If driver
|
||||
does not know event code, it aborts the request, returning error. Some
|
||||
drivers may need to deal with special cases based on the actual type
|
||||
of suspend operation being done at the system level. This is why
|
||||
there are flags.
|
||||
System devices like timers and CPUs may have special roles in the platform
|
||||
power management scheme. For example, system timers using a "dynamic tick"
|
||||
approach don't just save CPU cycles (by eliminating needless timer IRQs),
|
||||
but they may also open the door to using lower power CPU "idle" states that
|
||||
cost more than a jiffie to enter and exit. On x86 systems these are states
|
||||
like "C3"; note that periodic DMA transfers from a USB host controller will
|
||||
also prevent entry to a C3 state, much like a periodic timer IRQ.
|
||||
|
||||
Event codes are:
|
||||
That kind of runtime mechanism interaction is common. "System On Chip" (SOC)
|
||||
processors often have low power idle modes that can't be entered unless
|
||||
certain medium-speed clocks (often 12 or 48 MHz) are gated off. When the
|
||||
drivers gate those clocks effectively, then the system idle task may be able
|
||||
to use the lower power idle modes and thereby increase battery life.
|
||||
|
||||
ON -- no need to do anything except special cases like broken
|
||||
HW.
|
||||
If the CPU can have a "cpufreq" driver, there also may be opportunities
|
||||
to shift to lower voltage settings and reduce the power cost of executing
|
||||
a given number of instructions. (Without voltage adjustment, it's rare
|
||||
for cpufreq to save much power; the cost-per-instruction must go down.)
|
||||
|
||||
# NOTIFICATION -- pretty much same as ON?
|
||||
|
||||
FREEZE -- stop DMA and interrupts, and be prepared to reinit HW from
|
||||
scratch. That probably means stop accepting upstream requests, the
|
||||
actual policy of what to do with them being specific to a given
|
||||
driver. It's acceptable for a network driver to just drop packets
|
||||
while a block driver is expected to block the queue so no request is
|
||||
lost. (Use IDE as an example on how to do that). FREEZE requires no
|
||||
power state change, and it's expected for drivers to be able to
|
||||
quickly transition back to operating state.
|
||||
/sys/devices/.../power/state files
|
||||
==================================
|
||||
For now you can also test some of this functionality using sysfs.
|
||||
|
||||
SUSPEND -- like FREEZE, but also put hardware into low-power state. If
|
||||
there's need to distinguish several levels of sleep, additional flag
|
||||
is probably best way to do that.
|
||||
DEPRECATED: USE "power/state" ONLY FOR DRIVER TESTING, AND
|
||||
AVOID USING dev->power.power_state IN DRIVERS.
|
||||
|
||||
Transitions are only from a resumed state to a suspended state, never
|
||||
between 2 suspended states. (ON -> FREEZE or ON -> SUSPEND can happen,
|
||||
FREEZE -> SUSPEND or SUSPEND -> FREEZE can not).
|
||||
THESE WILL BE REMOVED. IF THE "power/state" FILE GETS REPLACED,
|
||||
IT WILL BECOME SOMETHING COUPLED TO THE BUS OR DRIVER.
|
||||
|
||||
All events are:
|
||||
In each device's directory, there is a 'power' directory, which contains
|
||||
at least a 'state' file. The value of this field is effectively boolean,
|
||||
PM_EVENT_ON or PM_EVENT_SUSPEND.
|
||||
|
||||
[NOTE NOTE NOTE: If you are driver author, you should not care; you
|
||||
should only look at event, and ignore flags.]
|
||||
* Reading from this file displays a value corresponding to
|
||||
the power.power_state.event field. All nonzero values are
|
||||
displayed as "2", corresponding to a low power state; zero
|
||||
is displayed as "0", corresponding to normal operation.
|
||||
|
||||
#Prepare for suspend -- userland is still running but we are going to
|
||||
#enter suspend state. This gives drivers chance to load firmware from
|
||||
#disk and store it in memory, or do other activities taht require
|
||||
#operating userland, ability to kmalloc GFP_KERNEL, etc... All of these
|
||||
#are forbiden once the suspend dance is started.. event = ON, flags =
|
||||
#PREPARE_TO_SUSPEND
|
||||
* Writing to this file initiates a transition using the
|
||||
specified event code number; only '0', '2', and '3' are
|
||||
accepted (without a newline); '2' and '3' are both
|
||||
mapped to PM_EVENT_SUSPEND.
|
||||
|
||||
Apm standby -- prepare for APM event. Quiesce devices to make life
|
||||
easier for APM BIOS. event = FREEZE, flags = APM_STANDBY
|
||||
On writes, the PM core relies on that recorded event code and the device/bus
|
||||
capabilities to determine whether it uses a partial suspend() or resume()
|
||||
sequence to change things so that the recorded event corresponds to the
|
||||
numeric parameter.
|
||||
|
||||
Apm suspend -- same as APM_STANDBY, but it we should probably avoid
|
||||
spinning down disks. event = FREEZE, flags = APM_SUSPEND
|
||||
- If the bus requires the irqs-disabled suspend_late()/resume_early()
|
||||
phases, writes fail because those operations are not supported here.
|
||||
|
||||
System halt, reboot -- quiesce devices to make life easier for BIOS. event
|
||||
= FREEZE, flags = SYSTEM_HALT or SYSTEM_REBOOT
|
||||
- If the recorded value is the expected value, nothing is done.
|
||||
|
||||
System shutdown -- at least disks need to be spun down, or data may be
|
||||
lost. Quiesce devices, just to make life easier for BIOS. event =
|
||||
FREEZE, flags = SYSTEM_SHUTDOWN
|
||||
- If the recorded value is nonzero, the device is partially resumed,
|
||||
using the bus.resume() and/or class.resume() methods.
|
||||
|
||||
Kexec -- turn off DMAs and put hardware into some state where new
|
||||
kernel can take over. event = FREEZE, flags = KEXEC
|
||||
- If the target value is nonzero, the device is partially suspended,
|
||||
using the class.suspend() and/or bus.suspend() methods and the
|
||||
PM_EVENT_SUSPEND message.
|
||||
|
||||
Powerdown at end of swsusp -- very similar to SYSTEM_SHUTDOWN, except wake
|
||||
may need to be enabled on some devices. This actually has at least 3
|
||||
subtypes, system can reboot, enter S4 and enter S5 at the end of
|
||||
swsusp. event = FREEZE, flags = SWSUSP and one of SYSTEM_REBOOT,
|
||||
SYSTEM_SHUTDOWN, SYSTEM_S4
|
||||
|
||||
Suspend to ram -- put devices into low power state. event = SUSPEND,
|
||||
flags = SUSPEND_TO_RAM
|
||||
|
||||
Freeze for swsusp snapshot -- stop DMA and interrupts. No need to put
|
||||
devices into low power mode, but you must be able to reinitialize
|
||||
device from scratch in resume method. This has two flavors, its done
|
||||
once on suspending kernel, once on resuming kernel. event = FREEZE,
|
||||
flags = DURING_SUSPEND or DURING_RESUME
|
||||
|
||||
Device detach requested from /sys -- deinitialize device; proably same as
|
||||
SYSTEM_SHUTDOWN, I do not understand this one too much. probably event
|
||||
= FREEZE, flags = DEV_DETACH.
|
||||
|
||||
#These are not really events sent:
|
||||
#
|
||||
#System fully on -- device is working normally; this is probably never
|
||||
#passed to suspend() method... event = ON, flags = 0
|
||||
#
|
||||
#Ready after resume -- userland is now running, again. Time to free any
|
||||
#memory you ate during prepare to suspend... event = ON, flags =
|
||||
#READY_AFTER_RESUME
|
||||
#
|
||||
Drivers have no way to tell whether their suspend() and resume() calls
|
||||
have come through the sysfs power/state file or as part of entering a
|
||||
system sleep state, except that when accessed through sysfs the normal
|
||||
parent/child sequencing rules are ignored. Drivers (such as bus, bridge,
|
||||
or hub drivers) which expose child devices may need to enforce those rules
|
||||
on their own.
|
||||
|
|
|
@ -52,3 +52,18 @@ suspend image will be as small as possible.
|
|||
|
||||
Reading from this file will display the current image size limit, which
|
||||
is set to 500 MB by default.
|
||||
|
||||
/sys/power/pm_trace controls the code which saves the last PM event point in
|
||||
the RTC across reboots, so that you can debug a machine that just hangs
|
||||
during suspend (or more commonly, during resume). Namely, the RTC is only
|
||||
used to save the last PM event point if this file contains '1'. Initially it
|
||||
contains '0' which may be changed to '1' by writing a string representing a
|
||||
nonzero integer into it.
|
||||
|
||||
To use this debugging feature you should attempt to suspend the machine, then
|
||||
reboot it and run
|
||||
|
||||
dmesg -s 1000000 | grep 'hash matches'
|
||||
|
||||
CAUTION: Using it will cause your machine's real-time (CMOS) clock to be
|
||||
set to a random invalid time after a resume.
|
||||
|
|
|
@ -41,11 +41,6 @@ Board-specific code:
|
|||
|
|
||||
.. more boards here ...
|
||||
|
||||
It should also be noted that each board is required to have some certain
|
||||
headers. At the time of this writing, io.h is the only thing that needs
|
||||
to be provided for each board, and can generally just reference generic
|
||||
functions (with the exception of isa_port2addr).
|
||||
|
||||
Next, for companion chips:
|
||||
.
|
||||
`-- arch
|
||||
|
@ -104,12 +99,13 @@ and then populate that with sub-directories for each member of the family.
|
|||
Both the Solution Engine and the hp6xx boards are an example of this.
|
||||
|
||||
After you have setup your new arch/sh/boards/ directory, remember that you
|
||||
also must add a directory in include/asm-sh for headers localized to this
|
||||
board. In order to interoperate seamlessly with the build system, it's best
|
||||
to have this directory the same as the arch/sh/boards/ directory name,
|
||||
though if your board is again part of a family, the build system has ways
|
||||
of dealing with this, and you can feel free to name the directory after
|
||||
the family member itself.
|
||||
should also add a directory in include/asm-sh for headers localized to this
|
||||
board (if there are going to be more than one). In order to interoperate
|
||||
seamlessly with the build system, it's best to have this directory the same
|
||||
as the arch/sh/boards/ directory name, though if your board is again part of
|
||||
a family, the build system has ways of dealing with this (via incdir-y
|
||||
overloading), and you can feel free to name the directory after the family
|
||||
member itself.
|
||||
|
||||
There are a few things that each board is required to have, both in the
|
||||
arch/sh/boards and the include/asm-sh/ heirarchy. In order to better
|
||||
|
@ -122,6 +118,7 @@ might look something like:
|
|||
* arch/sh/boards/vapor/setup.c - Setup code for imaginary board
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <asm/rtc.h> /* for board_time_init() */
|
||||
|
||||
const char *get_system_type(void)
|
||||
{
|
||||
|
@ -152,79 +149,57 @@ int __init platform_setup(void)
|
|||
}
|
||||
|
||||
Our new imaginary board will also have to tie into the machvec in order for it
|
||||
to be of any use. Currently the machvec is slowly on its way out, but is still
|
||||
required for the time being. As such, let us take a look at what needs to be
|
||||
done for the machvec assignment.
|
||||
to be of any use.
|
||||
|
||||
machvec functions fall into a number of categories:
|
||||
|
||||
- I/O functions to IO memory (inb etc) and PCI/main memory (readb etc).
|
||||
- I/O remapping functions (ioremap etc)
|
||||
- some initialisation functions
|
||||
- a 'heartbeat' function
|
||||
- some miscellaneous flags
|
||||
- I/O mapping functions (ioport_map, ioport_unmap, etc).
|
||||
- a 'heartbeat' function.
|
||||
- PCI and IRQ initialization routines.
|
||||
- Consistent allocators (for boards that need special allocators,
|
||||
particularly for allocating out of some board-specific SRAM for DMA
|
||||
handles).
|
||||
|
||||
The tree can be built in two ways:
|
||||
- as a fully generic build. All drivers are linked in, and all functions
|
||||
go through the machvec
|
||||
- as a machine specific build. In this case only the required drivers
|
||||
will be linked in, and some macros may be redefined to not go through
|
||||
the machvec where performance is important (in particular IO functions).
|
||||
There are machvec functions added and removed over time, so always be sure to
|
||||
consult include/asm-sh/machvec.h for the current state of the machvec.
|
||||
|
||||
There are three ways in which IO can be performed:
|
||||
- none at all. This is really only useful for the 'unknown' machine type,
|
||||
which us designed to run on a machine about which we know nothing, and
|
||||
so all all IO instructions do nothing.
|
||||
- fully custom. In this case all IO functions go to a machine specific
|
||||
set of functions which can do what they like
|
||||
- a generic set of functions. These will cope with most situations,
|
||||
and rely on a single function, mv_port2addr, which is called through the
|
||||
machine vector, and converts an IO address into a memory address, which
|
||||
can be read from/written to directly.
|
||||
The kernel will automatically wrap in generic routines for undefined function
|
||||
pointers in the machvec at boot time, as machvec functions are referenced
|
||||
unconditionally throughout most of the tree. Some boards have incredibly
|
||||
sparse machvecs (such as the dreamcast and sh03), whereas others must define
|
||||
virtually everything (rts7751r2d).
|
||||
|
||||
Thus adding a new machine involves the following steps (I will assume I am
|
||||
adding a machine called vapor):
|
||||
Adding a new machine is relatively trivial (using vapor as an example):
|
||||
|
||||
- add a new file include/asm-sh/vapor/io.h which contains prototypes for
|
||||
If the board-specific definitions are quite minimalistic, as is the case for
|
||||
the vast majority of boards, simply having a single board-specific header is
|
||||
sufficient.
|
||||
|
||||
- add a new file include/asm-sh/vapor.h which contains prototypes for
|
||||
any machine specific IO functions prefixed with the machine name, for
|
||||
example vapor_inb. These will be needed when filling out the machine
|
||||
vector.
|
||||
|
||||
This is the minimum that is required, however there are ample
|
||||
opportunities to optimise this. In particular, by making the prototypes
|
||||
inline function definitions, it is possible to inline the function when
|
||||
building machine specific versions. Note that the machine vector
|
||||
functions will still be needed, so that a module built for a generic
|
||||
setup can be loaded.
|
||||
Note that these prototypes are generated automatically by setting
|
||||
__IO_PREFIX to something sensible. A typical example would be:
|
||||
|
||||
- add a new file arch/sh/boards/vapor/mach.c. This contains the definition
|
||||
of the machine vector. When building the machine specific version, this
|
||||
will be the real machine vector (via an alias), while in the generic
|
||||
version is used to initialise the machine vector, and then freed, by
|
||||
making it initdata. This should be defined as:
|
||||
#define __IO_PREFIX vapor
|
||||
#include <asm/io_generic.h>
|
||||
|
||||
struct sh_machine_vector mv_vapor __initmv = {
|
||||
.mv_name = "vapor",
|
||||
}
|
||||
ALIAS_MV(vapor)
|
||||
somewhere in the board-specific header. Any boards being ported that still
|
||||
have a legacy io.h should remove it entirely and switch to the new model.
|
||||
|
||||
- finally add a file arch/sh/boards/vapor/io.c, which contains
|
||||
definitions of the machine specific io functions.
|
||||
- Add machine vector definitions to the board's setup.c. At a bare minimum,
|
||||
this must be defined as something like:
|
||||
|
||||
A note about initialisation functions. Three initialisation functions are
|
||||
provided in the machine vector:
|
||||
- mv_arch_init - called very early on from setup_arch
|
||||
- mv_init_irq - called from init_IRQ, after the generic SH interrupt
|
||||
initialisation
|
||||
- mv_init_pci - currently not used
|
||||
struct sh_machine_vector mv_vapor __initmv = {
|
||||
.mv_name = "vapor",
|
||||
};
|
||||
ALIAS_MV(vapor)
|
||||
|
||||
Any other remaining functions which need to be called at start up can be
|
||||
added to the list using the __initcalls macro (or module_init if the code
|
||||
can be built as a module). Many generic drivers probe to see if the device
|
||||
they are targeting is present, however this may not always be appropriate,
|
||||
so a flag can be added to the machine vector which will be set on those
|
||||
machines which have the hardware in question, reducing the probe to a
|
||||
single conditional.
|
||||
- finally add a file arch/sh/boards/vapor/io.c, which contains definitions of
|
||||
the machine specific io functions (if there are enough to warrant it).
|
||||
|
||||
3. Hooking into the Build System
|
||||
================================
|
||||
|
@ -303,4 +278,3 @@ which will in turn copy the defconfig for this board, run it through
|
|||
oldconfig (prompting you for any new options since the time of creation),
|
||||
and start you on your way to having a functional kernel for your new
|
||||
board.
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
Notes on register bank usage in the kernel
|
||||
==========================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The SH-3 and SH-4 CPU families traditionally include a single partial register
|
||||
bank (selected by SR.RB, only r0 ... r7 are banked), whereas other families
|
||||
may have more full-featured banking or simply no such capabilities at all.
|
||||
|
||||
SR.RB banking
|
||||
-------------
|
||||
|
||||
In the case of this type of banking, banked registers are mapped directly to
|
||||
r0 ... r7 if SR.RB is set to the bank we are interested in, otherwise ldc/stc
|
||||
can still be used to reference the banked registers (as r0_bank ... r7_bank)
|
||||
when in the context of another bank. The developer must keep the SR.RB value
|
||||
in mind when writing code that utilizes these banked registers, for obvious
|
||||
reasons. Userspace is also not able to poke at the bank1 values, so these can
|
||||
be used rather effectively as scratch registers by the kernel.
|
||||
|
||||
Presently the kernel uses several of these registers.
|
||||
|
||||
- r0_bank, r1_bank (referenced as k0 and k1, used for scratch
|
||||
registers when doing exception handling).
|
||||
- r2_bank (used to track the EXPEVT/INTEVT code)
|
||||
- Used by do_IRQ() and friends for doing irq mapping based off
|
||||
of the interrupt exception vector jump table offset
|
||||
- r6_bank (global interrupt mask)
|
||||
- The SR.IMASK interrupt handler makes use of this to set the
|
||||
interrupt priority level (used by local_irq_enable())
|
||||
- r7_bank (current)
|
||||
|
|
@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm:
|
|||
- drop-caches
|
||||
- zone_reclaim_mode
|
||||
- min_unmapped_ratio
|
||||
- min_slab_ratio
|
||||
- panic_on_oom
|
||||
|
||||
==============================================================
|
||||
|
@ -138,7 +139,6 @@ This is value ORed together of
|
|||
1 = Zone reclaim on
|
||||
2 = Zone reclaim writes dirty pages out
|
||||
4 = Zone reclaim swaps pages
|
||||
8 = Also do a global slab reclaim pass
|
||||
|
||||
zone_reclaim_mode is set during bootup to 1 if it is determined that pages
|
||||
from remote zones will cause a measurable performance reduction. The
|
||||
|
@ -162,18 +162,13 @@ Allowing regular swap effectively restricts allocations to the local
|
|||
node unless explicitly overridden by memory policies or cpuset
|
||||
configurations.
|
||||
|
||||
It may be advisable to allow slab reclaim if the system makes heavy
|
||||
use of files and builds up large slab caches. However, the slab
|
||||
shrink operation is global, may take a long time and free slabs
|
||||
in all nodes of the system.
|
||||
|
||||
=============================================================
|
||||
|
||||
min_unmapped_ratio:
|
||||
|
||||
This is available only on NUMA kernels.
|
||||
|
||||
A percentage of the file backed pages in each zone. Zone reclaim will only
|
||||
A percentage of the total pages in each zone. Zone reclaim will only
|
||||
occur if more than this percentage of pages are file backed and unmapped.
|
||||
This is to insure that a minimal amount of local pages is still available for
|
||||
file I/O even if the node is overallocated.
|
||||
|
@ -182,6 +177,24 @@ The default is 1 percent.
|
|||
|
||||
=============================================================
|
||||
|
||||
min_slab_ratio:
|
||||
|
||||
This is available only on NUMA kernels.
|
||||
|
||||
A percentage of the total pages in each zone. On Zone reclaim
|
||||
(fallback from the local zone occurs) slabs will be reclaimed if more
|
||||
than this percentage of pages in a zone are reclaimable slab pages.
|
||||
This insures that the slab growth stays under control even in NUMA
|
||||
systems that rarely perform global reclaim.
|
||||
|
||||
The default is 5 percent.
|
||||
|
||||
Note that slab reclaim is triggered in a per zone / node fashion.
|
||||
The process of reclaiming slab memory is currently not node specific
|
||||
and may not be fast.
|
||||
|
||||
=============================================================
|
||||
|
||||
panic_on_oom
|
||||
|
||||
This enables or disables panic on out-of-memory feature. If this is set to 1,
|
||||
|
|
|
@ -98,13 +98,13 @@ one or more packets could finish before an error stops further endpoint I/O.
|
|||
error, a failure to respond (often caused by
|
||||
device disconnect), or some other fault.
|
||||
|
||||
-ETIMEDOUT (**) No response packet received within the prescribed
|
||||
-ETIME (**) No response packet received within the prescribed
|
||||
bus turn-around time. This error may instead be
|
||||
reported as -EPROTO or -EILSEQ.
|
||||
|
||||
Note that the synchronous USB message functions
|
||||
also use this code to indicate timeout expired
|
||||
before the transfer completed.
|
||||
-ETIMEDOUT Synchronous USB message functions use this code
|
||||
to indicate timeout expired before the transfer
|
||||
completed, and no other error was reported by HC.
|
||||
|
||||
-EPIPE (**) Endpoint stalled. For non-control endpoints,
|
||||
reset this status with usb_clear_halt().
|
||||
|
@ -163,6 +163,3 @@ usb_get_*/usb_set_*():
|
|||
usb_control_msg():
|
||||
usb_bulk_msg():
|
||||
-ETIMEDOUT Timeout expired before the transfer completed.
|
||||
In the future this code may change to -ETIME,
|
||||
whose definition is a closer match to this sort
|
||||
of error.
|
||||
|
|
|
@ -433,6 +433,11 @@ Options supported:
|
|||
See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
|
||||
information on this driver.
|
||||
|
||||
AIRcable USB Dongle Bluetooth driver
|
||||
If there is the cdc_acm driver loaded in the system, you will find that the
|
||||
cdc_acm claims the device before AIRcable can. This is simply corrected
|
||||
by unloading both modules and then loading the aircable module before
|
||||
cdc_acm module
|
||||
|
||||
Generic Serial driver
|
||||
|
||||
|
|
|
@ -245,6 +245,13 @@ Debugging
|
|||
newfallback: use new unwinder but fall back to old if it gets
|
||||
stuck (default)
|
||||
|
||||
call_trace=[old|both|newfallback|new]
|
||||
old: use old inexact backtracer
|
||||
new: use new exact dwarf2 unwinder
|
||||
both: print entries from both
|
||||
newfallback: use new unwinder but fall back to old if it gets
|
||||
stuck (default)
|
||||
|
||||
Misc
|
||||
|
||||
noreplacement Don't replace instructions with more appropriate ones
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
Most of the text from Keith Owens, hacked by AK
|
||||
|
||||
x86_64 page size (PAGE_SIZE) is 4K.
|
||||
|
||||
Like all other architectures, x86_64 has a kernel stack for every
|
||||
active thread. These thread stacks are THREAD_SIZE (2*PAGE_SIZE) big.
|
||||
These stacks contain useful data as long as a thread is alive or a
|
||||
zombie. While the thread is in user space the kernel stack is empty
|
||||
except for the thread_info structure at the bottom.
|
||||
|
||||
In addition to the per thread stacks, there are specialized stacks
|
||||
associated with each cpu. These stacks are only used while the kernel
|
||||
is in control on that cpu, when a cpu returns to user space the
|
||||
specialized stacks contain no useful data. The main cpu stacks is
|
||||
|
||||
* Interrupt stack. IRQSTACKSIZE
|
||||
|
||||
Used for external hardware interrupts. If this is the first external
|
||||
hardware interrupt (i.e. not a nested hardware interrupt) then the
|
||||
kernel switches from the current task to the interrupt stack. Like
|
||||
the split thread and interrupt stacks on i386 (with CONFIG_4KSTACKS),
|
||||
this gives more room for kernel interrupt processing without having
|
||||
to increase the size of every per thread stack.
|
||||
|
||||
The interrupt stack is also used when processing a softirq.
|
||||
|
||||
Switching to the kernel interrupt stack is done by software based on a
|
||||
per CPU interrupt nest counter. This is needed because x86-64 "IST"
|
||||
hardware stacks cannot nest without races.
|
||||
|
||||
x86_64 also has a feature which is not available on i386, the ability
|
||||
to automatically switch to a new stack for designated events such as
|
||||
double fault or NMI, which makes it easier to handle these unusual
|
||||
events on x86_64. This feature is called the Interrupt Stack Table
|
||||
(IST). There can be up to 7 IST entries per cpu. The IST code is an
|
||||
index into the Task State Segment (TSS), the IST entries in the TSS
|
||||
point to dedicated stacks, each stack can be a different size.
|
||||
|
||||
An IST is selected by an non-zero value in the IST field of an
|
||||
interrupt-gate descriptor. When an interrupt occurs and the hardware
|
||||
loads such a descriptor, the hardware automatically sets the new stack
|
||||
pointer based on the IST value, then invokes the interrupt handler. If
|
||||
software wants to allow nested IST interrupts then the handler must
|
||||
adjust the IST values on entry to and exit from the interrupt handler.
|
||||
(this is occasionally done, e.g. for debug exceptions)
|
||||
|
||||
Events with different IST codes (i.e. with different stacks) can be
|
||||
nested. For example, a debug interrupt can safely be interrupted by an
|
||||
NMI. arch/x86_64/kernel/entry.S::paranoidentry adjusts the stack
|
||||
pointers on entry to and exit from all IST events, in theory allowing
|
||||
IST events with the same code to be nested. However in most cases, the
|
||||
stack size allocated to an IST assumes no nesting for the same code.
|
||||
If that assumption is ever broken then the stacks will become corrupt.
|
||||
|
||||
The currently assigned IST stacks are :-
|
||||
|
||||
* STACKFAULT_STACK. EXCEPTION_STKSZ (PAGE_SIZE).
|
||||
|
||||
Used for interrupt 12 - Stack Fault Exception (#SS).
|
||||
|
||||
This allows to recover from invalid stack segments. Rarely
|
||||
happens.
|
||||
|
||||
* DOUBLEFAULT_STACK. EXCEPTION_STKSZ (PAGE_SIZE).
|
||||
|
||||
Used for interrupt 8 - Double Fault Exception (#DF).
|
||||
|
||||
Invoked when handling a exception causes another exception. Happens
|
||||
when the kernel is very confused (e.g. kernel stack pointer corrupt)
|
||||
Using a separate stack allows to recover from it well enough in many
|
||||
cases to still output an oops.
|
||||
|
||||
* NMI_STACK. EXCEPTION_STKSZ (PAGE_SIZE).
|
||||
|
||||
Used for non-maskable interrupts (NMI).
|
||||
|
||||
NMI can be delivered at any time, including when the kernel is in the
|
||||
middle of switching stacks. Using IST for NMI events avoids making
|
||||
assumptions about the previous state of the kernel stack.
|
||||
|
||||
* DEBUG_STACK. DEBUG_STKSZ
|
||||
|
||||
Used for hardware debug interrupts (interrupt 1) and for software
|
||||
debug interrupts (INT3).
|
||||
|
||||
When debugging a kernel, debug interrupts (both hardware and
|
||||
software) can occur at any time. Using IST for these interrupts
|
||||
avoids making assumptions about the previous state of the kernel
|
||||
stack.
|
||||
|
||||
* MCE_STACK. EXCEPTION_STKSZ (PAGE_SIZE).
|
||||
|
||||
Used for interrupt 18 - Machine Check Exception (#MC).
|
||||
|
||||
MCE can be delivered at any time, including when the kernel is in the
|
||||
middle of switching stacks. Using IST for MCE events avoids making
|
||||
assumptions about the previous state of the kernel stack.
|
||||
|
||||
For more details see the Intel IA32 or AMD AMD64 architecture manuals.
|
25
MAINTAINERS
25
MAINTAINERS
|
@ -443,6 +443,23 @@ W: http://people.redhat.com/sgrubb/audit/
|
|||
T: git kernel.org:/pub/scm/linux/kernel/git/dwmw2/audit-2.6.git
|
||||
S: Maintained
|
||||
|
||||
AVR32 ARCHITECTURE
|
||||
P: Atmel AVR32 Support Team
|
||||
M: avr32@atmel.com
|
||||
P: Haavard Skinnemoen
|
||||
M: hskinnemoen@atmel.com
|
||||
W: http://www.atmel.com/products/AVR32/
|
||||
W: http://avr32linux.org/
|
||||
W: http://avrfreaks.net/
|
||||
S: Supported
|
||||
|
||||
AVR32/AT32AP MACHINE SUPPORT
|
||||
P: Atmel AVR32 Support Team
|
||||
M: avr32@atmel.com
|
||||
P: Haavard Skinnemoen
|
||||
M: hskinnemoen@atmel.com
|
||||
S: Supported
|
||||
|
||||
AX.25 NETWORK LAYER
|
||||
P: Ralf Baechle
|
||||
M: ralf@linux-mips.org
|
||||
|
@ -2049,6 +2066,13 @@ L: netfilter@lists.netfilter.org
|
|||
L: netfilter-devel@lists.netfilter.org
|
||||
S: Supported
|
||||
|
||||
NETLABEL
|
||||
P: Paul Moore
|
||||
M: paul.moore@hp.com
|
||||
W: http://netlabel.sf.net
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
NETROM NETWORK LAYER
|
||||
P: Ralf Baechle
|
||||
M: ralf@linux-mips.org
|
||||
|
@ -2673,7 +2697,6 @@ M: josejx@gentoo.org
|
|||
P: Daniel Drake
|
||||
M: dsd@gentoo.org
|
||||
W: http://softmac.sipsolutions.net/
|
||||
L: softmac-dev@sipsolutions.net
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
|
|
6
Makefile
6
Makefile
|
@ -1385,9 +1385,13 @@ endif #ifeq ($(config-targets),1)
|
|||
endif #ifeq ($(mixed-targets),1)
|
||||
|
||||
PHONY += checkstack kernelrelease kernelversion
|
||||
|
||||
# Use $(SUBARCH) here instead of $(ARCH) so that this works for UML.
|
||||
# In the UML case, $(SUBARCH) is the name of the underlying
|
||||
# architecture, while for all other arches, it is the same as $(ARCH).
|
||||
checkstack:
|
||||
$(OBJDUMP) -d vmlinux $$(find . -name '*.ko') | \
|
||||
$(PERL) $(src)/scripts/checkstack.pl $(ARCH)
|
||||
$(PERL) $(src)/scripts/checkstack.pl $(SUBARCH)
|
||||
|
||||
kernelrelease:
|
||||
$(if $(wildcard include/config/kernel.release), $(Q)echo $(KERNELRELEASE), \
|
||||
|
|
|
@ -381,7 +381,7 @@ config ALPHA_EV56
|
|||
|
||||
config ALPHA_EV56
|
||||
prompt "EV56 CPU (speed >= 333MHz)?"
|
||||
depends on ALPHA_NORITAKE && ALPHA_PRIMO
|
||||
depends on ALPHA_NORITAKE || ALPHA_PRIMO
|
||||
|
||||
config ALPHA_EV56
|
||||
prompt "EV56 CPU (speed >= 400MHz)?"
|
||||
|
|
|
@ -270,7 +270,7 @@ callback_init(void * kernel_end)
|
|||
void
|
||||
paging_init(void)
|
||||
{
|
||||
unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
|
||||
unsigned long zones_size[MAX_NR_ZONES] = {0, };
|
||||
unsigned long dma_pfn, high_pfn;
|
||||
|
||||
dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
|
||||
|
|
|
@ -284,21 +284,9 @@ static struct pxaficp_platform_data corgi_ficp_platform_data = {
|
|||
/*
|
||||
* USB Device Controller
|
||||
*/
|
||||
static void corgi_udc_command(int cmd)
|
||||
{
|
||||
switch(cmd) {
|
||||
case PXA2XX_UDC_CMD_CONNECT:
|
||||
GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
|
||||
break;
|
||||
case PXA2XX_UDC_CMD_DISCONNECT:
|
||||
GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct pxa2xx_udc_mach_info udc_info __initdata = {
|
||||
/* no connect GPIO; corgi can't tell connection status */
|
||||
.udc_command = corgi_udc_command,
|
||||
.gpio_pullup = CORGI_GPIO_USB_PULLUP,
|
||||
};
|
||||
|
||||
|
||||
|
@ -350,7 +338,6 @@ static void __init corgi_init(void)
|
|||
corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
|
||||
|
||||
pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
|
||||
pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT);
|
||||
pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
|
||||
|
||||
pxa_set_udc_info(&udc_info);
|
||||
|
|
|
@ -177,7 +177,7 @@ static void unmap_area_sections(unsigned long virt, unsigned long size)
|
|||
* Free the page table, if there was one.
|
||||
*/
|
||||
if ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_TABLE)
|
||||
pte_free_kernel(pmd_page_kernel(pmd));
|
||||
pte_free_kernel(pmd_page_vaddr(pmd));
|
||||
}
|
||||
|
||||
addr += PGDIR_SIZE;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb_otg.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see Documentation/kbuild/kconfig-language.txt.
|
||||
#
|
||||
|
||||
mainmenu "Linux Kernel Configuration"
|
||||
|
||||
config AVR32
|
||||
bool
|
||||
default y
|
||||
# With EMBEDDED=n, we get lots of stuff automatically selected
|
||||
# that we usually don't need on AVR32.
|
||||
select EMBEDDED
|
||||
help
|
||||
AVR32 is a high-performance 32-bit RISC microprocessor core,
|
||||
designed for cost-sensitive embedded applications, with particular
|
||||
emphasis on low power consumption and high code density.
|
||||
|
||||
There is an AVR32 Linux project with a web page at
|
||||
http://avr32linux.org/.
|
||||
|
||||
config UID16
|
||||
bool
|
||||
|
||||
config GENERIC_HARDIRQS
|
||||
bool
|
||||
default y
|
||||
|
||||
config HARDIRQS_SW_RESEND
|
||||
bool
|
||||
default y
|
||||
|
||||
config GENERIC_IRQ_PROBE
|
||||
bool
|
||||
default y
|
||||
|
||||
config RWSEM_GENERIC_SPINLOCK
|
||||
bool
|
||||
default y
|
||||
|
||||
config GENERIC_TIME
|
||||
bool
|
||||
default y
|
||||
|
||||
config RWSEM_XCHGADD_ALGORITHM
|
||||
bool
|
||||
|
||||
config GENERIC_BUST_SPINLOCK
|
||||
bool
|
||||
|
||||
config GENERIC_HWEIGHT
|
||||
bool
|
||||
default y
|
||||
|
||||
config GENERIC_CALIBRATE_DELAY
|
||||
bool
|
||||
default y
|
||||
|
||||
source "init/Kconfig"
|
||||
|
||||
menu "System Type and features"
|
||||
|
||||
config SUBARCH_AVR32B
|
||||
bool
|
||||
config MMU
|
||||
bool
|
||||
config PERFORMANCE_COUNTERS
|
||||
bool
|
||||
|
||||
config PLATFORM_AT32AP
|
||||
bool
|
||||
select SUBARCH_AVR32B
|
||||
select MMU
|
||||
select PERFORMANCE_COUNTERS
|
||||
|
||||
choice
|
||||
prompt "AVR32 CPU type"
|
||||
default CPU_AT32AP7000
|
||||
|
||||
config CPU_AT32AP7000
|
||||
bool "AT32AP7000"
|
||||
select PLATFORM_AT32AP
|
||||
endchoice
|
||||
|
||||
#
|
||||
# CPU Daughterboards for ATSTK1000
|
||||
config BOARD_ATSTK1002
|
||||
bool
|
||||
|
||||
choice
|
||||
prompt "AVR32 board type"
|
||||
default BOARD_ATSTK1000
|
||||
|
||||
config BOARD_ATSTK1000
|
||||
bool "ATSTK1000 evaluation board"
|
||||
select BOARD_ATSTK1002 if CPU_AT32AP7000
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Boot loader type"
|
||||
default LOADER_U_BOOT
|
||||
|
||||
config LOADER_U_BOOT
|
||||
bool "U-Boot (or similar) bootloader"
|
||||
endchoice
|
||||
|
||||
config LOAD_ADDRESS
|
||||
hex
|
||||
default 0x10000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y
|
||||
|
||||
config ENTRY_ADDRESS
|
||||
hex
|
||||
default 0x90000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y
|
||||
|
||||
config PHYS_OFFSET
|
||||
hex
|
||||
default 0x10000000 if CPU_AT32AP7000=y
|
||||
|
||||
source "kernel/Kconfig.preempt"
|
||||
|
||||
config HAVE_ARCH_BOOTMEM_NODE
|
||||
bool
|
||||
default n
|
||||
|
||||
config ARCH_HAVE_MEMORY_PRESENT
|
||||
bool
|
||||
default n
|
||||
|
||||
config NEED_NODE_MEMMAP_SIZE
|
||||
bool
|
||||
default n
|
||||
|
||||
config ARCH_FLATMEM_ENABLE
|
||||
bool
|
||||
default y
|
||||
|
||||
config ARCH_DISCONTIGMEM_ENABLE
|
||||
bool
|
||||
default n
|
||||
|
||||
config ARCH_SPARSEMEM_ENABLE
|
||||
bool
|
||||
default n
|
||||
|
||||
source "mm/Kconfig"
|
||||
|
||||
config OWNERSHIP_TRACE
|
||||
bool "Ownership trace support"
|
||||
default y
|
||||
help
|
||||
Say Y to generate an Ownership Trace message on every context switch,
|
||||
enabling Nexus-compliant debuggers to keep track of the PID of the
|
||||
currently executing task.
|
||||
|
||||
# FPU emulation goes here
|
||||
|
||||
source "kernel/Kconfig.hz"
|
||||
|
||||
config CMDLINE
|
||||
string "Default kernel command line"
|
||||
default ""
|
||||
help
|
||||
If you don't have a boot loader capable of passing a command line string
|
||||
to the kernel, you may specify one here. As a minimum, you should specify
|
||||
the memory size and the root device (e.g., mem=8M, root=/dev/nfs).
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Bus options"
|
||||
|
||||
config PCI
|
||||
bool
|
||||
|
||||
source "drivers/pci/Kconfig"
|
||||
|
||||
source "drivers/pcmcia/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Executable file formats"
|
||||
source "fs/Kconfig.binfmt"
|
||||
endmenu
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
source "drivers/Kconfig"
|
||||
|
||||
source "fs/Kconfig"
|
||||
|
||||
source "arch/avr32/Kconfig.debug"
|
||||
|
||||
source "security/Kconfig"
|
||||
|
||||
source "crypto/Kconfig"
|
||||
|
||||
source "lib/Kconfig"
|
|
@ -0,0 +1,19 @@
|
|||
menu "Kernel hacking"
|
||||
|
||||
config TRACE_IRQFLAGS_SUPPORT
|
||||
bool
|
||||
default y
|
||||
|
||||
source "lib/Kconfig.debug"
|
||||
|
||||
config KPROBES
|
||||
bool "Kprobes"
|
||||
depends on DEBUG_KERNEL
|
||||
help
|
||||
Kprobes allows you to trap at almost any kernel address and
|
||||
execute a callback function. register_kprobe() establishes
|
||||
a probepoint and specifies the callback. Kprobes is useful
|
||||
for kernel debugging, non-intrusive instrumentation and testing.
|
||||
If in doubt, say "N".
|
||||
|
||||
endmenu
|
|
@ -0,0 +1,84 @@
|
|||
#
|
||||
# This file is subject to the terms and conditions of the GNU General Public
|
||||
# License. See the file "COPYING" in the main directory of this archive
|
||||
# for more details.
|
||||
#
|
||||
# Copyright (C) 2004-2006 Atmel Corporation.
|
||||
|
||||
# Default target when executing plain make
|
||||
.PHONY: all
|
||||
all: uImage vmlinux.elf linux.lst
|
||||
|
||||
KBUILD_DEFCONFIG := atstk1002_defconfig
|
||||
|
||||
CFLAGS += -pipe -fno-builtin -mno-pic
|
||||
AFLAGS += -mrelax -mno-pic
|
||||
CFLAGS_MODULE += -mno-relax
|
||||
LDFLAGS_vmlinux += --relax
|
||||
|
||||
cpuflags-$(CONFIG_CPU_AP7000) += -mcpu=ap7000
|
||||
|
||||
CFLAGS += $(cpuflags-y)
|
||||
AFLAGS += $(cpuflags-y)
|
||||
|
||||
CHECKFLAGS += -D__avr32__
|
||||
|
||||
LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||
|
||||
head-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/head.o
|
||||
head-y += arch/avr32/kernel/head.o
|
||||
core-$(CONFIG_PLATFORM_AT32AP) += arch/avr32/mach-at32ap/
|
||||
core-$(CONFIG_BOARD_ATSTK1000) += arch/avr32/boards/atstk1000/
|
||||
core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/
|
||||
core-y += arch/avr32/kernel/
|
||||
core-y += arch/avr32/mm/
|
||||
libs-y += arch/avr32/lib/ #$(LIBGCC)
|
||||
|
||||
archincdir-$(CONFIG_PLATFORM_AT32AP) := arch-at32ap
|
||||
|
||||
include/asm-avr32/.arch: $(wildcard include/config/platform/*.h) include/config/auto.conf
|
||||
@echo ' SYMLINK include/asm-avr32/arch -> include/asm-avr32/$(archincdir-y)'
|
||||
ifneq ($(KBUILD_SRC),)
|
||||
$(Q)mkdir -p include/asm-avr32
|
||||
$(Q)ln -fsn $(srctree)/include/asm-avr32/$(archincdir-y) include/asm-avr32/arch
|
||||
else
|
||||
$(Q)ln -fsn $(archincdir-y) include/asm-avr32/arch
|
||||
endif
|
||||
@touch $@
|
||||
|
||||
archprepare: include/asm-avr32/.arch
|
||||
|
||||
BOOT_TARGETS := vmlinux.elf vmlinux.bin uImage uImage.srec
|
||||
|
||||
.PHONY: $(BOOT_TARGETS) install
|
||||
|
||||
boot := arch/$(ARCH)/boot/images
|
||||
|
||||
KBUILD_IMAGE := $(boot)/uImage
|
||||
vmlinux.elf: KBUILD_IMAGE := $(boot)/vmlinux.elf
|
||||
vmlinux.cso: KBUILD_IMAGE := $(boot)/vmlinux.cso
|
||||
uImage.srec: KBUILD_IMAGE := $(boot)/uImage.srec
|
||||
uImage: KBUILD_IMAGE := $(boot)/uImage
|
||||
|
||||
quiet_cmd_listing = LST $@
|
||||
cmd_listing = avr32-linux-objdump $(OBJDUMPFLAGS) -lS $< > $@
|
||||
quiet_cmd_disasm = DIS $@
|
||||
cmd_disasm = avr32-linux-objdump $(OBJDUMPFLAGS) -d $< > $@
|
||||
|
||||
vmlinux.elf vmlinux.bin uImage.srec uImage vmlinux.cso: vmlinux
|
||||
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
|
||||
|
||||
install: vmlinux
|
||||
$(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) $@
|
||||
|
||||
linux.s: vmlinux
|
||||
$(call if_changed,disasm)
|
||||
|
||||
linux.lst: vmlinux
|
||||
$(call if_changed,listing)
|
||||
|
||||
define archhelp
|
||||
@echo '* vmlinux.elf - ELF image with load address 0'
|
||||
@echo ' vmlinux.cso - PathFinder CSO image'
|
||||
@echo ' uImage - Create a bootable image for U-Boot'
|
||||
endef
|
|
@ -0,0 +1,2 @@
|
|||
obj-y += setup.o spi.o flash.o
|
||||
obj-$(CONFIG_BOARD_ATSTK1002) += atstk1002.o
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* ATSTK1002 daughterboard-specific init code
|
||||
*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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/init.h>
|
||||
|
||||
#include <asm/arch/board.h>
|
||||
|
||||
struct eth_platform_data __initdata eth0_data = {
|
||||
.valid = 1,
|
||||
.mii_phy_addr = 0x10,
|
||||
.is_rmii = 0,
|
||||
.hw_addr = { 0x6a, 0x87, 0x71, 0x14, 0xcd, 0xcb },
|
||||
};
|
||||
|
||||
extern struct lcdc_platform_data atstk1000_fb0_data;
|
||||
|
||||
static int __init atstk1002_init(void)
|
||||
{
|
||||
at32_add_system_devices();
|
||||
|
||||
at32_add_device_usart(1); /* /dev/ttyS0 */
|
||||
at32_add_device_usart(2); /* /dev/ttyS1 */
|
||||
at32_add_device_usart(3); /* /dev/ttyS2 */
|
||||
|
||||
at32_add_device_eth(0, ð0_data);
|
||||
at32_add_device_spi(0);
|
||||
at32_add_device_lcdc(0, &atstk1000_fb0_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
postcore_initcall(atstk1002_init);
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* ATSTK1000 board-specific flash initialization
|
||||
*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
|
||||
#include <asm/arch/smc.h>
|
||||
|
||||
static struct smc_config flash_config __initdata = {
|
||||
.ncs_read_setup = 0,
|
||||
.nrd_setup = 40,
|
||||
.ncs_write_setup = 0,
|
||||
.nwe_setup = 10,
|
||||
|
||||
.ncs_read_pulse = 80,
|
||||
.nrd_pulse = 40,
|
||||
.ncs_write_pulse = 65,
|
||||
.nwe_pulse = 55,
|
||||
|
||||
.read_cycle = 120,
|
||||
.write_cycle = 120,
|
||||
|
||||
.bus_width = 2,
|
||||
.nrd_controlled = 1,
|
||||
.nwe_controlled = 1,
|
||||
.byte_write = 1,
|
||||
};
|
||||
|
||||
static struct mtd_partition flash_parts[] = {
|
||||
{
|
||||
.name = "u-boot",
|
||||
.offset = 0x00000000,
|
||||
.size = 0x00020000, /* 128 KiB */
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
},
|
||||
{
|
||||
.name = "root",
|
||||
.offset = 0x00020000,
|
||||
.size = 0x007d0000,
|
||||
},
|
||||
{
|
||||
.name = "env",
|
||||
.offset = 0x007f0000,
|
||||
.size = 0x00010000,
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct physmap_flash_data flash_data = {
|
||||
.width = 2,
|
||||
.nr_parts = ARRAY_SIZE(flash_parts),
|
||||
.parts = flash_parts,
|
||||
};
|
||||
|
||||
static struct resource flash_resource = {
|
||||
.start = 0x00000000,
|
||||
.end = 0x007fffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_device flash_device = {
|
||||
.name = "physmap-flash",
|
||||
.id = 0,
|
||||
.resource = &flash_resource,
|
||||
.num_resources = 1,
|
||||
.dev = {
|
||||
.platform_data = &flash_data,
|
||||
},
|
||||
};
|
||||
|
||||
/* This needs to be called after the SMC has been initialized */
|
||||
static int __init atstk1000_flash_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = smc_set_configuration(0, &flash_config);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "atstk1000: failed to set NOR flash timing\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_device_register(&flash_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(atstk1000_flash_init);
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* ATSTK1000 board-specific setup code.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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/bootmem.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include <asm/arch/board.h>
|
||||
|
||||
/* Initialized by bootloader-specific startup code. */
|
||||
struct tag *bootloader_tags __initdata;
|
||||
|
||||
struct lcdc_platform_data __initdata atstk1000_fb0_data;
|
||||
|
||||
asmlinkage void __init board_early_init(void)
|
||||
{
|
||||
extern void sdram_init(void);
|
||||
|
||||
#ifdef CONFIG_LOADER_STANDALONE
|
||||
sdram_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void __init board_setup_fbmem(unsigned long fbmem_start,
|
||||
unsigned long fbmem_size)
|
||||
{
|
||||
if (!fbmem_size)
|
||||
return;
|
||||
|
||||
if (!fbmem_start) {
|
||||
void *fbmem;
|
||||
|
||||
fbmem = alloc_bootmem_low_pages(fbmem_size);
|
||||
fbmem_start = __pa(fbmem);
|
||||
} else {
|
||||
pg_data_t *pgdat;
|
||||
|
||||
for_each_online_pgdat(pgdat) {
|
||||
if (fbmem_start >= pgdat->bdata->node_boot_start
|
||||
&& fbmem_start <= pgdat->bdata->node_low_pfn)
|
||||
reserve_bootmem_node(pgdat, fbmem_start,
|
||||
fbmem_size);
|
||||
}
|
||||
}
|
||||
|
||||
printk("%luKiB framebuffer memory at address 0x%08lx\n",
|
||||
fbmem_size >> 10, fbmem_start);
|
||||
atstk1000_fb0_data.fbmem_start = fbmem_start;
|
||||
atstk1000_fb0_data.fbmem_size = fbmem_size;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* ATSTK1000 SPI devices
|
||||
*
|
||||
* Copyright (C) 2005 Atmel Norway
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
static struct spi_board_info spi_board_info[] __initdata = {
|
||||
{
|
||||
.modalias = "ltv350qv",
|
||||
.max_speed_hz = 16000000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static int board_init_spi(void)
|
||||
{
|
||||
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(board_init_spi);
|
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# Copyright (C) 2004-2006 Atmel Corporation
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU General Public
|
||||
# License. See the file "COPYING" in the main directory of this archive
|
||||
# for more details.
|
||||
#
|
||||
|
||||
MKIMAGE := $(srctree)/scripts/mkuboot.sh
|
||||
|
||||
extra-y := vmlinux.bin vmlinux.gz
|
||||
|
||||
OBJCOPYFLAGS_vmlinux.bin := -O binary
|
||||
$(obj)/vmlinux.bin: vmlinux FORCE
|
||||
$(call if_changed,objcopy)
|
||||
|
||||
$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
|
||||
$(call if_changed,gzip)
|
||||
|
||||
quiet_cmd_uimage = UIMAGE $@
|
||||
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A avr32 -O linux -T kernel \
|
||||
-C gzip -a $(CONFIG_LOAD_ADDRESS) -e $(CONFIG_ENTRY_ADDRESS) \
|
||||
-n 'Linux-$(KERNELRELEASE)' -d $< $@
|
||||
|
||||
targets += uImage uImage.srec
|
||||
$(obj)/uImage: $(obj)/vmlinux.gz
|
||||
$(call if_changed,uimage)
|
||||
@echo ' Image $@ is ready'
|
||||
|
||||
OBJCOPYFLAGS_uImage.srec := -I binary -O srec
|
||||
$(obj)/uImage.srec: $(obj)/uImage
|
||||
$(call if_changed,objcopy)
|
||||
|
||||
OBJCOPYFLAGS_vmlinux.elf := --change-section-lma .text-0x80000000 \
|
||||
--change-section-lma __ex_table-0x80000000 \
|
||||
--change-section-lma .rodata-0x80000000 \
|
||||
--change-section-lma .data-0x80000000 \
|
||||
--change-section-lma .init-0x80000000 \
|
||||
--change-section-lma .bss-0x80000000 \
|
||||
--change-section-lma .initrd-0x80000000 \
|
||||
--change-section-lma __param-0x80000000 \
|
||||
--change-section-lma __ksymtab-0x80000000 \
|
||||
--change-section-lma __ksymtab_gpl-0x80000000 \
|
||||
--change-section-lma __kcrctab-0x80000000 \
|
||||
--change-section-lma __kcrctab_gpl-0x80000000 \
|
||||
--change-section-lma __ksymtab_strings-0x80000000 \
|
||||
--change-section-lma .got-0x80000000 \
|
||||
--set-start 0xa0000000
|
||||
$(obj)/vmlinux.elf: vmlinux FORCE
|
||||
$(call if_changed,objcopy)
|
||||
|
||||
quiet_cmd_sfdwarf = SFDWARF $@
|
||||
cmd_sfdwarf = sfdwarf $< TO $@ GNUAVR IW $(SFDWARF_FLAGS) > $(obj)/sfdwarf.log
|
||||
|
||||
$(obj)/vmlinux.cso: $(obj)/vmlinux.elf FORCE
|
||||
$(call if_changed,sfdwarf)
|
||||
|
||||
install: $(BOOTIMAGE)
|
||||
sh $(srctree)/install-kernel.sh $<
|
||||
|
||||
# Generated files to be removed upon make clean
|
||||
clean-files := vmlinux* uImage uImage.srec
|
|
@ -0,0 +1,3 @@
|
|||
extra-y := head.o
|
||||
|
||||
obj-y := empty.o
|
|
@ -0,0 +1 @@
|
|||
/* Empty file */
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Startup code for use with the u-boot bootloader.
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/setup.h>
|
||||
|
||||
/*
|
||||
* The kernel is loaded where we want it to be and all caches
|
||||
* have just been flushed. We get two parameters from u-boot:
|
||||
*
|
||||
* r12 contains a magic number (ATAG_MAGIC)
|
||||
* r11 points to a tag table providing information about
|
||||
* the system.
|
||||
*/
|
||||
.section .init.text,"ax"
|
||||
.global _start
|
||||
_start:
|
||||
/* Check if the boot loader actually provided a tag table */
|
||||
lddpc r0, magic_number
|
||||
cp.w r12, r0
|
||||
brne no_tag_table
|
||||
|
||||
/* Initialize .bss */
|
||||
lddpc r2, bss_start_addr
|
||||
lddpc r3, end_addr
|
||||
mov r0, 0
|
||||
mov r1, 0
|
||||
1: st.d r2++, r0
|
||||
cp r2, r3
|
||||
brlo 1b
|
||||
|
||||
/*
|
||||
* Save the tag table address for later use. This must be done
|
||||
* _after_ .bss has been initialized...
|
||||
*/
|
||||
lddpc r0, tag_table_addr
|
||||
st.w r0[0], r11
|
||||
|
||||
/* Jump to loader-independent setup code */
|
||||
rjmp kernel_entry
|
||||
|
||||
.align 2
|
||||
magic_number:
|
||||
.long ATAG_MAGIC
|
||||
tag_table_addr:
|
||||
.long bootloader_tags
|
||||
bss_start_addr:
|
||||
.long __bss_start
|
||||
end_addr:
|
||||
.long _end
|
||||
|
||||
no_tag_table:
|
||||
sub r12, pc, (. - 2f)
|
||||
bral panic
|
||||
2: .asciz "Boot loader didn't provide correct magic number\n"
|
|
@ -1,158 +1,15 @@
|
|||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Linux kernel version: 2.6.18-rc1
|
||||
# Thu Jul 6 10:04:05 2006
|
||||
# Tue Jul 11 12:41:36 2006
|
||||
#
|
||||
CONFIG_MIPS=y
|
||||
|
||||
#
|
||||
# Machine selection
|
||||
#
|
||||
# CONFIG_MIPS_MTX1 is not set
|
||||
# CONFIG_MIPS_BOSPORUS is not set
|
||||
# CONFIG_MIPS_PB1000 is not set
|
||||
# CONFIG_MIPS_PB1100 is not set
|
||||
# CONFIG_MIPS_PB1500 is not set
|
||||
# CONFIG_MIPS_PB1550 is not set
|
||||
# CONFIG_MIPS_PB1200 is not set
|
||||
# CONFIG_MIPS_DB1000 is not set
|
||||
# CONFIG_MIPS_DB1100 is not set
|
||||
# CONFIG_MIPS_DB1500 is not set
|
||||
# CONFIG_MIPS_DB1550 is not set
|
||||
# CONFIG_MIPS_DB1200 is not set
|
||||
# CONFIG_MIPS_MIRAGE is not set
|
||||
# CONFIG_BASLER_EXCITE is not set
|
||||
# CONFIG_MIPS_COBALT is not set
|
||||
# CONFIG_MACH_DECSTATION is not set
|
||||
# CONFIG_MIPS_EV64120 is not set
|
||||
CONFIG_MIPS_EV96100=y
|
||||
# CONFIG_MIPS_IVR is not set
|
||||
# CONFIG_MIPS_ITE8172 is not set
|
||||
# CONFIG_MACH_JAZZ is not set
|
||||
# CONFIG_LASAT is not set
|
||||
# CONFIG_MIPS_ATLAS is not set
|
||||
# CONFIG_MIPS_MALTA is not set
|
||||
# CONFIG_MIPS_SEAD is not set
|
||||
# CONFIG_WR_PPMC is not set
|
||||
# CONFIG_MIPS_SIM is not set
|
||||
# CONFIG_MOMENCO_JAGUAR_ATX is not set
|
||||
# CONFIG_MOMENCO_OCELOT is not set
|
||||
# CONFIG_MOMENCO_OCELOT_3 is not set
|
||||
# CONFIG_MOMENCO_OCELOT_C is not set
|
||||
# CONFIG_MOMENCO_OCELOT_G is not set
|
||||
# CONFIG_MIPS_XXS1500 is not set
|
||||
# CONFIG_PNX8550_V2PCI is not set
|
||||
# CONFIG_PNX8550_JBS is not set
|
||||
# CONFIG_DDB5477 is not set
|
||||
# CONFIG_MACH_VR41XX is not set
|
||||
# CONFIG_PMC_YOSEMITE is not set
|
||||
# CONFIG_QEMU is not set
|
||||
# CONFIG_MARKEINS is not set
|
||||
# CONFIG_SGI_IP22 is not set
|
||||
# CONFIG_SGI_IP27 is not set
|
||||
# CONFIG_SGI_IP32 is not set
|
||||
# CONFIG_SIBYTE_BIGSUR is not set
|
||||
# CONFIG_SIBYTE_SWARM is not set
|
||||
# CONFIG_SIBYTE_SENTOSA is not set
|
||||
# CONFIG_SIBYTE_RHONE is not set
|
||||
# CONFIG_SIBYTE_CARMEL is not set
|
||||
# CONFIG_SIBYTE_PTSWARM is not set
|
||||
# CONFIG_SIBYTE_LITTLESUR is not set
|
||||
# CONFIG_SIBYTE_CRHINE is not set
|
||||
# CONFIG_SIBYTE_CRHONE is not set
|
||||
# CONFIG_SNI_RM200_PCI is not set
|
||||
# CONFIG_TOSHIBA_JMR3927 is not set
|
||||
# CONFIG_TOSHIBA_RBTX4927 is not set
|
||||
# CONFIG_TOSHIBA_RBTX4938 is not set
|
||||
CONFIG_AVR32=y
|
||||
CONFIG_GENERIC_HARDIRQS=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_GENERIC_IRQ_PROBE=y
|
||||
CONFIG_RWSEM_GENERIC_SPINLOCK=y
|
||||
CONFIG_GENERIC_FIND_NEXT_BIT=y
|
||||
CONFIG_GENERIC_HWEIGHT=y
|
||||
CONFIG_GENERIC_CALIBRATE_DELAY=y
|
||||
CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
|
||||
CONFIG_DMA_NONCOHERENT=y
|
||||
CONFIG_DMA_NEED_PCI_MAP_STATE=y
|
||||
CONFIG_CPU_BIG_ENDIAN=y
|
||||
# CONFIG_CPU_LITTLE_ENDIAN is not set
|
||||
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
|
||||
CONFIG_IRQ_CPU=y
|
||||
CONFIG_MIPS_GT64120=y
|
||||
CONFIG_SWAP_IO_SPACE=y
|
||||
CONFIG_MIPS_GT96100=y
|
||||
CONFIG_MIPS_L1_CACHE_SHIFT=5
|
||||
|
||||
#
|
||||
# CPU selection
|
||||
#
|
||||
# CONFIG_CPU_MIPS32_R1 is not set
|
||||
# CONFIG_CPU_MIPS32_R2 is not set
|
||||
# CONFIG_CPU_MIPS64_R1 is not set
|
||||
# CONFIG_CPU_MIPS64_R2 is not set
|
||||
# CONFIG_CPU_R3000 is not set
|
||||
# CONFIG_CPU_TX39XX is not set
|
||||
# CONFIG_CPU_VR41XX is not set
|
||||
# CONFIG_CPU_R4300 is not set
|
||||
# CONFIG_CPU_R4X00 is not set
|
||||
# CONFIG_CPU_TX49XX is not set
|
||||
# CONFIG_CPU_R5000 is not set
|
||||
# CONFIG_CPU_R5432 is not set
|
||||
# CONFIG_CPU_R6000 is not set
|
||||
# CONFIG_CPU_NEVADA is not set
|
||||
# CONFIG_CPU_R8000 is not set
|
||||
# CONFIG_CPU_R10000 is not set
|
||||
CONFIG_CPU_RM7000=y
|
||||
# CONFIG_CPU_RM9000 is not set
|
||||
# CONFIG_CPU_SB1 is not set
|
||||
CONFIG_SYS_HAS_CPU_R5000=y
|
||||
CONFIG_SYS_HAS_CPU_RM7000=y
|
||||
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
|
||||
CONFIG_SYS_SUPPORTS_64BIT_KERNEL=y
|
||||
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
|
||||
CONFIG_CPU_SUPPORTS_64BIT_KERNEL=y
|
||||
|
||||
#
|
||||
# Kernel type
|
||||
#
|
||||
CONFIG_32BIT=y
|
||||
# CONFIG_64BIT is not set
|
||||
CONFIG_PAGE_SIZE_4KB=y
|
||||
# CONFIG_PAGE_SIZE_8KB is not set
|
||||
# CONFIG_PAGE_SIZE_16KB is not set
|
||||
# CONFIG_PAGE_SIZE_64KB is not set
|
||||
CONFIG_BOARD_SCACHE=y
|
||||
CONFIG_RM7000_CPU_SCACHE=y
|
||||
CONFIG_CPU_HAS_PREFETCH=y
|
||||
CONFIG_MIPS_MT_DISABLED=y
|
||||
# CONFIG_MIPS_MT_SMTC is not set
|
||||
# CONFIG_MIPS_MT_SMP is not set
|
||||
# CONFIG_MIPS_VPE_LOADER is not set
|
||||
# CONFIG_64BIT_PHYS_ADDR is not set
|
||||
CONFIG_CPU_HAS_LLSC=y
|
||||
CONFIG_CPU_HAS_SYNC=y
|
||||
CONFIG_GENERIC_HARDIRQS=y
|
||||
CONFIG_GENERIC_IRQ_PROBE=y
|
||||
CONFIG_CPU_SUPPORTS_HIGHMEM=y
|
||||
CONFIG_ARCH_FLATMEM_ENABLE=y
|
||||
CONFIG_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_FLATMEM_MANUAL=y
|
||||
# CONFIG_DISCONTIGMEM_MANUAL is not set
|
||||
# CONFIG_SPARSEMEM_MANUAL is not set
|
||||
CONFIG_FLATMEM=y
|
||||
CONFIG_FLAT_NODE_MEM_MAP=y
|
||||
# CONFIG_SPARSEMEM_STATIC is not set
|
||||
CONFIG_SPLIT_PTLOCK_CPUS=4
|
||||
# CONFIG_RESOURCES_64BIT is not set
|
||||
# CONFIG_HZ_48 is not set
|
||||
# CONFIG_HZ_100 is not set
|
||||
# CONFIG_HZ_128 is not set
|
||||
# CONFIG_HZ_250 is not set
|
||||
# CONFIG_HZ_256 is not set
|
||||
CONFIG_HZ_1000=y
|
||||
# CONFIG_HZ_1024 is not set
|
||||
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
|
||||
CONFIG_HZ=1000
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
# CONFIG_PREEMPT_VOLUNTARY is not set
|
||||
# CONFIG_PREEMPT is not set
|
||||
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
|
||||
|
||||
#
|
||||
|
@ -166,34 +23,34 @@ CONFIG_INIT_ENV_ARG_LIMIT=32
|
|||
# General setup
|
||||
#
|
||||
CONFIG_LOCALVERSION=""
|
||||
CONFIG_LOCALVERSION_AUTO=y
|
||||
# CONFIG_LOCALVERSION_AUTO is not set
|
||||
CONFIG_SWAP=y
|
||||
CONFIG_SYSVIPC=y
|
||||
# CONFIG_SYSVIPC is not set
|
||||
# CONFIG_POSIX_MQUEUE is not set
|
||||
# CONFIG_BSD_PROCESS_ACCT is not set
|
||||
CONFIG_SYSCTL=y
|
||||
# CONFIG_AUDIT is not set
|
||||
# CONFIG_IKCONFIG is not set
|
||||
CONFIG_RELAY=y
|
||||
# CONFIG_RELAY is not set
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
|
||||
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
|
||||
CONFIG_EMBEDDED=y
|
||||
CONFIG_KALLSYMS=y
|
||||
# CONFIG_KALLSYMS_ALL is not set
|
||||
# CONFIG_KALLSYMS_EXTRA_PASS is not set
|
||||
# CONFIG_HOTPLUG is not set
|
||||
CONFIG_HOTPLUG=y
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_BUG=y
|
||||
CONFIG_ELF_CORE=y
|
||||
CONFIG_BASE_FULL=y
|
||||
CONFIG_RT_MUTEXES=y
|
||||
CONFIG_FUTEX=y
|
||||
CONFIG_EPOLL=y
|
||||
# CONFIG_BASE_FULL is not set
|
||||
# CONFIG_FUTEX is not set
|
||||
# CONFIG_EPOLL is not set
|
||||
CONFIG_SHMEM=y
|
||||
CONFIG_SLAB=y
|
||||
CONFIG_VM_EVENT_COUNTERS=y
|
||||
# CONFIG_SLAB is not set
|
||||
# CONFIG_VM_EVENT_COUNTERS is not set
|
||||
# CONFIG_TINY_SHMEM is not set
|
||||
CONFIG_BASE_SMALL=0
|
||||
# CONFIG_SLOB is not set
|
||||
CONFIG_BASE_SMALL=1
|
||||
CONFIG_SLOB=y
|
||||
|
||||
#
|
||||
# Loadable module support
|
||||
|
@ -201,52 +58,81 @@ CONFIG_BASE_SMALL=0
|
|||
CONFIG_MODULES=y
|
||||
CONFIG_MODULE_UNLOAD=y
|
||||
# CONFIG_MODULE_FORCE_UNLOAD is not set
|
||||
CONFIG_MODVERSIONS=y
|
||||
CONFIG_MODULE_SRCVERSION_ALL=y
|
||||
# CONFIG_MODVERSIONS is not set
|
||||
# CONFIG_MODULE_SRCVERSION_ALL is not set
|
||||
# CONFIG_KMOD is not set
|
||||
|
||||
#
|
||||
# Block layer
|
||||
#
|
||||
# CONFIG_LBD is not set
|
||||
# CONFIG_BLK_DEV_IO_TRACE is not set
|
||||
# CONFIG_LSF is not set
|
||||
|
||||
#
|
||||
# IO Schedulers
|
||||
#
|
||||
CONFIG_IOSCHED_NOOP=y
|
||||
CONFIG_IOSCHED_AS=y
|
||||
CONFIG_IOSCHED_DEADLINE=y
|
||||
CONFIG_IOSCHED_CFQ=y
|
||||
CONFIG_DEFAULT_AS=y
|
||||
# CONFIG_IOSCHED_AS is not set
|
||||
# CONFIG_IOSCHED_DEADLINE is not set
|
||||
# CONFIG_IOSCHED_CFQ is not set
|
||||
# CONFIG_DEFAULT_AS is not set
|
||||
# CONFIG_DEFAULT_DEADLINE is not set
|
||||
# CONFIG_DEFAULT_CFQ is not set
|
||||
# CONFIG_DEFAULT_NOOP is not set
|
||||
CONFIG_DEFAULT_IOSCHED="anticipatory"
|
||||
CONFIG_DEFAULT_NOOP=y
|
||||
CONFIG_DEFAULT_IOSCHED="noop"
|
||||
|
||||
#
|
||||
# Bus options (PCI, PCMCIA, EISA, ISA, TC)
|
||||
# System Type and features
|
||||
#
|
||||
CONFIG_HW_HAS_PCI=y
|
||||
# CONFIG_PCI is not set
|
||||
CONFIG_SUBARCH_AVR32B=y
|
||||
CONFIG_MMU=y
|
||||
CONFIG_PERFORMANCE_COUNTERS=y
|
||||
CONFIG_PLATFORM_AT32AP=y
|
||||
CONFIG_CPU_AT32AP7000=y
|
||||
CONFIG_BOARD_ATSTK1002=y
|
||||
CONFIG_BOARD_ATSTK1000=y
|
||||
CONFIG_LOADER_U_BOOT=y
|
||||
CONFIG_LOAD_ADDRESS=0x10000000
|
||||
CONFIG_ENTRY_ADDRESS=0x90000000
|
||||
CONFIG_PHYS_OFFSET=0x10000000
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
# CONFIG_PREEMPT_VOLUNTARY is not set
|
||||
# CONFIG_PREEMPT is not set
|
||||
# CONFIG_HAVE_ARCH_BOOTMEM_NODE is not set
|
||||
# CONFIG_ARCH_HAVE_MEMORY_PRESENT is not set
|
||||
# CONFIG_NEED_NODE_MEMMAP_SIZE is not set
|
||||
CONFIG_ARCH_FLATMEM_ENABLE=y
|
||||
# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
|
||||
# CONFIG_ARCH_SPARSEMEM_ENABLE is not set
|
||||
CONFIG_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_FLATMEM_MANUAL=y
|
||||
# CONFIG_DISCONTIGMEM_MANUAL is not set
|
||||
# CONFIG_SPARSEMEM_MANUAL is not set
|
||||
CONFIG_FLATMEM=y
|
||||
CONFIG_FLAT_NODE_MEM_MAP=y
|
||||
# CONFIG_SPARSEMEM_STATIC is not set
|
||||
CONFIG_SPLIT_PTLOCK_CPUS=4
|
||||
# CONFIG_RESOURCES_64BIT is not set
|
||||
# CONFIG_OWNERSHIP_TRACE is not set
|
||||
# CONFIG_HZ_100 is not set
|
||||
CONFIG_HZ_250=y
|
||||
# CONFIG_HZ_1000 is not set
|
||||
CONFIG_HZ=250
|
||||
CONFIG_CMDLINE=""
|
||||
|
||||
#
|
||||
# Bus options
|
||||
#
|
||||
|
||||
#
|
||||
# PCCARD (PCMCIA/CardBus) support
|
||||
#
|
||||
# CONFIG_PCCARD is not set
|
||||
|
||||
#
|
||||
# PCI Hotplug Support
|
||||
#
|
||||
|
||||
#
|
||||
# Executable file formats
|
||||
#
|
||||
CONFIG_BINFMT_ELF=y
|
||||
# CONFIG_BINFMT_MISC is not set
|
||||
CONFIG_TRAD_SIGNALS=y
|
||||
|
||||
#
|
||||
# Networking
|
||||
|
@ -257,18 +143,17 @@ CONFIG_NET=y
|
|||
# Networking options
|
||||
#
|
||||
# CONFIG_NETDEBUG is not set
|
||||
# CONFIG_PACKET is not set
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_PACKET_MMAP=y
|
||||
CONFIG_UNIX=y
|
||||
CONFIG_XFRM=y
|
||||
CONFIG_XFRM_USER=m
|
||||
CONFIG_NET_KEY=y
|
||||
# CONFIG_NET_KEY is not set
|
||||
CONFIG_INET=y
|
||||
# CONFIG_IP_MULTICAST is not set
|
||||
# CONFIG_IP_ADVANCED_ROUTER is not set
|
||||
CONFIG_IP_FIB_HASH=y
|
||||
CONFIG_IP_PNP=y
|
||||
# CONFIG_IP_PNP_DHCP is not set
|
||||
CONFIG_IP_PNP_BOOTP=y
|
||||
CONFIG_IP_PNP_DHCP=y
|
||||
# CONFIG_IP_PNP_BOOTP is not set
|
||||
# CONFIG_IP_PNP_RARP is not set
|
||||
# CONFIG_NET_IPIP is not set
|
||||
# CONFIG_NET_IPGRE is not set
|
||||
|
@ -279,8 +164,8 @@ CONFIG_IP_PNP_BOOTP=y
|
|||
# CONFIG_INET_IPCOMP is not set
|
||||
# CONFIG_INET_XFRM_TUNNEL is not set
|
||||
# CONFIG_INET_TUNNEL is not set
|
||||
CONFIG_INET_XFRM_MODE_TRANSPORT=m
|
||||
CONFIG_INET_XFRM_MODE_TUNNEL=m
|
||||
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
||||
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
||||
CONFIG_INET_DIAG=y
|
||||
CONFIG_INET_TCP_DIAG=y
|
||||
# CONFIG_TCP_CONG_ADVANCED is not set
|
||||
|
@ -288,7 +173,7 @@ CONFIG_TCP_CONG_BIC=y
|
|||
# CONFIG_IPV6 is not set
|
||||
# CONFIG_INET6_XFRM_TUNNEL is not set
|
||||
# CONFIG_INET6_TUNNEL is not set
|
||||
CONFIG_NETWORK_SECMARK=y
|
||||
# CONFIG_NETWORK_SECMARK is not set
|
||||
# CONFIG_NETFILTER is not set
|
||||
|
||||
#
|
||||
|
@ -327,16 +212,11 @@ CONFIG_NETWORK_SECMARK=y
|
|||
# Network testing
|
||||
#
|
||||
# CONFIG_NET_PKTGEN is not set
|
||||
# CONFIG_NET_TCPPROBE is not set
|
||||
# CONFIG_HAMRADIO is not set
|
||||
# CONFIG_IRDA is not set
|
||||
# CONFIG_BT is not set
|
||||
CONFIG_IEEE80211=m
|
||||
# CONFIG_IEEE80211_DEBUG is not set
|
||||
CONFIG_IEEE80211_CRYPT_WEP=m
|
||||
CONFIG_IEEE80211_CRYPT_CCMP=m
|
||||
CONFIG_IEEE80211_SOFTMAC=m
|
||||
# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
|
||||
CONFIG_WIRELESS_EXT=y
|
||||
# CONFIG_IEEE80211 is not set
|
||||
|
||||
#
|
||||
# Device Drivers
|
||||
|
@ -346,14 +226,15 @@ CONFIG_WIRELESS_EXT=y
|
|||
# Generic Driver Options
|
||||
#
|
||||
CONFIG_STANDALONE=y
|
||||
CONFIG_PREVENT_FIRMWARE_BUILD=y
|
||||
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
||||
# CONFIG_FW_LOADER is not set
|
||||
# CONFIG_DEBUG_DRIVER is not set
|
||||
# CONFIG_SYS_HYPERVISOR is not set
|
||||
|
||||
#
|
||||
# Connector - unified userspace <-> kernelspace linker
|
||||
#
|
||||
CONFIG_CONNECTOR=m
|
||||
# CONFIG_CONNECTOR is not set
|
||||
|
||||
#
|
||||
# Memory Technology Devices (MTD)
|
||||
|
@ -373,14 +254,15 @@ CONFIG_CONNECTOR=m
|
|||
# Block devices
|
||||
#
|
||||
# CONFIG_BLK_DEV_COW_COMMON is not set
|
||||
# CONFIG_BLK_DEV_LOOP is not set
|
||||
# CONFIG_BLK_DEV_NBD is not set
|
||||
# CONFIG_BLK_DEV_RAM is not set
|
||||
# CONFIG_BLK_DEV_INITRD is not set
|
||||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_CDROM_PKTCDVD_BUFFERS=8
|
||||
# CONFIG_CDROM_PKTCDVD_WCACHE is not set
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
# CONFIG_BLK_DEV_CRYPTOLOOP is not set
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_BLK_DEV_RAM_COUNT=16
|
||||
CONFIG_BLK_DEV_RAM_SIZE=4096
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
# CONFIG_CDROM_PKTCDVD is not set
|
||||
# CONFIG_ATA_OVER_ETH is not set
|
||||
|
||||
#
|
||||
# ATA/ATAPI/MFM/RLL support
|
||||
|
@ -390,7 +272,7 @@ CONFIG_ATA_OVER_ETH=m
|
|||
#
|
||||
# SCSI device support
|
||||
#
|
||||
CONFIG_RAID_ATTRS=m
|
||||
# CONFIG_RAID_ATTRS is not set
|
||||
# CONFIG_SCSI is not set
|
||||
|
||||
#
|
||||
|
@ -415,34 +297,22 @@ CONFIG_RAID_ATTRS=m
|
|||
# Network device support
|
||||
#
|
||||
CONFIG_NETDEVICES=y
|
||||
# CONFIG_DUMMY is not set
|
||||
CONFIG_DUMMY=y
|
||||
# CONFIG_BONDING is not set
|
||||
# CONFIG_EQUALIZER is not set
|
||||
# CONFIG_TUN is not set
|
||||
CONFIG_TUN=m
|
||||
|
||||
#
|
||||
# PHY device support
|
||||
#
|
||||
CONFIG_PHYLIB=m
|
||||
|
||||
#
|
||||
# MII PHY device drivers
|
||||
#
|
||||
CONFIG_MARVELL_PHY=m
|
||||
CONFIG_DAVICOM_PHY=m
|
||||
CONFIG_QSEMI_PHY=m
|
||||
CONFIG_LXT_PHY=m
|
||||
CONFIG_CICADA_PHY=m
|
||||
CONFIG_VITESSE_PHY=m
|
||||
CONFIG_SMSC_PHY=m
|
||||
# CONFIG_PHYLIB is not set
|
||||
|
||||
#
|
||||
# Ethernet (10 or 100Mbit)
|
||||
#
|
||||
CONFIG_NET_ETHERNET=y
|
||||
# CONFIG_MII is not set
|
||||
CONFIG_MIPS_GT96100ETH=y
|
||||
# CONFIG_DM9000 is not set
|
||||
CONFIG_MII=y
|
||||
CONFIG_MACB=y
|
||||
|
||||
#
|
||||
# Ethernet (1000 Mbit)
|
||||
|
@ -465,7 +335,15 @@ CONFIG_MIPS_GT96100ETH=y
|
|||
# Wan interfaces
|
||||
#
|
||||
# CONFIG_WAN is not set
|
||||
# CONFIG_PPP is not set
|
||||
CONFIG_PPP=m
|
||||
# CONFIG_PPP_MULTILINK is not set
|
||||
# CONFIG_PPP_FILTER is not set
|
||||
CONFIG_PPP_ASYNC=m
|
||||
# CONFIG_PPP_SYNC_TTY is not set
|
||||
CONFIG_PPP_DEFLATE=m
|
||||
# CONFIG_PPP_BSDCOMP is not set
|
||||
# CONFIG_PPP_MPPE is not set
|
||||
# CONFIG_PPPOE is not set
|
||||
# CONFIG_SLIP is not set
|
||||
# CONFIG_SHAPER is not set
|
||||
# CONFIG_NETCONSOLE is not set
|
||||
|
@ -485,65 +363,35 @@ CONFIG_MIPS_GT96100ETH=y
|
|||
#
|
||||
# Input device support
|
||||
#
|
||||
CONFIG_INPUT=y
|
||||
|
||||
#
|
||||
# Userland interfaces
|
||||
#
|
||||
CONFIG_INPUT_MOUSEDEV=y
|
||||
CONFIG_INPUT_MOUSEDEV_PSAUX=y
|
||||
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
|
||||
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
|
||||
# CONFIG_INPUT_JOYDEV is not set
|
||||
# CONFIG_INPUT_TSDEV is not set
|
||||
# CONFIG_INPUT_EVDEV is not set
|
||||
# CONFIG_INPUT_EVBUG is not set
|
||||
|
||||
#
|
||||
# Input Device Drivers
|
||||
#
|
||||
# CONFIG_INPUT_KEYBOARD is not set
|
||||
# CONFIG_INPUT_MOUSE is not set
|
||||
# CONFIG_INPUT_JOYSTICK is not set
|
||||
# CONFIG_INPUT_TOUCHSCREEN is not set
|
||||
# CONFIG_INPUT_MISC is not set
|
||||
# CONFIG_INPUT is not set
|
||||
|
||||
#
|
||||
# Hardware I/O ports
|
||||
#
|
||||
CONFIG_SERIO=y
|
||||
# CONFIG_SERIO_I8042 is not set
|
||||
CONFIG_SERIO_SERPORT=y
|
||||
# CONFIG_SERIO_LIBPS2 is not set
|
||||
CONFIG_SERIO_RAW=m
|
||||
# CONFIG_SERIO is not set
|
||||
# CONFIG_GAMEPORT is not set
|
||||
|
||||
#
|
||||
# Character devices
|
||||
#
|
||||
CONFIG_VT=y
|
||||
CONFIG_VT_CONSOLE=y
|
||||
CONFIG_HW_CONSOLE=y
|
||||
CONFIG_VT_HW_CONSOLE_BINDING=y
|
||||
# CONFIG_VT is not set
|
||||
# CONFIG_SERIAL_NONSTANDARD is not set
|
||||
|
||||
#
|
||||
# Serial drivers
|
||||
#
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_SERIAL_8250_NR_UARTS=4
|
||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
|
||||
# CONFIG_SERIAL_8250_EXTENDED is not set
|
||||
# CONFIG_SERIAL_8250 is not set
|
||||
|
||||
#
|
||||
# Non-8250 serial port support
|
||||
#
|
||||
CONFIG_SERIAL_AT91=y
|
||||
CONFIG_SERIAL_AT91_CONSOLE=y
|
||||
# CONFIG_SERIAL_AT91_TTYAT is not set
|
||||
CONFIG_SERIAL_CORE=y
|
||||
CONFIG_SERIAL_CORE_CONSOLE=y
|
||||
CONFIG_UNIX98_PTYS=y
|
||||
CONFIG_LEGACY_PTYS=y
|
||||
CONFIG_LEGACY_PTY_COUNT=256
|
||||
# CONFIG_LEGACY_PTYS is not set
|
||||
|
||||
#
|
||||
# IPMI
|
||||
|
@ -579,13 +427,23 @@ CONFIG_LEGACY_PTY_COUNT=256
|
|||
#
|
||||
# SPI support
|
||||
#
|
||||
# CONFIG_SPI is not set
|
||||
# CONFIG_SPI_MASTER is not set
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_DEBUG is not set
|
||||
CONFIG_SPI_MASTER=y
|
||||
|
||||
#
|
||||
# SPI Master Controller Drivers
|
||||
#
|
||||
CONFIG_SPI_ATMEL=m
|
||||
# CONFIG_SPI_BITBANG is not set
|
||||
|
||||
#
|
||||
# SPI Protocol Masters
|
||||
#
|
||||
|
||||
#
|
||||
# Dallas's 1-wire bus
|
||||
#
|
||||
# CONFIG_W1 is not set
|
||||
|
||||
#
|
||||
# Hardware Monitoring support
|
||||
|
@ -612,13 +470,28 @@ CONFIG_VIDEO_V4L2=y
|
|||
# Graphics support
|
||||
#
|
||||
# CONFIG_FIRMWARE_EDID is not set
|
||||
# CONFIG_FB is not set
|
||||
CONFIG_FB=m
|
||||
CONFIG_FB_CFB_FILLRECT=m
|
||||
CONFIG_FB_CFB_COPYAREA=m
|
||||
CONFIG_FB_CFB_IMAGEBLIT=m
|
||||
# CONFIG_FB_MACMODES is not set
|
||||
# CONFIG_FB_BACKLIGHT is not set
|
||||
# CONFIG_FB_MODE_HELPERS is not set
|
||||
# CONFIG_FB_TILEBLITTING is not set
|
||||
CONFIG_FB_SIDSA=m
|
||||
CONFIG_FB_SIDSA_DEFAULT_BPP=24
|
||||
# CONFIG_FB_S1D13XXX is not set
|
||||
# CONFIG_FB_VIRTUAL is not set
|
||||
|
||||
#
|
||||
# Console display driver support
|
||||
# Logo configuration
|
||||
#
|
||||
# CONFIG_VGA_CONSOLE is not set
|
||||
CONFIG_DUMMY_CONSOLE=y
|
||||
# CONFIG_LOGO is not set
|
||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
|
||||
CONFIG_LCD_CLASS_DEVICE=m
|
||||
CONFIG_LCD_DEVICE=y
|
||||
CONFIG_LCD_LTV350QV=m
|
||||
|
||||
#
|
||||
# Sound
|
||||
|
@ -697,15 +570,14 @@ CONFIG_EXT2_FS=y
|
|||
# CONFIG_FS_POSIX_ACL is not set
|
||||
# CONFIG_XFS_FS is not set
|
||||
# CONFIG_OCFS2_FS is not set
|
||||
# CONFIG_MINIX_FS is not set
|
||||
# CONFIG_ROMFS_FS is not set
|
||||
CONFIG_INOTIFY=y
|
||||
CONFIG_INOTIFY_USER=y
|
||||
CONFIG_MINIX_FS=m
|
||||
CONFIG_ROMFS_FS=m
|
||||
# CONFIG_INOTIFY is not set
|
||||
# CONFIG_QUOTA is not set
|
||||
CONFIG_DNOTIFY=y
|
||||
# CONFIG_DNOTIFY is not set
|
||||
# CONFIG_AUTOFS_FS is not set
|
||||
# CONFIG_AUTOFS4_FS is not set
|
||||
CONFIG_FUSE_FS=m
|
||||
# CONFIG_FUSE_FS is not set
|
||||
|
||||
#
|
||||
# CD-ROM/DVD Filesystems
|
||||
|
@ -716,8 +588,11 @@ CONFIG_FUSE_FS=m
|
|||
#
|
||||
# DOS/FAT/NT Filesystems
|
||||
#
|
||||
# CONFIG_MSDOS_FS is not set
|
||||
# CONFIG_VFAT_FS is not set
|
||||
CONFIG_FAT_FS=m
|
||||
CONFIG_MSDOS_FS=m
|
||||
CONFIG_VFAT_FS=m
|
||||
CONFIG_FAT_DEFAULT_CODEPAGE=437
|
||||
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
|
||||
# CONFIG_NTFS_FS is not set
|
||||
|
||||
#
|
||||
|
@ -726,10 +601,10 @@ CONFIG_FUSE_FS=m
|
|||
CONFIG_PROC_FS=y
|
||||
CONFIG_PROC_KCORE=y
|
||||
CONFIG_SYSFS=y
|
||||
# CONFIG_TMPFS is not set
|
||||
CONFIG_TMPFS=y
|
||||
# CONFIG_HUGETLB_PAGE is not set
|
||||
CONFIG_RAMFS=y
|
||||
# CONFIG_CONFIGFS_FS is not set
|
||||
CONFIG_CONFIGFS_FS=m
|
||||
|
||||
#
|
||||
# Miscellaneous filesystems
|
||||
|
@ -752,19 +627,25 @@ CONFIG_RAMFS=y
|
|||
# Network File Systems
|
||||
#
|
||||
CONFIG_NFS_FS=y
|
||||
# CONFIG_NFS_V3 is not set
|
||||
CONFIG_NFS_V3=y
|
||||
# CONFIG_NFS_V3_ACL is not set
|
||||
# CONFIG_NFS_V4 is not set
|
||||
# CONFIG_NFS_DIRECTIO is not set
|
||||
# CONFIG_NFSD is not set
|
||||
CONFIG_ROOT_NFS=y
|
||||
CONFIG_LOCKD=y
|
||||
CONFIG_LOCKD_V4=y
|
||||
CONFIG_NFS_COMMON=y
|
||||
CONFIG_SUNRPC=y
|
||||
# CONFIG_RPCSEC_GSS_KRB5 is not set
|
||||
# CONFIG_RPCSEC_GSS_SPKM3 is not set
|
||||
# CONFIG_SMB_FS is not set
|
||||
# CONFIG_CIFS is not set
|
||||
CONFIG_CIFS=m
|
||||
# CONFIG_CIFS_STATS is not set
|
||||
# CONFIG_CIFS_WEAK_PW_HASH is not set
|
||||
# CONFIG_CIFS_XATTR is not set
|
||||
# CONFIG_CIFS_DEBUG2 is not set
|
||||
# CONFIG_CIFS_EXPERIMENTAL is not set
|
||||
# CONFIG_NCP_FS is not set
|
||||
# CONFIG_CODA_FS is not set
|
||||
# CONFIG_AFS_FS is not set
|
||||
|
@ -779,60 +660,84 @@ CONFIG_MSDOS_PARTITION=y
|
|||
#
|
||||
# Native Language Support
|
||||
#
|
||||
# CONFIG_NLS is not set
|
||||
|
||||
#
|
||||
# Profiling support
|
||||
#
|
||||
# CONFIG_PROFILING is not set
|
||||
CONFIG_NLS=m
|
||||
CONFIG_NLS_DEFAULT="iso8859-1"
|
||||
CONFIG_NLS_CODEPAGE_437=m
|
||||
# CONFIG_NLS_CODEPAGE_737 is not set
|
||||
# CONFIG_NLS_CODEPAGE_775 is not set
|
||||
CONFIG_NLS_CODEPAGE_850=m
|
||||
# CONFIG_NLS_CODEPAGE_852 is not set
|
||||
# CONFIG_NLS_CODEPAGE_855 is not set
|
||||
# CONFIG_NLS_CODEPAGE_857 is not set
|
||||
# CONFIG_NLS_CODEPAGE_860 is not set
|
||||
# CONFIG_NLS_CODEPAGE_861 is not set
|
||||
# CONFIG_NLS_CODEPAGE_862 is not set
|
||||
# CONFIG_NLS_CODEPAGE_863 is not set
|
||||
# CONFIG_NLS_CODEPAGE_864 is not set
|
||||
# CONFIG_NLS_CODEPAGE_865 is not set
|
||||
# CONFIG_NLS_CODEPAGE_866 is not set
|
||||
# CONFIG_NLS_CODEPAGE_869 is not set
|
||||
# CONFIG_NLS_CODEPAGE_936 is not set
|
||||
# CONFIG_NLS_CODEPAGE_950 is not set
|
||||
# CONFIG_NLS_CODEPAGE_932 is not set
|
||||
# CONFIG_NLS_CODEPAGE_949 is not set
|
||||
# CONFIG_NLS_CODEPAGE_874 is not set
|
||||
# CONFIG_NLS_ISO8859_8 is not set
|
||||
# CONFIG_NLS_CODEPAGE_1250 is not set
|
||||
# CONFIG_NLS_CODEPAGE_1251 is not set
|
||||
# CONFIG_NLS_ASCII is not set
|
||||
CONFIG_NLS_ISO8859_1=m
|
||||
# CONFIG_NLS_ISO8859_2 is not set
|
||||
# CONFIG_NLS_ISO8859_3 is not set
|
||||
# CONFIG_NLS_ISO8859_4 is not set
|
||||
# CONFIG_NLS_ISO8859_5 is not set
|
||||
# CONFIG_NLS_ISO8859_6 is not set
|
||||
# CONFIG_NLS_ISO8859_7 is not set
|
||||
# CONFIG_NLS_ISO8859_9 is not set
|
||||
# CONFIG_NLS_ISO8859_13 is not set
|
||||
# CONFIG_NLS_ISO8859_14 is not set
|
||||
# CONFIG_NLS_ISO8859_15 is not set
|
||||
# CONFIG_NLS_KOI8_R is not set
|
||||
# CONFIG_NLS_KOI8_U is not set
|
||||
CONFIG_NLS_UTF8=m
|
||||
|
||||
#
|
||||
# Kernel hacking
|
||||
#
|
||||
# CONFIG_PRINTK_TIME is not set
|
||||
# CONFIG_MAGIC_SYSRQ is not set
|
||||
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
# CONFIG_UNUSED_SYMBOLS is not set
|
||||
# CONFIG_DEBUG_KERNEL is not set
|
||||
CONFIG_DEBUG_KERNEL=y
|
||||
CONFIG_LOG_BUF_SHIFT=14
|
||||
# CONFIG_DEBUG_FS is not set
|
||||
CONFIG_CROSSCOMPILE=y
|
||||
CONFIG_CMDLINE=""
|
||||
CONFIG_DETECT_SOFTLOCKUP=y
|
||||
# CONFIG_SCHEDSTATS is not set
|
||||
# CONFIG_DEBUG_SPINLOCK is not set
|
||||
# CONFIG_DEBUG_MUTEXES is not set
|
||||
# CONFIG_DEBUG_RWSEMS is not set
|
||||
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
|
||||
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
|
||||
# CONFIG_DEBUG_KOBJECT is not set
|
||||
CONFIG_DEBUG_BUGVERBOSE=y
|
||||
# CONFIG_DEBUG_INFO is not set
|
||||
CONFIG_DEBUG_FS=y
|
||||
# CONFIG_DEBUG_VM is not set
|
||||
CONFIG_FRAME_POINTER=y
|
||||
# CONFIG_UNWIND_INFO is not set
|
||||
CONFIG_FORCED_INLINING=y
|
||||
# CONFIG_RCU_TORTURE_TEST is not set
|
||||
CONFIG_KPROBES=y
|
||||
|
||||
#
|
||||
# Security options
|
||||
#
|
||||
CONFIG_KEYS=y
|
||||
CONFIG_KEYS_DEBUG_PROC_KEYS=y
|
||||
# CONFIG_KEYS is not set
|
||||
# CONFIG_SECURITY is not set
|
||||
|
||||
#
|
||||
# Cryptographic options
|
||||
#
|
||||
CONFIG_CRYPTO=y
|
||||
CONFIG_CRYPTO_HMAC=y
|
||||
CONFIG_CRYPTO_NULL=m
|
||||
CONFIG_CRYPTO_MD4=m
|
||||
CONFIG_CRYPTO_MD5=m
|
||||
CONFIG_CRYPTO_SHA1=m
|
||||
CONFIG_CRYPTO_SHA256=m
|
||||
CONFIG_CRYPTO_SHA512=m
|
||||
CONFIG_CRYPTO_WP512=m
|
||||
CONFIG_CRYPTO_TGR192=m
|
||||
CONFIG_CRYPTO_DES=m
|
||||
CONFIG_CRYPTO_BLOWFISH=m
|
||||
CONFIG_CRYPTO_TWOFISH=m
|
||||
CONFIG_CRYPTO_SERPENT=m
|
||||
CONFIG_CRYPTO_AES=m
|
||||
CONFIG_CRYPTO_CAST5=m
|
||||
CONFIG_CRYPTO_CAST6=m
|
||||
CONFIG_CRYPTO_TEA=m
|
||||
CONFIG_CRYPTO_ARC4=m
|
||||
CONFIG_CRYPTO_KHAZAD=m
|
||||
CONFIG_CRYPTO_ANUBIS=m
|
||||
CONFIG_CRYPTO_DEFLATE=m
|
||||
CONFIG_CRYPTO_MICHAEL_MIC=m
|
||||
CONFIG_CRYPTO_CRC32C=m
|
||||
# CONFIG_CRYPTO_TEST is not set
|
||||
# CONFIG_CRYPTO is not set
|
||||
|
||||
#
|
||||
# Hardware crypto devices
|
||||
|
@ -841,10 +746,9 @@ CONFIG_CRYPTO_CRC32C=m
|
|||
#
|
||||
# Library routines
|
||||
#
|
||||
# CONFIG_CRC_CCITT is not set
|
||||
CONFIG_CRC16=m
|
||||
CONFIG_CRC_CCITT=m
|
||||
# CONFIG_CRC16 is not set
|
||||
CONFIG_CRC32=m
|
||||
CONFIG_LIBCRC32C=m
|
||||
# CONFIG_LIBCRC32C is not set
|
||||
CONFIG_ZLIB_INFLATE=m
|
||||
CONFIG_ZLIB_DEFLATE=m
|
||||
CONFIG_PLIST=y
|
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Makefile for the Linux/AVR32 kernel.
|
||||
#
|
||||
|
||||
extra-y := head.o vmlinux.lds
|
||||
|
||||
obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o
|
||||
obj-y += syscall_table.o syscall-stubs.o irq.o
|
||||
obj-y += setup.o traps.o semaphore.o ptrace.o
|
||||
obj-y += signal.o sys_avr32.o process.o time.o
|
||||
obj-y += init_task.o switch_to.o cpu.o
|
||||
obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
||||
USE_STANDARD_AS_RULE := true
|
||||
|
||||
%.lds: %.lds.c FORCE
|
||||
$(call if_changed_dep,cpp_lds_S)
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Generate definitions needed by assembly language modules.
|
||||
* This code generates raw asm output which is post-processed
|
||||
* to extract and format the required data.
|
||||
*/
|
||||
|
||||
#include <linux/thread_info.h>
|
||||
|
||||
#define DEFINE(sym, val) \
|
||||
asm volatile("\n->" #sym " %0 " #val : : "i" (val))
|
||||
|
||||
#define BLANK() asm volatile("\n->" : : )
|
||||
|
||||
#define OFFSET(sym, str, mem) \
|
||||
DEFINE(sym, offsetof(struct str, mem));
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
OFFSET(TI_task, thread_info, task);
|
||||
OFFSET(TI_exec_domain, thread_info, exec_domain);
|
||||
OFFSET(TI_flags, thread_info, flags);
|
||||
OFFSET(TI_cpu, thread_info, cpu);
|
||||
OFFSET(TI_preempt_count, thread_info, preempt_count);
|
||||
OFFSET(TI_restart_block, thread_info, restart_block);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Export AVR32-specific functions for loadable modules.
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/checksum.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/delay.h>
|
||||
|
||||
/*
|
||||
* GCC functions
|
||||
*/
|
||||
extern unsigned long long __avr32_lsl64(unsigned long long u, unsigned long b);
|
||||
extern unsigned long long __avr32_lsr64(unsigned long long u, unsigned long b);
|
||||
extern unsigned long long __avr32_asr64(unsigned long long u, unsigned long b);
|
||||
EXPORT_SYMBOL(__avr32_lsl64);
|
||||
EXPORT_SYMBOL(__avr32_lsr64);
|
||||
EXPORT_SYMBOL(__avr32_asr64);
|
||||
|
||||
/*
|
||||
* String functions
|
||||
*/
|
||||
EXPORT_SYMBOL(memset);
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
|
||||
/*
|
||||
* Userspace access stuff.
|
||||
*/
|
||||
EXPORT_SYMBOL(copy_from_user);
|
||||
EXPORT_SYMBOL(copy_to_user);
|
||||
EXPORT_SYMBOL(__copy_user);
|
||||
EXPORT_SYMBOL(strncpy_from_user);
|
||||
EXPORT_SYMBOL(__strncpy_from_user);
|
||||
EXPORT_SYMBOL(clear_user);
|
||||
EXPORT_SYMBOL(__clear_user);
|
||||
EXPORT_SYMBOL(csum_partial);
|
||||
EXPORT_SYMBOL(csum_partial_copy_generic);
|
||||
|
||||
/* Delay loops (lib/delay.S) */
|
||||
EXPORT_SYMBOL(__ndelay);
|
||||
EXPORT_SYMBOL(__udelay);
|
||||
EXPORT_SYMBOL(__const_udelay);
|
||||
|
||||
/* Bit operations (lib/findbit.S) */
|
||||
EXPORT_SYMBOL(find_first_zero_bit);
|
||||
EXPORT_SYMBOL(find_next_zero_bit);
|
||||
EXPORT_SYMBOL(find_first_bit);
|
||||
EXPORT_SYMBOL(find_next_bit);
|
||||
EXPORT_SYMBOL(generic_find_next_zero_le_bit);
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
static DEFINE_PER_CPU(struct cpu, cpu_devices);
|
||||
|
||||
#ifdef CONFIG_PERFORMANCE_COUNTERS
|
||||
|
||||
/*
|
||||
* XXX: If/when a SMP-capable implementation of AVR32 will ever be
|
||||
* made, we must make sure that the code executes on the correct CPU.
|
||||
*/
|
||||
static ssize_t show_pc0event(struct sys_device *dev, char *buf)
|
||||
{
|
||||
unsigned long pccr;
|
||||
|
||||
pccr = sysreg_read(PCCR);
|
||||
return sprintf(buf, "0x%lx\n", (pccr >> 12) & 0x3f);
|
||||
}
|
||||
static ssize_t store_pc0event(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
char *endp;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf || val > 0x3f)
|
||||
return -EINVAL;
|
||||
val = (val << 12) | (sysreg_read(PCCR) & 0xfffc0fff);
|
||||
sysreg_write(PCCR, val);
|
||||
return count;
|
||||
}
|
||||
static ssize_t show_pc0count(struct sys_device *dev, char *buf)
|
||||
{
|
||||
unsigned long pcnt0;
|
||||
|
||||
pcnt0 = sysreg_read(PCNT0);
|
||||
return sprintf(buf, "%lu\n", pcnt0);
|
||||
}
|
||||
static ssize_t store_pc0count(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
char *endp;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf)
|
||||
return -EINVAL;
|
||||
sysreg_write(PCNT0, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pc1event(struct sys_device *dev, char *buf)
|
||||
{
|
||||
unsigned long pccr;
|
||||
|
||||
pccr = sysreg_read(PCCR);
|
||||
return sprintf(buf, "0x%lx\n", (pccr >> 18) & 0x3f);
|
||||
}
|
||||
static ssize_t store_pc1event(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
char *endp;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf || val > 0x3f)
|
||||
return -EINVAL;
|
||||
val = (val << 18) | (sysreg_read(PCCR) & 0xff03ffff);
|
||||
sysreg_write(PCCR, val);
|
||||
return count;
|
||||
}
|
||||
static ssize_t show_pc1count(struct sys_device *dev, char *buf)
|
||||
{
|
||||
unsigned long pcnt1;
|
||||
|
||||
pcnt1 = sysreg_read(PCNT1);
|
||||
return sprintf(buf, "%lu\n", pcnt1);
|
||||
}
|
||||
static ssize_t store_pc1count(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
char *endp;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf)
|
||||
return -EINVAL;
|
||||
sysreg_write(PCNT1, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pccycles(struct sys_device *dev, char *buf)
|
||||
{
|
||||
unsigned long pccnt;
|
||||
|
||||
pccnt = sysreg_read(PCCNT);
|
||||
return sprintf(buf, "%lu\n", pccnt);
|
||||
}
|
||||
static ssize_t store_pccycles(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
char *endp;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf)
|
||||
return -EINVAL;
|
||||
sysreg_write(PCCNT, val);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pcenable(struct sys_device *dev, char *buf)
|
||||
{
|
||||
unsigned long pccr;
|
||||
|
||||
pccr = sysreg_read(PCCR);
|
||||
return sprintf(buf, "%c\n", (pccr & 1)?'1':'0');
|
||||
}
|
||||
static ssize_t store_pcenable(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long pccr, val;
|
||||
char *endp;
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf)
|
||||
return -EINVAL;
|
||||
if (val)
|
||||
val = 1;
|
||||
|
||||
pccr = sysreg_read(PCCR);
|
||||
pccr = (pccr & ~1UL) | val;
|
||||
sysreg_write(PCCR, pccr);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static SYSDEV_ATTR(pc0event, 0600, show_pc0event, store_pc0event);
|
||||
static SYSDEV_ATTR(pc0count, 0600, show_pc0count, store_pc0count);
|
||||
static SYSDEV_ATTR(pc1event, 0600, show_pc1event, store_pc1event);
|
||||
static SYSDEV_ATTR(pc1count, 0600, show_pc1count, store_pc1count);
|
||||
static SYSDEV_ATTR(pccycles, 0600, show_pccycles, store_pccycles);
|
||||
static SYSDEV_ATTR(pcenable, 0600, show_pcenable, store_pcenable);
|
||||
|
||||
#endif /* CONFIG_PERFORMANCE_COUNTERS */
|
||||
|
||||
static int __init topology_init(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct cpu *c = &per_cpu(cpu_devices, cpu);
|
||||
|
||||
register_cpu(c, cpu);
|
||||
|
||||
#ifdef CONFIG_PERFORMANCE_COUNTERS
|
||||
sysdev_create_file(&c->sysdev, &attr_pc0event);
|
||||
sysdev_create_file(&c->sysdev, &attr_pc0count);
|
||||
sysdev_create_file(&c->sysdev, &attr_pc1event);
|
||||
sysdev_create_file(&c->sysdev, &attr_pc1count);
|
||||
sysdev_create_file(&c->sysdev, &attr_pccycles);
|
||||
sysdev_create_file(&c->sysdev, &attr_pcenable);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsys_initcall(topology_init);
|
||||
|
||||
static const char *cpu_names[] = {
|
||||
"Morgan",
|
||||
"AP7000",
|
||||
};
|
||||
#define NR_CPU_NAMES ARRAY_SIZE(cpu_names)
|
||||
|
||||
static const char *arch_names[] = {
|
||||
"AVR32A",
|
||||
"AVR32B",
|
||||
};
|
||||
#define NR_ARCH_NAMES ARRAY_SIZE(arch_names)
|
||||
|
||||
static const char *mmu_types[] = {
|
||||
"No MMU",
|
||||
"ITLB and DTLB",
|
||||
"Shared TLB",
|
||||
"MPU"
|
||||
};
|
||||
|
||||
void __init setup_processor(void)
|
||||
{
|
||||
unsigned long config0, config1;
|
||||
unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type;
|
||||
unsigned tmp;
|
||||
|
||||
config0 = sysreg_read(CONFIG0); /* 0x0000013e; */
|
||||
config1 = sysreg_read(CONFIG1); /* 0x01f689a2; */
|
||||
cpu_id = config0 >> 24;
|
||||
cpu_rev = (config0 >> 16) & 0xff;
|
||||
arch_id = (config0 >> 13) & 0x07;
|
||||
arch_rev = (config0 >> 10) & 0x07;
|
||||
mmu_type = (config0 >> 7) & 0x03;
|
||||
|
||||
boot_cpu_data.arch_type = arch_id;
|
||||
boot_cpu_data.cpu_type = cpu_id;
|
||||
boot_cpu_data.arch_revision = arch_rev;
|
||||
boot_cpu_data.cpu_revision = cpu_rev;
|
||||
boot_cpu_data.tlb_config = mmu_type;
|
||||
|
||||
tmp = (config1 >> 13) & 0x07;
|
||||
if (tmp) {
|
||||
boot_cpu_data.icache.ways = 1 << ((config1 >> 10) & 0x07);
|
||||
boot_cpu_data.icache.sets = 1 << ((config1 >> 16) & 0x0f);
|
||||
boot_cpu_data.icache.linesz = 1 << (tmp + 1);
|
||||
}
|
||||
tmp = (config1 >> 3) & 0x07;
|
||||
if (tmp) {
|
||||
boot_cpu_data.dcache.ways = 1 << (config1 & 0x07);
|
||||
boot_cpu_data.dcache.sets = 1 << ((config1 >> 6) & 0x0f);
|
||||
boot_cpu_data.dcache.linesz = 1 << (tmp + 1);
|
||||
}
|
||||
|
||||
if ((cpu_id >= NR_CPU_NAMES) || (arch_id >= NR_ARCH_NAMES)) {
|
||||
printk ("Unknown CPU configuration (ID %02x, arch %02x), "
|
||||
"continuing anyway...\n",
|
||||
cpu_id, arch_id);
|
||||
return;
|
||||
}
|
||||
|
||||
printk ("CPU: %s [%02x] revision %d (%s revision %d)\n",
|
||||
cpu_names[cpu_id], cpu_id, cpu_rev,
|
||||
arch_names[arch_id], arch_rev);
|
||||
printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]);
|
||||
printk ("CPU: features:");
|
||||
if (config0 & (1 << 6))
|
||||
printk(" fpu");
|
||||
if (config0 & (1 << 5))
|
||||
printk(" java");
|
||||
if (config0 & (1 << 4))
|
||||
printk(" perfctr");
|
||||
if (config0 & (1 << 3))
|
||||
printk(" ocd");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int c_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned int icache_size, dcache_size;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
icache_size = boot_cpu_data.icache.ways *
|
||||
boot_cpu_data.icache.sets *
|
||||
boot_cpu_data.icache.linesz;
|
||||
dcache_size = boot_cpu_data.dcache.ways *
|
||||
boot_cpu_data.dcache.sets *
|
||||
boot_cpu_data.dcache.linesz;
|
||||
|
||||
seq_printf(m, "processor\t: %d\n", cpu);
|
||||
|
||||
if (boot_cpu_data.arch_type < NR_ARCH_NAMES)
|
||||
seq_printf(m, "cpu family\t: %s revision %d\n",
|
||||
arch_names[boot_cpu_data.arch_type],
|
||||
boot_cpu_data.arch_revision);
|
||||
if (boot_cpu_data.cpu_type < NR_CPU_NAMES)
|
||||
seq_printf(m, "cpu type\t: %s revision %d\n",
|
||||
cpu_names[boot_cpu_data.cpu_type],
|
||||
boot_cpu_data.cpu_revision);
|
||||
|
||||
seq_printf(m, "i-cache\t\t: %dK (%u ways x %u sets x %u)\n",
|
||||
icache_size >> 10,
|
||||
boot_cpu_data.icache.ways,
|
||||
boot_cpu_data.icache.sets,
|
||||
boot_cpu_data.icache.linesz);
|
||||
seq_printf(m, "d-cache\t\t: %dK (%u ways x %u sets x %u)\n",
|
||||
dcache_size >> 10,
|
||||
boot_cpu_data.dcache.ways,
|
||||
boot_cpu_data.dcache.sets,
|
||||
boot_cpu_data.dcache.linesz);
|
||||
seq_printf(m, "bogomips\t: %lu.%02lu\n",
|
||||
boot_cpu_data.loops_per_jiffy / (500000/HZ),
|
||||
(boot_cpu_data.loops_per_jiffy / (5000/HZ)) % 100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return *pos < 1 ? (void *)1 : NULL;
|
||||
}
|
||||
|
||||
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void c_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct seq_operations cpuinfo_op = {
|
||||
.start = c_start,
|
||||
.next = c_next,
|
||||
.stop = c_stop,
|
||||
.show = c_show
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains the low-level entry-points into the kernel, that is,
|
||||
* exception handlers, debug trap handlers, interrupt handlers and the
|
||||
* system call handler.
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/asm.h>
|
||||
#include <asm/hardirq.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/ocd.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
# define preempt_stop mask_interrupts
|
||||
#else
|
||||
# define preempt_stop
|
||||
# define fault_resume_kernel fault_restore_all
|
||||
#endif
|
||||
|
||||
#define __MASK(x) ((1 << (x)) - 1)
|
||||
#define IRQ_MASK ((__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) | \
|
||||
(__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT))
|
||||
|
||||
.section .ex.text,"ax",@progbits
|
||||
.align 2
|
||||
exception_vectors:
|
||||
bral handle_critical
|
||||
.align 2
|
||||
bral handle_critical
|
||||
.align 2
|
||||
bral do_bus_error_write
|
||||
.align 2
|
||||
bral do_bus_error_read
|
||||
.align 2
|
||||
bral do_nmi_ll
|
||||
.align 2
|
||||
bral handle_address_fault
|
||||
.align 2
|
||||
bral handle_protection_fault
|
||||
.align 2
|
||||
bral handle_debug
|
||||
.align 2
|
||||
bral do_illegal_opcode_ll
|
||||
.align 2
|
||||
bral do_illegal_opcode_ll
|
||||
.align 2
|
||||
bral do_illegal_opcode_ll
|
||||
.align 2
|
||||
bral do_fpe_ll
|
||||
.align 2
|
||||
bral do_illegal_opcode_ll
|
||||
.align 2
|
||||
bral handle_address_fault
|
||||
.align 2
|
||||
bral handle_address_fault
|
||||
.align 2
|
||||
bral handle_protection_fault
|
||||
.align 2
|
||||
bral handle_protection_fault
|
||||
.align 2
|
||||
bral do_dtlb_modified
|
||||
|
||||
/*
|
||||
* r0 : PGD/PT/PTE
|
||||
* r1 : Offending address
|
||||
* r2 : Scratch register
|
||||
* r3 : Cause (5, 12 or 13)
|
||||
*/
|
||||
#define tlbmiss_save pushm r0-r3
|
||||
#define tlbmiss_restore popm r0-r3
|
||||
|
||||
.section .tlbx.ex.text,"ax",@progbits
|
||||
.global itlb_miss
|
||||
itlb_miss:
|
||||
tlbmiss_save
|
||||
rjmp tlb_miss_common
|
||||
|
||||
.section .tlbr.ex.text,"ax",@progbits
|
||||
dtlb_miss_read:
|
||||
tlbmiss_save
|
||||
rjmp tlb_miss_common
|
||||
|
||||
.section .tlbw.ex.text,"ax",@progbits
|
||||
dtlb_miss_write:
|
||||
tlbmiss_save
|
||||
|
||||
.global tlb_miss_common
|
||||
tlb_miss_common:
|
||||
mfsr r0, SYSREG_PTBR
|
||||
mfsr r1, SYSREG_TLBEAR
|
||||
|
||||
/* Is it the vmalloc space? */
|
||||
bld r1, 31
|
||||
brcs handle_vmalloc_miss
|
||||
|
||||
/* First level lookup */
|
||||
pgtbl_lookup:
|
||||
lsr r2, r1, PGDIR_SHIFT
|
||||
ld.w r0, r0[r2 << 2]
|
||||
bld r0, _PAGE_BIT_PRESENT
|
||||
brcc page_table_not_present
|
||||
|
||||
/* TODO: Check access rights on page table if necessary */
|
||||
|
||||
/* Translate to virtual address in P1. */
|
||||
andl r0, 0xf000
|
||||
sbr r0, 31
|
||||
|
||||
/* Second level lookup */
|
||||
lsl r1, (32 - PGDIR_SHIFT)
|
||||
lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
|
||||
add r2, r0, r1 << 2
|
||||
ld.w r1, r2[0]
|
||||
bld r1, _PAGE_BIT_PRESENT
|
||||
brcc page_not_present
|
||||
|
||||
/* Mark the page as accessed */
|
||||
sbr r1, _PAGE_BIT_ACCESSED
|
||||
st.w r2[0], r1
|
||||
|
||||
/* Drop software flags */
|
||||
andl r1, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
|
||||
mtsr SYSREG_TLBELO, r1
|
||||
|
||||
/* Figure out which entry we want to replace */
|
||||
mfsr r0, SYSREG_TLBARLO
|
||||
clz r2, r0
|
||||
brcc 1f
|
||||
mov r1, -1 /* All entries have been accessed, */
|
||||
mtsr SYSREG_TLBARLO, r1 /* so reset TLBAR */
|
||||
mov r2, 0 /* and start at 0 */
|
||||
1: mfsr r1, SYSREG_MMUCR
|
||||
lsl r2, 14
|
||||
andl r1, 0x3fff, COH
|
||||
or r1, r2
|
||||
mtsr SYSREG_MMUCR, r1
|
||||
|
||||
tlbw
|
||||
|
||||
tlbmiss_restore
|
||||
rete
|
||||
|
||||
handle_vmalloc_miss:
|
||||
/* Simply do the lookup in init's page table */
|
||||
mov r0, lo(swapper_pg_dir)
|
||||
orh r0, hi(swapper_pg_dir)
|
||||
rjmp pgtbl_lookup
|
||||
|
||||
|
||||
/* --- System Call --- */
|
||||
|
||||
.section .scall.text,"ax",@progbits
|
||||
system_call:
|
||||
pushm r12 /* r12_orig */
|
||||
stmts --sp, r0-lr
|
||||
zero_fp
|
||||
mfsr r0, SYSREG_RAR_SUP
|
||||
mfsr r1, SYSREG_RSR_SUP
|
||||
stm --sp, r0-r1
|
||||
|
||||
/* check for syscall tracing */
|
||||
get_thread_info r0
|
||||
ld.w r1, r0[TI_flags]
|
||||
bld r1, TIF_SYSCALL_TRACE
|
||||
brcs syscall_trace_enter
|
||||
|
||||
syscall_trace_cont:
|
||||
cp.w r8, NR_syscalls
|
||||
brhs syscall_badsys
|
||||
|
||||
lddpc lr, syscall_table_addr
|
||||
ld.w lr, lr[r8 << 2]
|
||||
mov r8, r5 /* 5th argument (6th is pushed by stub) */
|
||||
icall lr
|
||||
|
||||
.global syscall_return
|
||||
syscall_return:
|
||||
get_thread_info r0
|
||||
mask_interrupts /* make sure we don't miss an interrupt
|
||||
setting need_resched or sigpending
|
||||
between sampling and the rets */
|
||||
|
||||
/* Store the return value so that the correct value is loaded below */
|
||||
stdsp sp[REG_R12], r12
|
||||
|
||||
ld.w r1, r0[TI_flags]
|
||||
andl r1, _TIF_ALLWORK_MASK, COH
|
||||
brne syscall_exit_work
|
||||
|
||||
syscall_exit_cont:
|
||||
popm r8-r9
|
||||
mtsr SYSREG_RAR_SUP, r8
|
||||
mtsr SYSREG_RSR_SUP, r9
|
||||
ldmts sp++, r0-lr
|
||||
sub sp, -4 /* r12_orig */
|
||||
rets
|
||||
|
||||
.align 2
|
||||
syscall_table_addr:
|
||||
.long sys_call_table
|
||||
|
||||
syscall_badsys:
|
||||
mov r12, -ENOSYS
|
||||
rjmp syscall_return
|
||||
|
||||
.global ret_from_fork
|
||||
ret_from_fork:
|
||||
rcall schedule_tail
|
||||
|
||||
/* check for syscall tracing */
|
||||
get_thread_info r0
|
||||
ld.w r1, r0[TI_flags]
|
||||
andl r1, _TIF_ALLWORK_MASK, COH
|
||||
brne syscall_exit_work
|
||||
rjmp syscall_exit_cont
|
||||
|
||||
syscall_trace_enter:
|
||||
pushm r8-r12
|
||||
rcall syscall_trace
|
||||
popm r8-r12
|
||||
rjmp syscall_trace_cont
|
||||
|
||||
syscall_exit_work:
|
||||
bld r1, TIF_SYSCALL_TRACE
|
||||
brcc 1f
|
||||
unmask_interrupts
|
||||
rcall syscall_trace
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
|
||||
1: bld r1, TIF_NEED_RESCHED
|
||||
brcc 2f
|
||||
unmask_interrupts
|
||||
rcall schedule
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp 1b
|
||||
|
||||
2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
|
||||
tst r1, r2
|
||||
breq 3f
|
||||
unmask_interrupts
|
||||
mov r12, sp
|
||||
mov r11, r0
|
||||
rcall do_notify_resume
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp 1b
|
||||
|
||||
3: bld r1, TIF_BREAKPOINT
|
||||
brcc syscall_exit_cont
|
||||
mfsr r3, SYSREG_TLBEHI
|
||||
lddsp r2, sp[REG_PC]
|
||||
andl r3, 0xff, COH
|
||||
lsl r3, 1
|
||||
sbr r3, 30
|
||||
sbr r3, 0
|
||||
mtdr DBGREG_BWA2A, r2
|
||||
mtdr DBGREG_BWC2A, r3
|
||||
rjmp syscall_exit_cont
|
||||
|
||||
|
||||
/* The slow path of the TLB miss handler */
|
||||
page_table_not_present:
|
||||
page_not_present:
|
||||
tlbmiss_restore
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
mfsr r12, SYSREG_ECR
|
||||
mov r11, sp
|
||||
rcall do_page_fault
|
||||
rjmp ret_from_exception
|
||||
|
||||
/* This function expects to find offending PC in SYSREG_RAR_EX */
|
||||
save_full_context_ex:
|
||||
mfsr r8, SYSREG_RSR_EX
|
||||
mov r12, r8
|
||||
andh r8, (MODE_MASK >> 16), COH
|
||||
mfsr r11, SYSREG_RAR_EX
|
||||
brne 2f
|
||||
|
||||
1: pushm r11, r12 /* PC and SR */
|
||||
unmask_exceptions
|
||||
ret r12
|
||||
|
||||
2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR)
|
||||
stdsp sp[4], r10 /* replace saved SP */
|
||||
rjmp 1b
|
||||
|
||||
/* Low-level exception handlers */
|
||||
handle_critical:
|
||||
pushm r12
|
||||
pushm r0-r12
|
||||
rcall save_full_context_ex
|
||||
mfsr r12, SYSREG_ECR
|
||||
mov r11, sp
|
||||
rcall do_critical_exception
|
||||
|
||||
/* We should never get here... */
|
||||
bad_return:
|
||||
sub r12, pc, (. - 1f)
|
||||
bral panic
|
||||
.align 2
|
||||
1: .asciz "Return from critical exception!"
|
||||
|
||||
.align 1
|
||||
do_bus_error_write:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
mov r11, 1
|
||||
rjmp 1f
|
||||
|
||||
do_bus_error_read:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
mov r11, 0
|
||||
1: mfsr r12, SYSREG_BEAR
|
||||
mov r10, sp
|
||||
rcall do_bus_error
|
||||
rjmp ret_from_exception
|
||||
|
||||
.align 1
|
||||
do_nmi_ll:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
/* FIXME: Make sure RAR_NMI and RSR_NMI are pushed instead of *_EX */
|
||||
rcall save_full_context_ex
|
||||
mfsr r12, SYSREG_ECR
|
||||
mov r11, sp
|
||||
rcall do_nmi
|
||||
rjmp bad_return
|
||||
|
||||
handle_address_fault:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
mfsr r12, SYSREG_ECR
|
||||
mov r11, sp
|
||||
rcall do_address_exception
|
||||
rjmp ret_from_exception
|
||||
|
||||
handle_protection_fault:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
mfsr r12, SYSREG_ECR
|
||||
mov r11, sp
|
||||
rcall do_page_fault
|
||||
rjmp ret_from_exception
|
||||
|
||||
.align 1
|
||||
do_illegal_opcode_ll:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
mfsr r12, SYSREG_ECR
|
||||
mov r11, sp
|
||||
rcall do_illegal_opcode
|
||||
rjmp ret_from_exception
|
||||
|
||||
do_dtlb_modified:
|
||||
pushm r0-r3
|
||||
mfsr r1, SYSREG_TLBEAR
|
||||
mfsr r0, SYSREG_PTBR
|
||||
lsr r2, r1, PGDIR_SHIFT
|
||||
ld.w r0, r0[r2 << 2]
|
||||
lsl r1, (32 - PGDIR_SHIFT)
|
||||
lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
|
||||
|
||||
/* Translate to virtual address in P1 */
|
||||
andl r0, 0xf000
|
||||
sbr r0, 31
|
||||
add r2, r0, r1 << 2
|
||||
ld.w r3, r2[0]
|
||||
sbr r3, _PAGE_BIT_DIRTY
|
||||
mov r0, r3
|
||||
st.w r2[0], r3
|
||||
|
||||
/* The page table is up-to-date. Update the TLB entry as well */
|
||||
andl r0, lo(_PAGE_FLAGS_HARDWARE_MASK)
|
||||
mtsr SYSREG_TLBELO, r0
|
||||
|
||||
/* MMUCR[DRP] is updated automatically, so let's go... */
|
||||
tlbw
|
||||
|
||||
popm r0-r3
|
||||
rete
|
||||
|
||||
do_fpe_ll:
|
||||
sub sp, 4
|
||||
stmts --sp, r0-lr
|
||||
rcall save_full_context_ex
|
||||
unmask_interrupts
|
||||
mov r12, 26
|
||||
mov r11, sp
|
||||
rcall do_fpe
|
||||
rjmp ret_from_exception
|
||||
|
||||
ret_from_exception:
|
||||
mask_interrupts
|
||||
lddsp r4, sp[REG_SR]
|
||||
andh r4, (MODE_MASK >> 16), COH
|
||||
brne fault_resume_kernel
|
||||
|
||||
get_thread_info r0
|
||||
ld.w r1, r0[TI_flags]
|
||||
andl r1, _TIF_WORK_MASK, COH
|
||||
brne fault_exit_work
|
||||
|
||||
fault_resume_user:
|
||||
popm r8-r9
|
||||
mask_exceptions
|
||||
mtsr SYSREG_RAR_EX, r8
|
||||
mtsr SYSREG_RSR_EX, r9
|
||||
ldmts sp++, r0-lr
|
||||
sub sp, -4
|
||||
rete
|
||||
|
||||
fault_resume_kernel:
|
||||
#ifdef CONFIG_PREEMPT
|
||||
get_thread_info r0
|
||||
ld.w r2, r0[TI_preempt_count]
|
||||
cp.w r2, 0
|
||||
brne 1f
|
||||
ld.w r1, r0[TI_flags]
|
||||
bld r1, TIF_NEED_RESCHED
|
||||
brcc 1f
|
||||
lddsp r4, sp[REG_SR]
|
||||
bld r4, SYSREG_GM_OFFSET
|
||||
brcs 1f
|
||||
rcall preempt_schedule_irq
|
||||
1:
|
||||
#endif
|
||||
|
||||
popm r8-r9
|
||||
mask_exceptions
|
||||
mfsr r1, SYSREG_SR
|
||||
mtsr SYSREG_RAR_EX, r8
|
||||
mtsr SYSREG_RSR_EX, r9
|
||||
popm lr
|
||||
sub sp, -4 /* ignore SP */
|
||||
popm r0-r12
|
||||
sub sp, -4 /* ignore r12_orig */
|
||||
rete
|
||||
|
||||
irq_exit_work:
|
||||
/* Switch to exception mode so that we can share the same code. */
|
||||
mfsr r8, SYSREG_SR
|
||||
cbr r8, SYSREG_M0_OFFSET
|
||||
orh r8, hi(SYSREG_BIT(M1) | SYSREG_BIT(M2))
|
||||
mtsr SYSREG_SR, r8
|
||||
sub pc, -2
|
||||
get_thread_info r0
|
||||
ld.w r1, r0[TI_flags]
|
||||
|
||||
fault_exit_work:
|
||||
bld r1, TIF_NEED_RESCHED
|
||||
brcc 1f
|
||||
unmask_interrupts
|
||||
rcall schedule
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp fault_exit_work
|
||||
|
||||
1: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
|
||||
tst r1, r2
|
||||
breq 2f
|
||||
unmask_interrupts
|
||||
mov r12, sp
|
||||
mov r11, r0
|
||||
rcall do_notify_resume
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp fault_exit_work
|
||||
|
||||
2: bld r1, TIF_BREAKPOINT
|
||||
brcc fault_resume_user
|
||||
mfsr r3, SYSREG_TLBEHI
|
||||
lddsp r2, sp[REG_PC]
|
||||
andl r3, 0xff, COH
|
||||
lsl r3, 1
|
||||
sbr r3, 30
|
||||
sbr r3, 0
|
||||
mtdr DBGREG_BWA2A, r2
|
||||
mtdr DBGREG_BWC2A, r3
|
||||
rjmp fault_resume_user
|
||||
|
||||
/* If we get a debug trap from privileged context we end up here */
|
||||
handle_debug_priv:
|
||||
/* Fix up LR and SP in regs. r11 contains the mode we came from */
|
||||
mfsr r8, SYSREG_SR
|
||||
mov r9, r8
|
||||
andh r8, hi(~MODE_MASK)
|
||||
or r8, r11
|
||||
mtsr SYSREG_SR, r8
|
||||
sub pc, -2
|
||||
stdsp sp[REG_LR], lr
|
||||
mtsr SYSREG_SR, r9
|
||||
sub pc, -2
|
||||
sub r10, sp, -FRAME_SIZE_FULL
|
||||
stdsp sp[REG_SP], r10
|
||||
mov r12, sp
|
||||
rcall do_debug_priv
|
||||
|
||||
/* Now, put everything back */
|
||||
ssrf SR_EM_BIT
|
||||
popm r10, r11
|
||||
mtsr SYSREG_RAR_DBG, r10
|
||||
mtsr SYSREG_RSR_DBG, r11
|
||||
mfsr r8, SYSREG_SR
|
||||
mov r9, r8
|
||||
andh r8, hi(~MODE_MASK)
|
||||
andh r11, hi(MODE_MASK)
|
||||
or r8, r11
|
||||
mtsr SYSREG_SR, r8
|
||||
sub pc, -2
|
||||
popm lr
|
||||
mtsr SYSREG_SR, r9
|
||||
sub pc, -2
|
||||
sub sp, -4 /* skip SP */
|
||||
popm r0-r12
|
||||
sub sp, -4
|
||||
retd
|
||||
|
||||
/*
|
||||
* At this point, everything is masked, that is, interrupts,
|
||||
* exceptions and debugging traps. We might get called from
|
||||
* interrupt or exception context in some rare cases, but this
|
||||
* will be taken care of by do_debug(), so we're not going to
|
||||
* do a 100% correct context save here.
|
||||
*/
|
||||
handle_debug:
|
||||
sub sp, 4 /* r12_orig */
|
||||
stmts --sp, r0-lr
|
||||
mfsr r10, SYSREG_RAR_DBG
|
||||
mfsr r11, SYSREG_RSR_DBG
|
||||
unmask_exceptions
|
||||
pushm r10,r11
|
||||
andh r11, (MODE_MASK >> 16), COH
|
||||
brne handle_debug_priv
|
||||
|
||||
mov r12, sp
|
||||
rcall do_debug
|
||||
|
||||
lddsp r10, sp[REG_SR]
|
||||
andh r10, (MODE_MASK >> 16), COH
|
||||
breq debug_resume_user
|
||||
|
||||
debug_restore_all:
|
||||
popm r10,r11
|
||||
mask_exceptions
|
||||
mtsr SYSREG_RSR_DBG, r11
|
||||
mtsr SYSREG_RAR_DBG, r10
|
||||
ldmts sp++, r0-lr
|
||||
sub sp, -4
|
||||
retd
|
||||
|
||||
debug_resume_user:
|
||||
get_thread_info r0
|
||||
mask_interrupts
|
||||
|
||||
ld.w r1, r0[TI_flags]
|
||||
andl r1, _TIF_DBGWORK_MASK, COH
|
||||
breq debug_restore_all
|
||||
|
||||
1: bld r1, TIF_NEED_RESCHED
|
||||
brcc 2f
|
||||
unmask_interrupts
|
||||
rcall schedule
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp 1b
|
||||
|
||||
2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
|
||||
tst r1, r2
|
||||
breq 3f
|
||||
unmask_interrupts
|
||||
mov r12, sp
|
||||
mov r11, r0
|
||||
rcall do_notify_resume
|
||||
mask_interrupts
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp 1b
|
||||
|
||||
3: bld r1, TIF_SINGLE_STEP
|
||||
brcc debug_restore_all
|
||||
mfdr r2, DBGREG_DC
|
||||
sbr r2, DC_SS_BIT
|
||||
mtdr DBGREG_DC, r2
|
||||
rjmp debug_restore_all
|
||||
|
||||
.set rsr_int0, SYSREG_RSR_INT0
|
||||
.set rsr_int1, SYSREG_RSR_INT1
|
||||
.set rsr_int2, SYSREG_RSR_INT2
|
||||
.set rsr_int3, SYSREG_RSR_INT3
|
||||
.set rar_int0, SYSREG_RAR_INT0
|
||||
.set rar_int1, SYSREG_RAR_INT1
|
||||
.set rar_int2, SYSREG_RAR_INT2
|
||||
.set rar_int3, SYSREG_RAR_INT3
|
||||
|
||||
.macro IRQ_LEVEL level
|
||||
.type irq_level\level, @function
|
||||
irq_level\level:
|
||||
sub sp, 4 /* r12_orig */
|
||||
stmts --sp,r0-lr
|
||||
mfsr r8, rar_int\level
|
||||
mfsr r9, rsr_int\level
|
||||
pushm r8-r9
|
||||
|
||||
mov r11, sp
|
||||
mov r12, \level
|
||||
|
||||
rcall do_IRQ
|
||||
|
||||
lddsp r4, sp[REG_SR]
|
||||
andh r4, (MODE_MASK >> 16), COH
|
||||
#ifdef CONFIG_PREEMPT
|
||||
brne 2f
|
||||
#else
|
||||
brne 1f
|
||||
#endif
|
||||
|
||||
get_thread_info r0
|
||||
ld.w r1, r0[TI_flags]
|
||||
andl r1, _TIF_WORK_MASK, COH
|
||||
brne irq_exit_work
|
||||
|
||||
1: popm r8-r9
|
||||
mtsr rar_int\level, r8
|
||||
mtsr rsr_int\level, r9
|
||||
ldmts sp++,r0-lr
|
||||
sub sp, -4 /* ignore r12_orig */
|
||||
rete
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
2:
|
||||
get_thread_info r0
|
||||
ld.w r2, r0[TI_preempt_count]
|
||||
cp.w r2, 0
|
||||
brne 1b
|
||||
ld.w r1, r0[TI_flags]
|
||||
bld r1, TIF_NEED_RESCHED
|
||||
brcc 1b
|
||||
lddsp r4, sp[REG_SR]
|
||||
bld r4, SYSREG_GM_OFFSET
|
||||
brcs 1b
|
||||
rcall preempt_schedule_irq
|
||||
rjmp 1b
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.section .irq.text,"ax",@progbits
|
||||
|
||||
.global irq_level0
|
||||
.global irq_level1
|
||||
.global irq_level2
|
||||
.global irq_level3
|
||||
IRQ_LEVEL 0
|
||||
IRQ_LEVEL 1
|
||||
IRQ_LEVEL 2
|
||||
IRQ_LEVEL 3
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Non-board-specific low-level startup code
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/page.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
.section .init.text,"ax"
|
||||
.global kernel_entry
|
||||
kernel_entry:
|
||||
/* Initialize status register */
|
||||
lddpc r0, init_sr
|
||||
mtsr SYSREG_SR, r0
|
||||
|
||||
/* Set initial stack pointer */
|
||||
lddpc sp, stack_addr
|
||||
sub sp, -THREAD_SIZE
|
||||
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
/* Mark last stack frame */
|
||||
mov lr, 0
|
||||
mov r7, 0
|
||||
#endif
|
||||
|
||||
/* Set up the PIO, SDRAM controller, early printk, etc. */
|
||||
rcall board_early_init
|
||||
|
||||
/* Start the show */
|
||||
lddpc pc, kernel_start_addr
|
||||
|
||||
.align 2
|
||||
init_sr:
|
||||
.long 0x007f0000 /* Supervisor mode, everything masked */
|
||||
stack_addr:
|
||||
.long init_thread_union
|
||||
kernel_start_addr:
|
||||
.long start_kernel
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/mqueue.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
static struct fs_struct init_fs = INIT_FS;
|
||||
static struct files_struct init_files = INIT_FILES;
|
||||
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
|
||||
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
|
||||
struct mm_struct init_mm = INIT_MM(init_mm);
|
||||
|
||||
EXPORT_SYMBOL(init_mm);
|
||||
|
||||
/*
|
||||
* Initial thread structure. Must be aligned on an 8192-byte boundary.
|
||||
*/
|
||||
union thread_union init_thread_union
|
||||
__attribute__((__section__(".data.init_task"))) =
|
||||
{ INIT_THREAD_INFO(init_task) };
|
||||
|
||||
/*
|
||||
* Initial task structure.
|
||||
*
|
||||
* All other task structs will be allocated on slabs in fork.c
|
||||
*/
|
||||
struct task_struct init_task = INIT_TASK(init_task);
|
||||
|
||||
EXPORT_SYMBOL(init_task);
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* Based on arch/i386/kernel/irq.c
|
||||
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This file contains the code used by various IRQ handling routines:
|
||||
* asking for different IRQ's should be done through these routines
|
||||
* instead of just grabbing them. Thus setups with different IRQ numbers
|
||||
* shouldn't result in any weird surprises, and installing new handlers
|
||||
* should be easier.
|
||||
*
|
||||
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
|
||||
* Naturally it's not a 1:1 relation, but there are similarities.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sysdev.h>
|
||||
|
||||
/*
|
||||
* 'what should we do if we get a hw irq event on an illegal vector'.
|
||||
* each architecture has to answer this themselves.
|
||||
*/
|
||||
void ack_bad_irq(unsigned int irq)
|
||||
{
|
||||
printk("unexpected IRQ %u\n", irq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int show_interrupts(struct seq_file *p, void *v)
|
||||
{
|
||||
int i = *(loff_t *)v, cpu;
|
||||
struct irqaction *action;
|
||||
unsigned long flags;
|
||||
|
||||
if (i == 0) {
|
||||
seq_puts(p, " ");
|
||||
for_each_online_cpu(cpu)
|
||||
seq_printf(p, "CPU%d ", cpu);
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
if (i < NR_IRQS) {
|
||||
spin_lock_irqsave(&irq_desc[i].lock, flags);
|
||||
action = irq_desc[i].action;
|
||||
if (!action)
|
||||
goto unlock;
|
||||
|
||||
seq_printf(p, "%3d: ", i);
|
||||
for_each_online_cpu(cpu)
|
||||
seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
|
||||
seq_printf(p, " %s", action->name);
|
||||
for (action = action->next; action; action = action->next)
|
||||
seq_printf(p, ", %s", action->name);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Kernel Probes (KProbes)
|
||||
*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* Based on arch/ppc64/kernel/kprobes.c
|
||||
* Copyright (C) IBM Corporation, 2002, 2004
|
||||
*
|
||||
* 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/kprobes.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/kdebug.h>
|
||||
#include <asm/ocd.h>
|
||||
|
||||
DEFINE_PER_CPU(struct kprobe *, current_kprobe);
|
||||
static unsigned long kprobe_status;
|
||||
static struct pt_regs jprobe_saved_regs;
|
||||
|
||||
int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((unsigned long)p->addr & 0x01) {
|
||||
printk("Attempt to register kprobe at an unaligned address\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
/* XXX: Might be a good idea to check if p->addr is a valid
|
||||
* kernel address as well... */
|
||||
|
||||
if (!ret) {
|
||||
pr_debug("copy kprobe at %p\n", p->addr);
|
||||
memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
|
||||
p->opcode = *p->addr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __kprobes arch_arm_kprobe(struct kprobe *p)
|
||||
{
|
||||
pr_debug("arming kprobe at %p\n", p->addr);
|
||||
*p->addr = BREAKPOINT_INSTRUCTION;
|
||||
flush_icache_range((unsigned long)p->addr,
|
||||
(unsigned long)p->addr + sizeof(kprobe_opcode_t));
|
||||
}
|
||||
|
||||
void __kprobes arch_disarm_kprobe(struct kprobe *p)
|
||||
{
|
||||
pr_debug("disarming kprobe at %p\n", p->addr);
|
||||
*p->addr = p->opcode;
|
||||
flush_icache_range((unsigned long)p->addr,
|
||||
(unsigned long)p->addr + sizeof(kprobe_opcode_t));
|
||||
}
|
||||
|
||||
static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long dc;
|
||||
|
||||
pr_debug("preparing to singlestep over %p (PC=%08lx)\n",
|
||||
p->addr, regs->pc);
|
||||
|
||||
BUG_ON(!(sysreg_read(SR) & SYSREG_BIT(SR_D)));
|
||||
|
||||
dc = __mfdr(DBGREG_DC);
|
||||
dc |= DC_SS;
|
||||
__mtdr(DBGREG_DC, dc);
|
||||
|
||||
/*
|
||||
* We must run the instruction from its original location
|
||||
* since it may actually reference PC.
|
||||
*
|
||||
* TODO: Do the instruction replacement directly in icache.
|
||||
*/
|
||||
*p->addr = p->opcode;
|
||||
flush_icache_range((unsigned long)p->addr,
|
||||
(unsigned long)p->addr + sizeof(kprobe_opcode_t));
|
||||
}
|
||||
|
||||
static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long dc;
|
||||
|
||||
pr_debug("resuming execution at PC=%08lx\n", regs->pc);
|
||||
|
||||
dc = __mfdr(DBGREG_DC);
|
||||
dc &= ~DC_SS;
|
||||
__mtdr(DBGREG_DC, dc);
|
||||
|
||||
*p->addr = BREAKPOINT_INSTRUCTION;
|
||||
flush_icache_range((unsigned long)p->addr,
|
||||
(unsigned long)p->addr + sizeof(kprobe_opcode_t));
|
||||
}
|
||||
|
||||
static void __kprobes set_current_kprobe(struct kprobe *p)
|
||||
{
|
||||
__get_cpu_var(current_kprobe) = p;
|
||||
}
|
||||
|
||||
static int __kprobes kprobe_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe *p;
|
||||
void *addr = (void *)regs->pc;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("kprobe_handler: kprobe_running=%d\n",
|
||||
kprobe_running());
|
||||
|
||||
/*
|
||||
* We don't want to be preempted for the entire
|
||||
* duration of kprobe processing
|
||||
*/
|
||||
preempt_disable();
|
||||
|
||||
/* Check that we're not recursing */
|
||||
if (kprobe_running()) {
|
||||
p = get_kprobe(addr);
|
||||
if (p) {
|
||||
if (kprobe_status == KPROBE_HIT_SS) {
|
||||
printk("FIXME: kprobe hit while single-stepping!\n");
|
||||
goto no_kprobe;
|
||||
}
|
||||
|
||||
printk("FIXME: kprobe hit while handling another kprobe\n");
|
||||
goto no_kprobe;
|
||||
} else {
|
||||
p = kprobe_running();
|
||||
if (p->break_handler && p->break_handler(p, regs))
|
||||
goto ss_probe;
|
||||
}
|
||||
/* If it's not ours, can't be delete race, (we hold lock). */
|
||||
goto no_kprobe;
|
||||
}
|
||||
|
||||
p = get_kprobe(addr);
|
||||
if (!p)
|
||||
goto no_kprobe;
|
||||
|
||||
kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
set_current_kprobe(p);
|
||||
if (p->pre_handler && p->pre_handler(p, regs))
|
||||
/* handler has already set things up, so skip ss setup */
|
||||
return 1;
|
||||
|
||||
ss_probe:
|
||||
prepare_singlestep(p, regs);
|
||||
kprobe_status = KPROBE_HIT_SS;
|
||||
return 1;
|
||||
|
||||
no_kprobe:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __kprobes post_kprobe_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe *cur = kprobe_running();
|
||||
|
||||
pr_debug("post_kprobe_handler, cur=%p\n", cur);
|
||||
|
||||
if (!cur)
|
||||
return 0;
|
||||
|
||||
if (cur->post_handler) {
|
||||
kprobe_status = KPROBE_HIT_SSDONE;
|
||||
cur->post_handler(cur, regs, 0);
|
||||
}
|
||||
|
||||
resume_execution(cur, regs);
|
||||
reset_current_kprobe();
|
||||
preempt_enable_no_resched();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
|
||||
{
|
||||
struct kprobe *cur = kprobe_running();
|
||||
|
||||
pr_debug("kprobe_fault_handler: trapnr=%d\n", trapnr);
|
||||
|
||||
if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
|
||||
return 1;
|
||||
|
||||
if (kprobe_status & KPROBE_HIT_SS) {
|
||||
resume_execution(cur, regs);
|
||||
preempt_enable_no_resched();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper routine to for handling exceptions.
|
||||
*/
|
||||
int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct die_args *args = (struct die_args *)data;
|
||||
int ret = NOTIFY_DONE;
|
||||
|
||||
pr_debug("kprobe_exceptions_notify: val=%lu, data=%p\n",
|
||||
val, data);
|
||||
|
||||
switch (val) {
|
||||
case DIE_BREAKPOINT:
|
||||
if (kprobe_handler(args->regs))
|
||||
ret = NOTIFY_STOP;
|
||||
break;
|
||||
case DIE_SSTEP:
|
||||
if (post_kprobe_handler(args->regs))
|
||||
ret = NOTIFY_STOP;
|
||||
break;
|
||||
case DIE_FAULT:
|
||||
if (kprobe_running()
|
||||
&& kprobe_fault_handler(args->regs, args->trapnr))
|
||||
ret = NOTIFY_STOP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct jprobe *jp = container_of(p, struct jprobe, kp);
|
||||
|
||||
memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs));
|
||||
|
||||
/*
|
||||
* TODO: We should probably save some of the stack here as
|
||||
* well, since gcc may pass arguments on the stack for certain
|
||||
* functions (lots of arguments, large aggregates, varargs)
|
||||
*/
|
||||
|
||||
/* setup return addr to the jprobe handler routine */
|
||||
regs->pc = (unsigned long)jp->entry;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void __kprobes jprobe_return(void)
|
||||
{
|
||||
asm volatile("breakpoint" ::: "memory");
|
||||
}
|
||||
|
||||
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* FIXME - we should ideally be validating that we got here 'cos
|
||||
* of the "trap" in jprobe_return() above, before restoring the
|
||||
* saved regs...
|
||||
*/
|
||||
memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __init arch_init_kprobes(void)
|
||||
{
|
||||
printk("KPROBES: Enabling monitor mode (MM|DBE)...\n");
|
||||
__mtdr(DBGREG_DC, DC_MM | DC_DBE);
|
||||
|
||||
/* TODO: Register kretprobe trampoline */
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* AVR32-specific kernel module loader
|
||||
*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* GOT initialization parts are based on the s390 version
|
||||
* Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH,
|
||||
* IBM Corporation
|
||||
*
|
||||
* 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/moduleloader.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
void *module_alloc(unsigned long size)
|
||||
{
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
return vmalloc(size);
|
||||
}
|
||||
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
{
|
||||
vfree(mod->arch.syminfo);
|
||||
mod->arch.syminfo = NULL;
|
||||
|
||||
vfree(module_region);
|
||||
/* FIXME: if module_region == mod->init_region, trim exception
|
||||
* table entries. */
|
||||
}
|
||||
|
||||
static inline int check_rela(Elf32_Rela *rela, struct module *module,
|
||||
char *strings, Elf32_Sym *symbols)
|
||||
{
|
||||
struct mod_arch_syminfo *info;
|
||||
|
||||
info = module->arch.syminfo + ELF32_R_SYM(rela->r_info);
|
||||
switch (ELF32_R_TYPE(rela->r_info)) {
|
||||
case R_AVR32_GOT32:
|
||||
case R_AVR32_GOT16:
|
||||
case R_AVR32_GOT8:
|
||||
case R_AVR32_GOT21S:
|
||||
case R_AVR32_GOT18SW: /* mcall */
|
||||
case R_AVR32_GOT16S: /* ld.w */
|
||||
if (rela->r_addend != 0) {
|
||||
printk(KERN_ERR
|
||||
"GOT relocation against %s at offset %u with addend\n",
|
||||
strings + symbols[ELF32_R_SYM(rela->r_info)].st_name,
|
||||
rela->r_offset);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
if (info->got_offset == -1UL) {
|
||||
info->got_offset = module->arch.got_size;
|
||||
module->arch.got_size += sizeof(void *);
|
||||
}
|
||||
pr_debug("GOT[%3lu] %s\n", info->got_offset,
|
||||
strings + symbols[ELF32_R_SYM(rela->r_info)].st_name);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
|
||||
char *secstrings, struct module *module)
|
||||
{
|
||||
Elf32_Shdr *symtab;
|
||||
Elf32_Sym *symbols;
|
||||
Elf32_Rela *rela;
|
||||
char *strings;
|
||||
int nrela, i, j;
|
||||
int ret;
|
||||
|
||||
/* Find the symbol table */
|
||||
symtab = NULL;
|
||||
for (i = 0; i < hdr->e_shnum; i++)
|
||||
switch (sechdrs[i].sh_type) {
|
||||
case SHT_SYMTAB:
|
||||
symtab = &sechdrs[i];
|
||||
break;
|
||||
}
|
||||
if (!symtab) {
|
||||
printk(KERN_ERR "module %s: no symbol table\n", module->name);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
/* Allocate room for one syminfo structure per symbol. */
|
||||
module->arch.nsyms = symtab->sh_size / sizeof(Elf_Sym);
|
||||
module->arch.syminfo = vmalloc(module->arch.nsyms
|
||||
* sizeof(struct mod_arch_syminfo));
|
||||
if (!module->arch.syminfo)
|
||||
return -ENOMEM;
|
||||
|
||||
symbols = (void *)hdr + symtab->sh_offset;
|
||||
strings = (void *)hdr + sechdrs[symtab->sh_link].sh_offset;
|
||||
for (i = 0; i < module->arch.nsyms; i++) {
|
||||
if (symbols[i].st_shndx == SHN_UNDEF &&
|
||||
strcmp(strings + symbols[i].st_name,
|
||||
"_GLOBAL_OFFSET_TABLE_") == 0)
|
||||
/* "Define" it as absolute. */
|
||||
symbols[i].st_shndx = SHN_ABS;
|
||||
module->arch.syminfo[i].got_offset = -1UL;
|
||||
module->arch.syminfo[i].got_initialized = 0;
|
||||
}
|
||||
|
||||
/* Allocate GOT entries for symbols that need it. */
|
||||
module->arch.got_size = 0;
|
||||
for (i = 0; i < hdr->e_shnum; i++) {
|
||||
if (sechdrs[i].sh_type != SHT_RELA)
|
||||
continue;
|
||||
nrela = sechdrs[i].sh_size / sizeof(Elf32_Rela);
|
||||
rela = (void *)hdr + sechdrs[i].sh_offset;
|
||||
for (j = 0; j < nrela; j++) {
|
||||
ret = check_rela(rela + j, module,
|
||||
strings, symbols);
|
||||
if (ret)
|
||||
goto out_free_syminfo;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase core size to make room for GOT and set start
|
||||
* offset for GOT.
|
||||
*/
|
||||
module->core_size = ALIGN(module->core_size, 4);
|
||||
module->arch.got_offset = module->core_size;
|
||||
module->core_size += module->arch.got_size;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_syminfo:
|
||||
vfree(module->arch.syminfo);
|
||||
module->arch.syminfo = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int reloc_overflow(struct module *module, const char *reloc_name,
|
||||
Elf32_Addr relocation)
|
||||
{
|
||||
printk(KERN_ERR "module %s: Value %lx does not fit relocation %s\n",
|
||||
module->name, (unsigned long)relocation, reloc_name);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
#define get_u16(loc) (*((uint16_t *)loc))
|
||||
#define put_u16(loc, val) (*((uint16_t *)loc) = (val))
|
||||
|
||||
int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
|
||||
unsigned int symindex, unsigned int relindex,
|
||||
struct module *module)
|
||||
{
|
||||
Elf32_Shdr *symsec = sechdrs + symindex;
|
||||
Elf32_Shdr *relsec = sechdrs + relindex;
|
||||
Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
|
||||
Elf32_Rela *rel = (void *)relsec->sh_addr;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rela); i++, rel++) {
|
||||
struct mod_arch_syminfo *info;
|
||||
Elf32_Sym *sym;
|
||||
Elf32_Addr relocation;
|
||||
uint32_t *location;
|
||||
uint32_t value;
|
||||
|
||||
location = (void *)dstsec->sh_addr + rel->r_offset;
|
||||
sym = (Elf32_Sym *)symsec->sh_addr + ELF32_R_SYM(rel->r_info);
|
||||
relocation = sym->st_value + rel->r_addend;
|
||||
|
||||
info = module->arch.syminfo + ELF32_R_SYM(rel->r_info);
|
||||
|
||||
/* Initialize GOT entry if necessary */
|
||||
switch (ELF32_R_TYPE(rel->r_info)) {
|
||||
case R_AVR32_GOT32:
|
||||
case R_AVR32_GOT16:
|
||||
case R_AVR32_GOT8:
|
||||
case R_AVR32_GOT21S:
|
||||
case R_AVR32_GOT18SW:
|
||||
case R_AVR32_GOT16S:
|
||||
if (!info->got_initialized) {
|
||||
Elf32_Addr *gotent;
|
||||
|
||||
gotent = (module->module_core
|
||||
+ module->arch.got_offset
|
||||
+ info->got_offset);
|
||||
*gotent = relocation;
|
||||
info->got_initialized = 1;
|
||||
}
|
||||
|
||||
relocation = info->got_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ELF32_R_TYPE(rel->r_info)) {
|
||||
case R_AVR32_32:
|
||||
case R_AVR32_32_CPENT:
|
||||
*location = relocation;
|
||||
break;
|
||||
case R_AVR32_22H_PCREL:
|
||||
relocation -= (Elf32_Addr)location;
|
||||
if ((relocation & 0xffe00001) != 0
|
||||
&& (relocation & 0xffc00001) != 0xffc00000)
|
||||
return reloc_overflow(module,
|
||||
"R_AVR32_22H_PCREL",
|
||||
relocation);
|
||||
relocation >>= 1;
|
||||
|
||||
value = *location;
|
||||
value = ((value & 0xe1ef0000)
|
||||
| (relocation & 0xffff)
|
||||
| ((relocation & 0x10000) << 4)
|
||||
| ((relocation & 0x1e0000) << 8));
|
||||
*location = value;
|
||||
break;
|
||||
case R_AVR32_11H_PCREL:
|
||||
relocation -= (Elf32_Addr)location;
|
||||
if ((relocation & 0xfffffc01) != 0
|
||||
&& (relocation & 0xfffff801) != 0xfffff800)
|
||||
return reloc_overflow(module,
|
||||
"R_AVR32_11H_PCREL",
|
||||
relocation);
|
||||
value = get_u16(location);
|
||||
value = ((value & 0xf00c)
|
||||
| ((relocation & 0x1fe) << 3)
|
||||
| ((relocation & 0x600) >> 9));
|
||||
put_u16(location, value);
|
||||
break;
|
||||
case R_AVR32_9H_PCREL:
|
||||
relocation -= (Elf32_Addr)location;
|
||||
if ((relocation & 0xffffff01) != 0
|
||||
&& (relocation & 0xfffffe01) != 0xfffffe00)
|
||||
return reloc_overflow(module,
|
||||
"R_AVR32_9H_PCREL",
|
||||
relocation);
|
||||
value = get_u16(location);
|
||||
value = ((value & 0xf00f)
|
||||
| ((relocation & 0x1fe) << 3));
|
||||
put_u16(location, value);
|
||||
break;
|
||||
case R_AVR32_9UW_PCREL:
|
||||
relocation -= ((Elf32_Addr)location) & 0xfffffffc;
|
||||
if ((relocation & 0xfffffc03) != 0)
|
||||
return reloc_overflow(module,
|
||||
"R_AVR32_9UW_PCREL",
|
||||
relocation);
|
||||
value = get_u16(location);
|
||||
value = ((value & 0xf80f)
|
||||
| ((relocation & 0x1fc) << 2));
|
||||
put_u16(location, value);
|
||||
break;
|
||||
case R_AVR32_GOTPC:
|
||||
/*
|
||||
* R6 = PC - (PC - GOT)
|
||||
*
|
||||
* At this point, relocation contains the
|
||||
* value of PC. Just subtract the value of
|
||||
* GOT, and we're done.
|
||||
*/
|
||||
pr_debug("GOTPC: PC=0x%lx, got_offset=0x%lx, core=0x%p\n",
|
||||
relocation, module->arch.got_offset,
|
||||
module->module_core);
|
||||
relocation -= ((unsigned long)module->module_core
|
||||
+ module->arch.got_offset);
|
||||
*location = relocation;
|
||||
break;
|
||||
case R_AVR32_GOT18SW:
|
||||
if ((relocation & 0xfffe0003) != 0
|
||||
&& (relocation & 0xfffc0003) != 0xffff0000)
|
||||
return reloc_overflow(module, "R_AVR32_GOT18SW",
|
||||
relocation);
|
||||
relocation >>= 2;
|
||||
/* fall through */
|
||||
case R_AVR32_GOT16S:
|
||||
if ((relocation & 0xffff8000) != 0
|
||||
&& (relocation & 0xffff0000) != 0xffff0000)
|
||||
return reloc_overflow(module, "R_AVR32_GOT16S",
|
||||
relocation);
|
||||
pr_debug("GOT reloc @ 0x%lx -> %lu\n",
|
||||
rel->r_offset, relocation);
|
||||
value = *location;
|
||||
value = ((value & 0xffff0000)
|
||||
| (relocation & 0xffff));
|
||||
*location = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "module %s: Unknown relocation: %u\n",
|
||||
module->name, ELF32_R_TYPE(rel->r_info));
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab,
|
||||
unsigned int symindex, unsigned int relindex,
|
||||
struct module *module)
|
||||
{
|
||||
printk(KERN_ERR "module %s: REL relocations are not supported\n",
|
||||
module->name);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
||||
struct module *module)
|
||||
{
|
||||
vfree(module->arch.syminfo);
|
||||
module->arch.syminfo = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void module_arch_cleanup(struct module *module)
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/ocd.h>
|
||||
|
||||
void (*pm_power_off)(void) = NULL;
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
/*
|
||||
* This file handles the architecture-dependent parts of process handling..
|
||||
*/
|
||||
|
||||
void cpu_idle(void)
|
||||
{
|
||||
/* endless idle loop with no priority at all */
|
||||
while (1) {
|
||||
/* TODO: Enter sleep mode */
|
||||
while (!need_resched())
|
||||
cpu_relax();
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
preempt_disable();
|
||||
}
|
||||
}
|
||||
|
||||
void machine_halt(void)
|
||||
{
|
||||
}
|
||||
|
||||
void machine_power_off(void)
|
||||
{
|
||||
}
|
||||
|
||||
void machine_restart(char *cmd)
|
||||
{
|
||||
__mtdr(DBGREG_DC, DC_DBE);
|
||||
__mtdr(DBGREG_DC, DC_RES);
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* PC is actually discarded when returning from a system call -- the
|
||||
* return address must be stored in LR. This function will make sure
|
||||
* LR points to do_exit before starting the thread.
|
||||
*
|
||||
* Also, when returning from fork(), r12 is 0, so we must copy the
|
||||
* argument as well.
|
||||
*
|
||||
* r0 : The argument to the main thread function
|
||||
* r1 : The address of do_exit
|
||||
* r2 : The address of the main thread function
|
||||
*/
|
||||
asmlinkage extern void kernel_thread_helper(void);
|
||||
__asm__(" .type kernel_thread_helper, @function\n"
|
||||
"kernel_thread_helper:\n"
|
||||
" mov r12, r0\n"
|
||||
" mov lr, r2\n"
|
||||
" mov pc, r1\n"
|
||||
" .size kernel_thread_helper, . - kernel_thread_helper");
|
||||
|
||||
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.r0 = (unsigned long)arg;
|
||||
regs.r1 = (unsigned long)fn;
|
||||
regs.r2 = (unsigned long)do_exit;
|
||||
regs.lr = (unsigned long)kernel_thread_helper;
|
||||
regs.pc = (unsigned long)kernel_thread_helper;
|
||||
regs.sr = MODE_SUPERVISOR;
|
||||
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
|
||||
0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc
|
||||
*/
|
||||
void exit_thread(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
void flush_thread(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *dead_task)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
static const char *cpu_modes[] = {
|
||||
"Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
|
||||
"Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
|
||||
};
|
||||
|
||||
void show_regs(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long sp = regs->sp;
|
||||
unsigned long lr = regs->lr;
|
||||
unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
|
||||
|
||||
if (!user_mode(regs))
|
||||
sp = (unsigned long)regs + FRAME_SIZE_FULL;
|
||||
|
||||
print_symbol("PC is at %s\n", instruction_pointer(regs));
|
||||
print_symbol("LR is at %s\n", lr);
|
||||
printk("pc : [<%08lx>] lr : [<%08lx>] %s\n"
|
||||
"sp : %08lx r12: %08lx r11: %08lx\n",
|
||||
instruction_pointer(regs),
|
||||
lr, print_tainted(), sp, regs->r12, regs->r11);
|
||||
printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
|
||||
regs->r10, regs->r9, regs->r8);
|
||||
printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
|
||||
regs->r7, regs->r6, regs->r5, regs->r4);
|
||||
printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
|
||||
regs->r3, regs->r2, regs->r1, regs->r0);
|
||||
printk("Flags: %c%c%c%c%c\n",
|
||||
regs->sr & SR_Q ? 'Q' : 'q',
|
||||
regs->sr & SR_V ? 'V' : 'v',
|
||||
regs->sr & SR_N ? 'N' : 'n',
|
||||
regs->sr & SR_Z ? 'Z' : 'z',
|
||||
regs->sr & SR_C ? 'C' : 'c');
|
||||
printk("Mode bits: %c%c%c%c%c%c%c%c%c\n",
|
||||
regs->sr & SR_H ? 'H' : 'h',
|
||||
regs->sr & SR_R ? 'R' : 'r',
|
||||
regs->sr & SR_J ? 'J' : 'j',
|
||||
regs->sr & SR_EM ? 'E' : 'e',
|
||||
regs->sr & SR_I3M ? '3' : '.',
|
||||
regs->sr & SR_I2M ? '2' : '.',
|
||||
regs->sr & SR_I1M ? '1' : '.',
|
||||
regs->sr & SR_I0M ? '0' : '.',
|
||||
regs->sr & SR_GM ? 'G' : 'g');
|
||||
printk("CPU Mode: %s\n", cpu_modes[mode]);
|
||||
|
||||
show_trace(NULL, (unsigned long *)sp, regs);
|
||||
}
|
||||
EXPORT_SYMBOL(show_regs);
|
||||
|
||||
/* Fill in the fpu structure for a core dump. This is easy -- we don't have any */
|
||||
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
|
||||
{
|
||||
/* Not valid */
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage void ret_from_fork(void);
|
||||
|
||||
int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
|
||||
unsigned long unused,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *childregs;
|
||||
|
||||
childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)p->thread_info)) - 1;
|
||||
*childregs = *regs;
|
||||
|
||||
if (user_mode(regs))
|
||||
childregs->sp = usp;
|
||||
else
|
||||
childregs->sp = (unsigned long)p->thread_info + THREAD_SIZE;
|
||||
|
||||
childregs->r12 = 0; /* Set return value for child */
|
||||
|
||||
p->thread.cpu_context.sr = MODE_SUPERVISOR | SR_GM;
|
||||
p->thread.cpu_context.ksp = (unsigned long)childregs;
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* r12-r8 are dummy parameters to force the compiler to use the stack */
|
||||
asmlinkage int sys_fork(struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||
unsigned long parent_tidptr,
|
||||
unsigned long child_tidptr, struct pt_regs *regs)
|
||||
{
|
||||
if (!newsp)
|
||||
newsp = regs->sp;
|
||||
return do_fork(clone_flags, newsp, regs, 0,
|
||||
(int __user *)parent_tidptr,
|
||||
(int __user *)child_tidptr);
|
||||
}
|
||||
|
||||
asmlinkage int sys_vfork(struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs,
|
||||
0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int sys_execve(char __user *ufilename, char __user *__user *uargv,
|
||||
char __user *__user *uenvp, struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
char *filename;
|
||||
|
||||
filename = getname(ufilename);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
|
||||
error = do_execve(filename, uargv, uenvp, regs);
|
||||
if (error == 0)
|
||||
current->ptrace &= ~PT_DTRACE;
|
||||
putname(filename);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function is supposed to answer the question "who called
|
||||
* schedule()?"
|
||||
*/
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
unsigned long pc;
|
||||
unsigned long stack_page;
|
||||
|
||||
if (!p || p == current || p->state == TASK_RUNNING)
|
||||
return 0;
|
||||
|
||||
stack_page = (unsigned long)p->thread_info;
|
||||
BUG_ON(!stack_page);
|
||||
|
||||
/*
|
||||
* The stored value of PC is either the address right after
|
||||
* the call to __switch_to() or ret_from_fork.
|
||||
*/
|
||||
pc = thread_saved_pc(p);
|
||||
if (in_sched_functions(pc)) {
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
unsigned long fp = p->thread.cpu_context.r7;
|
||||
BUG_ON(fp < stack_page || fp > (THREAD_SIZE + stack_page));
|
||||
pc = *(unsigned long *)fp;
|
||||
#else
|
||||
/*
|
||||
* We depend on the frame size of schedule here, which
|
||||
* is actually quite ugly. It might be possible to
|
||||
* determine the frame size automatically at build
|
||||
* time by doing this:
|
||||
* - compile sched.c
|
||||
* - disassemble the resulting sched.o
|
||||
* - look for 'sub sp,??' shortly after '<schedule>:'
|
||||
*/
|
||||
unsigned long sp = p->thread.cpu_context.ksp + 16;
|
||||
BUG_ON(sp < stack_page || sp > (THREAD_SIZE + stack_page));
|
||||
pc = *(unsigned long *)sp;
|
||||
#endif
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#undef DEBUG
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <asm/traps.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ocd.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/kdebug.h>
|
||||
|
||||
static struct pt_regs *get_user_regs(struct task_struct *tsk)
|
||||
{
|
||||
return (struct pt_regs *)((unsigned long) tsk->thread_info +
|
||||
THREAD_SIZE - sizeof(struct pt_regs));
|
||||
}
|
||||
|
||||
static void ptrace_single_step(struct task_struct *tsk)
|
||||
{
|
||||
pr_debug("ptrace_single_step: pid=%u, SR=0x%08lx\n",
|
||||
tsk->pid, tsk->thread.cpu_context.sr);
|
||||
if (!(tsk->thread.cpu_context.sr & SR_D)) {
|
||||
/*
|
||||
* Set a breakpoint at the current pc to force the
|
||||
* process into debug mode. The syscall/exception
|
||||
* exit code will set a breakpoint at the return
|
||||
* address when this flag is set.
|
||||
*/
|
||||
pr_debug("ptrace_single_step: Setting TIF_BREAKPOINT\n");
|
||||
set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
|
||||
}
|
||||
|
||||
/* The monitor code will do the actual step for us */
|
||||
set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching
|
||||
*
|
||||
* Make sure any single step bits, etc. are not set
|
||||
*/
|
||||
void ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
clear_tsk_thread_flag(child, TIF_SINGLE_STEP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle hitting a breakpoint
|
||||
*/
|
||||
static void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
info.si_addr = (void __user *)instruction_pointer(regs);
|
||||
|
||||
pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
|
||||
tsk->pid, info.si_addr);
|
||||
force_sig_info(SIGTRAP, &info, tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the word at offset "offset" into the task's "struct user". We
|
||||
* actually access the pt_regs struct stored on the kernel stack.
|
||||
*/
|
||||
static int ptrace_read_user(struct task_struct *tsk, unsigned long offset,
|
||||
unsigned long __user *data)
|
||||
{
|
||||
unsigned long *regs;
|
||||
unsigned long value;
|
||||
|
||||
pr_debug("ptrace_read_user(%p, %#lx, %p)\n",
|
||||
tsk, offset, data);
|
||||
|
||||
if (offset & 3 || offset >= sizeof(struct user)) {
|
||||
printk("ptrace_read_user: invalid offset 0x%08lx\n", offset);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
regs = (unsigned long *)get_user_regs(tsk);
|
||||
|
||||
value = 0;
|
||||
if (offset < sizeof(struct pt_regs))
|
||||
value = regs[offset / sizeof(regs[0])];
|
||||
|
||||
return put_user(value, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the word "value" to offset "offset" into the task's "struct
|
||||
* user". We actually access the pt_regs struct stored on the kernel
|
||||
* stack.
|
||||
*/
|
||||
static int ptrace_write_user(struct task_struct *tsk, unsigned long offset,
|
||||
unsigned long value)
|
||||
{
|
||||
unsigned long *regs;
|
||||
|
||||
if (offset & 3 || offset >= sizeof(struct user)) {
|
||||
printk("ptrace_write_user: invalid offset 0x%08lx\n", offset);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (offset >= sizeof(struct pt_regs))
|
||||
return 0;
|
||||
|
||||
regs = (unsigned long *)get_user_regs(tsk);
|
||||
regs[offset / sizeof(regs[0])] = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
|
||||
{
|
||||
struct pt_regs *regs = get_user_regs(tsk);
|
||||
|
||||
return copy_to_user(uregs, regs, sizeof(*regs)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs)
|
||||
{
|
||||
struct pt_regs newregs;
|
||||
int ret;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&newregs, uregs, sizeof(newregs)) == 0) {
|
||||
struct pt_regs *regs = get_user_regs(tsk);
|
||||
|
||||
ret = -EINVAL;
|
||||
if (valid_user_regs(&newregs)) {
|
||||
*regs = newregs;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||
{
|
||||
unsigned long tmp;
|
||||
int ret;
|
||||
|
||||
pr_debug("arch_ptrace(%ld, %ld, %#lx, %#lx)\n",
|
||||
request, child->pid, addr, data);
|
||||
|
||||
pr_debug("ptrace: Enabling monitor mode...\n");
|
||||
__mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE);
|
||||
|
||||
switch (request) {
|
||||
/* Read the word at location addr in the child process */
|
||||
case PTRACE_PEEKTEXT:
|
||||
case PTRACE_PEEKDATA:
|
||||
ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
|
||||
if (ret == sizeof(tmp))
|
||||
ret = put_user(tmp, (unsigned long __user *)data);
|
||||
else
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
||||
case PTRACE_PEEKUSR:
|
||||
ret = ptrace_read_user(child, addr,
|
||||
(unsigned long __user *)data);
|
||||
break;
|
||||
|
||||
/* Write the word in data at location addr */
|
||||
case PTRACE_POKETEXT:
|
||||
case PTRACE_POKEDATA:
|
||||
ret = access_process_vm(child, addr, &data, sizeof(data), 1);
|
||||
if (ret == sizeof(data))
|
||||
ret = 0;
|
||||
else
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
||||
case PTRACE_POKEUSR:
|
||||
ret = ptrace_write_user(child, addr, data);
|
||||
break;
|
||||
|
||||
/* continue and stop at next (return from) syscall */
|
||||
case PTRACE_SYSCALL:
|
||||
/* restart after signal */
|
||||
case PTRACE_CONT:
|
||||
ret = -EIO;
|
||||
if (!valid_signal(data))
|
||||
break;
|
||||
if (request == PTRACE_SYSCALL)
|
||||
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
else
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
child->exit_code = data;
|
||||
/* XXX: Are we sure no breakpoints are active here? */
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
/*
|
||||
* Make the child exit. Best I can do is send it a
|
||||
* SIGKILL. Perhaps it should be put in the status that it
|
||||
* wants to exit.
|
||||
*/
|
||||
case PTRACE_KILL:
|
||||
ret = 0;
|
||||
if (child->exit_state == EXIT_ZOMBIE)
|
||||
break;
|
||||
child->exit_code = SIGKILL;
|
||||
wake_up_process(child);
|
||||
break;
|
||||
|
||||
/*
|
||||
* execute single instruction.
|
||||
*/
|
||||
case PTRACE_SINGLESTEP:
|
||||
ret = -EIO;
|
||||
if (!valid_signal(data))
|
||||
break;
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
ptrace_single_step(child);
|
||||
child->exit_code = data;
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
/* Detach a process that was attached */
|
||||
case PTRACE_DETACH:
|
||||
ret = ptrace_detach(child, data);
|
||||
break;
|
||||
|
||||
case PTRACE_GETREGS:
|
||||
ret = ptrace_getregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETREGS:
|
||||
ret = ptrace_setregs(child, (const void __user *)data);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
|
||||
pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC));
|
||||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage void syscall_trace(void)
|
||||
{
|
||||
pr_debug("syscall_trace called\n");
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return;
|
||||
|
||||
pr_debug("syscall_trace: notifying parent\n");
|
||||
/* The 0x80 provides a way for the tracing parent to
|
||||
* distinguish between a syscall stop and SIGTRAP delivery */
|
||||
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
|
||||
? 0x80 : 0));
|
||||
|
||||
/*
|
||||
* this isn't the same as continuing with a signal, but it
|
||||
* will do for normal use. strace only continues with a
|
||||
* signal if the stopping signal is not SIGTRAP. -brl
|
||||
*/
|
||||
if (current->exit_code) {
|
||||
pr_debug("syscall_trace: sending signal %d to PID %u\n",
|
||||
current->exit_code, current->pid);
|
||||
send_sig(current->exit_code, current, 1);
|
||||
current->exit_code = 0;
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage void do_debug_priv(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long dc, ds;
|
||||
unsigned long die_val;
|
||||
|
||||
ds = __mfdr(DBGREG_DS);
|
||||
|
||||
pr_debug("do_debug_priv: pc = %08lx, ds = %08lx\n", regs->pc, ds);
|
||||
|
||||
if (ds & DS_SSS)
|
||||
die_val = DIE_SSTEP;
|
||||
else
|
||||
die_val = DIE_BREAKPOINT;
|
||||
|
||||
if (notify_die(die_val, regs, 0, SIGTRAP) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
if (likely(ds & DS_SSS)) {
|
||||
extern void itlb_miss(void);
|
||||
extern void tlb_miss_common(void);
|
||||
struct thread_info *ti;
|
||||
|
||||
dc = __mfdr(DBGREG_DC);
|
||||
dc &= ~DC_SS;
|
||||
__mtdr(DBGREG_DC, dc);
|
||||
|
||||
ti = current_thread_info();
|
||||
ti->flags |= _TIF_BREAKPOINT;
|
||||
|
||||
/* The TLB miss handlers don't check thread flags */
|
||||
if ((regs->pc >= (unsigned long)&itlb_miss)
|
||||
&& (regs->pc <= (unsigned long)&tlb_miss_common)) {
|
||||
__mtdr(DBGREG_BWA2A, sysreg_read(RAR_EX));
|
||||
__mtdr(DBGREG_BWC2A, 0x40000001 | (get_asid() << 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're running in supervisor mode, the breakpoint
|
||||
* will take us where we want directly, no need to
|
||||
* single step.
|
||||
*/
|
||||
if ((regs->sr & MODE_MASK) != MODE_SUPERVISOR)
|
||||
ti->flags |= TIF_SINGLE_STEP;
|
||||
} else {
|
||||
panic("Unable to handle debug trap at pc = %08lx\n",
|
||||
regs->pc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle breakpoints, single steps and other debuggy things. To keep
|
||||
* things simple initially, we run with interrupts and exceptions
|
||||
* disabled all the time.
|
||||
*/
|
||||
asmlinkage void do_debug(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long dc, ds;
|
||||
|
||||
ds = __mfdr(DBGREG_DS);
|
||||
pr_debug("do_debug: pc = %08lx, ds = %08lx\n", regs->pc, ds);
|
||||
|
||||
if (test_thread_flag(TIF_BREAKPOINT)) {
|
||||
pr_debug("TIF_BREAKPOINT set\n");
|
||||
/* We're taking care of it */
|
||||
clear_thread_flag(TIF_BREAKPOINT);
|
||||
__mtdr(DBGREG_BWC2A, 0);
|
||||
}
|
||||
|
||||
if (test_thread_flag(TIF_SINGLE_STEP)) {
|
||||
pr_debug("TIF_SINGLE_STEP set, ds = 0x%08lx\n", ds);
|
||||
if (ds & DS_SSS) {
|
||||
dc = __mfdr(DBGREG_DC);
|
||||
dc &= ~DC_SS;
|
||||
__mtdr(DBGREG_DC, dc);
|
||||
|
||||
clear_thread_flag(TIF_SINGLE_STEP);
|
||||
ptrace_break(current, regs);
|
||||
}
|
||||
} else {
|
||||
/* regular breakpoint */
|
||||
ptrace_break(current, regs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* AVR32 sempahore implementation.
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* Based on linux/arch/i386/kernel/semaphore.c
|
||||
* Copyright (C) 1999 Linus Torvalds
|
||||
*
|
||||
* 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/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
/*
|
||||
* Semaphores are implemented using a two-way counter:
|
||||
* The "count" variable is decremented for each process
|
||||
* that tries to acquire the semaphore, while the "sleeping"
|
||||
* variable is a count of such acquires.
|
||||
*
|
||||
* Notably, the inline "up()" and "down()" functions can
|
||||
* efficiently test if they need to do any extra work (up
|
||||
* needs to do something only if count was negative before
|
||||
* the increment operation.
|
||||
*
|
||||
* "sleeping" and the contention routine ordering is protected
|
||||
* by the spinlock in the semaphore's waitqueue head.
|
||||
*
|
||||
* Note that these functions are only called when there is
|
||||
* contention on the lock, and as such all this is the
|
||||
* "non-critical" part of the whole semaphore business. The
|
||||
* critical part is the inline stuff in <asm/semaphore.h>
|
||||
* where we want to avoid any extra jumps and calls.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Logic:
|
||||
* - only on a boundary condition do we need to care. When we go
|
||||
* from a negative count to a non-negative, we wake people up.
|
||||
* - when we go from a non-negative count to a negative do we
|
||||
* (a) synchronize with the "sleeper" count and (b) make sure
|
||||
* that we're on the wakeup list before we synchronize so that
|
||||
* we cannot lose wakeup events.
|
||||
*/
|
||||
|
||||
void __up(struct semaphore *sem)
|
||||
{
|
||||
wake_up(&sem->wait);
|
||||
}
|
||||
EXPORT_SYMBOL(__up);
|
||||
|
||||
void __sched __down(struct semaphore *sem)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
DECLARE_WAITQUEUE(wait, tsk);
|
||||
unsigned long flags;
|
||||
|
||||
tsk->state = TASK_UNINTERRUPTIBLE;
|
||||
spin_lock_irqsave(&sem->wait.lock, flags);
|
||||
add_wait_queue_exclusive_locked(&sem->wait, &wait);
|
||||
|
||||
sem->sleepers++;
|
||||
for (;;) {
|
||||
int sleepers = sem->sleepers;
|
||||
|
||||
/*
|
||||
* Add "everybody else" into it. They aren't
|
||||
* playing, because we own the spinlock in
|
||||
* the wait_queue_head.
|
||||
*/
|
||||
if (atomic_add_return(sleepers - 1, &sem->count) >= 0) {
|
||||
sem->sleepers = 0;
|
||||
break;
|
||||
}
|
||||
sem->sleepers = 1; /* us - see -1 above */
|
||||
spin_unlock_irqrestore(&sem->wait.lock, flags);
|
||||
|
||||
schedule();
|
||||
|
||||
spin_lock_irqsave(&sem->wait.lock, flags);
|
||||
tsk->state = TASK_UNINTERRUPTIBLE;
|
||||
}
|
||||
remove_wait_queue_locked(&sem->wait, &wait);
|
||||
wake_up_locked(&sem->wait);
|
||||
spin_unlock_irqrestore(&sem->wait.lock, flags);
|
||||
tsk->state = TASK_RUNNING;
|
||||
}
|
||||
EXPORT_SYMBOL(__down);
|
||||
|
||||
int __sched __down_interruptible(struct semaphore *sem)
|
||||
{
|
||||
int retval = 0;
|
||||
struct task_struct *tsk = current;
|
||||
DECLARE_WAITQUEUE(wait, tsk);
|
||||
unsigned long flags;
|
||||
|
||||
tsk->state = TASK_INTERRUPTIBLE;
|
||||
spin_lock_irqsave(&sem->wait.lock, flags);
|
||||
add_wait_queue_exclusive_locked(&sem->wait, &wait);
|
||||
|
||||
sem->sleepers++;
|
||||
for (;;) {
|
||||
int sleepers = sem->sleepers;
|
||||
|
||||
/*
|
||||
* With signals pending, this turns into the trylock
|
||||
* failure case - we won't be sleeping, and we can't
|
||||
* get the lock as it has contention. Just correct the
|
||||
* count and exit.
|
||||
*/
|
||||
if (signal_pending(current)) {
|
||||
retval = -EINTR;
|
||||
sem->sleepers = 0;
|
||||
atomic_add(sleepers, &sem->count);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add "everybody else" into it. They aren't
|
||||
* playing, because we own the spinlock in
|
||||
* the wait_queue_head.
|
||||
*/
|
||||
if (atomic_add_return(sleepers - 1, &sem->count) >= 0) {
|
||||
sem->sleepers = 0;
|
||||
break;
|
||||
}
|
||||
sem->sleepers = 1; /* us - see -1 above */
|
||||
spin_unlock_irqrestore(&sem->wait.lock, flags);
|
||||
|
||||
schedule();
|
||||
|
||||
spin_lock_irqsave(&sem->wait.lock, flags);
|
||||
tsk->state = TASK_INTERRUPTIBLE;
|
||||
}
|
||||
remove_wait_queue_locked(&sem->wait, &wait);
|
||||
wake_up_locked(&sem->wait);
|
||||
spin_unlock_irqrestore(&sem->wait.lock, flags);
|
||||
|
||||
tsk->state = TASK_RUNNING;
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(__down_interruptible);
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
#include <asm/arch/board.h>
|
||||
#include <asm/arch/init.h>
|
||||
|
||||
extern int root_mountflags;
|
||||
|
||||
/*
|
||||
* Bootloader-provided information about physical memory
|
||||
*/
|
||||
struct tag_mem_range *mem_phys;
|
||||
struct tag_mem_range *mem_reserved;
|
||||
struct tag_mem_range *mem_ramdisk;
|
||||
|
||||
/*
|
||||
* Initialize loops_per_jiffy as 5000000 (500MIPS).
|
||||
* Better make it too large than too small...
|
||||
*/
|
||||
struct avr32_cpuinfo boot_cpu_data = {
|
||||
.loops_per_jiffy = 5000000
|
||||
};
|
||||
EXPORT_SYMBOL(boot_cpu_data);
|
||||
|
||||
static char command_line[COMMAND_LINE_SIZE];
|
||||
|
||||
/*
|
||||
* Should be more than enough, but if you have a _really_ complex
|
||||
* setup, you might need to increase the size of this...
|
||||
*/
|
||||
static struct tag_mem_range __initdata mem_range_cache[32];
|
||||
static unsigned mem_range_next_free;
|
||||
|
||||
/*
|
||||
* Standard memory resources
|
||||
*/
|
||||
static struct resource mem_res[] = {
|
||||
{
|
||||
.name = "Kernel code",
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.flags = IORESOURCE_MEM
|
||||
},
|
||||
{
|
||||
.name = "Kernel data",
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define kernel_code mem_res[0]
|
||||
#define kernel_data mem_res[1]
|
||||
|
||||
/*
|
||||
* Early framebuffer allocation. Works as follows:
|
||||
* - If fbmem_size is zero, nothing will be allocated or reserved.
|
||||
* - If fbmem_start is zero when setup_bootmem() is called,
|
||||
* fbmem_size bytes will be allocated from the bootmem allocator.
|
||||
* - If fbmem_start is nonzero, an area of size fbmem_size will be
|
||||
* reserved at the physical address fbmem_start if necessary. If
|
||||
* the area isn't in a memory region known to the kernel, it will
|
||||
* be left alone.
|
||||
*
|
||||
* Board-specific code may use these variables to set up platform data
|
||||
* for the framebuffer driver if fbmem_size is nonzero.
|
||||
*/
|
||||
static unsigned long __initdata fbmem_start;
|
||||
static unsigned long __initdata fbmem_size;
|
||||
|
||||
/*
|
||||
* "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for
|
||||
* use as framebuffer.
|
||||
*
|
||||
* "fbmem=xxx[kKmM]@yyy[kKmM]" defines a memory region of size xxx and
|
||||
* starting at yyy to be reserved for use as framebuffer.
|
||||
*
|
||||
* The kernel won't verify that the memory region starting at yyy
|
||||
* actually contains usable RAM.
|
||||
*/
|
||||
static int __init early_parse_fbmem(char *p)
|
||||
{
|
||||
fbmem_size = memparse(p, &p);
|
||||
if (*p == '@')
|
||||
fbmem_start = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("fbmem", early_parse_fbmem);
|
||||
|
||||
static inline void __init resource_init(void)
|
||||
{
|
||||
struct tag_mem_range *region;
|
||||
|
||||
kernel_code.start = __pa(init_mm.start_code);
|
||||
kernel_code.end = __pa(init_mm.end_code - 1);
|
||||
kernel_data.start = __pa(init_mm.end_code);
|
||||
kernel_data.end = __pa(init_mm.brk - 1);
|
||||
|
||||
for (region = mem_phys; region; region = region->next) {
|
||||
struct resource *res;
|
||||
unsigned long phys_start, phys_end;
|
||||
|
||||
if (region->size == 0)
|
||||
continue;
|
||||
|
||||
phys_start = region->addr;
|
||||
phys_end = phys_start + region->size - 1;
|
||||
|
||||
res = alloc_bootmem_low(sizeof(*res));
|
||||
res->name = "System RAM";
|
||||
res->start = phys_start;
|
||||
res->end = phys_end;
|
||||
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
||||
|
||||
request_resource (&iomem_resource, res);
|
||||
|
||||
if (kernel_code.start >= res->start &&
|
||||
kernel_code.end <= res->end)
|
||||
request_resource (res, &kernel_code);
|
||||
if (kernel_data.start >= res->start &&
|
||||
kernel_data.end <= res->end)
|
||||
request_resource (res, &kernel_data);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init parse_tag_core(struct tag *tag)
|
||||
{
|
||||
if (tag->hdr.size > 2) {
|
||||
if ((tag->u.core.flags & 1) == 0)
|
||||
root_mountflags &= ~MS_RDONLY;
|
||||
ROOT_DEV = new_decode_dev(tag->u.core.rootdev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
__tagtable(ATAG_CORE, parse_tag_core);
|
||||
|
||||
static int __init parse_tag_mem_range(struct tag *tag,
|
||||
struct tag_mem_range **root)
|
||||
{
|
||||
struct tag_mem_range *cur, **pprev;
|
||||
struct tag_mem_range *new;
|
||||
|
||||
/*
|
||||
* Ignore zero-sized entries. If we're running standalone, the
|
||||
* SDRAM code may emit such entries if something goes
|
||||
* wrong...
|
||||
*/
|
||||
if (tag->u.mem_range.size == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Copy the data so the bootmem init code doesn't need to care
|
||||
* about it.
|
||||
*/
|
||||
if (mem_range_next_free >=
|
||||
(sizeof(mem_range_cache) / sizeof(mem_range_cache[0])))
|
||||
panic("Physical memory map too complex!\n");
|
||||
|
||||
new = &mem_range_cache[mem_range_next_free++];
|
||||
*new = tag->u.mem_range;
|
||||
|
||||
pprev = root;
|
||||
cur = *root;
|
||||
while (cur) {
|
||||
pprev = &cur->next;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
*pprev = new;
|
||||
new->next = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init parse_tag_mem(struct tag *tag)
|
||||
{
|
||||
return parse_tag_mem_range(tag, &mem_phys);
|
||||
}
|
||||
__tagtable(ATAG_MEM, parse_tag_mem);
|
||||
|
||||
static int __init parse_tag_cmdline(struct tag *tag)
|
||||
{
|
||||
strlcpy(saved_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
|
||||
|
||||
static int __init parse_tag_rdimg(struct tag *tag)
|
||||
{
|
||||
return parse_tag_mem_range(tag, &mem_ramdisk);
|
||||
}
|
||||
__tagtable(ATAG_RDIMG, parse_tag_rdimg);
|
||||
|
||||
static int __init parse_tag_clock(struct tag *tag)
|
||||
{
|
||||
/*
|
||||
* We'll figure out the clocks by peeking at the system
|
||||
* manager regs directly.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
__tagtable(ATAG_CLOCK, parse_tag_clock);
|
||||
|
||||
static int __init parse_tag_rsvd_mem(struct tag *tag)
|
||||
{
|
||||
return parse_tag_mem_range(tag, &mem_reserved);
|
||||
}
|
||||
__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem);
|
||||
|
||||
static int __init parse_tag_ethernet(struct tag *tag)
|
||||
{
|
||||
#if 0
|
||||
const struct platform_device *pdev;
|
||||
|
||||
/*
|
||||
* We really need a bus type that supports "classes"...this
|
||||
* will do for now (until we must handle other kinds of
|
||||
* ethernet controllers)
|
||||
*/
|
||||
pdev = platform_get_device("macb", tag->u.ethernet.mac_index);
|
||||
if (pdev && pdev->dev.platform_data) {
|
||||
struct eth_platform_data *data = pdev->dev.platform_data;
|
||||
|
||||
data->valid = 1;
|
||||
data->mii_phy_addr = tag->u.ethernet.mii_phy_addr;
|
||||
memcpy(data->hw_addr, tag->u.ethernet.hw_address,
|
||||
sizeof(data->hw_addr));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
__tagtable(ATAG_ETHERNET, parse_tag_ethernet);
|
||||
|
||||
/*
|
||||
* Scan the tag table for this tag, and call its parse function. The
|
||||
* tag table is built by the linker from all the __tagtable
|
||||
* declarations.
|
||||
*/
|
||||
static int __init parse_tag(struct tag *tag)
|
||||
{
|
||||
extern struct tagtable __tagtable_begin, __tagtable_end;
|
||||
struct tagtable *t;
|
||||
|
||||
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
|
||||
if (tag->hdr.tag == t->tag) {
|
||||
t->parse(tag);
|
||||
break;
|
||||
}
|
||||
|
||||
return t < &__tagtable_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all tags in the list we got from the boot loader
|
||||
*/
|
||||
static void __init parse_tags(struct tag *t)
|
||||
{
|
||||
for (; t->hdr.tag != ATAG_NONE; t = tag_next(t))
|
||||
if (!parse_tag(t))
|
||||
printk(KERN_WARNING
|
||||
"Ignoring unrecognised tag 0x%08x\n",
|
||||
t->hdr.tag);
|
||||
}
|
||||
|
||||
void __init setup_arch (char **cmdline_p)
|
||||
{
|
||||
struct clk *cpu_clk;
|
||||
|
||||
parse_tags(bootloader_tags);
|
||||
|
||||
setup_processor();
|
||||
setup_platform();
|
||||
|
||||
cpu_clk = clk_get(NULL, "cpu");
|
||||
if (IS_ERR(cpu_clk)) {
|
||||
printk(KERN_WARNING "Warning: Unable to get CPU clock\n");
|
||||
} else {
|
||||
unsigned long cpu_hz = clk_get_rate(cpu_clk);
|
||||
|
||||
/*
|
||||
* Well, duh, but it's probably a good idea to
|
||||
* increment the use count.
|
||||
*/
|
||||
clk_enable(cpu_clk);
|
||||
|
||||
boot_cpu_data.clk = cpu_clk;
|
||||
boot_cpu_data.loops_per_jiffy = cpu_hz * 4;
|
||||
printk("CPU: Running at %lu.%03lu MHz\n",
|
||||
((cpu_hz + 500) / 1000) / 1000,
|
||||
((cpu_hz + 500) / 1000) % 1000);
|
||||
}
|
||||
|
||||
init_mm.start_code = (unsigned long) &_text;
|
||||
init_mm.end_code = (unsigned long) &_etext;
|
||||
init_mm.end_data = (unsigned long) &_edata;
|
||||
init_mm.brk = (unsigned long) &_end;
|
||||
|
||||
strlcpy(command_line, saved_command_line, COMMAND_LINE_SIZE);
|
||||
*cmdline_p = command_line;
|
||||
parse_early_param();
|
||||
|
||||
setup_bootmem();
|
||||
|
||||
board_setup_fbmem(fbmem_start, fbmem_size);
|
||||
|
||||
#ifdef CONFIG_VT
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
|
||||
paging_init();
|
||||
|
||||
resource_init();
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* Based on linux/arch/sh/kernel/signal.c
|
||||
* Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* 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/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ucontext.h>
|
||||
|
||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||
|
||||
asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
return do_sigaltstack(uss, uoss, regs->sp);
|
||||
}
|
||||
|
||||
struct rt_sigframe
|
||||
{
|
||||
struct siginfo info;
|
||||
struct ucontext uc;
|
||||
unsigned long retcode;
|
||||
};
|
||||
|
||||
static int
|
||||
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#define COPY(x) err |= __get_user(regs->x, &sc->x)
|
||||
COPY(sr);
|
||||
COPY(pc);
|
||||
COPY(lr);
|
||||
COPY(sp);
|
||||
COPY(r12);
|
||||
COPY(r11);
|
||||
COPY(r10);
|
||||
COPY(r9);
|
||||
COPY(r8);
|
||||
COPY(r7);
|
||||
COPY(r6);
|
||||
COPY(r5);
|
||||
COPY(r4);
|
||||
COPY(r3);
|
||||
COPY(r2);
|
||||
COPY(r1);
|
||||
COPY(r0);
|
||||
#undef COPY
|
||||
|
||||
/*
|
||||
* Don't allow anyone to pretend they're running in supervisor
|
||||
* mode or something...
|
||||
*/
|
||||
err |= !valid_user_regs(regs);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
struct rt_sigframe __user *frame;
|
||||
sigset_t set;
|
||||
|
||||
frame = (struct rt_sigframe __user *)regs->sp;
|
||||
pr_debug("SIG return: frame = %p\n", frame);
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
|
||||
goto badframe;
|
||||
|
||||
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
||||
goto badframe;
|
||||
|
||||
sigdelsetmask(&set, ~_BLOCKABLE);
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
|
||||
goto badframe;
|
||||
|
||||
pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n",
|
||||
regs->pc, regs->lr, regs->sp);
|
||||
|
||||
return regs->r12;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#define COPY(x) err |= __put_user(regs->x, &sc->x)
|
||||
COPY(sr);
|
||||
COPY(pc);
|
||||
COPY(lr);
|
||||
COPY(sp);
|
||||
COPY(r12);
|
||||
COPY(r11);
|
||||
COPY(r10);
|
||||
COPY(r9);
|
||||
COPY(r8);
|
||||
COPY(r7);
|
||||
COPY(r6);
|
||||
COPY(r5);
|
||||
COPY(r4);
|
||||
COPY(r3);
|
||||
COPY(r2);
|
||||
COPY(r1);
|
||||
COPY(r0);
|
||||
#undef COPY
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void __user *
|
||||
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
|
||||
{
|
||||
unsigned long sp = regs->sp;
|
||||
|
||||
if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
|
||||
sp = current->sas_ss_sp + current->sas_ss_size;
|
||||
|
||||
return (void __user *)((sp - framesize) & ~3);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
||||
sigset_t *set, struct pt_regs *regs)
|
||||
{
|
||||
struct rt_sigframe __user *frame;
|
||||
int err = 0;
|
||||
|
||||
frame = get_sigframe(ka, regs, sizeof(*frame));
|
||||
err = -EFAULT;
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Set up the return code:
|
||||
*
|
||||
* mov r8, __NR_rt_sigreturn
|
||||
* scall
|
||||
*
|
||||
* Note: This will blow up since we're using a non-executable
|
||||
* stack. Better use SA_RESTORER.
|
||||
*/
|
||||
#if __NR_rt_sigreturn > 127
|
||||
# error __NR_rt_sigreturn must be < 127 to fit in a short mov
|
||||
#endif
|
||||
err = __put_user(0x3008d733 | (__NR_rt_sigreturn << 20),
|
||||
&frame->retcode);
|
||||
|
||||
err |= copy_siginfo_to_user(&frame->info, info);
|
||||
|
||||
/* Set up the ucontext */
|
||||
err |= __put_user(0, &frame->uc.uc_flags);
|
||||
err |= __put_user(NULL, &frame->uc.uc_link);
|
||||
err |= __put_user((void __user *)current->sas_ss_sp,
|
||||
&frame->uc.uc_stack.ss_sp);
|
||||
err |= __put_user(sas_ss_flags(regs->sp),
|
||||
&frame->uc.uc_stack.ss_flags);
|
||||
err |= __put_user(current->sas_ss_size,
|
||||
&frame->uc.uc_stack.ss_size);
|
||||
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs);
|
||||
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
regs->r12 = sig;
|
||||
regs->r11 = (unsigned long) &frame->info;
|
||||
regs->r10 = (unsigned long) &frame->uc;
|
||||
regs->sp = (unsigned long) frame;
|
||||
if (ka->sa.sa_flags & SA_RESTORER)
|
||||
regs->lr = (unsigned long)ka->sa.sa_restorer;
|
||||
else {
|
||||
printk(KERN_NOTICE "[%s:%d] did not set SA_RESTORER\n",
|
||||
current->comm, current->pid);
|
||||
regs->lr = (unsigned long) &frame->retcode;
|
||||
}
|
||||
|
||||
pr_debug("SIG deliver [%s:%d]: sig=%d sp=0x%lx pc=0x%lx->0x%p lr=0x%lx\n",
|
||||
current->comm, current->pid, sig, regs->sp,
|
||||
regs->pc, ka->sa.sa_handler, regs->lr);
|
||||
|
||||
regs->pc = (unsigned long) ka->sa.sa_handler;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void restart_syscall(struct pt_regs *regs)
|
||||
{
|
||||
if (regs->r12 == -ERESTART_RESTARTBLOCK)
|
||||
regs->r8 = __NR_restart_syscall;
|
||||
else
|
||||
regs->r12 = regs->r12_orig;
|
||||
regs->pc -= 2;
|
||||
}
|
||||
|
||||
static inline void
|
||||
handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
|
||||
sigset_t *oldset, struct pt_regs *regs, int syscall)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Set up the stack frame
|
||||
*/
|
||||
ret = setup_rt_frame(sig, ka, info, oldset, regs);
|
||||
|
||||
/*
|
||||
* Check that the resulting registers are sane
|
||||
*/
|
||||
ret |= !valid_user_regs(regs);
|
||||
|
||||
/*
|
||||
* Block the signal if we were unsuccessful.
|
||||
*/
|
||||
if (ret != 0 || !(ka->sa.sa_flags & SA_NODEFER)) {
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
sigorsets(¤t->blocked, ¤t->blocked,
|
||||
&ka->sa.sa_mask);
|
||||
sigaddset(¤t->blocked, sig);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
force_sigsegv(sig, current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that 'init' is a special process: it doesn't get signals it
|
||||
* doesn't want to handle. Thus you cannot kill init even with a
|
||||
* SIGKILL even by mistake.
|
||||
*/
|
||||
int do_signal(struct pt_regs *regs, sigset_t *oldset, int syscall)
|
||||
{
|
||||
siginfo_t info;
|
||||
int signr;
|
||||
struct k_sigaction ka;
|
||||
|
||||
/*
|
||||
* We want the common case to go fast, which is why we may in
|
||||
* certain cases get here from kernel mode. Just return
|
||||
* without doing anything if so.
|
||||
*/
|
||||
if (!user_mode(regs))
|
||||
return 0;
|
||||
|
||||
if (try_to_freeze()) {
|
||||
signr = 0;
|
||||
if (!signal_pending(current))
|
||||
goto no_signal;
|
||||
}
|
||||
|
||||
if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
||||
oldset = ¤t->saved_sigmask;
|
||||
else if (!oldset)
|
||||
oldset = ¤t->blocked;
|
||||
|
||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||
no_signal:
|
||||
if (syscall) {
|
||||
switch (regs->r12) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
case -ERESTARTNOHAND:
|
||||
if (signr > 0) {
|
||||
regs->r12 = -EINTR;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case -ERESTARTSYS:
|
||||
if (signr > 0 && !(ka.sa.sa_flags & SA_RESTART)) {
|
||||
regs->r12 = -EINTR;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case -ERESTARTNOINTR:
|
||||
restart_syscall(regs);
|
||||
}
|
||||
}
|
||||
|
||||
if (signr == 0) {
|
||||
/* No signal to deliver -- put the saved sigmask back */
|
||||
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
|
||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
||||
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
handle_signal(signr, &ka, &info, oldset, regs, syscall);
|
||||
return 1;
|
||||
}
|
||||
|
||||
asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
|
||||
{
|
||||
int syscall = 0;
|
||||
|
||||
if ((sysreg_read(SR) & MODE_MASK) == MODE_SUPERVISOR)
|
||||
syscall = 1;
|
||||
|
||||
if (ti->flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
|
||||
do_signal(regs, ¤t->blocked, syscall);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/sysreg.h>
|
||||
|
||||
.text
|
||||
.global __switch_to
|
||||
.type __switch_to, @function
|
||||
|
||||
/* Switch thread context from "prev" to "next", returning "last"
|
||||
* r12 : prev
|
||||
* r11 : &prev->thread + 1
|
||||
* r10 : &next->thread
|
||||
*/
|
||||
__switch_to:
|
||||
stm --r11, r0,r1,r2,r3,r4,r5,r6,r7,sp,lr
|
||||
mfsr r9, SYSREG_SR
|
||||
st.w --r11, r9
|
||||
ld.w r8, r10++
|
||||
/*
|
||||
* schedule() may have been called from a mode with a different
|
||||
* set of registers. Make sure we don't lose anything here.
|
||||
*/
|
||||
pushm r10,r12
|
||||
mtsr SYSREG_SR, r8
|
||||
frs /* flush the return stack */
|
||||
sub pc, -2 /* flush the pipeline */
|
||||
popm r10,r12
|
||||
ldm r10++, r0,r1,r2,r3,r4,r5,r6,r7,sp,pc
|
||||
.size __switch_to, . - __switch_to
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/mman.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
asmlinkage int sys_pipe(unsigned long __user *filedes)
|
||||
{
|
||||
int fd[2];
|
||||
int error;
|
||||
|
||||
error = do_pipe(fd);
|
||||
if (!error) {
|
||||
if (copy_to_user(filedes, fd, sizeof(fd)))
|
||||
error = -EFAULT;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, off_t offset)
|
||||
{
|
||||
int error = -EBADF;
|
||||
struct file *file = NULL;
|
||||
|
||||
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
|
||||
if (!(flags & MAP_ANONYMOUS)) {
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
return error;
|
||||
}
|
||||
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
error = do_mmap_pgoff(file, addr, len, prot, flags, offset);
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
|
||||
if (file)
|
||||
fput(file);
|
||||
return error;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Stubs for syscalls that require access to pt_regs or that take more
|
||||
* than five parameters.
|
||||
*/
|
||||
|
||||
#define ARG6 r3
|
||||
|
||||
.text
|
||||
.global __sys_rt_sigsuspend
|
||||
.type __sys_rt_sigsuspend,@function
|
||||
__sys_rt_sigsuspend:
|
||||
mov r10, sp
|
||||
rjmp sys_rt_sigsuspend
|
||||
|
||||
.global __sys_sigaltstack
|
||||
.type __sys_sigaltstack,@function
|
||||
__sys_sigaltstack:
|
||||
mov r10, sp
|
||||
rjmp sys_sigaltstack
|
||||
|
||||
.global __sys_rt_sigreturn
|
||||
.type __sys_rt_sigreturn,@function
|
||||
__sys_rt_sigreturn:
|
||||
mov r12, sp
|
||||
rjmp sys_rt_sigreturn
|
||||
|
||||
.global __sys_fork
|
||||
.type __sys_fork,@function
|
||||
__sys_fork:
|
||||
mov r12, sp
|
||||
rjmp sys_fork
|
||||
|
||||
.global __sys_clone
|
||||
.type __sys_clone,@function
|
||||
__sys_clone:
|
||||
mov r8, sp
|
||||
rjmp sys_clone
|
||||
|
||||
.global __sys_vfork
|
||||
.type __sys_vfork,@function
|
||||
__sys_vfork:
|
||||
mov r12, sp
|
||||
rjmp sys_vfork
|
||||
|
||||
.global __sys_execve
|
||||
.type __sys_execve,@function
|
||||
__sys_execve:
|
||||
mov r9, sp
|
||||
rjmp sys_execve
|
||||
|
||||
.global __sys_mmap2
|
||||
.type __sys_mmap2,@function
|
||||
__sys_mmap2:
|
||||
pushm lr
|
||||
st.w --sp, ARG6
|
||||
rcall sys_mmap2
|
||||
sub sp, -4
|
||||
popm pc
|
||||
|
||||
.global __sys_sendto
|
||||
.type __sys_sendto,@function
|
||||
__sys_sendto:
|
||||
pushm lr
|
||||
st.w --sp, ARG6
|
||||
rcall sys_sendto
|
||||
sub sp, -4
|
||||
popm pc
|
||||
|
||||
.global __sys_recvfrom
|
||||
.type __sys_recvfrom,@function
|
||||
__sys_recvfrom:
|
||||
pushm lr
|
||||
st.w --sp, ARG6
|
||||
rcall sys_recvfrom
|
||||
sub sp, -4
|
||||
popm pc
|
||||
|
||||
.global __sys_pselect6
|
||||
.type __sys_pselect6,@function
|
||||
__sys_pselect6:
|
||||
pushm lr
|
||||
st.w --sp, ARG6
|
||||
rcall sys_pselect6
|
||||
sub sp, -4
|
||||
popm pc
|
||||
|
||||
.global __sys_splice
|
||||
.type __sys_splice,@function
|
||||
__sys_splice:
|
||||
pushm lr
|
||||
st.w --sp, ARG6
|
||||
rcall sys_splice
|
||||
sub sp, -4
|
||||
popm pc
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* AVR32 system call table
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_NFSD) && !defined(CONFIG_NFSD_MODULE)
|
||||
#define sys_nfsservctl sys_ni_syscall
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SYSV_IPC)
|
||||
# define sys_ipc sys_ni_syscall
|
||||
#endif
|
||||
|
||||
.section .rodata,"a",@progbits
|
||||
.type sys_call_table,@object
|
||||
.global sys_call_table
|
||||
.align 2
|
||||
sys_call_table:
|
||||
.long sys_restart_syscall
|
||||
.long sys_exit
|
||||
.long __sys_fork
|
||||
.long sys_read
|
||||
.long sys_write
|
||||
.long sys_open /* 5 */
|
||||
.long sys_close
|
||||
.long sys_umask
|
||||
.long sys_creat
|
||||
.long sys_link
|
||||
.long sys_unlink /* 10 */
|
||||
.long __sys_execve
|
||||
.long sys_chdir
|
||||
.long sys_time
|
||||
.long sys_mknod
|
||||
.long sys_chmod /* 15 */
|
||||
.long sys_chown
|
||||
.long sys_lchown
|
||||
.long sys_lseek
|
||||
.long sys_llseek
|
||||
.long sys_getpid /* 20 */
|
||||
.long sys_mount
|
||||
.long sys_umount
|
||||
.long sys_setuid
|
||||
.long sys_getuid
|
||||
.long sys_stime /* 25 */
|
||||
.long sys_ptrace
|
||||
.long sys_alarm
|
||||
.long sys_pause
|
||||
.long sys_utime
|
||||
.long sys_newstat /* 30 */
|
||||
.long sys_newfstat
|
||||
.long sys_newlstat
|
||||
.long sys_access
|
||||
.long sys_chroot
|
||||
.long sys_sync /* 35 */
|
||||
.long sys_fsync
|
||||
.long sys_kill
|
||||
.long sys_rename
|
||||
.long sys_mkdir
|
||||
.long sys_rmdir /* 40 */
|
||||
.long sys_dup
|
||||
.long sys_pipe
|
||||
.long sys_times
|
||||
.long __sys_clone
|
||||
.long sys_brk /* 45 */
|
||||
.long sys_setgid
|
||||
.long sys_getgid
|
||||
.long sys_getcwd
|
||||
.long sys_geteuid
|
||||
.long sys_getegid /* 50 */
|
||||
.long sys_acct
|
||||
.long sys_setfsuid
|
||||
.long sys_setfsgid
|
||||
.long sys_ioctl
|
||||
.long sys_fcntl /* 55 */
|
||||
.long sys_setpgid
|
||||
.long sys_mremap
|
||||
.long sys_setresuid
|
||||
.long sys_getresuid
|
||||
.long sys_setreuid /* 60 */
|
||||
.long sys_setregid
|
||||
.long sys_ustat
|
||||
.long sys_dup2
|
||||
.long sys_getppid
|
||||
.long sys_getpgrp /* 65 */
|
||||
.long sys_setsid
|
||||
.long sys_rt_sigaction
|
||||
.long __sys_rt_sigreturn
|
||||
.long sys_rt_sigprocmask
|
||||
.long sys_rt_sigpending /* 70 */
|
||||
.long sys_rt_sigtimedwait
|
||||
.long sys_rt_sigqueueinfo
|
||||
.long __sys_rt_sigsuspend
|
||||
.long sys_sethostname
|
||||
.long sys_setrlimit /* 75 */
|
||||
.long sys_getrlimit
|
||||
.long sys_getrusage
|
||||
.long sys_gettimeofday
|
||||
.long sys_settimeofday
|
||||
.long sys_getgroups /* 80 */
|
||||
.long sys_setgroups
|
||||
.long sys_select
|
||||
.long sys_symlink
|
||||
.long sys_fchdir
|
||||
.long sys_readlink /* 85 */
|
||||
.long sys_pread64
|
||||
.long sys_pwrite64
|
||||
.long sys_swapon
|
||||
.long sys_reboot
|
||||
.long __sys_mmap2 /* 90 */
|
||||
.long sys_munmap
|
||||
.long sys_truncate
|
||||
.long sys_ftruncate
|
||||
.long sys_fchmod
|
||||
.long sys_fchown /* 95 */
|
||||
.long sys_getpriority
|
||||
.long sys_setpriority
|
||||
.long sys_wait4
|
||||
.long sys_statfs
|
||||
.long sys_fstatfs /* 100 */
|
||||
.long sys_vhangup
|
||||
.long __sys_sigaltstack
|
||||
.long sys_syslog
|
||||
.long sys_setitimer
|
||||
.long sys_getitimer /* 105 */
|
||||
.long sys_swapoff
|
||||
.long sys_sysinfo
|
||||
.long sys_ipc
|
||||
.long sys_sendfile
|
||||
.long sys_setdomainname /* 110 */
|
||||
.long sys_newuname
|
||||
.long sys_adjtimex
|
||||
.long sys_mprotect
|
||||
.long __sys_vfork
|
||||
.long sys_init_module /* 115 */
|
||||
.long sys_delete_module
|
||||
.long sys_quotactl
|
||||
.long sys_getpgid
|
||||
.long sys_bdflush
|
||||
.long sys_sysfs /* 120 */
|
||||
.long sys_personality
|
||||
.long sys_ni_syscall /* reserved for afs_syscall */
|
||||
.long sys_getdents
|
||||
.long sys_flock
|
||||
.long sys_msync /* 125 */
|
||||
.long sys_readv
|
||||
.long sys_writev
|
||||
.long sys_getsid
|
||||
.long sys_fdatasync
|
||||
.long sys_sysctl /* 130 */
|
||||
.long sys_mlock
|
||||
.long sys_munlock
|
||||
.long sys_mlockall
|
||||
.long sys_munlockall
|
||||
.long sys_sched_setparam /* 135 */
|
||||
.long sys_sched_getparam
|
||||
.long sys_sched_setscheduler
|
||||
.long sys_sched_getscheduler
|
||||
.long sys_sched_yield
|
||||
.long sys_sched_get_priority_max /* 140 */
|
||||
.long sys_sched_get_priority_min
|
||||
.long sys_sched_rr_get_interval
|
||||
.long sys_nanosleep
|
||||
.long sys_poll
|
||||
.long sys_nfsservctl /* 145 */
|
||||
.long sys_setresgid
|
||||
.long sys_getresgid
|
||||
.long sys_prctl
|
||||
.long sys_socket
|
||||
.long sys_bind /* 150 */
|
||||
.long sys_connect
|
||||
.long sys_listen
|
||||
.long sys_accept
|
||||
.long sys_getsockname
|
||||
.long sys_getpeername /* 155 */
|
||||
.long sys_socketpair
|
||||
.long sys_send
|
||||
.long sys_recv
|
||||
.long __sys_sendto
|
||||
.long __sys_recvfrom /* 160 */
|
||||
.long sys_shutdown
|
||||
.long sys_setsockopt
|
||||
.long sys_getsockopt
|
||||
.long sys_sendmsg
|
||||
.long sys_recvmsg /* 165 */
|
||||
.long sys_truncate64
|
||||
.long sys_ftruncate64
|
||||
.long sys_stat64
|
||||
.long sys_lstat64
|
||||
.long sys_fstat64 /* 170 */
|
||||
.long sys_pivot_root
|
||||
.long sys_mincore
|
||||
.long sys_madvise
|
||||
.long sys_getdents64
|
||||
.long sys_fcntl64 /* 175 */
|
||||
.long sys_gettid
|
||||
.long sys_readahead
|
||||
.long sys_setxattr
|
||||
.long sys_lsetxattr
|
||||
.long sys_fsetxattr /* 180 */
|
||||
.long sys_getxattr
|
||||
.long sys_lgetxattr
|
||||
.long sys_fgetxattr
|
||||
.long sys_listxattr
|
||||
.long sys_llistxattr /* 185 */
|
||||
.long sys_flistxattr
|
||||
.long sys_removexattr
|
||||
.long sys_lremovexattr
|
||||
.long sys_fremovexattr
|
||||
.long sys_tkill /* 190 */
|
||||
.long sys_sendfile64
|
||||
.long sys_futex
|
||||
.long sys_sched_setaffinity
|
||||
.long sys_sched_getaffinity
|
||||
.long sys_capget /* 195 */
|
||||
.long sys_capset
|
||||
.long sys_io_setup
|
||||
.long sys_io_destroy
|
||||
.long sys_io_getevents
|
||||
.long sys_io_submit /* 200 */
|
||||
.long sys_io_cancel
|
||||
.long sys_fadvise64
|
||||
.long sys_exit_group
|
||||
.long sys_lookup_dcookie
|
||||
.long sys_epoll_create /* 205 */
|
||||
.long sys_epoll_ctl
|
||||
.long sys_epoll_wait
|
||||
.long sys_remap_file_pages
|
||||
.long sys_set_tid_address
|
||||
.long sys_timer_create /* 210 */
|
||||
.long sys_timer_settime
|
||||
.long sys_timer_gettime
|
||||
.long sys_timer_getoverrun
|
||||
.long sys_timer_delete
|
||||
.long sys_clock_settime /* 215 */
|
||||
.long sys_clock_gettime
|
||||
.long sys_clock_getres
|
||||
.long sys_clock_nanosleep
|
||||
.long sys_statfs64
|
||||
.long sys_fstatfs64 /* 220 */
|
||||
.long sys_tgkill
|
||||
.long sys_ni_syscall /* reserved for TUX */
|
||||
.long sys_utimes
|
||||
.long sys_fadvise64_64
|
||||
.long sys_cacheflush /* 225 */
|
||||
.long sys_ni_syscall /* sys_vserver */
|
||||
.long sys_mq_open
|
||||
.long sys_mq_unlink
|
||||
.long sys_mq_timedsend
|
||||
.long sys_mq_timedreceive /* 230 */
|
||||
.long sys_mq_notify
|
||||
.long sys_mq_getsetattr
|
||||
.long sys_kexec_load
|
||||
.long sys_waitid
|
||||
.long sys_add_key /* 235 */
|
||||
.long sys_request_key
|
||||
.long sys_keyctl
|
||||
.long sys_ioprio_set
|
||||
.long sys_ioprio_get
|
||||
.long sys_inotify_init /* 240 */
|
||||
.long sys_inotify_add_watch
|
||||
.long sys_inotify_rm_watch
|
||||
.long sys_openat
|
||||
.long sys_mkdirat
|
||||
.long sys_mknodat /* 245 */
|
||||
.long sys_fchownat
|
||||
.long sys_futimesat
|
||||
.long sys_fstatat64
|
||||
.long sys_unlinkat
|
||||
.long sys_renameat /* 250 */
|
||||
.long sys_linkat
|
||||
.long sys_symlinkat
|
||||
.long sys_readlinkat
|
||||
.long sys_fchmodat
|
||||
.long sys_faccessat /* 255 */
|
||||
.long __sys_pselect6
|
||||
.long sys_ppoll
|
||||
.long sys_unshare
|
||||
.long sys_set_robust_list
|
||||
.long sys_get_robust_list /* 260 */
|
||||
.long __sys_splice
|
||||
.long sys_sync_file_range
|
||||
.long sys_tee
|
||||
.long sys_vmsplice
|
||||
.long sys_ni_syscall /* r8 is saturated at nr_syscalls */
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* Based on MIPS implementation arch/mips/kernel/time.c
|
||||
* Copyright 2001 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/clk.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sysdev.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
static cycle_t read_cycle_count(void)
|
||||
{
|
||||
return (cycle_t)sysreg_read(COUNT);
|
||||
}
|
||||
|
||||
static struct clocksource clocksource_avr32 = {
|
||||
.name = "avr32",
|
||||
.rating = 350,
|
||||
.read = read_cycle_count,
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.shift = 16,
|
||||
.is_continuous = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* By default we provide the null RTC ops
|
||||
*/
|
||||
static unsigned long null_rtc_get_time(void)
|
||||
{
|
||||
return mktime(2004, 1, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int null_rtc_set_time(unsigned long sec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long (*rtc_get_time)(void) = null_rtc_get_time;
|
||||
static int (*rtc_set_time)(unsigned long) = null_rtc_set_time;
|
||||
|
||||
/* how many counter cycles in a jiffy? */
|
||||
static unsigned long cycles_per_jiffy;
|
||||
|
||||
/* cycle counter value at the previous timer interrupt */
|
||||
static unsigned int timerhi, timerlo;
|
||||
|
||||
/* the count value for the next timer interrupt */
|
||||
static unsigned int expirelo;
|
||||
|
||||
static void avr32_timer_ack(void)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
/* Ack this timer interrupt and set the next one */
|
||||
expirelo += cycles_per_jiffy;
|
||||
if (expirelo == 0) {
|
||||
printk(KERN_DEBUG "expirelo == 0\n");
|
||||
sysreg_write(COMPARE, expirelo + 1);
|
||||
} else {
|
||||
sysreg_write(COMPARE, expirelo);
|
||||
}
|
||||
|
||||
/* Check to see if we have missed any timer interrupts */
|
||||
count = sysreg_read(COUNT);
|
||||
if ((count - expirelo) < 0x7fffffff) {
|
||||
expirelo = count + cycles_per_jiffy;
|
||||
sysreg_write(COMPARE, expirelo);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int avr32_hpt_read(void)
|
||||
{
|
||||
return sysreg_read(COUNT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from MIPS c0_hpt_timer_init().
|
||||
*
|
||||
* Why is it so complicated, and what is "count"? My assumption is
|
||||
* that `count' specifies the "reference cycle", i.e. the cycle since
|
||||
* reset that should mean "zero". The reason COUNT is written twice is
|
||||
* probably to make sure we don't get any timer interrupts while we
|
||||
* are messing with the counter.
|
||||
*/
|
||||
static void avr32_hpt_init(unsigned int count)
|
||||
{
|
||||
count = sysreg_read(COUNT) - count;
|
||||
expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy;
|
||||
sysreg_write(COUNT, expirelo - cycles_per_jiffy);
|
||||
sysreg_write(COMPARE, expirelo);
|
||||
sysreg_write(COUNT, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scheduler clock - returns current time in nanosec units.
|
||||
*/
|
||||
unsigned long long sched_clock(void)
|
||||
{
|
||||
/* There must be better ways...? */
|
||||
return (unsigned long long)jiffies * (1000000000 / HZ);
|
||||
}
|
||||
|
||||
/*
|
||||
* local_timer_interrupt() does profiling and process accounting on a
|
||||
* per-CPU basis.
|
||||
*
|
||||
* In UP mode, it is invoked from the (global) timer_interrupt.
|
||||
*/
|
||||
static void local_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
if (current->pid)
|
||||
profile_tick(CPU_PROFILING, regs);
|
||||
update_process_times(user_mode(regs));
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
/* ack timer interrupt and try to set next interrupt */
|
||||
count = avr32_hpt_read();
|
||||
avr32_timer_ack();
|
||||
|
||||
/* Update timerhi/timerlo for intra-jiffy calibration */
|
||||
timerhi += count < timerlo; /* Wrap around */
|
||||
timerlo = count;
|
||||
|
||||
/*
|
||||
* Call the generic timer interrupt handler
|
||||
*/
|
||||
write_seqlock(&xtime_lock);
|
||||
do_timer(regs);
|
||||
write_sequnlock(&xtime_lock);
|
||||
|
||||
/*
|
||||
* In UP mode, we call local_timer_interrupt() to do profiling
|
||||
* and process accounting.
|
||||
*
|
||||
* SMP is not supported yet.
|
||||
*/
|
||||
local_timer_interrupt(irq, dev_id, regs);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irqaction timer_irqaction = {
|
||||
.handler = timer_interrupt,
|
||||
.flags = IRQF_DISABLED,
|
||||
.name = "timer",
|
||||
};
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
unsigned long mult, shift, count_hz;
|
||||
int ret;
|
||||
|
||||
xtime.tv_sec = rtc_get_time();
|
||||
xtime.tv_nsec = 0;
|
||||
|
||||
set_normalized_timespec(&wall_to_monotonic,
|
||||
-xtime.tv_sec, -xtime.tv_nsec);
|
||||
|
||||
printk("Before time_init: count=%08lx, compare=%08lx\n",
|
||||
(unsigned long)sysreg_read(COUNT),
|
||||
(unsigned long)sysreg_read(COMPARE));
|
||||
|
||||
count_hz = clk_get_rate(boot_cpu_data.clk);
|
||||
shift = clocksource_avr32.shift;
|
||||
mult = clocksource_hz2mult(count_hz, shift);
|
||||
clocksource_avr32.mult = mult;
|
||||
|
||||
printk("Cycle counter: mult=%lu, shift=%lu\n", mult, shift);
|
||||
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
tmp = TICK_NSEC;
|
||||
tmp <<= shift;
|
||||
tmp += mult / 2;
|
||||
do_div(tmp, mult);
|
||||
|
||||
cycles_per_jiffy = tmp;
|
||||
}
|
||||
|
||||
/* This sets up the high precision timer for the first interrupt. */
|
||||
avr32_hpt_init(avr32_hpt_read());
|
||||
|
||||
printk("After time_init: count=%08lx, compare=%08lx\n",
|
||||
(unsigned long)sysreg_read(COUNT),
|
||||
(unsigned long)sysreg_read(COMPARE));
|
||||
|
||||
ret = clocksource_register(&clocksource_avr32);
|
||||
if (ret)
|
||||
printk(KERN_ERR
|
||||
"timer: could not register clocksource: %d\n", ret);
|
||||
|
||||
ret = setup_irq(0, &timer_irqaction);
|
||||
if (ret)
|
||||
printk("timer: could not request IRQ 0: %d\n", ret);
|
||||
}
|
||||
|
||||
static struct sysdev_class timer_class = {
|
||||
set_kset_name("timer"),
|
||||
};
|
||||
|
||||
static struct sys_device timer_device = {
|
||||
.id = 0,
|
||||
.cls = &timer_class,
|
||||
};
|
||||
|
||||
static int __init init_timer_sysfs(void)
|
||||
{
|
||||
int err = sysdev_class_register(&timer_class);
|
||||
if (!err)
|
||||
err = sysdev_register(&timer_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
device_initcall(init_timer_sysfs);
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#undef DEBUG
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <asm/traps.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/ocd.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
|
||||
{
|
||||
unsigned long p;
|
||||
int i;
|
||||
|
||||
printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
|
||||
|
||||
for (p = bottom & ~31; p < top; ) {
|
||||
printk("%04lx: ", p & 0xffff);
|
||||
|
||||
for (i = 0; i < 8; i++, p += 4) {
|
||||
unsigned int val;
|
||||
|
||||
if (p < bottom || p >= top)
|
||||
printk(" ");
|
||||
else {
|
||||
if (__get_user(val, (unsigned int __user *)p)) {
|
||||
printk("\n");
|
||||
goto out;
|
||||
}
|
||||
printk("%08x ", val);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long __user *fp;
|
||||
unsigned long __user *last_fp = NULL;
|
||||
|
||||
if (regs) {
|
||||
fp = (unsigned long __user *)regs->r7;
|
||||
} else if (tsk == current) {
|
||||
register unsigned long __user *real_fp __asm__("r7");
|
||||
fp = real_fp;
|
||||
} else {
|
||||
fp = (unsigned long __user *)tsk->thread.cpu_context.r7;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the stack until (a) we get an exception, (b) the frame
|
||||
* pointer becomes zero, or (c) the frame pointer gets stuck
|
||||
* at the same value.
|
||||
*/
|
||||
while (fp && fp != last_fp) {
|
||||
unsigned long lr, new_fp = 0;
|
||||
|
||||
last_fp = fp;
|
||||
if (__get_user(lr, fp))
|
||||
break;
|
||||
if (fp && __get_user(new_fp, fp + 1))
|
||||
break;
|
||||
fp = (unsigned long __user *)new_fp;
|
||||
|
||||
printk(" [<%08lx>] ", lr);
|
||||
print_symbol("%s\n", lr);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#else
|
||||
static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
||||
while (!kstack_end(sp)) {
|
||||
addr = *sp++;
|
||||
if (kernel_text_address(addr)) {
|
||||
printk(" [<%08lx>] ", addr);
|
||||
print_symbol("%s\n", addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void show_trace(struct task_struct *tsk, unsigned long *sp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (regs &&
|
||||
(((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
|
||||
((regs->sr & MODE_MASK) == MODE_USER)))
|
||||
return;
|
||||
|
||||
printk ("Call trace:");
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
printk("\n");
|
||||
#endif
|
||||
|
||||
__show_trace(tsk, sp, regs);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
void show_stack(struct task_struct *tsk, unsigned long *sp)
|
||||
{
|
||||
unsigned long stack;
|
||||
|
||||
if (!tsk)
|
||||
tsk = current;
|
||||
if (sp == 0) {
|
||||
if (tsk == current) {
|
||||
register unsigned long *real_sp __asm__("sp");
|
||||
sp = real_sp;
|
||||
} else {
|
||||
sp = (unsigned long *)tsk->thread.cpu_context.ksp;
|
||||
}
|
||||
}
|
||||
|
||||
stack = (unsigned long)sp;
|
||||
dump_mem("Stack: ", stack,
|
||||
THREAD_SIZE + (unsigned long)tsk->thread_info);
|
||||
show_trace(tsk, sp, NULL);
|
||||
}
|
||||
|
||||
void dump_stack(void)
|
||||
{
|
||||
show_stack(NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
|
||||
|
||||
int register_die_notifier(struct notifier_block *nb)
|
||||
{
|
||||
pr_debug("register_die_notifier: %p\n", nb);
|
||||
|
||||
return atomic_notifier_chain_register(&avr32_die_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_die_notifier);
|
||||
|
||||
int unregister_die_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_unregister(&avr32_die_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_die_notifier);
|
||||
|
||||
static DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
void __die(const char *str, struct pt_regs *regs, unsigned long err,
|
||||
const char *file, const char *func, unsigned long line)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
static int die_counter;
|
||||
|
||||
console_verbose();
|
||||
spin_lock_irq(&die_lock);
|
||||
bust_spinlocks(1);
|
||||
|
||||
printk(KERN_ALERT "%s", str);
|
||||
if (file && func)
|
||||
printk(" in %s:%s, line %ld", file, func, line);
|
||||
printk("[#%d]:\n", ++die_counter);
|
||||
print_modules();
|
||||
show_regs(regs);
|
||||
printk("Process %s (pid: %d, stack limit = 0x%p)\n",
|
||||
tsk->comm, tsk->pid, tsk->thread_info + 1);
|
||||
|
||||
if (!user_mode(regs) || in_interrupt()) {
|
||||
dump_mem("Stack: ", regs->sp,
|
||||
THREAD_SIZE + (unsigned long)tsk->thread_info);
|
||||
}
|
||||
|
||||
bust_spinlocks(0);
|
||||
spin_unlock_irq(&die_lock);
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
|
||||
const char *file, const char *func, unsigned long line)
|
||||
{
|
||||
if (!user_mode(regs))
|
||||
__die(str, regs, err, file, func, line);
|
||||
}
|
||||
|
||||
asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_SUBARCH_AVR32B
|
||||
/*
|
||||
* The exception entry always saves RSR_EX. For NMI, this is
|
||||
* wrong; it should be RSR_NMI
|
||||
*/
|
||||
regs->sr = sysreg_read(RSR_NMI);
|
||||
#endif
|
||||
|
||||
printk("NMI taken!!!!\n");
|
||||
die("NMI", regs, ecr);
|
||||
BUG();
|
||||
}
|
||||
|
||||
asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
printk("Unable to handle critical exception %lu at pc = %08lx!\n",
|
||||
ecr, regs->pc);
|
||||
die("Oops", regs, ecr);
|
||||
BUG();
|
||||
}
|
||||
|
||||
asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (ecr == ECR_ADDR_ALIGN_X)
|
||||
pr_debug("Instruction Address Exception at pc = %08lx\n",
|
||||
regs->pc);
|
||||
else if (ecr == ECR_ADDR_ALIGN_R)
|
||||
pr_debug("Data Address Exception (Read) at pc = %08lx\n",
|
||||
regs->pc);
|
||||
else if (ecr == ECR_ADDR_ALIGN_W)
|
||||
pr_debug("Data Address Exception (Write) at pc = %08lx\n",
|
||||
regs->pc);
|
||||
else
|
||||
BUG();
|
||||
|
||||
show_regs(regs);
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_ADRALN;
|
||||
info.si_addr = (void __user *)regs->pc;
|
||||
|
||||
force_sig_info(SIGBUS, &info, current);
|
||||
}
|
||||
|
||||
/* This way of handling undefined instructions is stolen from ARM */
|
||||
static LIST_HEAD(undef_hook);
|
||||
static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
void register_undef_hook(struct undef_hook *hook)
|
||||
{
|
||||
spin_lock_irq(&undef_lock);
|
||||
list_add(&hook->node, &undef_hook);
|
||||
spin_unlock_irq(&undef_lock);
|
||||
}
|
||||
|
||||
void unregister_undef_hook(struct undef_hook *hook)
|
||||
{
|
||||
spin_lock_irq(&undef_lock);
|
||||
list_del(&hook->node);
|
||||
spin_unlock_irq(&undef_lock);
|
||||
}
|
||||
|
||||
static int do_cop_absent(u32 insn)
|
||||
{
|
||||
int cop_nr;
|
||||
u32 cpucr;
|
||||
if ( (insn & 0xfdf00000) == 0xf1900000 )
|
||||
/* LDC0 */
|
||||
cop_nr = 0;
|
||||
else
|
||||
cop_nr = (insn >> 13) & 0x7;
|
||||
|
||||
/* Try enabling the coprocessor */
|
||||
cpucr = sysreg_read(CPUCR);
|
||||
cpucr |= (1 << (24 + cop_nr));
|
||||
sysreg_write(CPUCR, cpucr);
|
||||
|
||||
cpucr = sysreg_read(CPUCR);
|
||||
if ( !(cpucr & (1 << (24 + cop_nr))) ){
|
||||
printk("Coprocessor #%i not found!\n", cop_nr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BUG
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
|
||||
{
|
||||
char *file;
|
||||
u16 line;
|
||||
char c;
|
||||
|
||||
if (__get_user(line, (u16 __user *)(regs->pc + 2)))
|
||||
return;
|
||||
if (__get_user(file, (char * __user *)(regs->pc + 4))
|
||||
|| (unsigned long)file < PAGE_OFFSET
|
||||
|| __get_user(c, file))
|
||||
file = "<bad filename>";
|
||||
|
||||
printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
|
||||
}
|
||||
#else
|
||||
static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
u32 insn;
|
||||
struct undef_hook *hook;
|
||||
siginfo_t info;
|
||||
void __user *pc;
|
||||
|
||||
if (!user_mode(regs))
|
||||
goto kernel_trap;
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
pc = (void __user *)instruction_pointer(regs);
|
||||
if (__get_user(insn, (u32 __user *)pc))
|
||||
goto invalid_area;
|
||||
|
||||
if (ecr == ECR_COPROC_ABSENT) {
|
||||
if (do_cop_absent(insn) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irq(&undef_lock);
|
||||
list_for_each_entry(hook, &undef_hook, node) {
|
||||
if ((insn & hook->insn_mask) == hook->insn_val) {
|
||||
if (hook->fn(regs, insn) == 0) {
|
||||
spin_unlock_irq(&undef_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&undef_lock);
|
||||
|
||||
invalid_area:
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Illegal instruction at pc = %08lx\n", regs->pc);
|
||||
if (regs->pc < TASK_SIZE) {
|
||||
unsigned long ptbr, pgd, pte, *p;
|
||||
|
||||
ptbr = sysreg_read(PTBR);
|
||||
p = (unsigned long *)ptbr;
|
||||
pgd = p[regs->pc >> 22];
|
||||
p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
|
||||
pte = p[(regs->pc >> 12) & 0x3ff];
|
||||
printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
|
||||
}
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_addr = (void __user *)regs->pc;
|
||||
switch (ecr) {
|
||||
case ECR_ILLEGAL_OPCODE:
|
||||
case ECR_UNIMPL_INSTRUCTION:
|
||||
info.si_code = ILL_ILLOPC;
|
||||
break;
|
||||
case ECR_PRIVILEGE_VIOLATION:
|
||||
info.si_code = ILL_PRVOPC;
|
||||
break;
|
||||
case ECR_COPROC_ABSENT:
|
||||
info.si_code = ILL_COPROC;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
return;
|
||||
|
||||
kernel_trap:
|
||||
#ifdef CONFIG_BUG
|
||||
if (__kernel_text_address(instruction_pointer(regs))) {
|
||||
insn = *(u16 *)instruction_pointer(regs);
|
||||
if (insn == AVR32_BUG_OPCODE) {
|
||||
do_bug_verbose(regs, insn);
|
||||
die("Kernel BUG", regs, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
die("Oops: Illegal instruction in kernel code", regs, ecr);
|
||||
}
|
||||
|
||||
asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
printk("Floating-point exception at pc = %08lx\n", regs->pc);
|
||||
|
||||
/* We have no FPU... */
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_addr = (void __user *)regs->pc;
|
||||
info.si_code = ILL_COPROC;
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
}
|
||||
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* AVR32 linker script for the Linux kernel
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define LOAD_OFFSET 0x00000000
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
|
||||
OUTPUT_FORMAT("elf32-avr32", "elf32-avr32", "elf32-avr32")
|
||||
OUTPUT_ARCH(avr32)
|
||||
ENTRY(_start)
|
||||
|
||||
/* Big endian */
|
||||
jiffies = jiffies_64 + 4;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = CONFIG_ENTRY_ADDRESS;
|
||||
.init : AT(ADDR(.init) - LOAD_OFFSET) {
|
||||
_stext = .;
|
||||
__init_begin = .;
|
||||
_sinittext = .;
|
||||
*(.text.reset)
|
||||
*(.init.text)
|
||||
_einittext = .;
|
||||
. = ALIGN(4);
|
||||
__tagtable_begin = .;
|
||||
*(.taglist)
|
||||
__tagtable_end = .;
|
||||
*(.init.data)
|
||||
. = ALIGN(16);
|
||||
__setup_start = .;
|
||||
*(.init.setup)
|
||||
__setup_end = .;
|
||||
. = ALIGN(4);
|
||||
__initcall_start = .;
|
||||
*(.initcall1.init)
|
||||
*(.initcall2.init)
|
||||
*(.initcall3.init)
|
||||
*(.initcall4.init)
|
||||
*(.initcall5.init)
|
||||
*(.initcall6.init)
|
||||
*(.initcall7.init)
|
||||
__initcall_end = .;
|
||||
__con_initcall_start = .;
|
||||
*(.con_initcall.init)
|
||||
__con_initcall_end = .;
|
||||
__security_initcall_start = .;
|
||||
*(.security_initcall.init)
|
||||
__security_initcall_end = .;
|
||||
. = ALIGN(32);
|
||||
__initramfs_start = .;
|
||||
*(.init.ramfs)
|
||||
__initramfs_end = .;
|
||||
. = ALIGN(4096);
|
||||
__init_end = .;
|
||||
}
|
||||
|
||||
. = ALIGN(8192);
|
||||
.text : AT(ADDR(.text) - LOAD_OFFSET) {
|
||||
_evba = .;
|
||||
_text = .;
|
||||
*(.ex.text)
|
||||
. = 0x50;
|
||||
*(.tlbx.ex.text)
|
||||
. = 0x60;
|
||||
*(.tlbr.ex.text)
|
||||
. = 0x70;
|
||||
*(.tlbw.ex.text)
|
||||
. = 0x100;
|
||||
*(.scall.text)
|
||||
*(.irq.text)
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
KPROBES_TEXT
|
||||
*(.fixup)
|
||||
*(.gnu.warning)
|
||||
_etext = .;
|
||||
} = 0xd703d703
|
||||
|
||||
. = ALIGN(4);
|
||||
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
|
||||
__start___ex_table = .;
|
||||
*(__ex_table)
|
||||
__stop___ex_table = .;
|
||||
}
|
||||
|
||||
RODATA
|
||||
|
||||
. = ALIGN(8192);
|
||||
|
||||
.data : AT(ADDR(.data) - LOAD_OFFSET) {
|
||||
_data = .;
|
||||
_sdata = .;
|
||||
/*
|
||||
* First, the init task union, aligned to an 8K boundary.
|
||||
*/
|
||||
*(.data.init_task)
|
||||
|
||||
/* Then, the cacheline aligned data */
|
||||
. = ALIGN(32);
|
||||
*(.data.cacheline_aligned)
|
||||
|
||||
/* And the rest... */
|
||||
*(.data.rel*)
|
||||
*(.data)
|
||||
CONSTRUCTORS
|
||||
|
||||
_edata = .;
|
||||
}
|
||||
|
||||
|
||||
. = ALIGN(8);
|
||||
.bss : AT(ADDR(.bss) - LOAD_OFFSET) {
|
||||
__bss_start = .;
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
. = ALIGN(8);
|
||||
__bss_stop = .;
|
||||
_end = .;
|
||||
}
|
||||
|
||||
/* When something in the kernel is NOT compiled as a module, the module
|
||||
* cleanup code and data are put into these segments. Both can then be
|
||||
* thrown away, as cleanup code is never called unless it's a module.
|
||||
*/
|
||||
/DISCARD/ : {
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
}
|
||||
|
||||
DWARF_DEBUG
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for AVR32-specific library files
|
||||
#
|
||||
|
||||
lib-y := copy_user.o clear_user.o
|
||||
lib-y += strncpy_from_user.o strnlen_user.o
|
||||
lib-y += delay.o memset.o memcpy.o findbit.o
|
||||
lib-y += csum_partial.o csum_partial_copy_generic.o
|
||||
lib-y += io-readsw.o io-readsl.o io-writesw.o io-writesl.o
|
||||
lib-y += __avr32_lsl64.o __avr32_lsr64.o __avr32_asr64.o
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DWtype __avr32_asr64(DWtype u, word_type b)
|
||||
*/
|
||||
.text
|
||||
.global __avr32_asr64
|
||||
.type __avr32_asr64,@function
|
||||
__avr32_asr64:
|
||||
cp.w r12, 0
|
||||
reteq r12
|
||||
|
||||
rsub r9, r12, 32
|
||||
brle 1f
|
||||
|
||||
lsl r8, r11, r9
|
||||
lsr r10, r10, r12
|
||||
asr r11, r11, r12
|
||||
or r10, r8
|
||||
retal r12
|
||||
|
||||
1: neg r9
|
||||
asr r10, r11, r9
|
||||
asr r11, 31
|
||||
retal r12
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DWtype __avr32_lsl64(DWtype u, word_type b)
|
||||
*/
|
||||
.text
|
||||
.global __avr32_lsl64
|
||||
.type __avr32_lsl64,@function
|
||||
__avr32_lsl64:
|
||||
cp.w r12, 0
|
||||
reteq r12
|
||||
|
||||
rsub r9, r12, 32
|
||||
brle 1f
|
||||
|
||||
lsr r8, r10, r9
|
||||
lsl r10, r10, r12
|
||||
lsl r11, r11, r12
|
||||
or r11, r8
|
||||
retal r12
|
||||
|
||||
1: neg r9
|
||||
lsl r11, r10, r9
|
||||
mov r10, 0
|
||||
retal r12
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DWtype __avr32_lsr64(DWtype u, word_type b)
|
||||
*/
|
||||
.text
|
||||
.global __avr32_lsr64
|
||||
.type __avr32_lsr64,@function
|
||||
__avr32_lsr64:
|
||||
cp.w r12, 0
|
||||
reteq r12
|
||||
|
||||
rsub r9, r12, 32
|
||||
brle 1f
|
||||
|
||||
lsl r8, r11, r9
|
||||
lsr r11, r11, r12
|
||||
lsr r10, r10, r12
|
||||
or r10, r8
|
||||
retal r12
|
||||
|
||||
1: neg r9
|
||||
lsr r10, r11, r9
|
||||
mov r11, 0
|
||||
retal r12
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/page.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
.text
|
||||
.align 1
|
||||
.global clear_user
|
||||
.type clear_user, "function"
|
||||
clear_user:
|
||||
branch_if_kernel r8, __clear_user
|
||||
ret_if_privileged r8, r12, r11, r11
|
||||
|
||||
.global __clear_user
|
||||
.type __clear_user, "function"
|
||||
__clear_user:
|
||||
mov r9, r12
|
||||
mov r8, 0
|
||||
andl r9, 3, COH
|
||||
brne 5f
|
||||
|
||||
1: sub r11, 4
|
||||
brlt 2f
|
||||
|
||||
10: st.w r12++, r8
|
||||
sub r11, 4
|
||||
brge 10b
|
||||
|
||||
2: sub r11, -4
|
||||
reteq 0
|
||||
|
||||
/* Unaligned count or address */
|
||||
bld r11, 1
|
||||
brcc 12f
|
||||
11: st.h r12++, r8
|
||||
sub r11, 2
|
||||
reteq 0
|
||||
12: st.b r12++, r8
|
||||
retal 0
|
||||
|
||||
/* Unaligned address */
|
||||
5: cp.w r11, 4
|
||||
brlt 2b
|
||||
|
||||
lsl r9, 2
|
||||
add pc, pc, r9
|
||||
13: st.b r12++, r8
|
||||
sub r11, 1
|
||||
14: st.b r12++, r8
|
||||
sub r11, 1
|
||||
15: st.b r12++, r8
|
||||
sub r11, 1
|
||||
rjmp 1b
|
||||
|
||||
.size clear_user, . - clear_user
|
||||
.size __clear_user, . - __clear_user
|
||||
|
||||
.section .fixup, "ax"
|
||||
.align 1
|
||||
18: sub r11, -4
|
||||
19: retal r11
|
||||
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 10b, 18b
|
||||
.long 11b, 19b
|
||||
.long 12b, 19b
|
||||
.long 13b, 19b
|
||||
.long 14b, 19b
|
||||
.long 15b, 19b
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copy to/from userspace with optional address space checking.
|
||||
*
|
||||
* Copyright 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/page.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
/*
|
||||
* __kernel_size_t
|
||||
* __copy_user(void *to, const void *from, __kernel_size_t n)
|
||||
*
|
||||
* Returns the number of bytes not copied. Might be off by
|
||||
* max 3 bytes if we get a fault in the main loop.
|
||||
*
|
||||
* The address-space checking functions simply fall through to
|
||||
* the non-checking version.
|
||||
*/
|
||||
.text
|
||||
.align 1
|
||||
.global copy_from_user
|
||||
.type copy_from_user, @function
|
||||
copy_from_user:
|
||||
branch_if_kernel r8, __copy_user
|
||||
ret_if_privileged r8, r11, r10, r10
|
||||
rjmp __copy_user
|
||||
.size copy_from_user, . - copy_from_user
|
||||
|
||||
.global copy_to_user
|
||||
.type copy_to_user, @function
|
||||
copy_to_user:
|
||||
branch_if_kernel r8, __copy_user
|
||||
ret_if_privileged r8, r12, r10, r10
|
||||
.size copy_to_user, . - copy_to_user
|
||||
|
||||
.global __copy_user
|
||||
.type __copy_user, @function
|
||||
__copy_user:
|
||||
mov r9, r11
|
||||
andl r9, 3, COH
|
||||
brne 6f
|
||||
|
||||
/* At this point, from is word-aligned */
|
||||
1: sub r10, 4
|
||||
brlt 3f
|
||||
|
||||
2:
|
||||
10: ld.w r8, r11++
|
||||
11: st.w r12++, r8
|
||||
sub r10, 4
|
||||
brge 2b
|
||||
|
||||
3: sub r10, -4
|
||||
reteq 0
|
||||
|
||||
/*
|
||||
* Handle unaligned count. Need to be careful with r10 here so
|
||||
* that we return the correct value even if we get a fault
|
||||
*/
|
||||
4:
|
||||
20: ld.ub r8, r11++
|
||||
21: st.b r12++, r8
|
||||
sub r10, 1
|
||||
reteq 0
|
||||
22: ld.ub r8, r11++
|
||||
23: st.b r12++, r8
|
||||
sub r10, 1
|
||||
reteq 0
|
||||
24: ld.ub r8, r11++
|
||||
25: st.b r12++, r8
|
||||
retal 0
|
||||
|
||||
/* Handle unaligned from-pointer */
|
||||
6: cp.w r10, 4
|
||||
brlt 4b
|
||||
rsub r9, r9, 4
|
||||
|
||||
30: ld.ub r8, r11++
|
||||
31: st.b r12++, r8
|
||||
sub r10, 1
|
||||
sub r9, 1
|
||||
breq 1b
|
||||
32: ld.ub r8, r11++
|
||||
33: st.b r12++, r8
|
||||
sub r10, 1
|
||||
sub r9, 1
|
||||
breq 1b
|
||||
34: ld.ub r8, r11++
|
||||
35: st.b r12++, r8
|
||||
sub r10, 1
|
||||
rjmp 1b
|
||||
.size __copy_user, . - __copy_user
|
||||
|
||||
.section .fixup,"ax"
|
||||
.align 1
|
||||
19: sub r10, -4
|
||||
29: retal r10
|
||||
|
||||
.section __ex_table,"a"
|
||||
.align 2
|
||||
.long 10b, 19b
|
||||
.long 11b, 19b
|
||||
.long 20b, 29b
|
||||
.long 21b, 29b
|
||||
.long 22b, 29b
|
||||
.long 23b, 29b
|
||||
.long 24b, 29b
|
||||
.long 25b, 29b
|
||||
.long 30b, 29b
|
||||
.long 31b, 29b
|
||||
.long 32b, 29b
|
||||
.long 33b, 29b
|
||||
.long 34b, 29b
|
||||
.long 35b, 29b
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* unsigned int csum_partial(const unsigned char *buff,
|
||||
* int len, unsigned int sum)
|
||||
*/
|
||||
.text
|
||||
.global csum_partial
|
||||
.type csum_partial,"function"
|
||||
.align 1
|
||||
csum_partial:
|
||||
/* checksum complete words, aligned or not */
|
||||
3: sub r11, 4
|
||||
brlt 5f
|
||||
4: ld.w r9, r12++
|
||||
add r10, r9
|
||||
acr r10
|
||||
sub r11, 4
|
||||
brge 4b
|
||||
|
||||
/* return if we had a whole number of words */
|
||||
5: sub r11, -4
|
||||
reteq r10
|
||||
|
||||
/* checksum any remaining bytes at the end */
|
||||
mov r9, 0
|
||||
mov r8, 0
|
||||
cp r11, 2
|
||||
brlt 6f
|
||||
ld.uh r9, r12++
|
||||
sub r11, 2
|
||||
breq 7f
|
||||
lsl r9, 16
|
||||
6: ld.ub r8, r12++
|
||||
lsl r8, 8
|
||||
7: or r9, r8
|
||||
add r10, r9
|
||||
acr r10
|
||||
|
||||
retal r10
|
||||
.size csum_partial, . - csum_partial
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/errno.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
/*
|
||||
* unsigned int csum_partial_copy_generic(const char *src, char *dst, int len
|
||||
* int sum, int *src_err_ptr,
|
||||
* int *dst_err_ptr)
|
||||
*
|
||||
* Copy src to dst while checksumming, otherwise like csum_partial.
|
||||
*/
|
||||
|
||||
.macro ld_src size, reg, ptr
|
||||
9999: ld.\size \reg, \ptr
|
||||
.section __ex_table, "a"
|
||||
.long 9999b, fixup_ld_src
|
||||
.previous
|
||||
.endm
|
||||
|
||||
.macro st_dst size, ptr, reg
|
||||
9999: st.\size \ptr, \reg
|
||||
.section __ex_table, "a"
|
||||
.long 9999b, fixup_st_dst
|
||||
.previous
|
||||
.endm
|
||||
|
||||
.text
|
||||
.global csum_partial_copy_generic
|
||||
.type csum_partial_copy_generic,"function"
|
||||
.align 1
|
||||
csum_partial_copy_generic:
|
||||
pushm r4-r7,lr
|
||||
|
||||
/* The inner loop */
|
||||
1: sub r10, 4
|
||||
brlt 5f
|
||||
2: ld_src w, r5, r12++
|
||||
st_dst w, r11++, r5
|
||||
add r9, r5
|
||||
acr r9
|
||||
sub r10, 4
|
||||
brge 2b
|
||||
|
||||
/* return if we had a whole number of words */
|
||||
5: sub r10, -4
|
||||
brne 7f
|
||||
|
||||
6: mov r12, r9
|
||||
popm r4-r7,pc
|
||||
|
||||
/* handle additional bytes at the tail */
|
||||
7: mov r5, 0
|
||||
mov r4, 32
|
||||
8: ld_src ub, r6, r12++
|
||||
st_dst b, r11++, r6
|
||||
lsl r5, 8
|
||||
sub r4, 8
|
||||
bfins r5, r6, 0, 8
|
||||
sub r10, 1
|
||||
brne 8b
|
||||
|
||||
lsl r5, r5, r4
|
||||
add r9, r5
|
||||
acr r9
|
||||
rjmp 6b
|
||||
|
||||
/* Exception handler */
|
||||
.section .fixup,"ax"
|
||||
.align 1
|
||||
fixup_ld_src:
|
||||
mov r9, -EFAULT
|
||||
cp.w r8, 0
|
||||
breq 1f
|
||||
st.w r8[0], r9
|
||||
|
||||
1: /*
|
||||
* TODO: zero the complete destination - computing the rest
|
||||
* is too much work
|
||||
*/
|
||||
|
||||
mov r9, 0
|
||||
rjmp 6b
|
||||
|
||||
fixup_st_dst:
|
||||
mov r9, -EFAULT
|
||||
lddsp r8, sp[20]
|
||||
cp.w r8, 0
|
||||
breq 1f
|
||||
st.w r8[0], r9
|
||||
1: mov r9, 0
|
||||
rjmp 6b
|
||||
|
||||
.previous
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Precise Delay Loops for avr32
|
||||
*
|
||||
* Copyright (C) 1993 Linus Torvalds
|
||||
* Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/delay.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
int read_current_timer(unsigned long *timer_value)
|
||||
{
|
||||
*timer_value = sysreg_read(COUNT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __delay(unsigned long loops)
|
||||
{
|
||||
unsigned bclock, now;
|
||||
|
||||
bclock = sysreg_read(COUNT);
|
||||
do {
|
||||
now = sysreg_read(COUNT);
|
||||
} while ((now - bclock) < loops);
|
||||
}
|
||||
|
||||
inline void __const_udelay(unsigned long xloops)
|
||||
{
|
||||
unsigned long long loops;
|
||||
|
||||
asm("mulu.d %0, %1, %2"
|
||||
: "=r"(loops)
|
||||
: "r"(current_cpu_data.loops_per_jiffy * HZ), "r"(xloops));
|
||||
__delay(loops >> 32);
|
||||
}
|
||||
|
||||
void __udelay(unsigned long usecs)
|
||||
{
|
||||
__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
|
||||
}
|
||||
|
||||
void __ndelay(unsigned long nsecs)
|
||||
{
|
||||
__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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>
|
||||
|
||||
.text
|
||||
/*
|
||||
* unsigned long find_first_zero_bit(const unsigned long *addr,
|
||||
* unsigned long size)
|
||||
*/
|
||||
ENTRY(find_first_zero_bit)
|
||||
cp.w r11, 0
|
||||
reteq r11
|
||||
mov r9, r11
|
||||
1: ld.w r8, r12[0]
|
||||
com r8
|
||||
brne .L_found
|
||||
sub r12, -4
|
||||
sub r9, 32
|
||||
brgt 1b
|
||||
retal r11
|
||||
|
||||
/*
|
||||
* unsigned long find_next_zero_bit(const unsigned long *addr,
|
||||
* unsigned long size,
|
||||
* unsigned long offset)
|
||||
*/
|
||||
ENTRY(find_next_zero_bit)
|
||||
lsr r8, r10, 5
|
||||
sub r9, r11, r10
|
||||
retle r11
|
||||
|
||||
lsl r8, 2
|
||||
add r12, r8
|
||||
andl r10, 31, COH
|
||||
breq 1f
|
||||
|
||||
/* offset is not word-aligned. Handle the first (32 - r10) bits */
|
||||
ld.w r8, r12[0]
|
||||
com r8
|
||||
sub r12, -4
|
||||
lsr r8, r8, r10
|
||||
brne .L_found
|
||||
|
||||
/* r9 = r9 - (32 - r10) = r9 + r10 - 32 */
|
||||
add r9, r10
|
||||
sub r9, 32
|
||||
retle r11
|
||||
|
||||
/* Main loop. offset must be word-aligned */
|
||||
1: ld.w r8, r12[0]
|
||||
com r8
|
||||
brne .L_found
|
||||
sub r12, -4
|
||||
sub r9, 32
|
||||
brgt 1b
|
||||
retal r11
|
||||
|
||||
/* Common return path for when a bit is actually found. */
|
||||
.L_found:
|
||||
brev r8
|
||||
clz r10, r8
|
||||
rsub r9, r11
|
||||
add r10, r9
|
||||
|
||||
/* XXX: If we don't have to return exactly "size" when the bit
|
||||
is not found, we may drop this "min" thing */
|
||||
min r12, r11, r10
|
||||
retal r12
|
||||
|
||||
/*
|
||||
* unsigned long find_first_bit(const unsigned long *addr,
|
||||
* unsigned long size)
|
||||
*/
|
||||
ENTRY(find_first_bit)
|
||||
cp.w r11, 0
|
||||
reteq r11
|
||||
mov r9, r11
|
||||
1: ld.w r8, r12[0]
|
||||
cp.w r8, 0
|
||||
brne .L_found
|
||||
sub r12, -4
|
||||
sub r9, 32
|
||||
brgt 1b
|
||||
retal r11
|
||||
|
||||
/*
|
||||
* unsigned long find_next_bit(const unsigned long *addr,
|
||||
* unsigned long size,
|
||||
* unsigned long offset)
|
||||
*/
|
||||
ENTRY(find_next_bit)
|
||||
lsr r8, r10, 5
|
||||
sub r9, r11, r10
|
||||
retle r11
|
||||
|
||||
lsl r8, 2
|
||||
add r12, r8
|
||||
andl r10, 31, COH
|
||||
breq 1f
|
||||
|
||||
/* offset is not word-aligned. Handle the first (32 - r10) bits */
|
||||
ld.w r8, r12[0]
|
||||
sub r12, -4
|
||||
lsr r8, r8, r10
|
||||
brne .L_found
|
||||
|
||||
/* r9 = r9 - (32 - r10) = r9 + r10 - 32 */
|
||||
add r9, r10
|
||||
sub r9, 32
|
||||
retle r11
|
||||
|
||||
/* Main loop. offset must be word-aligned */
|
||||
1: ld.w r8, r12[0]
|
||||
cp.w r8, 0
|
||||
brne .L_found
|
||||
sub r12, -4
|
||||
sub r9, 32
|
||||
brgt 1b
|
||||
retal r11
|
||||
|
||||
ENTRY(generic_find_next_zero_le_bit)
|
||||
lsr r8, r10, 5
|
||||
sub r9, r11, r10
|
||||
retle r11
|
||||
|
||||
lsl r8, 2
|
||||
add r12, r8
|
||||
andl r10, 31, COH
|
||||
breq 1f
|
||||
|
||||
/* offset is not word-aligned. Handle the first (32 - r10) bits */
|
||||
ldswp.w r8, r12[0]
|
||||
sub r12, -4
|
||||
lsr r8, r8, r10
|
||||
brne .L_found
|
||||
|
||||
/* r9 = r9 - (32 - r10) = r9 + r10 - 32 */
|
||||
add r9, r10
|
||||
sub r9, 32
|
||||
retle r11
|
||||
|
||||
/* Main loop. offset must be word-aligned */
|
||||
1: ldswp.w r8, r12[0]
|
||||
cp.w r8, 0
|
||||
brne .L_found
|
||||
sub r12, -4
|
||||
sub r9, 32
|
||||
brgt 1b
|
||||
retal r11
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.global __raw_readsl
|
||||
.type __raw_readsl,@function
|
||||
__raw_readsl:
|
||||
cp.w r10, 0
|
||||
reteq r12
|
||||
|
||||
/*
|
||||
* If r11 isn't properly aligned, we might get an exception on
|
||||
* some implementations. But there's not much we can do about it.
|
||||
*/
|
||||
1: ld.w r8, r12[0]
|
||||
sub r10, 1
|
||||
st.w r11++, r8
|
||||
brne 1b
|
||||
|
||||
retal r12
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.Lnot_word_aligned:
|
||||
/*
|
||||
* Bad alignment will cause a hardware exception, which is as
|
||||
* good as anything. No need for us to check for proper alignment.
|
||||
*/
|
||||
ld.uh r8, r12[0]
|
||||
sub r10, 1
|
||||
st.h r11++, r8
|
||||
|
||||
/* fall through */
|
||||
|
||||
.global __raw_readsw
|
||||
.type __raw_readsw,@function
|
||||
__raw_readsw:
|
||||
cp.w r10, 0
|
||||
reteq r12
|
||||
mov r9, 3
|
||||
tst r11, r9
|
||||
brne .Lnot_word_aligned
|
||||
|
||||
sub r10, 2
|
||||
brlt 2f
|
||||
|
||||
1: ldins.h r8:t, r12[0]
|
||||
ldins.h r8:b, r12[0]
|
||||
st.w r11++, r8
|
||||
sub r10, 2
|
||||
brge 1b
|
||||
|
||||
2: sub r10, -2
|
||||
reteq r12
|
||||
|
||||
ld.uh r8, r12[0]
|
||||
st.h r11++, r8
|
||||
retal r12
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.global __raw_writesl
|
||||
.type __raw_writesl,@function
|
||||
__raw_writesl:
|
||||
cp.w r10, 0
|
||||
reteq r12
|
||||
|
||||
1: ld.w r8, r11++
|
||||
sub r10, 1
|
||||
st.w r12[0], r8
|
||||
brne 1b
|
||||
|
||||
retal r12
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.Lnot_word_aligned:
|
||||
ld.uh r8, r11++
|
||||
sub r10, 1
|
||||
st.h r12[0], r8
|
||||
|
||||
.global __raw_writesw
|
||||
.type __raw_writesw,@function
|
||||
__raw_writesw:
|
||||
cp.w r10, 0
|
||||
mov r9, 3
|
||||
reteq r12
|
||||
tst r11, r9
|
||||
brne .Lnot_word_aligned
|
||||
|
||||
sub r10, 2
|
||||
brlt 2f
|
||||
|
||||
1: ld.w r8, r11++
|
||||
bfextu r9, r8, 16, 16
|
||||
st.h r12[0], r9
|
||||
st.h r12[0], r8
|
||||
sub r10, 2
|
||||
brge 1b
|
||||
|
||||
2: sub r10, -2
|
||||
reteq r12
|
||||
|
||||
ld.uh r8, r11++
|
||||
st.h r12[0], r8
|
||||
retal r12
|
|
@ -0,0 +1,33 @@
|
|||
/* Definitions for various functions 'borrowed' from gcc-3.4.3 */
|
||||
|
||||
#define BITS_PER_UNIT 8
|
||||
|
||||
typedef int QItype __attribute__ ((mode (QI)));
|
||||
typedef unsigned int UQItype __attribute__ ((mode (QI)));
|
||||
typedef int HItype __attribute__ ((mode (HI)));
|
||||
typedef unsigned int UHItype __attribute__ ((mode (HI)));
|
||||
typedef int SItype __attribute__ ((mode (SI)));
|
||||
typedef unsigned int USItype __attribute__ ((mode (SI)));
|
||||
typedef int DItype __attribute__ ((mode (DI)));
|
||||
typedef unsigned int UDItype __attribute__ ((mode (DI)));
|
||||
typedef float SFtype __attribute__ ((mode (SF)));
|
||||
typedef float DFtype __attribute__ ((mode (DF)));
|
||||
typedef int word_type __attribute__ ((mode (__word__)));
|
||||
|
||||
#define W_TYPE_SIZE (4 * BITS_PER_UNIT)
|
||||
#define Wtype SItype
|
||||
#define UWtype USItype
|
||||
#define HWtype SItype
|
||||
#define UHWtype USItype
|
||||
#define DWtype DItype
|
||||
#define UDWtype UDItype
|
||||
#define __NW(a,b) __ ## a ## si ## b
|
||||
#define __NDW(a,b) __ ## a ## di ## b
|
||||
|
||||
struct DWstruct {Wtype high, low;};
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct DWstruct s;
|
||||
DWtype ll;
|
||||
} DWunion;
|
|
@ -0,0 +1,98 @@
|
|||
/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
|
||||
Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This definition file 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, or (at your option) any later version.
|
||||
|
||||
This definition file 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.
|
||||
|
||||
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. */
|
||||
|
||||
/* Borrowed from gcc-3.4.3 */
|
||||
|
||||
#define __BITS4 (W_TYPE_SIZE / 4)
|
||||
#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
|
||||
#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
|
||||
#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
|
||||
|
||||
#define count_leading_zeros(count, x) ((count) = __builtin_clz(x))
|
||||
|
||||
#define __udiv_qrnnd_c(q, r, n1, n0, d) \
|
||||
do { \
|
||||
UWtype __d1, __d0, __q1, __q0; \
|
||||
UWtype __r1, __r0, __m; \
|
||||
__d1 = __ll_highpart (d); \
|
||||
__d0 = __ll_lowpart (d); \
|
||||
\
|
||||
__r1 = (n1) % __d1; \
|
||||
__q1 = (n1) / __d1; \
|
||||
__m = (UWtype) __q1 * __d0; \
|
||||
__r1 = __r1 * __ll_B | __ll_highpart (n0); \
|
||||
if (__r1 < __m) \
|
||||
{ \
|
||||
__q1--, __r1 += (d); \
|
||||
if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
|
||||
if (__r1 < __m) \
|
||||
__q1--, __r1 += (d); \
|
||||
} \
|
||||
__r1 -= __m; \
|
||||
\
|
||||
__r0 = __r1 % __d1; \
|
||||
__q0 = __r1 / __d1; \
|
||||
__m = (UWtype) __q0 * __d0; \
|
||||
__r0 = __r0 * __ll_B | __ll_lowpart (n0); \
|
||||
if (__r0 < __m) \
|
||||
{ \
|
||||
__q0--, __r0 += (d); \
|
||||
if (__r0 >= (d)) \
|
||||
if (__r0 < __m) \
|
||||
__q0--, __r0 += (d); \
|
||||
} \
|
||||
__r0 -= __m; \
|
||||
\
|
||||
(q) = (UWtype) __q1 * __ll_B | __q0; \
|
||||
(r) = __r0; \
|
||||
} while (0)
|
||||
|
||||
#define udiv_qrnnd __udiv_qrnnd_c
|
||||
|
||||
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
|
||||
do { \
|
||||
UWtype __x; \
|
||||
__x = (al) - (bl); \
|
||||
(sh) = (ah) - (bh) - (__x > (al)); \
|
||||
(sl) = __x; \
|
||||
} while (0)
|
||||
|
||||
#define umul_ppmm(w1, w0, u, v) \
|
||||
do { \
|
||||
UWtype __x0, __x1, __x2, __x3; \
|
||||
UHWtype __ul, __vl, __uh, __vh; \
|
||||
\
|
||||
__ul = __ll_lowpart (u); \
|
||||
__uh = __ll_highpart (u); \
|
||||
__vl = __ll_lowpart (v); \
|
||||
__vh = __ll_highpart (v); \
|
||||
\
|
||||
__x0 = (UWtype) __ul * __vl; \
|
||||
__x1 = (UWtype) __ul * __vh; \
|
||||
__x2 = (UWtype) __uh * __vl; \
|
||||
__x3 = (UWtype) __uh * __vh; \
|
||||
\
|
||||
__x1 += __ll_highpart (__x0);/* this can't give carry */ \
|
||||
__x1 += __x2; /* but this indeed can */ \
|
||||
if (__x1 < __x2) /* did we get it? */ \
|
||||
__x3 += __ll_B; /* yes, add it in the proper pos. */ \
|
||||
\
|
||||
(w1) = __x3 + __ll_highpart (__x1); \
|
||||
(w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \
|
||||
} while (0)
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* void *memcpy(void *to, const void *from, unsigned long n)
|
||||
*
|
||||
* This implementation does word-aligned loads in the main loop,
|
||||
* possibly sacrificing alignment of stores.
|
||||
*
|
||||
* Hopefully, in most cases, both "to" and "from" will be
|
||||
* word-aligned to begin with.
|
||||
*/
|
||||
.text
|
||||
.global memcpy
|
||||
.type memcpy, @function
|
||||
memcpy:
|
||||
mov r9, r11
|
||||
andl r9, 3, COH
|
||||
brne 1f
|
||||
|
||||
/* At this point, "from" is word-aligned */
|
||||
2: sub r10, 4
|
||||
mov r9, r12
|
||||
brlt 4f
|
||||
|
||||
3: ld.w r8, r11++
|
||||
sub r10, 4
|
||||
st.w r12++, r8
|
||||
brge 3b
|
||||
|
||||
4: neg r10
|
||||
reteq r9
|
||||
|
||||
/* Handle unaligned count */
|
||||
lsl r10, 2
|
||||
add pc, pc, r10
|
||||
ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
retal r9
|
||||
|
||||
/* Handle unaligned "from" pointer */
|
||||
1: sub r10, 4
|
||||
brlt 4b
|
||||
add r10, r9
|
||||
lsl r9, 2
|
||||
add pc, pc, r9
|
||||
ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
rjmp 2b
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* Based on linux/arch/arm/lib/memset.S
|
||||
* Copyright (C) 1995-2000 Russell King
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ASM optimised string functions
|
||||
*/
|
||||
#include <asm/asm.h>
|
||||
|
||||
/*
|
||||
* r12: void *b
|
||||
* r11: int c
|
||||
* r10: size_t len
|
||||
*
|
||||
* Returns b in r12
|
||||
*/
|
||||
.text
|
||||
.global memset
|
||||
.type memset, @function
|
||||
.align 5
|
||||
memset:
|
||||
mov r9, r12
|
||||
mov r8, r12
|
||||
or r11, r11, r11 << 8
|
||||
andl r9, 3, COH
|
||||
brne 1f
|
||||
|
||||
2: or r11, r11, r11 << 16
|
||||
sub r10, 4
|
||||
brlt 5f
|
||||
|
||||
/* Let's do some real work */
|
||||
4: st.w r8++, r11
|
||||
sub r10, 4
|
||||
brge 4b
|
||||
|
||||
/*
|
||||
* When we get here, we've got less than 4 bytes to set. r10
|
||||
* might be negative.
|
||||
*/
|
||||
5: sub r10, -4
|
||||
reteq r12
|
||||
|
||||
/* Fastpath ends here, exactly 32 bytes from memset */
|
||||
|
||||
/* Handle unaligned count or pointer */
|
||||
bld r10, 1
|
||||
brcc 6f
|
||||
st.b r8++, r11
|
||||
st.b r8++, r11
|
||||
bld r10, 0
|
||||
retcc r12
|
||||
6: st.b r8++, r11
|
||||
retal r12
|
||||
|
||||
/* Handle unaligned pointer */
|
||||
1: sub r10, 4
|
||||
brlt 5b
|
||||
add r10, r9
|
||||
lsl r9, 1
|
||||
add pc, r9
|
||||
st.b r8++, r11
|
||||
st.b r8++, r11
|
||||
st.b r8++, r11
|
||||
rjmp 2b
|
||||
|
||||
.size memset, . - memset
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copy to/from userspace with optional address space checking.
|
||||
*
|
||||
* Copyright 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/errno.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
/*
|
||||
* long strncpy_from_user(char *dst, const char *src, long count)
|
||||
*
|
||||
* On success, returns the length of the string, not including
|
||||
* the terminating NUL.
|
||||
*
|
||||
* If the string is longer than count, returns count
|
||||
*
|
||||
* If userspace access fails, returns -EFAULT
|
||||
*/
|
||||
.text
|
||||
.align 1
|
||||
.global strncpy_from_user
|
||||
.type strncpy_from_user, "function"
|
||||
strncpy_from_user:
|
||||
mov r9, -EFAULT
|
||||
branch_if_kernel r8, __strncpy_from_user
|
||||
ret_if_privileged r8, r11, r10, r9
|
||||
|
||||
.global __strncpy_from_user
|
||||
.type __strncpy_from_user, "function"
|
||||
__strncpy_from_user:
|
||||
cp.w r10, 0
|
||||
reteq 0
|
||||
|
||||
mov r9, r10
|
||||
|
||||
1: ld.ub r8, r11++
|
||||
st.b r12++, r8
|
||||
cp.w r8, 0
|
||||
breq 2f
|
||||
sub r9, 1
|
||||
brne 1b
|
||||
|
||||
2: sub r10, r9
|
||||
retal r10
|
||||
|
||||
.section .fixup, "ax"
|
||||
.align 1
|
||||
3: mov r12, -EFAULT
|
||||
retal r12
|
||||
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 1b, 3b
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copy to/from userspace with optional address space checking.
|
||||
*
|
||||
* Copyright 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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 <asm/page.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
.text
|
||||
.align 1
|
||||
.global strnlen_user
|
||||
.type strnlen_user, "function"
|
||||
strnlen_user:
|
||||
branch_if_kernel r8, __strnlen_user
|
||||
sub r8, r11, 1
|
||||
add r8, r12
|
||||
retcs 0
|
||||
brmi adjust_length /* do a closer inspection */
|
||||
|
||||
.global __strnlen_user
|
||||
.type __strnlen_user, "function"
|
||||
__strnlen_user:
|
||||
mov r10, r12
|
||||
|
||||
10: ld.ub r8, r12++
|
||||
cp.w r8, 0
|
||||
breq 2f
|
||||
sub r11, 1
|
||||
brne 10b
|
||||
|
||||
sub r12, -1
|
||||
2: sub r12, r10
|
||||
retal r12
|
||||
|
||||
|
||||
.type adjust_length, "function"
|
||||
adjust_length:
|
||||
cp.w r12, 0 /* addr must always be < TASK_SIZE */
|
||||
retmi 0
|
||||
|
||||
pushm lr
|
||||
lddpc lr, _task_size
|
||||
sub r11, lr, r12
|
||||
mov r9, r11
|
||||
rcall __strnlen_user
|
||||
cp.w r12, r9
|
||||
brgt 1f
|
||||
popm pc
|
||||
1: popm pc, r12=0
|
||||
|
||||
.align 2
|
||||
_task_size:
|
||||
.long TASK_SIZE
|
||||
|
||||
.section .fixup, "ax"
|
||||
.align 1
|
||||
19: retal 0
|
||||
|
||||
.section __ex_table, "a"
|
||||
.align 2
|
||||
.long 10b, 19b
|
|
@ -0,0 +1,2 @@
|
|||
obj-y += at32ap.o clock.o pio.o intc.o extint.o hsmc.o
|
||||
obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/arch/init.h>
|
||||
#include <asm/arch/sm.h>
|
||||
|
||||
struct at32_sm system_manager;
|
||||
|
||||
static int __init at32_sm_init(void)
|
||||
{
|
||||
struct resource *regs;
|
||||
struct at32_sm *sm = &system_manager;
|
||||
int ret = -ENXIO;
|
||||
|
||||
regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
goto fail;
|
||||
|
||||
spin_lock_init(&sm->lock);
|
||||
sm->pdev = &at32_sm_device;
|
||||
|
||||
ret = -ENOMEM;
|
||||
sm->regs = ioremap(regs->start, regs->end - regs->start + 1);
|
||||
if (!sm->regs)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __init setup_platform(void)
|
||||
{
|
||||
at32_sm_init();
|
||||
at32_clock_init();
|
||||
at32_portmux_init();
|
||||
|
||||
/* FIXME: This doesn't belong here */
|
||||
at32_setup_serial_console(1);
|
||||
}
|
||||
|
||||
static int __init pdc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk *pclk, *hclk;
|
||||
|
||||
pclk = clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(pclk)) {
|
||||
dev_err(&pdev->dev, "no pclk defined\n");
|
||||
return PTR_ERR(pclk);
|
||||
}
|
||||
hclk = clk_get(&pdev->dev, "hclk");
|
||||
if (IS_ERR(hclk)) {
|
||||
dev_err(&pdev->dev, "no hclk defined\n");
|
||||
clk_put(pclk);
|
||||
return PTR_ERR(hclk);
|
||||
}
|
||||
|
||||
clk_enable(pclk);
|
||||
clk_enable(hclk);
|
||||
|
||||
dev_info(&pdev->dev, "Atmel Peripheral DMA Controller enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pdc_driver = {
|
||||
.probe = pdc_probe,
|
||||
.driver = {
|
||||
.name = "pdc",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pdc_init(void)
|
||||
{
|
||||
return platform_driver_register(&pdc_driver);
|
||||
}
|
||||
arch_initcall(pdc_init);
|
|
@ -0,0 +1,876 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Atmel Corporation
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/arch/board.h>
|
||||
#include <asm/arch/portmux.h>
|
||||
#include <asm/arch/sm.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "pio.h"
|
||||
#include "sm.h"
|
||||
|
||||
#define PBMEM(base) \
|
||||
{ \
|
||||
.start = base, \
|
||||
.end = base + 0x3ff, \
|
||||
.flags = IORESOURCE_MEM, \
|
||||
}
|
||||
#define IRQ(num) \
|
||||
{ \
|
||||
.start = num, \
|
||||
.end = num, \
|
||||
.flags = IORESOURCE_IRQ, \
|
||||
}
|
||||
#define NAMED_IRQ(num, _name) \
|
||||
{ \
|
||||
.start = num, \
|
||||
.end = num, \
|
||||
.name = _name, \
|
||||
.flags = IORESOURCE_IRQ, \
|
||||
}
|
||||
|
||||
#define DEFINE_DEV(_name, _id) \
|
||||
static struct platform_device _name##_id##_device = { \
|
||||
.name = #_name, \
|
||||
.id = _id, \
|
||||
.resource = _name##_id##_resource, \
|
||||
.num_resources = ARRAY_SIZE(_name##_id##_resource), \
|
||||
}
|
||||
#define DEFINE_DEV_DATA(_name, _id) \
|
||||
static struct platform_device _name##_id##_device = { \
|
||||
.name = #_name, \
|
||||
.id = _id, \
|
||||
.dev = { \
|
||||
.platform_data = &_name##_id##_data, \
|
||||
}, \
|
||||
.resource = _name##_id##_resource, \
|
||||
.num_resources = ARRAY_SIZE(_name##_id##_resource), \
|
||||
}
|
||||
|
||||
#define DEV_CLK(_name, devname, bus, _index) \
|
||||
static struct clk devname##_##_name = { \
|
||||
.name = #_name, \
|
||||
.dev = &devname##_device.dev, \
|
||||
.parent = &bus##_clk, \
|
||||
.mode = bus##_clk_mode, \
|
||||
.get_rate = bus##_clk_get_rate, \
|
||||
.index = _index, \
|
||||
}
|
||||
|
||||
enum {
|
||||
PIOA,
|
||||
PIOB,
|
||||
PIOC,
|
||||
PIOD,
|
||||
};
|
||||
|
||||
enum {
|
||||
FUNC_A,
|
||||
FUNC_B,
|
||||
};
|
||||
|
||||
unsigned long at32ap7000_osc_rates[3] = {
|
||||
[0] = 32768,
|
||||
/* FIXME: these are ATSTK1002-specific */
|
||||
[1] = 20000000,
|
||||
[2] = 12000000,
|
||||
};
|
||||
|
||||
static unsigned long osc_get_rate(struct clk *clk)
|
||||
{
|
||||
return at32ap7000_osc_rates[clk->index];
|
||||
}
|
||||
|
||||
static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
|
||||
{
|
||||
unsigned long div, mul, rate;
|
||||
|
||||
if (!(control & SM_BIT(PLLEN)))
|
||||
return 0;
|
||||
|
||||
div = SM_BFEXT(PLLDIV, control) + 1;
|
||||
mul = SM_BFEXT(PLLMUL, control) + 1;
|
||||
|
||||
rate = clk->parent->get_rate(clk->parent);
|
||||
rate = (rate + div / 2) / div;
|
||||
rate *= mul;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long pll0_get_rate(struct clk *clk)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
control = sm_readl(&system_manager, PM_PLL0);
|
||||
|
||||
return pll_get_rate(clk, control);
|
||||
}
|
||||
|
||||
static unsigned long pll1_get_rate(struct clk *clk)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
control = sm_readl(&system_manager, PM_PLL1);
|
||||
|
||||
return pll_get_rate(clk, control);
|
||||
}
|
||||
|
||||
/*
|
||||
* The AT32AP7000 has five primary clock sources: One 32kHz
|
||||
* oscillator, two crystal oscillators and two PLLs.
|
||||
*/
|
||||
static struct clk osc32k = {
|
||||
.name = "osc32k",
|
||||
.get_rate = osc_get_rate,
|
||||
.users = 1,
|
||||
.index = 0,
|
||||
};
|
||||
static struct clk osc0 = {
|
||||
.name = "osc0",
|
||||
.get_rate = osc_get_rate,
|
||||
.users = 1,
|
||||
.index = 1,
|
||||
};
|
||||
static struct clk osc1 = {
|
||||
.name = "osc1",
|
||||
.get_rate = osc_get_rate,
|
||||
.index = 2,
|
||||
};
|
||||
static struct clk pll0 = {
|
||||
.name = "pll0",
|
||||
.get_rate = pll0_get_rate,
|
||||
.parent = &osc0,
|
||||
};
|
||||
static struct clk pll1 = {
|
||||
.name = "pll1",
|
||||
.get_rate = pll1_get_rate,
|
||||
.parent = &osc0,
|
||||
};
|
||||
|
||||
/*
|
||||
* The main clock can be either osc0 or pll0. The boot loader may
|
||||
* have chosen one for us, so we don't really know which one until we
|
||||
* have a look at the SM.
|
||||
*/
|
||||
static struct clk *main_clock;
|
||||
|
||||
/*
|
||||
* Synchronous clocks are generated from the main clock. The clocks
|
||||
* must satisfy the constraint
|
||||
* fCPU >= fHSB >= fPB
|
||||
* i.e. each clock must not be faster than its parent.
|
||||
*/
|
||||
static unsigned long bus_clk_get_rate(struct clk *clk, unsigned int shift)
|
||||
{
|
||||
return main_clock->get_rate(main_clock) >> shift;
|
||||
};
|
||||
|
||||
static void cpu_clk_mode(struct clk *clk, int enabled)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
mask = sm_readl(sm, PM_CPU_MASK);
|
||||
if (enabled)
|
||||
mask |= 1 << clk->index;
|
||||
else
|
||||
mask &= ~(1 << clk->index);
|
||||
sm_writel(sm, PM_CPU_MASK, mask);
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long cpu_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
unsigned long cksel, shift = 0;
|
||||
|
||||
cksel = sm_readl(&system_manager, PM_CKSEL);
|
||||
if (cksel & SM_BIT(CPUDIV))
|
||||
shift = SM_BFEXT(CPUSEL, cksel) + 1;
|
||||
|
||||
return bus_clk_get_rate(clk, shift);
|
||||
}
|
||||
|
||||
static void hsb_clk_mode(struct clk *clk, int enabled)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
mask = sm_readl(sm, PM_HSB_MASK);
|
||||
if (enabled)
|
||||
mask |= 1 << clk->index;
|
||||
else
|
||||
mask &= ~(1 << clk->index);
|
||||
sm_writel(sm, PM_HSB_MASK, mask);
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long hsb_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
unsigned long cksel, shift = 0;
|
||||
|
||||
cksel = sm_readl(&system_manager, PM_CKSEL);
|
||||
if (cksel & SM_BIT(HSBDIV))
|
||||
shift = SM_BFEXT(HSBSEL, cksel) + 1;
|
||||
|
||||
return bus_clk_get_rate(clk, shift);
|
||||
}
|
||||
|
||||
static void pba_clk_mode(struct clk *clk, int enabled)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
mask = sm_readl(sm, PM_PBA_MASK);
|
||||
if (enabled)
|
||||
mask |= 1 << clk->index;
|
||||
else
|
||||
mask &= ~(1 << clk->index);
|
||||
sm_writel(sm, PM_PBA_MASK, mask);
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long pba_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
unsigned long cksel, shift = 0;
|
||||
|
||||
cksel = sm_readl(&system_manager, PM_CKSEL);
|
||||
if (cksel & SM_BIT(PBADIV))
|
||||
shift = SM_BFEXT(PBASEL, cksel) + 1;
|
||||
|
||||
return bus_clk_get_rate(clk, shift);
|
||||
}
|
||||
|
||||
static void pbb_clk_mode(struct clk *clk, int enabled)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
mask = sm_readl(sm, PM_PBB_MASK);
|
||||
if (enabled)
|
||||
mask |= 1 << clk->index;
|
||||
else
|
||||
mask &= ~(1 << clk->index);
|
||||
sm_writel(sm, PM_PBB_MASK, mask);
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long pbb_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
unsigned long cksel, shift = 0;
|
||||
|
||||
cksel = sm_readl(&system_manager, PM_CKSEL);
|
||||
if (cksel & SM_BIT(PBBDIV))
|
||||
shift = SM_BFEXT(PBBSEL, cksel) + 1;
|
||||
|
||||
return bus_clk_get_rate(clk, shift);
|
||||
}
|
||||
|
||||
static struct clk cpu_clk = {
|
||||
.name = "cpu",
|
||||
.get_rate = cpu_clk_get_rate,
|
||||
.users = 1,
|
||||
};
|
||||
static struct clk hsb_clk = {
|
||||
.name = "hsb",
|
||||
.parent = &cpu_clk,
|
||||
.get_rate = hsb_clk_get_rate,
|
||||
};
|
||||
static struct clk pba_clk = {
|
||||
.name = "pba",
|
||||
.parent = &hsb_clk,
|
||||
.mode = hsb_clk_mode,
|
||||
.get_rate = pba_clk_get_rate,
|
||||
.index = 1,
|
||||
};
|
||||
static struct clk pbb_clk = {
|
||||
.name = "pbb",
|
||||
.parent = &hsb_clk,
|
||||
.mode = hsb_clk_mode,
|
||||
.get_rate = pbb_clk_get_rate,
|
||||
.users = 1,
|
||||
.index = 2,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Generic Clock operations
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
static void genclk_mode(struct clk *clk, int enabled)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
BUG_ON(clk->index > 7);
|
||||
|
||||
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
|
||||
if (enabled)
|
||||
control |= SM_BIT(CEN);
|
||||
else
|
||||
control &= ~SM_BIT(CEN);
|
||||
sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, control);
|
||||
}
|
||||
|
||||
static unsigned long genclk_get_rate(struct clk *clk)
|
||||
{
|
||||
u32 control;
|
||||
unsigned long div = 1;
|
||||
|
||||
BUG_ON(clk->index > 7);
|
||||
|
||||
if (!clk->parent)
|
||||
return 0;
|
||||
|
||||
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
|
||||
if (control & SM_BIT(DIVEN))
|
||||
div = 2 * (SM_BFEXT(DIV, control) + 1);
|
||||
|
||||
return clk->parent->get_rate(clk->parent) / div;
|
||||
}
|
||||
|
||||
static long genclk_set_rate(struct clk *clk, unsigned long rate, int apply)
|
||||
{
|
||||
u32 control;
|
||||
unsigned long parent_rate, actual_rate, div;
|
||||
|
||||
BUG_ON(clk->index > 7);
|
||||
|
||||
if (!clk->parent)
|
||||
return 0;
|
||||
|
||||
parent_rate = clk->parent->get_rate(clk->parent);
|
||||
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
|
||||
|
||||
if (rate > 3 * parent_rate / 4) {
|
||||
actual_rate = parent_rate;
|
||||
control &= ~SM_BIT(DIVEN);
|
||||
} else {
|
||||
div = (parent_rate + rate) / (2 * rate) - 1;
|
||||
control = SM_BFINS(DIV, div, control) | SM_BIT(DIVEN);
|
||||
actual_rate = parent_rate / (2 * (div + 1));
|
||||
}
|
||||
|
||||
printk("clk %s: new rate %lu (actual rate %lu)\n",
|
||||
clk->name, rate, actual_rate);
|
||||
|
||||
if (apply)
|
||||
sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index,
|
||||
control);
|
||||
|
||||
return actual_rate;
|
||||
}
|
||||
|
||||
int genclk_set_parent(struct clk *clk, struct clk *parent)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
BUG_ON(clk->index > 7);
|
||||
|
||||
printk("clk %s: new parent %s (was %s)\n",
|
||||
clk->name, parent->name,
|
||||
clk->parent ? clk->parent->name : "(null)");
|
||||
|
||||
control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
|
||||
|
||||
if (parent == &osc1 || parent == &pll1)
|
||||
control |= SM_BIT(OSCSEL);
|
||||
else if (parent == &osc0 || parent == &pll0)
|
||||
control &= ~SM_BIT(OSCSEL);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (parent == &pll0 || parent == &pll1)
|
||||
control |= SM_BIT(PLLSEL);
|
||||
else
|
||||
control &= ~SM_BIT(PLLSEL);
|
||||
|
||||
sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, control);
|
||||
clk->parent = parent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* System peripherals
|
||||
* -------------------------------------------------------------------- */
|
||||
static struct resource sm_resource[] = {
|
||||
PBMEM(0xfff00000),
|
||||
NAMED_IRQ(19, "eim"),
|
||||
NAMED_IRQ(20, "pm"),
|
||||
NAMED_IRQ(21, "rtc"),
|
||||
};
|
||||
struct platform_device at32_sm_device = {
|
||||
.name = "sm",
|
||||
.id = 0,
|
||||
.resource = sm_resource,
|
||||
.num_resources = ARRAY_SIZE(sm_resource),
|
||||
};
|
||||
DEV_CLK(pclk, at32_sm, pbb, 0);
|
||||
|
||||
static struct resource intc0_resource[] = {
|
||||
PBMEM(0xfff00400),
|
||||
};
|
||||
struct platform_device at32_intc0_device = {
|
||||
.name = "intc",
|
||||
.id = 0,
|
||||
.resource = intc0_resource,
|
||||
.num_resources = ARRAY_SIZE(intc0_resource),
|
||||
};
|
||||
DEV_CLK(pclk, at32_intc0, pbb, 1);
|
||||
|
||||
static struct clk ebi_clk = {
|
||||
.name = "ebi",
|
||||
.parent = &hsb_clk,
|
||||
.mode = hsb_clk_mode,
|
||||
.get_rate = hsb_clk_get_rate,
|
||||
.users = 1,
|
||||
};
|
||||
static struct clk hramc_clk = {
|
||||
.name = "hramc",
|
||||
.parent = &hsb_clk,
|
||||
.mode = hsb_clk_mode,
|
||||
.get_rate = hsb_clk_get_rate,
|
||||
.users = 1,
|
||||
};
|
||||
|
||||
static struct resource smc0_resource[] = {
|
||||
PBMEM(0xfff03400),
|
||||
};
|
||||
DEFINE_DEV(smc, 0);
|
||||
DEV_CLK(pclk, smc0, pbb, 13);
|
||||
DEV_CLK(mck, smc0, hsb, 0);
|
||||
|
||||
static struct platform_device pdc_device = {
|
||||
.name = "pdc",
|
||||
.id = 0,
|
||||
};
|
||||
DEV_CLK(hclk, pdc, hsb, 4);
|
||||
DEV_CLK(pclk, pdc, pba, 16);
|
||||
|
||||
static struct clk pico_clk = {
|
||||
.name = "pico",
|
||||
.parent = &cpu_clk,
|
||||
.mode = cpu_clk_mode,
|
||||
.get_rate = cpu_clk_get_rate,
|
||||
.users = 1,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* PIO
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
static struct resource pio0_resource[] = {
|
||||
PBMEM(0xffe02800),
|
||||
IRQ(13),
|
||||
};
|
||||
DEFINE_DEV(pio, 0);
|
||||
DEV_CLK(mck, pio0, pba, 10);
|
||||
|
||||
static struct resource pio1_resource[] = {
|
||||
PBMEM(0xffe02c00),
|
||||
IRQ(14),
|
||||
};
|
||||
DEFINE_DEV(pio, 1);
|
||||
DEV_CLK(mck, pio1, pba, 11);
|
||||
|
||||
static struct resource pio2_resource[] = {
|
||||
PBMEM(0xffe03000),
|
||||
IRQ(15),
|
||||
};
|
||||
DEFINE_DEV(pio, 2);
|
||||
DEV_CLK(mck, pio2, pba, 12);
|
||||
|
||||
static struct resource pio3_resource[] = {
|
||||
PBMEM(0xffe03400),
|
||||
IRQ(16),
|
||||
};
|
||||
DEFINE_DEV(pio, 3);
|
||||
DEV_CLK(mck, pio3, pba, 13);
|
||||
|
||||
void __init at32_add_system_devices(void)
|
||||
{
|
||||
system_manager.eim_first_irq = NR_INTERNAL_IRQS;
|
||||
|
||||
platform_device_register(&at32_sm_device);
|
||||
platform_device_register(&at32_intc0_device);
|
||||
platform_device_register(&smc0_device);
|
||||
platform_device_register(&pdc_device);
|
||||
|
||||
platform_device_register(&pio0_device);
|
||||
platform_device_register(&pio1_device);
|
||||
platform_device_register(&pio2_device);
|
||||
platform_device_register(&pio3_device);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* USART
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
static struct resource usart0_resource[] = {
|
||||
PBMEM(0xffe00c00),
|
||||
IRQ(7),
|
||||
};
|
||||
DEFINE_DEV(usart, 0);
|
||||
DEV_CLK(usart, usart0, pba, 4);
|
||||
|
||||
static struct resource usart1_resource[] = {
|
||||
PBMEM(0xffe01000),
|
||||
IRQ(7),
|
||||
};
|
||||
DEFINE_DEV(usart, 1);
|
||||
DEV_CLK(usart, usart1, pba, 4);
|
||||
|
||||
static struct resource usart2_resource[] = {
|
||||
PBMEM(0xffe01400),
|
||||
IRQ(8),
|
||||
};
|
||||
DEFINE_DEV(usart, 2);
|
||||
DEV_CLK(usart, usart2, pba, 5);
|
||||
|
||||
static struct resource usart3_resource[] = {
|
||||
PBMEM(0xffe01800),
|
||||
IRQ(9),
|
||||
};
|
||||
DEFINE_DEV(usart, 3);
|
||||
DEV_CLK(usart, usart3, pba, 6);
|
||||
|
||||
static inline void configure_usart0_pins(void)
|
||||
{
|
||||
portmux_set_func(PIOA, 8, FUNC_B); /* RXD */
|
||||
portmux_set_func(PIOA, 9, FUNC_B); /* TXD */
|
||||
}
|
||||
|
||||
static inline void configure_usart1_pins(void)
|
||||
{
|
||||
portmux_set_func(PIOA, 17, FUNC_A); /* RXD */
|
||||
portmux_set_func(PIOA, 18, FUNC_A); /* TXD */
|
||||
}
|
||||
|
||||
static inline void configure_usart2_pins(void)
|
||||
{
|
||||
portmux_set_func(PIOB, 26, FUNC_B); /* RXD */
|
||||
portmux_set_func(PIOB, 27, FUNC_B); /* TXD */
|
||||
}
|
||||
|
||||
static inline void configure_usart3_pins(void)
|
||||
{
|
||||
portmux_set_func(PIOB, 18, FUNC_B); /* RXD */
|
||||
portmux_set_func(PIOB, 17, FUNC_B); /* TXD */
|
||||
}
|
||||
|
||||
static struct platform_device *setup_usart(unsigned int id)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
pdev = &usart0_device;
|
||||
configure_usart0_pins();
|
||||
break;
|
||||
case 1:
|
||||
pdev = &usart1_device;
|
||||
configure_usart1_pins();
|
||||
break;
|
||||
case 2:
|
||||
pdev = &usart2_device;
|
||||
configure_usart2_pins();
|
||||
break;
|
||||
case 3:
|
||||
pdev = &usart3_device;
|
||||
configure_usart3_pins();
|
||||
break;
|
||||
default:
|
||||
pdev = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
struct platform_device *__init at32_add_device_usart(unsigned int id)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
pdev = setup_usart(id);
|
||||
if (pdev)
|
||||
platform_device_register(pdev);
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
struct platform_device *at91_default_console_device;
|
||||
|
||||
void __init at32_setup_serial_console(unsigned int usart_id)
|
||||
{
|
||||
at91_default_console_device = setup_usart(usart_id);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Ethernet
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
static struct eth_platform_data macb0_data;
|
||||
static struct resource macb0_resource[] = {
|
||||
PBMEM(0xfff01800),
|
||||
IRQ(25),
|
||||
};
|
||||
DEFINE_DEV_DATA(macb, 0);
|
||||
DEV_CLK(hclk, macb0, hsb, 8);
|
||||
DEV_CLK(pclk, macb0, pbb, 6);
|
||||
|
||||
struct platform_device *__init
|
||||
at32_add_device_eth(unsigned int id, struct eth_platform_data *data)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
pdev = &macb0_device;
|
||||
|
||||
portmux_set_func(PIOC, 3, FUNC_A); /* TXD0 */
|
||||
portmux_set_func(PIOC, 4, FUNC_A); /* TXD1 */
|
||||
portmux_set_func(PIOC, 7, FUNC_A); /* TXEN */
|
||||
portmux_set_func(PIOC, 8, FUNC_A); /* TXCK */
|
||||
portmux_set_func(PIOC, 9, FUNC_A); /* RXD0 */
|
||||
portmux_set_func(PIOC, 10, FUNC_A); /* RXD1 */
|
||||
portmux_set_func(PIOC, 13, FUNC_A); /* RXER */
|
||||
portmux_set_func(PIOC, 15, FUNC_A); /* RXDV */
|
||||
portmux_set_func(PIOC, 16, FUNC_A); /* MDC */
|
||||
portmux_set_func(PIOC, 17, FUNC_A); /* MDIO */
|
||||
|
||||
if (!data->is_rmii) {
|
||||
portmux_set_func(PIOC, 0, FUNC_A); /* COL */
|
||||
portmux_set_func(PIOC, 1, FUNC_A); /* CRS */
|
||||
portmux_set_func(PIOC, 2, FUNC_A); /* TXER */
|
||||
portmux_set_func(PIOC, 5, FUNC_A); /* TXD2 */
|
||||
portmux_set_func(PIOC, 6, FUNC_A); /* TXD3 */
|
||||
portmux_set_func(PIOC, 11, FUNC_A); /* RXD2 */
|
||||
portmux_set_func(PIOC, 12, FUNC_A); /* RXD3 */
|
||||
portmux_set_func(PIOC, 14, FUNC_A); /* RXCK */
|
||||
portmux_set_func(PIOC, 18, FUNC_A); /* SPD */
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(pdev->dev.platform_data, data, sizeof(struct eth_platform_data));
|
||||
platform_device_register(pdev);
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* SPI
|
||||
* -------------------------------------------------------------------- */
|
||||
static struct resource spi0_resource[] = {
|
||||
PBMEM(0xffe00000),
|
||||
IRQ(3),
|
||||
};
|
||||
DEFINE_DEV(spi, 0);
|
||||
DEV_CLK(mck, spi0, pba, 0);
|
||||
|
||||
struct platform_device *__init at32_add_device_spi(unsigned int id)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
pdev = &spi0_device;
|
||||
portmux_set_func(PIOA, 0, FUNC_A); /* MISO */
|
||||
portmux_set_func(PIOA, 1, FUNC_A); /* MOSI */
|
||||
portmux_set_func(PIOA, 2, FUNC_A); /* SCK */
|
||||
portmux_set_func(PIOA, 3, FUNC_A); /* NPCS0 */
|
||||
portmux_set_func(PIOA, 4, FUNC_A); /* NPCS1 */
|
||||
portmux_set_func(PIOA, 5, FUNC_A); /* NPCS2 */
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
platform_device_register(pdev);
|
||||
return pdev;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* LCDC
|
||||
* -------------------------------------------------------------------- */
|
||||
static struct lcdc_platform_data lcdc0_data;
|
||||
static struct resource lcdc0_resource[] = {
|
||||
{
|
||||
.start = 0xff000000,
|
||||
.end = 0xff000fff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
IRQ(1),
|
||||
};
|
||||
DEFINE_DEV_DATA(lcdc, 0);
|
||||
DEV_CLK(hclk, lcdc0, hsb, 7);
|
||||
static struct clk lcdc0_pixclk = {
|
||||
.name = "pixclk",
|
||||
.dev = &lcdc0_device.dev,
|
||||
.mode = genclk_mode,
|
||||
.get_rate = genclk_get_rate,
|
||||
.set_rate = genclk_set_rate,
|
||||
.set_parent = genclk_set_parent,
|
||||
.index = 7,
|
||||
};
|
||||
|
||||
struct platform_device *__init
|
||||
at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
pdev = &lcdc0_device;
|
||||
portmux_set_func(PIOC, 19, FUNC_A); /* CC */
|
||||
portmux_set_func(PIOC, 20, FUNC_A); /* HSYNC */
|
||||
portmux_set_func(PIOC, 21, FUNC_A); /* PCLK */
|
||||
portmux_set_func(PIOC, 22, FUNC_A); /* VSYNC */
|
||||
portmux_set_func(PIOC, 23, FUNC_A); /* DVAL */
|
||||
portmux_set_func(PIOC, 24, FUNC_A); /* MODE */
|
||||
portmux_set_func(PIOC, 25, FUNC_A); /* PWR */
|
||||
portmux_set_func(PIOC, 26, FUNC_A); /* DATA0 */
|
||||
portmux_set_func(PIOC, 27, FUNC_A); /* DATA1 */
|
||||
portmux_set_func(PIOC, 28, FUNC_A); /* DATA2 */
|
||||
portmux_set_func(PIOC, 29, FUNC_A); /* DATA3 */
|
||||
portmux_set_func(PIOC, 30, FUNC_A); /* DATA4 */
|
||||
portmux_set_func(PIOC, 31, FUNC_A); /* DATA5 */
|
||||
portmux_set_func(PIOD, 0, FUNC_A); /* DATA6 */
|
||||
portmux_set_func(PIOD, 1, FUNC_A); /* DATA7 */
|
||||
portmux_set_func(PIOD, 2, FUNC_A); /* DATA8 */
|
||||
portmux_set_func(PIOD, 3, FUNC_A); /* DATA9 */
|
||||
portmux_set_func(PIOD, 4, FUNC_A); /* DATA10 */
|
||||
portmux_set_func(PIOD, 5, FUNC_A); /* DATA11 */
|
||||
portmux_set_func(PIOD, 6, FUNC_A); /* DATA12 */
|
||||
portmux_set_func(PIOD, 7, FUNC_A); /* DATA13 */
|
||||
portmux_set_func(PIOD, 8, FUNC_A); /* DATA14 */
|
||||
portmux_set_func(PIOD, 9, FUNC_A); /* DATA15 */
|
||||
portmux_set_func(PIOD, 10, FUNC_A); /* DATA16 */
|
||||
portmux_set_func(PIOD, 11, FUNC_A); /* DATA17 */
|
||||
portmux_set_func(PIOD, 12, FUNC_A); /* DATA18 */
|
||||
portmux_set_func(PIOD, 13, FUNC_A); /* DATA19 */
|
||||
portmux_set_func(PIOD, 14, FUNC_A); /* DATA20 */
|
||||
portmux_set_func(PIOD, 15, FUNC_A); /* DATA21 */
|
||||
portmux_set_func(PIOD, 16, FUNC_A); /* DATA22 */
|
||||
portmux_set_func(PIOD, 17, FUNC_A); /* DATA23 */
|
||||
|
||||
clk_set_parent(&lcdc0_pixclk, &pll0);
|
||||
clk_set_rate(&lcdc0_pixclk, clk_get_rate(&pll0));
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(pdev->dev.platform_data, data,
|
||||
sizeof(struct lcdc_platform_data));
|
||||
|
||||
platform_device_register(pdev);
|
||||
return pdev;
|
||||
}
|
||||
|
||||
struct clk *at32_clock_list[] = {
|
||||
&osc32k,
|
||||
&osc0,
|
||||
&osc1,
|
||||
&pll0,
|
||||
&pll1,
|
||||
&cpu_clk,
|
||||
&hsb_clk,
|
||||
&pba_clk,
|
||||
&pbb_clk,
|
||||
&at32_sm_pclk,
|
||||
&at32_intc0_pclk,
|
||||
&ebi_clk,
|
||||
&hramc_clk,
|
||||
&smc0_pclk,
|
||||
&smc0_mck,
|
||||
&pdc_hclk,
|
||||
&pdc_pclk,
|
||||
&pico_clk,
|
||||
&pio0_mck,
|
||||
&pio1_mck,
|
||||
&pio2_mck,
|
||||
&pio3_mck,
|
||||
&usart0_usart,
|
||||
&usart1_usart,
|
||||
&usart2_usart,
|
||||
&usart3_usart,
|
||||
&macb0_hclk,
|
||||
&macb0_pclk,
|
||||
&spi0_mck,
|
||||
&lcdc0_hclk,
|
||||
&lcdc0_pixclk,
|
||||
};
|
||||
unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
|
||||
|
||||
void __init at32_portmux_init(void)
|
||||
{
|
||||
at32_init_pio(&pio0_device);
|
||||
at32_init_pio(&pio1_device);
|
||||
at32_init_pio(&pio2_device);
|
||||
at32_init_pio(&pio3_device);
|
||||
}
|
||||
|
||||
void __init at32_clock_init(void)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0;
|
||||
int i;
|
||||
|
||||
if (sm_readl(sm, PM_MCCTRL) & SM_BIT(PLLSEL))
|
||||
main_clock = &pll0;
|
||||
else
|
||||
main_clock = &osc0;
|
||||
|
||||
if (sm_readl(sm, PM_PLL0) & SM_BIT(PLLOSC))
|
||||
pll0.parent = &osc1;
|
||||
if (sm_readl(sm, PM_PLL1) & SM_BIT(PLLOSC))
|
||||
pll1.parent = &osc1;
|
||||
|
||||
/*
|
||||
* Turn on all clocks that have at least one user already, and
|
||||
* turn off everything else. We only do this for module
|
||||
* clocks, and even though it isn't particularly pretty to
|
||||
* check the address of the mode function, it should do the
|
||||
* trick...
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) {
|
||||
struct clk *clk = at32_clock_list[i];
|
||||
|
||||
if (clk->mode == &cpu_clk_mode)
|
||||
cpu_mask |= 1 << clk->index;
|
||||
else if (clk->mode == &hsb_clk_mode)
|
||||
hsb_mask |= 1 << clk->index;
|
||||
else if (clk->mode == &pba_clk_mode)
|
||||
pba_mask |= 1 << clk->index;
|
||||
else if (clk->mode == &pbb_clk_mode)
|
||||
pbb_mask |= 1 << clk->index;
|
||||
}
|
||||
|
||||
sm_writel(sm, PM_CPU_MASK, cpu_mask);
|
||||
sm_writel(sm, PM_HSB_MASK, hsb_mask);
|
||||
sm_writel(sm, PM_PBA_MASK, pba_mask);
|
||||
sm_writel(sm, PM_PBB_MASK, pbb_mask);
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Clock management for AT32AP CPUs
|
||||
*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* Based on arch/arm/mach-at91rm9200/clock.c
|
||||
* Copyright (C) 2005 David Brownell
|
||||
* Copyright (C) 2005 Ivan Kokshaysky
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
static spinlock_t clk_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
struct clk *clk_get(struct device *dev, const char *id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < at32_nr_clocks; i++) {
|
||||
struct clk *clk = at32_clock_list[i];
|
||||
|
||||
if (clk->dev == dev && strcmp(id, clk->name) == 0)
|
||||
return clk;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get);
|
||||
|
||||
void clk_put(struct clk *clk)
|
||||
{
|
||||
/* clocks are static for now, we can't free them */
|
||||
}
|
||||
EXPORT_SYMBOL(clk_put);
|
||||
|
||||
static void __clk_enable(struct clk *clk)
|
||||
{
|
||||
if (clk->parent)
|
||||
__clk_enable(clk->parent);
|
||||
if (clk->users++ == 0 && clk->mode)
|
||||
clk->mode(clk, 1);
|
||||
}
|
||||
|
||||
int clk_enable(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clk_lock, flags);
|
||||
__clk_enable(clk);
|
||||
spin_unlock_irqrestore(&clk_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_enable);
|
||||
|
||||
static void __clk_disable(struct clk *clk)
|
||||
{
|
||||
BUG_ON(clk->users == 0);
|
||||
|
||||
if (--clk->users == 0 && clk->mode)
|
||||
clk->mode(clk, 0);
|
||||
if (clk->parent)
|
||||
__clk_disable(clk->parent);
|
||||
}
|
||||
|
||||
void clk_disable(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clk_lock, flags);
|
||||
__clk_disable(clk);
|
||||
spin_unlock_irqrestore(&clk_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_disable);
|
||||
|
||||
unsigned long clk_get_rate(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long rate;
|
||||
|
||||
spin_lock_irqsave(&clk_lock, flags);
|
||||
rate = clk->get_rate(clk);
|
||||
spin_unlock_irqrestore(&clk_lock, flags);
|
||||
|
||||
return rate;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_rate);
|
||||
|
||||
long clk_round_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
unsigned long flags, actual_rate;
|
||||
|
||||
if (!clk->set_rate)
|
||||
return -ENOSYS;
|
||||
|
||||
spin_lock_irqsave(&clk_lock, flags);
|
||||
actual_rate = clk->set_rate(clk, rate, 0);
|
||||
spin_unlock_irqrestore(&clk_lock, flags);
|
||||
|
||||
return actual_rate;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_round_rate);
|
||||
|
||||
int clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
unsigned long flags;
|
||||
long ret;
|
||||
|
||||
if (!clk->set_rate)
|
||||
return -ENOSYS;
|
||||
|
||||
spin_lock_irqsave(&clk_lock, flags);
|
||||
ret = clk->set_rate(clk, rate, 1);
|
||||
spin_unlock_irqrestore(&clk_lock, flags);
|
||||
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_rate);
|
||||
|
||||
int clk_set_parent(struct clk *clk, struct clk *parent)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!clk->set_parent)
|
||||
return -ENOSYS;
|
||||
|
||||
spin_lock_irqsave(&clk_lock, flags);
|
||||
ret = clk->set_parent(clk, parent);
|
||||
spin_unlock_irqrestore(&clk_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_parent);
|
||||
|
||||
struct clk *clk_get_parent(struct clk *clk)
|
||||
{
|
||||
return clk->parent;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_parent);
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Clock management for AT32AP CPUs
|
||||
*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* Based on arch/arm/mach-at91rm9200/clock.c
|
||||
* Copyright (C) 2005 David Brownell
|
||||
* Copyright (C) 2005 Ivan Kokshaysky
|
||||
*
|
||||
* 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/clk.h>
|
||||
|
||||
struct clk {
|
||||
const char *name; /* Clock name/function */
|
||||
struct device *dev; /* Device the clock is used by */
|
||||
struct clk *parent; /* Parent clock, if any */
|
||||
void (*mode)(struct clk *clk, int enabled);
|
||||
unsigned long (*get_rate)(struct clk *clk);
|
||||
long (*set_rate)(struct clk *clk, unsigned long rate,
|
||||
int apply);
|
||||
int (*set_parent)(struct clk *clk, struct clk *parent);
|
||||
u16 users; /* Enabled if non-zero */
|
||||
u16 index; /* Sibling index */
|
||||
};
|
||||
|
||||
extern struct clk *at32_clock_list[];
|
||||
extern unsigned int at32_nr_clocks;
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* External interrupt handling for AT32AP CPUs
|
||||
*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/arch/sm.h>
|
||||
|
||||
#include "sm.h"
|
||||
|
||||
static void eim_ack_irq(unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = get_irq_chip_data(irq);
|
||||
sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq));
|
||||
}
|
||||
|
||||
static void eim_mask_irq(unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = get_irq_chip_data(irq);
|
||||
sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq));
|
||||
}
|
||||
|
||||
static void eim_mask_ack_irq(unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = get_irq_chip_data(irq);
|
||||
sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq));
|
||||
sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq));
|
||||
}
|
||||
|
||||
static void eim_unmask_irq(unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = get_irq_chip_data(irq);
|
||||
sm_writel(sm, EIM_IER, 1 << (irq - sm->eim_first_irq));
|
||||
}
|
||||
|
||||
static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
|
||||
{
|
||||
struct at32_sm *sm = get_irq_chip_data(irq);
|
||||
unsigned int i = irq - sm->eim_first_irq;
|
||||
u32 mode, edge, level;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
flow_type &= IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
|
||||
mode = sm_readl(sm, EIM_MODE);
|
||||
edge = sm_readl(sm, EIM_EDGE);
|
||||
level = sm_readl(sm, EIM_LEVEL);
|
||||
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
mode |= 1 << i;
|
||||
level &= ~(1 << i);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
mode |= 1 << i;
|
||||
level |= 1 << i;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
mode &= ~(1 << i);
|
||||
edge |= 1 << i;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
mode &= ~(1 << i);
|
||||
edge &= ~(1 << i);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
sm_writel(sm, EIM_MODE, mode);
|
||||
sm_writel(sm, EIM_EDGE, edge);
|
||||
sm_writel(sm, EIM_LEVEL, level);
|
||||
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct irq_chip eim_chip = {
|
||||
.name = "eim",
|
||||
.ack = eim_ack_irq,
|
||||
.mask = eim_mask_irq,
|
||||
.mask_ack = eim_mask_ack_irq,
|
||||
.unmask = eim_unmask_irq,
|
||||
.set_type = eim_set_irq_type,
|
||||
};
|
||||
|
||||
static void demux_eim_irq(unsigned int irq, struct irq_desc *desc,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct at32_sm *sm = desc->handler_data;
|
||||
struct irq_desc *ext_desc;
|
||||
unsigned long status, pending;
|
||||
unsigned int i, ext_irq;
|
||||
|
||||
spin_lock(&sm->lock);
|
||||
|
||||
status = sm_readl(sm, EIM_ISR);
|
||||
pending = status & sm_readl(sm, EIM_IMR);
|
||||
|
||||
while (pending) {
|
||||
i = fls(pending) - 1;
|
||||
pending &= ~(1 << i);
|
||||
|
||||
ext_irq = i + sm->eim_first_irq;
|
||||
ext_desc = irq_desc + ext_irq;
|
||||
ext_desc->handle_irq(ext_irq, ext_desc, regs);
|
||||
}
|
||||
|
||||
spin_unlock(&sm->lock);
|
||||
}
|
||||
|
||||
static int __init eim_init(void)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
unsigned int i;
|
||||
unsigned int nr_irqs;
|
||||
unsigned int int_irq;
|
||||
u32 pattern;
|
||||
|
||||
/*
|
||||
* The EIM is really the same module as SM, so register
|
||||
* mapping, etc. has been taken care of already.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Find out how many interrupt lines that are actually
|
||||
* implemented in hardware.
|
||||
*/
|
||||
sm_writel(sm, EIM_IDR, ~0UL);
|
||||
sm_writel(sm, EIM_MODE, ~0UL);
|
||||
pattern = sm_readl(sm, EIM_MODE);
|
||||
nr_irqs = fls(pattern);
|
||||
|
||||
sm->eim_chip = &eim_chip;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
set_irq_chip(sm->eim_first_irq + i, &eim_chip);
|
||||
set_irq_chip_data(sm->eim_first_irq + i, sm);
|
||||
}
|
||||
|
||||
int_irq = platform_get_irq_byname(sm->pdev, "eim");
|
||||
|
||||
set_irq_chained_handler(int_irq, demux_eim_irq);
|
||||
set_irq_data(int_irq, sm);
|
||||
|
||||
printk("EIM: External Interrupt Module at 0x%p, IRQ %u\n",
|
||||
sm->regs, int_irq);
|
||||
printk("EIM: Handling %u external IRQs, starting with IRQ %u\n",
|
||||
nr_irqs, sm->eim_first_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(eim_init);
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Static Memory Controller for AT32 chips
|
||||
*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define DEBUG
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/smc.h>
|
||||
|
||||
#include "hsmc.h"
|
||||
|
||||
#define NR_CHIP_SELECTS 6
|
||||
|
||||
struct hsmc {
|
||||
void __iomem *regs;
|
||||
struct clk *pclk;
|
||||
struct clk *mck;
|
||||
};
|
||||
|
||||
static struct hsmc *hsmc;
|
||||
|
||||
int smc_set_configuration(int cs, const struct smc_config *config)
|
||||
{
|
||||
unsigned long mul;
|
||||
unsigned long offset;
|
||||
u32 setup, pulse, cycle, mode;
|
||||
|
||||
if (!hsmc)
|
||||
return -ENODEV;
|
||||
if (cs >= NR_CHIP_SELECTS)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* cycles = x / T = x * f
|
||||
* = ((x * 1000000000) * ((f * 65536) / 1000000000)) / 65536
|
||||
* = ((x * 1000000000) * (((f / 10000) * 65536) / 100000)) / 65536
|
||||
*/
|
||||
mul = (clk_get_rate(hsmc->mck) / 10000) << 16;
|
||||
mul /= 100000;
|
||||
|
||||
#define ns2cyc(x) ((((x) * mul) + 65535) >> 16)
|
||||
|
||||
setup = (HSMC_BF(NWE_SETUP, ns2cyc(config->nwe_setup))
|
||||
| HSMC_BF(NCS_WR_SETUP, ns2cyc(config->ncs_write_setup))
|
||||
| HSMC_BF(NRD_SETUP, ns2cyc(config->nrd_setup))
|
||||
| HSMC_BF(NCS_RD_SETUP, ns2cyc(config->ncs_read_setup)));
|
||||
pulse = (HSMC_BF(NWE_PULSE, ns2cyc(config->nwe_pulse))
|
||||
| HSMC_BF(NCS_WR_PULSE, ns2cyc(config->ncs_write_pulse))
|
||||
| HSMC_BF(NRD_PULSE, ns2cyc(config->nrd_pulse))
|
||||
| HSMC_BF(NCS_RD_PULSE, ns2cyc(config->ncs_read_pulse)));
|
||||
cycle = (HSMC_BF(NWE_CYCLE, ns2cyc(config->write_cycle))
|
||||
| HSMC_BF(NRD_CYCLE, ns2cyc(config->read_cycle)));
|
||||
|
||||
switch (config->bus_width) {
|
||||
case 1:
|
||||
mode = HSMC_BF(DBW, HSMC_DBW_8_BITS);
|
||||
break;
|
||||
case 2:
|
||||
mode = HSMC_BF(DBW, HSMC_DBW_16_BITS);
|
||||
break;
|
||||
case 4:
|
||||
mode = HSMC_BF(DBW, HSMC_DBW_32_BITS);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config->nrd_controlled)
|
||||
mode |= HSMC_BIT(READ_MODE);
|
||||
if (config->nwe_controlled)
|
||||
mode |= HSMC_BIT(WRITE_MODE);
|
||||
if (config->byte_write)
|
||||
mode |= HSMC_BIT(BAT);
|
||||
|
||||
pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n",
|
||||
cs, setup, pulse, cycle, mode);
|
||||
|
||||
offset = cs * 0x10;
|
||||
hsmc_writel(hsmc, SETUP0 + offset, setup);
|
||||
hsmc_writel(hsmc, PULSE0 + offset, pulse);
|
||||
hsmc_writel(hsmc, CYCLE0 + offset, cycle);
|
||||
hsmc_writel(hsmc, MODE0 + offset, mode);
|
||||
hsmc_readl(hsmc, MODE0); /* I/O barrier */
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(smc_set_configuration);
|
||||
|
||||
static int hsmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *regs;
|
||||
struct clk *pclk, *mck;
|
||||
int ret;
|
||||
|
||||
if (hsmc)
|
||||
return -EBUSY;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
return -ENXIO;
|
||||
pclk = clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(pclk))
|
||||
return PTR_ERR(pclk);
|
||||
mck = clk_get(&pdev->dev, "mck");
|
||||
if (IS_ERR(mck)) {
|
||||
ret = PTR_ERR(mck);
|
||||
goto out_put_pclk;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
hsmc = kzalloc(sizeof(struct hsmc), GFP_KERNEL);
|
||||
if (!hsmc)
|
||||
goto out_put_clocks;
|
||||
|
||||
clk_enable(pclk);
|
||||
clk_enable(mck);
|
||||
|
||||
hsmc->pclk = pclk;
|
||||
hsmc->mck = mck;
|
||||
hsmc->regs = ioremap(regs->start, regs->end - regs->start + 1);
|
||||
if (!hsmc->regs)
|
||||
goto out_disable_clocks;
|
||||
|
||||
dev_info(&pdev->dev, "Atmel Static Memory Controller at 0x%08lx\n",
|
||||
(unsigned long)regs->start);
|
||||
|
||||
platform_set_drvdata(pdev, hsmc);
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_clocks:
|
||||
clk_disable(mck);
|
||||
clk_disable(pclk);
|
||||
kfree(hsmc);
|
||||
out_put_clocks:
|
||||
clk_put(mck);
|
||||
out_put_pclk:
|
||||
clk_put(pclk);
|
||||
hsmc = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver hsmc_driver = {
|
||||
.probe = hsmc_probe,
|
||||
.driver = {
|
||||
.name = "smc",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init hsmc_init(void)
|
||||
{
|
||||
return platform_driver_register(&hsmc_driver);
|
||||
}
|
||||
arch_initcall(hsmc_init);
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Register definitions for Atmel Static Memory Controller (SMC)
|
||||
*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __ASM_AVR32_HSMC_H__
|
||||
#define __ASM_AVR32_HSMC_H__
|
||||
|
||||
/* HSMC register offsets */
|
||||
#define HSMC_SETUP0 0x0000
|
||||
#define HSMC_PULSE0 0x0004
|
||||
#define HSMC_CYCLE0 0x0008
|
||||
#define HSMC_MODE0 0x000c
|
||||
#define HSMC_SETUP1 0x0010
|
||||
#define HSMC_PULSE1 0x0014
|
||||
#define HSMC_CYCLE1 0x0018
|
||||
#define HSMC_MODE1 0x001c
|
||||
#define HSMC_SETUP2 0x0020
|
||||
#define HSMC_PULSE2 0x0024
|
||||
#define HSMC_CYCLE2 0x0028
|
||||
#define HSMC_MODE2 0x002c
|
||||
#define HSMC_SETUP3 0x0030
|
||||
#define HSMC_PULSE3 0x0034
|
||||
#define HSMC_CYCLE3 0x0038
|
||||
#define HSMC_MODE3 0x003c
|
||||
#define HSMC_SETUP4 0x0040
|
||||
#define HSMC_PULSE4 0x0044
|
||||
#define HSMC_CYCLE4 0x0048
|
||||
#define HSMC_MODE4 0x004c
|
||||
#define HSMC_SETUP5 0x0050
|
||||
#define HSMC_PULSE5 0x0054
|
||||
#define HSMC_CYCLE5 0x0058
|
||||
#define HSMC_MODE5 0x005c
|
||||
|
||||
/* Bitfields in SETUP0 */
|
||||
#define HSMC_NWE_SETUP_OFFSET 0
|
||||
#define HSMC_NWE_SETUP_SIZE 6
|
||||
#define HSMC_NCS_WR_SETUP_OFFSET 8
|
||||
#define HSMC_NCS_WR_SETUP_SIZE 6
|
||||
#define HSMC_NRD_SETUP_OFFSET 16
|
||||
#define HSMC_NRD_SETUP_SIZE 6
|
||||
#define HSMC_NCS_RD_SETUP_OFFSET 24
|
||||
#define HSMC_NCS_RD_SETUP_SIZE 6
|
||||
|
||||
/* Bitfields in PULSE0 */
|
||||
#define HSMC_NWE_PULSE_OFFSET 0
|
||||
#define HSMC_NWE_PULSE_SIZE 7
|
||||
#define HSMC_NCS_WR_PULSE_OFFSET 8
|
||||
#define HSMC_NCS_WR_PULSE_SIZE 7
|
||||
#define HSMC_NRD_PULSE_OFFSET 16
|
||||
#define HSMC_NRD_PULSE_SIZE 7
|
||||
#define HSMC_NCS_RD_PULSE_OFFSET 24
|
||||
#define HSMC_NCS_RD_PULSE_SIZE 7
|
||||
|
||||
/* Bitfields in CYCLE0 */
|
||||
#define HSMC_NWE_CYCLE_OFFSET 0
|
||||
#define HSMC_NWE_CYCLE_SIZE 9
|
||||
#define HSMC_NRD_CYCLE_OFFSET 16
|
||||
#define HSMC_NRD_CYCLE_SIZE 9
|
||||
|
||||
/* Bitfields in MODE0 */
|
||||
#define HSMC_READ_MODE_OFFSET 0
|
||||
#define HSMC_READ_MODE_SIZE 1
|
||||
#define HSMC_WRITE_MODE_OFFSET 1
|
||||
#define HSMC_WRITE_MODE_SIZE 1
|
||||
#define HSMC_EXNW_MODE_OFFSET 4
|
||||
#define HSMC_EXNW_MODE_SIZE 2
|
||||
#define HSMC_BAT_OFFSET 8
|
||||
#define HSMC_BAT_SIZE 1
|
||||
#define HSMC_DBW_OFFSET 12
|
||||
#define HSMC_DBW_SIZE 2
|
||||
#define HSMC_TDF_CYCLES_OFFSET 16
|
||||
#define HSMC_TDF_CYCLES_SIZE 4
|
||||
#define HSMC_TDF_MODE_OFFSET 20
|
||||
#define HSMC_TDF_MODE_SIZE 1
|
||||
#define HSMC_PMEN_OFFSET 24
|
||||
#define HSMC_PMEN_SIZE 1
|
||||
#define HSMC_PS_OFFSET 28
|
||||
#define HSMC_PS_SIZE 2
|
||||
|
||||
/* Constants for READ_MODE */
|
||||
#define HSMC_READ_MODE_NCS_CONTROLLED 0
|
||||
#define HSMC_READ_MODE_NRD_CONTROLLED 1
|
||||
|
||||
/* Constants for WRITE_MODE */
|
||||
#define HSMC_WRITE_MODE_NCS_CONTROLLED 0
|
||||
#define HSMC_WRITE_MODE_NWE_CONTROLLED 1
|
||||
|
||||
/* Constants for EXNW_MODE */
|
||||
#define HSMC_EXNW_MODE_DISABLED 0
|
||||
#define HSMC_EXNW_MODE_RESERVED 1
|
||||
#define HSMC_EXNW_MODE_FROZEN 2
|
||||
#define HSMC_EXNW_MODE_READY 3
|
||||
|
||||
/* Constants for BAT */
|
||||
#define HSMC_BAT_BYTE_SELECT 0
|
||||
#define HSMC_BAT_BYTE_WRITE 1
|
||||
|
||||
/* Constants for DBW */
|
||||
#define HSMC_DBW_8_BITS 0
|
||||
#define HSMC_DBW_16_BITS 1
|
||||
#define HSMC_DBW_32_BITS 2
|
||||
|
||||
/* Bit manipulation macros */
|
||||
#define HSMC_BIT(name) \
|
||||
(1 << HSMC_##name##_OFFSET)
|
||||
#define HSMC_BF(name,value) \
|
||||
(((value) & ((1 << HSMC_##name##_SIZE) - 1)) \
|
||||
<< HSMC_##name##_OFFSET)
|
||||
#define HSMC_BFEXT(name,value) \
|
||||
(((value) >> HSMC_##name##_OFFSET) \
|
||||
& ((1 << HSMC_##name##_SIZE) - 1))
|
||||
#define HSMC_BFINS(name,value,old) \
|
||||
(((old) & ~(((1 << HSMC_##name##_SIZE) - 1) \
|
||||
<< HSMC_##name##_OFFSET)) | HSMC_BF(name,value))
|
||||
|
||||
/* Register access macros */
|
||||
#define hsmc_readl(port,reg) \
|
||||
readl((port)->regs + HSMC_##reg)
|
||||
#define hsmc_writel(port,reg,value) \
|
||||
writel((value), (port)->regs + HSMC_##reg)
|
||||
|
||||
#endif /* __ASM_AVR32_HSMC_H__ */
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "intc.h"
|
||||
|
||||
struct intc {
|
||||
void __iomem *regs;
|
||||
struct irq_chip chip;
|
||||
};
|
||||
|
||||
extern struct platform_device at32_intc0_device;
|
||||
|
||||
/*
|
||||
* TODO: We may be able to implement mask/unmask by setting IxM flags
|
||||
* in the status register.
|
||||
*/
|
||||
static void intc_mask_irq(unsigned int irq)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void intc_unmask_irq(unsigned int irq)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static struct intc intc0 = {
|
||||
.chip = {
|
||||
.name = "intc",
|
||||
.mask = intc_mask_irq,
|
||||
.unmask = intc_unmask_irq,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* All interrupts go via intc at some point.
|
||||
*/
|
||||
asmlinkage void do_IRQ(int level, struct pt_regs *regs)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
unsigned int irq;
|
||||
unsigned long status_reg;
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
irq_enter();
|
||||
|
||||
irq = intc_readl(&intc0, INTCAUSE0 - 4 * level);
|
||||
desc = irq_desc + irq;
|
||||
desc->handle_irq(irq, desc, regs);
|
||||
|
||||
/*
|
||||
* Clear all interrupt level masks so that we may handle
|
||||
* interrupts during softirq processing. If this is a nested
|
||||
* interrupt, interrupts must stay globally disabled until we
|
||||
* return.
|
||||
*/
|
||||
status_reg = sysreg_read(SR);
|
||||
status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M)
|
||||
| SYSREG_BIT(I2M) | SYSREG_BIT(I3M));
|
||||
sysreg_write(SR, status_reg);
|
||||
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
extern void _evba(void);
|
||||
extern void irq_level0(void);
|
||||
struct resource *regs;
|
||||
struct clk *pclk;
|
||||
unsigned int i;
|
||||
u32 offset, readback;
|
||||
|
||||
regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
printk(KERN_EMERG "intc: no mmio resource defined\n");
|
||||
goto fail;
|
||||
}
|
||||
pclk = clk_get(&at32_intc0_device.dev, "pclk");
|
||||
if (IS_ERR(pclk)) {
|
||||
printk(KERN_EMERG "intc: no clock defined\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clk_enable(pclk);
|
||||
|
||||
intc0.regs = ioremap(regs->start, regs->end - regs->start + 1);
|
||||
if (!intc0.regs) {
|
||||
printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n",
|
||||
(unsigned long)regs->start);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all interrupts to level 0 (lowest priority). The
|
||||
* priority level may be changed by calling
|
||||
* irq_set_priority().
|
||||
*
|
||||
*/
|
||||
offset = (unsigned long)&irq_level0 - (unsigned long)&_evba;
|
||||
for (i = 0; i < NR_INTERNAL_IRQS; i++) {
|
||||
intc_writel(&intc0, INTPR0 + 4 * i, offset);
|
||||
readback = intc_readl(&intc0, INTPR0 + 4 * i);
|
||||
if (readback == offset)
|
||||
set_irq_chip_and_handler(i, &intc0.chip,
|
||||
handle_simple_irq);
|
||||
}
|
||||
|
||||
/* Unmask all interrupt levels */
|
||||
sysreg_write(SR, (sysreg_read(SR)
|
||||
& ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M)));
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
panic("Interrupt controller initialization failed!\n");
|
||||
}
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* Automatically generated by gen-header.xsl
|
||||
*/
|
||||
#ifndef __ASM_AVR32_PERIHP_INTC_H__
|
||||
#define __ASM_AVR32_PERIHP_INTC_H__
|
||||
|
||||
#define INTC_NUM_INT_GRPS 33
|
||||
|
||||
#define INTC_INTPR0 0x0
|
||||
# define INTC_INTPR0_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR0_INTLEV_SIZE 2
|
||||
# define INTC_INTPR0_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR0_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ0 0x100
|
||||
# define INTC_INTREQ0_IREQUEST0_OFFSET 0
|
||||
# define INTC_INTREQ0_IREQUEST0_SIZE 1
|
||||
# define INTC_INTREQ0_IREQUEST1_OFFSET 1
|
||||
# define INTC_INTREQ0_IREQUEST1_SIZE 1
|
||||
#define INTC_INTPR1 0x4
|
||||
# define INTC_INTPR1_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR1_INTLEV_SIZE 2
|
||||
# define INTC_INTPR1_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR1_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ1 0x104
|
||||
# define INTC_INTREQ1_IREQUEST32_OFFSET 0
|
||||
# define INTC_INTREQ1_IREQUEST32_SIZE 1
|
||||
# define INTC_INTREQ1_IREQUEST33_OFFSET 1
|
||||
# define INTC_INTREQ1_IREQUEST33_SIZE 1
|
||||
# define INTC_INTREQ1_IREQUEST34_OFFSET 2
|
||||
# define INTC_INTREQ1_IREQUEST34_SIZE 1
|
||||
# define INTC_INTREQ1_IREQUEST35_OFFSET 3
|
||||
# define INTC_INTREQ1_IREQUEST35_SIZE 1
|
||||
# define INTC_INTREQ1_IREQUEST36_OFFSET 4
|
||||
# define INTC_INTREQ1_IREQUEST36_SIZE 1
|
||||
# define INTC_INTREQ1_IREQUEST37_OFFSET 5
|
||||
# define INTC_INTREQ1_IREQUEST37_SIZE 1
|
||||
#define INTC_INTPR2 0x8
|
||||
# define INTC_INTPR2_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR2_INTLEV_SIZE 2
|
||||
# define INTC_INTPR2_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR2_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ2 0x108
|
||||
# define INTC_INTREQ2_IREQUEST64_OFFSET 0
|
||||
# define INTC_INTREQ2_IREQUEST64_SIZE 1
|
||||
# define INTC_INTREQ2_IREQUEST65_OFFSET 1
|
||||
# define INTC_INTREQ2_IREQUEST65_SIZE 1
|
||||
# define INTC_INTREQ2_IREQUEST66_OFFSET 2
|
||||
# define INTC_INTREQ2_IREQUEST66_SIZE 1
|
||||
# define INTC_INTREQ2_IREQUEST67_OFFSET 3
|
||||
# define INTC_INTREQ2_IREQUEST67_SIZE 1
|
||||
# define INTC_INTREQ2_IREQUEST68_OFFSET 4
|
||||
# define INTC_INTREQ2_IREQUEST68_SIZE 1
|
||||
#define INTC_INTPR3 0xc
|
||||
# define INTC_INTPR3_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR3_INTLEV_SIZE 2
|
||||
# define INTC_INTPR3_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR3_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ3 0x10c
|
||||
# define INTC_INTREQ3_IREQUEST96_OFFSET 0
|
||||
# define INTC_INTREQ3_IREQUEST96_SIZE 1
|
||||
#define INTC_INTPR4 0x10
|
||||
# define INTC_INTPR4_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR4_INTLEV_SIZE 2
|
||||
# define INTC_INTPR4_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR4_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ4 0x110
|
||||
# define INTC_INTREQ4_IREQUEST128_OFFSET 0
|
||||
# define INTC_INTREQ4_IREQUEST128_SIZE 1
|
||||
#define INTC_INTPR5 0x14
|
||||
# define INTC_INTPR5_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR5_INTLEV_SIZE 2
|
||||
# define INTC_INTPR5_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR5_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ5 0x114
|
||||
# define INTC_INTREQ5_IREQUEST160_OFFSET 0
|
||||
# define INTC_INTREQ5_IREQUEST160_SIZE 1
|
||||
#define INTC_INTPR6 0x18
|
||||
# define INTC_INTPR6_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR6_INTLEV_SIZE 2
|
||||
# define INTC_INTPR6_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR6_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ6 0x118
|
||||
# define INTC_INTREQ6_IREQUEST192_OFFSET 0
|
||||
# define INTC_INTREQ6_IREQUEST192_SIZE 1
|
||||
#define INTC_INTPR7 0x1c
|
||||
# define INTC_INTPR7_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR7_INTLEV_SIZE 2
|
||||
# define INTC_INTPR7_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR7_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ7 0x11c
|
||||
# define INTC_INTREQ7_IREQUEST224_OFFSET 0
|
||||
# define INTC_INTREQ7_IREQUEST224_SIZE 1
|
||||
#define INTC_INTPR8 0x20
|
||||
# define INTC_INTPR8_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR8_INTLEV_SIZE 2
|
||||
# define INTC_INTPR8_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR8_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ8 0x120
|
||||
# define INTC_INTREQ8_IREQUEST256_OFFSET 0
|
||||
# define INTC_INTREQ8_IREQUEST256_SIZE 1
|
||||
#define INTC_INTPR9 0x24
|
||||
# define INTC_INTPR9_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR9_INTLEV_SIZE 2
|
||||
# define INTC_INTPR9_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR9_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ9 0x124
|
||||
# define INTC_INTREQ9_IREQUEST288_OFFSET 0
|
||||
# define INTC_INTREQ9_IREQUEST288_SIZE 1
|
||||
#define INTC_INTPR10 0x28
|
||||
# define INTC_INTPR10_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR10_INTLEV_SIZE 2
|
||||
# define INTC_INTPR10_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR10_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ10 0x128
|
||||
# define INTC_INTREQ10_IREQUEST320_OFFSET 0
|
||||
# define INTC_INTREQ10_IREQUEST320_SIZE 1
|
||||
#define INTC_INTPR11 0x2c
|
||||
# define INTC_INTPR11_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR11_INTLEV_SIZE 2
|
||||
# define INTC_INTPR11_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR11_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ11 0x12c
|
||||
# define INTC_INTREQ11_IREQUEST352_OFFSET 0
|
||||
# define INTC_INTREQ11_IREQUEST352_SIZE 1
|
||||
#define INTC_INTPR12 0x30
|
||||
# define INTC_INTPR12_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR12_INTLEV_SIZE 2
|
||||
# define INTC_INTPR12_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR12_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ12 0x130
|
||||
# define INTC_INTREQ12_IREQUEST384_OFFSET 0
|
||||
# define INTC_INTREQ12_IREQUEST384_SIZE 1
|
||||
#define INTC_INTPR13 0x34
|
||||
# define INTC_INTPR13_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR13_INTLEV_SIZE 2
|
||||
# define INTC_INTPR13_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR13_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ13 0x134
|
||||
# define INTC_INTREQ13_IREQUEST416_OFFSET 0
|
||||
# define INTC_INTREQ13_IREQUEST416_SIZE 1
|
||||
#define INTC_INTPR14 0x38
|
||||
# define INTC_INTPR14_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR14_INTLEV_SIZE 2
|
||||
# define INTC_INTPR14_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR14_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ14 0x138
|
||||
# define INTC_INTREQ14_IREQUEST448_OFFSET 0
|
||||
# define INTC_INTREQ14_IREQUEST448_SIZE 1
|
||||
#define INTC_INTPR15 0x3c
|
||||
# define INTC_INTPR15_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR15_INTLEV_SIZE 2
|
||||
# define INTC_INTPR15_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR15_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ15 0x13c
|
||||
# define INTC_INTREQ15_IREQUEST480_OFFSET 0
|
||||
# define INTC_INTREQ15_IREQUEST480_SIZE 1
|
||||
#define INTC_INTPR16 0x40
|
||||
# define INTC_INTPR16_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR16_INTLEV_SIZE 2
|
||||
# define INTC_INTPR16_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR16_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ16 0x140
|
||||
# define INTC_INTREQ16_IREQUEST512_OFFSET 0
|
||||
# define INTC_INTREQ16_IREQUEST512_SIZE 1
|
||||
#define INTC_INTPR17 0x44
|
||||
# define INTC_INTPR17_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR17_INTLEV_SIZE 2
|
||||
# define INTC_INTPR17_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR17_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ17 0x144
|
||||
# define INTC_INTREQ17_IREQUEST544_OFFSET 0
|
||||
# define INTC_INTREQ17_IREQUEST544_SIZE 1
|
||||
#define INTC_INTPR18 0x48
|
||||
# define INTC_INTPR18_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR18_INTLEV_SIZE 2
|
||||
# define INTC_INTPR18_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR18_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ18 0x148
|
||||
# define INTC_INTREQ18_IREQUEST576_OFFSET 0
|
||||
# define INTC_INTREQ18_IREQUEST576_SIZE 1
|
||||
#define INTC_INTPR19 0x4c
|
||||
# define INTC_INTPR19_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR19_INTLEV_SIZE 2
|
||||
# define INTC_INTPR19_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR19_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ19 0x14c
|
||||
# define INTC_INTREQ19_IREQUEST608_OFFSET 0
|
||||
# define INTC_INTREQ19_IREQUEST608_SIZE 1
|
||||
# define INTC_INTREQ19_IREQUEST609_OFFSET 1
|
||||
# define INTC_INTREQ19_IREQUEST609_SIZE 1
|
||||
# define INTC_INTREQ19_IREQUEST610_OFFSET 2
|
||||
# define INTC_INTREQ19_IREQUEST610_SIZE 1
|
||||
# define INTC_INTREQ19_IREQUEST611_OFFSET 3
|
||||
# define INTC_INTREQ19_IREQUEST611_SIZE 1
|
||||
#define INTC_INTPR20 0x50
|
||||
# define INTC_INTPR20_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR20_INTLEV_SIZE 2
|
||||
# define INTC_INTPR20_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR20_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ20 0x150
|
||||
# define INTC_INTREQ20_IREQUEST640_OFFSET 0
|
||||
# define INTC_INTREQ20_IREQUEST640_SIZE 1
|
||||
#define INTC_INTPR21 0x54
|
||||
# define INTC_INTPR21_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR21_INTLEV_SIZE 2
|
||||
# define INTC_INTPR21_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR21_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ21 0x154
|
||||
# define INTC_INTREQ21_IREQUEST672_OFFSET 0
|
||||
# define INTC_INTREQ21_IREQUEST672_SIZE 1
|
||||
#define INTC_INTPR22 0x58
|
||||
# define INTC_INTPR22_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR22_INTLEV_SIZE 2
|
||||
# define INTC_INTPR22_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR22_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ22 0x158
|
||||
# define INTC_INTREQ22_IREQUEST704_OFFSET 0
|
||||
# define INTC_INTREQ22_IREQUEST704_SIZE 1
|
||||
# define INTC_INTREQ22_IREQUEST705_OFFSET 1
|
||||
# define INTC_INTREQ22_IREQUEST705_SIZE 1
|
||||
# define INTC_INTREQ22_IREQUEST706_OFFSET 2
|
||||
# define INTC_INTREQ22_IREQUEST706_SIZE 1
|
||||
#define INTC_INTPR23 0x5c
|
||||
# define INTC_INTPR23_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR23_INTLEV_SIZE 2
|
||||
# define INTC_INTPR23_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR23_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ23 0x15c
|
||||
# define INTC_INTREQ23_IREQUEST736_OFFSET 0
|
||||
# define INTC_INTREQ23_IREQUEST736_SIZE 1
|
||||
# define INTC_INTREQ23_IREQUEST737_OFFSET 1
|
||||
# define INTC_INTREQ23_IREQUEST737_SIZE 1
|
||||
# define INTC_INTREQ23_IREQUEST738_OFFSET 2
|
||||
# define INTC_INTREQ23_IREQUEST738_SIZE 1
|
||||
#define INTC_INTPR24 0x60
|
||||
# define INTC_INTPR24_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR24_INTLEV_SIZE 2
|
||||
# define INTC_INTPR24_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR24_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ24 0x160
|
||||
# define INTC_INTREQ24_IREQUEST768_OFFSET 0
|
||||
# define INTC_INTREQ24_IREQUEST768_SIZE 1
|
||||
#define INTC_INTPR25 0x64
|
||||
# define INTC_INTPR25_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR25_INTLEV_SIZE 2
|
||||
# define INTC_INTPR25_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR25_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ25 0x164
|
||||
# define INTC_INTREQ25_IREQUEST800_OFFSET 0
|
||||
# define INTC_INTREQ25_IREQUEST800_SIZE 1
|
||||
#define INTC_INTPR26 0x68
|
||||
# define INTC_INTPR26_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR26_INTLEV_SIZE 2
|
||||
# define INTC_INTPR26_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR26_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ26 0x168
|
||||
# define INTC_INTREQ26_IREQUEST832_OFFSET 0
|
||||
# define INTC_INTREQ26_IREQUEST832_SIZE 1
|
||||
#define INTC_INTPR27 0x6c
|
||||
# define INTC_INTPR27_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR27_INTLEV_SIZE 2
|
||||
# define INTC_INTPR27_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR27_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ27 0x16c
|
||||
# define INTC_INTREQ27_IREQUEST864_OFFSET 0
|
||||
# define INTC_INTREQ27_IREQUEST864_SIZE 1
|
||||
#define INTC_INTPR28 0x70
|
||||
# define INTC_INTPR28_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR28_INTLEV_SIZE 2
|
||||
# define INTC_INTPR28_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR28_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ28 0x170
|
||||
# define INTC_INTREQ28_IREQUEST896_OFFSET 0
|
||||
# define INTC_INTREQ28_IREQUEST896_SIZE 1
|
||||
#define INTC_INTPR29 0x74
|
||||
# define INTC_INTPR29_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR29_INTLEV_SIZE 2
|
||||
# define INTC_INTPR29_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR29_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ29 0x174
|
||||
# define INTC_INTREQ29_IREQUEST928_OFFSET 0
|
||||
# define INTC_INTREQ29_IREQUEST928_SIZE 1
|
||||
#define INTC_INTPR30 0x78
|
||||
# define INTC_INTPR30_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR30_INTLEV_SIZE 2
|
||||
# define INTC_INTPR30_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR30_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ30 0x178
|
||||
# define INTC_INTREQ30_IREQUEST960_OFFSET 0
|
||||
# define INTC_INTREQ30_IREQUEST960_SIZE 1
|
||||
#define INTC_INTPR31 0x7c
|
||||
# define INTC_INTPR31_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR31_INTLEV_SIZE 2
|
||||
# define INTC_INTPR31_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR31_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ31 0x17c
|
||||
# define INTC_INTREQ31_IREQUEST992_OFFSET 0
|
||||
# define INTC_INTREQ31_IREQUEST992_SIZE 1
|
||||
#define INTC_INTPR32 0x80
|
||||
# define INTC_INTPR32_INTLEV_OFFSET 30
|
||||
# define INTC_INTPR32_INTLEV_SIZE 2
|
||||
# define INTC_INTPR32_OFFSET_OFFSET 0
|
||||
# define INTC_INTPR32_OFFSET_SIZE 24
|
||||
#define INTC_INTREQ32 0x180
|
||||
# define INTC_INTREQ32_IREQUEST1024_OFFSET 0
|
||||
# define INTC_INTREQ32_IREQUEST1024_SIZE 1
|
||||
#define INTC_INTCAUSE0 0x20c
|
||||
# define INTC_INTCAUSE0_CAUSEGRP_OFFSET 0
|
||||
# define INTC_INTCAUSE0_CAUSEGRP_SIZE 6
|
||||
#define INTC_INTCAUSE1 0x208
|
||||
# define INTC_INTCAUSE1_CAUSEGRP_OFFSET 0
|
||||
# define INTC_INTCAUSE1_CAUSEGRP_SIZE 6
|
||||
#define INTC_INTCAUSE2 0x204
|
||||
# define INTC_INTCAUSE2_CAUSEGRP_OFFSET 0
|
||||
# define INTC_INTCAUSE2_CAUSEGRP_SIZE 6
|
||||
#define INTC_INTCAUSE3 0x200
|
||||
# define INTC_INTCAUSE3_CAUSEGRP_OFFSET 0
|
||||
# define INTC_INTCAUSE3_CAUSEGRP_SIZE 6
|
||||
|
||||
#define INTC_BIT(name) (1 << INTC_##name##_OFFSET)
|
||||
#define INTC_MKBF(name, value) (((value) & ((1 << INTC_##name##_SIZE) - 1)) << INTC_##name##_OFFSET)
|
||||
#define INTC_GETBF(name, value) (((value) >> INTC_##name##_OFFSET) & ((1 << INTC_##name##_SIZE) - 1))
|
||||
|
||||
#define intc_readl(port,reg) readl((port)->regs + INTC_##reg)
|
||||
#define intc_writel(port,reg,value) writel((value), (port)->regs + INTC_##reg)
|
||||
|
||||
#endif /* __ASM_AVR32_PERIHP_INTC_H__ */
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Atmel PIO2 Port Multiplexer support
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/arch/portmux.h>
|
||||
|
||||
#include "pio.h"
|
||||
|
||||
#define MAX_NR_PIO_DEVICES 8
|
||||
|
||||
struct pio_device {
|
||||
void __iomem *regs;
|
||||
const struct platform_device *pdev;
|
||||
struct clk *clk;
|
||||
u32 alloc_mask;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
static struct pio_device pio_dev[MAX_NR_PIO_DEVICES];
|
||||
|
||||
void portmux_set_func(unsigned int portmux_id, unsigned int pin_id,
|
||||
unsigned int function_id)
|
||||
{
|
||||
struct pio_device *pio;
|
||||
u32 mask = 1 << pin_id;
|
||||
|
||||
BUG_ON(portmux_id >= MAX_NR_PIO_DEVICES);
|
||||
|
||||
pio = &pio_dev[portmux_id];
|
||||
|
||||
if (function_id)
|
||||
pio_writel(pio, BSR, mask);
|
||||
else
|
||||
pio_writel(pio, ASR, mask);
|
||||
pio_writel(pio, PDR, mask);
|
||||
}
|
||||
|
||||
static int __init pio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pio_device *pio = NULL;
|
||||
|
||||
BUG_ON(pdev->id >= MAX_NR_PIO_DEVICES);
|
||||
pio = &pio_dev[pdev->id];
|
||||
BUG_ON(!pio->regs);
|
||||
|
||||
/* TODO: Interrupts */
|
||||
|
||||
platform_set_drvdata(pdev, pio);
|
||||
|
||||
printk(KERN_INFO "%s: Atmel Port Multiplexer at 0x%p (irq %d)\n",
|
||||
pio->name, pio->regs, platform_get_irq(pdev, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pio_driver = {
|
||||
.probe = pio_probe,
|
||||
.driver = {
|
||||
.name = "pio",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pio_init(void)
|
||||
{
|
||||
return platform_driver_register(&pio_driver);
|
||||
}
|
||||
subsys_initcall(pio_init);
|
||||
|
||||
void __init at32_init_pio(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *regs;
|
||||
struct pio_device *pio;
|
||||
|
||||
if (pdev->id > MAX_NR_PIO_DEVICES) {
|
||||
dev_err(&pdev->dev, "only %d PIO devices supported\n",
|
||||
MAX_NR_PIO_DEVICES);
|
||||
return;
|
||||
}
|
||||
|
||||
pio = &pio_dev[pdev->id];
|
||||
snprintf(pio->name, sizeof(pio->name), "pio%d", pdev->id);
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "no mmio resource defined\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pio->clk = clk_get(&pdev->dev, "mck");
|
||||
if (IS_ERR(pio->clk))
|
||||
/*
|
||||
* This is a fatal error, but if we continue we might
|
||||
* be so lucky that we manage to initialize the
|
||||
* console and display this message...
|
||||
*/
|
||||
dev_err(&pdev->dev, "no mck clock defined\n");
|
||||
else
|
||||
clk_enable(pio->clk);
|
||||
|
||||
pio->pdev = pdev;
|
||||
pio->regs = ioremap(regs->start, regs->end - regs->start + 1);
|
||||
|
||||
pio_writel(pio, ODR, ~0UL);
|
||||
pio_writel(pio, PER, ~0UL);
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Atmel PIO2 Port Multiplexer support
|
||||
*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __ARCH_AVR32_AT32AP_PIO_H__
|
||||
#define __ARCH_AVR32_AT32AP_PIO_H__
|
||||
|
||||
/* PIO register offsets */
|
||||
#define PIO_PER 0x0000
|
||||
#define PIO_PDR 0x0004
|
||||
#define PIO_PSR 0x0008
|
||||
#define PIO_OER 0x0010
|
||||
#define PIO_ODR 0x0014
|
||||
#define PIO_OSR 0x0018
|
||||
#define PIO_IFER 0x0020
|
||||
#define PIO_IFDR 0x0024
|
||||
#define PIO_ISFR 0x0028
|
||||
#define PIO_SODR 0x0030
|
||||
#define PIO_CODR 0x0034
|
||||
#define PIO_ODSR 0x0038
|
||||
#define PIO_PDSR 0x003c
|
||||
#define PIO_IER 0x0040
|
||||
#define PIO_IDR 0x0044
|
||||
#define PIO_IMR 0x0048
|
||||
#define PIO_ISR 0x004c
|
||||
#define PIO_MDER 0x0050
|
||||
#define PIO_MDDR 0x0054
|
||||
#define PIO_MDSR 0x0058
|
||||
#define PIO_PUDR 0x0060
|
||||
#define PIO_PUER 0x0064
|
||||
#define PIO_PUSR 0x0068
|
||||
#define PIO_ASR 0x0070
|
||||
#define PIO_BSR 0x0074
|
||||
#define PIO_ABSR 0x0078
|
||||
#define PIO_OWER 0x00a0
|
||||
#define PIO_OWDR 0x00a4
|
||||
#define PIO_OWSR 0x00a8
|
||||
|
||||
/* Bitfields in PER */
|
||||
|
||||
/* Bitfields in PDR */
|
||||
|
||||
/* Bitfields in PSR */
|
||||
|
||||
/* Bitfields in OER */
|
||||
|
||||
/* Bitfields in ODR */
|
||||
|
||||
/* Bitfields in OSR */
|
||||
|
||||
/* Bitfields in IFER */
|
||||
|
||||
/* Bitfields in IFDR */
|
||||
|
||||
/* Bitfields in ISFR */
|
||||
|
||||
/* Bitfields in SODR */
|
||||
|
||||
/* Bitfields in CODR */
|
||||
|
||||
/* Bitfields in ODSR */
|
||||
|
||||
/* Bitfields in PDSR */
|
||||
|
||||
/* Bitfields in IER */
|
||||
|
||||
/* Bitfields in IDR */
|
||||
|
||||
/* Bitfields in IMR */
|
||||
|
||||
/* Bitfields in ISR */
|
||||
|
||||
/* Bitfields in MDER */
|
||||
|
||||
/* Bitfields in MDDR */
|
||||
|
||||
/* Bitfields in MDSR */
|
||||
|
||||
/* Bitfields in PUDR */
|
||||
|
||||
/* Bitfields in PUER */
|
||||
|
||||
/* Bitfields in PUSR */
|
||||
|
||||
/* Bitfields in ASR */
|
||||
|
||||
/* Bitfields in BSR */
|
||||
|
||||
/* Bitfields in ABSR */
|
||||
#define PIO_P0_OFFSET 0
|
||||
#define PIO_P0_SIZE 1
|
||||
#define PIO_P1_OFFSET 1
|
||||
#define PIO_P1_SIZE 1
|
||||
#define PIO_P2_OFFSET 2
|
||||
#define PIO_P2_SIZE 1
|
||||
#define PIO_P3_OFFSET 3
|
||||
#define PIO_P3_SIZE 1
|
||||
#define PIO_P4_OFFSET 4
|
||||
#define PIO_P4_SIZE 1
|
||||
#define PIO_P5_OFFSET 5
|
||||
#define PIO_P5_SIZE 1
|
||||
#define PIO_P6_OFFSET 6
|
||||
#define PIO_P6_SIZE 1
|
||||
#define PIO_P7_OFFSET 7
|
||||
#define PIO_P7_SIZE 1
|
||||
#define PIO_P8_OFFSET 8
|
||||
#define PIO_P8_SIZE 1
|
||||
#define PIO_P9_OFFSET 9
|
||||
#define PIO_P9_SIZE 1
|
||||
#define PIO_P10_OFFSET 10
|
||||
#define PIO_P10_SIZE 1
|
||||
#define PIO_P11_OFFSET 11
|
||||
#define PIO_P11_SIZE 1
|
||||
#define PIO_P12_OFFSET 12
|
||||
#define PIO_P12_SIZE 1
|
||||
#define PIO_P13_OFFSET 13
|
||||
#define PIO_P13_SIZE 1
|
||||
#define PIO_P14_OFFSET 14
|
||||
#define PIO_P14_SIZE 1
|
||||
#define PIO_P15_OFFSET 15
|
||||
#define PIO_P15_SIZE 1
|
||||
#define PIO_P16_OFFSET 16
|
||||
#define PIO_P16_SIZE 1
|
||||
#define PIO_P17_OFFSET 17
|
||||
#define PIO_P17_SIZE 1
|
||||
#define PIO_P18_OFFSET 18
|
||||
#define PIO_P18_SIZE 1
|
||||
#define PIO_P19_OFFSET 19
|
||||
#define PIO_P19_SIZE 1
|
||||
#define PIO_P20_OFFSET 20
|
||||
#define PIO_P20_SIZE 1
|
||||
#define PIO_P21_OFFSET 21
|
||||
#define PIO_P21_SIZE 1
|
||||
#define PIO_P22_OFFSET 22
|
||||
#define PIO_P22_SIZE 1
|
||||
#define PIO_P23_OFFSET 23
|
||||
#define PIO_P23_SIZE 1
|
||||
#define PIO_P24_OFFSET 24
|
||||
#define PIO_P24_SIZE 1
|
||||
#define PIO_P25_OFFSET 25
|
||||
#define PIO_P25_SIZE 1
|
||||
#define PIO_P26_OFFSET 26
|
||||
#define PIO_P26_SIZE 1
|
||||
#define PIO_P27_OFFSET 27
|
||||
#define PIO_P27_SIZE 1
|
||||
#define PIO_P28_OFFSET 28
|
||||
#define PIO_P28_SIZE 1
|
||||
#define PIO_P29_OFFSET 29
|
||||
#define PIO_P29_SIZE 1
|
||||
#define PIO_P30_OFFSET 30
|
||||
#define PIO_P30_SIZE 1
|
||||
#define PIO_P31_OFFSET 31
|
||||
#define PIO_P31_SIZE 1
|
||||
|
||||
/* Bitfields in OWER */
|
||||
|
||||
/* Bitfields in OWDR */
|
||||
|
||||
/* Bitfields in OWSR */
|
||||
|
||||
/* Bit manipulation macros */
|
||||
#define PIO_BIT(name) (1 << PIO_##name##_OFFSET)
|
||||
#define PIO_BF(name,value) (((value) & ((1 << PIO_##name##_SIZE) - 1)) << PIO_##name##_OFFSET)
|
||||
#define PIO_BFEXT(name,value) (((value) >> PIO_##name##_OFFSET) & ((1 << PIO_##name##_SIZE) - 1))
|
||||
#define PIO_BFINS(name,value,old) (((old) & ~(((1 << PIO_##name##_SIZE) - 1) << PIO_##name##_OFFSET)) | PIO_BF(name,value))
|
||||
|
||||
/* Register access macros */
|
||||
#define pio_readl(port,reg) readl((port)->regs + PIO_##reg)
|
||||
#define pio_writel(port,reg,value) writel((value), (port)->regs + PIO_##reg)
|
||||
|
||||
void at32_init_pio(struct platform_device *pdev);
|
||||
|
||||
#endif /* __ARCH_AVR32_AT32AP_PIO_H__ */
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* System Manager driver for AT32AP CPUs
|
||||
*
|
||||
* Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* 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/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/intc.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <asm/arch/sm.h>
|
||||
|
||||
#include "sm.h"
|
||||
|
||||
#define SM_EIM_IRQ_RESOURCE 1
|
||||
#define SM_PM_IRQ_RESOURCE 2
|
||||
#define SM_RTC_IRQ_RESOURCE 3
|
||||
|
||||
#define to_eim(irqc) container_of(irqc, struct at32_sm, irqc)
|
||||
|
||||
struct at32_sm system_manager;
|
||||
|
||||
int __init at32_sm_init(void)
|
||||
{
|
||||
struct resource *regs;
|
||||
struct at32_sm *sm = &system_manager;
|
||||
int ret = -ENXIO;
|
||||
|
||||
regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
goto fail;
|
||||
|
||||
spin_lock_init(&sm->lock);
|
||||
sm->pdev = &at32_sm_device;
|
||||
|
||||
ret = -ENOMEM;
|
||||
sm->regs = ioremap(regs->start, regs->end - regs->start + 1);
|
||||
if (!sm->regs)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* External Interrupt Module (EIM).
|
||||
*
|
||||
* EIM gets level- or edge-triggered interrupts of either polarity
|
||||
* from the outside and converts it to active-high level-triggered
|
||||
* interrupts that the internal interrupt controller can handle. EIM
|
||||
* also provides masking/unmasking of interrupts, as well as
|
||||
* acknowledging of edge-triggered interrupts.
|
||||
*/
|
||||
|
||||
static irqreturn_t spurious_eim_interrupt(int irq, void *dev_id,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
printk(KERN_WARNING "Spurious EIM interrupt %d\n", irq);
|
||||
disable_irq(irq);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static struct irqaction eim_spurious_action = {
|
||||
.handler = spurious_eim_interrupt,
|
||||
};
|
||||
|
||||
static irqreturn_t eim_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
struct irq_controller * irqc = dev_id;
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
unsigned long pending;
|
||||
|
||||
/*
|
||||
* No need to disable interrupts globally. The interrupt
|
||||
* level relevant to this group must be masked all the time,
|
||||
* so we know that this particular EIM instance will not be
|
||||
* re-entered.
|
||||
*/
|
||||
spin_lock(&sm->lock);
|
||||
|
||||
pending = intc_get_pending(sm->irqc.irq_group);
|
||||
if (unlikely(!pending)) {
|
||||
printk(KERN_ERR "EIM (group %u): No interrupts pending!\n",
|
||||
sm->irqc.irq_group);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
do {
|
||||
struct irqaction *action;
|
||||
unsigned int i;
|
||||
|
||||
i = fls(pending) - 1;
|
||||
pending &= ~(1 << i);
|
||||
action = sm->action[i];
|
||||
|
||||
/* Acknowledge the interrupt */
|
||||
sm_writel(sm, EIM_ICR, 1 << i);
|
||||
|
||||
spin_unlock(&sm->lock);
|
||||
|
||||
if (action->flags & SA_INTERRUPT)
|
||||
local_irq_disable();
|
||||
action->handler(sm->irqc.first_irq + i, action->dev_id, regs);
|
||||
local_irq_enable();
|
||||
spin_lock(&sm->lock);
|
||||
if (action->flags & SA_SAMPLE_RANDOM)
|
||||
add_interrupt_randomness(sm->irqc.first_irq + i);
|
||||
} while (pending);
|
||||
|
||||
unlock:
|
||||
spin_unlock(&sm->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void eim_mask(struct irq_controller *irqc, unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
unsigned int i;
|
||||
|
||||
i = irq - sm->irqc.first_irq;
|
||||
sm_writel(sm, EIM_IDR, 1 << i);
|
||||
}
|
||||
|
||||
static void eim_unmask(struct irq_controller *irqc, unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
unsigned int i;
|
||||
|
||||
i = irq - sm->irqc.first_irq;
|
||||
sm_writel(sm, EIM_IER, 1 << i);
|
||||
}
|
||||
|
||||
static int eim_setup(struct irq_controller *irqc, unsigned int irq,
|
||||
struct irqaction *action)
|
||||
{
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
sm->action[irq - sm->irqc.first_irq] = action;
|
||||
/* Acknowledge earlier interrupts */
|
||||
sm_writel(sm, EIM_ICR, (1<<(irq - sm->irqc.first_irq)));
|
||||
eim_unmask(irqc, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eim_free(struct irq_controller *irqc, unsigned int irq,
|
||||
void *dev)
|
||||
{
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
eim_mask(irqc, irq);
|
||||
sm->action[irq - sm->irqc.first_irq] = &eim_spurious_action;
|
||||
}
|
||||
|
||||
static int eim_set_type(struct irq_controller *irqc, unsigned int irq,
|
||||
unsigned int type)
|
||||
{
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
unsigned long flags;
|
||||
u32 value, pattern;
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
|
||||
pattern = 1 << (irq - sm->irqc.first_irq);
|
||||
|
||||
value = sm_readl(sm, EIM_MODE);
|
||||
if (type & IRQ_TYPE_LEVEL)
|
||||
value |= pattern;
|
||||
else
|
||||
value &= ~pattern;
|
||||
sm_writel(sm, EIM_MODE, value);
|
||||
value = sm_readl(sm, EIM_EDGE);
|
||||
if (type & IRQ_EDGE_RISING)
|
||||
value |= pattern;
|
||||
else
|
||||
value &= ~pattern;
|
||||
sm_writel(sm, EIM_EDGE, value);
|
||||
value = sm_readl(sm, EIM_LEVEL);
|
||||
if (type & IRQ_LEVEL_HIGH)
|
||||
value |= pattern;
|
||||
else
|
||||
value &= ~pattern;
|
||||
sm_writel(sm, EIM_LEVEL, value);
|
||||
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int eim_get_type(struct irq_controller *irqc,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct at32_sm *sm = to_eim(irqc);
|
||||
unsigned long flags;
|
||||
unsigned int type = 0;
|
||||
u32 mode, edge, level, pattern;
|
||||
|
||||
pattern = 1 << (irq - sm->irqc.first_irq);
|
||||
|
||||
spin_lock_irqsave(&sm->lock, flags);
|
||||
mode = sm_readl(sm, EIM_MODE);
|
||||
edge = sm_readl(sm, EIM_EDGE);
|
||||
level = sm_readl(sm, EIM_LEVEL);
|
||||
spin_unlock_irqrestore(&sm->lock, flags);
|
||||
|
||||
if (mode & pattern)
|
||||
type |= IRQ_TYPE_LEVEL;
|
||||
if (edge & pattern)
|
||||
type |= IRQ_EDGE_RISING;
|
||||
if (level & pattern)
|
||||
type |= IRQ_LEVEL_HIGH;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static struct irq_controller_class eim_irq_class = {
|
||||
.typename = "EIM",
|
||||
.handle = eim_handle_irq,
|
||||
.setup = eim_setup,
|
||||
.free = eim_free,
|
||||
.mask = eim_mask,
|
||||
.unmask = eim_unmask,
|
||||
.set_type = eim_set_type,
|
||||
.get_type = eim_get_type,
|
||||
};
|
||||
|
||||
static int __init eim_init(void)
|
||||
{
|
||||
struct at32_sm *sm = &system_manager;
|
||||
unsigned int i;
|
||||
u32 pattern;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The EIM is really the same module as SM, so register
|
||||
* mapping, etc. has been taken care of already.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Find out how many interrupt lines that are actually
|
||||
* implemented in hardware.
|
||||
*/
|
||||
sm_writel(sm, EIM_IDR, ~0UL);
|
||||
sm_writel(sm, EIM_MODE, ~0UL);
|
||||
pattern = sm_readl(sm, EIM_MODE);
|
||||
sm->irqc.nr_irqs = fls(pattern);
|
||||
|
||||
ret = -ENOMEM;
|
||||
sm->action = kmalloc(sizeof(*sm->action) * sm->irqc.nr_irqs,
|
||||
GFP_KERNEL);
|
||||
if (!sm->action)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < sm->irqc.nr_irqs; i++)
|
||||
sm->action[i] = &eim_spurious_action;
|
||||
|
||||
spin_lock_init(&sm->lock);
|
||||
sm->irqc.irq_group = sm->pdev->resource[SM_EIM_IRQ_RESOURCE].start;
|
||||
sm->irqc.class = &eim_irq_class;
|
||||
|
||||
ret = intc_register_controller(&sm->irqc);
|
||||
if (ret < 0)
|
||||
goto out_free_actions;
|
||||
|
||||
printk("EIM: External Interrupt Module at 0x%p, IRQ group %u\n",
|
||||
sm->regs, sm->irqc.irq_group);
|
||||
printk("EIM: Handling %u external IRQs, starting with IRQ%u\n",
|
||||
sm->irqc.nr_irqs, sm->irqc.first_irq);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_actions:
|
||||
kfree(sm->action);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
arch_initcall(eim_init);
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Register definitions for SM
|
||||
*
|
||||
* System Manager
|
||||
*/
|
||||
#ifndef __ASM_AVR32_SM_H__
|
||||
#define __ASM_AVR32_SM_H__
|
||||
|
||||
/* SM register offsets */
|
||||
#define SM_PM_MCCTRL 0x0000
|
||||
#define SM_PM_CKSEL 0x0004
|
||||
#define SM_PM_CPU_MASK 0x0008
|
||||
#define SM_PM_HSB_MASK 0x000c
|
||||
#define SM_PM_PBA_MASK 0x0010
|
||||
#define SM_PM_PBB_MASK 0x0014
|
||||
#define SM_PM_PLL0 0x0020
|
||||
#define SM_PM_PLL1 0x0024
|
||||
#define SM_PM_VCTRL 0x0030
|
||||
#define SM_PM_VMREF 0x0034
|
||||
#define SM_PM_VMV 0x0038
|
||||
#define SM_PM_IER 0x0040
|
||||
#define SM_PM_IDR 0x0044
|
||||
#define SM_PM_IMR 0x0048
|
||||
#define SM_PM_ISR 0x004c
|
||||
#define SM_PM_ICR 0x0050
|
||||
#define SM_PM_GCCTRL 0x0060
|
||||
#define SM_RTC_CTRL 0x0080
|
||||
#define SM_RTC_VAL 0x0084
|
||||
#define SM_RTC_TOP 0x0088
|
||||
#define SM_RTC_IER 0x0090
|
||||
#define SM_RTC_IDR 0x0094
|
||||
#define SM_RTC_IMR 0x0098
|
||||
#define SM_RTC_ISR 0x009c
|
||||
#define SM_RTC_ICR 0x00a0
|
||||
#define SM_WDT_CTRL 0x00b0
|
||||
#define SM_WDT_CLR 0x00b4
|
||||
#define SM_WDT_EXT 0x00b8
|
||||
#define SM_RC_RCAUSE 0x00c0
|
||||
#define SM_EIM_IER 0x0100
|
||||
#define SM_EIM_IDR 0x0104
|
||||
#define SM_EIM_IMR 0x0108
|
||||
#define SM_EIM_ISR 0x010c
|
||||
#define SM_EIM_ICR 0x0110
|
||||
#define SM_EIM_MODE 0x0114
|
||||
#define SM_EIM_EDGE 0x0118
|
||||
#define SM_EIM_LEVEL 0x011c
|
||||
#define SM_EIM_TEST 0x0120
|
||||
#define SM_EIM_NMIC 0x0124
|
||||
|
||||
/* Bitfields in PM_MCCTRL */
|
||||
|
||||
/* Bitfields in PM_CKSEL */
|
||||
#define SM_CPUSEL_OFFSET 0
|
||||
#define SM_CPUSEL_SIZE 3
|
||||
#define SM_CPUDIV_OFFSET 7
|
||||
#define SM_CPUDIV_SIZE 1
|
||||
#define SM_HSBSEL_OFFSET 8
|
||||
#define SM_HSBSEL_SIZE 3
|
||||
#define SM_HSBDIV_OFFSET 15
|
||||
#define SM_HSBDIV_SIZE 1
|
||||
#define SM_PBASEL_OFFSET 16
|
||||
#define SM_PBASEL_SIZE 3
|
||||
#define SM_PBADIV_OFFSET 23
|
||||
#define SM_PBADIV_SIZE 1
|
||||
#define SM_PBBSEL_OFFSET 24
|
||||
#define SM_PBBSEL_SIZE 3
|
||||
#define SM_PBBDIV_OFFSET 31
|
||||
#define SM_PBBDIV_SIZE 1
|
||||
|
||||
/* Bitfields in PM_CPU_MASK */
|
||||
|
||||
/* Bitfields in PM_HSB_MASK */
|
||||
|
||||
/* Bitfields in PM_PBA_MASK */
|
||||
|
||||
/* Bitfields in PM_PBB_MASK */
|
||||
|
||||
/* Bitfields in PM_PLL0 */
|
||||
#define SM_PLLEN_OFFSET 0
|
||||
#define SM_PLLEN_SIZE 1
|
||||
#define SM_PLLOSC_OFFSET 1
|
||||
#define SM_PLLOSC_SIZE 1
|
||||
#define SM_PLLOPT_OFFSET 2
|
||||
#define SM_PLLOPT_SIZE 3
|
||||
#define SM_PLLDIV_OFFSET 8
|
||||
#define SM_PLLDIV_SIZE 8
|
||||
#define SM_PLLMUL_OFFSET 16
|
||||
#define SM_PLLMUL_SIZE 8
|
||||
#define SM_PLLCOUNT_OFFSET 24
|
||||
#define SM_PLLCOUNT_SIZE 6
|
||||
#define SM_PLLTEST_OFFSET 31
|
||||
#define SM_PLLTEST_SIZE 1
|
||||
|
||||
/* Bitfields in PM_PLL1 */
|
||||
|
||||
/* Bitfields in PM_VCTRL */
|
||||
#define SM_VAUTO_OFFSET 0
|
||||
#define SM_VAUTO_SIZE 1
|
||||
#define SM_PM_VCTRL_VAL_OFFSET 8
|
||||
#define SM_PM_VCTRL_VAL_SIZE 7
|
||||
|
||||
/* Bitfields in PM_VMREF */
|
||||
#define SM_REFSEL_OFFSET 0
|
||||
#define SM_REFSEL_SIZE 4
|
||||
|
||||
/* Bitfields in PM_VMV */
|
||||
#define SM_PM_VMV_VAL_OFFSET 0
|
||||
#define SM_PM_VMV_VAL_SIZE 8
|
||||
|
||||
/* Bitfields in PM_IER */
|
||||
|
||||
/* Bitfields in PM_IDR */
|
||||
|
||||
/* Bitfields in PM_IMR */
|
||||
|
||||
/* Bitfields in PM_ISR */
|
||||
|
||||
/* Bitfields in PM_ICR */
|
||||
#define SM_LOCK0_OFFSET 0
|
||||
#define SM_LOCK0_SIZE 1
|
||||
#define SM_LOCK1_OFFSET 1
|
||||
#define SM_LOCK1_SIZE 1
|
||||
#define SM_WAKE_OFFSET 2
|
||||
#define SM_WAKE_SIZE 1
|
||||
#define SM_VOK_OFFSET 3
|
||||
#define SM_VOK_SIZE 1
|
||||
#define SM_VMRDY_OFFSET 4
|
||||
#define SM_VMRDY_SIZE 1
|
||||
#define SM_CKRDY_OFFSET 5
|
||||
#define SM_CKRDY_SIZE 1
|
||||
|
||||
/* Bitfields in PM_GCCTRL */
|
||||
#define SM_OSCSEL_OFFSET 0
|
||||
#define SM_OSCSEL_SIZE 1
|
||||
#define SM_PLLSEL_OFFSET 1
|
||||
#define SM_PLLSEL_SIZE 1
|
||||
#define SM_CEN_OFFSET 2
|
||||
#define SM_CEN_SIZE 1
|
||||
#define SM_CPC_OFFSET 3
|
||||
#define SM_CPC_SIZE 1
|
||||
#define SM_DIVEN_OFFSET 4
|
||||
#define SM_DIVEN_SIZE 1
|
||||
#define SM_DIV_OFFSET 8
|
||||
#define SM_DIV_SIZE 8
|
||||
|
||||
/* Bitfields in RTC_CTRL */
|
||||
#define SM_PCLR_OFFSET 1
|
||||
#define SM_PCLR_SIZE 1
|
||||
#define SM_TOPEN_OFFSET 2
|
||||
#define SM_TOPEN_SIZE 1
|
||||
#define SM_CLKEN_OFFSET 3
|
||||
#define SM_CLKEN_SIZE 1
|
||||
#define SM_PSEL_OFFSET 8
|
||||
#define SM_PSEL_SIZE 16
|
||||
|
||||
/* Bitfields in RTC_VAL */
|
||||
#define SM_RTC_VAL_VAL_OFFSET 0
|
||||
#define SM_RTC_VAL_VAL_SIZE 31
|
||||
|
||||
/* Bitfields in RTC_TOP */
|
||||
#define SM_RTC_TOP_VAL_OFFSET 0
|
||||
#define SM_RTC_TOP_VAL_SIZE 32
|
||||
|
||||
/* Bitfields in RTC_IER */
|
||||
|
||||
/* Bitfields in RTC_IDR */
|
||||
|
||||
/* Bitfields in RTC_IMR */
|
||||
|
||||
/* Bitfields in RTC_ISR */
|
||||
|
||||
/* Bitfields in RTC_ICR */
|
||||
#define SM_TOPI_OFFSET 0
|
||||
#define SM_TOPI_SIZE 1
|
||||
|
||||
/* Bitfields in WDT_CTRL */
|
||||
#define SM_KEY_OFFSET 24
|
||||
#define SM_KEY_SIZE 8
|
||||
|
||||
/* Bitfields in WDT_CLR */
|
||||
|
||||
/* Bitfields in WDT_EXT */
|
||||
|
||||
/* Bitfields in RC_RCAUSE */
|
||||
#define SM_POR_OFFSET 0
|
||||
#define SM_POR_SIZE 1
|
||||
#define SM_BOD_OFFSET 1
|
||||
#define SM_BOD_SIZE 1
|
||||
#define SM_EXT_OFFSET 2
|
||||
#define SM_EXT_SIZE 1
|
||||
#define SM_WDT_OFFSET 3
|
||||
#define SM_WDT_SIZE 1
|
||||
#define SM_NTAE_OFFSET 4
|
||||
#define SM_NTAE_SIZE 1
|
||||
#define SM_SERP_OFFSET 5
|
||||
#define SM_SERP_SIZE 1
|
||||
|
||||
/* Bitfields in EIM_IER */
|
||||
|
||||
/* Bitfields in EIM_IDR */
|
||||
|
||||
/* Bitfields in EIM_IMR */
|
||||
|
||||
/* Bitfields in EIM_ISR */
|
||||
|
||||
/* Bitfields in EIM_ICR */
|
||||
|
||||
/* Bitfields in EIM_MODE */
|
||||
|
||||
/* Bitfields in EIM_EDGE */
|
||||
#define SM_INT0_OFFSET 0
|
||||
#define SM_INT0_SIZE 1
|
||||
#define SM_INT1_OFFSET 1
|
||||
#define SM_INT1_SIZE 1
|
||||
#define SM_INT2_OFFSET 2
|
||||
#define SM_INT2_SIZE 1
|
||||
#define SM_INT3_OFFSET 3
|
||||
#define SM_INT3_SIZE 1
|
||||
|
||||
/* Bitfields in EIM_LEVEL */
|
||||
|
||||
/* Bitfields in EIM_TEST */
|
||||
#define SM_TESTEN_OFFSET 31
|
||||
#define SM_TESTEN_SIZE 1
|
||||
|
||||
/* Bitfields in EIM_NMIC */
|
||||
#define SM_EN_OFFSET 0
|
||||
#define SM_EN_SIZE 1
|
||||
|
||||
/* Bit manipulation macros */
|
||||
#define SM_BIT(name) (1 << SM_##name##_OFFSET)
|
||||
#define SM_BF(name,value) (((value) & ((1 << SM_##name##_SIZE) - 1)) << SM_##name##_OFFSET)
|
||||
#define SM_BFEXT(name,value) (((value) >> SM_##name##_OFFSET) & ((1 << SM_##name##_SIZE) - 1))
|
||||
#define SM_BFINS(name,value,old) (((old) & ~(((1 << SM_##name##_SIZE) - 1) << SM_##name##_OFFSET)) | SM_BF(name,value))
|
||||
|
||||
/* Register access macros */
|
||||
#define sm_readl(port,reg) readl((port)->regs + SM_##reg)
|
||||
#define sm_writel(port,reg,value) writel((value), (port)->regs + SM_##reg)
|
||||
|
||||
#endif /* __ASM_AVR32_SM_H__ */
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче