Merge branch 'master' into upstream
This commit is contained in:
Коммит
c226951b93
|
@ -1363,6 +1363,11 @@ 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.
|
||||
|
||||
resume= [SWSUSP]
|
||||
Specify the partition device for software suspend
|
||||
|
||||
|
|
|
@ -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
|
||||
=====
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
24
MAINTAINERS
24
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
|
||||
|
@ -2031,6 +2048,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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
|
@ -0,0 +1,754 @@
|
|||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Linux kernel version: 2.6.18-rc1
|
||||
# Tue Jul 11 12:41:36 2006
|
||||
#
|
||||
CONFIG_AVR32=y
|
||||
CONFIG_GENERIC_HARDIRQS=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_GENERIC_IRQ_PROBE=y
|
||||
CONFIG_RWSEM_GENERIC_SPINLOCK=y
|
||||
CONFIG_GENERIC_HWEIGHT=y
|
||||
CONFIG_GENERIC_CALIBRATE_DELAY=y
|
||||
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
|
||||
|
||||
#
|
||||
# Code maturity level options
|
||||
#
|
||||
CONFIG_EXPERIMENTAL=y
|
||||
CONFIG_BROKEN_ON_SMP=y
|
||||
CONFIG_INIT_ENV_ARG_LIMIT=32
|
||||
|
||||
#
|
||||
# General setup
|
||||
#
|
||||
CONFIG_LOCALVERSION=""
|
||||
# CONFIG_LOCALVERSION_AUTO is not set
|
||||
CONFIG_SWAP=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 is not set
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
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=y
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_BUG=y
|
||||
CONFIG_ELF_CORE=y
|
||||
# CONFIG_BASE_FULL is not set
|
||||
# CONFIG_FUTEX is not set
|
||||
# CONFIG_EPOLL is not set
|
||||
CONFIG_SHMEM=y
|
||||
# CONFIG_SLAB is not set
|
||||
# CONFIG_VM_EVENT_COUNTERS is not set
|
||||
# CONFIG_TINY_SHMEM is not set
|
||||
CONFIG_BASE_SMALL=1
|
||||
CONFIG_SLOB=y
|
||||
|
||||
#
|
||||
# Loadable module support
|
||||
#
|
||||
CONFIG_MODULES=y
|
||||
CONFIG_MODULE_UNLOAD=y
|
||||
# CONFIG_MODULE_FORCE_UNLOAD is not set
|
||||
# CONFIG_MODVERSIONS is not set
|
||||
# CONFIG_MODULE_SRCVERSION_ALL is not set
|
||||
# CONFIG_KMOD is not set
|
||||
|
||||
#
|
||||
# Block layer
|
||||
#
|
||||
# CONFIG_BLK_DEV_IO_TRACE is not set
|
||||
|
||||
#
|
||||
# IO Schedulers
|
||||
#
|
||||
CONFIG_IOSCHED_NOOP=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=y
|
||||
CONFIG_DEFAULT_IOSCHED="noop"
|
||||
|
||||
#
|
||||
# System Type and features
|
||||
#
|
||||
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
|
||||
|
||||
#
|
||||
# Executable file formats
|
||||
#
|
||||
CONFIG_BINFMT_ELF=y
|
||||
# CONFIG_BINFMT_MISC is not set
|
||||
|
||||
#
|
||||
# Networking
|
||||
#
|
||||
CONFIG_NET=y
|
||||
|
||||
#
|
||||
# Networking options
|
||||
#
|
||||
# CONFIG_NETDEBUG is not set
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_PACKET_MMAP=y
|
||||
CONFIG_UNIX=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=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
|
||||
# CONFIG_ARPD is not set
|
||||
# CONFIG_SYN_COOKIES is not set
|
||||
# CONFIG_INET_AH is not set
|
||||
# CONFIG_INET_ESP is not set
|
||||
# CONFIG_INET_IPCOMP is not set
|
||||
# CONFIG_INET_XFRM_TUNNEL is not set
|
||||
# CONFIG_INET_TUNNEL is not set
|
||||
# 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
|
||||
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 is not set
|
||||
# CONFIG_NETFILTER is not set
|
||||
|
||||
#
|
||||
# DCCP Configuration (EXPERIMENTAL)
|
||||
#
|
||||
# CONFIG_IP_DCCP is not set
|
||||
|
||||
#
|
||||
# SCTP Configuration (EXPERIMENTAL)
|
||||
#
|
||||
# CONFIG_IP_SCTP is not set
|
||||
|
||||
#
|
||||
# TIPC Configuration (EXPERIMENTAL)
|
||||
#
|
||||
# CONFIG_TIPC is not set
|
||||
# CONFIG_ATM is not set
|
||||
# CONFIG_BRIDGE is not set
|
||||
# CONFIG_VLAN_8021Q is not set
|
||||
# CONFIG_DECNET is not set
|
||||
# CONFIG_LLC2 is not set
|
||||
# CONFIG_IPX is not set
|
||||
# CONFIG_ATALK is not set
|
||||
# CONFIG_X25 is not set
|
||||
# CONFIG_LAPB is not set
|
||||
# CONFIG_NET_DIVERT is not set
|
||||
# CONFIG_ECONET is not set
|
||||
# CONFIG_WAN_ROUTER is not set
|
||||
|
||||
#
|
||||
# QoS and/or fair queueing
|
||||
#
|
||||
# CONFIG_NET_SCHED is not set
|
||||
|
||||
#
|
||||
# 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 is not set
|
||||
|
||||
#
|
||||
# Device Drivers
|
||||
#
|
||||
|
||||
#
|
||||
# Generic Driver Options
|
||||
#
|
||||
CONFIG_STANDALONE=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 is not set
|
||||
|
||||
#
|
||||
# Memory Technology Devices (MTD)
|
||||
#
|
||||
# CONFIG_MTD is not set
|
||||
|
||||
#
|
||||
# Parallel port support
|
||||
#
|
||||
# CONFIG_PARPORT is not set
|
||||
|
||||
#
|
||||
# Plug and Play support
|
||||
#
|
||||
|
||||
#
|
||||
# Block devices
|
||||
#
|
||||
# CONFIG_BLK_DEV_COW_COMMON is not set
|
||||
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
|
||||
#
|
||||
# CONFIG_IDE is not set
|
||||
|
||||
#
|
||||
# SCSI device support
|
||||
#
|
||||
# CONFIG_RAID_ATTRS is not set
|
||||
# CONFIG_SCSI is not set
|
||||
|
||||
#
|
||||
# Multi-device support (RAID and LVM)
|
||||
#
|
||||
# CONFIG_MD is not set
|
||||
|
||||
#
|
||||
# Fusion MPT device support
|
||||
#
|
||||
# CONFIG_FUSION is not set
|
||||
|
||||
#
|
||||
# IEEE 1394 (FireWire) support
|
||||
#
|
||||
|
||||
#
|
||||
# I2O device support
|
||||
#
|
||||
|
||||
#
|
||||
# Network device support
|
||||
#
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_DUMMY=y
|
||||
# CONFIG_BONDING is not set
|
||||
# CONFIG_EQUALIZER is not set
|
||||
CONFIG_TUN=m
|
||||
|
||||
#
|
||||
# PHY device support
|
||||
#
|
||||
# CONFIG_PHYLIB is not set
|
||||
|
||||
#
|
||||
# Ethernet (10 or 100Mbit)
|
||||
#
|
||||
CONFIG_NET_ETHERNET=y
|
||||
CONFIG_MII=y
|
||||
CONFIG_MACB=y
|
||||
|
||||
#
|
||||
# Ethernet (1000 Mbit)
|
||||
#
|
||||
|
||||
#
|
||||
# Ethernet (10000 Mbit)
|
||||
#
|
||||
|
||||
#
|
||||
# Token Ring devices
|
||||
#
|
||||
|
||||
#
|
||||
# Wireless LAN (non-hamradio)
|
||||
#
|
||||
# CONFIG_NET_RADIO is not set
|
||||
|
||||
#
|
||||
# Wan interfaces
|
||||
#
|
||||
# CONFIG_WAN 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
|
||||
# CONFIG_NETPOLL is not set
|
||||
# CONFIG_NET_POLL_CONTROLLER is not set
|
||||
|
||||
#
|
||||
# ISDN subsystem
|
||||
#
|
||||
# CONFIG_ISDN is not set
|
||||
|
||||
#
|
||||
# Telephony Support
|
||||
#
|
||||
# CONFIG_PHONE is not set
|
||||
|
||||
#
|
||||
# Input device support
|
||||
#
|
||||
# CONFIG_INPUT is not set
|
||||
|
||||
#
|
||||
# Hardware I/O ports
|
||||
#
|
||||
# CONFIG_SERIO is not set
|
||||
# CONFIG_GAMEPORT is not set
|
||||
|
||||
#
|
||||
# Character devices
|
||||
#
|
||||
# CONFIG_VT is not set
|
||||
# CONFIG_SERIAL_NONSTANDARD is not set
|
||||
|
||||
#
|
||||
# Serial drivers
|
||||
#
|
||||
# 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 is not set
|
||||
|
||||
#
|
||||
# IPMI
|
||||
#
|
||||
# CONFIG_IPMI_HANDLER is not set
|
||||
|
||||
#
|
||||
# Watchdog Cards
|
||||
#
|
||||
# CONFIG_WATCHDOG is not set
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
# CONFIG_RTC is not set
|
||||
# CONFIG_GEN_RTC is not set
|
||||
# CONFIG_DTLK is not set
|
||||
# CONFIG_R3964 is not set
|
||||
|
||||
#
|
||||
# Ftape, the floppy tape device driver
|
||||
#
|
||||
# CONFIG_RAW_DRIVER is not set
|
||||
|
||||
#
|
||||
# TPM devices
|
||||
#
|
||||
# CONFIG_TCG_TPM is not set
|
||||
# CONFIG_TELCLOCK is not set
|
||||
|
||||
#
|
||||
# I2C support
|
||||
#
|
||||
# CONFIG_I2C is not set
|
||||
|
||||
#
|
||||
# SPI support
|
||||
#
|
||||
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
|
||||
#
|
||||
|
||||
#
|
||||
# Hardware Monitoring support
|
||||
#
|
||||
# CONFIG_HWMON is not set
|
||||
# CONFIG_HWMON_VID is not set
|
||||
|
||||
#
|
||||
# Misc devices
|
||||
#
|
||||
|
||||
#
|
||||
# Multimedia devices
|
||||
#
|
||||
# CONFIG_VIDEO_DEV is not set
|
||||
CONFIG_VIDEO_V4L2=y
|
||||
|
||||
#
|
||||
# Digital Video Broadcasting Devices
|
||||
#
|
||||
# CONFIG_DVB is not set
|
||||
|
||||
#
|
||||
# Graphics support
|
||||
#
|
||||
# CONFIG_FIRMWARE_EDID 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
|
||||
|
||||
#
|
||||
# Logo configuration
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# CONFIG_SOUND is not set
|
||||
|
||||
#
|
||||
# USB support
|
||||
#
|
||||
# CONFIG_USB_ARCH_HAS_HCD is not set
|
||||
# CONFIG_USB_ARCH_HAS_OHCI is not set
|
||||
# CONFIG_USB_ARCH_HAS_EHCI is not set
|
||||
|
||||
#
|
||||
# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
|
||||
#
|
||||
|
||||
#
|
||||
# USB Gadget Support
|
||||
#
|
||||
# CONFIG_USB_GADGET is not set
|
||||
|
||||
#
|
||||
# MMC/SD Card support
|
||||
#
|
||||
# CONFIG_MMC is not set
|
||||
|
||||
#
|
||||
# LED devices
|
||||
#
|
||||
# CONFIG_NEW_LEDS is not set
|
||||
|
||||
#
|
||||
# LED drivers
|
||||
#
|
||||
|
||||
#
|
||||
# LED Triggers
|
||||
#
|
||||
|
||||
#
|
||||
# InfiniBand support
|
||||
#
|
||||
|
||||
#
|
||||
# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
|
||||
#
|
||||
|
||||
#
|
||||
# Real Time Clock
|
||||
#
|
||||
# CONFIG_RTC_CLASS is not set
|
||||
|
||||
#
|
||||
# DMA Engine support
|
||||
#
|
||||
# CONFIG_DMA_ENGINE is not set
|
||||
|
||||
#
|
||||
# DMA Clients
|
||||
#
|
||||
|
||||
#
|
||||
# DMA Devices
|
||||
#
|
||||
|
||||
#
|
||||
# File systems
|
||||
#
|
||||
CONFIG_EXT2_FS=y
|
||||
# CONFIG_EXT2_FS_XATTR is not set
|
||||
# CONFIG_EXT2_FS_XIP is not set
|
||||
# CONFIG_EXT3_FS is not set
|
||||
# CONFIG_REISERFS_FS is not set
|
||||
# CONFIG_JFS_FS is not set
|
||||
# CONFIG_FS_POSIX_ACL is not set
|
||||
# CONFIG_XFS_FS is not set
|
||||
# CONFIG_OCFS2_FS is not set
|
||||
CONFIG_MINIX_FS=m
|
||||
CONFIG_ROMFS_FS=m
|
||||
# CONFIG_INOTIFY is not set
|
||||
# CONFIG_QUOTA is not set
|
||||
# CONFIG_DNOTIFY is not set
|
||||
# CONFIG_AUTOFS_FS is not set
|
||||
# CONFIG_AUTOFS4_FS is not set
|
||||
# CONFIG_FUSE_FS is not set
|
||||
|
||||
#
|
||||
# CD-ROM/DVD Filesystems
|
||||
#
|
||||
# CONFIG_ISO9660_FS is not set
|
||||
# CONFIG_UDF_FS is not set
|
||||
|
||||
#
|
||||
# DOS/FAT/NT Filesystems
|
||||
#
|
||||
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
|
||||
|
||||
#
|
||||
# Pseudo filesystems
|
||||
#
|
||||
CONFIG_PROC_FS=y
|
||||
CONFIG_PROC_KCORE=y
|
||||
CONFIG_SYSFS=y
|
||||
CONFIG_TMPFS=y
|
||||
# CONFIG_HUGETLB_PAGE is not set
|
||||
CONFIG_RAMFS=y
|
||||
CONFIG_CONFIGFS_FS=m
|
||||
|
||||
#
|
||||
# Miscellaneous filesystems
|
||||
#
|
||||
# CONFIG_ADFS_FS is not set
|
||||
# CONFIG_AFFS_FS is not set
|
||||
# CONFIG_HFS_FS is not set
|
||||
# CONFIG_HFSPLUS_FS is not set
|
||||
# CONFIG_BEFS_FS is not set
|
||||
# CONFIG_BFS_FS is not set
|
||||
# CONFIG_EFS_FS is not set
|
||||
# CONFIG_CRAMFS is not set
|
||||
# CONFIG_VXFS_FS is not set
|
||||
# CONFIG_HPFS_FS is not set
|
||||
# CONFIG_QNX4FS_FS is not set
|
||||
# CONFIG_SYSV_FS is not set
|
||||
# CONFIG_UFS_FS is not set
|
||||
|
||||
#
|
||||
# Network File Systems
|
||||
#
|
||||
CONFIG_NFS_FS=y
|
||||
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=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
|
||||
# CONFIG_9P_FS is not set
|
||||
|
||||
#
|
||||
# Partition Types
|
||||
#
|
||||
# CONFIG_PARTITION_ADVANCED is not set
|
||||
CONFIG_MSDOS_PARTITION=y
|
||||
|
||||
#
|
||||
# Native Language Support
|
||||
#
|
||||
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_TRACE_IRQFLAGS_SUPPORT=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
# CONFIG_UNUSED_SYMBOLS is not set
|
||||
CONFIG_DEBUG_KERNEL=y
|
||||
CONFIG_LOG_BUF_SHIFT=14
|
||||
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 is not set
|
||||
# CONFIG_SECURITY is not set
|
||||
|
||||
#
|
||||
# Cryptographic options
|
||||
#
|
||||
# CONFIG_CRYPTO is not set
|
||||
|
||||
#
|
||||
# Hardware crypto devices
|
||||
#
|
||||
|
||||
#
|
||||
# Library routines
|
||||
#
|
||||
CONFIG_CRC_CCITT=m
|
||||
# CONFIG_CRC16 is not set
|
||||
CONFIG_CRC32=m
|
||||
# CONFIG_LIBCRC32C is not set
|
||||
CONFIG_ZLIB_INFLATE=m
|
||||
CONFIG_ZLIB_DEFLATE=m
|
|
@ -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__ */
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for the Linux/AVR32 kernel.
|
||||
#
|
||||
|
||||
obj-y += init.o clear_page.o copy_page.o dma-coherent.o
|
||||
obj-y += ioremap.o cache.o fault.o tlb.o
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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/highmem.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cachectl.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* If you attempt to flush anything more than this, you need superuser
|
||||
* privileges. The value is completely arbitrary.
|
||||
*/
|
||||
#define CACHEFLUSH_MAX_LEN 1024
|
||||
|
||||
void invalidate_dcache_region(void *start, size_t size)
|
||||
{
|
||||
unsigned long v, begin, end, linesz;
|
||||
|
||||
linesz = boot_cpu_data.dcache.linesz;
|
||||
|
||||
//printk("invalidate dcache: %p + %u\n", start, size);
|
||||
|
||||
/* You asked for it, you got it */
|
||||
begin = (unsigned long)start & ~(linesz - 1);
|
||||
end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
|
||||
|
||||
for (v = begin; v < end; v += linesz)
|
||||
invalidate_dcache_line((void *)v);
|
||||
}
|
||||
|
||||
void clean_dcache_region(void *start, size_t size)
|
||||
{
|
||||
unsigned long v, begin, end, linesz;
|
||||
|
||||
linesz = boot_cpu_data.dcache.linesz;
|
||||
begin = (unsigned long)start & ~(linesz - 1);
|
||||
end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
|
||||
|
||||
for (v = begin; v < end; v += linesz)
|
||||
clean_dcache_line((void *)v);
|
||||
flush_write_buffer();
|
||||
}
|
||||
|
||||
void flush_dcache_region(void *start, size_t size)
|
||||
{
|
||||
unsigned long v, begin, end, linesz;
|
||||
|
||||
linesz = boot_cpu_data.dcache.linesz;
|
||||
begin = (unsigned long)start & ~(linesz - 1);
|
||||
end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
|
||||
|
||||
for (v = begin; v < end; v += linesz)
|
||||
flush_dcache_line((void *)v);
|
||||
flush_write_buffer();
|
||||
}
|
||||
|
||||
void invalidate_icache_region(void *start, size_t size)
|
||||
{
|
||||
unsigned long v, begin, end, linesz;
|
||||
|
||||
linesz = boot_cpu_data.icache.linesz;
|
||||
begin = (unsigned long)start & ~(linesz - 1);
|
||||
end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
|
||||
|
||||
for (v = begin; v < end; v += linesz)
|
||||
invalidate_icache_line((void *)v);
|
||||
}
|
||||
|
||||
static inline void __flush_icache_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long v, linesz;
|
||||
|
||||
linesz = boot_cpu_data.dcache.linesz;
|
||||
for (v = start; v < end; v += linesz) {
|
||||
clean_dcache_line((void *)v);
|
||||
invalidate_icache_line((void *)v);
|
||||
}
|
||||
|
||||
flush_write_buffer();
|
||||
}
|
||||
|
||||
/*
|
||||
* This one is called after a module has been loaded.
|
||||
*/
|
||||
void flush_icache_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long linesz;
|
||||
|
||||
linesz = boot_cpu_data.dcache.linesz;
|
||||
__flush_icache_range(start & ~(linesz - 1),
|
||||
(end + linesz - 1) & ~(linesz - 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* This one is called from do_no_page(), do_swap_page() and install_page().
|
||||
*/
|
||||
void flush_icache_page(struct vm_area_struct *vma, struct page *page)
|
||||
{
|
||||
if (vma->vm_flags & VM_EXEC) {
|
||||
void *v = kmap(page);
|
||||
__flush_icache_range((unsigned long)v, (unsigned long)v + PAGE_SIZE);
|
||||
kunmap(v);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This one is used by copy_to_user_page()
|
||||
*/
|
||||
void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
|
||||
unsigned long addr, int len)
|
||||
{
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
flush_icache_range(addr, addr + len);
|
||||
}
|
||||
|
||||
asmlinkage int sys_cacheflush(int operation, void __user *addr, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (len > CACHEFLUSH_MAX_LEN) {
|
||||
ret = -EPERM;
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EFAULT;
|
||||
if (!access_ok(VERIFY_WRITE, addr, len))
|
||||
goto out;
|
||||
|
||||
switch (operation) {
|
||||
case CACHE_IFLUSH:
|
||||
flush_icache_range((unsigned long)addr,
|
||||
(unsigned long)addr + len);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* clear_page
|
||||
* r12: P1 address (to)
|
||||
*/
|
||||
.text
|
||||
.global clear_page
|
||||
clear_page:
|
||||
sub r9, r12, -PAGE_SIZE
|
||||
mov r10, 0
|
||||
mov r11, 0
|
||||
0: st.d r12++, r10
|
||||
cp r12, r9
|
||||
brne 0b
|
||||
mov pc, lr
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* copy_page
|
||||
*
|
||||
* r12 to (P1 address)
|
||||
* r11 from (P1 address)
|
||||
* r8-r10 scratch
|
||||
*/
|
||||
.text
|
||||
.global copy_page
|
||||
copy_page:
|
||||
sub r10, r11, -(1 << PAGE_SHIFT)
|
||||
/* pref r11[0] */
|
||||
1: /* pref r11[8] */
|
||||
ld.d r8, r11++
|
||||
st.d r12++, r8
|
||||
cp r11, r10
|
||||
brlo 1b
|
||||
mov pc, lr
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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/dma-mapping.h>
|
||||
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
void dma_cache_sync(void *vaddr, size_t size, int direction)
|
||||
{
|
||||
/*
|
||||
* No need to sync an uncached area
|
||||
*/
|
||||
if (PXSEG(vaddr) == P2SEG)
|
||||
return;
|
||||
|
||||
switch (direction) {
|
||||
case DMA_FROM_DEVICE: /* invalidate only */
|
||||
dma_cache_inv(vaddr, size);
|
||||
break;
|
||||
case DMA_TO_DEVICE: /* writeback only */
|
||||
dma_cache_wback(vaddr, size);
|
||||
break;
|
||||
case DMA_BIDIRECTIONAL: /* writeback and invalidate */
|
||||
dma_cache_wback_inv(vaddr, size);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(dma_cache_sync);
|
||||
|
||||
static struct page *__dma_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *handle, gfp_t gfp)
|
||||
{
|
||||
struct page *page, *free, *end;
|
||||
int order;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
order = get_order(size);
|
||||
|
||||
page = alloc_pages(gfp, order);
|
||||
if (!page)
|
||||
return NULL;
|
||||
split_page(page, order);
|
||||
|
||||
/*
|
||||
* When accessing physical memory with valid cache data, we
|
||||
* get a cache hit even if the virtual memory region is marked
|
||||
* as uncached.
|
||||
*
|
||||
* Since the memory is newly allocated, there is no point in
|
||||
* doing a writeback. If the previous owner cares, he should
|
||||
* have flushed the cache before releasing the memory.
|
||||
*/
|
||||
invalidate_dcache_region(phys_to_virt(page_to_phys(page)), size);
|
||||
|
||||
*handle = page_to_bus(page);
|
||||
free = page + (size >> PAGE_SHIFT);
|
||||
end = page + (1 << order);
|
||||
|
||||
/*
|
||||
* Free any unused pages
|
||||
*/
|
||||
while (free < end) {
|
||||
__free_page(free);
|
||||
free++;
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static void __dma_free(struct device *dev, size_t size,
|
||||
struct page *page, dma_addr_t handle)
|
||||
{
|
||||
struct page *end = page + (PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
|
||||
while (page < end)
|
||||
__free_page(page++);
|
||||
}
|
||||
|
||||
void *dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *handle, gfp_t gfp)
|
||||
{
|
||||
struct page *page;
|
||||
void *ret = NULL;
|
||||
|
||||
page = __dma_alloc(dev, size, handle, gfp);
|
||||
if (page)
|
||||
ret = phys_to_uncached(page_to_phys(page));
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_alloc_coherent);
|
||||
|
||||
void dma_free_coherent(struct device *dev, size_t size,
|
||||
void *cpu_addr, dma_addr_t handle)
|
||||
{
|
||||
void *addr = phys_to_cached(uncached_to_phys(cpu_addr));
|
||||
struct page *page;
|
||||
|
||||
pr_debug("dma_free_coherent addr %p (phys %08lx) size %u\n",
|
||||
cpu_addr, (unsigned long)handle, (unsigned)size);
|
||||
BUG_ON(!virt_addr_valid(addr));
|
||||
page = virt_to_page(addr);
|
||||
__dma_free(dev, size, page, handle);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_free_coherent);
|
||||
|
||||
#if 0
|
||||
void *dma_alloc_writecombine(struct device *dev, size_t size,
|
||||
dma_addr_t *handle, gfp_t gfp)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = __dma_alloc(dev, size, handle, gfp);
|
||||
|
||||
/* Now, map the page into P3 with write-combining turned on */
|
||||
return __ioremap(page_to_phys(page), size, _PAGE_BUFFER);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_alloc_writecombine);
|
||||
|
||||
void dma_free_writecombine(struct device *dev, size_t size,
|
||||
void *cpu_addr, dma_addr_t handle)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
iounmap(cpu_addr);
|
||||
|
||||
page = bus_to_page(handle);
|
||||
__dma_free(dev, size, page, handle);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_free_writecombine);
|
||||
#endif
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2006 Atmel Corporation
|
||||
*
|
||||
* Based on linux/arch/sh/mm/fault.c:
|
||||
* Copyright (C) 1999 Niibe Yutaka
|
||||
*
|
||||
* 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/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include <asm/kdebug.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/sysreg.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dump_code(unsigned long pc)
|
||||
{
|
||||
char *p = (char *)pc;
|
||||
char val;
|
||||
int i;
|
||||
|
||||
|
||||
printk(KERN_DEBUG "Code:");
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (__get_user(val, p + i))
|
||||
break;
|
||||
printk(" %02x", val);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
|
||||
|
||||
/* Hook to register for page fault notifications */
|
||||
int register_page_fault_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_register(¬ify_page_fault_chain, nb);
|
||||
}
|
||||
|
||||
int unregister_page_fault_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb);
|
||||
}
|
||||
|
||||
static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
|
||||
int trap, int sig)
|
||||
{
|
||||
struct die_args args = {
|
||||
.regs = regs,
|
||||
.trapnr = trap,
|
||||
};
|
||||
return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args);
|
||||
}
|
||||
#else
|
||||
static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
|
||||
int trap, int sig)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This routine handles page faults. It determines the address and the
|
||||
* problem, and then passes it off to one of the appropriate routines.
|
||||
*
|
||||
* ecr is the Exception Cause Register. Possible values are:
|
||||
* 5: Page not found (instruction access)
|
||||
* 6: Protection fault (instruction access)
|
||||
* 12: Page not found (read access)
|
||||
* 13: Page not found (write access)
|
||||
* 14: Protection fault (read access)
|
||||
* 15: Protection fault (write access)
|
||||
*/
|
||||
asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
struct mm_struct *mm;
|
||||
struct vm_area_struct *vma;
|
||||
const struct exception_table_entry *fixup;
|
||||
unsigned long address;
|
||||
unsigned long page;
|
||||
int writeaccess = 0;
|
||||
|
||||
if (notify_page_fault(DIE_PAGE_FAULT, regs,
|
||||
ecr, SIGSEGV) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
address = sysreg_read(TLBEAR);
|
||||
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
|
||||
/*
|
||||
* If we're in an interrupt or have no user context, we must
|
||||
* not take the fault...
|
||||
*/
|
||||
if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM))
|
||||
goto no_context;
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
|
||||
vma = find_vma(mm, address);
|
||||
if (!vma)
|
||||
goto bad_area;
|
||||
if (vma->vm_start <= address)
|
||||
goto good_area;
|
||||
if (!(vma->vm_flags & VM_GROWSDOWN))
|
||||
goto bad_area;
|
||||
if (expand_stack(vma, address))
|
||||
goto bad_area;
|
||||
|
||||
/*
|
||||
* Ok, we have a good vm_area for this memory access, so we
|
||||
* can handle it...
|
||||
*/
|
||||
good_area:
|
||||
//pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags);
|
||||
switch (ecr) {
|
||||
case ECR_PROTECTION_X:
|
||||
case ECR_TLB_MISS_X:
|
||||
if (!(vma->vm_flags & VM_EXEC))
|
||||
goto bad_area;
|
||||
break;
|
||||
case ECR_PROTECTION_R:
|
||||
case ECR_TLB_MISS_R:
|
||||
if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
|
||||
goto bad_area;
|
||||
break;
|
||||
case ECR_PROTECTION_W:
|
||||
case ECR_TLB_MISS_W:
|
||||
if (!(vma->vm_flags & VM_WRITE))
|
||||
goto bad_area;
|
||||
writeaccess = 1;
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled case %lu in do_page_fault!", ecr);
|
||||
}
|
||||
|
||||
/*
|
||||
* If for any reason at all we couldn't handle the fault, make
|
||||
* sure we exit gracefully rather than endlessly redo the
|
||||
* fault.
|
||||
*/
|
||||
survive:
|
||||
switch (handle_mm_fault(mm, vma, address, writeaccess)) {
|
||||
case VM_FAULT_MINOR:
|
||||
tsk->min_flt++;
|
||||
break;
|
||||
case VM_FAULT_MAJOR:
|
||||
tsk->maj_flt++;
|
||||
break;
|
||||
case VM_FAULT_SIGBUS:
|
||||
goto do_sigbus;
|
||||
case VM_FAULT_OOM:
|
||||
goto out_of_memory;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
up_read(&mm->mmap_sem);
|
||||
return;
|
||||
|
||||
/*
|
||||
* Something tried to access memory that isn't in our memory
|
||||
* map. Fix it, but check if it's kernel or user first...
|
||||
*/
|
||||
bad_area:
|
||||
pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n",
|
||||
tsk->comm, tsk->pid, address, ecr);
|
||||
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
if (user_mode(regs)) {
|
||||
/* Hmm...we have to pass address and ecr somehow... */
|
||||
/* tsk->thread.address = address;
|
||||
tsk->thread.error_code = ecr; */
|
||||
#ifdef DEBUG
|
||||
show_regs(regs);
|
||||
dump_code(regs->pc);
|
||||
|
||||
page = sysreg_read(PTBR);
|
||||
printk("ptbr = %08lx", page);
|
||||
if (page) {
|
||||
page = ((unsigned long *)page)[address >> 22];
|
||||
printk(" pgd = %08lx", page);
|
||||
if (page & _PAGE_PRESENT) {
|
||||
page &= PAGE_MASK;
|
||||
address &= 0x003ff000;
|
||||
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
|
||||
printk(" pte = %08lx\n", page);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pr_debug("Sending SIGSEGV to PID %d...\n",
|
||||
tsk->pid);
|
||||
force_sig(SIGSEGV, tsk);
|
||||
return;
|
||||
}
|
||||
|
||||
no_context:
|
||||
pr_debug("No context\n");
|
||||
|
||||
/* Are we prepared to handle this kernel fault? */
|
||||
fixup = search_exception_tables(regs->pc);
|
||||
if (fixup) {
|
||||
regs->pc = fixup->fixup;
|
||||
pr_debug("Found fixup at %08lx\n", fixup->fixup);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Oops. The kernel tried to access some bad page. We'll have
|
||||
* to terminate things with extreme prejudice.
|
||||
*/
|
||||
if (address < PAGE_SIZE)
|
||||
printk(KERN_ALERT
|
||||
"Unable to handle kernel NULL pointer dereference");
|
||||
else
|
||||
printk(KERN_ALERT
|
||||
"Unable to handle kernel paging request");
|
||||
printk(" at virtual address %08lx\n", address);
|
||||
printk(KERN_ALERT "pc = %08lx\n", regs->pc);
|
||||
|
||||
page = sysreg_read(PTBR);
|
||||
printk(KERN_ALERT "ptbr = %08lx", page);
|
||||
if (page) {
|
||||
page = ((unsigned long *)page)[address >> 22];
|
||||
printk(" pgd = %08lx", page);
|
||||
if (page & _PAGE_PRESENT) {
|
||||
page &= PAGE_MASK;
|
||||
address &= 0x003ff000;
|
||||
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
|
||||
printk(" pte = %08lx\n", page);
|
||||
}
|
||||
}
|
||||
die("\nOops", regs, ecr);
|
||||
do_exit(SIGKILL);
|
||||
|
||||
/*
|
||||
* We ran out of memory, or some other thing happened to us
|
||||
* that made us unable to handle the page fault gracefully.
|
||||
*/
|
||||
out_of_memory:
|
||||
printk("Out of memory\n");
|
||||
up_read(&mm->mmap_sem);
|
||||
if (current->pid == 1) {
|
||||
yield();
|
||||
down_read(&mm->mmap_sem);
|
||||
goto survive;
|
||||
}
|
||||
printk("VM: Killing process %s\n", tsk->comm);
|
||||
if (user_mode(regs))
|
||||
do_exit(SIGKILL);
|
||||
goto no_context;
|
||||
|
||||
do_sigbus:
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
/*
|
||||
* Send a sigbus, regardless of whether we were in kernel or
|
||||
* user mode.
|
||||
*/
|
||||
/* address, error_code, trap_no, ... */
|
||||
#ifdef DEBUG
|
||||
show_regs(regs);
|
||||
dump_code(regs->pc);
|
||||
#endif
|
||||
pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid);
|
||||
force_sig(SIGBUS, tsk);
|
||||
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
if (!user_mode(regs))
|
||||
goto no_context;
|
||||
}
|
||||
|
||||
asmlinkage void do_bus_error(unsigned long addr, int write_access,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
printk(KERN_ALERT
|
||||
"Bus error at physical address 0x%08lx (%s access)\n",
|
||||
addr, write_access ? "write" : "read");
|
||||
printk(KERN_INFO "DTLB dump:\n");
|
||||
dump_dtlb();
|
||||
die("Bus Error", regs, write_access);
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
/*
|
||||
* This functionality is currently not possible to implement because
|
||||
* we're using segmentation to ensure a fixed mapping of the kernel
|
||||
* virtual address space.
|
||||
*
|
||||
* It would be possible to implement this, but it would require us to
|
||||
* disable segmentation at startup and load the kernel mappings into
|
||||
* the TLB like any other pages. There will be lots of trickery to
|
||||
* avoid recursive invocation of the TLB miss handler, though...
|
||||
*/
|
||||
#ifdef CONFIG_DEBUG_PAGEALLOC
|
||||
void kernel_map_pages(struct page *page, int numpages, int enable)
|
||||
{
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_map_pages);
|
||||
#endif
|
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* 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/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/initrd.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/nodemask.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
|
||||
|
||||
pgd_t swapper_pg_dir[PTRS_PER_PGD];
|
||||
|
||||
struct page *empty_zero_page;
|
||||
|
||||
/*
|
||||
* Cache of MMU context last used.
|
||||
*/
|
||||
unsigned long mmu_context_cache = NO_CONTEXT;
|
||||
|
||||
#define START_PFN (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT)
|
||||
#define MAX_LOW_PFN (NODE_DATA(0)->bdata->node_low_pfn)
|
||||
|
||||
void show_mem(void)
|
||||
{
|
||||
int total = 0, reserved = 0, cached = 0;
|
||||
int slab = 0, free = 0, shared = 0;
|
||||
pg_data_t *pgdat;
|
||||
|
||||
printk("Mem-info:\n");
|
||||
show_free_areas();
|
||||
|
||||
for_each_online_pgdat(pgdat) {
|
||||
struct page *page, *end;
|
||||
|
||||
page = pgdat->node_mem_map;
|
||||
end = page + pgdat->node_spanned_pages;
|
||||
|
||||
do {
|
||||
total++;
|
||||
if (PageReserved(page))
|
||||
reserved++;
|
||||
else if (PageSwapCache(page))
|
||||
cached++;
|
||||
else if (PageSlab(page))
|
||||
slab++;
|
||||
else if (!page_count(page))
|
||||
free++;
|
||||
else
|
||||
shared += page_count(page) - 1;
|
||||
page++;
|
||||
} while (page < end);
|
||||
}
|
||||
|
||||
printk ("%d pages of RAM\n", total);
|
||||
printk ("%d free pages\n", free);
|
||||
printk ("%d reserved pages\n", reserved);
|
||||
printk ("%d slab pages\n", slab);
|
||||
printk ("%d pages shared\n", shared);
|
||||
printk ("%d pages swap cached\n", cached);
|
||||
}
|
||||
|
||||
static void __init print_memory_map(const char *what,
|
||||
struct tag_mem_range *mem)
|
||||
{
|
||||
printk ("%s:\n", what);
|
||||
for (; mem; mem = mem->next) {
|
||||
printk (" %08lx - %08lx\n",
|
||||
(unsigned long)mem->addr,
|
||||
(unsigned long)(mem->addr + mem->size));
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_LOWMEM HIGHMEM_START
|
||||
#define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM)
|
||||
|
||||
/*
|
||||
* Sort a list of memory regions in-place by ascending address.
|
||||
*
|
||||
* We're using bubble sort because we only have singly linked lists
|
||||
* with few elements.
|
||||
*/
|
||||
static void __init sort_mem_list(struct tag_mem_range **pmem)
|
||||
{
|
||||
int done;
|
||||
struct tag_mem_range **a, **b;
|
||||
|
||||
if (!*pmem)
|
||||
return;
|
||||
|
||||
do {
|
||||
done = 1;
|
||||
a = pmem, b = &(*pmem)->next;
|
||||
while (*b) {
|
||||
if ((*a)->addr > (*b)->addr) {
|
||||
struct tag_mem_range *tmp;
|
||||
tmp = (*b)->next;
|
||||
(*b)->next = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
done = 0;
|
||||
}
|
||||
a = &(*a)->next;
|
||||
b = &(*a)->next;
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a free memory region large enough for storing the
|
||||
* bootmem bitmap.
|
||||
*/
|
||||
static unsigned long __init
|
||||
find_bootmap_pfn(const struct tag_mem_range *mem)
|
||||
{
|
||||
unsigned long bootmap_pages, bootmap_len;
|
||||
unsigned long node_pages = PFN_UP(mem->size);
|
||||
unsigned long bootmap_addr = mem->addr;
|
||||
struct tag_mem_range *reserved = mem_reserved;
|
||||
struct tag_mem_range *ramdisk = mem_ramdisk;
|
||||
unsigned long kern_start = virt_to_phys(_stext);
|
||||
unsigned long kern_end = virt_to_phys(_end);
|
||||
|
||||
bootmap_pages = bootmem_bootmap_pages(node_pages);
|
||||
bootmap_len = bootmap_pages << PAGE_SHIFT;
|
||||
|
||||
/*
|
||||
* Find a large enough region without reserved pages for
|
||||
* storing the bootmem bitmap. We can take advantage of the
|
||||
* fact that all lists have been sorted.
|
||||
*
|
||||
* We have to check explicitly reserved regions as well as the
|
||||
* kernel image and any RAMDISK images...
|
||||
*
|
||||
* Oh, and we have to make sure we don't overwrite the taglist
|
||||
* since we're going to use it until the bootmem allocator is
|
||||
* fully up and running.
|
||||
*/
|
||||
while (1) {
|
||||
if ((bootmap_addr < kern_end) &&
|
||||
((bootmap_addr + bootmap_len) > kern_start))
|
||||
bootmap_addr = kern_end;
|
||||
|
||||
while (reserved &&
|
||||
(bootmap_addr >= (reserved->addr + reserved->size)))
|
||||
reserved = reserved->next;
|
||||
|
||||
if (reserved &&
|
||||
((bootmap_addr + bootmap_len) >= reserved->addr)) {
|
||||
bootmap_addr = reserved->addr + reserved->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (ramdisk &&
|
||||
(bootmap_addr >= (ramdisk->addr + ramdisk->size)))
|
||||
ramdisk = ramdisk->next;
|
||||
|
||||
if (!ramdisk ||
|
||||
((bootmap_addr + bootmap_len) < ramdisk->addr))
|
||||
break;
|
||||
|
||||
bootmap_addr = ramdisk->addr + ramdisk->size;
|
||||
}
|
||||
|
||||
if ((PFN_UP(bootmap_addr) + bootmap_len) >= (mem->addr + mem->size))
|
||||
return ~0UL;
|
||||
|
||||
return PFN_UP(bootmap_addr);
|
||||
}
|
||||
|
||||
void __init setup_bootmem(void)
|
||||
{
|
||||
unsigned bootmap_size;
|
||||
unsigned long first_pfn, bootmap_pfn, pages;
|
||||
unsigned long max_pfn, max_low_pfn;
|
||||
unsigned long kern_start = virt_to_phys(_stext);
|
||||
unsigned long kern_end = virt_to_phys(_end);
|
||||
unsigned node = 0;
|
||||
struct tag_mem_range *bank, *res;
|
||||
|
||||
sort_mem_list(&mem_phys);
|
||||
sort_mem_list(&mem_reserved);
|
||||
|
||||
print_memory_map("Physical memory", mem_phys);
|
||||
print_memory_map("Reserved memory", mem_reserved);
|
||||
|
||||
nodes_clear(node_online_map);
|
||||
|
||||
if (mem_ramdisk) {
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
initrd_start = __va(mem_ramdisk->addr);
|
||||
initrd_end = initrd_start + mem_ramdisk->size;
|
||||
|
||||
print_memory_map("RAMDISK images", mem_ramdisk);
|
||||
if (mem_ramdisk->next)
|
||||
printk(KERN_WARNING
|
||||
"Warning: Only the first RAMDISK image "
|
||||
"will be used\n");
|
||||
sort_mem_list(&mem_ramdisk);
|
||||
#else
|
||||
printk(KERN_WARNING "RAM disk image present, but "
|
||||
"no initrd support in kernel!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mem_phys->next)
|
||||
printk(KERN_WARNING "Only using first memory bank\n");
|
||||
|
||||
for (bank = mem_phys; bank; bank = NULL) {
|
||||
first_pfn = PFN_UP(bank->addr);
|
||||
max_low_pfn = max_pfn = PFN_DOWN(bank->addr + bank->size);
|
||||
bootmap_pfn = find_bootmap_pfn(bank);
|
||||
if (bootmap_pfn > max_pfn)
|
||||
panic("No space for bootmem bitmap!\n");
|
||||
|
||||
if (max_low_pfn > MAX_LOWMEM_PFN) {
|
||||
max_low_pfn = MAX_LOWMEM_PFN;
|
||||
#ifndef CONFIG_HIGHMEM
|
||||
/*
|
||||
* Lowmem is memory that can be addressed
|
||||
* directly through P1/P2
|
||||
*/
|
||||
printk(KERN_WARNING
|
||||
"Node %u: Only %ld MiB of memory will be used.\n",
|
||||
node, MAX_LOWMEM >> 20);
|
||||
printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
|
||||
#else
|
||||
#error HIGHMEM is not supported by AVR32 yet
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the boot-time allocator with low memory only. */
|
||||
bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn,
|
||||
first_pfn, max_low_pfn);
|
||||
|
||||
printk("Node %u: bdata = %p, bdata->node_bootmem_map = %p\n",
|
||||
node, NODE_DATA(node)->bdata,
|
||||
NODE_DATA(node)->bdata->node_bootmem_map);
|
||||
|
||||
/*
|
||||
* Register fully available RAM pages with the bootmem
|
||||
* allocator.
|
||||
*/
|
||||
pages = max_low_pfn - first_pfn;
|
||||
free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn),
|
||||
PFN_PHYS(pages));
|
||||
|
||||
/*
|
||||
* Reserve space for the kernel image (if present in
|
||||
* this node)...
|
||||
*/
|
||||
if ((kern_start >= PFN_PHYS(first_pfn)) &&
|
||||
(kern_start < PFN_PHYS(max_pfn))) {
|
||||
printk("Node %u: Kernel image %08lx - %08lx\n",
|
||||
node, kern_start, kern_end);
|
||||
reserve_bootmem_node(NODE_DATA(node), kern_start,
|
||||
kern_end - kern_start);
|
||||
}
|
||||
|
||||
/* ...the bootmem bitmap... */
|
||||
reserve_bootmem_node(NODE_DATA(node),
|
||||
PFN_PHYS(bootmap_pfn),
|
||||
bootmap_size);
|
||||
|
||||
/* ...any RAMDISK images... */
|
||||
for (res = mem_ramdisk; res; res = res->next) {
|
||||
if (res->addr > PFN_PHYS(max_pfn))
|
||||
break;
|
||||
|
||||
if (res->addr >= PFN_PHYS(first_pfn)) {
|
||||
printk("Node %u: RAMDISK %08lx - %08lx\n",
|
||||
node,
|
||||
(unsigned long)res->addr,
|
||||
(unsigned long)(res->addr + res->size));
|
||||
reserve_bootmem_node(NODE_DATA(node),
|
||||
res->addr, res->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* ...and any other reserved regions. */
|
||||
for (res = mem_reserved; res; res = res->next) {
|
||||
if (res->addr > PFN_PHYS(max_pfn))
|
||||
break;
|
||||
|
||||
if (res->addr >= PFN_PHYS(first_pfn)) {
|
||||
printk("Node %u: Reserved %08lx - %08lx\n",
|
||||
node,
|
||||
(unsigned long)res->addr,
|
||||
(unsigned long)(res->addr + res->size));
|
||||
reserve_bootmem_node(NODE_DATA(node),
|
||||
res->addr, res->size);
|
||||
}
|
||||
}
|
||||
|
||||
node_set_online(node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* paging_init() sets up the page tables
|
||||
*
|
||||
* This routine also unmaps the page at virtual kernel address 0, so
|
||||
* that we can trap those pesky NULL-reference errors in the kernel.
|
||||
*/
|
||||
void __init paging_init(void)
|
||||
{
|
||||
extern unsigned long _evba;
|
||||
void *zero_page;
|
||||
int nid;
|
||||
|
||||
/*
|
||||
* Make sure we can handle exceptions before enabling
|
||||
* paging. Not that we should ever _get_ any exceptions this
|
||||
* early, but you never know...
|
||||
*/
|
||||
printk("Exception vectors start at %p\n", &_evba);
|
||||
sysreg_write(EVBA, (unsigned long)&_evba);
|
||||
|
||||
/*
|
||||
* Since we are ready to handle exceptions now, we should let
|
||||
* the CPU generate them...
|
||||
*/
|
||||
__asm__ __volatile__ ("csrf %0" : : "i"(SR_EM_BIT));
|
||||
|
||||
/*
|
||||
* Allocate the zero page. The allocator will panic if it
|
||||
* can't satisfy the request, so no need to check.
|
||||
*/
|
||||
zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0),
|
||||
PAGE_SIZE);
|
||||
|
||||
{
|
||||
pgd_t *pg_dir;
|
||||
int i;
|
||||
|
||||
pg_dir = swapper_pg_dir;
|
||||
sysreg_write(PTBR, (unsigned long)pg_dir);
|
||||
|
||||
for (i = 0; i < PTRS_PER_PGD; i++)
|
||||
pgd_val(pg_dir[i]) = 0;
|
||||
|
||||
enable_mmu();
|
||||
printk ("CPU: Paging enabled\n");
|
||||
}
|
||||
|
||||
for_each_online_node(nid) {
|
||||
pg_data_t *pgdat = NODE_DATA(nid);
|
||||
unsigned long zones_size[MAX_NR_ZONES];
|
||||
unsigned long low, start_pfn;
|
||||
|
||||
start_pfn = pgdat->bdata->node_boot_start;
|
||||
start_pfn >>= PAGE_SHIFT;
|
||||
low = pgdat->bdata->node_low_pfn;
|
||||
|
||||
memset(zones_size, 0, sizeof(zones_size));
|
||||
zones_size[ZONE_NORMAL] = low - start_pfn;
|
||||
|
||||
printk("Node %u: start_pfn = 0x%lx, low = 0x%lx\n",
|
||||
nid, start_pfn, low);
|
||||
|
||||
free_area_init_node(nid, pgdat, zones_size, start_pfn, NULL);
|
||||
|
||||
printk("Node %u: mem_map starts at %p\n",
|
||||
pgdat->node_id, pgdat->node_mem_map);
|
||||
}
|
||||
|
||||
mem_map = NODE_DATA(0)->node_mem_map;
|
||||
|
||||
memset(zero_page, 0, PAGE_SIZE);
|
||||
empty_zero_page = virt_to_page(zero_page);
|
||||
flush_dcache_page(empty_zero_page);
|
||||
}
|
||||
|
||||
void __init mem_init(void)
|
||||
{
|
||||
int codesize, reservedpages, datasize, initsize;
|
||||
int nid, i;
|
||||
|
||||
reservedpages = 0;
|
||||
high_memory = NULL;
|
||||
|
||||
/* this will put all low memory onto the freelists */
|
||||
for_each_online_node(nid) {
|
||||
pg_data_t *pgdat = NODE_DATA(nid);
|
||||
unsigned long node_pages = 0;
|
||||
void *node_high_memory;
|
||||
|
||||
num_physpages += pgdat->node_present_pages;
|
||||
|
||||
if (pgdat->node_spanned_pages != 0)
|
||||
node_pages = free_all_bootmem_node(pgdat);
|
||||
|
||||
totalram_pages += node_pages;
|
||||
|
||||
for (i = 0; i < node_pages; i++)
|
||||
if (PageReserved(pgdat->node_mem_map + i))
|
||||
reservedpages++;
|
||||
|
||||
node_high_memory = (void *)((pgdat->node_start_pfn
|
||||
+ pgdat->node_spanned_pages)
|
||||
<< PAGE_SHIFT);
|
||||
if (node_high_memory > high_memory)
|
||||
high_memory = node_high_memory;
|
||||
}
|
||||
|
||||
max_mapnr = MAP_NR(high_memory);
|
||||
|
||||
codesize = (unsigned long)_etext - (unsigned long)_text;
|
||||
datasize = (unsigned long)_edata - (unsigned long)_data;
|
||||
initsize = (unsigned long)__init_end - (unsigned long)__init_begin;
|
||||
|
||||
printk ("Memory: %luk/%luk available (%dk kernel code, "
|
||||
"%dk reserved, %dk data, %dk init)\n",
|
||||
(unsigned long)nr_free_pages() << (PAGE_SHIFT - 10),
|
||||
totalram_pages << (PAGE_SHIFT - 10),
|
||||
codesize >> 10,
|
||||
reservedpages << (PAGE_SHIFT - 10),
|
||||
datasize >> 10,
|
||||
initsize >> 10);
|
||||
}
|
||||
|
||||
static inline void free_area(unsigned long addr, unsigned long end, char *s)
|
||||
{
|
||||
unsigned int size = (end - addr) >> 10;
|
||||
|
||||
for (; addr < end; addr += PAGE_SIZE) {
|
||||
struct page *page = virt_to_page(addr);
|
||||
ClearPageReserved(page);
|
||||
init_page_count(page);
|
||||
free_page(addr);
|
||||
totalram_pages++;
|
||||
}
|
||||
|
||||
if (size && s)
|
||||
printk(KERN_INFO "Freeing %s memory: %dK (%lx - %lx)\n",
|
||||
s, size, end - (size << 10), end);
|
||||
}
|
||||
|
||||
void free_initmem(void)
|
||||
{
|
||||
free_area((unsigned long)__init_begin, (unsigned long)__init_end,
|
||||
"init");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
|
||||
static int keep_initrd;
|
||||
|
||||
void free_initrd_mem(unsigned long start, unsigned long end)
|
||||
{
|
||||
if (!keep_initrd)
|
||||
free_area(start, end, "initrd");
|
||||
}
|
||||
|
||||
static int __init keepinitrd_setup(char *__unused)
|
||||
{
|
||||
keep_initrd = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("keepinitrd", keepinitrd_setup);
|
||||
#endif
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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/vmalloc.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/addrspace.h>
|
||||
|
||||
static inline int remap_area_pte(pte_t *pte, unsigned long address,
|
||||
unsigned long end, unsigned long phys_addr,
|
||||
pgprot_t prot)
|
||||
{
|
||||
unsigned long pfn;
|
||||
|
||||
pfn = phys_addr >> PAGE_SHIFT;
|
||||
do {
|
||||
WARN_ON(!pte_none(*pte));
|
||||
|
||||
set_pte(pte, pfn_pte(pfn, prot));
|
||||
address += PAGE_SIZE;
|
||||
pfn++;
|
||||
pte++;
|
||||
} while (address && (address < end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int remap_area_pmd(pmd_t *pmd, unsigned long address,
|
||||
unsigned long end, unsigned long phys_addr,
|
||||
pgprot_t prot)
|
||||
{
|
||||
unsigned long next;
|
||||
|
||||
phys_addr -= address;
|
||||
|
||||
do {
|
||||
pte_t *pte = pte_alloc_kernel(pmd, address);
|
||||
if (!pte)
|
||||
return -ENOMEM;
|
||||
|
||||
next = (address + PMD_SIZE) & PMD_MASK;
|
||||
if (remap_area_pte(pte, address, next,
|
||||
address + phys_addr, prot))
|
||||
return -ENOMEM;
|
||||
|
||||
address = next;
|
||||
pmd++;
|
||||
} while (address && (address < end));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remap_area_pud(pud_t *pud, unsigned long address,
|
||||
unsigned long end, unsigned long phys_addr,
|
||||
pgprot_t prot)
|
||||
{
|
||||
unsigned long next;
|
||||
|
||||
phys_addr -= address;
|
||||
|
||||
do {
|
||||
pmd_t *pmd = pmd_alloc(&init_mm, pud, address);
|
||||
if (!pmd)
|
||||
return -ENOMEM;
|
||||
next = (address + PUD_SIZE) & PUD_MASK;
|
||||
if (remap_area_pmd(pmd, address, next,
|
||||
phys_addr + address, prot))
|
||||
return -ENOMEM;
|
||||
|
||||
address = next;
|
||||
pud++;
|
||||
} while (address && address < end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remap_area_pages(unsigned long address, unsigned long phys_addr,
|
||||
size_t size, pgprot_t prot)
|
||||
{
|
||||
unsigned long end = address + size;
|
||||
unsigned long next;
|
||||
pgd_t *pgd;
|
||||
int err = 0;
|
||||
|
||||
phys_addr -= address;
|
||||
|
||||
pgd = pgd_offset_k(address);
|
||||
flush_cache_all();
|
||||
BUG_ON(address >= end);
|
||||
|
||||
spin_lock(&init_mm.page_table_lock);
|
||||
do {
|
||||
pud_t *pud = pud_alloc(&init_mm, pgd, address);
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!pud)
|
||||
break;
|
||||
|
||||
next = (address + PGDIR_SIZE) & PGDIR_MASK;
|
||||
if (next < address || next > end)
|
||||
next = end;
|
||||
err = remap_area_pud(pud, address, next,
|
||||
phys_addr + address, prot);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
address = next;
|
||||
pgd++;
|
||||
} while (address && (address < end));
|
||||
|
||||
spin_unlock(&init_mm.page_table_lock);
|
||||
flush_tlb_all();
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-map an arbitrary physical address space into the kernel virtual
|
||||
* address space. Needed when the kernel wants to access physical
|
||||
* memory directly.
|
||||
*/
|
||||
void __iomem *__ioremap(unsigned long phys_addr, size_t size,
|
||||
unsigned long flags)
|
||||
{
|
||||
void *addr;
|
||||
struct vm_struct *area;
|
||||
unsigned long offset, last_addr;
|
||||
pgprot_t prot;
|
||||
|
||||
/*
|
||||
* Check if we can simply use the P4 segment. This area is
|
||||
* uncacheable, so if caching/buffering is requested, we can't
|
||||
* use it.
|
||||
*/
|
||||
if ((phys_addr >= P4SEG) && (flags == 0))
|
||||
return (void __iomem *)phys_addr;
|
||||
|
||||
/* Don't allow wraparound or zero size */
|
||||
last_addr = phys_addr + size - 1;
|
||||
if (!size || last_addr < phys_addr)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* XXX: When mapping regular RAM, we'd better make damn sure
|
||||
* it's never used for anything else. But this is really the
|
||||
* caller's responsibility...
|
||||
*/
|
||||
if (PHYSADDR(P2SEGADDR(phys_addr)) == phys_addr)
|
||||
return (void __iomem *)P2SEGADDR(phys_addr);
|
||||
|
||||
/* Mappings have to be page-aligned */
|
||||
offset = phys_addr & ~PAGE_MASK;
|
||||
phys_addr &= PAGE_MASK;
|
||||
size = PAGE_ALIGN(last_addr + 1) - phys_addr;
|
||||
|
||||
prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY
|
||||
| _PAGE_ACCESSED | _PAGE_TYPE_SMALL | flags);
|
||||
|
||||
/*
|
||||
* Ok, go for it..
|
||||
*/
|
||||
area = get_vm_area(size, VM_IOREMAP);
|
||||
if (!area)
|
||||
return NULL;
|
||||
area->phys_addr = phys_addr;
|
||||
addr = area->addr;
|
||||
if (remap_area_pages((unsigned long)addr, phys_addr, size, prot)) {
|
||||
vunmap(addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (void __iomem *)(offset + (char *)addr);
|
||||
}
|
||||
EXPORT_SYMBOL(__ioremap);
|
||||
|
||||
void __iounmap(void __iomem *addr)
|
||||
{
|
||||
struct vm_struct *p;
|
||||
|
||||
if ((unsigned long)addr >= P4SEG)
|
||||
return;
|
||||
|
||||
p = remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr));
|
||||
if (unlikely(!p)) {
|
||||
printk (KERN_ERR "iounmap: bad address %p\n", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
kfree (p);
|
||||
}
|
||||
EXPORT_SYMBOL(__iounmap);
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* AVR32 TLB operations
|
||||
*
|
||||
* 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/mm.h>
|
||||
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
#define _TLBEHI_I 0x100
|
||||
|
||||
void show_dtlb_entry(unsigned int index)
|
||||
{
|
||||
unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
mmucr_save = sysreg_read(MMUCR);
|
||||
tlbehi_save = sysreg_read(TLBEHI);
|
||||
mmucr = mmucr_save & 0x13;
|
||||
mmucr |= index << 14;
|
||||
sysreg_write(MMUCR, mmucr);
|
||||
|
||||
asm volatile("tlbr" : : : "memory");
|
||||
cpu_sync_pipeline();
|
||||
|
||||
tlbehi = sysreg_read(TLBEHI);
|
||||
tlbelo = sysreg_read(TLBELO);
|
||||
|
||||
printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
|
||||
index,
|
||||
(tlbehi & 0x200)?'1':'0',
|
||||
(tlbelo & 0x100)?'1':'0',
|
||||
(tlbehi & 0xff),
|
||||
(tlbehi >> 12), (tlbelo >> 12),
|
||||
(tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
|
||||
(tlbelo & 0x200)?'1':'0',
|
||||
(tlbelo & 0x080)?'1':'0',
|
||||
(tlbelo & 0x001)?'1':'0',
|
||||
(tlbelo & 0x002)?'1':'0');
|
||||
|
||||
sysreg_write(MMUCR, mmucr_save);
|
||||
sysreg_write(TLBEHI, tlbehi_save);
|
||||
cpu_sync_pipeline();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void dump_dtlb(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
printk("ID V G ASID VPN PFN AP SZ C B W D\n");
|
||||
for (i = 0; i < 32; i++)
|
||||
show_dtlb_entry(i);
|
||||
}
|
||||
|
||||
static unsigned long last_mmucr;
|
||||
|
||||
static inline void set_replacement_pointer(unsigned shift)
|
||||
{
|
||||
unsigned long mmucr, mmucr_save;
|
||||
|
||||
mmucr = mmucr_save = sysreg_read(MMUCR);
|
||||
|
||||
/* Does this mapping already exist? */
|
||||
__asm__ __volatile__(
|
||||
" tlbs\n"
|
||||
" mfsr %0, %1"
|
||||
: "=r"(mmucr)
|
||||
: "i"(SYSREG_MMUCR));
|
||||
|
||||
if (mmucr & SYSREG_BIT(MMUCR_N)) {
|
||||
/* Not found -- pick a not-recently-accessed entry */
|
||||
unsigned long rp;
|
||||
unsigned long tlbar = sysreg_read(TLBARLO);
|
||||
|
||||
rp = 32 - fls(tlbar);
|
||||
if (rp == 32) {
|
||||
rp = 0;
|
||||
sysreg_write(TLBARLO, -1L);
|
||||
}
|
||||
|
||||
mmucr &= 0x13;
|
||||
mmucr |= (rp << shift);
|
||||
|
||||
sysreg_write(MMUCR, mmucr);
|
||||
}
|
||||
|
||||
last_mmucr = mmucr;
|
||||
}
|
||||
|
||||
static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid)
|
||||
{
|
||||
unsigned long vpn;
|
||||
|
||||
vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;
|
||||
sysreg_write(TLBEHI, vpn);
|
||||
cpu_sync_pipeline();
|
||||
|
||||
set_replacement_pointer(14);
|
||||
|
||||
sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
|
||||
|
||||
/* Let's go */
|
||||
asm volatile("nop\n\ttlbw" : : : "memory");
|
||||
cpu_sync_pipeline();
|
||||
}
|
||||
|
||||
void update_mmu_cache(struct vm_area_struct *vma,
|
||||
unsigned long address, pte_t pte)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* ptrace may call this routine */
|
||||
if (vma && current->active_mm != vma->vm_mm)
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
update_dtlb(address, pte, get_asid());
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void __flush_tlb_page(unsigned long asid, unsigned long page)
|
||||
{
|
||||
unsigned long mmucr, tlbehi;
|
||||
|
||||
page |= asid;
|
||||
sysreg_write(TLBEHI, page);
|
||||
cpu_sync_pipeline();
|
||||
asm volatile("tlbs");
|
||||
mmucr = sysreg_read(MMUCR);
|
||||
|
||||
if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
|
||||
unsigned long tlbarlo;
|
||||
unsigned long entry;
|
||||
|
||||
/* Clear the "valid" bit */
|
||||
tlbehi = sysreg_read(TLBEHI);
|
||||
tlbehi &= ~_TLBEHI_VALID;
|
||||
sysreg_write(TLBEHI, tlbehi);
|
||||
cpu_sync_pipeline();
|
||||
|
||||
/* mark the entry as "not accessed" */
|
||||
entry = (mmucr >> 14) & 0x3f;
|
||||
tlbarlo = sysreg_read(TLBARLO);
|
||||
tlbarlo |= (0x80000000 >> entry);
|
||||
sysreg_write(TLBARLO, tlbarlo);
|
||||
|
||||
/* update the entry with valid bit clear */
|
||||
asm volatile("tlbw");
|
||||
cpu_sync_pipeline();
|
||||
}
|
||||
}
|
||||
|
||||
void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
|
||||
{
|
||||
if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
|
||||
unsigned long flags, asid;
|
||||
unsigned long saved_asid = MMU_NO_ASID;
|
||||
|
||||
asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
|
||||
page &= PAGE_MASK;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (vma->vm_mm != current->mm) {
|
||||
saved_asid = get_asid();
|
||||
set_asid(asid);
|
||||
}
|
||||
|
||||
__flush_tlb_page(asid, page);
|
||||
|
||||
if (saved_asid != MMU_NO_ASID)
|
||||
set_asid(saved_asid);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
|
||||
if (mm->context != NO_CONTEXT) {
|
||||
unsigned long flags;
|
||||
int size;
|
||||
|
||||
local_irq_save(flags);
|
||||
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
|
||||
if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
|
||||
mm->context = NO_CONTEXT;
|
||||
if (mm == current->mm)
|
||||
activate_context(mm);
|
||||
} else {
|
||||
unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;
|
||||
unsigned long saved_asid = MMU_NO_ASID;
|
||||
|
||||
start &= PAGE_MASK;
|
||||
end += (PAGE_SIZE - 1);
|
||||
end &= PAGE_MASK;
|
||||
if (mm != current->mm) {
|
||||
saved_asid = get_asid();
|
||||
set_asid(asid);
|
||||
}
|
||||
|
||||
while (start < end) {
|
||||
__flush_tlb_page(asid, start);
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
if (saved_asid != MMU_NO_ASID)
|
||||
set_asid(saved_asid);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: If this is only called for addresses > TASK_SIZE, we can probably
|
||||
* skip the ASID stuff and just use the Global bit...
|
||||
*/
|
||||
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
unsigned long flags;
|
||||
int size;
|
||||
|
||||
local_irq_save(flags);
|
||||
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
|
||||
if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
|
||||
flush_tlb_all();
|
||||
} else {
|
||||
unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;
|
||||
unsigned long saved_asid = get_asid();
|
||||
|
||||
start &= PAGE_MASK;
|
||||
end += (PAGE_SIZE - 1);
|
||||
end &= PAGE_MASK;
|
||||
set_asid(asid);
|
||||
while (start < end) {
|
||||
__flush_tlb_page(asid, start);
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
set_asid(saved_asid);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void flush_tlb_mm(struct mm_struct *mm)
|
||||
{
|
||||
/* Invalidate all TLB entries of this process by getting a new ASID */
|
||||
if (mm->context != NO_CONTEXT) {
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
mm->context = NO_CONTEXT;
|
||||
if (mm == current->mm)
|
||||
activate_context(mm);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static void *tlb_start(struct seq_file *tlb, loff_t *pos)
|
||||
{
|
||||
static unsigned long tlb_index;
|
||||
|
||||
if (*pos >= 32)
|
||||
return NULL;
|
||||
|
||||
tlb_index = 0;
|
||||
return &tlb_index;
|
||||
}
|
||||
|
||||
static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
|
||||
{
|
||||
unsigned long *index = v;
|
||||
|
||||
if (*index >= 31)
|
||||
return NULL;
|
||||
|
||||
++*pos;
|
||||
++*index;
|
||||
return index;
|
||||
}
|
||||
|
||||
static void tlb_stop(struct seq_file *tlb, void *v)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int tlb_show(struct seq_file *tlb, void *v)
|
||||
{
|
||||
unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
|
||||
unsigned long *index = v;
|
||||
|
||||
if (*index == 0)
|
||||
seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n");
|
||||
|
||||
BUG_ON(*index >= 32);
|
||||
|
||||
local_irq_save(flags);
|
||||
mmucr_save = sysreg_read(MMUCR);
|
||||
tlbehi_save = sysreg_read(TLBEHI);
|
||||
mmucr = mmucr_save & 0x13;
|
||||
mmucr |= *index << 14;
|
||||
sysreg_write(MMUCR, mmucr);
|
||||
|
||||
asm volatile("tlbr" : : : "memory");
|
||||
cpu_sync_pipeline();
|
||||
|
||||
tlbehi = sysreg_read(TLBEHI);
|
||||
tlbelo = sysreg_read(TLBELO);
|
||||
|
||||
sysreg_write(MMUCR, mmucr_save);
|
||||
sysreg_write(TLBEHI, tlbehi_save);
|
||||
cpu_sync_pipeline();
|
||||
local_irq_restore(flags);
|
||||
|
||||
seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
|
||||
*index,
|
||||
(tlbehi & 0x200)?'1':'0',
|
||||
(tlbelo & 0x100)?'1':'0',
|
||||
(tlbehi & 0xff),
|
||||
(tlbehi >> 12), (tlbelo >> 12),
|
||||
(tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
|
||||
(tlbelo & 0x200)?'1':'0',
|
||||
(tlbelo & 0x080)?'1':'0',
|
||||
(tlbelo & 0x001)?'1':'0',
|
||||
(tlbelo & 0x002)?'1':'0');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations tlb_ops = {
|
||||
.start = tlb_start,
|
||||
.next = tlb_next,
|
||||
.stop = tlb_stop,
|
||||
.show = tlb_show,
|
||||
};
|
||||
|
||||
static int tlb_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &tlb_ops);
|
||||
}
|
||||
|
||||
static struct file_operations proc_tlb_operations = {
|
||||
.open = tlb_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static int __init proctlb_init(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
entry = create_proc_entry("tlb", 0, NULL);
|
||||
if (entry)
|
||||
entry->proc_fops = &proc_tlb_operations;
|
||||
return 0;
|
||||
}
|
||||
late_initcall(proctlb_init);
|
||||
#endif /* CONFIG_PROC_FS */
|
|
@ -27,7 +27,11 @@ config GENERIC_CALIBRATE_DELAY
|
|||
|
||||
config GENERIC_HARDIRQS
|
||||
bool
|
||||
default n
|
||||
default y
|
||||
|
||||
config GENERIC_HARDIRQS_NO__DO_IRQ
|
||||
bool
|
||||
default y
|
||||
|
||||
config GENERIC_TIME
|
||||
bool
|
||||
|
@ -251,6 +255,12 @@ config MB93091_NO_MB
|
|||
endchoice
|
||||
endif
|
||||
|
||||
config FUJITSU_MB93493
|
||||
bool "MB93493 Multimedia chip"
|
||||
help
|
||||
Select this option if the MB93493 multimedia chip is going to be
|
||||
used.
|
||||
|
||||
choice
|
||||
prompt "GP-Relative data support"
|
||||
default GPREL_DATA_8
|
||||
|
|
|
@ -10,15 +10,14 @@ extra-y:= head.o init_task.o vmlinux.lds
|
|||
obj-y := $(heads-y) entry.o entry-table.o break.o switch_to.o kernel_thread.o \
|
||||
process.o traps.o ptrace.o signal.o dma.o \
|
||||
sys_frv.o time.o semaphore.o setup.o frv_ksyms.o \
|
||||
debug-stub.o irq.o irq-routing.o sleep.o uaccess.o
|
||||
debug-stub.o irq.o sleep.o uaccess.o
|
||||
|
||||
obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-io.o
|
||||
|
||||
obj-$(CONFIG_MB93091_VDK) += irq-mb93091.o
|
||||
obj-$(CONFIG_MB93093_PDK) += irq-mb93093.o
|
||||
obj-$(CONFIG_FUJITSU_MB93493) += irq-mb93493.o
|
||||
obj-$(CONFIG_PM) += pm.o cmode.o
|
||||
obj-$(CONFIG_MB93093_PDK) += pm-mb93093.o
|
||||
obj-$(CONFIG_FUJITSU_MB93493) += irq-mb93493.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_FUTEX) += futex.o
|
||||
obj-$(CONFIG_MODULES) += module.o
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <asm/delay.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/irc-regs.h>
|
||||
#include <asm/irq-routing.h>
|
||||
|
||||
#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
|
||||
|
||||
|
@ -33,83 +32,131 @@
|
|||
#define __get_IFR() ({ __reg16(0xffc0000c); })
|
||||
#define __clr_IFR(M) do { __reg16(0xffc0000c) = ~(M); wmb(); } while(0)
|
||||
|
||||
static void frv_fpga_doirq(struct irq_source *source);
|
||||
static void frv_fpga_control(struct irq_group *group, int irq, int on);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* FPGA IRQ multiplexor
|
||||
* on-motherboard FPGA PIC operations
|
||||
*/
|
||||
static struct irq_source frv_fpga[4] = {
|
||||
#define __FPGA(X, M) \
|
||||
[X] = { \
|
||||
.muxname = "fpga."#X, \
|
||||
.irqmask = M, \
|
||||
.doirq = frv_fpga_doirq, \
|
||||
}
|
||||
|
||||
__FPGA(0, 0x0028),
|
||||
__FPGA(1, 0x0050),
|
||||
__FPGA(2, 0x1c00),
|
||||
__FPGA(3, 0x6386),
|
||||
};
|
||||
|
||||
static struct irq_group frv_fpga_irqs = {
|
||||
.first_irq = IRQ_BASE_FPGA,
|
||||
.control = frv_fpga_control,
|
||||
.sources = {
|
||||
[ 1] = &frv_fpga[3],
|
||||
[ 2] = &frv_fpga[3],
|
||||
[ 3] = &frv_fpga[0],
|
||||
[ 4] = &frv_fpga[1],
|
||||
[ 5] = &frv_fpga[0],
|
||||
[ 6] = &frv_fpga[1],
|
||||
[ 7] = &frv_fpga[3],
|
||||
[ 8] = &frv_fpga[3],
|
||||
[ 9] = &frv_fpga[3],
|
||||
[10] = &frv_fpga[2],
|
||||
[11] = &frv_fpga[2],
|
||||
[12] = &frv_fpga[2],
|
||||
[13] = &frv_fpga[3],
|
||||
[14] = &frv_fpga[3],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static void frv_fpga_control(struct irq_group *group, int index, int on)
|
||||
static void frv_fpga_mask(unsigned int irq)
|
||||
{
|
||||
uint16_t imr = __get_IMR();
|
||||
|
||||
if (on)
|
||||
imr &= ~(1 << index);
|
||||
else
|
||||
imr |= 1 << index;
|
||||
imr |= 1 << (irq - IRQ_BASE_FPGA);
|
||||
|
||||
__set_IMR(imr);
|
||||
}
|
||||
|
||||
static void frv_fpga_doirq(struct irq_source *source)
|
||||
static void frv_fpga_ack(unsigned int irq)
|
||||
{
|
||||
uint16_t mask, imr;
|
||||
|
||||
imr = __get_IMR();
|
||||
mask = source->irqmask & ~imr & __get_IFR();
|
||||
if (mask) {
|
||||
__set_IMR(imr | mask);
|
||||
__clr_IFR(mask);
|
||||
distribute_irqs(&frv_fpga_irqs, mask);
|
||||
__set_IMR(imr);
|
||||
}
|
||||
__clr_IFR(1 << (irq - IRQ_BASE_FPGA));
|
||||
}
|
||||
|
||||
static void frv_fpga_mask_ack(unsigned int irq)
|
||||
{
|
||||
uint16_t imr = __get_IMR();
|
||||
|
||||
imr |= 1 << (irq - IRQ_BASE_FPGA);
|
||||
__set_IMR(imr);
|
||||
|
||||
__clr_IFR(1 << (irq - IRQ_BASE_FPGA));
|
||||
}
|
||||
|
||||
static void frv_fpga_unmask(unsigned int irq)
|
||||
{
|
||||
uint16_t imr = __get_IMR();
|
||||
|
||||
imr &= ~(1 << (irq - IRQ_BASE_FPGA));
|
||||
|
||||
__set_IMR(imr);
|
||||
}
|
||||
|
||||
static struct irq_chip frv_fpga_pic = {
|
||||
.name = "mb93091",
|
||||
.ack = frv_fpga_ack,
|
||||
.mask = frv_fpga_mask,
|
||||
.mask_ack = frv_fpga_mask_ack,
|
||||
.unmask = frv_fpga_unmask,
|
||||
};
|
||||
|
||||
/*
|
||||
* FPGA PIC interrupt handler
|
||||
*/
|
||||
static irqreturn_t fpga_interrupt(int irq, void *_mask, struct pt_regs *regs)
|
||||
{
|
||||
uint16_t imr, mask = (unsigned long) _mask;
|
||||
|
||||
imr = __get_IMR();
|
||||
mask = mask & ~imr & __get_IFR();
|
||||
|
||||
/* poll all the triggered IRQs */
|
||||
while (mask) {
|
||||
int irq;
|
||||
|
||||
asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask));
|
||||
irq = 31 - irq;
|
||||
mask &= ~(1 << irq);
|
||||
|
||||
generic_handle_irq(IRQ_BASE_FPGA + irq, regs);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* define an interrupt action for each FPGA PIC output
|
||||
* - use dev_id to indicate the FPGA PIC input to output mappings
|
||||
*/
|
||||
static struct irqaction fpga_irq[4] = {
|
||||
[0] = {
|
||||
.handler = fpga_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_SHARED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "fpga.0",
|
||||
.dev_id = (void *) 0x0028UL,
|
||||
},
|
||||
[1] = {
|
||||
.handler = fpga_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_SHARED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "fpga.1",
|
||||
.dev_id = (void *) 0x0050UL,
|
||||
},
|
||||
[2] = {
|
||||
.handler = fpga_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_SHARED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "fpga.2",
|
||||
.dev_id = (void *) 0x1c00UL,
|
||||
},
|
||||
[3] = {
|
||||
.handler = fpga_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_SHARED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "fpga.3",
|
||||
.dev_id = (void *) 0x6386UL,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* initialise the motherboard FPGA's PIC
|
||||
*/
|
||||
void __init fpga_init(void)
|
||||
{
|
||||
int irq;
|
||||
|
||||
/* all PIC inputs are all set to be low-level driven, apart from the
|
||||
* NMI button (15) which is fixed at falling-edge
|
||||
*/
|
||||
__set_IMR(0x7ffe);
|
||||
__clr_IFR(0x0000);
|
||||
|
||||
frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL0);
|
||||
frv_irq_route_external(&frv_fpga[1], IRQ_CPU_EXTERNAL1);
|
||||
frv_irq_route_external(&frv_fpga[2], IRQ_CPU_EXTERNAL2);
|
||||
frv_irq_route_external(&frv_fpga[3], IRQ_CPU_EXTERNAL3);
|
||||
frv_irq_set_group(&frv_fpga_irqs);
|
||||
for (irq = IRQ_BASE_FPGA + 1; irq <= IRQ_BASE_FPGA + 14; irq++)
|
||||
set_irq_chip_and_handler(irq, &frv_fpga_pic, handle_level_irq);
|
||||
|
||||
set_irq_chip_and_handler(IRQ_FPGA_NMI, &frv_fpga_pic, handle_edge_irq);
|
||||
|
||||
/* the FPGA drives the first four external IRQ inputs on the CPU PIC */
|
||||
setup_irq(IRQ_CPU_EXTERNAL0, &fpga_irq[0]);
|
||||
setup_irq(IRQ_CPU_EXTERNAL1, &fpga_irq[1]);
|
||||
setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[2]);
|
||||
setup_irq(IRQ_CPU_EXTERNAL3, &fpga_irq[3]);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* irq-mb93093.c: MB93093 FPGA interrupt handling
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -24,7 +24,6 @@
|
|||
#include <asm/delay.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/irc-regs.h>
|
||||
#include <asm/irq-routing.h>
|
||||
|
||||
#define __reg16(ADDR) (*(volatile unsigned short *)(__region_CS2 + (ADDR)))
|
||||
|
||||
|
@ -33,66 +32,102 @@
|
|||
#define __get_IFR() ({ __reg16(0x02); })
|
||||
#define __clr_IFR(M) do { __reg16(0x02) = ~(M); wmb(); } while(0)
|
||||
|
||||
static void frv_fpga_doirq(struct irq_source *source);
|
||||
static void frv_fpga_control(struct irq_group *group, int irq, int on);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* FPGA IRQ multiplexor
|
||||
* off-CPU FPGA PIC operations
|
||||
*/
|
||||
static struct irq_source frv_fpga[4] = {
|
||||
#define __FPGA(X, M) \
|
||||
[X] = { \
|
||||
.muxname = "fpga."#X, \
|
||||
.irqmask = M, \
|
||||
.doirq = frv_fpga_doirq, \
|
||||
}
|
||||
|
||||
__FPGA(0, 0x0700),
|
||||
};
|
||||
|
||||
static struct irq_group frv_fpga_irqs = {
|
||||
.first_irq = IRQ_BASE_FPGA,
|
||||
.control = frv_fpga_control,
|
||||
.sources = {
|
||||
[ 8] = &frv_fpga[0],
|
||||
[ 9] = &frv_fpga[0],
|
||||
[10] = &frv_fpga[0],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static void frv_fpga_control(struct irq_group *group, int index, int on)
|
||||
static void frv_fpga_mask(unsigned int irq)
|
||||
{
|
||||
uint16_t imr = __get_IMR();
|
||||
|
||||
if (on)
|
||||
imr &= ~(1 << index);
|
||||
else
|
||||
imr |= 1 << index;
|
||||
imr |= 1 << (irq - IRQ_BASE_FPGA);
|
||||
__set_IMR(imr);
|
||||
}
|
||||
|
||||
static void frv_fpga_ack(unsigned int irq)
|
||||
{
|
||||
__clr_IFR(1 << (irq - IRQ_BASE_FPGA));
|
||||
}
|
||||
|
||||
static void frv_fpga_mask_ack(unsigned int irq)
|
||||
{
|
||||
uint16_t imr = __get_IMR();
|
||||
|
||||
imr |= 1 << (irq - IRQ_BASE_FPGA);
|
||||
__set_IMR(imr);
|
||||
|
||||
__clr_IFR(1 << (irq - IRQ_BASE_FPGA));
|
||||
}
|
||||
|
||||
static void frv_fpga_unmask(unsigned int irq)
|
||||
{
|
||||
uint16_t imr = __get_IMR();
|
||||
|
||||
imr &= ~(1 << (irq - IRQ_BASE_FPGA));
|
||||
|
||||
__set_IMR(imr);
|
||||
}
|
||||
|
||||
static void frv_fpga_doirq(struct irq_source *source)
|
||||
static struct irq_chip frv_fpga_pic = {
|
||||
.name = "mb93093",
|
||||
.ack = frv_fpga_ack,
|
||||
.mask = frv_fpga_mask,
|
||||
.mask_ack = frv_fpga_mask_ack,
|
||||
.unmask = frv_fpga_unmask,
|
||||
.end = frv_fpga_end,
|
||||
};
|
||||
|
||||
/*
|
||||
* FPGA PIC interrupt handler
|
||||
*/
|
||||
static irqreturn_t fpga_interrupt(int irq, void *_mask, struct pt_regs *regs)
|
||||
{
|
||||
uint16_t mask, imr;
|
||||
uint16_t imr, mask = (unsigned long) _mask;
|
||||
|
||||
imr = __get_IMR();
|
||||
mask = source->irqmask & ~imr & __get_IFR();
|
||||
if (mask) {
|
||||
__set_IMR(imr | mask);
|
||||
__clr_IFR(mask);
|
||||
distribute_irqs(&frv_fpga_irqs, mask);
|
||||
__set_IMR(imr);
|
||||
mask = mask & ~imr & __get_IFR();
|
||||
|
||||
/* poll all the triggered IRQs */
|
||||
while (mask) {
|
||||
int irq;
|
||||
|
||||
asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask));
|
||||
irq = 31 - irq;
|
||||
mask &= ~(1 << irq);
|
||||
|
||||
generic_irq_handle(IRQ_BASE_FPGA + irq, regs);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* define an interrupt action for each FPGA PIC output
|
||||
* - use dev_id to indicate the FPGA PIC input to output mappings
|
||||
*/
|
||||
static struct irqaction fpga_irq[1] = {
|
||||
[0] = {
|
||||
.handler = fpga_interrupt,
|
||||
.flags = IRQF_DISABLED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "fpga.0",
|
||||
.dev_id = (void *) 0x0700UL,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* initialise the motherboard FPGA's PIC
|
||||
*/
|
||||
void __init fpga_init(void)
|
||||
{
|
||||
int irq;
|
||||
|
||||
/* all PIC inputs are all set to be edge triggered */
|
||||
__set_IMR(0x0700);
|
||||
__clr_IFR(0x0000);
|
||||
|
||||
frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL2);
|
||||
frv_irq_set_group(&frv_fpga_irqs);
|
||||
for (irq = IRQ_BASE_FPGA + 8; irq <= IRQ_BASE_FPGA + 10; irq++)
|
||||
set_irq_chip_and_handler(irq, &frv_fpga_pic, handle_edge_irq);
|
||||
|
||||
/* the FPGA drives external IRQ input #2 on the CPU PIC */
|
||||
setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[0]);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* irq-mb93493.c: MB93493 companion chip interrupt handler
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -24,84 +24,126 @@
|
|||
#include <asm/delay.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/irc-regs.h>
|
||||
#include <asm/irq-routing.h>
|
||||
#include <asm/mb93493-irqs.h>
|
||||
#include <asm/mb93493-regs.h>
|
||||
|
||||
static void frv_mb93493_doirq(struct irq_source *source);
|
||||
#define IRQ_ROUTE_ONE(X) (X##_ROUTE << (X - IRQ_BASE_MB93493))
|
||||
|
||||
#define IRQ_ROUTING \
|
||||
(IRQ_ROUTE_ONE(IRQ_MB93493_VDC) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_VCC) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_OUT) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_I2C_0) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_I2C_1) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_USB) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_LOCAL_BUS) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_PCMCIA) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_GPIO) | \
|
||||
IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_IN))
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* MB93493 companion chip IRQ multiplexor
|
||||
* daughter board PIC operations
|
||||
* - there is no way to ACK interrupts in the MB93493 chip
|
||||
*/
|
||||
static struct irq_source frv_mb93493[2] = {
|
||||
[0] = {
|
||||
.muxname = "mb93493.0",
|
||||
.muxdata = __region_CS3 + 0x3d0,
|
||||
.doirq = frv_mb93493_doirq,
|
||||
.irqmask = 0x0000,
|
||||
},
|
||||
[1] = {
|
||||
.muxname = "mb93493.1",
|
||||
.muxdata = __region_CS3 + 0x3d4,
|
||||
.doirq = frv_mb93493_doirq,
|
||||
.irqmask = 0x0000,
|
||||
},
|
||||
static void frv_mb93493_mask(unsigned int irq)
|
||||
{
|
||||
uint32_t iqsr;
|
||||
volatile void *piqsr;
|
||||
|
||||
if (IRQ_ROUTING & (1 << (irq - IRQ_BASE_MB93493)))
|
||||
piqsr = __addr_MB93493_IQSR(1);
|
||||
else
|
||||
piqsr = __addr_MB93493_IQSR(0);
|
||||
|
||||
iqsr = readl(piqsr);
|
||||
iqsr &= ~(1 << (irq - IRQ_BASE_MB93493 + 16));
|
||||
writel(iqsr, piqsr);
|
||||
}
|
||||
|
||||
static void frv_mb93493_ack(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static void frv_mb93493_unmask(unsigned int irq)
|
||||
{
|
||||
uint32_t iqsr;
|
||||
volatile void *piqsr;
|
||||
|
||||
if (IRQ_ROUTING & (1 << (irq - IRQ_BASE_MB93493)))
|
||||
piqsr = __addr_MB93493_IQSR(1);
|
||||
else
|
||||
piqsr = __addr_MB93493_IQSR(0);
|
||||
|
||||
iqsr = readl(piqsr);
|
||||
iqsr |= 1 << (irq - IRQ_BASE_MB93493 + 16);
|
||||
writel(iqsr, piqsr);
|
||||
}
|
||||
|
||||
static struct irq_chip frv_mb93493_pic = {
|
||||
.name = "mb93093",
|
||||
.ack = frv_mb93493_ack,
|
||||
.mask = frv_mb93493_mask,
|
||||
.mask_ack = frv_mb93493_mask,
|
||||
.unmask = frv_mb93493_unmask,
|
||||
};
|
||||
|
||||
static void frv_mb93493_control(struct irq_group *group, int index, int on)
|
||||
/*
|
||||
* MB93493 PIC interrupt handler
|
||||
*/
|
||||
static irqreturn_t mb93493_interrupt(int irq, void *_piqsr, struct pt_regs *regs)
|
||||
{
|
||||
struct irq_source *source;
|
||||
volatile void *piqsr = _piqsr;
|
||||
uint32_t iqsr;
|
||||
|
||||
if ((frv_mb93493[0].irqmask & (1 << index)))
|
||||
source = &frv_mb93493[0];
|
||||
else
|
||||
source = &frv_mb93493[1];
|
||||
iqsr = readl(piqsr);
|
||||
iqsr = iqsr & (iqsr >> 16) & 0xffff;
|
||||
|
||||
iqsr = readl(source->muxdata);
|
||||
if (on)
|
||||
iqsr |= 1 << (index + 16);
|
||||
else
|
||||
iqsr &= ~(1 << (index + 16));
|
||||
/* poll all the triggered IRQs */
|
||||
while (iqsr) {
|
||||
int irq;
|
||||
|
||||
writel(iqsr, source->muxdata);
|
||||
asm("scan %1,gr0,%0" : "=r"(irq) : "r"(iqsr));
|
||||
irq = 31 - irq;
|
||||
iqsr &= ~(1 << irq);
|
||||
|
||||
generic_handle_irq(IRQ_BASE_MB93493 + irq, regs);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irq_group frv_mb93493_irqs = {
|
||||
.first_irq = IRQ_BASE_MB93493,
|
||||
.control = frv_mb93493_control,
|
||||
/*
|
||||
* define an interrupt action for each MB93493 PIC output
|
||||
* - use dev_id to indicate the MB93493 PIC input to output mappings
|
||||
*/
|
||||
static struct irqaction mb93493_irq[2] = {
|
||||
[0] = {
|
||||
.handler = mb93493_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_SHARED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "mb93493.0",
|
||||
.dev_id = (void *) __addr_MB93493_IQSR(0),
|
||||
},
|
||||
[1] = {
|
||||
.handler = mb93493_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_SHARED,
|
||||
.mask = CPU_MASK_NONE,
|
||||
.name = "mb93493.1",
|
||||
.dev_id = (void *) __addr_MB93493_IQSR(1),
|
||||
}
|
||||
};
|
||||
|
||||
static void frv_mb93493_doirq(struct irq_source *source)
|
||||
/*
|
||||
* initialise the motherboard MB93493's PIC
|
||||
*/
|
||||
void __init mb93493_init(void)
|
||||
{
|
||||
uint32_t mask = readl(source->muxdata);
|
||||
mask = mask & (mask >> 16) & 0xffff;
|
||||
int irq;
|
||||
|
||||
if (mask)
|
||||
distribute_irqs(&frv_mb93493_irqs, mask);
|
||||
}
|
||||
|
||||
static void __init mb93493_irq_route(int irq, int source)
|
||||
{
|
||||
frv_mb93493[source].irqmask |= 1 << (irq - IRQ_BASE_MB93493);
|
||||
frv_mb93493_irqs.sources[irq - IRQ_BASE_MB93493] = &frv_mb93493[source];
|
||||
}
|
||||
|
||||
void __init route_mb93493_irqs(void)
|
||||
{
|
||||
frv_irq_route_external(&frv_mb93493[0], IRQ_CPU_MB93493_0);
|
||||
frv_irq_route_external(&frv_mb93493[1], IRQ_CPU_MB93493_1);
|
||||
|
||||
frv_irq_set_group(&frv_mb93493_irqs);
|
||||
|
||||
mb93493_irq_route(IRQ_MB93493_VDC, IRQ_MB93493_VDC_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_VCC, IRQ_MB93493_VCC_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_AUDIO_IN, IRQ_MB93493_AUDIO_IN_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_I2C_0, IRQ_MB93493_I2C_0_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_I2C_1, IRQ_MB93493_I2C_1_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_USB, IRQ_MB93493_USB_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_LOCAL_BUS, IRQ_MB93493_LOCAL_BUS_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_PCMCIA, IRQ_MB93493_PCMCIA_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_GPIO, IRQ_MB93493_GPIO_ROUTE);
|
||||
mb93493_irq_route(IRQ_MB93493_AUDIO_OUT, IRQ_MB93493_AUDIO_OUT_ROUTE);
|
||||
for (irq = IRQ_BASE_MB93493 + 0; irq <= IRQ_BASE_MB93493 + 10; irq++)
|
||||
set_irq_chip_and_handler(irq, &frv_mb93493_pic, handle_edge_irq);
|
||||
|
||||
/* the MB93493 drives external IRQ inputs on the CPU PIC */
|
||||
setup_irq(IRQ_CPU_MB93493_0, &mb93493_irq[0]);
|
||||
setup_irq(IRQ_CPU_MB93493_1, &mb93493_irq[1]);
|
||||
}
|
||||
|
|
|
@ -1,291 +0,0 @@
|
|||
/* irq-routing.c: IRQ routing
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq-routing.h>
|
||||
#include <asm/irc-regs.h>
|
||||
#include <asm/serial-regs.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
struct irq_level frv_irq_levels[16] = {
|
||||
[0 ... 15] = {
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
}
|
||||
};
|
||||
|
||||
struct irq_group *irq_groups[NR_IRQ_GROUPS];
|
||||
|
||||
extern struct irq_group frv_cpu_irqs;
|
||||
|
||||
void __init frv_irq_route(struct irq_source *source, int irqlevel)
|
||||
{
|
||||
source->level = &frv_irq_levels[irqlevel];
|
||||
source->next = frv_irq_levels[irqlevel].sources;
|
||||
frv_irq_levels[irqlevel].sources = source;
|
||||
}
|
||||
|
||||
void __init frv_irq_route_external(struct irq_source *source, int irq)
|
||||
{
|
||||
int irqlevel = 0;
|
||||
|
||||
switch (irq) {
|
||||
case IRQ_CPU_EXTERNAL0: irqlevel = IRQ_XIRQ0_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL1: irqlevel = IRQ_XIRQ1_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL2: irqlevel = IRQ_XIRQ2_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL3: irqlevel = IRQ_XIRQ3_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL4: irqlevel = IRQ_XIRQ4_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL5: irqlevel = IRQ_XIRQ5_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL6: irqlevel = IRQ_XIRQ6_LEVEL; break;
|
||||
case IRQ_CPU_EXTERNAL7: irqlevel = IRQ_XIRQ7_LEVEL; break;
|
||||
default: BUG();
|
||||
}
|
||||
|
||||
source->level = &frv_irq_levels[irqlevel];
|
||||
source->next = frv_irq_levels[irqlevel].sources;
|
||||
frv_irq_levels[irqlevel].sources = source;
|
||||
}
|
||||
|
||||
void __init frv_irq_set_group(struct irq_group *group)
|
||||
{
|
||||
irq_groups[group->first_irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP] = group;
|
||||
}
|
||||
|
||||
void distribute_irqs(struct irq_group *group, unsigned long irqmask)
|
||||
{
|
||||
struct irqaction *action;
|
||||
int irq;
|
||||
|
||||
while (irqmask) {
|
||||
asm("scan %1,gr0,%0" : "=r"(irq) : "r"(irqmask));
|
||||
if (irq < 0 || irq > 31)
|
||||
asm volatile("break");
|
||||
irq = 31 - irq;
|
||||
|
||||
irqmask &= ~(1 << irq);
|
||||
action = group->actions[irq];
|
||||
|
||||
irq += group->first_irq;
|
||||
|
||||
if (action) {
|
||||
int status = 0;
|
||||
|
||||
// if (!(action->flags & IRQF_DISABLED))
|
||||
// local_irq_enable();
|
||||
|
||||
do {
|
||||
status |= action->flags;
|
||||
action->handler(irq, action->dev_id, __frame);
|
||||
action = action->next;
|
||||
} while (action);
|
||||
|
||||
if (status & IRQF_SAMPLE_RANDOM)
|
||||
add_interrupt_randomness(irq);
|
||||
local_irq_disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* CPU UART interrupts
|
||||
*/
|
||||
static void frv_cpuuart_doirq(struct irq_source *source)
|
||||
{
|
||||
// uint8_t iir = readb(source->muxdata + UART_IIR * 8);
|
||||
// if ((iir & 0x0f) != UART_IIR_NO_INT)
|
||||
distribute_irqs(&frv_cpu_irqs, source->irqmask);
|
||||
}
|
||||
|
||||
struct irq_source frv_cpuuart[2] = {
|
||||
#define __CPUUART(X, A) \
|
||||
[X] = { \
|
||||
.muxname = "uart", \
|
||||
.muxdata = (volatile void __iomem *)(unsigned long)A,\
|
||||
.irqmask = 1 << IRQ_CPU_UART##X, \
|
||||
.doirq = frv_cpuuart_doirq, \
|
||||
}
|
||||
|
||||
__CPUUART(0, UART0_BASE),
|
||||
__CPUUART(1, UART1_BASE),
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* CPU DMA interrupts
|
||||
*/
|
||||
static void frv_cpudma_doirq(struct irq_source *source)
|
||||
{
|
||||
uint32_t cstr = readl(source->muxdata + DMAC_CSTRx);
|
||||
if (cstr & DMAC_CSTRx_INT)
|
||||
distribute_irqs(&frv_cpu_irqs, source->irqmask);
|
||||
}
|
||||
|
||||
struct irq_source frv_cpudma[8] = {
|
||||
#define __CPUDMA(X, A) \
|
||||
[X] = { \
|
||||
.muxname = "dma", \
|
||||
.muxdata = (volatile void __iomem *)(unsigned long)A,\
|
||||
.irqmask = 1 << IRQ_CPU_DMA##X, \
|
||||
.doirq = frv_cpudma_doirq, \
|
||||
}
|
||||
|
||||
__CPUDMA(0, 0xfe000900),
|
||||
__CPUDMA(1, 0xfe000980),
|
||||
__CPUDMA(2, 0xfe000a00),
|
||||
__CPUDMA(3, 0xfe000a80),
|
||||
__CPUDMA(4, 0xfe001000),
|
||||
__CPUDMA(5, 0xfe001080),
|
||||
__CPUDMA(6, 0xfe001100),
|
||||
__CPUDMA(7, 0xfe001180),
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* CPU timer interrupts - can't tell whether they've generated an interrupt or not
|
||||
*/
|
||||
static void frv_cputimer_doirq(struct irq_source *source)
|
||||
{
|
||||
distribute_irqs(&frv_cpu_irqs, source->irqmask);
|
||||
}
|
||||
|
||||
struct irq_source frv_cputimer[3] = {
|
||||
#define __CPUTIMER(X) \
|
||||
[X] = { \
|
||||
.muxname = "timer", \
|
||||
.muxdata = NULL, \
|
||||
.irqmask = 1 << IRQ_CPU_TIMER##X, \
|
||||
.doirq = frv_cputimer_doirq, \
|
||||
}
|
||||
|
||||
__CPUTIMER(0),
|
||||
__CPUTIMER(1),
|
||||
__CPUTIMER(2),
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* external CPU interrupts - can't tell directly whether they've generated an interrupt or not
|
||||
*/
|
||||
static void frv_cpuexternal_doirq(struct irq_source *source)
|
||||
{
|
||||
distribute_irqs(&frv_cpu_irqs, source->irqmask);
|
||||
}
|
||||
|
||||
struct irq_source frv_cpuexternal[8] = {
|
||||
#define __CPUEXTERNAL(X) \
|
||||
[X] = { \
|
||||
.muxname = "ext", \
|
||||
.muxdata = NULL, \
|
||||
.irqmask = 1 << IRQ_CPU_EXTERNAL##X, \
|
||||
.doirq = frv_cpuexternal_doirq, \
|
||||
}
|
||||
|
||||
__CPUEXTERNAL(0),
|
||||
__CPUEXTERNAL(1),
|
||||
__CPUEXTERNAL(2),
|
||||
__CPUEXTERNAL(3),
|
||||
__CPUEXTERNAL(4),
|
||||
__CPUEXTERNAL(5),
|
||||
__CPUEXTERNAL(6),
|
||||
__CPUEXTERNAL(7),
|
||||
};
|
||||
|
||||
#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
|
||||
|
||||
struct irq_group frv_cpu_irqs = {
|
||||
.sources = {
|
||||
[IRQ_CPU_UART0] = &frv_cpuuart[0],
|
||||
[IRQ_CPU_UART1] = &frv_cpuuart[1],
|
||||
[IRQ_CPU_TIMER0] = &frv_cputimer[0],
|
||||
[IRQ_CPU_TIMER1] = &frv_cputimer[1],
|
||||
[IRQ_CPU_TIMER2] = &frv_cputimer[2],
|
||||
[IRQ_CPU_DMA0] = &frv_cpudma[0],
|
||||
[IRQ_CPU_DMA1] = &frv_cpudma[1],
|
||||
[IRQ_CPU_DMA2] = &frv_cpudma[2],
|
||||
[IRQ_CPU_DMA3] = &frv_cpudma[3],
|
||||
[IRQ_CPU_DMA4] = &frv_cpudma[4],
|
||||
[IRQ_CPU_DMA5] = &frv_cpudma[5],
|
||||
[IRQ_CPU_DMA6] = &frv_cpudma[6],
|
||||
[IRQ_CPU_DMA7] = &frv_cpudma[7],
|
||||
[IRQ_CPU_EXTERNAL0] = &frv_cpuexternal[0],
|
||||
[IRQ_CPU_EXTERNAL1] = &frv_cpuexternal[1],
|
||||
[IRQ_CPU_EXTERNAL2] = &frv_cpuexternal[2],
|
||||
[IRQ_CPU_EXTERNAL3] = &frv_cpuexternal[3],
|
||||
[IRQ_CPU_EXTERNAL4] = &frv_cpuexternal[4],
|
||||
[IRQ_CPU_EXTERNAL5] = &frv_cpuexternal[5],
|
||||
[IRQ_CPU_EXTERNAL6] = &frv_cpuexternal[6],
|
||||
[IRQ_CPU_EXTERNAL7] = &frv_cpuexternal[7],
|
||||
},
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* route the CPU's interrupt sources
|
||||
*/
|
||||
void __init route_cpu_irqs(void)
|
||||
{
|
||||
frv_irq_set_group(&frv_cpu_irqs);
|
||||
|
||||
__set_IITMR(0, 0x003f0000); /* DMA0-3, TIMER0-2 IRQ detect levels */
|
||||
__set_IITMR(1, 0x20000000); /* ERR0-1, UART0-1, DMA4-7 IRQ detect levels */
|
||||
|
||||
/* route UART and error interrupts */
|
||||
frv_irq_route(&frv_cpuuart[0], IRQ_UART0_LEVEL);
|
||||
frv_irq_route(&frv_cpuuart[1], IRQ_UART1_LEVEL);
|
||||
|
||||
set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL, IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
|
||||
|
||||
/* route DMA channel interrupts */
|
||||
frv_irq_route(&frv_cpudma[0], IRQ_DMA0_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[1], IRQ_DMA1_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[2], IRQ_DMA2_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[3], IRQ_DMA3_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[4], IRQ_DMA4_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[5], IRQ_DMA5_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[6], IRQ_DMA6_LEVEL);
|
||||
frv_irq_route(&frv_cpudma[7], IRQ_DMA7_LEVEL);
|
||||
|
||||
set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL, IRQ_DMA0_LEVEL);
|
||||
set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL, IRQ_DMA4_LEVEL);
|
||||
|
||||
/* route timer interrupts */
|
||||
frv_irq_route(&frv_cputimer[0], IRQ_TIMER0_LEVEL);
|
||||
frv_irq_route(&frv_cputimer[1], IRQ_TIMER1_LEVEL);
|
||||
frv_irq_route(&frv_cputimer[2], IRQ_TIMER2_LEVEL);
|
||||
|
||||
set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
|
||||
|
||||
/* route external interrupts */
|
||||
frv_irq_route(&frv_cpuexternal[0], IRQ_XIRQ0_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[1], IRQ_XIRQ1_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[2], IRQ_XIRQ2_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[3], IRQ_XIRQ3_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[4], IRQ_XIRQ4_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[5], IRQ_XIRQ5_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[6], IRQ_XIRQ6_LEVEL);
|
||||
frv_irq_route(&frv_cpuexternal[7], IRQ_XIRQ7_LEVEL);
|
||||
|
||||
set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL, IRQ_XIRQ4_LEVEL);
|
||||
set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL, IRQ_XIRQ0_LEVEL);
|
||||
|
||||
#if defined(CONFIG_MB93091_VDK)
|
||||
__set_TM1(0x55550000); /* XIRQ7-0 all active low */
|
||||
#elif defined(CONFIG_MB93093_PDK)
|
||||
__set_TM1(0x15550000); /* XIRQ7 active high, 6-0 all active low */
|
||||
#else
|
||||
#error dont know external IRQ trigger levels for this setup
|
||||
#endif
|
||||
|
||||
} /* end route_cpu_irqs() */
|
|
@ -1,6 +1,6 @@
|
|||
/* irq.c: FRV IRQ handling
|
||||
*
|
||||
* Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -9,13 +9,6 @@
|
|||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* (mostly architecture independent, will move to kernel/irq.c in 2.5.)
|
||||
*
|
||||
* IRQs 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/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/signal.h>
|
||||
|
@ -43,19 +36,16 @@
|
|||
#include <asm/delay.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/irc-regs.h>
|
||||
#include <asm/irq-routing.h>
|
||||
#include <asm/gdb-stub.h>
|
||||
|
||||
#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
|
||||
|
||||
extern void __init fpga_init(void);
|
||||
extern void __init route_mb93493_irqs(void);
|
||||
#ifdef CONFIG_FUJITSU_MB93493
|
||||
extern void __init mb93493_init(void);
|
||||
#endif
|
||||
|
||||
static void register_irq_proc (unsigned int irq);
|
||||
|
||||
/*
|
||||
* Special irq handlers.
|
||||
*/
|
||||
|
||||
irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) { return IRQ_HANDLED; }
|
||||
#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
|
||||
|
||||
atomic_t irq_err_count;
|
||||
|
||||
|
@ -64,215 +54,86 @@ atomic_t irq_err_count;
|
|||
*/
|
||||
int show_interrupts(struct seq_file *p, void *v)
|
||||
{
|
||||
struct irqaction *action;
|
||||
struct irq_group *group;
|
||||
int i = *(loff_t *) v, cpu;
|
||||
struct irqaction * action;
|
||||
unsigned long flags;
|
||||
int level, grp, ix, i, j;
|
||||
|
||||
i = *(loff_t *) v;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
seq_printf(p, " ");
|
||||
for_each_online_cpu(j)
|
||||
seq_printf(p, "CPU%d ",j);
|
||||
if (i == 0) {
|
||||
char cpuname[12];
|
||||
|
||||
seq_printf(p, " ");
|
||||
for_each_present_cpu(cpu) {
|
||||
sprintf(cpuname, "CPU%d", cpu);
|
||||
seq_printf(p, " %10s", cpuname);
|
||||
}
|
||||
seq_putc(p, '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
case 1 ... NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP:
|
||||
local_irq_save(flags);
|
||||
if (i < NR_IRQS) {
|
||||
spin_lock_irqsave(&irq_desc[i].lock, flags);
|
||||
action = irq_desc[i].action;
|
||||
if (action) {
|
||||
seq_printf(p, "%3d: ", i);
|
||||
for_each_present_cpu(cpu)
|
||||
seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
|
||||
seq_printf(p, " %10s", irq_desc[i].chip->name ? : "-");
|
||||
seq_printf(p, " %s", action->name);
|
||||
for (action = action->next;
|
||||
action;
|
||||
action = action->next)
|
||||
seq_printf(p, ", %s", action->name);
|
||||
|
||||
grp = (i - 1) / NR_IRQ_ACTIONS_PER_GROUP;
|
||||
group = irq_groups[grp];
|
||||
if (!group)
|
||||
goto skip;
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
ix = (i - 1) % NR_IRQ_ACTIONS_PER_GROUP;
|
||||
action = group->actions[ix];
|
||||
if (!action)
|
||||
goto skip;
|
||||
|
||||
seq_printf(p, "%3d: ", i - 1);
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
seq_printf(p, "%10u ", kstat_irqs(i));
|
||||
#else
|
||||
for_each_online_cpu(j)
|
||||
seq_printf(p, "%10u ", kstat_cpu(j).irqs[i - 1]);
|
||||
#endif
|
||||
|
||||
level = group->sources[ix]->level - frv_irq_levels;
|
||||
|
||||
seq_printf(p, " %12s@%x", group->sources[ix]->muxname, level);
|
||||
seq_printf(p, " %s", action->name);
|
||||
|
||||
for (action = action->next; action; action = action->next)
|
||||
seq_printf(p, ", %s", action->name);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
skip:
|
||||
local_irq_restore(flags);
|
||||
break;
|
||||
|
||||
case NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP + 1:
|
||||
seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
|
||||
} else if (i == NR_IRQS) {
|
||||
seq_printf(p, "Err: %10u\n", atomic_read(&irq_err_count));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic enable/disable code: this just calls
|
||||
* down into the PIC-specific version for the actual
|
||||
* hardware disable after having gotten the irq
|
||||
* controller lock.
|
||||
* on-CPU PIC operations
|
||||
*/
|
||||
|
||||
/**
|
||||
* disable_irq_nosync - disable an irq without waiting
|
||||
* @irq: Interrupt to disable
|
||||
*
|
||||
* Disable the selected interrupt line. Disables and Enables are
|
||||
* nested.
|
||||
* Unlike disable_irq(), this function does not ensure existing
|
||||
* instances of the IRQ handler have completed before returning.
|
||||
*
|
||||
* This function may be called from IRQ context.
|
||||
*/
|
||||
|
||||
void disable_irq_nosync(unsigned int irq)
|
||||
static void frv_cpupic_ack(unsigned int irqlevel)
|
||||
{
|
||||
struct irq_source *source;
|
||||
struct irq_group *group;
|
||||
struct irq_level *level;
|
||||
unsigned long flags;
|
||||
int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
|
||||
|
||||
group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
|
||||
if (!group)
|
||||
BUG();
|
||||
|
||||
source = group->sources[idx];
|
||||
if (!source)
|
||||
BUG();
|
||||
|
||||
level = source->level;
|
||||
|
||||
spin_lock_irqsave(&level->lock, flags);
|
||||
|
||||
if (group->control) {
|
||||
if (!group->disable_cnt[idx]++)
|
||||
group->control(group, idx, 0);
|
||||
} else if (!level->disable_count++) {
|
||||
__set_MASK(level - frv_irq_levels);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&level->lock, flags);
|
||||
__clr_RC(irqlevel);
|
||||
__clr_IRL();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(disable_irq_nosync);
|
||||
|
||||
/**
|
||||
* disable_irq - disable an irq and wait for completion
|
||||
* @irq: Interrupt to disable
|
||||
*
|
||||
* Disable the selected interrupt line. Enables and Disables are
|
||||
* nested.
|
||||
* This function waits for any pending IRQ handlers for this interrupt
|
||||
* to complete before returning. If you use this function while
|
||||
* holding a resource the IRQ handler may need you will deadlock.
|
||||
*
|
||||
* This function may be called - with care - from IRQ context.
|
||||
*/
|
||||
|
||||
void disable_irq(unsigned int irq)
|
||||
static void frv_cpupic_mask(unsigned int irqlevel)
|
||||
{
|
||||
disable_irq_nosync(irq);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (!local_irq_count(smp_processor_id())) {
|
||||
do {
|
||||
barrier();
|
||||
} while (irq_desc[irq].status & IRQ_INPROGRESS);
|
||||
}
|
||||
#endif
|
||||
__set_MASK(irqlevel);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(disable_irq);
|
||||
|
||||
/**
|
||||
* enable_irq - enable handling of an irq
|
||||
* @irq: Interrupt to enable
|
||||
*
|
||||
* Undoes the effect of one call to disable_irq(). If this
|
||||
* matches the last disable, processing of interrupts on this
|
||||
* IRQ line is re-enabled.
|
||||
*
|
||||
* This function may be called from IRQ context.
|
||||
*/
|
||||
|
||||
void enable_irq(unsigned int irq)
|
||||
static void frv_cpupic_mask_ack(unsigned int irqlevel)
|
||||
{
|
||||
struct irq_source *source;
|
||||
struct irq_group *group;
|
||||
struct irq_level *level;
|
||||
unsigned long flags;
|
||||
int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
|
||||
int count;
|
||||
|
||||
group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
|
||||
if (!group)
|
||||
BUG();
|
||||
|
||||
source = group->sources[idx];
|
||||
if (!source)
|
||||
BUG();
|
||||
|
||||
level = source->level;
|
||||
|
||||
spin_lock_irqsave(&level->lock, flags);
|
||||
|
||||
if (group->control)
|
||||
count = group->disable_cnt[idx];
|
||||
else
|
||||
count = level->disable_count;
|
||||
|
||||
switch (count) {
|
||||
case 1:
|
||||
if (group->control) {
|
||||
if (group->actions[idx])
|
||||
group->control(group, idx, 1);
|
||||
} else {
|
||||
if (level->usage)
|
||||
__clr_MASK(level - frv_irq_levels);
|
||||
}
|
||||
/* fall-through */
|
||||
|
||||
default:
|
||||
count--;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
if (group->control)
|
||||
group->disable_cnt[idx] = count;
|
||||
else
|
||||
level->disable_count = count;
|
||||
|
||||
spin_unlock_irqrestore(&level->lock, flags);
|
||||
__set_MASK(irqlevel);
|
||||
__clr_RC(irqlevel);
|
||||
__clr_IRL();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(enable_irq);
|
||||
static void frv_cpupic_unmask(unsigned int irqlevel)
|
||||
{
|
||||
__clr_MASK(irqlevel);
|
||||
}
|
||||
|
||||
static void frv_cpupic_end(unsigned int irqlevel)
|
||||
{
|
||||
__clr_MASK(irqlevel);
|
||||
}
|
||||
|
||||
static struct irq_chip frv_cpu_pic = {
|
||||
.name = "cpu",
|
||||
.ack = frv_cpupic_ack,
|
||||
.mask = frv_cpupic_mask,
|
||||
.mask_ack = frv_cpupic_mask_ack,
|
||||
.unmask = frv_cpupic_unmask,
|
||||
.end = frv_cpupic_end,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handles all normal device IRQ's
|
||||
* - registers are referred to by the __frame variable (GR28)
|
||||
|
@ -281,463 +142,65 @@ EXPORT_SYMBOL(enable_irq);
|
|||
*/
|
||||
asmlinkage void do_IRQ(void)
|
||||
{
|
||||
struct irq_source *source;
|
||||
int level, cpu;
|
||||
|
||||
irq_enter();
|
||||
|
||||
level = (__frame->tbr >> 4) & 0xf;
|
||||
cpu = smp_processor_id();
|
||||
|
||||
if ((unsigned long) __frame - (unsigned long) (current + 1) < 512)
|
||||
BUG();
|
||||
|
||||
__set_MASK(level);
|
||||
__clr_RC(level);
|
||||
__clr_IRL();
|
||||
|
||||
kstat_this_cpu.irqs[level]++;
|
||||
|
||||
for (source = frv_irq_levels[level].sources; source; source = source->next)
|
||||
source->doirq(source);
|
||||
|
||||
__clr_MASK(level);
|
||||
|
||||
generic_handle_irq(__get_IRL(), __frame);
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
} /* end do_IRQ() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* handles all NMIs when not co-opted by the debugger
|
||||
* - registers are referred to by the __frame variable (GR28)
|
||||
*/
|
||||
asmlinkage void do_NMI(void)
|
||||
{
|
||||
} /* end do_NMI() */
|
||||
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* request_irq - allocate an interrupt line
|
||||
* @irq: Interrupt line to allocate
|
||||
* @handler: Function to be called when the IRQ occurs
|
||||
* @irqflags: Interrupt type flags
|
||||
* @devname: An ascii name for the claiming device
|
||||
* @dev_id: A cookie passed back to the handler function
|
||||
*
|
||||
* This call allocates interrupt resources and enables the
|
||||
* interrupt line and IRQ handling. From the point this
|
||||
* call is made your handler function may be invoked. Since
|
||||
* your handler function must clear any interrupt the board
|
||||
* raises, you must take care both to initialise your hardware
|
||||
* and to set up the interrupt handler in the right order.
|
||||
*
|
||||
* Dev_id must be globally unique. Normally the address of the
|
||||
* device data structure is used as the cookie. Since the handler
|
||||
* receives this value it makes sense to use it.
|
||||
*
|
||||
* If your interrupt is shared you must pass a non NULL dev_id
|
||||
* as this is required when freeing the interrupt.
|
||||
*
|
||||
* Flags:
|
||||
*
|
||||
* IRQF_SHARED Interrupt is shared
|
||||
*
|
||||
* IRQF_DISABLED Disable local interrupts while processing
|
||||
*
|
||||
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
|
||||
*
|
||||
*/
|
||||
|
||||
int request_irq(unsigned int irq,
|
||||
irqreturn_t (*handler)(int, void *, struct pt_regs *),
|
||||
unsigned long irqflags,
|
||||
const char * devname,
|
||||
void *dev_id)
|
||||
{
|
||||
int retval;
|
||||
struct irqaction *action;
|
||||
|
||||
#if 1
|
||||
/*
|
||||
* Sanity-check: shared interrupts should REALLY pass in
|
||||
* a real dev-ID, otherwise we'll have trouble later trying
|
||||
* to figure out which interrupt is which (messes up the
|
||||
* interrupt freeing logic etc).
|
||||
*/
|
||||
if (irqflags & IRQF_SHARED) {
|
||||
if (!dev_id)
|
||||
printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n",
|
||||
devname, (&irq)[-1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
|
||||
return -EINVAL;
|
||||
if (!handler)
|
||||
return -EINVAL;
|
||||
|
||||
action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
|
||||
if (!action)
|
||||
return -ENOMEM;
|
||||
|
||||
action->handler = handler;
|
||||
action->flags = irqflags;
|
||||
action->mask = CPU_MASK_NONE;
|
||||
action->name = devname;
|
||||
action->next = NULL;
|
||||
action->dev_id = dev_id;
|
||||
|
||||
retval = setup_irq(irq, action);
|
||||
if (retval)
|
||||
kfree(action);
|
||||
return retval;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(request_irq);
|
||||
|
||||
/**
|
||||
* free_irq - free an interrupt
|
||||
* @irq: Interrupt line to free
|
||||
* @dev_id: Device identity to free
|
||||
*
|
||||
* Remove an interrupt handler. The handler is removed and if the
|
||||
* interrupt line is no longer in use by any driver it is disabled.
|
||||
* On a shared IRQ the caller must ensure the interrupt is disabled
|
||||
* on the card it drives before calling this function. The function
|
||||
* does not return until any executing interrupts for this IRQ
|
||||
* have completed.
|
||||
*
|
||||
* This function may be called from interrupt context.
|
||||
*
|
||||
* Bugs: Attempting to free an irq in a handler for the same irq hangs
|
||||
* the machine.
|
||||
*/
|
||||
|
||||
void free_irq(unsigned int irq, void *dev_id)
|
||||
{
|
||||
struct irq_source *source;
|
||||
struct irq_group *group;
|
||||
struct irq_level *level;
|
||||
struct irqaction **p, **pp;
|
||||
unsigned long flags;
|
||||
|
||||
if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
|
||||
return;
|
||||
|
||||
group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
|
||||
if (!group)
|
||||
BUG();
|
||||
|
||||
source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
|
||||
if (!source)
|
||||
BUG();
|
||||
|
||||
level = source->level;
|
||||
p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
|
||||
|
||||
spin_lock_irqsave(&level->lock, flags);
|
||||
|
||||
for (pp = p; *pp; pp = &(*pp)->next) {
|
||||
struct irqaction *action = *pp;
|
||||
|
||||
if (action->dev_id != dev_id)
|
||||
continue;
|
||||
|
||||
/* found it - remove from the list of entries */
|
||||
*pp = action->next;
|
||||
|
||||
level->usage--;
|
||||
|
||||
if (p == pp && group->control)
|
||||
group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 0);
|
||||
|
||||
if (level->usage == 0)
|
||||
__set_MASK(level - frv_irq_levels);
|
||||
|
||||
spin_unlock_irqrestore(&level->lock,flags);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Wait to make sure it's not being used on another CPU */
|
||||
while (desc->status & IRQ_INPROGRESS)
|
||||
barrier();
|
||||
#endif
|
||||
kfree(action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(free_irq);
|
||||
|
||||
/*
|
||||
* IRQ autodetection code..
|
||||
*
|
||||
* This depends on the fact that any interrupt that comes in on to an
|
||||
* unassigned IRQ will cause GxICR_DETECT to be set
|
||||
*/
|
||||
|
||||
static DECLARE_MUTEX(probe_sem);
|
||||
|
||||
/**
|
||||
* probe_irq_on - begin an interrupt autodetect
|
||||
*
|
||||
* Commence probing for an interrupt. The interrupts are scanned
|
||||
* and a mask of potential interrupt lines is returned.
|
||||
*
|
||||
*/
|
||||
|
||||
unsigned long probe_irq_on(void)
|
||||
{
|
||||
down(&probe_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(probe_irq_on);
|
||||
|
||||
/*
|
||||
* Return a mask of triggered interrupts (this
|
||||
* can handle only legacy ISA interrupts).
|
||||
*/
|
||||
|
||||
/**
|
||||
* probe_irq_mask - scan a bitmap of interrupt lines
|
||||
* @val: mask of interrupts to consider
|
||||
*
|
||||
* Scan the ISA bus interrupt lines and return a bitmap of
|
||||
* active interrupts. The interrupt probe logic state is then
|
||||
* returned to its previous value.
|
||||
*
|
||||
* Note: we need to scan all the irq's even though we will
|
||||
* only return ISA irq numbers - just so that we reset them
|
||||
* all to a known state.
|
||||
*/
|
||||
unsigned int probe_irq_mask(unsigned long xmask)
|
||||
{
|
||||
up(&probe_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(probe_irq_mask);
|
||||
|
||||
/*
|
||||
* Return the one interrupt that triggered (this can
|
||||
* handle any interrupt source).
|
||||
*/
|
||||
|
||||
/**
|
||||
* probe_irq_off - end an interrupt autodetect
|
||||
* @xmask: mask of potential interrupts (unused)
|
||||
*
|
||||
* Scans the unused interrupt lines and returns the line which
|
||||
* appears to have triggered the interrupt. If no interrupt was
|
||||
* found then zero is returned. If more than one interrupt is
|
||||
* found then minus the first candidate is returned to indicate
|
||||
* their is doubt.
|
||||
*
|
||||
* The interrupt probe logic state is returned to its previous
|
||||
* value.
|
||||
*
|
||||
* BUGS: When used in a module (which arguably shouldnt happen)
|
||||
* nothing prevents two IRQ probe callers from overlapping. The
|
||||
* results of this are non-optimal.
|
||||
*/
|
||||
|
||||
int probe_irq_off(unsigned long xmask)
|
||||
{
|
||||
up(&probe_sem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(probe_irq_off);
|
||||
|
||||
/* this was setup_x86_irq but it seems pretty generic */
|
||||
int setup_irq(unsigned int irq, struct irqaction *new)
|
||||
{
|
||||
struct irq_source *source;
|
||||
struct irq_group *group;
|
||||
struct irq_level *level;
|
||||
struct irqaction **p, **pp;
|
||||
unsigned long flags;
|
||||
|
||||
group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
|
||||
if (!group)
|
||||
BUG();
|
||||
|
||||
source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
|
||||
if (!source)
|
||||
BUG();
|
||||
|
||||
level = source->level;
|
||||
|
||||
p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
|
||||
|
||||
/*
|
||||
* Some drivers like serial.c use request_irq() heavily,
|
||||
* so we have to be careful not to interfere with a
|
||||
* running system.
|
||||
*/
|
||||
if (new->flags & IRQF_SAMPLE_RANDOM) {
|
||||
/*
|
||||
* This function might sleep, we want to call it first,
|
||||
* outside of the atomic block.
|
||||
* Yes, this might clear the entropy pool if the wrong
|
||||
* driver is attempted to be loaded, without actually
|
||||
* installing a new handler, but is this really a problem,
|
||||
* only the sysadmin is able to do this.
|
||||
*/
|
||||
rand_initialize_irq(irq);
|
||||
}
|
||||
|
||||
/* must juggle the interrupt processing stuff with interrupts disabled */
|
||||
spin_lock_irqsave(&level->lock, flags);
|
||||
|
||||
/* can't share interrupts unless all parties agree to */
|
||||
if (level->usage != 0 && !(level->flags & new->flags & IRQF_SHARED)) {
|
||||
spin_unlock_irqrestore(&level->lock,flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* add new interrupt at end of irq queue */
|
||||
pp = p;
|
||||
while (*pp)
|
||||
pp = &(*pp)->next;
|
||||
|
||||
*pp = new;
|
||||
|
||||
level->usage++;
|
||||
level->flags = new->flags;
|
||||
|
||||
/* turn the interrupts on */
|
||||
if (level->usage == 1)
|
||||
__clr_MASK(level - frv_irq_levels);
|
||||
|
||||
if (p == pp && group->control)
|
||||
group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 1);
|
||||
|
||||
spin_unlock_irqrestore(&level->lock, flags);
|
||||
register_irq_proc(irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct proc_dir_entry * root_irq_dir;
|
||||
static struct proc_dir_entry * irq_dir [NR_IRQS];
|
||||
|
||||
#define HEX_DIGITS 8
|
||||
|
||||
static unsigned int parse_hex_value (const char __user *buffer,
|
||||
unsigned long count, unsigned long *ret)
|
||||
{
|
||||
unsigned char hexnum [HEX_DIGITS];
|
||||
unsigned long value;
|
||||
int i;
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
if (count > HEX_DIGITS)
|
||||
count = HEX_DIGITS;
|
||||
if (copy_from_user(hexnum, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Parse the first 8 characters as a hex string, any non-hex char
|
||||
* is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
|
||||
*/
|
||||
value = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
unsigned int c = hexnum[i];
|
||||
|
||||
switch (c) {
|
||||
case '0' ... '9': c -= '0'; break;
|
||||
case 'a' ... 'f': c -= 'a'-10; break;
|
||||
case 'A' ... 'F': c -= 'A'-10; break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
value = (value << 4) | c;
|
||||
}
|
||||
out:
|
||||
*ret = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
unsigned long *mask = (unsigned long *) data;
|
||||
if (count < HEX_DIGITS+1)
|
||||
return -EINVAL;
|
||||
return sprintf (page, "%08lx\n", *mask);
|
||||
}
|
||||
|
||||
static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
unsigned long *mask = (unsigned long *) data, full_count = count, err;
|
||||
unsigned long new_value;
|
||||
|
||||
show_state();
|
||||
err = parse_hex_value(buffer, count, &new_value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*mask = new_value;
|
||||
return full_count;
|
||||
}
|
||||
|
||||
#define MAX_NAMELEN 10
|
||||
|
||||
static void register_irq_proc (unsigned int irq)
|
||||
{
|
||||
char name [MAX_NAMELEN];
|
||||
|
||||
if (!root_irq_dir || irq_dir[irq])
|
||||
return;
|
||||
|
||||
memset(name, 0, MAX_NAMELEN);
|
||||
sprintf(name, "%d", irq);
|
||||
|
||||
/* create /proc/irq/1234 */
|
||||
irq_dir[irq] = proc_mkdir(name, root_irq_dir);
|
||||
}
|
||||
|
||||
unsigned long prof_cpu_mask = -1;
|
||||
|
||||
void init_irq_proc (void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
int i;
|
||||
|
||||
/* create /proc/irq */
|
||||
root_irq_dir = proc_mkdir("irq", NULL);
|
||||
|
||||
/* create /proc/irq/prof_cpu_mask */
|
||||
entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->nlink = 1;
|
||||
entry->data = (void *)&prof_cpu_mask;
|
||||
entry->read_proc = prof_cpu_mask_read_proc;
|
||||
entry->write_proc = prof_cpu_mask_write_proc;
|
||||
|
||||
/*
|
||||
* Create entries for all existing IRQs.
|
||||
*/
|
||||
for (i = 0; i < NR_IRQS; i++)
|
||||
register_irq_proc(i);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* initialise the interrupt system
|
||||
*/
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
route_cpu_irqs();
|
||||
int level;
|
||||
|
||||
for (level = 1; level <= 14; level++)
|
||||
set_irq_chip_and_handler(level, &frv_cpu_pic,
|
||||
handle_level_irq);
|
||||
|
||||
set_irq_handler(IRQ_CPU_TIMER0, handle_edge_irq);
|
||||
|
||||
/* set the trigger levels for internal interrupt sources
|
||||
* - timers all falling-edge
|
||||
* - ERR0 is rising-edge
|
||||
* - all others are high-level
|
||||
*/
|
||||
__set_IITMR(0, 0x003f0000); /* DMA0-3, TIMER0-2 */
|
||||
__set_IITMR(1, 0x20000000); /* ERR0-1, UART0-1, DMA4-7 */
|
||||
|
||||
/* route internal interrupts */
|
||||
set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL,
|
||||
IRQ_DMA0_LEVEL);
|
||||
set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
|
||||
set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL,
|
||||
IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
|
||||
set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL,
|
||||
IRQ_DMA4_LEVEL);
|
||||
|
||||
/* route external interrupts */
|
||||
set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL,
|
||||
IRQ_XIRQ4_LEVEL);
|
||||
set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL,
|
||||
IRQ_XIRQ0_LEVEL);
|
||||
|
||||
#if defined(CONFIG_MB93091_VDK)
|
||||
__set_TM1(0x55550000); /* XIRQ7-0 all active low */
|
||||
#elif defined(CONFIG_MB93093_PDK)
|
||||
__set_TM1(0x15550000); /* XIRQ7 active high, 6-0 all active low */
|
||||
#else
|
||||
#error dont know external IRQ trigger levels for this setup
|
||||
#endif
|
||||
|
||||
fpga_init();
|
||||
#ifdef CONFIG_FUJITSU_MB93493
|
||||
route_mb93493_irqs();
|
||||
mb93493_init();
|
||||
#endif
|
||||
} /* end init_IRQ() */
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include <asm/mb-regs.h>
|
||||
#include <asm/mb93493-regs.h>
|
||||
#include <asm/gdb-stub.h>
|
||||
#include <asm/irq-routing.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <asm/timer-regs.h>
|
||||
#include <asm/mb-regs.h>
|
||||
#include <asm/mb86943a.h>
|
||||
#include <asm/irq-routing.h>
|
||||
|
||||
#include <linux/timex.h>
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include <asm/io.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/irq-routing.h>
|
||||
|
||||
#include "pci-frv.h"
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ void show_mem(void)
|
|||
*/
|
||||
void __init paging_init(void)
|
||||
{
|
||||
unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
|
||||
unsigned long zones_size[MAX_NR_ZONES] = {0, };
|
||||
|
||||
/* allocate some pages for kernel housekeeping tasks */
|
||||
empty_bad_page_table = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
|
||||
|
|
|
@ -138,7 +138,7 @@ void paging_init(void)
|
|||
#endif
|
||||
|
||||
{
|
||||
unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
|
||||
unsigned long zones_size[MAX_NR_ZONES] = {0, };
|
||||
|
||||
zones_size[ZONE_DMA] = 0 >> PAGE_SHIFT;
|
||||
zones_size[ZONE_NORMAL] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT;
|
||||
|
|
|
@ -494,7 +494,7 @@ config HIGHMEM64G
|
|||
endchoice
|
||||
|
||||
choice
|
||||
depends on EXPERIMENTAL && !X86_PAE
|
||||
depends on EXPERIMENTAL
|
||||
prompt "Memory split" if EMBEDDED
|
||||
default VMSPLIT_3G
|
||||
help
|
||||
|
@ -516,6 +516,7 @@ choice
|
|||
config VMSPLIT_3G
|
||||
bool "3G/1G user/kernel split"
|
||||
config VMSPLIT_3G_OPT
|
||||
depends on !HIGHMEM
|
||||
bool "3G/1G user/kernel split (for full 1G low memory)"
|
||||
config VMSPLIT_2G
|
||||
bool "2G/2G user/kernel split"
|
||||
|
@ -794,6 +795,7 @@ config HOTPLUG_CPU
|
|||
config COMPAT_VDSO
|
||||
bool "Compat VDSO support"
|
||||
default y
|
||||
depends on !PARAVIRT
|
||||
help
|
||||
Map the VDSO to the predictable old-style address too.
|
||||
---help---
|
||||
|
|
|
@ -1154,9 +1154,11 @@ out:
|
|||
|
||||
static void set_time(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (got_clock_diff) { /* Must know time zone in order to set clock */
|
||||
xtime.tv_sec = get_cmos_time() + clock_cmos_diff;
|
||||
xtime.tv_nsec = 0;
|
||||
ts.tv_sec = get_cmos_time() + clock_cmos_diff;
|
||||
ts.tv_nsec = 0;
|
||||
do_settimeofday(&ts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1232,13 +1234,8 @@ static int suspend(int vetoable)
|
|||
restore_processor_state();
|
||||
|
||||
local_irq_disable();
|
||||
write_seqlock(&xtime_lock);
|
||||
spin_lock(&i8253_lock);
|
||||
reinit_timer();
|
||||
set_time();
|
||||
|
||||
spin_unlock(&i8253_lock);
|
||||
write_sequnlock(&xtime_lock);
|
||||
reinit_timer();
|
||||
|
||||
if (err == APM_NO_ERROR)
|
||||
err = APM_SUCCESS;
|
||||
|
@ -1365,9 +1362,7 @@ static void check_events(void)
|
|||
ignore_bounce = 1;
|
||||
if ((event != APM_NORMAL_RESUME)
|
||||
|| (ignore_normal_resume == 0)) {
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
set_time();
|
||||
write_sequnlock_irq(&xtime_lock);
|
||||
device_resume();
|
||||
pm_send_all(PM_RESUME, (void *)0);
|
||||
queue_event(event, NULL);
|
||||
|
@ -1383,9 +1378,7 @@ static void check_events(void)
|
|||
break;
|
||||
|
||||
case APM_UPDATE_TIME:
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
set_time();
|
||||
write_sequnlock_irq(&xtime_lock);
|
||||
break;
|
||||
|
||||
case APM_CRITICAL_SUSPEND:
|
||||
|
@ -2339,6 +2332,7 @@ static int __init apm_init(void)
|
|||
ret = kernel_thread(apm, NULL, CLONE_KERNEL | SIGCHLD);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "apm: disabled - Unable to start kernel thread.\n");
|
||||
remove_proc_entry("apm", NULL);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -2348,7 +2342,13 @@ static int __init apm_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
misc_register(&apm_device);
|
||||
/*
|
||||
* Note we don't actually care if the misc_device cannot be registered.
|
||||
* this driver can do its job without it, even if userspace can't
|
||||
* control it. just log the error
|
||||
*/
|
||||
if (misc_register(&apm_device))
|
||||
printk(KERN_WARNING "apm: Could not register misc device.\n");
|
||||
|
||||
if (HZ != 100)
|
||||
idle_period = (idle_period * HZ) / 100;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче