Skip to main content.
Google custom search

NetBSD Documentation: Porting NetBSD to a new ARM SoC

Written and contributed by Jonathan Sevy

Introduction

This article describes experiences in porting NetBSD to a new ARM-based SoC. The specific chip addressed is the ARM9-based Agere Vx115 cellular telephony digital baseband chip, and the Vx115 VEP evaluation board hosting the chip. The information presented here is intended to assist with the porting of NetBSD to any new ARM-based platform.

Porting Variants

There are a couple of variants possible when porting NetBSD depending on the level of support already available in the kernel.

  • Processor architecture port

This involves adapting the NetBSD kernel to run on a new processor architecture type (like MIPS, PowerPC, ARM, etc., though those types are already supported). This is a major undertaking that requires deep understanding of both the NetBSD core and the processor architecture.

  • SoC port

This involves porting NetBSD to a new SoC which uses as its processor core a type already supported by NetBSD but provides some unique set of on-chip peripheral devices. This involves defining the bus structures needed to interconnect the devices on the SoC, as well as drivers for the peripherals.

  • Platform port

This involves developing code to allow NetBSD to run on a particular platform (evaluation board) that uses a chip (SoC) already supported by the NetBSD kernel. This involves creating appropriate platform startup code, as well as support for off-chip peripherals on the board, as well as an appropriate root filesystem with the platform applications and libraries. This document describes the latter two activities. The Vx115 SoC is based on an ARM9 processor core already supported by NetBSD (though the exact variant, the ARM926EJ-S, was not directly supported in the kernel version used and required minor adaptation), so a processor port was not needed, but the SoC provides a uniques set of on-chip peripherals, and was provided on an evaluation board with various supporting hardware components.

Scope

The focus of this porting effort was to get NetBSD running on the platform with only the minimum support needed to host a console and shell on the serial port. Only those on- and off-chip peripherals needed to support this operation were addressed; the SoC provides many other peripherals which would need support (drivers) to comprise anything near a full board-support package.

Such a "basic" port requires addressing a relatively small number of issues, described in the following sections.

Each of these involves creating various configuration, source and header files, and the contents of these files is discussed in the subsequent sections.

Acknowledgments and References

As is standard practice with open-source systems, much of the required functionality can be found in corresponding files for existing ports. This project makes full use of this available source code (with thanks to all of the original authors). In fact, it's because of the work of others, and their openness in sharing it, that NetBSD and other open-source systems are quite straightforward to adapt to new platforms.

There's a great deal of information available on the NetBSD Project website on building and working with NetBSD systems. Of particular interest for building is the NetBSD Guide , especially the sections on building and cross-compiling .

Also invaluable is the web-based source cross-referencing tool initially provided as the Linux Cross-Reference by the LXR Project . This tool has been applied to the NetBSD source (as well as many others) by Robert Watson in his FreeBSD and Linux Kernel Cross-Reference.

Jon Sevy
jsevy@cs.drexel.edu
http://gicl.cs.drexel.edu/people/sevy

Building

I do most of my development work on an x86 Linux system, and thus wanted to be able to cross-build the NetBSD system from within the Linux environment. Fortunately, NetBSD provides a great build system that supports both cross-compilation (building for a processor architecture different from that of the build host) and cross-platform builds (building the system on a non-NetBSD build host).

The build process consists of several steps:

  • Building the toolchain

  • Building the kernel

  • Building the userspace libraries and applications

  • Building the root filesystem

NetBSD comes with a wonderful shell script, build.sh, that makes all of this easy.

Building the Toolchain

Building the toolchain can be accomplished using the build.sh script: just run

./build.sh -u -m evbarm tools        

from within the top-level source directory.

 Parameters:

  • -u:    don't run "make clean" before building; this prevents everything from being rebuilt if changes are made (not really essential for this first build step)

  • -m evbarm:    the machine architecture type; this is for ARM evaluation boards

  • tools: build just the toolchain, not the whole system

I actually found I had a problem when trying to build the toolchain on a Fedora Core 5 system because the native Fedora tools (which are used to build the NetBSD toolchain) were too new (gcc 4.1). The NetBSD toolchain would build cleanly only with gcc version 3 tools. Luckily, Fedora provides optional packages for the GCC 3.2 toolset, which can be installed with

yum install compat-gcc-3.2        

The toolchain build process then becomes

export HOST_CC=gcc32
export HOST_CXX=g++32
./build.sh -u -m evbarm tools        

to ensure the gcc-3-based tools are used to build the toolchain. Note that these tools (and the export statements) aren't needed once the NetBSD toolchain is built - they're just needed to "bootstrap" the toolchain itself. Note also that the latest NetBSD distribution should build cleanly with the gcc 4-based tools, so this step shouldn't be necessary.

Building the Kernel

Building the NetBSD kernel is also accomplished using the build.sh script: just run

./build.sh -u -m evbarm kernel=VX115_VEP

from within the top-level source directory.

Parameters:

  • -u:    don't run "make clean" before building; this prevents the toolchain from being rebuilt

  • -m evbarm:    the machine architecture type; this is for ARM evaluation boards

  • kernel=VX115_VEP:    the kernel configuration file to use; in this case, it's for the VX115_VEP verification board. The configuration file is in sys/srch/evbarm/conf.

This command performs a number of operations:

  • creates a build directory sys/arch/evbarm/compile/obj/VX115_VEP

  • runs the nbconfig "config" utility on the VX115_VEP configuration file to create required source files for the build. This config command does quite a lot;  it actually creates source files for the autoconfig framework specific to the system and device configuration. This is discussed in more detail in the section on autoconfiguration.

  • runs "make depend" on the source files to create the dependency tree for the source files

  • runs "make" to build the kernel and drivers

When the command completes, it will create the kernel objects in the sys/arch/evbarm/compile/obj/VX115_VEP directory. The object files will include the ELF kernel object, and may contain the output in different formats depending on the configuration.

  • netbsd: ELF object file

  • netbsd.bin: netbsd converted to plain binary for direct loading into memory

  • netbsd.bin.srec: netbsd.bin converted to S-record format for loading to flash

  • netbsd.gdb: ELF object file with debugging symbols (for use with debugger)

Building the Userspace Libraries and Applications

Since the NetBSD distribution includes all of the userspace libraries and applications, the build.sh script handles these, too: just run

./build.sh -u -m evbarm

from within the top-level source directory. This will create a directory obj/destdir.evbarm with all of the userspace files. 

Building the Root Filesystem

The default build for NetBSD creates a filesystem for a desktop system, which is way too big for an embedded system. I pulled out just the minimum basic libraries and apps needed to have a console into a separate directory called rootfs_ramdisk. (In fact, this is almost certainly not the bare minimum, but was small enough to fit in the ramdisk, so I didn't bother to strip it further.)

  • rootfs_ramdisk

    • bin

      • [ cat chmod cp date df echo ed expr hostname kill ln ls  mkdir  mv  ps  pwd  rm  rmdir  sh  sleep  stty  sync  test

    • dev

      • contents auto-generated as discussed below

    • etc

      • mkttys  passwd.conf  rc.conf  rc.lkm  rc.shutdown  ttys group  motd rc  rc.local  rc.subr

      • rc.d

        • local motd mountall root

    • lib

      • libcrypt.so  libcrypt.so.0  libcrypt.so.0.2  libc.so  libc.so.12  libc.so.12.128.2  libedit.so  libedit.so.2  libedit.so.2.9  libevent.so  libevent.so.0  libevent.so.0.2  libkvm.so  libkvm.so.5  libkvm.so.5.2  libm.so  libm.so.0  libm.so.0.2  libtermcap.so  libtermcap.so.0  libtermcap.so.0.5  libtermlib.so  libtermlib.so.0  libtermlib.so.0.5  libutil.so  libutil.so.7  libutil.so.7.6  libz.so  libz.so.0  libz.so.0.4

    • libexec

      • ld.elf_so

    • sbin

      • dmesg  fsck  fsck_ffs  init  init.shell  ldconfig  mknod  modload  modunload  mount  mount_ffs  mount_filecore mount_kernfs  mount_mfs  mount_null  mount_overlay  mount_procfs  mount_ufs  nologin  rndctl  savecore   sysctl ttyflags  umount

    • usr

      • libexec

        • getty

This directory is a directory of files on the build host filesystem (Linux, in my case, using an ext3 filesystem). It's necessary to create a single file which contains the filesystem image for NetBSD - that is, the  set of objects which are currently in the build host filesystem, but packaged such that they form a NetBSD filesystem. This is done with the tool nbmakefs, which is built as part of the toolchain.

nbmakefs -s 5000k rootfs_ramdisk.img rootfs_ramdisk

This will create a filesystem of total size 5000 kB - the resulting image will be 5000 kB in size (or about 4.9 MB). This value is also used in the kernel configuration to "reserve" this much space in the kernel for the filesystem ramdisk image.

Inserting the Ramdisk Image into the Kernel

The ramdisk created above is inserted into the kernel using the tool mdsetimage:

arm--netbsdelf-mdsetimage -v -s netbsd rootfs_ramdisk.img

where netbsd is the kernel image created from the kernel build process.

Further Packaging

For the evaluation platform here, the platform loader used s-record format for the loadable files, so it was necessary to use the objcopy tool to generate the loadable srec from the kernel+ramdisk image. In addition, the kernel was to be loaded at address 0x20400000 in the system Flash, while it was configured to execute at 0x24300000, which required the --change-addresses argument during s-record generation. This sort of thin is very platform-specific, so these steps may not be relevant for another platform.

arm--netbsdelf-objcopy -S -O binary netbsd netbsd.bin

arm--netbsdelf-objcopy --input-target=binary --output-target=srec \
  --change-addresses=0x20400000 -v netbsd.bin netbsd.bin.srec

Device Autoconfiguration Process

The NetBSD device architecture is a tree consisting of children (devices and buses) descended from parents (buses). The "family tree" of devices is (partially) defined in kernel configuration files named files.*, where each child (device or bus) is specified as being attached to a specific parent (bus). The syntax of this attachment is discussed in the kernel configuration section , and described in detail in the NetBSD man page on config . The root of the tree is the "root" bus; all other buses and devices are descendants of this. In particular, for ARM-based systems, the bus "mainbus" is attached to the root in the file files.arm, and this is the bus to which the cpu is attached (in files.arm) as well as all system buses (in the processor-specific files.vx115).

The above defines the basic relationship between devices and buses. However, the actual instances of a specific device or bus on a platform are defined in the main platform configuration file - here, the file VX115_VEP. In this file, instances of buses and devices are specified with the bus/device base name followed by a digit - for example, mainbus0. There may be multiple instances of the same device, differentiated by their numerical suffixes - for example, vx115_com0 and vx115_com1. In addition, the specification of a device instance can provide a device locator, which is a list of parameters specific to that instance - for example, the base address of the device instance, IRQ, etc.  The parameters that make up the locator, along with default values, are part of the parent (bus) specification in the files.* files. These parameters are used during the autoconfiguration process, described below.

The tree structure of devices is built by the kernel configuration process, in which the NetBSD config utility is run on the platform configuration files to create source files with data structures reflecting the specified device relationships. The tree structure created by the kernel configuration process is used during kernel initialization in a process called autoconfiguration. This involves the sequential initialization of devices in a depth-first search of the tree of devices, as follows.

  • Each bus and device defines two methods, match( ) and attach( ), and registers them with the autoconfiguration system using the CFATTACH_DECL macro. 

  • During kernel initialization, each parent (bus) instance first probes its child instances by calling their match( ) functions, supplying the information (such as the device address and other parameters) that was specified in the child device's instance specification. The match( ) function uses the supplied parameters to determine if the information corresponds to that child, and if so, returns a non-zero value. 

  • When a child instance has returned that it's a match, the parent then calls the device's attach( ) method, in which the child instance initializes itself. If the child is itself a parent (bus), it will probe its children as part of its attach( ) method.

Each parent uses the NetBSD config_search method and its own search method to probe its children. config_search provides the parent search method with a cfdata struct which contains the device instance-specific locator parameters defined in the device instance specification. This data is passed to the match( ) and attach( ) functions. The cfdata structure associated with a device has a very general structure, with a loc field that's an array containing the bus-specific locator parameters with array indices that are defines generated by the config utility from the parameter names in the bus specification. For convenience, the parameters are usually copied into a more "structured" struct before being passed to match( ) and attach( ).

A Few More Details

The parent device search routine uses the NetBSD kernel function config_match function to look up and call its children's match( ) functions. The config_match( ) function calls the match( ) function for a device as follows.

config_match( )

  • calls config_cfattach_lookup( ) with device name as argument

    • calls config_cfdriver_lookup( )

      • runs through allcfdrivers list

      • returns cfdriver with matching name

    • returns config_cfattach_lookup_cd( ) with driver as arg

      • runs through cfdriver's cfattachlist field to find name match

      • returns cfattach which matches name

  • call the cfattach struct's field cf_match = match function of child

The cfdata structures passed to parents (buses) during the autoconfiguration process are generated in the file ioconf.c during the configuration process from files.vx115 and VX115_VEP. Each bus structure (parent) defines cfdata locator fields in files.vx115; the format of the declaration of a bus and its locator fields is

device foobus {[field1 = default1], ... [fieldn = defaultn]}
  • field1 through fieldn are locator names; the configuration process generates defines from the locator names with names FOOBUSCF_FIELD1, ..., FOOBUSCF_FIELDN, which are used as the array offsets to the corresponding information in the cfdata struct's loc array

  • defaults become defined values FOOBUSCF_FIELD1_DEFAULT, ... , FOOBUSCF_FIELDn_DEFAULT

For example, the files.vx115 file defines its internal APB with the text

device  vx115_apb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic

This bus thus defines 4 locators, which result in the generation of the defines VX115_APBCF_ADDR, VX115_APBCF_SIZE, VX115_APBCF_INTR, and VX115_APBCF_INDEX for the locator positions in the cfdata struct's loc array, and the default-value defines VX115_APBCF_ADDR_DEFAULT = -1, VX115_APBCF_SIZE_DEFAULT = 0, VX115_APBCF_INTR_DEFAULT = -1, and VX115_APBCF_INDEX_DEFAULT = 0

Device instances and their locator values for cfdata loc fields are specified in VX115_VEP. The format of the device instance specification is defined in the NetBSD man page config(5). The declarations for the timer device attached to the Vx115's APB is

vx115_clk0   at vx115_apb? addr 0x700C5000 size 0x68  intr 9        

Any field not specified gets default value in cfdata loc field, so in this case index = 0. To make it easier to work with the locator values outside of the rather unstructured cfdata loc array, it's typical to define a struct for the bus that mirrors the cfdata loc fields. For the Vx115's buses we define in vx115_var.h the struct type

struct vx115_attach_args
{

    bus_space_tag_t     sa_iot;     /* bus tag */
    bus_addr_t          sa_addr;    /* device base address */
    bus_size_t          sa_size;    /* device io space size */
    int                 sa_intr;    /* interrupt number */
    int                 sa_index;   /* device index */
};

This is initialized when the bus's devices are being probed from the corresponding device's cfdata struct, and then passed to the device's match( ) and attach( ) functions; note the use of the locator defines as offsets in the loc array of the cfdata struct.

intvx115_apb_search(struct device * parent, struct cfdata * cf, void *aux)
{
    struct vx115_softc *sc = (struct vx115_softc *) parent;
    struct vx115_attach_args aa;
    
    DPRINTF("vx115_apb_search\n");
    aa.sa_iot   = sc->sc_iot;
    aa.sa_addr  = cf->cf_loc[VX115_APBCF_ADDR];
    aa.sa_size  = cf->cf_loc[VX115_APBCF_SIZE];
    aa.sa_index = cf->cf_loc[VX115_APBCF_INDEX];
    aa.sa_intr  = cf->cf_loc[VX115_APBCF_INTR];

    if (config_match(parent, cf, &aa))
        config_attach(parent, cf, &aa, vx115_apb_print);
    
    return 0;
}

Kernel Configuration

As discussed in the previous section, there are a number of files that specify the configuration for the NetBSD kernel. Some of the files are specific to the processor, and shared by any machine which uses that processor, while others are specific to the machine (development board).

Directories

The files (configuration, source and header) associated with an ARM SoC or platform port are placed in subdirectories created for that SoC or platform. The SoC-specific directory is created in the sys/arch/arm directory; it's sys/arch/arm/vx115 in this case. The platform-specific directory is created in the sys/arch/evbarm directory; here, it's sys/arch/evbarm/vx115_vep.

Processor Configuration Files

These are files that are specific to the SoC, and present regardless of the board it is used on. These files are placed within the sys/arch/arm/<processor-name> subdirectory (sys/arch/arm/vx115 in this case).

This specification file contains fundamental device specifications, attachment indications and source-file inclusions for peripherals associated with the processor, i.e., used by any platform based on the processor. This includes things like

  • basic irq and bus operations

  • system buses (we have an AHB and APB in this case)

  • system timer (clock) used for the fundamental system time reference

  • interrupt controller

  • console serial interface (UART)

This file is used by the NetBSD autoconfiguration system, to include appropriate source files for compilation and to create appropriate structures reflecting the organization of the system (which peripherals are attached to which bus, what parameters the buses pass to their children, etc.) This file is included by the board-specific configuration file (in this case, files.vx115_vep , described below).

As can be seen in files.vx115 , each bus or device has 3 basic parts to its specification

  • device <device-name>

    • Provides a name for the device, for use in further references in the code and configuration files. 

    • For buses, this also supplies a list of the parameters that it will supply to its children when they are initialized by the bus, as well as default values for the parameters if specific values are not specified for the child in the machine-specific configuration file (here VX115_VEP , discussed below)

  • attach <device-name> at <bus-name>

    • Indicates the device is to be a child of a particular bus; this is used during autoconfiguration, when each bus probes for and initializes its children

  • file <source-file-path>

    • Indicates the specified file should be included in the compilation

The vx115.files configuration file has just the basic set of configuration options (buses, interrupts, timers and serial interface) needed to initially bring up the system. Additional lines would be added as support is added for each of the additional on-chip system peripherals.

Note that the system AHB and APB buses are attached to something called "mainbus". This is the root bus in NetBSD. The CPU itself (the ARM926 core) is attached to this as part of the standard ARM system configuration (specified in the file sys/arch/arm/conf/files.arm).

Board Configuration Files

These files provide configuration for features that are board-specific. Note that this includes things like startup code (since this depends on the specific memory selects used on a board),  as well as the exact specification of kernel features to be supported.

These files are placed in the directory sys/arch/evbarm/conf.

This specifies board-specific files to be included in the build, including:

  • CPU and integrated peripheral support, by including "arch/arm/vx115/files.vx115"

  • board machdep file, which contains some of the principal startup code for the board (see the section on initial startup for description of startup sequence)

  • softinterrupt support (just the generic ARM support is used here)

 Again, the particular file files.vx115_vep specifies the bare minimum to get the system going.

This file is used in building the system Makefile, and adds defines and extra specifications to the build. For us, it specifies the following.

  • Indicate first file in image (start code)

    SYSTEM_FIRST_OBJ=vx115_vep_start.o

  •  Indicate any final steps to perform on image after main image made

    SYSTEM_LD_TAIL_EXTRA+=; \

  • Specify make target additions to make above further kernel images

    EXTRA_KERNELS+= ${KERNELS:@.KERNEL.@${.KERNEL.}.bin@}

    SYSTEM_FIRST_SFILE=    ${THISARM}/vx115_vep/vx115_vep_start.S

    echo ${OBJCOPY} -S -O binary $@ $@.bin; \

    ${OBJCOPY} -S -O binary $@ $@.bin; \

    echo gzip \< $@.bin \> $@.bin.gz; \

    gzip < $@.bin > $@.bin.gz

    EXTRA_KERNELS+= ${KERNELS:@.KERNEL.@${.KERNEL.}.bin.gz@}

This specifies general build options for board.

  • Specify the machine and processor architecture

    machine evbarm arm

  • Include files.vx115_vep

  • Specify the value to be used for HZ, the system clock tick rate (default is HZ=100; we use 64 to get a better divisor for our 32kHz clock)

    options HZ=64

  • Specify kernel virtual and physical load locations, used in arch/evbarm/conf/Makefile.evbarm.inc

    makeoptions KERNEL_BASE_PHYS=0x24300000

  • Define board type and include makefile additions

    makeoptions BOARDTYPE="vx115_vep" makeoptions BOARDMKFRAG="${THISARM}/conf/mk.vx115_vep"

  • Specify interrupt implementation   

    options ARM_INTR_IMPL="<arch/arm/vx115/vx115_intr.h>"

  • Additional defines

    makeoptions KERNEL_BASE_VIRT=0xc0200000

This is the main config file, passed as argument to nbconfig. It's similar to the Linux .config file.

  • Include std.vx115_vep (which includes files.vx115_vep and specifies mk.vx115_vep)

  • All other main kernel options

  • Specifies instances of devices and buses defined in VX115_VEP, and locator parameter values to be passed to devices during autoconfiguration. The syntax of this device instance specification is described in the autoconfiguration section, and in detail in the NetBSD config(5) man page .

# The main bus device

mainbus0 at root

# The boot cpu

cpu0 at mainbus?

# Vx115 AHB and APB

vx115_ahb0 at mainbus?

vx115_apb0 at mainbus?

# On-chip interrupt controller

vx115_pic0 at vx115_apb? addr 0x700C1000 size 0x14c

# On-chip timer

vx115_clk0 at vx115_apb? addr 0x700C5000 size 0x68  intr 9

# On-chip serial UART

vx115_com0 at vx115_apb? addr 0x700E2000 size 0x7c  intr 19

Initialization

Initial startup

The first code executed in the NetBSD kernel is low-level startup code provided as part of the board-specific software. This code is specified by the SYSTEM_FIRST_OBJ and SYSTEM_FIRST_SFILE defines in the Makefile fragment mk.vx115_vep (discussed in the section on configuration ); the source file is vx115_vep_start.S in this project.

This assembly file is responsible for setting up the system so initial kernel execution can take place. It performs the following operations.

  • Copy the kernel to RAM

  • Set up initial MMU page tables, using all section (1 MB) maps to start. In particular:

    • Map all physical addresses to same virtual addresses (VA == PA), for any peripherals that may need this

    • Map the physical peripheral space to the corresponding virtual address range

      • Do this so can use bus mapping functions during setup in the init_arm( ) function

    • Map the RAM with VA == PA, but set to be cacheable

    • Map kernel virtual addresses to RAM physical addresses

  • Enable the MMU

The initial MMU mapping is really just to allow the kernel to start executing from its relocated location in RAM, before it sets up its real page tables in the init_arm( ) routine, and to allow it to access peripherals during configuration.

Machine startup

The file vx115_vep_machdep.c provides platform-specific initialization. This file defines several functions and data structures: an initarm( ) function that performs basic system initialization, a cpu_reboot( ) function that restarts the system on reboot, and a pmap_devmap structure that defines the peripheral virtual-to-physical memory mapping. Fortunately, the vast majority of these functions are common across platforms, so existing code can be used with minor modifications. The vx115_vep_machdep.c file was based on the smdk2800_machdep.c source, and used the cpu_reboot function verbatim and the  initarm function with minor changes.

struct pmap_devmap vx115_vep_devmap[ ]

  • array of specifications of virtual-physical memory mappings for peripherals

initarm( )

The initarm( ) function provides basic machine setup. It's called during initialization from arch/arm/arm32/locore.S, before the kernel main( ) function is called in kern/init_main.c. The initialization performed in this function is as follows.

  • call set_cpufuncs to set up basic cpu functions

  • call pmap_devmap_register(vx115_vep_devmap) so can use bus_space_map; the appropriate ranges must have been mapped in vx115_vep_start.S

  • call vx115_intr_bootstrap(PIC1_BASE_PHYS, PIC1_SIZE) to do early setup of interrupts needed for spl functionality

  • call consinit for early console output

  • set up bootconfig struct

  • set values of memory parameters

    • physical_start, physical_end, physical_freestart, physical_freeend, physmem

  • allocate pages for L1 page directory and L2 page tables

  • allocate pages for vectors, stacks, msgbuf

  • set up page tables

    • call pmap_link_l2pt to link L2 page tables into L1 page table

    • call pmap_chunk to map a chunk of memory into L1/L2 page tables, pmap_entry to map a single page, pmap_section to map a single section

  • call pmap_devmap_bootstrap to map static devices

  • call cpu_domains to set cpu domains

  • call setttb to set page table base

  • call cpu_tlb_flushID to flush TLB for I and D

  • call arm32_vector_init

  • call set_stackptr to set stack pointers

  • set data abort, prefetch abort, undefined instruction handlers

  • call uvm_setpagesize to set up page size

  • call uvm_page_physload to load physical pages into VM system, and add free pages to free page pool

  • call pmap_bootstrap to initialize the BSD page map structure from the ARM-specific page tables built above

  • return stack pointer

consinit( )

The consinit( ) function is used to initialize the serial console just sufficiently to allow console messages to be printed. Further initialization of the serial interface is deferred to the autoconfig process.

Bus Operations

A NetBSD system defines bus operations that are used to perform various memory operations. Each bus type specifies its set of operations in a bus_space struct generally referred to as a bus tag. This struct specifies many types of memory operations; NetBSD defines many generic operations, and  any bus-specific operations can be defined as needed. The vx115_io.c file defines a bus_space struct called vx115_bs_tag, as well as several bus-specific operations used in this struct.

Each bus instance selects in its attach( ) function an appropriate bus tag that defines the operations for that bus type. The bus then passes the bus tag to its children during autoconfiguration. The children use this tag in calls to the standard NetBSD memory functions such as bus_space_map( ), bus_space_read_4( ), etc.

Interrupt Handling

Interrupt handling is part of the processor-specific port, since the interrupt controller is part of the Vx115 SoC. The interrupt handling is contained in two files,  vx115_intr.h and vx115_intr.c , in the arch/arm/vx115 directory. Writing the interrupt handling code has two primary components: developing code to handle system priority level (spl) changes, and writing the actual interrupt handler. The SPL management is here because it involves enabling and disabling subsets of interrupts.

System Priority Level (SPL) Handlers

The SPL handlers are used by the system to change the system priority level (no surprise...), which amounts to masking interrupts corresponding to lower-priority sources. This is the principal mechanism that NetBSD uses to synchronize access to critical data structures in drivers by code running in thread context and code running in interrupt context: by preventing interrupts, the code is protected from access by another task (preemption is disabled with interrupts disabled) and from access by an interrupt handler (if the relevant interrupt has priority at or below the current level, which is the natural situation). It also protects timing-sensitive code against unexpected delays due to background (lower-priority) interrupt activity.

A block of code is protected by enclosing it between calls to spl<level>( ) and splx( ), where there's a separate spl<level> function for each of the available system levels - splserial( ), splclock( ), splnet( ), etc. The complete list can be found on the spl man page.

For example, to protect against network-related interrupts and those at lower priority, code like the following would be used:

int oldSPL = splnet(newSPL);

/* protected code here */

splx(oldCode);
        

This functions somewhat like the Linux functions local_irq_save( ) / local_irq_restore( ) and disable_irq( ) / enable_irq( ). It gives finer-grained control over which interrupts are disabled than local_irq_save( ), which just disables all interrupts, but provides more timing control than disable_irq, which disables just a single interrupt and thus doesn't suppress interrupts at lower priority.

The various programmer-accessible spl functions are implemented in terms of several low-level functions that are implemented for the platform.

int _splraise(int);
int _spllower(int);
void splx(int);

These functions do what you'd expect: they raise, lower, or set absolutely the system priority level by masking lower-priority interrupts and leaving those above enabled. The raise and lower functions are very similar to the splx( ) function, but first check to see if a change needs to be made. The splx( ) and _spllower( ) functions also check to see if any soft interrupts have been unmasked, as discussed below.)

There are really only two issues for the spl functions:

  • how to mask interrupts

  • which interrupts to mask for a specified priority level

The first issue is hardware dependent, and usually quite straightforward: the system interrupt controller usually has a mask register that allows interrupts to be masked or unmasked - writing a '1' in a bit enables the interrupt corresponding to that bit, while a '0' disables the interrupt, or some such approach. The second issue - determining which interrupts to mask at a specific system priority level - is only a bit more involved, but leads to some somewhat ugly code.

For the ARM architecture, most platforms specify the interrupts to enable at each system priority level using an array that maps the system priority level to the specific interrupt mask to be used at that level.

int vx115_pic_spl_mask[NIPL];
int vx115_pic_spl_soft_mask[NIPL];        

Here, NIPL is the number of system priority levels (defined in arch/evbarm/intr.h). The vx115_pic_spl_mask array then holds the mask to use for the interrupt controller corresponding to each system priority level. When the priority level is changed, the mask in the array corresponding to the new level is written to the interrupt controller:

vx115_splx(int new)
{
        
    ...

    current_spl_level = new;
        
    /* enable hardware interrupts appropriate to current spl level */
    vx115_write_pic(PIC_ENABLE_SET, vx115_pic_spl_mask[current_spl_level]);


    /* disable hardware interrupts appropriate to current spl level */
    vx115_write_pic(PIC_ENABLE_CLEAR, ~vx115_pic_spl_mask[current_spl_level]);
        
    ...
        
}
        

(The Vx115 interrupt controller has separate registers for enabling and disabling interrupts, hence the two write operations.)

The second array, vx115_pic_spl_soft_mask, is needed because in addition to masking hardware interrupts, the spl functions must also manage "soft" interrupts. Here, a soft interrupt isn't the same thing as an ARM software interrupt (the SWI instruction), but refers to a lower-priority routine used in conjunction with a higher-priority hardware interrupt handler. NetBSD soft interrupts are provided so a high-priority interrupt service routine can defer some lower-priority processing to avoid holding the system at a high priority level for an extended period. The device initialization code calls softintr_establish( ) to register the soft interrupt routine at one of four soft interrupt priority levels, and then the hardware interrupt handler calls softintr_schedule( ) when it wants the routine executed. Soft interrupts are treated just like hardware interrupts, being called from within the platform's interrupt dispatch code (discussed below), but after all pending hardware interrupts have been handled. 

Just as with hardware interrupts, soft interrupts are associated with a priority level, but the soft interrupt priorities are lower than any of the hardware interrupt priorities. Thus when the system is at a specific priority level, all soft interrupts at or below that level must be blocked. The vx115_pic_spl_soft_mask array values specify which soft interrupts should be enabled at each system priority level. The hardware interrupt dispatcher (discussed in more detail below) tests to see if any software interrupts are waiting to run, and executes them if any are currently enabled, i.e., unmasked according to the value in the vx115_pic_spl_soft_mask array corresponding to the current system level:

/* if there are software interrupts pending, process */
if (softint_pending & vx115_pic_spl_soft_mask[current_spl_level])
    vx115_do_pending();
        

Probably the best way to view the soft interrupt masks is as extensions of the hardware interrupt masks: it's as if the mask register has 64 bits instead of just 32 bits, with the lower 32 corresponding to hardware interrupts and the upper 32 corresponding to software interrupts (though only four bits are used at present). Some other evbarm platforms utilize unused bits within the hardware mask to represent software interrupts, avoiding the need for "extension" of the mask field. However, for the Vx115, all 32 bits in the hardware mask correspond to existing hardware interrupt sources, so the "extension" masks were needed. This also helps to logically separate the hardware interrupts from the software interrupts (but complicates the process of initializing the masks).

How the arrays are initialized and updated is the messy part. I used code for vx115_pic_init_interrupt_masks( ) and vx115_update_intr_masks( ) that has seemingly been propagated from system to system, and modified it to handle initialization of the dual arrays. The process of array initialization and modification is the following. Note that a '1' bit in a mask indicates that the interrupt corresponding to the bit is to be enabled, and a '0' indicates it is to be disabled.

vx115_pic_init_interrupt_masks( )

  • called at system init time (from the interrupt controller attach function)

  • sets up the initial soft interrupt hierarchy; this affects only the soft-interrupt mask array, as soft interrupts are the lowest priority in the system and masking at a soft interrupt priority level won't affect hardware interrupts (at least not on our system)

vx115_update_intr_masks( )

  • called whenever a new hardware interrupt is registered, with the irq number and associated priority level as arguments

  • select appropriate array for modification: if the irq number is below 32, it's a hardware interrupt, so the hardware interrupt array is selected, while if the irq number is 32 or above, it's a software interrupt and the software interrupt is selected

  • clear the bit corresponding to the supplied irq number in all the masks in the array for the specified priority and below, and set the bit in the masks for higher priorities; in this way the interrupt will be disabled at this and higher system priority levels, and enabled at lower system priority levels

  • do some "consistency magic": make sure that the settings at certain priority levels include or exclude the settings at other levels. This was copied verbatim from another platforms, and should probably be revisited...

  • reset the interrupt controller mask register to reflect the change

Interrupt Registration and Dispatching

Interrupt handlers are registered (usually in device attach routines) using vx115_intr_establish( ). The platform interrupt subsystem maintains an array of the handlers registered for each irq number. The Vx115 platform currently allows only one handler per irq; however, there's nothing preventing the use of a linked list per irq to allow the registration of multiple handlers per irq, if desired. Each handler is registered along with a "cookie", an arbitrary void* argument that's supplied to it when it's run, and the priority level for the interrupt. The vx115_intr_establish( ) routine calls vx115_update_intr_masks( ), which sets the system priority level masks to take into account the new irq at the specified priority level.

vx115_intr_establish( )

  • store supplied handler, cookie, and priority in handler array entry for specified irq number

  • call vx115_update_intr_masks( ) so SPL masks will reflect new irq at specified priority level

The main interrupt dispatcher, vx115_irq_dispatcher( ), is called from the low-level IRQ vector assembly routine to dispatch interrupts to the interrupt handlers corresponding to the currently asserted interrupts. The dispatcher has a simple structure.

vx115_irq_dispatcher( )

  • save current spl (note that interrupts are enabled - not globally disabled - when the interrupt handler is called)

  • while interrupt status register is nonzero

    • select one of the asserted irqs

    • raise the spl to the level corresponding to the selected irq

    • call the handler registered for that irq

    • clear the interrupt in the controller

    • restore the saved spl

  • if any soft interrupts are asserted, dispatch to associated soft interrupt handlers

Soft Interrupt Registration and Dispatching

Soft interrupts are registered through a call to the generic ARM soft interrupt routine softintr_establish( ), which manages the list of soft interrupts. Note that the generic soft interrupt handling code specifically supports the registration of multiple handlers for each soft interrupt level. The platform-specific code therefore doesn't play a role in soft-interrupt registration.

Soft interrupts are handled much like hard interrupts: when a soft interrupt is pending and the system priority level is lower than the priority of the interrupt, any associated handlers are executed. The platform maintains a static bitmask named softint_pending to determine which soft interrupts are currently asserted, just like the hardware interrupt status register. Of course, since this is just a software variable, there's no hardware mechanism for informing the system when the soft interrupt status has changed, i.e., when a new bit has been set in the softint_pending variable. The system thus must test the variable at appropriate times to determine if there's a soft interrupt waiting:

  • at the end of the hardware dispatcher, since a soft interrupt may have been scheduled by one of the hardware interrupt handlers

  • when the system priority is lowered, since there may be a soft interrupt that has been "masked" because the system priority level was too high for it to run

  • when a new soft interrupt is scheduled

In each of these places, there's a call to vx115_do_pending( ) to execute any asserted unmasked soft interrupt(s).

_setsoftintr( )

A soft interrupt is scheduled for execution through a call to the generic ARM soft interrupt routine softintr_schedule( ), and this calls the platform-specific routine _setsoftintr( ). For the vx115, this routine is quite simple:

  • set the appropriate bit in a static variable indicating which soft interrupts are asserted

  • if the system priority level is lower than the priority of the associated soft interrupt, call vx115_do_pending( ) to execute the asserted soft interrupt(s)

vx115_do_pending( )

 The vx115_do_pending( ) routine is called to execute any pending soft interrupts. It's called at specific times, as discussed above. The routine is simple:

  • while softint_pending masked by the current vx115_pic_spl_soft_mask is nonzero (has bits set)

    • check each of the four available soft interrupts; if it's asserted and its priority is above the system priority level

      • clear the associated bit in softint_pending

      • calls the handlers using the generic ARM soft interrupt function softintr_dispatch( ).

Initialization

The interrupt controller is really just a peripheral on the Vx115's peripheral bus (APB), so it's treated like any other peripheral as far as initialization is concerned. The interrupt controller registers a match and attach function with the autoconfig system using the standard CFATTACH_DECL macro.

CFATTACH_DECL(vx115_pic, sizeof(struct vx115_pic_softc), vx115_pic_match, vx115_pic_attach, NULL, NULL);
      

However, the SPL services are called early in the boot process, by the debug printf statements, before the autoconfig stuff is run. Even though the SPL functions just set the interrupt mask, this involves a register write, which uses the bus-write functions associated with the interrupt controller's parent bus, which isn't assigned until the controller's attach function is called. So there's an early-initialization function, vx115_intr_bootstrap( ),  that's called to initialize just the bus-operations field in the softc struct so the SPL functions will work (even though they'll have no effect since the interrupt controller itself isn't initialized). Note that other platforms just bypass the whole autoconfig process and "hardwire" the interrupt controller.

System Timer

The system timer source code is in the source file arch/arm/vx115_clk.c , and provides the basic NetBSD system time reference. The main functionality is as a driver for one of the hardware timers on the SoC that responds to timer interrupts by calling appropriate system functions to update the time. The timer source also provides several other functions for services such as delays.

Initialization

The driver provides the usual autoconfig match/attach functions to initialize the bus operations structure.

System clock

The system clock provides ticks at the rate defined by the configuration define HZ. The default value for HZ is 100, giving a system tick period of 10 ms. However, because the Vx115 timer uses a 32 kHz clock as a source, for this platform HZ is defined as 64 to give an integer value for the hardware timer max count value.

The system clock is started through a call to cpu_initclocks( ) from within the initclocks( ) function in kern/kern_clocks.c. This starts the process of timer interrupt generation.

cpu_initclocks( )

  • register the timer interrupt handler

  • configure the hardware timer to start generating interrupts at the rate HZ

vx115_clk_intr( )

  • handler for timer interrupts

  • call hardclock( ) so system can update time

delay( )

  • loop until at least the specified number of microseconds has elapsed

microtime( )

  • return the time to the nearest microsecond

Time-of-day functions

resettodr( )

Serial Driver

The serial driver provides the console interface that's used to deliver debug messages and interact with the system. The driver is contained in the source file vx115_com.c , in the arch/arm/vx115 subdirectory. The driver provides several interfaces for use by various NetBSD subsystems: the standard autoconfig functions, a set of character device functions, and functions supporting console functionality.

Initialization

The serial driver provides the usual autoconfig match/attach functions:

  • vx115_com_match

  • vx115_com_attach

as well as the usual autoconfig attachment declaration:

CFATTACH_DECL(vx115_com, sizeof(struct vx115_com_softc), vx115_com_match, vx115_com_attach, NULL, NULL);

tty functions

These functions are assigned to fields in the serial driver's softc structure's tty field. The tty structure is allocated during the attach routine, and then partially initialized by assigning these function pointers. The NetBSD tty subsystem handles further initialization (it obtains a pointer to the tty struct using the vx115_com_tty( ) method of the cdevsw struct). These functions handle the device-specific tty operations.

  • vx115_com_start

    • used by the tty subsystem to start output

      • assigned by the NetBSD tty subsystem during initialization to be the t_linesw->l_start function of the tty struct, which is called to transmit from the tty buffer

    • get output parameters for the softc struct (the byte count and output position in the tty output queue), for use by the interrupt routines 

    • mark the driver as busy so the softc parameters won't be changed until transmission is done

    • turn on transmit interrupts so output will begin

      • actual output is performed from within interrupt routines

  • vx115_com_hwiflow

    • used to provide HW flow control; not used on this platform

  • vx115_com_param

    • used for termios parameter changes (baud rate, etc.)

cdevsw functions

These are the basic interface functions for character drivers in NetBSD.

  • vx115_com_open

  • vx115_com_close

  • vx115_com_read

  • vx115_com_write

  • vx115_com_ioctl

  • vx115_com_stop

  • vx115_com_tty

  • vx115_com_poll

consdev functions

The consdev struct contains pointers to methods used for console input and output. These functions are used for low-level operations like kernel print statements. For example, the kernel printf function uses the following call sequence:

printf -> kprintf -> KPRINTF_PUTCHAR -> putchar -> v_putc -> cnputc -> cn_tab.cn_putc = vx115_com_cnputc

The functions are very low-level routines that just directly write to or read from the serial interface.

  • vx115_com_cngetc

    • spin until there's a character in the receive FIFO, then read and return the character

  • vx115_com_cnputc

    • spin until there's room in the transmit FIFO, then write the supplied character to the FIFO

  • vx115_com_cnpollc

Interrupt routines

The interrupt routines handle the actual transfer of data between the driver's buffers and the serial interface FIFO. The hardware routines transfer between the hardware FIFO and the buffers, while the soft interrupt routines interpret the data and finish transmission as needed.

Transmit

Transmission is done primarily through the hardware interrupt routine, which is called whenever the transmit FIFO level falls below a threshold. The soft interrupt routine is scheduled only when the transmission is complete, and just synchronizes the tty buffer pointers to account for the transmitted data and checks to see if new data has become available for transmission, restarting transmission if so. 

vx115_tx_hard
  • while the transmit FIFO's not full

    • copy data from the tty output buffer to the FIFO and update the softc count and buffer pointer

  • if the count is 0, transmission's complete

    • turn off the transmit interrupts

    • clear the sc_tx_busy flag, which will cause the soft interrupt routine to run

vx115_com_txsoft
  • if the sc_tx_busy flag is clear, transmission is complete

    • call ndflush to update the tty buffer pointers to account for the data transmitted

    • call the tty t_linesw->l_start method to initiate a new transmission if new data is available

      • this just directly calls the driver's vx115_com_start method

Receive

vx115_rx_hard
  • while the receive FIFO's not empty and the driver receive buffer's not full

    • copy data from the receive FIFO to the driver's receive buffer and update the softc count and buffer pointer

  • if the receive buffer's full, turn off receive interrupts

vx115_rx_soft
  • while the receive buffer has characters

    • read next character from receive buffer, check for framing, parity errors

    • call the tty method t_linesw->l_rint to deliver the character to the tty layer

  • update receive buffer pointers and count

  • turn on receive interrupts if they're off and there's now room in buffer

Early initialization

Because it's desirable to have serial output early in the boot process for debugging purposes during execution of the initarm( ) routine, there's an early initialization of the serial driver in the init_arm( ) function.

Character and Block Device Major Number Registration

There's one additional step involved in getting devices supported by NetBSD, and that's to include their device major numbers in the file arch/arm/conf/majors.arm32. This involves including a line in this file of the form

device-major <name> char <num> [block <num>] [<rule>]

The name must be the prefix of the cdevsw or bdevsw struct name in the source file - or rather, the corresponding character device switch structure variable must be obtained by appending "_cdevsw" to the name used in this file (and block devices should append "_bdevsw").

For the vx115_com serial device here, the cdevsw struct is named

vx115_com_cdevsw

in the source file vx115_com.c, and the corresponding line added to the majors.arm32 file is

device-major    vx115_com        char 110        vx115_com

The major number 110 was selected arbitrarily as an available (unused) major number.

This process is described in detail in the document "The Auto-Generation Block/Character Device Switch Tables by config(8)" .

Supporting the ARM926EJ-S Variant

The processor used in this SoC is a variant of the generic ARM9 core, the ARM926EJ-S. A few tweaks were needed in the generic ARM configuration code to provide support for this variant. Note that this is nothing like providing full support for any new features in a variant core, but just getting the core to be recognized as an ARM9 variant by the system. Note also that the latest version of NetBSD fully supports this processor, so these mods are no longer required.

arch/arm/arm32/cpu.c

  • an addition was made to the CPU identifier list cpuids[]:

{ CPU_ID_ARM926EJS, CPU_CLASS_ARM9ES, "ARM926EJ-S", generic_steppings }
  • a change was propagated from a more recent kernel version to wtnames[14]:

"**unknown 14**" to "write-back-locking-C"

arch/arm/include/armreg.h

  • the identifier for this ARM9 variant was added to CPU arch type defines:

CPU_ID_ARCH_V5TEJ   0x00060000
  • the ID for this variant was added to CPU ID defines:

#define CPU_ID_ARM926EJS    0x41069260

Observations and Comparison with Linux Porting Efforts

Porting NetBSD to this SoC turned out to be a very straightforward effort, with very few "potholes". I've done a few Linux ports before, and the adaptation of NetBSD went at least as smoothly as any of the Linux efforts. In fact, I found NetBSD to have several advantages that made it substantially easier to port to a new platform than Linux, especially for a "newbie" like myself with no previous NetBSD porting experience.

  • NetBSD builds its own NetBSD-specific toolchain as part of the build process. With Linux, you need to acquire or build yourself a toolchain that's appropriate for the processor architecture and C library variant you're using. While there are a number of nice aids for this process (e.g., Dan Kegel's crosstool), it can still be a significant pain - for example, use the wrong binutils variant and things break in mysterious ways. With NetBSD, just run the included build script and the toolchain's there.

  • The NetBSD source includes the full system source - kernel and applications. While this can reduce the flexibility somewhat (for example, there's only one C library provided), it makes porting and building much easier. With Linux, one of the main initial tasks is pulling together the source for the components you want in your root filesystem, and creating the build tree and scripts to create the desired filesystem images. On the flip side, however, there are some Linux distributions available (both free and commercial) that provide full system source and a build environment - for example, the Snapgear distribution, or the MontaVista offering - and one advantage these have is that they generally provide a broad selection of library and application choices, and tend to be more easily configurable than the standard NetBSD distribution in selecting the components to be included.

However, NetBSD has a few drawbacks (some significant), particularly for use in embedded systems.

  • There is at the time of this writing no Flash Translation Layer or dedicated Flash filesystem that provides journaling or wear-leveling to support Flash memory (NOR or NAND). You need to have the root filesystem on a disk or embedded as a RAM filesystem in the kernel image (or mounted via NFS). Since the embedded space is extremely Flash-centric, this is a significant hole. There are apparently efforts to correct this, but in early 2007 it's not there yet. (With the recent release of Apple's iPhone, which runs OSX, whose kernel, Darwin, is BSD-based, and which uses Flash as its storage medium, it looks like there is some solution that exists, but whether this is open-source at this point I don't know.)

  • The NetBSD kernel doesn't provide the option to build itself as a self-extracting compressed image (a zImage in Linux parlance). The kernel image can be built as a compressed image, but it relies on the bootloader to be able to uncompress it. However, NetBSD does provide a utility called gzboot, which decompresses a gzipped kernel to RAM and jumps to its entry point. This provides essentially the same functionality as the Linux zImage. (Thanks to Allen Briggs for the gzboot tip.)

Both Linux and NetBSD benefit tremendously from being open-source systems. As noted in the introduction, it's because of the work of others, and their openness in sharing it, that NetBSD and Linux and most other open-source systems are quite straightforward to adapt to new platforms. And there's nothing quite so satisfying as finally seeing on your console (debug messages and warts and all):

NetBSD/evbarm (Vx115 VEP) booting...

physmemory: 3840 pages at 0x24100000 -> 0x24ffffff

Allocating page tables

freestart = 0x24100000, free_pages = 512 (0x00000200)

IRQ stack: p0x242f2000 v0xc01f2000

ABT stack: p0x242f1000 v0xc01f1000

UND stack: p0x242f0000 v0xc01f0000

SVC stack: p0x242ee000 v0xc01ee000

Creating L1 page table at 0x242fc000

Mapping kernel

pmap_map_chunk: pa=0x24300000 va=0xc0200000 size=0x183000 resid=0x183000 prot=0x3 cache=1

SLLLLLLLLPPP

pmap_map_chunk: pa=0x24483000 va=0xc0383000 size=0x56d000 resid=0x56d000 prot=0x3 cache=1

PPPPPPPPPPPPPLLLLLLLSSSSLLLLLLLLLLLLLLL

Constructing L2 page tables

pmap_map_chunk: pa=0x242f2000 va=0xc01f2000 size=0x1000 resid=0x1000 prot=0x3 cache=1

P

pmap_map_chunk: pa=0x242f1000 va=0xc01f1000 size=0x1000 resid=0x1000 prot=0x3 cache=1

P

pmap_map_chunk: pa=0x242f0000 va=0xc01f0000 size=0x1000 resid=0x1000 prot=0x3 cache=1

P

pmap_map_chunk: pa=0x242ee000 va=0xc01ee000 size=0x2000 resid=0x2000 prot=0x3 cache=1

PP

pmap_map_chunk: pa=0x242fc000 va=0xc01fc000 size=0x4000 resid=0x4000 prot=0x3 cache=2

PPPP

pmap_map_chunk: pa=0x242fb000 va=0xc01fb000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242fa000 va=0xc01fa000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242f9000 va=0xc01f9000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242f8000 va=0xc01f8000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242f7000 va=0xc01f7000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242f6000 va=0xc01f6000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242f5000 va=0xc01f5000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

pmap_map_chunk: pa=0x242f4000 va=0xc01f4000 size=0x1000 resid=0x1000 prot=0x3 cache=2

P

devmap: 70000000 -> 700fffff @ fd000000

pmap_map_chunk: pa=0x70000000 va=0xfd000000 size=0x100000 resid=0x100000 prot=0x3 cache=0

S

devmap: fc000000 -> fc1fffff @ fc000000

pmap_map_chunk: pa=0xfc000000 va=0xfc000000 size=0x200000 resid=0x200000 prot=0x3 cache=0

SS

freestart = 0x249f0000, free_pages = 1552 (0x610)

switching to new L1 page table  @0x242fc000...done!

bootstrap done.

init subsystems: stacks vectors undefined page pmap done.

Loaded initial symtab at 0xc038b3f4, strtab at 0xc03b81b0, # entries 11404

abcdefg

Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005

    The NetBSD Foundation, Inc.  All rights reserved.

Copyright (c) 1982, 1986, 1989, 1991, 1993

    The Regents of the University of California.  All rights reserved.

pmap_postinit: Allocated 35 static L1 descriptor tables

NetBSD 3.0.1 (VX115_VEP) #8: Tue Jan  2 19:17:21 EST 2007

        root@localhost.localdomain:/home/agere/vx115_netbsd/build/usr/src/sys/arch/evbarm/compile/obj/VX115_VEP

total memory = 15360 KB

avail memory = 5404 KB

mainbus0 (root)

cpu0 at mainbus0: ARM926EJ-S rev 5 (ARM9E-S core)

cpu0: WB enabled EABT

cpu0: 16KB/32B 4-way Instruction cache

cpu0: 16KB/32B 4-way write-back-locking-C Data cache

vx115_ahb0 at mainbus0

vx115_apb0 at mainbus0

vx115_clk0 at vx115_apb0 addr 0x700c5000-0x700c5067 intr 9

vx115_pic0 at vx115_apb0 addr 0x700c1000-0x700c114b

vx115_com0 at vx115_apb0 addr 0x700e2000-0x700e207b intr 19

vx115_com0: major = 110: console

md0: internal 5000 KB image area

boot device: <unknown>

root on md0a dumps on md0b

mountroot: trying msdos...

mountroot: trying ffs...

root file system type: ffs

WARNING: CHECK AND RESET THE DATE!

init: copying out flags `-s' 3

init: copying out path `/sbin/init' 11

Jan  3 00:17:46 init: /etc/pwd.db: No such file or directory

Enter pathname of shell or RETURN for /bin/sh:

# ls

bin     dev     etc     lib     libexec sbin    usr

# ps

ps: warning: /var/run/dev.db: No such file or directory

PID TTY STAT    TIME COMMAND

  5 ?   Ss   0:00.13 -sh

  7 ?   R+   0:00.02 ps

#

Comments/questions: jsevy@cs.drexel.edu

Appendix: Source Files

sys/arch/arm/vx115

files.vx115

#
# Configuration info for Agere Vx115 CPU support
#

file    arch/arm/arm32/irq_dispatch.S

# standard memory-mapped bus ops
file    arch/arm/vx115/vx115_io.c        

# vx115 onchip buses: have ahb, apb
# vx115 ahb
device  vx115_ahb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic
attach  vx115_ahb at mainbus
file    arch/arm/vx115/vx115_ahb.c        

# vx115 apb
device  vx115_apb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic
attach  vx115_apb at mainbus
file    arch/arm/vx115/vx115_apb.c


# clock device
device  vx115_clk 
attach  vx115_clk at vx115_apb
file    arch/arm/vx115/vx115_clk.c


# interrupt controller
device  vx115_pic 
attach  vx115_pic at vx115_apb
file    arch/arm/vx115/vx115_intr.c


# vx115 serial device
device  vx115_com: tty
attach  vx115_com at vx115_apb
file    arch/arm/vx115/vx115_com.c

vx115_apb.c

/*
 * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu>
 * All rights reserved.
 * 
 * Based on ixp12x0.c
 *
 * Copyright (c) 2002, 2003
 *    Ichiro FUKUHARA <ichiro@ichiro.org>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Ichiro FUKUHARA.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ixp12x0.c,v 1.13 2004/08/30 15:05:16 drochner Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <uvm/uvm.h>

#include <machine/bus.h>

#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_var.h>

#include <locators.h>


#define DEBUG_APB

#ifdef DEBUG_APB
#define DPRINTF(fmt...)  printf(fmt)
#else
#define DPRINTF(fmt...)  
#endif


static int vx115_apb_match(struct device *parent, struct cfdata *cf, void *aux);
static void vx115_apb_attach(struct device *parent, struct device *self, void *aux);
static int vx115_apb_search(struct device * parent, struct cfdata * cf, void *aux);


CFATTACH_DECL(vx115_apb, sizeof(struct vx115_softc), vx115_apb_match, vx115_apb_attach, NULL, NULL);



static int
vx115_apb_print(void *aux, const char *name)
{
    struct vx115_attach_args *sa = (struct vx115_attach_args *) aux;

    if (sa->sa_size)
        aprint_normal(" addr 0x%lx", sa->sa_addr);
    if (sa->sa_size > 1)
        aprint_normal("-0x%lx", sa->sa_addr + sa->sa_size - 1);
    if (sa->sa_intr != VX115_APBCF_INTR_DEFAULT)
        aprint_normal(" intr %d", sa->sa_intr);
    if (sa->sa_index != VX115_APBCF_INDEX_DEFAULT)
        aprint_normal(" unit %d", sa->sa_index);
    aprint_normal("\n");
    
    return (UNCONF);
}

static int
vx115_apb_match(struct device *parent, struct cfdata *cf, void *aux)
{
    DPRINTF("vx115_apb_match\n");
    
    return (1);
}


void
vx115_apb_attach(struct device *parent, struct device *self, void *aux)
{
    struct vx115_softc *sc = (struct vx115_softc *) self;
    
    DPRINTF("\nvx115_apb_attach\n");
    
    /* assign bus tag: standard vx115 bus ops */
    sc->sc_iot = &vx115_bs_tag;

    /* attach devices */
    config_search(vx115_apb_search, self, NULL);
    
    return;
}


int
vx115_apb_search(struct device * parent, struct cfdata * cf, void *aux)
{
    struct vx115_softc *sc = (struct vx115_softc *) parent;
    struct vx115_attach_args aa;

    DPRINTF("vx115_apb_search\n");
    
    aa.sa_iot   = sc->sc_iot;
    aa.sa_addr  = cf->cf_loc[VX115_APBCF_ADDR];
    aa.sa_size  = cf->cf_loc[VX115_APBCF_SIZE];
    aa.sa_index = cf->cf_loc[VX115_APBCF_INDEX];
    aa.sa_intr  = cf->cf_loc[VX115_APBCF_INTR];

    if (config_match(parent, cf, &aa))
        config_attach(parent, cf, &aa, vx115_apb_print);

    return 0;
}

vx115_clk.c

/*$NetBSD: epclk.c,v 1.4 2005/02/26 12:00:52 simonb Exp $*/

/*
 * Agere Vx115 clock functions
 * Copyright (c) 2006, Jon Sevy <jsevy@cs.drexel.edu>
 * 
 * Based on epclk.c
 * Copyright (c) 2004 Jesse Off
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *This product includes software developed by the NetBSD
 *Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Driver for the Vx115 clock tick.
 * We use Timer 1 for the system clock
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vx115_clk.c,v 1.4 2005/02/26 12:00:52 simonb Exp $");

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/device.h>

#include <dev/clock_subr.h>

#include <machine/bus.h>
#include <machine/intr.h>

#include <arm/cpufunc.h>
#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_var.h>

#include <opt_hz.h>     /* for HZ */


#define DEBUG_CLK
#ifdef DEBUG_CLK
#define DPRINTF(fmt...)  printf(fmt)
#else
#define DPRINTF(fmt...)  
#endif


static int vx115_clk_match(struct device *, struct cfdata *, void *);
static void vx115_clk_attach(struct device *, struct device *, void *);

void rtcinit(void);

/* callback functions for intr_functions */
static int vx115_clk_intr(void* arg);

struct vx115_clk_softc {
    struct device       device;
    bus_space_tag_t     sc_iot;
    bus_space_handle_t  sc_ioh;
    int                 sc_intr;
};

static struct vx115_clk_softc *vx115_clk_sc = NULL;
static struct timeval lasttv;

    
    
/* Match value for clock timer; running at 16kHz, want HZ ticks per second  */
/* BTW, we use HZ == 64 or HZ == 128 so have a nice divisor                 */
/* NOTE: don't change there without visiting the functions below which      */
/* convert between timer counts and microseconds                            */
#define VX115_CLK_SOURCE        (32768/2)
#define VX115_TIMER_LATCH       (VX115_CLK_SOURCE / HZ)
#define VX115_USEC_PER_TICK     (1000000 / HZ)         

static uint32_t vx115_timer_count_to_usec(uint32_t count)
{
    uint32_t result;
    
    /* convert specified number of ticks to usec, and round up  */
    /* note that with 16 kHz tick rate, maximum count will be   */
    /* 256 (for HZ = 64), so we won't have overflow issues      */ 
    result = (1000000 * count) / (VX115_CLK_SOURCE);
    
    if ((result*VX115_CLK_SOURCE) != (count*1000000))
    {
        /* round up */
        result += 1;
    }
    
    return result;
}

/* This may only be called when overflow is avoided; typically, */
/* it will be used when usec < VX115_USEC_PER_TICK              */
static uint32_t vx115_usec_to_timer_count(uint32_t usec)
{
    uint32_t result;
    
    /* convert specified number of usec to timer ticks, and round up */
    result = (VX115_CLK_SOURCE * usec) / 1000000;
    
    if ((result*1000000) != (usec*VX115_CLK_SOURCE))
    {
        /* round up */
        result += 1;
    }
    
    return result;
    
}


/* macros to simplify writing to the timer controller */
#define vx115_read_clk(offset)           bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset)
#define vx115_write_clk(offset, value)   bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value)



CFATTACH_DECL(vx115_clk, sizeof(struct vx115_clk_softc), vx115_clk_match, vx115_clk_attach, NULL, NULL);



static int
vx115_clk_match(struct device *parent, struct cfdata *match, void *aux)
{
    DPRINTF("vx115_clk_match\n");
    
    return 2;
}

static void
vx115_clk_attach(struct device *parent, struct device *self, void *aux)
{
    struct vx115_clk_softc *sc = (struct vx115_clk_softc*) self;
    struct vx115_attach_args *sa = (struct vx115_attach_args*) aux;
    
    DPRINTF("vx115_clk_attach\n");
    
    sc->sc_iot = sa->sa_iot;
    sc->sc_intr = sa->sa_intr;
    
    if (vx115_clk_sc == NULL)
        vx115_clk_sc = sc;

    /* map bus space and get handle */
    if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh) != 0)
        panic("%s: Cannot map registers", self->dv_xname);    
    
}

/*
 * vx115_clk_intr:
 *
 *Handle the hardclock interrupt.
 */
static int
vx115_clk_intr(void *arg)
{
    struct vx115_clk_softc *sc = vx115_clk_sc;

    /* make sure it's the kernel timer that generated the interupt  */
    /* need to do this since the interrupt line is shared by the    */
    /* other interval and PWM timers                                */
    if (vx115_read_clk(TIM_TMRSR) & TIM_TMRSR_I1S)
    {
        /* clear the interrupt bit in the timer */
        vx115_write_clk(TIM_TMRSR, TIM_TMRSR_I1S);
        
        /* call the kernel timer handler */
        hardclock((struct clockframe*) arg);
#if 0        
        if (hardclock_ticks % HZ == 0)
            printf("time %i sec\n", hardclock_ticks/HZ);
#endif
        return 1;
    }
    else
    {
        /* it's one of the other timers; just pass it on */
        return 0;
    }
    
}

/*
 * setstatclockrate:
 *
 *Set the rate of the statistics clock.
 *
 *We assume that hz is either stathz or profhz, and that neither
 *will change after being set by cpu_initclocks().  We could
 *recalculate the intervals here, but that would be a pain.
 */
void
setstatclockrate(int hz)
{
    /* use hardclock */
}

/*
 * cpu_initclocks:
 *
 *Initialize the clock and get it going.
 */
void
cpu_initclocks(void)
{
    struct vx115_clk_softc *sc = vx115_clk_sc;
    
    stathz = profhz = 0;
    
    /* set up and enable interval timer 1 as kernel timer, */
    /* using 32kHz clock source */
    /* Note that since clock rate divisor is 2 for TMRCNTRATE = 0   */
    /* the actual clock rate is 16kHz                               */
    vx115_write_clk(TIM_TMRCR, (TIM_TMRCR_ITE1 | TIM_TMRCR_ITC1_32K));

    /* register interrupt handler */
    vx115_intr_establish(sc->sc_intr, IPL_CLOCK, vx115_clk_intr, NULL);
    vx115_configure_irq(sc->sc_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH);
    vx115_enable_irq(sc->sc_intr);
    
    /* Enable interrupts from timer 1 */
    vx115_write_clk(TIM_TMRIE, TIM_TMRIE_I1E);

    /* load the max register; when this happens, we're running  */
    vx115_write_clk(TIM_ITMAXC1, VX115_TIMER_LATCH);
}




/*
 * microtime:
 *
 *Fill in the specified timeval struct with the current time
 *accurate to the microsecond.
 */
void
microtime(register struct timeval *tvp)
{
    struct vx115_clk_softc *sc = vx115_clk_sc;
    u_int oldirqstate;
    u_int current_count;
    
#ifdef DEBUG
    if (vx115_clk_sc == NULL) {
        printf("microtime: called before initialize vx115_clk\n");
        tvp->tv_sec = 0;
        tvp->tv_usec = 0;
        return;
    }
#endif

    oldirqstate = disable_interrupts(I32_bit);
    
    /* get current timer count */
    current_count = vx115_read_clk(TIM_ITCNT1);

    /* Fill in the timeval struct. */
    *tvp = time;

    /* Refine the usec field using current timer count */
    tvp->tv_usec += vx115_timer_count_to_usec(VX115_TIMER_LATCH - current_count);
    
    /* Make sure microseconds doesn't overflow. */
    while (__predict_false(tvp->tv_usec >= 1000000)) 
    {
        tvp->tv_usec -= 1000000;
        tvp->tv_sec++;
    }

    /* Make sure the time has advanced. */
    if (__predict_false(tvp->tv_sec == lasttv.tv_sec && tvp->tv_usec <= lasttv.tv_usec)) 
    {
        tvp->tv_usec = lasttv.tv_usec + 1;
        if (tvp->tv_usec >= 1000000) 
        {
            tvp->tv_usec -= 1000000;
            tvp->tv_sec++;
        }
    }

    lasttv = *tvp;
    
    restore_interrupts(oldirqstate);
}



extern int hardclock_ticks;
static void tdelay(unsigned int ticks)
{
    u_int32_t   start, end, current;
    
    current = hardclock_ticks;
    start = current;
    end = start + ticks;
    
    /* just loop for the specified number of ticks */
    while (current < end)
        current = hardclock_ticks;
}

static void udelay(unsigned int usec)
{
    struct vx115_clk_softc *sc = vx115_clk_sc;
    u_int32_t start, end, current;
    
    current = vx115_read_clk(TIM_ITCNT1);
    start = current;
    end = start - vx115_usec_to_timer_count(usec);
    if (end <= 0)
    {
        /* need for counter to wrap; adjust end value, and  */
        /* wait for current to be below end but above start */
        end += VX115_TIMER_LATCH;
        while (!((current <= end) && (current > start)))
            current = vx115_read_clk(TIM_ITCNT1);
    }
    else
    {
        /* just wait until count value is at or below end value */
        while (current > end)
            current = vx115_read_clk(TIM_ITCNT1);
    }
}



/*
 * delay:
 *
 *Delay for at least N microseconds. Note that due to our coarse clock,
 *  our resolution is 61 us. But we round up so we'll wait at least as
 *  long as requested.
 */
void
delay(unsigned int usec)
{

#ifdef DEBUG
    if (vx115_clk_sc == NULL) {
        printf("delay: called before start vx115_clk\n");
        return;
    }
#endif

    if (usec >= VX115_USEC_PER_TICK)
    {
        printf("delay: called with large value (requested delay = %i us)\n", usec);
        
        /* have more than 1 tick; just do in ticks */
        unsigned int ticks = usec/VX115_USEC_PER_TICK;
        if (ticks*VX115_USEC_PER_TICK != usec)
            ticks += 1;
            
        tdelay(ticks);
    }
    else
    {
        /* less than 1 tick; can do as usec */
        udelay(usec);
    }
    
}

todr_chip_handle_t todr_handle;

/*
 * todr_attach:
 *
 *Set the specified time-of-day register as the system real-time clock.
 */
void
todr_attach(todr_chip_handle_t todr)
{
    
    if (todr_handle)
        panic("todr_attach: rtc already configured");
    todr_handle = todr;
}

/*
 * inittodr:
 *
 *Initialize time from the time-of-day register.
 */
#define MINYEAR      2003   /* minimum plausible year */

void
inittodr(time_t base)
{
    time_t deltat;
    int badbase;
    
    if (base < (MINYEAR - 1970) * SECYR) 
    {
        printf("WARNING: preposterous time in file system\n");
        /* read the system clock anyway */
        base = (MINYEAR - 1970) * SECYR;
        badbase = 1;
    } 
    else
        badbase = 0;
    
    if (todr_handle == NULL 
        || todr_gettime(todr_handle, (struct timeval *)&time) != 0 
        || time.tv_sec == 0) 
    {
        /*
         * Believe the time in the file system for lack of
         * anything better, resetting the TODR.
         */
        time.tv_sec = base;
        time.tv_usec = 0;
        if (todr_handle != NULL && !badbase) 
        {
            printf("WARNING: preposterous clock chip time\n");
            resettodr();
        }
        goto bad;
    }
    
    if (!badbase) 
    {
        /*
         * See if we gained/lost two or more days; if
         * so, assume something is amiss.
         */
        deltat = time.tv_sec - base;
        if (deltat < 0)
            deltat = -deltat;
        if (deltat < 2 * SECDAY)
            return;/* all is well */
        printf("WARNING: clock %s %ld days\n", time.tv_sec < base ? "lost" : "gained", (long)deltat / SECDAY);
    }
    
bad:
    printf("WARNING: CHECK AND RESET THE DATE!\n");
}

/*
 * resettodr:
 *
 *Reset the time-of-day register with the current time.
 */
void
resettodr(void)
{

    if (time.tv_sec == 0)
        return;
    
    if ((todr_handle != NULL) && (todr_settime(todr_handle, (struct timeval *)&time) != 0))
        printf("resettodr: failed to set time\n");
}

vx115_com.c

/*
 * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu>
 * All rights reserved.
 * 
 * Based on ixp12x0_com.c
 * 
 * Copyright (c) 1998, 1999, 2001, 2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Ichiro FUKUHARA and Naoto Shimazaki.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by IWAMOTO Toshihiro.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by the NetBSD
 *        Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)com.c       7.5 (Berkeley) 5/16/91
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vx115_com.c,v 1.19 2003/08/07 16:26:53 agc Exp $");

#include "opt_ddb.h"
#include "opt_kgdb.h"

#include "rnd.h"
#if NRND > 0 && defined(RND_COM)
#include <sys/rnd.h>
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/uio.h>
#include <sys/vnode.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_com.h>
#include <arm/vx115/vx115_com_var.h>
#include <arm/vx115/vx115_var.h>

#include <dev/cons.h>


#define DEBUG_COM
//#define LOWLEVEL_DEBUG_COM

#ifdef DEBUG_COM
#define DPRINTF(fmt...)  printf(fmt)
void COM_DEBUG_PRINT_STRING(char *str);
#ifdef LOWLEVEL_DEBUG_COM
#define COM_LOWLEVEL_DEBUG_PRINT_STRING(str)  COM_DEBUG_PRINT_STRING(str)
#else
#define COM_LOWLEVEL_DEBUG_PRINT_STRING(str) 
#endif
#else
#define DPRINTF(fmt...) 
#define COM_DEBUG_PRINT_STRING(str) 
#define COM_LOWLEVEL_DEBUG_PRINT_STRING(str)
#endif




/* Vx115 com softc struct */
struct vx115_com_softc 
{
    struct device       sc_dev;
    bus_addr_t          sc_addr;
    bus_space_tag_t     sc_iot;
    bus_space_handle_t  sc_ioh;

    void    *sc_softintr;

    struct tty *sc_tty;

    u_char  *sc_rbuf, *sc_ebuf;

    u_char  *sc_tba;
    u_int   sc_tbc, sc_heldtbc;

    u_char  *sc_rbget,*sc_rbput;
    u_int sc_rbavail;

    /* status flags */
    u_int sc_hwflags, sc_swflags;

    u_int sc_rx_flags;
    int sc_tx_busy;
    int sc_tx_stopped;
    int sc_rx_ready;
    int sc_heldchange;

    /* control registers */
    u_int sc_baud_divisor;
    u_int sc_rx_control;
    u_int sc_tx_control;
    u_int sc_char_counter_control;
    
    /* power management hooks */
    int (*enable)(struct vx115_com_softc *);
    int (*disable)(struct vx115_com_softc *);

    int enabled;
};


/* Vx115 com console softc struct */
struct vx115_com_cons_softc 
{
    bus_space_tag_t        sc_iot;
    bus_space_handle_t     sc_ioh;
    bus_addr_t             sc_addr;
    int                    sc_ospeed;
    tcflag_t               sc_cflag;
    int                    sc_attached;
};


/* Utility functions */
static void vx115_com_break(struct vx115_com_softc *sc, int onoff);
static void vx115_com_shutdown(struct vx115_com_softc *sc);
static void vx115_com_iflush(struct vx115_com_softc *);
static void vx115_com_set_cr(struct vx115_com_softc *);
static u_int cflag_to_rx_control(tcflag_t cflag);
static u_int cflag_to_tx_control(tcflag_t cflag);

/* tty functions */
static int vx115_com_param(struct tty *, struct termios *);
static int vx115_com_hwiflow(struct tty *, int);
static void vx115_com_start(struct tty *);

/* Console functions */
static int vx115_com_cngetc(dev_t);
static void vx115_com_cnputc(dev_t, int);
static void vx115_com_cnpollc(dev_t, int);

/* Soft interrupt functions */
static void vx115_com_soft(void* arg);
inline static void vx115_com_tx_soft(struct vx115_com_softc *, struct tty *);
inline static void vx115_com_rx_soft(struct vx115_com_softc *, struct tty *);

/* Hardware interrupt functions */
static void vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh);
static void vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh);




static struct vx115_com_cons_softc vx115_com_cn_sc;

static struct cnm_state vx115_com_cnm_state;

extern struct cfdriver vx115_com_cd;


dev_type_open(vx115_com_open);
dev_type_close(vx115_com_close);
dev_type_read(vx115_com_read);
dev_type_write(vx115_com_write);
dev_type_ioctl(vx115_com_ioctl);
dev_type_stop(vx115_com_stop);
dev_type_tty(vx115_com_tty);
dev_type_poll(vx115_com_poll);

/* char dev switch structure for tty */
const struct cdevsw vx115_com_cdevsw = 
{
    vx115_com_open,
    vx115_com_close, 
    vx115_com_read, 
    vx115_com_write, 
    vx115_com_ioctl,
    vx115_com_stop, 
    vx115_com_tty, 
    vx115_com_poll, 
    nommap,             /* mmap not supported */
    ttykqfilter, 
    D_TTY
};

/* console method struct */
struct consdev vx115_com_cons = 
{
    NULL,               /* cn_probe: probe hardware and fill in consdev info */
    NULL,               /* cn_init: turn on as console */
    vx115_com_cngetc,   /* cn_getc: kernel getchar interface */
    vx115_com_cnputc,   /* cn_putc: kernel putchar interface */
    vx115_com_cnpollc,  /* cn_pollc: turn on and off polling */
    NULL,               /* cn_bell: ring bell */
    NULL,               /* cn_halt: stop device */
    NULL,               /* cn_flush: flush output */
    NODEV,              /* major/minor of device */
    CN_NORMAL           /* priority */
};



#ifndef DEFAULT_COMSPEED
#define DEFAULT_COMSPEED 115200
#endif

#define COMUNIT_MASK    0x7ffff
#define COMDIALOUT_MASK 0x80000
#define COMUNIT(x)      (minor(x) & COMUNIT_MASK)
#define COMDIALOUT(x)   (minor(x) & COMDIALOUT_MASK)

#define COM_ISALIVE(sc)     ((sc)->enabled != 0 && ISSET((sc)->sc_dev.dv_flags, DVF_ACTIVE))

#define COM_BARRIER(t,h,f)  bus_space_barrier((t), (h), 0, COM_NPORTS, (f))

/* we're uniprocessor, so no need for locks */
#define COM_LOCK(sc)    
#define COM_UNLOCK(sc)

/* macros to make code more readable */
#define REG_READ(offset)          bus_space_read_4(iot, ioh, offset)
#define REG_WRITE(offset,value)   bus_space_write_4(iot, ioh, offset, value)

#define SET(t,f)    (t) |= (f)
#define CLR(t,f)    (t) &= ~(f)
#define ISSET(t,f)  ((t) & (f))


/* receive interrupts: FIFO threshold */
#define RECEIVE_INTERRUPT_MASK        ASCC_RX_CTRL_FICE_TH

/* character counter interrupt mask */
#define CHAR_CNT_CNTL_INTERRUPT_MASK  (ASCC_CHAR_COUNTER_CONTROL_ENABLE_FREERUNNING | ASCC_CHAR_COUNTER_CONTROL_INT_ENABLE)

/* transmit interrupts: FIFO threshold */
#define TRANSMIT_INTERRUPT_MASK       ASCC_TX_CTRL_FICE_TH


#ifdef DEBUG_COM
void COM_DEBUG_PRINT_STRING(char *str)
{
    while(*str != '\0')
    {
        bus_space_tag_t     iot = vx115_com_cn_sc.sc_iot;
        bus_space_handle_t  ioh = vx115_com_cn_sc.sc_ioh;

        /* spin while tx fifo full */
        while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF)
            ;
        
        /* write the next char */
        REG_WRITE(ASCC_TX_RX_FIFO, *str++);
    }
}
#endif


static int  vx115_com_match(struct device *, struct cfdata *, void *);
static void vx115_com_attach(struct device *, struct device *, void *);
static void vx115_com_attach_subr(struct vx115_com_softc *sc);
static int vx115_com_intr(void* arg);


CFATTACH_DECL(vx115_com, sizeof(struct vx115_com_softc),
    vx115_com_match, vx115_com_attach, NULL, NULL);


/*********************************************************************
 * Autoconfig functions
 */
 
static int
vx115_com_match(struct device *parent, struct cfdata *match, void *aux)
{
    COM_DEBUG_PRINT_STRING("vx115_com_match\n");
    
    if (strcmp(match->cf_name, "vx115_com") == 0)
        return 1;
    return 0;
}

static void
vx115_com_attach(struct device *parent, struct device *self, void *aux)
{
    struct vx115_com_softc *sc = (struct vx115_com_softc *)self;
    struct vx115_attach_args *sa = (struct vx115_attach_args *)aux;

    sc->sc_iot = sa->sa_iot;
    sc->sc_addr = sa->sa_addr;

    printf("\n");
    COM_DEBUG_PRINT_STRING("vx115_com_attach\n");

    bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh);

    vx115_com_attach_subr(sc);
    
    vx115_intr_establish(sa->sa_intr, IPL_SERIAL, vx115_com_intr, sc);
    vx115_configure_irq(sa->sa_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH);
    vx115_enable_irq(sa->sa_intr);
}



static void
vx115_com_attach_subr(struct vx115_com_softc *sc)
{
    struct tty *tp;

    COM_DEBUG_PRINT_STRING("vx115_com_attach_subr\n");
    
    /* check if this is com port for console */
    if ((sc->sc_iot == vx115_com_cn_sc.sc_iot) && (sc->sc_addr == vx115_com_cn_sc.sc_addr)) 
    {
        vx115_com_cn_sc.sc_attached = 1;
        
        //delay(10000);    /* wait for output to finish */
        
        /* make sure the console is always "hardwired" */
        SET(sc->sc_hwflags, COM_HW_CONSOLE);
        SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
    }

    tp = ttymalloc();
    tp->t_oproc = vx115_com_start;
    tp->t_param = vx115_com_param;
    tp->t_hwiflow = vx115_com_hwiflow;

    sc->sc_tty = tp;
    sc->sc_rbuf = malloc(VX115_COM_RING_SIZE << 1, M_DEVBUF, M_NOWAIT);
    sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
    sc->sc_rbavail = VX115_COM_RING_SIZE;
    if (sc->sc_rbuf == NULL) 
    {
        printf("%s: unable to allocate ring buffer\n", sc->sc_dev.dv_xname);
        return;
    }
    sc->sc_ebuf = sc->sc_rbuf + (VX115_COM_RING_SIZE << 1);
    sc->sc_tbc = 0;

    /* initalize control registers: 8-B-1 and appropriate baud rate */
    /* set FIFO thresholds to the halfway point */
    sc->sc_baud_divisor = VX115_BAUD_DIVISOR(DEFAULT_COMSPEED);
    sc->sc_rx_control = ASCC_RX_CTRL_RXCHSIZE_8 | ASCC_RX_CTRL_RXP_NO | ASCC_RX_CTRL_RXTH32 | ASCC_RX_CTRL_BRDEN;
    sc->sc_tx_control = ASCC_TX_CTRL_TXCHSIZE_8 | ASCC_TX_CTRL_TXP_NO | ASCC_TX_CTRL_TXTH32;
    
    /* clear disable bit, if set */
    sc->sc_rx_control &= ~ASCC_RX_CTRL_RXID;
    sc->sc_tx_control &= ~ASCC_TX_CTRL_TXID;
    
    /* also set the rx timeout count */
    sc->sc_char_counter_control = (0x100 & ASCC_CHAR_COUNTER_CONTROL_COUNT_MASK);
    
    tty_attach(tp);

    if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) 
    {
        int maj;

        /* locate the major number */
        maj = cdevsw_lookup_major(&vx115_com_cdevsw);
        cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
        aprint_normal("%s: major = %i: console\n", sc->sc_dev.dv_xname, maj);
    }

    sc->sc_softintr = softintr_establish(IPL_SOFTSERIAL, vx115_com_soft, sc);

#if NRND > 0 && defined(RND_COM)
    rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname,
              RND_TYPE_TTY, 0);
#endif

    /* if there are no enable/disable functions, assume the device is always enabled */
    if (!sc->enable)
        sc->enabled = 1;

    SET(sc->sc_hwflags, COM_HW_DEV_OK);
}





/************************************************************** 
 * cdevsw functions
 */

int
vx115_com_open(dev_t dev, int flag, int mode, struct proc *p)
{
    struct vx115_com_softc *sc;
    struct tty *tp;
    int s, s2;
    int error;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_open\n");
    
    sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) || sc->sc_rbuf == NULL)
        return (ENXIO);

    if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
        return (ENXIO);

#ifdef KGDB
    /*
     * If this is the kgdb port, no other use is permitted.
     */
    if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
        return (EBUSY);
#endif

    tp = sc->sc_tty;

    if (ISSET(tp->t_state, TS_ISOPEN) &&
        ISSET(tp->t_state, TS_XCLUDE) &&
        p->p_ucred->cr_uid != 0)
        return (EBUSY);

    s = spltty();

    /*
     * Do the following iff this is a first open.
     */
    if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) 
    {
        struct termios t;

        tp->t_dev = dev;

        s2 = splserial();
        COM_LOCK(sc);

        if (sc->enable) 
        {
            if ((*sc->enable)(sc)) 
            {
                COM_UNLOCK(sc);
                splx(s2);
                splx(s);
                printf("%s: device enable failed\n", sc->sc_dev.dv_xname);
                return (EIO);
            }
            sc->enabled = 1;
            
        }

        /* Turn on receive interrupts. */
        sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK;
        sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK;
    
        vx115_com_set_cr(sc);

        COM_UNLOCK(sc);
        splx(s2);

        /*
         * Initialize the termios status to the defaults.  Add in the
         * sticky bits from TIOCSFLAGS.
         */
        t.c_ispeed = 0;
        if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) 
        {
            t.c_ospeed = vx115_com_cn_sc.sc_ospeed;
            t.c_cflag = vx115_com_cn_sc.sc_cflag;
        } 
        else 
        {
            t.c_ospeed = TTYDEF_SPEED;
            t.c_cflag = TTYDEF_CFLAG;
        }
        if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
            SET(t.c_cflag, CLOCAL);
        if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
            SET(t.c_cflag, CRTSCTS);
        if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
            SET(t.c_cflag, MDMBUF);
        
        /* Make sure vx115_com_param() will do something. */
        tp->t_ospeed = 0;
        (void) vx115_com_param(tp, &t);
        tp->t_iflag = TTYDEF_IFLAG;
        tp->t_oflag = TTYDEF_OFLAG;
        tp->t_lflag = TTYDEF_LFLAG;
        ttychars(tp);
        ttsetwater(tp);

        s2 = splserial();
        COM_LOCK(sc);

        /* Clear the input ring, and unblock. */
        sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
        sc->sc_rbavail = VX115_COM_RING_SIZE;
        vx115_com_iflush(sc);
        CLR(sc->sc_rx_flags, RX_ANY_BLOCK);

#ifdef COM_DEBUG
        if (vx115_com_debug)
            comstatus(sc, "vx115_com_open  ");
#endif

        COM_UNLOCK(sc);
        splx(s2);
    }
    
    splx(s);

    error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
    if (error)
        goto bad;

    error = (*tp->t_linesw->l_open)(dev, tp);
    if (error)
        goto bad;

    return (0);

bad:
    if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
        /*
         * We failed to open the device, and nobody else had it opened.
         * Clean up the state as appropriate.
         */
        vx115_com_shutdown(sc);
    }

    return (error);
}

int
vx115_com_close(dev, flag, mode, p)
    dev_t dev;
    int flag, mode;
    struct proc *p;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_close\n");
    
    /* XXX This is for cons.c. */
    if (!ISSET(tp->t_state, TS_ISOPEN))
        return (0);

    (*tp->t_linesw->l_close)(tp, flag);
    ttyclose(tp);

    if (COM_ISALIVE(sc) == 0)
        return (0);

    if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
        /*
         * Although we got a last close, the device may still be in
         * use; e.g. if this was the dialout node, and there are still
         * processes waiting for carrier on the non-dialout node.
         */
        vx115_com_shutdown(sc);
    }

    return (0);
}

int
vx115_com_read(dev, uio, flag)
    dev_t dev;
    struct uio *uio;
    int flag;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_read\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp->t_linesw->l_read)(tp, uio, flag));
}

int
vx115_com_write(dev, uio, flag)
    dev_t dev;
    struct uio *uio;
    int flag;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_write\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp->t_linesw->l_write)(tp, uio, flag));
}

int
vx115_com_poll(dev, events, p)
    dev_t dev;
    int events;
    struct proc *p;
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_poll\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp->t_linesw->l_poll)(tp, events, p));
}

/* Stop output on a line. */
void
vx115_com_stop(struct tty *tp, int flag)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev));
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_stop\n");
    
    s = splserial();
    COM_LOCK(sc);
    if (ISSET(tp->t_state, TS_BUSY)) 
    {
        /* Stop transmitting at the next chunk. */
        sc->sc_tbc = 0;
        sc->sc_heldtbc = 0;
        if (!ISSET(tp->t_state, TS_TTSTOP))
            SET(tp->t_state, TS_FLUSH);
    }
    COM_UNLOCK(sc);    
    splx(s);
}

struct tty *
vx115_com_tty(dev_t dev)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tty\n");
    
    return (tp);
}

int
vx115_com_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc->sc_tty;
    int error;
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_ioctl\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);

    error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p);
    if (error != EPASSTHROUGH)
        return (error);

    error = ttioctl(tp, cmd, data, flag, p);
    if (error != EPASSTHROUGH)
        return (error);

    error = 0;

    s = splserial();
    COM_LOCK(sc);    

    switch (cmd) {
    case TIOCSBRK:
        vx115_com_break(sc, 1);
        break;

    case TIOCCBRK:
        vx115_com_break(sc, 0);
        break;

    case TIOCGFLAGS:
        *(int *)data = sc->sc_swflags;
        break;

    case TIOCSFLAGS:
        error = suser(p->p_ucred, &p->p_acflag); 
        if (error)
            break;
        sc->sc_swflags = *(int *)data;
        break;

    default:
        error = EPASSTHROUGH;
        break;
    }

    COM_UNLOCK(sc);
    splx(s);

    return (error);
}



/**************************************************************
 * Utility functions
 */
 

/* Flush the hardware receive FIFO */
static void
vx115_com_iflush(struct vx115_com_softc *sc)
{
    bus_space_tag_t iot = sc->sc_iot;
    bus_space_handle_t ioh = sc->sc_ioh;
    int timo;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_iflush\n");
    
    timo = 50000;
    /* flush any pending I/O */
    while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE) && --timo )
    {
        REG_READ(ASCC_TX_RX_FIFO);
    }
    
#ifdef DIAGNOSTIC
    if (!timo)
        printf("%s: com_iflush timeout\n", sc->sc_dev.dv_xname);
#endif
}


/* Set or clear generation of break condition */
static void
vx115_com_break(struct vx115_com_softc *sc, int onoff)
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_break\n");
    
    if (onoff)
        sc->sc_tx_control |= ASCC_TX_CTRL_BREAK;
    else
        sc->sc_tx_control &= ~ASCC_TX_CTRL_BREAK;
        
    if (!sc->sc_heldchange) 
    {
        if (sc->sc_tx_busy) 
        {
            sc->sc_heldtbc = sc->sc_tbc;
            sc->sc_tbc = 0;
            sc->sc_heldchange = 1;
        } 
        else
        {
            vx115_com_set_cr(sc);
        }
    }
}


static void
vx115_com_shutdown(struct vx115_com_softc *sc)
{
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_shutdown\n");
    
    s = splserial();
    COM_LOCK(sc);    

    /* Clear any break condition set with TIOCSBRK. */
    vx115_com_break(sc, 0);

    /* Turn off interrupts. */
    sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK;
    sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK;
    sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
    /* set disable bit */
    sc->sc_rx_control |= ASCC_RX_CTRL_RXID;
    sc->sc_tx_control |= ASCC_TX_CTRL_TXID;
    
    vx115_com_set_cr(sc);

    if (sc->disable) {
#ifdef DIAGNOSTIC
        if (!sc->enabled)
            panic("vx115_com_shutdown: not enabled?");
#endif
        (*sc->disable)(sc);
        sc->enabled = 0;
    }
    COM_UNLOCK(sc);
    splx(s);
}


static u_int
cflag_to_rx_control(tcflag_t cflag)
{
    u_int cr = 0;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_rx_control\n");
    
    if (cflag & PARENB)
        cr |= (cflag & PARODD) ? ASCC_RX_CTRL_RXP_ODD : ASCC_RX_CTRL_RXP_EVEN;
    
    cr |= ((cflag & CSIZE) == CS8) ? ASCC_RX_CTRL_RXCHSIZE_8 : ASCC_RX_CTRL_RXCHSIZE_7;
    cr |= (cflag & CSTOPB) ? ASCC_RX_CTRL_RXSTOP2 : ASCC_RX_CTRL_RXSTOP1;
    
    return (cr);
}

static u_int
cflag_to_tx_control(tcflag_t cflag)
{
    u_int cr = 0;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("cflag_to_tx_control\n");
    
    if (cflag & PARENB)
        cr |= (cflag & PARODD) ? ASCC_TX_CTRL_TXP_ODD : ASCC_TX_CTRL_TXP_EVEN;
    
    cr |= ((cflag & CSIZE) == CS8) ? ASCC_TX_CTRL_TXCHSIZE_8 : ASCC_TX_CTRL_TXCHSIZE_7;
    cr |= (cflag & CSTOPB) ? ASCC_TX_CTRL_TXSTOP2 : ASCC_TX_CTRL_TXSTOP1;
    
    return (cr);
}


static void
vx115_com_set_cr(struct vx115_com_softc *sc)
{
    bus_space_tag_t iot = sc->sc_iot;
    bus_space_handle_t ioh = sc->sc_ioh;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_set_cr\n");
    
    /* set control regs and baud divisor */
    REG_WRITE(ASCC_RX_CONTROL, sc->sc_rx_control);
    REG_WRITE(ASCC_TX_CONTROL, sc->sc_tx_control);
    REG_WRITE(ASCC_BAUD_DIVISOR, sc->sc_baud_divisor);
    REG_WRITE(ASCC_CHAR_COUNTER_CONTROL, sc->sc_char_counter_control);
}



/**************************************************************************
 * tty functions
 * Assigned to associated tty struct fields in vx115_com_open
 */
 
/* Assigned to standard termios line discipline t_linesw->l_start method.   */
/* Called from within our vx115_txsoft method.                              */
static void 
vx115_com_start(struct tty *tp)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev));
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start\n");
    
    if (COM_ISALIVE(sc) == 0)
        return;

    s = spltty();
    if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
        goto out;
    if (sc->sc_tx_stopped)
        goto out;

    if (tp->t_outq.c_cc <= tp->t_lowat) 
    {
        if (ISSET(tp->t_state, TS_ASLEEP)) 
        {
            CLR(tp->t_state, TS_ASLEEP);
            wakeup(&tp->t_outq);
        }
        selwakeup(&tp->t_wsel);
        if (tp->t_outq.c_cc == 0)
            goto out;
    }

    /* output the first contiguous region of buffer space. */
    /* set spl to splserial to prevent race condition on sc_tbc,sc_tba, sc_tx_busy */
    (void)splserial();
    COM_LOCK(sc);

    sc->sc_tba = tp->t_outq.c_cf;
    sc->sc_tbc = ndqb(&tp->t_outq, 0);
    
    if (sc->sc_tbc > 0)
    {
        COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start: sc->sc_tbc > 0\n");
        
        SET(tp->t_state, TS_BUSY);
        sc->sc_tx_busy = 1;
    
        /* Enable transmit interrupts */
        sc->sc_tx_control |= TRANSMIT_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
    }

    COM_UNLOCK(sc);
    
out:
    splx(s);
    return;
}


static int
vx115_com_param(struct tty *tp, struct termios *t)
{
    struct vx115_com_softc *sc = device_lookup(&vx115_com_cd, COMUNIT(tp->t_dev));
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_param\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);

    if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
        return (EINVAL);

    sc->sc_baud_divisor = VX115_BAUD_DIVISOR(t->c_ospeed);

    /*
     * For the console, always force CLOCAL and !HUPCL, so that the port
     * is always active.
     */
    if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) || ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) 
    {
        SET(t->c_cflag, CLOCAL);
        CLR(t->c_cflag, HUPCL);
    }

    /*
     * If there were no changes, don't do anything.  This avoids dropping
     * input and improves performance when all we did was frob things like
     * VMIN and VTIME.
     */
    if ((tp->t_ospeed == t->c_ospeed) && (tp->t_cflag == t->c_cflag))
        return (0);

    sc->sc_rx_control |= cflag_to_rx_control(t->c_cflag);
    sc->sc_tx_control |= cflag_to_tx_control(t->c_cflag);
    
    s = splserial();
    COM_LOCK(sc);    
    
    /* don't use hardware flow control */

    /* copy to tty */
    tp->t_ispeed = 0;
    tp->t_ospeed = t->c_ospeed;
    tp->t_cflag = t->c_cflag;

    if (!sc->sc_heldchange) 
    {
        if (sc->sc_tx_busy) 
        {
            sc->sc_heldtbc = sc->sc_tbc;
            sc->sc_tbc = 0;
            sc->sc_heldchange = 1;
        } 
        else
            vx115_com_set_cr(sc);
    }

    COM_UNLOCK(sc);
    splx(s);

    /*
     * Update the tty layer's idea of the carrier bit.
     * We tell tty the carrier is always on.
     */
    (void) (*tp->t_linesw->l_modem)(tp, 1);

#ifdef COM_DEBUG
    if (com_debug)
        comstatus(sc, "comparam ");
#endif

    if (!ISSET(t->c_cflag, CHWFLOW)) {
        if (sc->sc_tx_stopped) {
            sc->sc_tx_stopped = 0;
            vx115_com_start(tp);
        }
    }

    return (0);
}



static int
vx115_com_hwiflow(tp, block)
    struct tty *tp;
    int block;
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_hwiflow\n");
    
    return (0);
}



/**************************************************************
 * Console functions
 */
 
int
vx115_com_cnattach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh, int ospeed, tcflag_t cflag)
{
    unsigned int baud_divisor, rx_control, tx_control;
    
    cn_tab = &vx115_com_cons;
    cn_init_magic(&vx115_com_cnm_state);
    //cn_set_magic("\047\001"); /* default magic is BREAK */
    
    vx115_com_cn_sc.sc_iot = iot;
    vx115_com_cn_sc.sc_ioh = ioh;
    vx115_com_cn_sc.sc_addr = iobase;
    vx115_com_cn_sc.sc_ospeed = ospeed;
    vx115_com_cn_sc.sc_cflag = cflag;

    baud_divisor = VX115_BAUD_DIVISOR(ospeed);
    rx_control = cflag_to_rx_control(cflag);
    tx_control = cflag_to_tx_control(cflag);
    
    /* set rx and tx to interrupt on threshold achievement, */
    /* and set thresholds to the halfway point              */
    rx_control |= ASCC_RX_CTRL_FICE_TH | ASCC_RX_CTRL_RXTH32;
    tx_control |= ASCC_TX_CTRL_FICE_TH | ASCC_TX_CTRL_TXTH32;
    
    /* clear disable bit, if set */
    rx_control &= ~ASCC_RX_CTRL_RXID;
    tx_control &= ~ASCC_TX_CTRL_TXID;
    
    /* enable the UART; note that interrupts will not yet be hooked up       */
    /* however, the kernel printf function depends only on vx115_com_cnputc, */
    /* which doesn't require that interrupts be ready, so we get output      */
    bus_space_write_4(iot, ioh, ASCC_BAUD_DIVISOR, baud_divisor);
    bus_space_write_4(iot, ioh, ASCC_RX_CONTROL, rx_control);
    bus_space_write_4(iot, ioh, ASCC_TX_CONTROL, tx_control);
    
    return (0);
}


static void
vx115_com_cnpollc(dev, on)
    dev_t dev;
    int on;
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnpollc\n");
}


static void
vx115_com_cnputc(dev_t dev, int c)
{
    int s;
    bus_space_tag_t     iot = vx115_com_cn_sc.sc_iot;
    bus_space_handle_t  ioh = vx115_com_cn_sc.sc_ioh;

    //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cnputc\n");
    
    s = splserial();

    /* spin while tx fifo full */
    while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF)
        ;
    
    /* write the char */
    REG_WRITE(ASCC_TX_RX_FIFO, c);

    splx(s);
}


static int
vx115_com_cngetc(dev_t dev)
{
    int c;
    int s;
    bus_space_tag_t     iot = vx115_com_cn_sc.sc_iot;
    bus_space_handle_t  ioh = vx115_com_cn_sc.sc_ioh;

    //COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_cngetc\n");
    
    s = splserial();

    /* spin while rx fifo empty */
    while(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE)
        ;

    /* get the char */
    c = REG_READ(ASCC_TX_RX_FIFO);
    c &= 0xff;
    
    splx(s);

    return (c);
}



/**************************************************************
 * Soft interrupt functions
 */


inline static void
vx115_com_tx_soft(struct vx115_com_softc *sc, struct tty *tp)
{
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_soft\n");
    
    CLR(tp->t_state, TS_BUSY);
    
    /* adjust tty buffer pointers to account for data that's been sent */
    if (ISSET(tp->t_state, TS_FLUSH))
        CLR(tp->t_state, TS_FLUSH);
    else
        ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
    
    /* for the standard tty line discipline, just calls vx115_com_start;    */
    /* this will check to see if any new data's available, and set transmit */
    /* interrupt if so to begin transmission                                */
    (*tp->t_linesw->l_start)(tp);
}



inline static void
vx115_com_rx_soft(struct vx115_com_softc *sc, struct tty *tp)
{
    int (*rint) __P((int c, struct tty *tp)) = tp->t_linesw->l_rint;
    u_char *get, *end;
    u_int cc, scc;
    u_char lsr;
    int code;
    int s;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_soft\n");
    
    end = sc->sc_ebuf;
    get = sc->sc_rbget;
    scc = cc = VX115_COM_RING_SIZE - sc->sc_rbavail;
    

    while (cc) 
    {
        code = get[0];
        lsr  = get[1];
        
        if (ISSET(lsr, (ASCC_TX_RX_FIFO_ERROR_RPE_RFE | ASCC_TX_RX_FIFO_ERROR_BRE)))
        {
            if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_RPE_RFE))
            {
                /* we have either a parity or framing error; unfortunately, */
                /* the ASCC doesn't tell us which, so just call it parity   */
                SET(code, TTY_PE);
            }
            
            if (ISSET(lsr, ASCC_TX_RX_FIFO_ERROR_BRE))
            {
                /* break condition is reported through TTY_FE */
                SET(code, TTY_FE);
            }
        }
        
        if ((*rint)(code, tp) != 0) 
        {
            /* error; not sure what to do... */
        }
        
        get += 2;
        if (get >= end)
            get = sc->sc_rbuf;
        cc--;
    }

    if (cc != scc) 
    {
        sc->sc_rbget = get;
        s = splserial();
        COM_LOCK(sc);
        
        sc->sc_rbavail += scc - cc;
        
        if (sc->sc_rbavail >= 1) 
        {
            /* buffers should be ok again, release possible block */
            if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) 
            {
                CLR(sc->sc_rx_flags, RX_IBUF_FULL);
                
                /* enable receive interrupts */
                sc->sc_rx_control |= RECEIVE_INTERRUPT_MASK;
                sc->sc_char_counter_control |= CHAR_CNT_CNTL_INTERRUPT_MASK;
                vx115_com_set_cr(sc);
            }
            
        }
        
        COM_UNLOCK(sc);
        splx(s);
    }
}


static void
vx115_com_soft(void* arg)
{
    struct vx115_com_softc *sc = arg;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_soft\n");
    
    if (COM_ISALIVE(sc) == 0)
        return;

    if (sc->sc_rx_ready) 
    {
        sc->sc_rx_ready = 0;
        vx115_com_rx_soft(sc, sc->sc_tty);
    }
    
    if (!sc->sc_tx_busy) 
    {
        /* previous transmissions complete; see if have any new stuff to send */
        vx115_com_tx_soft(sc, sc->sc_tty);
    }
}




/**************************************************************
 * Hardware interrupt functions
 */

static void
vx115_com_rx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh)
{
    u_char *put, *end;
    u_int cc;
    u_int32_t c;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_rx_hard\n");
    
    if (ISSET(sc->sc_rx_flags, RX_IBUF_FULL)) 
    {
        /* overflow - turn off interrupts */
        sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK;
        sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
        
        return;
    }
    
    /* get receive buffer parameters */
    end = sc->sc_ebuf;
    put = sc->sc_rbput;
    cc = sc->sc_rbavail;
        
    /* copy characters from ASCC FIFO to receive buffer */
    /* while FIFO not empty and buffer not full; also   */
    /* check for magic character sequence               */
    while ((cc > 0) && !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_RFE))
    {
        c = REG_READ(ASCC_TX_RX_FIFO);
        put[0] = c & 0xff;
        put[1] = (c >> 8) & 0xff;
        cn_check_magic(sc->sc_tty->t_dev, put[0], vx115_com_cnm_state);
        put += 2;
        if (put >= end)
            put = sc->sc_rbuf;
        cc--;
    }

    /*
     * Current string of incoming characters ended because
     * no more data was available or we ran out of space.
     * Schedule a receive event if any data was received.
     * If we're out of space, turn off receive interrupts.
     */
    sc->sc_rbput = put;
    sc->sc_rbavail = cc;
    sc->sc_rx_ready = 1;

    /*
     * If we're out of space, disable receive interrupts
     * until the queue has drained a bit.
     */
    if (cc == 0) 
    {
        SET(sc->sc_rx_flags, RX_IBUF_FULL);
        sc->sc_rx_control &= ~RECEIVE_INTERRUPT_MASK;
        sc->sc_char_counter_control &= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
    }        
}



static void
vx115_com_tx_hard(struct vx115_com_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh)
{
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_tx_hard\n");
    
    /* If we've delayed a parameter change, do it now, and restart output. */
    if (sc->sc_heldchange) 
    {
        vx115_com_set_cr(sc);
        sc->sc_heldchange = 0;
        sc->sc_tbc = sc->sc_heldtbc;
        sc->sc_heldtbc = 0;
    }

    
    
    /* output the next chunk of the contiguous buffer, if any. */
    if (sc->sc_tbc > 0) 
    {
        int n = 0;
        
        while ( !(REG_READ(ASCC_FIFO_STATUS) & ASCC_FIFO_STATUS_TFF) )
        {
            if (n >= sc->sc_tbc)
                break;
            REG_WRITE(ASCC_TX_RX_FIFO, 0xff & *(sc->sc_tba + n));
            n++;
        }
        sc->sc_tbc -= n;
        sc->sc_tba += n;
    }
    
    if (sc->sc_tbc == 0) 
    {
        /* we've finished with transmit buffer; disable transmit completion interrupts; */
        /* will be reenabled when vx115_com_start called to transmit more  */
        sc->sc_tx_control &= ~TRANSMIT_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
        sc->sc_tx_busy = 0;
    }
}


static int
vx115_com_intr(void* arg)
{
    struct vx115_com_softc *sc = arg;
    bus_space_tag_t    iot = sc->sc_iot;
    bus_space_handle_t ioh = sc->sc_ioh;
    u_int status;
    
    if (COM_ISALIVE(sc) == 0)
        return (0);

    COM_LOCK(sc);
    
    /* read main status reg to see what's up */
    status = REG_READ(ASCC_STATUS);
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("i\n");

    do 
    {

        if (status & ASCC_STATUS_ROE)
        {
            /* receive overrun */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("o\n");
        }       

        if (status & ASCC_STATUS_RPE)
        {
            /* parity error */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("p\n");
        }       

        if (status & ASCC_STATUS_RFE)
        {
            /* framing error */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("f\n");
        }       

        if (status & ASCC_STATUS_MISC_ANY)
        {
            /* it's a modem status interrupt */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("m\n");
        }  

        if (status & (ASCC_STATUS_RFT | ASCC_STATUS_CCE0))
        {
            /* it's a receive FIFO interrupt; either hit */
            /* threshold, or timed out with chars avail  */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("r\n");
            vx115_com_rx_hard(sc, iot, ioh);
        }

        if (status & ASCC_STATUS_TFT)
        {
            /* it's a transmit FIFO interrupt */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("t\n");
            vx115_com_tx_hard(sc, iot, ioh);
        }
        
        /* see if we're done */
        status = REG_READ(ASCC_STATUS);

    } while (status);

    COM_UNLOCK(sc);

    /* Wake up the poller. */
    softintr_schedule(sc->sc_softintr);

    return (1);
}

vx115_intr.h

/*
 * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu>
 * All rights reserved.
 * 
 * Based on code by Jason R. Thorpe
 *
 * Copyright (c) 2002 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *This product includes software developed for the NetBSD Project by
 *Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _VX115_INTR_H_
#define _VX115_INTR_H_


#define ARM_IRQ_HANDLER _C_LABEL(vx115_irq_dispatcher)


#ifndef _LOCORE

#include <arm/cpu.h>
#include <arm/armreg.h>
#include <arm/cpufunc.h>
#include <arm/softintr.h>

#include <machine/atomic.h>
#include <machine/bus.h>
#include <machine/intr.h>

#include <arm/vx115/vx115_reg.h>

typedef int (* vx115_irq_handler_t)(void *);


#define NR_IRQS     32
#define VALID_IRQ(i)    ((i!=24) && (i!=27) && (i<NR_IRQS))

#define IRQ_EXT2        1
#define IRQ_EXT3        2
#define IRQ_EXT4        3
#define IRQ_EXT5        4
#define IRQ_EXT6        5
#define IRQ_EXT7        6
#define IRQ_DMA1_ERROR  7
#define IRQ_DMA1        8   
#define IRQ_TIMER1      9
#define IRQ_AGPIOA      10
#define IRQ_AGPIOB      11
#define IRQ_SDMCC0      12
#define IRQ_SDMCC1      13
#define IRQ_SSP3        14
#define IRQ_I2C         15
#define IRQ_SSP1        16
#define IRQ_GPIOA       17
#define IRQ_GPIOB       18
#define IRQ_UART0       19
#define IRQ_KEYPAD      20
#define IRQ_DISPLAY     21
#define IRQ_DISP_SYNC   22
#define IRQ_CAMERA      23
#define IRQ_RESERVED24  24
#define IRQ_DSP_PCU     25
#define IRQ_ARM7_PCU    26
#define IRQ_RESERVED27  27
#define IRQ_UART0_WAKE  28
#define IRQ_USB_OTG     29
#define IRQ_USB_EXTINT  30
#define IRQ_SWI         31


/* priorities assigned to each interrupt source */
#define IRQ_TIMER1_PRIORITY         1
#define IRQ_DMA1_ERROR_PRIORITY     2
#define IRQ_DMA1_PRIORITY           3
#define IRQ_DISPLAY_PRIORITY        4
#define IRQ_DISP_SYNC_PRIORITY      5
#define IRQ_SSP1_PRIORITY           6
#define IRQ_SSP3_PRIORITY           7
#define IRQ_UART0_WAKE_PRIORITY     8
#define IRQ_UART0_PRIORITY          9
#define IRQ_SDMCC0_PRIORITY         10
#define IRQ_SDMCC1_PRIORITY         11
#define IRQ_I2C_PRIORITY            12
#define IRQ_KEYPAD_PRIORITY         13
#define IRQ_GPIOA_PRIORITY          14
#define IRQ_GPIOB_PRIORITY          15
#define IRQ_USB_EXTINT_PRIORITY     16
#define IRQ_USB_OTG_PRIORITY        17
#define IRQ_DSP_PCU_PRIORITY        18
#define IRQ_ARM7_PCU_PRIORITY       19
#define IRQ_SWI_PRIORITY            20
#define IRQ_CAMERA_PRIORITY         21
#define IRQ_EXT2_PRIORITY           22
#define IRQ_EXT3_PRIORITY           23
#define IRQ_AGPIOA_PRIORITY         24
#define IRQ_AGPIOB_PRIORITY         25
#define IRQ_EXT4_PRIORITY           26
#define IRQ_EXT5_PRIORITY           27
#define IRQ_EXT6_PRIORITY           28
#define IRQ_EXT7_PRIORITY           29
#define IRQ_RESERVED24_PRIORITY     30
#define IRQ_RESERVED27_PRIORITY     31
    


extern __volatile int current_spl_level;
extern __volatile int softint_pending;
extern int vx115_pic_spl_soft_mask[];
void vx115_do_pending(void);

/*
 * We use all of the bits in the hardware interrupt controller,
 * so we use a separate mask for the soft interrupts, and just
 * map them to corresponding bits
 */
#define SI_TO_IRQBIT(si)  (1U<<(si))

#define VX115_IRQ_MIN   0
#define VX115_IRQ_MAX   32


struct vx115_pic_softc
{
    struct device       dev;
    bus_space_tag_t     sc_iot;
    bus_space_handle_t  sc_ioh;
};

extern struct vx115_pic_softc *vx115_pic_sc;
extern int vx115_pic_spl_mask[NIPL];
extern int vx115_pic_spl_soft_mask[NIPL];

/* macros to simplify writing to the interrupt controller */
#define vx115_read_pic(offset)           bus_space_read_4(vx115_pic_sc->sc_iot, vx115_pic_sc->sc_ioh, offset)
#define vx115_write_pic(offset, value)   bus_space_write_4(vx115_pic_sc->sc_iot, vx115_pic_sc->sc_ioh, offset, value)


/*
 * Utility function for interrupt handler.
 */
static __inline int
find_first_bit( uint32_t bits )
{
    int count;

    /* CLZ is available only on ARMv5 */
    asm( "clz %0, %1" : "=r" (count) : "r" (bits) );
    return 31-count;
}


static __inline void
vx115_setipl(int new)
{
    current_spl_level = new;
    
    /* enable hardware interrupts appropriate to current spl level */
    vx115_write_pic(PIC_ENABLE_SET, vx115_pic_spl_mask[current_spl_level]);
    
    /* clear hardware interrupts not appropriate to current spl level */
    vx115_write_pic(PIC_ENABLE_CLEAR, ~vx115_pic_spl_mask[current_spl_level]);

}


static __inline void
vx115_splx(int new)
{
    int psw;

    psw = disable_interrupts(I32_bit);
    vx115_setipl(new);
    restore_interrupts(psw);

    /* if there are software interrupts pending, process */
    if (softint_pending & vx115_pic_spl_soft_mask[current_spl_level])
        vx115_do_pending();
}


static __inline int
vx115_splraise(int ipl)
{
    int old, psw;

    psw = disable_interrupts(I32_bit);
    
    old = current_spl_level;
    if( ipl > old )
        vx115_setipl(ipl);
    
    restore_interrupts(psw);

    return (old);
}

static __inline int
vx115_spllower(int ipl)
{
    int old, psw;

    psw = disable_interrupts(I32_bit);
    
    old = current_spl_level;
    if( ipl < old )
        vx115_setipl(ipl);
    
    restore_interrupts(psw);
    
    /* if there are software interrupts pending, process */
    if (softint_pending & vx115_pic_spl_soft_mask[current_spl_level])
        vx115_do_pending();
        
    return(old);
}

static __inline void
vx115_setsoftintr(int si)
{
    atomic_set_bit( (u_int *)&softint_pending, SI_TO_IRQBIT(si) );

    /* Process unmasked pending soft interrupts. */
    if ( softint_pending & vx115_pic_spl_soft_mask[current_spl_level] )
        vx115_do_pending();
}




int     _splraise(int);
int     _spllower(int);
void    splx(int);
void    _setsoftintr(int);

#if !defined(EVBARM_SPL_NOINLINE)

#define splx(new)           vx115_splx(new)
#define _spllower(ipl)      vx115_spllower(ipl)
#define _splraise(ipl)      vx115_splraise(ipl)
#define _setsoftintr(si)    vx115_setsoftintr(si)

#endif  /* !EVBARM_SPL_NOINTR */

/*
 * This function *MUST* be called very early on in a port's
 * initarm() function, before ANY spl*() functions are called.
 *
 * The parameters are the virtual address of the Vx115's Interrupt
 * Controller registers and the size of the reg region.
 */
void vx115_intr_bootstrap(bus_addr_t addr, bus_size_t size);

void vx115_irq_dispatcher(void *);
void *vx115_intr_establish(int irqno, int level, int (*func)(void *), void *cookie);
void vx115_update_intr_masks(int irqno, int level);




#define VX115_INT_DISABLED             (unsigned int)0x00000000
#define VX115_INT_ENABLED              (unsigned int)0x00000001

#define VX115_INT_SENSE_LEVEL          (unsigned int)0x00000000
#define VX115_INT_SENSE_EDGE           (unsigned int)0x00000002

#define VX115_INT_POLARITY_LOW         (unsigned int)0x00000000
#define VX115_INT_POLARITY_HIGH        (unsigned int)0x00000004
#define VX115_INT_POLARITY_HIGH_LOW    (unsigned int)0x00000000
#define VX115_INT_POLARITY_LOW_HIGH    (unsigned int)0x00000004


int vx115_configure_irq(unsigned int irq, unsigned int sense, unsigned int polarity);
void vx115_enable_irq(unsigned int irq);
void vx115_disable_irq(unsigned int irq);


#endif /* ! _LOCORE */

#endif /* _VX115_INTR_H_ */

vx115_intr.c

/* Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu>
 * 
 * Based on PXA interrupt controller,
 * Copyright (c) 2002  Genetec Corporation.  All rights reserved.
 * Written by Hiroyuki Bessho for Genetec Corporation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *This product includes software developed for the NetBSD Project by
 *Genetec Corporation.
 * 4. The name of Genetec Corporation may not be used to endorse or 
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GENETEC CORPORATION
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * IRQ handler for the Agere Vx115 processor.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vx115_intr.c,v 1.5 2003/07/15 00:24:55 lukem Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>

#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/lock.h>

#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_var.h>
#include <arm/vx115/vx115_intr.h>


#define DEBUG_INTR

#ifdef DEBUG_INTR
#define DPRINTF(fmt...)  printf(fmt)

static void DPRINTF_MASKS(void)
{
    int i;
    
    printf("pri       hard mask       soft mask\n");
    for (i = 0; i < NIPL; i++)
        printf(" %i      0x%08x     0x%08x\n", i, vx115_pic_spl_mask[i], vx115_pic_spl_soft_mask[i]);
}

#else
#define DPRINTF(fmt...)  
#define DPRINTF_MASKS()   
#endif


struct vx115_pic_softc vx115_pic_init_sc;

struct vx115_pic_softc *vx115_pic_sc;


/* number of hardware IRQs */
#define NR_IRQS 32

/* Array mapping priorities to associated IRQ number                    */
/* The [IRQ_XXX_PRIORITY] values are defined in vx115_irq.h, and can    */
/* be changed without needing to change this array initialization       */
/* Note that these priority levels differ from the system priority      */
/* levels (SPLs), and are used just to prioritize between simultaneous  */
/* interrupts at the controller.                                        */
static u_int32_t irq_priorities[NR_IRQS] =
{
    0,
    [IRQ_TIMER1_PRIORITY]       = IRQ_TIMER1,
    [IRQ_DMA1_ERROR_PRIORITY]   = IRQ_DMA1_ERROR,
    [IRQ_DMA1_PRIORITY]         = IRQ_DMA1,
    [IRQ_DISPLAY_PRIORITY]      = IRQ_DISPLAY,
    [IRQ_DISP_SYNC_PRIORITY]    = IRQ_DISP_SYNC,
    [IRQ_SSP1_PRIORITY]         = IRQ_SSP1,
    [IRQ_SSP3_PRIORITY]         = IRQ_SSP3,
    [IRQ_UART0_WAKE_PRIORITY]   = IRQ_UART0_WAKE,
    [IRQ_UART0_PRIORITY]        = IRQ_UART0,
    [IRQ_SDMCC0_PRIORITY]       = IRQ_SDMCC0,
    [IRQ_SDMCC1_PRIORITY]       = IRQ_SDMCC1,
    [IRQ_I2C_PRIORITY]          = IRQ_I2C,
    [IRQ_KEYPAD_PRIORITY]       = IRQ_KEYPAD,
    [IRQ_GPIOA_PRIORITY]        = IRQ_GPIOA,
    [IRQ_GPIOB_PRIORITY]        = IRQ_GPIOB,
    [IRQ_USB_EXTINT_PRIORITY]   = IRQ_USB_EXTINT,
    [IRQ_USB_OTG_PRIORITY]      = IRQ_USB_OTG,
    [IRQ_DSP_PCU_PRIORITY]      = IRQ_DSP_PCU,
    [IRQ_ARM7_PCU_PRIORITY]     = IRQ_ARM7_PCU,
    [IRQ_SWI_PRIORITY]          = IRQ_SWI,
    [IRQ_CAMERA_PRIORITY]       = IRQ_CAMERA,
    [IRQ_EXT2_PRIORITY]         = IRQ_EXT2,
    [IRQ_EXT3_PRIORITY]         = IRQ_EXT3,
    [IRQ_AGPIOA_PRIORITY]       = IRQ_AGPIOA,
    [IRQ_AGPIOB_PRIORITY]       = IRQ_AGPIOB,
    [IRQ_EXT4_PRIORITY]         = IRQ_EXT4,
    [IRQ_EXT5_PRIORITY]         = IRQ_EXT5,
    [IRQ_EXT6_PRIORITY]         = IRQ_EXT6,
    [IRQ_EXT7_PRIORITY]         = IRQ_EXT7,
    [IRQ_RESERVED24_PRIORITY]   = IRQ_RESERVED24,
    [IRQ_RESERVED27_PRIORITY]   = IRQ_RESERVED27
};


/*
 * PIC autoconf glue
 */
static int vx115_pic_match(struct device *, struct cfdata *, void *);
static void vx115_pic_attach(struct device *, struct device *, void *);


CFATTACH_DECL(vx115_pic, sizeof(struct vx115_pic_softc), vx115_pic_match, vx115_pic_attach, NULL, NULL);

static int vx115_pic_attached;

static int stray_interrupt(void *);
static void vx115_pic_init_interrupt_masks(void);

/*
 * interrupt dispatch table. 
 */
#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
struct intrhand {
    TAILQ_ENTRY(intrhand) ih_list;  /* link on intrq list */
    int (*ih_func)(void *);         /* handler */
    void *ih_arg;                   /* arg for handler */
};
#endif

static struct {
#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
    TAILQ_HEAD(,intrhand) list;
#else
    vx115_irq_handler_t func;
#endif
    void *cookie;/* NULL for stackframe */
    /* struct evbnt ev; */
} handler[NR_IRQS];


__volatile int softint_pending;
__volatile int current_spl_level;


/* interrupt enable masks for each level; used for spl changes  */
/* we use separate masks for the hardware and software IRQs,    */
/* since the hardware IRQs use 32 bits                          */
int vx115_pic_spl_mask[NIPL];
int vx115_pic_spl_soft_mask[NIPL];

static int extirq_level[NR_IRQS];




static int
vx115_pic_match(struct device *parent, struct cfdata *cf, void *aux)
{
    struct vx115_attach_args *sa = aux;
    
    DPRINTF("vx115_pic_match\n");
    
    if (vx115_pic_attached || sa->sa_addr != PIC1_BASE_PHYS)
        return (0);
    
    return (1);
}

void
vx115_pic_attach(struct device *parent, struct device *self, void *aux)
{
    struct vx115_pic_softc     *sc = (struct vx115_pic_softc*)self;
    struct vx115_attach_args   *sa = (struct vx115_attach_args*)aux;
    int i;
    
    DPRINTF("vx115_pic_attach\n");
    
    /* get bus space and handle */
    sc->sc_iot = sa->sa_iot;
    
    if (vx115_pic_sc == NULL)
        vx115_pic_sc = sc;

    /* map bus space and get handle */
    if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh) != 0)
        panic("%s: Cannot map registers", self->dv_xname);    
    
    vx115_pic_attached = 1;

    /* disable all interrupts */
    vx115_write_pic(PIC_ENABLE_CLEAR, INT_REQ_SRC_CLR_ALL);

    /* clear all interrupts */
    vx115_write_pic(PIC_SOURCE_CLEAR, INT_REQ_SRC_CLR_ALL);
    
    /* set up controller priority assignment registers */
    for (i = 1; i < NR_IRQS; i++)
    {
        /* set the priority control register to the associate IRQ number */
        vx115_write_pic(PIC_IPCR_1 + ((i-1)<<2), irq_priorities[i]);
    }
    
    /* enable all irq priority levels, but clear FRZ bit */
    vx115_write_pic(PIC_PRIORITY_ENABLE_SET, PIC_IPE_ALL);
    vx115_write_pic(PIC_PRIORITY_ENABLE_CLEAR, PIC_IPE_E0);    

    /* set all handlers initially to dummy handler */
    for(i = 0; i < sizeof handler / sizeof handler[0]; ++i)
    {
        handler[i].func = stray_interrupt;
        handler[i].cookie = (void *)(intptr_t) i;
        extirq_level[i] = IPL_SERIAL;
    }

    vx115_pic_init_interrupt_masks();
    
    _splraise(IPL_SERIAL);
    enable_interrupts(I32_bit);
}

/*
 * Invoked very early on from the board-specific initarm(), in order to
 * allow us to set up softc pointer, which is needed for spl functions.
 */
void
vx115_intr_bootstrap(bus_addr_t addr, bus_size_t size)
{
    /* set up inital softc struct; this will be used only until device formally attached */
    /* assign bus tag: standard vx115 bus ops */
    vx115_pic_init_sc.sc_iot = &vx115_bs_tag;
    
    /* map bus space and set handle */
    if (bus_space_map(vx115_pic_init_sc.sc_iot, addr, size, 0, &vx115_pic_init_sc.sc_ioh) != 0)
        panic("%s: Cannot map initial interrupt softc region", "vx115_intr_bootstrap");
        
    /* assign softc pointer to init softc struct */
    vx115_pic_sc = & vx115_pic_init_sc;
}

static __inline void
__raise(int ipl)
{
    if (current_spl_level < ipl)
        vx115_setipl(ipl);
}


/*
 * Map a software interrupt queue to an interrupt priority level.
 */
static const int si_to_ipl[SI_NQUEUES] = {
    IPL_SOFT,          /* SI_SOFT */
    IPL_SOFTCLOCK,     /* SI_SOFTCLOCK */
    IPL_SOFTNET,       /* SI_SOFTNET */
    IPL_SOFTSERIAL,    /* SI_SOFTSERIAL */
};

/*
 * called from irq_entry.
 */
void
vx115_irq_dispatcher(void *arg)
{
    struct clockframe *frame = arg;
    uint32_t irqbits;
    int irqno;
    int saved_spl_level;
    
    saved_spl_level = current_spl_level;
    
    while ((irqbits = vx115_read_pic(PIC_STATUS)) != 0) 
    {
        /* FOR LATER: handle IRQs in priority order - the PIC does this for us */
        irqno = find_first_bit(irqbits);
        
        /* raise spl if necessary to stop interrupts of lower priorities */
        if (saved_spl_level < extirq_level[irqno])
            vx115_setipl(extirq_level[irqno]);
        
        (*handler[irqno].func)( (handler[irqno].cookie == 0) ? frame : handler[irqno].cookie );

        /* restore spl if it was changed */
        if (saved_spl_level < extirq_level[irqno])
            vx115_setipl(saved_spl_level);
        
        /* clear interrupt */
        vx115_write_pic(PIC_SOURCE_CLEAR, (1<<irqno));
        
    }
    
    /* do any pending soft interrutps */
    if(softint_pending & vx115_pic_spl_soft_mask[current_spl_level])
        vx115_do_pending();
}

static int
stray_interrupt(void *cookie)
{
    int irqno = (int)cookie;
    printf("stray interrupt %d\n", irqno);
    
    if (irqno < NR_IRQS) 
    {
        /* disable this interrupt */
        int save = disable_interrupts(I32_bit);
        vx115_write_pic(PIC_ENABLE_CLEAR, ~(1U<<irqno));
        restore_interrupts(save);
    }
    
    return 0;
}




/*
 * Update priority masks for new interrupt at specified priority level.
 */

void
vx115_update_intr_masks(int irqno, int level)
{
    int mask;
    int *mask_array;
    int psw = disable_interrupts(I32_bit);
    int i;
    
    /* determine whether hard or soft interrupt, and set mask    */
    /* and array affected accordingly (hard irq's are < 32)      */
    if (irqno < 32)
    {
        /* hard interrupt */
        mask_array = vx115_pic_spl_mask;
        mask = 1U << irqno;
    }
    else
    {
        mask_array = vx115_pic_spl_soft_mask;
        mask = 1U << (irqno - 32);
    }

    /* Enable interrupt when at lower priority level */
    for(i = 0; i < level; ++i)
        mask_array[i] |= mask;

    /* Disable interrupt when at supplied or higher priority level */
    for( ; i < NIPL-1; ++i)
        mask_array[i] &= ~mask;

    /*
     * Enforce a heirarchy that gives "slow" device (or devices with
     * limited input buffer space/"real-time" requirements) a better
     * chance at not dropping data.
     */
    mask_array[IPL_BIO]        &= mask_array[IPL_SOFTNET];
    mask_array[IPL_NET]        &= mask_array[IPL_BIO];
    mask_array[IPL_SOFTSERIAL] &= mask_array[IPL_NET];
    mask_array[IPL_TTY]        &= mask_array[IPL_SOFTSERIAL];
    
    /*
     * splvm() blocks all interrupts that use the kernel memory
     * allocation facilities.
     */
    mask_array[IPL_VM] &= mask_array[IPL_TTY];
    
    /*
     * Audio devices are not allowed to perform memory allocation
     * in their interrupt routines, and they have fairly "real-time"
     * requirements, so give them a high interrupt priority.
     */
    mask_array[IPL_AUDIO] &= mask_array[IPL_VM];
    
    /*
     * splclock() must block anything that uses the scheduler.
     */
    mask_array[IPL_CLOCK] &= mask_array[IPL_AUDIO];
    
    /*
     * splhigh() must block "everything".
     */
    mask_array[IPL_HIGH] &= mask_array[IPL_STATCLOCK];
    
    /*
     * XXX We need serial drivers to run at the absolute highest priority
     * in order to avoid overruns, so serial > high.
     */
    mask_array[IPL_SERIAL] &= mask_array[IPL_HIGH];
    
    /* enable hardware interrupts appropriate to current spl level */
    vx115_write_pic(PIC_ENABLE_SET, vx115_pic_spl_mask[current_spl_level]);
    
    /* disable hardware interrupts appropriate to current spl level */
    vx115_write_pic(PIC_ENABLE_CLEAR, ~vx115_pic_spl_mask[current_spl_level]);
    
    restore_interrupts(psw);
    
    DPRINTF("vx115_update_intr_masks: irqno %i, level %i\n", irqno, level);
    DPRINTF_MASKS();
}


static void
vx115_pic_init_interrupt_masks(void)
{

    DPRINTF("vx115_pic_init_intr_masks\n");
    
    
    memset(vx115_pic_spl_mask, 0, sizeof(vx115_pic_spl_mask));
    memset(vx115_pic_spl_soft_mask, 0, sizeof(vx115_pic_spl_soft_mask));
    
    /*
     * IPL_NONE has soft interrupts enabled only, at least until
     * hardware handlers are installed.
     */
    vx115_pic_spl_soft_mask[IPL_NONE] =
        SI_TO_IRQBIT(SI_SOFT) |
        SI_TO_IRQBIT(SI_SOFTCLOCK) |
        SI_TO_IRQBIT(SI_SOFTNET) |
        SI_TO_IRQBIT(SI_SOFTSERIAL);

    /*
     * Initialize the soft interrupt masks to block themselves.
     */
    vx115_pic_spl_soft_mask[IPL_SOFT]       = ~SI_TO_IRQBIT(SI_SOFT);
    vx115_pic_spl_soft_mask[IPL_SOFTCLOCK]  = ~SI_TO_IRQBIT(SI_SOFTCLOCK);
    vx115_pic_spl_soft_mask[IPL_SOFTNET]    = ~SI_TO_IRQBIT(SI_SOFTNET);
    vx115_pic_spl_soft_mask[IPL_SOFTSERIAL] = ~SI_TO_IRQBIT(SI_SOFTSERIAL);
    
    vx115_pic_spl_soft_mask[IPL_SOFT] &= vx115_pic_spl_soft_mask[IPL_NONE];
    
    /*
     * splsoftclock() is the only interface that users of the
     * generic software interrupt facility have to block their
     * soft intrs, so splsoftclock() must also block IPL_SOFT.
     */
    vx115_pic_spl_soft_mask[IPL_SOFTCLOCK] &= vx115_pic_spl_soft_mask[IPL_SOFT];
    
    /*
     * splsoftnet() must also block splsoftclock(), since we don't
     * want timer-driven network events to occur while we're
     * processing incoming packets.
     */
    vx115_pic_spl_soft_mask[IPL_SOFTNET] &= vx115_pic_spl_soft_mask[IPL_SOFTCLOCK];
    
    DPRINTF_MASKS();
}


void
vx115_do_pending(void)
{
    static __cpu_simple_lock_t processing = __SIMPLELOCK_UNLOCKED;
    int oldirqstate, spl_save;
    
    if (__cpu_simple_lock_try(&processing) == 0)
        return;
    
    spl_save = current_spl_level;
    
    oldirqstate = disable_interrupts(I32_bit);
        
#define DO_SOFTINT(si,ipl)  \
        if ((softint_pending & vx115_pic_spl_soft_mask[current_spl_level]) & SI_TO_IRQBIT(si)) { \
            softint_pending &= ~SI_TO_IRQBIT(si);       \
            __raise(ipl);                               \
            restore_interrupts(oldirqstate);            \
            softintr_dispatch(si);                      \
            oldirqstate = disable_interrupts(I32_bit);  \
            vx115_setipl(spl_save);                     \
        }
    
    do 
    {
        DO_SOFTINT(SI_SOFTSERIAL,IPL_SOFTSERIAL);
        DO_SOFTINT(SI_SOFTNET, IPL_SOFTNET);
        DO_SOFTINT(SI_SOFTCLOCK, IPL_SOFTCLOCK);
        DO_SOFTINT(SI_SOFT, IPL_SOFT);
    } while( softint_pending & vx115_pic_spl_soft_mask[current_spl_level] );

    __cpu_simple_unlock(&processing);
    
    restore_interrupts(oldirqstate);
}


#undef splx
void
splx(int ipl)
{
    vx115_splx(ipl);
}

#undef _splraise
int
_splraise(int ipl)
{
    return vx115_splraise(ipl);
}

#undef _spllower
int
_spllower(int ipl)
{
    return vx115_spllower(ipl);
}

#undef _setsoftintr
void
_setsoftintr(int si)
{
    return vx115_setsoftintr(si);
}


void *
vx115_intr_establish(int irqno, int level, int (*func)(void *), void *cookie)
{
    int psw;
    
    if (irqno < VX115_IRQ_MIN || irqno >= VX115_IRQ_MAX)
        panic("intr_establish: bogus irq number %d", irqno);
    
    psw = disable_interrupts(I32_bit);
    
    handler[irqno].cookie = cookie;
    handler[irqno].func = func;
    extirq_level[irqno] = level;
        
    vx115_update_intr_masks(irqno, level);
    
    restore_interrupts(psw);
    
    return (&handler[irqno]);
}


/* Configure the irq's sense (level or edge) and polarity (low-high or high-low) */
int vx115_configure_irq(unsigned int irq, unsigned int sense, unsigned int polarity)
{
    unsigned int irq_mask;
    unsigned int irq_enabled;
    unsigned int offset;

    /* world's most annoying register offset calculation */
    offset = irq << 2;

    if ((1 <= irq) && (irq <= 7))
    {
        offset += 0xA4;
    }
    else if ((8 <= irq) && (irq <= 26))
    {
        offset += 0xE0;
    }
    else if ((27 <= irq) && (irq <= 28))
    {
        offset += 0x58;
    }
    else if ((29 <= irq) && (irq <= 30))
    {
        offset += 0x64;
    }
    else 
    {
        /* bad irq number supplied */
        return -1;
    }

    /* create mask for this irq */
    irq_mask = 1 << irq;

    /* get current interrupt enabled state */
    irq_enabled = vx115_read_pic(PIC_ENABLE_SET) & irq_mask;

    /* assign supplied parameters */
    vx115_write_pic(offset, sense | polarity | VX115_INT_ENABLED);

    /* clear the interrupt in case got a false one */
    vx115_write_pic(PIC_SOURCE_CLEAR, irq_mask);

    /* reenable the irq if enabled on entry */
    vx115_write_pic(PIC_ENABLE_SET, irq_enabled);

    return 0;
}



void vx115_enable_irq(unsigned int irq)
{
    vx115_write_pic(PIC_ENABLE_SET, 1 << irq);
}


void vx115_disable_irq(unsigned int irq)
{
    vx115_write_pic(PIC_ENABLE_CLEAR, 1 << irq);
}


vx115_io.c

/*
 * Copyright (c) 2006, Jon Sevy
 * 
 * Based on ixp12x0_io.c,
 * Copyright (c) 2002, 2003
 *    Ichiro FUKUHARA <ichiro@ichiro.org>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Ichiro FUKUHARA.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vx115_io.c,v 1.9 2003/07/13 07:15:22 igy Exp $");

/*
 * bus_space I/O functions for vx115
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>

#include <uvm/uvm.h>

#include <machine/bus.h>

#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_var.h>

/* Proto types for all the bus_space structure functions */
bs_protos(vx115);
bs_protos(generic);
bs_protos(generic_armv4);
bs_protos(bs_notimpl);

struct bus_space vx115_bs_tag = {
    /* cookie */
    (void *) 0,

    /* mapping/unmapping */
    vx115_bs_map,
    vx115_bs_unmap,
    vx115_bs_subregion,

    /* allocation/deallocation */
    vx115_bs_alloc,
    vx115_bs_free,

    /* get kernel virtual address */
    vx115_bs_vaddr,

    /* mmap bus space for userland */
    bs_notimpl_bs_mmap,

    /* barrier */
    vx115_bs_barrier,

    /* read (single) */
    generic_bs_r_1,
    generic_armv4_bs_r_2,
    generic_bs_r_4,
    bs_notimpl_bs_r_8,

    /* read multiple */
    generic_bs_rm_1,
    generic_armv4_bs_rm_2,
    generic_bs_rm_4,
    bs_notimpl_bs_rm_8,

    /* read region */
    generic_bs_rr_1,
    generic_armv4_bs_rr_2,
    generic_bs_rr_4,
    bs_notimpl_bs_rr_8,

    /* write (single) */
    generic_bs_w_1,
    generic_armv4_bs_w_2,
    generic_bs_w_4,
    bs_notimpl_bs_w_8,

    /* write multiple */
    generic_bs_wm_1,
    generic_armv4_bs_wm_2,
    generic_bs_wm_4,
    bs_notimpl_bs_wm_8,

    /* write region */
    generic_bs_wr_1,
    generic_armv4_bs_wr_2,
    generic_bs_wr_4,
    bs_notimpl_bs_wr_8,

    /* set multiple */
    bs_notimpl_bs_sm_1,
    bs_notimpl_bs_sm_2,
    bs_notimpl_bs_sm_4,
    bs_notimpl_bs_sm_8,

    /* set region */
    bs_notimpl_bs_sr_1,
    generic_armv4_bs_sr_2,
    generic_bs_sr_4,
    bs_notimpl_bs_sr_8,

    /* copy */
    bs_notimpl_bs_c_1,
    generic_armv4_bs_c_2,
    bs_notimpl_bs_c_4,
    bs_notimpl_bs_c_8,
};

/* Common routines */

int
vx115_bs_map(void *t, bus_addr_t bpa, bus_size_t size,
           int flags, bus_space_handle_t *bshp)
{
    const struct pmap_devmap    *pd;

    paddr_t        startpa;
    paddr_t        endpa;
    paddr_t        pa;
    paddr_t        offset;
    vaddr_t        va;
    pt_entry_t    *pte;

    if ((pd = pmap_devmap_find_pa(bpa, size)) != NULL) {
        /* Device was statically mapped. */
        *bshp = pd->pd_va + (bpa - pd->pd_pa);
        return 0;
    }

    endpa = round_page(bpa + size);
    offset = bpa & PAGE_MASK;
    startpa = trunc_page(bpa);
        
    if ((va = uvm_km_valloc(kernel_map, endpa - startpa)) == 0)
        return ENOMEM;

    *bshp = va + offset;

    for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE) {
        pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE);
        pte = vtopte(va);
        *pte &= ~L2_S_CACHE_MASK;
        PTE_SYNC(pte);
    }
    pmap_update(pmap_kernel());

    return 0;
}

void
vx115_bs_unmap(void *t, bus_space_handle_t bsh, bus_size_t size)
{
    vaddr_t    va;
    vaddr_t    endva;

    if (pmap_devmap_find_va(bsh, size) != NULL) {
        /* Device was statically mapped; nothing to do. */
        return;
    }

    endva = round_page(bsh + size);
    va = trunc_page(bsh);

    pmap_kremove(va, endva - va);
    uvm_km_free(kernel_map, va, endva - va);
}

int
vx115_bs_subregion(t, bsh, offset, size, nbshp)
    void *t;
    bus_space_handle_t bsh;
    bus_size_t offset, size;
    bus_space_handle_t *nbshp;
{

    *nbshp = bsh + offset;
    return (0);
}

void *
vx115_bs_vaddr(t, bsh)
    void *t;
    bus_space_handle_t bsh;
{
    return ((void *)bsh);
}

int
vx115_bs_alloc(void *t,
         bus_addr_t rstart, bus_addr_t rend, bus_size_t size,
         bus_size_t alignment, bus_size_t boundary,
         int flags, bus_addr_t *bpap,
         bus_space_handle_t *bshp)
{
    panic("vx115_bs_alloc(): not implemented\n");
}

void    
vx115_bs_free(void *t, bus_space_handle_t bsh, bus_size_t size)
{
    panic("vx115_bs_free(): not implemented\n");
}

void
vx115_bs_barrier(t, bsh, offset, len, flags)
    void *t;
    bus_space_handle_t bsh;
    bus_size_t offset, len;
    int flags;
{
/* NULL */
}    

/* End of vx115_io.c */

sys/arch/evbarm/conf

files.vx115_vep

#
# Agere Vx115 VEP evaluation board configuration info
#

# Use the generic ARM soft interrupt code.
file    arch/arm/arm/softintr.c

# CPU support and integrated peripherals
include "arch/arm/vx115/files.vx115"

# board-specific startup source file
file    arch/evbarm/vx115_vep/vx115_vep_machdep.c

mk.vx115_vep

# Makefile additions for Agere Vx115 VEP platform

SYSTEM_FIRST_OBJ=    vx115_vep_start.o
SYSTEM_FIRST_SFILE=  ${THISARM}/vx115_vep/vx115_vep_start.S

# Used to generate S-record for loading into flash
KERNEL_LOAD_PHYS=0x20400000

SYSTEM_LD_TAIL_EXTRA+=; \
echo ${OBJCOPY} -S -O binary $@ $@.bin; \
${OBJCOPY} -S -O binary $@ $@.bin; \
echo gzip \< $@.bin \> $@.bin.gz; \
gzip < $@.bin > $@.bin.gz; \
echo ${OBJCOPY} --input-target=binary --output-target=srec --change-addresses=$(KERNEL_LOAD_PHYS) -v $@.bin $@.bin.srec \
${OBJCOPY} --input-target=binary --output-target=srec --change-addresses=$(KERNEL_LOAD_PHYS) -v $@.bin $@.bin.srec

EXTRA_KERNELS+= ${KERNELS:@.KERNEL.@${.KERNEL.}.bin@}
EXTRA_KERNELS+= ${KERNELS:@.KERNEL.@${.KERNEL.}.bin.gz@}

std.vx115_vep

#
# NetBSD/evbarm options for Agere Vx115 VEP evaluation board
#

machine    evbarm arm

# Pull in Vx115 VEP config definitions.
include "arch/evbarm/conf/files.vx115_vep"


options     EXEC_ELF32
options     EXEC_AOUT
options     EXEC_SCRIPT

# To support easy transit to ../arch/arm/arm32
options     ARM32

# use HZ=64 to get even divisor in vx115_clk.c
options     HZ=64


makeoptions    BOARDTYPE="vx115_vep"
makeoptions    BOARDMKFRAG="${THISARM}/conf/mk.vx115_vep"
makeoptions    KERNEL_BASE_PHYS=0x24300000
makeoptions    KERNEL_BASE_VIRT=0xc0200000


options     ARM_INTR_IMPL="<arch/arm/vx115/vx115_intr.h>"

VX115_VEP

#
#    Kernel configuration for Agere Vx115
#

include    "arch/evbarm/conf/std.vx115_vep"

#options     INCLUDE_CONFIG_FILE    # embed config file in kernel binary
options      MSGBUFSIZE=65536
#options     KSTACK_CHECK_MAGIC

# estimated number of users
maxusers     32

# Standard system options

options      RTC_OFFSET=0    # hardware clock is this many mins. west of GMT
options      NTP        # NTP phase/frequency locked loop

# CPU options

options      CPU_ARM9    # Support the ARM9TDMI core

# File systems

file-system    FFS        # UFS
#file-system   LFS        # log-structured file system
file-system    MFS        # memory file system
#file-system    NFS        # Network file system
#file-system   ADOSFS     # AmigaDOS-compatible file system
#file-system    EXT2FS     # second extended file system (linux)
#file-system   CD9660     # ISO 9660 + Rock Ridge file system
file-system    MSDOSFS    # MS-DOS file system
#file-system   FDESC      # /dev/fd
#file-system   FILECORE   # Acorn filecore file system
file-system    KERNFS     # /kern
file-system    NULLFS     # loopback file system
#file-system   PORTAL     # portal filesystem (still experimental)
file-system    PROCFS     # /proc
#file-system   UMAPFS     # NULLFS + uid and gid remapping
file-system    UNION      # union file system

# File system options
#options     QUOTA        # UFS quotas
#options     FFS_EI        # FFS Endian Independent support
#options     NFSSERVER
#options     SOFTDEP
#options     FFS_NO_SNAPSHOT    # ffs snapshots

# Networking options

#options     GATEWAY        # packet forwarding
options     INET        # IP + ICMP + TCP + UDP
options     INET6        # IPV6
#options     IPSEC        # IP security
#options     IPSEC_ESP    # IP security (encryption part; define w/ IPSEC)
#options     IPSEC_NAT_T    # IPsec NAT traversal (NAT-T)
#options     IPSEC_DEBUG    # debug for IP security
#options     MROUTING    # IP multicast routing
#options     NS        # XNS
#options     NSIP        # XNS tunneling over IP
#options     ISO,TPIP    # OSI
#options     EON        # OSI tunneling over IP
#options     CCITT,LLC,HDLC    # X.25
#options     NETATALK    # AppleTalk networking
#options     PFIL_HOOKS    # pfil(9) packet filter hooks
#options     PPP_BSDCOMP    # BSD-Compress compression support for PPP
#options     PPP_DEFLATE    # Deflate compression support for PPP
#options     PPP_FILTER    # Active filter support for PPP (requires bpf)
#options     TCP_DEBUG    # Record last TCP_NDEBUG packets with SO_DEBUG

#options     NFS_BOOT_BOOTP
#options     NFS_BOOT_DHCP
#options     NFS_BOOT_BOOTPARAM

# Compatibility options

#options     COMPAT_LINUX
#options     COMPAT_16    # NetBSD 1.6
#options     COMPAT_20    # NetBSD 2.0
#options     COMPAT_43    # 4.3BSD compatibility.
#options     COMPAT_16    # NetBSD 1.6 compatibility.
#options     COMPAT_15    # NetBSD 1.5 compatibility.
#options     COMPAT_14    # NetBSD 1.4 compatibility.
#options     COMPAT_13    # NetBSD 1.3 compatibility.
#options     COMPAT_12    # NetBSD 1.2 compatibility.
#options     COMPAT_11    # NetBSD 1.1 compatibility.
#options     COMPAT_10    # NetBSD 1.0 compatibility.
#options     COMPAT_09    # NetBSD 0.9 compatibility.
#options     TCP_COMPAT_42    # 4.2BSD TCP/IP bug compat. Not recommended.

# Shared memory options

options     SYSVMSG        # System V-like message queues
options     SYSVSEM        # System V-like semaphores
options     SEMMNI=10      # number of semaphore identifiers
options     SEMMNS=60      # number of semaphores in system
options     SEMUME=10      # max number of undo entries per process
options     SEMMNU=30      # number of undo structures in system
options     SYSVSHM        # System V-like memory sharing
options     SHMMAXPGS=1024    # 1024 pages is the default

# Device options

options     MEMORY_DISK_HOOKS            # boottime setup of ramdisk
options     MEMORY_DISK_ROOT_SIZE=10000   # Size in 512-byte blocks
options     MEMORY_DISK_IS_ROOT          # use memory disk as root
options     MEMORY_DISK_SERVER=1        # make the ramdisk writeable

# Console options.  The default console is speed is 115200 baud.
#options     CONSPEED=115200        # Console speed

# Miscellaneous kernel options
options     KTRACE             # system call tracing, a la ktrace(1)
options     IRQSTATS           # manage IRQ statistics
#options     LKM               # loadable kernel modules
#options     KMEMSTATS         # kernel memory statistics
#options     SCSIVERBOSE       # Verbose SCSI errors
#options     PCIVERBOSE        # Verbose PCI descriptions
#options     MIIVERBOSE        # Verbose MII autoconfuration messages
#options     PCI_CONFIG_DUMP   # verbosely dump PCI config space
#options     DDB_KEYCODE=0x40
#options     USERCONF          # userconf(4) support
#options     PIPE_SOCKETPAIR   # smaller, but slower pipe(2)

# Development and Debugging options

#options     PERFCTRS    # performance counters
options     DIAGNOSTIC    # internally consistency checks
options     DEBUG
#options     PMAP_DEBUG    # Enable pmap_debug_level code
#options     IPKDB        # remote kernel debugging
options     VERBOSE_INIT_ARM # verbose bootstraping messages
options     DDB        # in-kernel debugger
options     DDB_ONPANIC=1
options     DDB_HISTORY_SIZE=100    # Enable history editing in DDB
makeoptions    DEBUG="-g"    # compile full symbol table
options     SYMTAB_SPACE=450000

#options     PMAP_INCLUDE_PTE_SYNC
#options     LOCKDEBUG

config        netbsd        root on ? type ?
#config        netbsd-epe0   root on epe0 type nfs
#config        netbsd-wd0    root on wd0 type ffs
#config        netbsd-sd0    root on sd0 type ffs
#config        netbsd-md0    root on md0 type ffs


# The main bus device
mainbus0    at root

# The boot cpu
cpu0        at mainbus?

# Vx115 AHB and APB
vx115_ahb0   at mainbus?
vx115_apb0   at mainbus?

# On-chip interrupt controller
vx115_pic0   at vx115_apb? addr 0x700C1000 size 0x14c

# On-chip timer
vx115_clk0   at vx115_apb? addr 0x700C5000 size 0x68  intr 9

# On-chip serial UART
vx115_com0   at vx115_apb? addr 0x700E2000 size 0x7c  intr 19



# Pseudo-Devices

# disk/mass storage pseudo-devices
pseudo-device    md        1    # memory disk device (ramdisk)
pseudo-device    vnd        4    # disk-like interface to files
#pseudo-device    fss       4    # file system snapshot device

# network pseudo-devices
pseudo-device    bpfilter   4    # Berkeley packet filter
pseudo-device    loop            # network loopback
pseudo-device    kttcp           # network loopback

# miscellaneous pseudo-devices
pseudo-device    pty             # pseudo-terminals
pseudo-device    rnd             # /dev/random and in-kernel generator
#options    RND_COM
pseudo-device    clockctl        # user control of clock subsystem
pseudo-device    ksyms           # /dev/ksyms

# data mover pseudo-devices
#pseudo-device    swdmover       # softare dmover(9) back-end
#pseudo-device    dmoverio       # /dev/dmover dmover(9) interface

#
# wscons options
#
# builtin terminal emulations
#options     WSEMUL_SUN        # sun terminal emulation
options     WSEMUL_VT100        # VT100 / VT220 emulation
# customization of console and kernel output - see dev/wscons/wsdisplayvar.h
#options     WSDISPLAY_CUSTOM_OUTPUT    # color customization from wsconsctl(8)
#options     WS_DEFAULT_FG=WSCOL_WHITE
#options     WS_DEFAULT_BG=WSCOL_BLACK
#options     WS_DEFAULT_COLATTR="(0)"
#options     WS_DEFAULT_MONOATTR="(0)"
#options     WS_KERNEL_FG=WSCOL_GREEN
#options     WS_KERNEL_BG=WSCOL_BLACK
#options     WS_KERNEL_COLATTR=""
#options     WS_KERNEL_MONOATTR=""
# customization of console border color
#options     WSDISPLAY_CUSTOM_BORDER    # border customization from wsconsctl(8)
#options     WSDISPLAY_BORDER_COLOR=WSCOL_BLUE    # default color
# compatibility to other console drivers
#options     WSDISPLAY_COMPAT_PCVT        # emulate some ioctls
#options     WSDISPLAY_COMPAT_SYSCONS    # emulate some ioctls
#options     WSDISPLAY_COMPAT_USL        # VT handling
#options     WSDISPLAY_COMPAT_RAWKBD        # can get raw scancodes
# see dev/pckbc/wskbdmap_mfii.c for implemented layouts
#options     PCKBD_LAYOUT="(KB_DE | KB_NODEAD)"
# allocate a number of virtual screens at autoconfiguration time
#options     WSDISPLAY_DEFAULTSCREENS=4
# use a large software cursor that doesn't blink
#options     PCDISPLAY_SOFTCURSOR
# modify the screen type of the console; defaults to "80x25"
#options     VGA_CONSOLE_SCREENTYPE="\"80x24\""
# work around a hardware bug that loaded fonts don't work; found on ATI cards
#options     VGA_CONSOLE_ATI_BROKEN_FONTSEL
# the following enables some functions to get mouse console support.
# if you want a really secure system, it may be better not to enable them,
# see wsmoused(8), section SECURITY CONSIDERATIONS for more info.
#options     WSDISPLAY_CHARFUNCS        # mouse console support
# console scrolling support.
#options     WSDISPLAY_SCROLLSUPPORT
# enable VGA raster mode capable of displaying multilingual text on console
#options     VGA_RASTERCONSOLE

# wscons pseudo-devices
#pseudo-device    wsmux            # mouse & keyboard multiplexor
#pseudo-device    wsfont

sys/arch/evbarm/vx115_vep

vx115_vep_start.S

/*
 * vx115_vep_start.S
 * Copyright (c) 2007, J. Sevy <jsevy@cs.drexel.edu>
 *
 * Based on g42xxeb_start.S
 * Copyright (c) 2002, 2003  Genetec Corporation.  All rights reserved.
 * Written by Hiroyuki Bessho for Genetec Corporation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of Genetec Corporation may not be used to endorse or 
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GENETEC CORPORATION
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <machine/asm.h>
#include <arm/armreg.h>
#include <arm/arm32/pte.h>
#include <arm/arm32/pmap.h>        /* for PMAP_DOMAIN_KERNEL */


#ifndef SRAM_START
#define SRAM_START    0x24000000
#endif

/*
 * CPWAIT -- Canonical method to wait for CP15 update.
 * NOTE: Clobbers the specified temp reg.
 * copied from arm/arm/cpufunc_asm_xscale.S
 * XXX: better be in a common header file.
 */
#define    CPWAIT_BRANCH    \
    sub pc, pc, #4

#define    CPWAIT(tmp)                                           \
    mrc p15, 0, tmp, c2, c0, 0  /* arbitrary read of CP15 */    ;\
    mov tmp, tmp                /* wait for it to complete */   ;\
    CPWAIT_BRANCH               /* branch to next insn */
    
/*
 * Kernel start routine for Agere Vx115 VEP board.
 * This code is executed from Flash when the bootloader jumps to it.
 */
    .text

    .global _C_LABEL(vx115_vep_start)
_C_LABEL(vx115_vep_start):
    
    /* move code to RAM */
    ldr    r1, Lcopy_size
    adr    r0, _C_LABEL(vx115_vep_start)
    add    r1, r1, #3
    mov    r1, r1, LSR #2         /* get size of code in words */
    mov    r2, #SRAM_START
    add    r2, r2, #0x100000    /* lower 1MB RAM reserved for comm stack */
    add    r2, r2, #0x200000    /* code placed 2MB above kernel base */
    mov    r4, r2

    /* copy kernel to RAM */
5:  
ldr    r3, [r0], #4
    str    r3, [r2], #4
    subs   r1, r1, #1
    bhi    5b

    /* jump to RAM */
    ldr    r0, Lstart_off
    add    pc, r4, r0


Lcopy_size:    .word _edata-_C_LABEL(vx115_vep_start)
Lstart_off:    .word vx115_vep_start_ram-_C_LABEL(vx115_vep_start)

vx115_vep_start_ram:    
    /*
     *  Kernel is loaded in SDRAM (0x24300000), and is expected to run
     *  in VA 0xc0300000.
     */

    /* build page table from scratch */
    ldr    r0, Lstartup_pagetable
    adr    r4, mmu_init_table
    b      3f

2:
    str    r3, [r0, r2]
    add    r2, r2, #4
    add    r3, r3, #(L1_S_SIZE)
    adds   r1, r1, #-1
    bhi    2b
3:    
    ldmia  r4!, {r1,r2,r3}   /* # of sections, PA|attr, VA */
    cmp    r1, #0
    bne    2b    


    mcr    p15, 0, r0, c2, c0, 0    /* Set TTB */
    mcr    p15, 0, r0, c8, c7, 0    /* Flush TLB */

    /* Set the Domain Access register.  Very important! */
    mov    r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
    mcr    p15, 0, r0, c3, c0, 0

    /* Enable MMU */
    mrc    p15, 0, r0, c1, c0, 0
    orr    r0, r0, #CPU_CONTROL_MMU_ENABLE
    mcr    p15, 0, r0, c1, c0, 0
    CPWAIT(r0)

    /* Jump to kernel code in TRUE VA */
    adr    r0, Lstart
    ldr    pc, [r0]

Lstart:
    .word    start


#define MMU_INIT(va,pa,n_sec,attr)  \
    .word    n_sec                 ;\
    .word    4*((va)>>L1_S_SHIFT)  ;\
    .word    (pa) | (attr)

#define STARTUP_PAGETABLE_ADDR  0x24100000 + 0x4000

Lstartup_pagetable:
.word STARTUP_PAGETABLE_ADDR

mmu_init_table:    
    /* fill all table VA==PA */
    MMU_INIT(0x00000000, 0x00000000, 1<<(32-L1_S_SHIFT), L1_TYPE_S|L1_S_AP(AP_KRW))
    /* map in peripheral space */
    MMU_INIT(0xFD000000, 0x70000000, 1, L1_TYPE_S|L1_S_AP(AP_KRW))
    /* map SDRAM VA==PA, WT cacheable */
    MMU_INIT(0x24100000, 0x24100000, 15, L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW))
    /* map VA 0xc0000000..0xc0efffff to PA 0x24100000..0x24ffffff */
    MMU_INIT(0xc0000000, 0x24100000, 15, L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW))
    .word 0    /* end of table */

vx115_vep_machdep.c

/*
 * Copyright (c) 2006 Jon Sevy <jsevy@cs.drexel.edu>
 * All rights reserved.
 * 
 * Based on smdk2800_machdep.c
 *
 * Copyright (c) 2002, 2003, 2005 Fujitsu Component Limited
 * Copyright (c) 2002, 2003, 2005 Genetec Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of The Fujitsu Component Limited nor the name of
 *    Genetec corporation may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY FUJITSU COMPONENT LIMITED AND GENETEC
 * CORPORATION ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL FUJITSU COMPONENT LIMITED OR GENETEC
 * CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Copyright (c) 2001,2002 ARM Ltd
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ARM LTD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * Copyright (c) 1997,1998 Mark Brinicombe.
 * Copyright (c) 1997,1998 Causality Limited.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Mark Brinicombe
 *    for the NetBSD Project.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Machine dependent functions for kernel setup for integrator board
 *
 * Created      : 24/11/97
 */

/*
 * Machine dependent functions for kernel setup for Samsung SMDK2800
 * derived from integrator_machdep.c
 */

#include <sys/cdefs.h>

#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_ipkdb.h"
#include "opt_pmap_debug.h"
#include "opt_md.h"

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/exec.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/reboot.h>
#include <sys/termios.h>
#include <sys/ksyms.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>
#include <dev/md.h>

#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#ifdef KGDB
#include <sys/kgdb.h>
#endif

#include <machine/bootconfig.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/frame.h>
#include <machine/intr.h>
#include <arm/undefined.h>

#include <arm/arm32/machdep.h>

#include <arm/vx115/vx115_reg.h>
#include <arm/vx115/vx115_var.h>
#include <evbarm/vx115_vep/vx115_vep_var.h>

#include "ksyms.h"

/* Kernel text starts 2MB in from the bottom of the kernel address space. */
#define KERNEL_TEXT_BASE    (KERNEL_BASE + KERNEL_TEXT_OFFSET)
#define KERNEL_VM_BASE      (KERNEL_BASE + KERNEL_VM_OFFSET)



/*
 * Address to call from cpu_reset() to reset the machine.
 * This is machine architecture dependent as it varies depending
 * on where the ROM appears when you turn the MMU off.
 */
u_int cpu_reset_address = (u_int)0;

/* Define various stack sizes in pages */
#define IRQ_STACK_SIZE    1
#define ABT_STACK_SIZE    1
#ifdef IPKDB
#define UND_STACK_SIZE    2
#else
#define UND_STACK_SIZE    1
#endif

BootConfig bootconfig;        /* Boot config storage */
char *boot_args = NULL;
char *boot_file = NULL;

vm_offset_t physical_start;
vm_offset_t physical_freestart;
vm_offset_t physical_freeend;
vm_offset_t physical_end;
u_int free_pages;
vm_offset_t pagetables_start;
int physmem = 0;

#ifndef PMAP_STATIC_L1S
int max_processes = 64;        /* Default number */
#endif                /* !PMAP_STATIC_L1S */

/* Physical and virtual addresses for some global pages */
pv_addr_t systempage;
pv_addr_t irqstack;
pv_addr_t undstack;
pv_addr_t abtstack;
pv_addr_t kernelstack;

vm_offset_t msgbufphys;

extern u_int data_abort_handler_address;
extern u_int prefetch_abort_handler_address;
extern u_int undefined_handler_address;

#ifdef PMAP_DEBUG
extern int pmap_debug_level;
#endif

#define KERNEL_PT_SYS           0    /* L2 table for mapping zero page */
#define KERNEL_PT_KERNEL        1    /* L2 table for mapping kernel */
#define KERNEL_PT_KERNEL_NUM    3    /* L2 tables for mapping kernel VM */

#define KERNEL_PT_VMDATA        (KERNEL_PT_KERNEL + KERNEL_PT_KERNEL_NUM)

#define KERNEL_PT_VMDATA_NUM    4    /* start with 16MB of KVM */
#define NUM_KERNEL_PTS          (KERNEL_PT_VMDATA + KERNEL_PT_VMDATA_NUM)

pv_addr_t kernel_pt_table[NUM_KERNEL_PTS];

struct user *proc0paddr;

/* Prototypes */

void consinit(void);
void kgdb_port_init(void);


#include <arm/vx115/vx115_com.h>

/*
 * Define the default console speed for the board.  This is generally
 * what the firmware provided with the board defaults to.
 */
#ifndef CONSPEED
#define CONSPEED B115200    /* TTYDEF_SPEED */
#endif
#ifndef CONMODE
#define CONMODE ((TTYDEF_CFLAG & ~(CSIZE | CSTOPB | PARENB)) | CS8)   /* 8N1 */
#endif

int comcnspeed = CONSPEED;
int comcnmode = CONMODE;

/*
 * void cpu_reboot(int howto, char *bootstr)
 *
 * Reboots the system
 *
 * Deal with any syncing, unmounting, dumping and shutdown hooks,
 * then reset the CPU.
 */
void
cpu_reboot(int howto, char *bootstr)
{

    /*
     * If we are still cold then hit the air brakes
     * and crash to earth fast
     */
    if (cold) {
        doshutdownhooks();
        printf("The operating system has halted.\n");
        printf("Please press any key to reboot.\n\n");
        cngetc();
        printf("rebooting...\n");
        cpu_reset();
        /* NOTREACHED */
    }
    /* Disable console buffering */

    /*
     * If RB_NOSYNC was not specified sync the discs.
     * Note: Unless cold is set to 1 here, syslogd will die during the
     * unmount.  It looks like syslogd is getting woken up only to find
     * that it cannot page part of the binary in as the filesystem has
     * been unmounted.
     */
    if (!(howto & RB_NOSYNC))
        bootsync();

    /* Say NO to interrupts */
    splhigh();

    /* Do a dump if requested. */
    if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP)
        dumpsys();

    /* Run any shutdown hooks */
    doshutdownhooks();

    /* Make sure IRQ's are disabled */
    IRQdisable;

    if (howto & RB_HALT) {
        printf("The operating system has halted.\n");
        printf("Please press any key to reboot.\n\n");
        cngetc();
    }
    printf("rebooting...\n");
    cpu_reset();
    /* NOTREACHED */
}

/*
 * All built-in peripheral registers are statically mapped in start up
 * routine.  This table tells pmap subsystem about it, and to map them
 * at the same position.
 */
static const struct pmap_devmap vx115_vep_devmap[] = 
{
    {
        /* most onchip peripherals */
        VX115_PERIPH_BASE_VIRT,
        VX115_PERIPH_BASE_PHYS,
        VX115_PERIPH_SIZE,
        VM_PROT_READ|VM_PROT_WRITE, 
        PTE_NOCACHE,
    },
    {
        /* shared RAM and remaining peripherals */
        VX115_SM_PCU_RCPC_BASE_VIRT,
        VX115_SM_PCU_RCPC_BASE_PHYS,
        VX115_SM_PCU_RCPC_SIZE,
        VM_PROT_READ|VM_PROT_WRITE, 
        PTE_NOCACHE,
    },
    { 0, 0, 0, 0 }
};


/*
 * u_int initarm(...)
 *
 * Initial entry point on startup. This gets called before main() is
 * entered.
 * It should be responsible for setting up everything that must be
 * in place when main is called.
 * This includes
 *   Taking a copy of the boot configuration structure.
 *   Initialising the physical console so characters can be printed.
 *   Setting up page tables for the kernel
 *   Relocating the kernel to the bottom of physical memory
 */

u_int
initarm(void *arg)
{
    int loop;
    int loop1;
    u_int l1pagetable;
    extern int etext asm("_etext");
    extern int end asm("_end");
    pv_addr_t kernel_l1pt;
    

    /* set up the CPU / MMU / TLB functions */
    if (set_cpufuncs())
        panic("CPU not recognized!");

    /* we register the devmap here so can use bus_space_map the      */
    /* appropriate ranges must have been mapped in vx115_vep_start.S */
    pmap_devmap_register(vx115_vep_devmap);
    
    /* do early setup of interrupt structs needed for spl functionality */
    vx115_intr_bootstrap(PIC1_BASE_PHYS, PIC1_SIZE);
    
    /* initialize console for early output */
    consinit();
    
#ifdef VERBOSE_INIT_ARM
    printf("consinit done\n");
#endif
    
#ifdef KGDB
    kgdb_port_init();
#endif

#ifdef VERBOSE_INIT_ARM
    printf("\nNetBSD/evbarm (Vx115 VEP) booting...\n");
#endif

    /*
     * We have the following memory map
     *
     * Physical Address Range     Description
     * -----------------------    ----------------------------------
     * 0x20000000 - 0x21ffffff    Spansion flash Memory   (32MB)
     * 0x24000000 - 0x24ffffff    SRAM (16MB); lowest 1 MB reserved for comms stack
     * 0x28000000 - 0x29ffffff    Spansion flash Memory   (32MB)
     * 0x2c000000 - 0x2cffffff    SRAM (16MB)
     * 
     * initarm() has the responsibility for creating the kernel
     * page tables.
     * It must also set up various memory pointers that are used
     * by pmap etc.
     */

    /* Fake bootconfig structure for the benefit of pmap.c */
    /* XXX must make the memory description h/w independent */
    bootconfig.dramblocks = 2;
    bootconfig.dram[0].address = SRAM_BANK_0_START + SRAM_BANK_0_OFFSET;
    bootconfig.dram[0].pages = (SRAM_BANK_0_SIZE - SRAM_BANK_0_OFFSET) / PAGE_SIZE;
    bootconfig.dram[1].address = SRAM_BANK_1_START;
    bootconfig.dram[1].pages = SRAM_BANK_1_SIZE / PAGE_SIZE;
    
    /*
     * Set up the variables that define the availablilty of
     * physical memory.  For now, we're going to set
     * physical_freeend to 0x24200000 (where the kernel
     * was loaded), and allocate the memory we need downwards.
     * If we get too close to the bottom of SDRAM, we
     * will panic.  We will update physical_freestart and
     * physical_freeend later to reflect what pmap_bootstrap()
     * wants to see.
     */
    physical_start = bootconfig.dram[0].address;
    physical_end = physical_start + (bootconfig.dram[0].pages * PAGE_SIZE);

    physical_freestart = bootconfig.dram[0].address;
    physical_freeend = bootconfig.dram[0].address + KERNEL_TEXT_OFFSET;

    physmem = (physical_end - physical_start) / PAGE_SIZE;

#ifdef VERBOSE_INIT_ARM
    /* Tell the user about the memory */
    printf("physmemory: %d pages at 0x%08lx -> 0x%08lx\n", physmem,
        physical_start, physical_end - 1);
#endif

    /*
     * The kernel starts 2MB in from the bottom of physical memory.
     * We are going to allocate our bootstrap pages downwards
     * from there.
     *
     * We need to allocate some fixed page tables to get the kernel
     * going.  We allocate one page directory and a number of page
     * tables and store the physical addresses in the kernel_pt_table
     * array.
     *
     * The kernel page directory must be on a 16K boundary.  The page
     * tables must be on 4K boundaries.  What we do is allocate the
     * page directory on the first 16K boundary that we encounter, and
     * the page tables on 4K boundaries otherwise.  Since we allocate
     * at least 3 L2 page tables, we are guaranteed to encounter at
     * least one 16K aligned region.
     */

#ifdef VERBOSE_INIT_ARM
    printf("Allocating page tables\n");
#endif

    free_pages = (physical_freeend - physical_freestart) / PAGE_SIZE;

#ifdef VERBOSE_INIT_ARM
    printf("freestart = 0x%08lx, free_pages = %d (0x%08x)\n", physical_freestart, free_pages, free_pages);
#endif


    /* Define macros to simplify memory allocation */
#define    valloc_pages(var, np)                \
    alloc_pages((var).pv_pa, (np));            \
    (var).pv_va = KERNEL_BASE + (var).pv_pa - physical_start;

#define alloc_pages(var, np)                \
    physical_freeend -= ((np) * PAGE_SIZE);        \
    if (physical_freeend < physical_freestart)    \
        panic("initarm: out of memory");    \
    (var) = physical_freeend;            \
    free_pages -= (np);                \
    memset((char *)(var), 0, ((np) * PAGE_SIZE));


    loop1 = 0;
    kernel_l1pt.pv_pa = 0;
    for (loop = 0; loop <= NUM_KERNEL_PTS; ++loop) {
        /* Are we 16KB aligned for an L1 ? */
        if (((physical_freeend - L1_TABLE_SIZE) & (L1_TABLE_SIZE - 1)) == 0
            && kernel_l1pt.pv_pa == 0) {
            valloc_pages(kernel_l1pt, L1_TABLE_SIZE / PAGE_SIZE);
        } else {
            valloc_pages(kernel_pt_table[loop1],
                L2_TABLE_SIZE / PAGE_SIZE);
            ++loop1;
        }
    }

    /* This should never be able to happen but better confirm that. */
    if (!kernel_l1pt.pv_pa || (kernel_l1pt.pv_pa & (L1_TABLE_SIZE-1)) != 0)
        panic("initarm: Failed to align the kernel page directory\n");

    /*
     * Allocate a page for the system page mapped to V0x00000000
     * This page will just contain the system vectors and can be
     * shared by all processes.
     */
    alloc_pages(systempage.pv_pa, 1);

    /* Allocate stacks for all modes */
    valloc_pages(irqstack, IRQ_STACK_SIZE);
    valloc_pages(abtstack, ABT_STACK_SIZE);
    valloc_pages(undstack, UND_STACK_SIZE);
    valloc_pages(kernelstack, UPAGES);

#ifdef VERBOSE_INIT_ARM
    printf("IRQ stack: p0x%08lx v0x%08lx\n", irqstack.pv_pa,
        irqstack.pv_va);
    printf("ABT stack: p0x%08lx v0x%08lx\n", abtstack.pv_pa,
        abtstack.pv_va);
    printf("UND stack: p0x%08lx v0x%08lx\n", undstack.pv_pa,
        undstack.pv_va);
    printf("SVC stack: p0x%08lx v0x%08lx\n", kernelstack.pv_pa,
        kernelstack.pv_va);
#endif

    alloc_pages(msgbufphys, round_page(MSGBUFSIZE) / PAGE_SIZE);

    /*
     * Ok we have allocated physical pages for the primary kernel
     * page tables
     */

#ifdef VERBOSE_INIT_ARM
    printf("Creating L1 page table at 0x%08lx\n", kernel_l1pt.pv_pa);
#endif

    /*
     * Now we start construction of the L1 page table
     * We start by mapping the L2 page tables into the L1.
     * This means that we can replace L1 mappings later on if necessary
     */
    l1pagetable = kernel_l1pt.pv_pa;

    /* Map the L2 pages tables in the L1 page table */
    pmap_link_l2pt(l1pagetable, 0x00000000, &kernel_pt_table[KERNEL_PT_SYS]);
    
    for (loop = 0; loop < KERNEL_PT_KERNEL_NUM; loop++)
        pmap_link_l2pt(l1pagetable, KERNEL_BASE + loop * 0x00400000, &kernel_pt_table[KERNEL_PT_KERNEL + loop]);
    
    for (loop = 0; loop < KERNEL_PT_VMDATA_NUM; loop++)
        pmap_link_l2pt(l1pagetable, KERNEL_VM_BASE + loop * 0x00400000, &kernel_pt_table[KERNEL_PT_VMDATA + loop]);

    /* update the top of the kernel VM */
    pmap_curmaxkvaddr = KERNEL_VM_BASE + (KERNEL_PT_VMDATA_NUM * 0x00400000);

#ifdef VERBOSE_INIT_ARM
    printf("Mapping kernel\n");
#endif

    /* Now we fill in the L2 pagetable for the kernel static code/data */
    {
        size_t textsize = (uintptr_t)&etext - KERNEL_TEXT_BASE;
        size_t totalsize = (uintptr_t)&end - KERNEL_TEXT_BASE;
        u_int logical;

        textsize = (textsize + PGOFSET) & ~PGOFSET;
        totalsize = (totalsize + PGOFSET) & ~PGOFSET;

        logical = KERNEL_TEXT_OFFSET;    /* offset of kernel text from base */

        logical += pmap_map_chunk(l1pagetable, KERNEL_BASE + logical,
            physical_start + logical, textsize,
            VM_PROT_READ | VM_PROT_WRITE, PTE_CACHE);
        logical += pmap_map_chunk(l1pagetable, KERNEL_BASE + logical,
            physical_start + logical, totalsize - textsize,
            VM_PROT_READ | VM_PROT_WRITE, PTE_CACHE);
    }

#ifdef VERBOSE_INIT_ARM
    printf("Constructing L2 page tables\n");
#endif

    /* Map the stack pages */
    pmap_map_chunk(l1pagetable, irqstack.pv_va, irqstack.pv_pa,
        IRQ_STACK_SIZE * PAGE_SIZE, VM_PROT_READ | VM_PROT_WRITE,
        PTE_CACHE);
    pmap_map_chunk(l1pagetable, abtstack.pv_va, abtstack.pv_pa,
        ABT_STACK_SIZE * PAGE_SIZE, VM_PROT_READ | VM_PROT_WRITE,
        PTE_CACHE);
    pmap_map_chunk(l1pagetable, undstack.pv_va, undstack.pv_pa,
        UND_STACK_SIZE * PAGE_SIZE, VM_PROT_READ | VM_PROT_WRITE,
        PTE_CACHE);
    pmap_map_chunk(l1pagetable, kernelstack.pv_va, kernelstack.pv_pa,
        UPAGES * PAGE_SIZE, VM_PROT_READ | VM_PROT_WRITE, PTE_CACHE);

    pmap_map_chunk(l1pagetable, kernel_l1pt.pv_va, kernel_l1pt.pv_pa,
        L1_TABLE_SIZE, VM_PROT_READ | VM_PROT_WRITE, PTE_PAGETABLE);

    for (loop = 0; loop < NUM_KERNEL_PTS; ++loop) {
        pmap_map_chunk(l1pagetable, kernel_pt_table[loop].pv_va,
            kernel_pt_table[loop].pv_pa, L2_TABLE_SIZE,
            VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE);
    }

    /* Map the vector page. */
    pmap_map_entry(l1pagetable, vector_page, systempage.pv_pa, VM_PROT_READ | VM_PROT_WRITE, PTE_CACHE);

    /*
     * map integrated peripherals at same address in l1pagetable
     * so that we can continue to use console.
     */
    pmap_devmap_bootstrap(l1pagetable, vx115_vep_devmap);

    /*
     * Now we have the real page tables in place so we can switch to them.
     * Once this is done we will be running with the REAL kernel page
     * tables.
     */

    /*
     * Update the physical_freestart/physical_freeend/free_pages
     * variables.
     */
    physical_freestart = physical_start + (((((uintptr_t)&end) + PGOFSET) & ~PGOFSET) - KERNEL_BASE);
    physical_freeend = physical_end;
    free_pages = (physical_freeend - physical_freestart) / PAGE_SIZE;


    /* Switch tables */
#ifdef VERBOSE_INIT_ARM
    printf("freestart = 0x%08lx, free_pages = %d (0x%x)\n", physical_freestart, free_pages, free_pages);
    printf("switching to new L1 page table  @%#lx...", kernel_l1pt.pv_pa);
#endif

    cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT);
    setttb(kernel_l1pt.pv_pa);
    cpu_tlb_flushID();
    cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2));

    /*
     * Moved from cpu_startup() as data_abort_handler() references
     * this during uvm init
     */
    proc0paddr = (struct user *)kernelstack.pv_va;
    lwp0.l_addr = proc0paddr;

#ifdef VERBOSE_INIT_ARM
    printf("done!\n");
#endif

#ifdef VERBOSE_INIT_ARM
    printf("bootstrap done.\n");
#endif

    arm32_vector_init(ARM_VECTORS_LOW, ARM_VEC_ALL);

    /*
     * Pages were allocated during the secondary bootstrap for the
     * stacks for different CPU modes.
     * We must now set the r13 registers in the different CPU modes to
     * point to these stacks.
     * Since the ARM stacks use STMFD etc. we must set r13 to the top end
     * of the stack memory.
     */
     
#ifdef VERBOSE_INIT_ARM
    printf("init subsystems: stacks ");
#endif

    set_stackptr(PSR_IRQ32_MODE, irqstack.pv_va + IRQ_STACK_SIZE * PAGE_SIZE);
    set_stackptr(PSR_ABT32_MODE, abtstack.pv_va + ABT_STACK_SIZE * PAGE_SIZE);
    set_stackptr(PSR_UND32_MODE, undstack.pv_va + UND_STACK_SIZE * PAGE_SIZE);

    /*
     * Well we should set a data abort handler.
     * Once things get going this will change as we will need a proper handler.
     * Until then we will use a handler that just panics but tells us why.
     * Initialisation of the vectors will just panic on a data abort.
     * This just fills in a slightly better one.
     */
#ifdef VERBOSE_INIT_ARM
    printf("vectors ");
#endif

    data_abort_handler_address = (u_int)data_abort_handler;
    prefetch_abort_handler_address = (u_int)prefetch_abort_handler;
    undefined_handler_address = (u_int)undefinedinstruction_bounce;

    
#ifdef VERBOSE_INIT_ARM
    printf("undefined ");
#endif
    
    /* Initialise the undefined instruction handlers */
    undefined_init();

#ifdef VERBOSE_INIT_ARM
    printf("page ");
#endif

    /* Load memory into UVM. */
    uvm_setpagesize();    /* initialize PAGE_SIZE-dependent variables */
    uvm_page_physload(atop(physical_freestart), atop(physical_freeend),
        atop(physical_freestart), atop(physical_freeend),
        VM_FREELIST_DEFAULT);

#ifdef VERBOSE_INIT_ARM
    printf("pmap ");
#endif

    /* Boot strap pmap telling it where the kernel page table is */
    pmap_bootstrap((pd_entry_t *)kernel_l1pt.pv_va, KERNEL_VM_BASE,
        KERNEL_VM_BASE + KERNEL_VM_SIZE);

#ifdef VERBOSE_INIT_ARM
    printf("done.\n");
#endif

#ifdef IPKDB
    /* Initialise ipkdb */
    ipkdb_init();
    if (boothowto & RB_KDB)
        ipkdb_connect(0);
#endif

#ifdef KGDB
    if (boothowto & RB_KDB) {
        kgdb_debug_init = 1;
        kgdb_connect(1);
    }
#endif

#if NKSYMS || defined(DDB) || defined(LKM)
    /* Firmware doesn't load symbols. */
    ksyms_init(0, NULL, NULL);
#endif

#ifdef DDB
    db_machine_init();
    if (boothowto & RB_KDB)
        Debugger();
#endif

    /* We return the new stack pointer address */
    return (kernelstack.pv_va + USPACE_SVC_STACK_TOP);
}

void
consinit(void)
{
    bus_space_handle_t ioh;
    static int consinit_done = 0;
    
    if (consinit_done != 0)
        return;

    consinit_done = 1;
    
    /* map the serial interface range to get a bus handle */
    bus_space_map(&vx115_bs_tag, ASCC0_BASE_PHYS, ASCC0_SIZE, 0, &ioh);

    /* initialize the console functions */
    if (vx115_com_cnattach(&vx115_bs_tag, ASCC0_BASE_PHYS, ioh, comcnspeed, comcnmode))
    {
        panic("can't init serial console");
    }

    consinit_done = 0;
}


Back to NetBSD Documentation: Kernel