<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<article>
  <title>Porting NetBSD To A New ARM SoC</title>

  <sect1 remap="h1">
    <title>Introduction</title>

    <para>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.</para>

    <sect2 remap="h2">
      <title>Porting Variants</title>

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

      <para><itemizedlist>
          <listitem>
            <para>Processor architecture port</para>
          </listitem>
        </itemizedlist> 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. <itemizedlist>
          <listitem>
            <para>SoC port</para>
          </listitem>
        </itemizedlist> 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. <itemizedlist>
          <listitem>
            <para>Platform port</para>
          </listitem>
        </itemizedlist> 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.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Scope</title>

      <para>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.</para>

      <para>Such a "basic" port requires addressing a relatively small number
      of issues, described in the following sections. <itemizedlist>
          <listitem>
            <para><link linkend="building">Building NetBSD system
            components</link></para>
          </listitem>

          <listitem>
            <para><link linkend="autoconfig">The autoconfiguration
            system</link></para>
          </listitem>

          <listitem>
            <para><link linkend="kernel_config">Kernel
            configuration</link></para>
          </listitem>

          <listitem>
            <para><link linkend="initialization">Kernel startup and board
            initialization</link></para>
          </listitem>

          <listitem>
            <para><link linkend="bus">Bus operations</link></para>
          </listitem>

          <listitem>
            <para><link linkend="interrupts">Interrupt and system priority
            level&nbsp;handling</link></para>
          </listitem>

          <listitem>
            <para><link linkend="timer">System timer</link></para>
          </listitem>

          <listitem>
            <para><link linkend="serial">Serial driver and
            console</link></para>
          </listitem>

          <listitem>
            <para><link linkend="device_major">Device major number
            support</link></para>
          </listitem>

          <listitem>
            <para><link linkend="arm_variant">Supporting the ARM925EJ-S
            variant</link></para>
          </listitem>

          <listitem>
            <para><link linkend="observations">Observations and
            conclusions</link></para>
          </listitem>
        </itemizedlist> Each of these involves creating various configuration,
      source and header files, and the contents of these files is discussed in
      the subsequent sections.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Acknowledgments and References</title>

      <para>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.</para>

      <para>There's a great deal of information available on the <ulink
      url="http://www.netbsd.org/">NetBSD Project website</ulink> on building
      and working with NetBSD systems. Of particular interest for building is
      the <ulink url="http://www.netbsd.org/guide/en/">NetBSD Guide</ulink> ,
      especially the sections on <ulink
      url="http://www.netbsd.org/guide/en/part-compile.xml">building</ulink>
      and <ulink
      url="http://www.netbsd.org/guide/en/chap-build.xml">cross-compiling</ulink>
      .</para>

      <para>Also invaluable is the web-based source cross-referencing tool
      initially provided as the <ulink url="http://lxr.linux.no/">Linux
      Cross-Reference</ulink> by the <ulink
      url="http://lxr.sourceforge.net/">LXR Project</ulink> . This tool has
      been applied to the NetBSD source (as well as many others) by Robert
      Watson in his <ulink url="http://fxr.watson.org/">FreeBSD and Linux
      Kernel Cross-Reference</ulink>.</para>

      <para></para>

      <para><screen>Jon Sevy
<ulink url="mailto:jsevy@cs.drexel.edu">jsevy@cs.drexel.edu
</ulink><ulink url="http://gicl.cs.drexel.edu/people/sevy">http://gicl.cs.drexel.edu/people/sevy</ulink></screen></para>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="building" />Building</title>

    <para>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).</para>

    <para>The build process consists of several steps:</para>

    <para><itemizedlist>
        <listitem>
          <para>Building the toolchain</para>
        </listitem>

        <listitem>
          <para>Building the kernel</para>
        </listitem>

        <listitem>
          <para>Building the userspace libraries and applications</para>
        </listitem>

        <listitem>
          <para>Building the root filesystem</para>
        </listitem>
      </itemizedlist> NetBSD comes with a wonderful shell script, build.sh,
    that makes all of this easy.</para>

    <sect2 remap="h2">
      <title>Building the Toolchain</title>

      <para>Building the toolchain can be accomplished using the build.sh
      script: just run</para>

      <para><screen>./build.sh -u -m evbarm tools        </screen> from within
      the top-level source directory.</para>

      <para>&nbsp;Parameters:</para>

      <para><itemizedlist>
          <listitem>
            <para>-u:&nbsp;&nbsp;&nbsp; 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)</para>
          </listitem>

          <listitem>
            <para>-m evbarm:&nbsp;&nbsp;&nbsp; the machine architecture type;
            this is for ARM evaluation boards</para>
          </listitem>

          <listitem>
            <para>tools: build just&nbsp;the toolchain, not the whole
            system</para>
          </listitem>
        </itemizedlist> 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 toochain) 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</para>

      <para><screen>yum install compat-gcc-3.2        </screen></para>

      <para>The toolchain build process then becomes</para>

      <para><screen>export HOST_CC=gcc32
export HOST_CXX=g++32
./build.sh -u -m evbarm tools        </screen> 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.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Building the Kernel</title>

      <para>Building the NetBSD kernel is also accomplished using the build.sh
      script: just run</para>

      <para><screen>./build.sh -u -m evbarm kernel=VX115_VEP</screen> from
      within the top-level source directory.</para>

      <para>Parameters:</para>

      <para><itemizedlist>
          <listitem>
            <para>-u:&nbsp;&nbsp;&nbsp; don't run "make clean" before
            building; this prevents the toolchain from being rebuilt</para>
          </listitem>

          <listitem>
            <para>-m evbarm:&nbsp;&nbsp;&nbsp; the machine architecture type;
            this is for ARM evaluation boards</para>
          </listitem>

          <listitem>
            <para>kernel=VX115_VEP:&nbsp;&nbsp;&nbsp; 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.</para>
          </listitem>
        </itemizedlist></para>

      <para>This command performs a number of operations:</para>

      <para><itemizedlist>
          <listitem>
            <para>creates a build directory
            sys/arch/evbarm/compile/obj/VX115_VEP</para>
          </listitem>

          <listitem>
            <para>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; &nbsp;it actually creates
            source files for the autoconfig framework specific to the system
            and device configuration. This is discussed in more detail in the
            <link linkend="autoconfig">section on
            autoconfiguration</link>.</para>
          </listitem>

          <listitem>
            <para>runs "make depend" on the source files to create the
            dependency tree for the source files</para>
          </listitem>

          <listitem>
            <para>runs "make" to build the kernel and drivers</para>
          </listitem>
        </itemizedlist> 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,&nbsp;and may contain
      the output in different formats depending on the configuration.</para>

      <para><itemizedlist>
          <listitem>
            <para>netbsd: ELF object file</para>
          </listitem>

          <listitem>
            <para>netbsd.bin: netbsd converted to plain binary for direct
            loading into memory</para>
          </listitem>

          <listitem>
            <para>netbsd.bin.srec: netbsd.bin converted to S-record format for
            loading to flash</para>
          </listitem>

          <listitem>
            <para>netbsd.gdb: ELF object file with debugging symbols (for use
            with debugger)</para>
          </listitem>
        </itemizedlist></para>
    </sect2>

    <sect2 remap="h2">
      <title>Building the Userspace Libraries and Applications</title>

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

      <para><screen>./build.sh -u -m evbarm</screen> from within the top-level
      source directory. This will create a directory obj/destdir.evbarm with
      all of the userspace files.&nbsp;</para>
    </sect2>

    <sect2 remap="h2">
      <title>Building the Root Filesystem</title>

      <para>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.)</para>

      <para><itemizedlist>
          <listitem>
            <para>rootfs_ramdisk</para>

            <itemizedlist>
              <listitem>
                <para>bin</para>

                <itemizedlist>
                  <listitem>
                    <para>[ cat chmod cp date df echo ed expr hostname kill ln
                    ls&nbsp; mkdir&nbsp; mv&nbsp; ps&nbsp; pwd&nbsp; rm&nbsp;
                    rmdir&nbsp; sh&nbsp; sleep&nbsp; stty&nbsp; sync&nbsp;
                    test</para>
                  </listitem>
                </itemizedlist>
              </listitem>

              <listitem>
                <para>dev</para>

                <itemizedlist>
                  <listitem>
                    <para>contents auto-generated as discussed below</para>
                  </listitem>
                </itemizedlist>
              </listitem>

              <listitem>
                <para>etc</para>

                <itemizedlist>
                  <listitem>
                    <para>mkttys&nbsp; passwd.conf&nbsp; rc.conf&nbsp;
                    rc.lkm&nbsp; rc.shutdown&nbsp; ttys group&nbsp;
                    motd&nbsp;rc&nbsp; rc.local&nbsp; rc.subr</para>
                  </listitem>

                  <listitem>
                    <para>rc.d</para>

                    <itemizedlist>
                      <listitem>
                        <para>local motd mountall root</para>
                      </listitem>
                    </itemizedlist>
                  </listitem>
                </itemizedlist>
              </listitem>

              <listitem>
                <para>lib</para>

                <itemizedlist>
                  <listitem>
                    <para>libcrypt.so&nbsp; libcrypt.so.0&nbsp;
                    libcrypt.so.0.2&nbsp; libc.so&nbsp; libc.so.12&nbsp;
                    libc.so.12.128.2&nbsp; libedit.so&nbsp; libedit.so.2&nbsp;
                    libedit.so.2.9&nbsp; libevent.so&nbsp; libevent.so.0&nbsp;
                    libevent.so.0.2&nbsp; libkvm.so&nbsp; libkvm.so.5&nbsp;
                    libkvm.so.5.2&nbsp; libm.so&nbsp; libm.so.0&nbsp;
                    libm.so.0.2&nbsp; libtermcap.so&nbsp;
                    libtermcap.so.0&nbsp; libtermcap.so.0.5&nbsp;
                    libtermlib.so&nbsp; libtermlib.so.0&nbsp;
                    libtermlib.so.0.5&nbsp; libutil.so&nbsp;
                    libutil.so.7&nbsp; libutil.so.7.6&nbsp; libz.so&nbsp;
                    libz.so.0&nbsp; libz.so.0.4</para>
                  </listitem>
                </itemizedlist>
              </listitem>

              <listitem>
                <para>libexec</para>

                <itemizedlist>
                  <listitem>
                    <para>ld.elf_so</para>
                  </listitem>
                </itemizedlist>
              </listitem>

              <listitem>
                <para>sbin</para>

                <itemizedlist>
                  <listitem>
                    <para>dmesg&nbsp; fsck&nbsp; fsck_ffs&nbsp; init&nbsp;
                    init.shell&nbsp; ldconfig&nbsp; mknod&nbsp; modload&nbsp;
                    modunload&nbsp; mount&nbsp; mount_ffs&nbsp; mount_filecore
                    mount_kernfs&nbsp; mount_mfs&nbsp; mount_null&nbsp;
                    mount_overlay&nbsp; mount_procfs&nbsp; mount_ufs&nbsp;
                    nologin&nbsp; rndctl&nbsp; savecore&nbsp;&nbsp; sysctl
                    ttyflags&nbsp; umount</para>
                  </listitem>
                </itemizedlist>
              </listitem>

              <listitem>
                <para>usr</para>

                <itemizedlist>
                  <listitem>
                    <para>libexec</para>

                    <itemizedlist>
                      <listitem>
                        <para>getty</para>
                      </listitem>
                    </itemizedlist>
                  </listitem>
                </itemizedlist>
              </listitem>
            </itemizedlist>
          </listitem>
        </itemizedlist></para>

      <para>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 &nbsp;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.</para>

      <para><screen>nbmakefs -s 5000k rootfs_ramdisk.img rootfs_ramdisk</screen>
      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.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Inserting the Ramdisk Image into the Kernel</title>

      <para>The ramdisk created above is inserted into the kernel using the
      tool mdsetimage:</para>

      <para><screen>arm--netbsdelf-mdsetimage -v -s netbsd rootfs_ramdisk.img</screen>
      where netbsd is the kernel image created from the kernel build
      process.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Further Packaging</title>

      <para>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.</para>

      <para><screen>arm--netbsdelf-objcopy -S -O binary netbsd netbsd.bin</screen>
      <screen>arm--netbsdelf-objcopy --input-target=binary --output-target=srec \
  --change-addresses=0x20400000 -v netbsd.bin netbsd.bin.srec</screen></para>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="autoconfig" />Device Autoconfiguration Process</title>

    <para>The NetBSD device architecture is a tree consisting of&nbsp;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 <link linkend="kernel_config">kernel configuration section</link> ,
    and described in detail in the <ulink
    url="http://netbsd.gw.com/cgi-bin/man-cgi?config+5+NetBSD-current">NetBSD
    man page on config</ulink> . The&nbsp;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).</para>

    <para>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. &nbsp;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.</para>

    <para>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.</para>

    <para><itemizedlist>
        <listitem>
          <para>Each bus and device defines two methods, match( ) and attach(
          ), and registers them with the autoconfiguration system using the
          CFATTACH_DECL macro.&nbsp;</para>
        </listitem>

        <listitem>
          <para>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.&nbsp;</para>
        </listitem>

        <listitem>
          <para>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.</para>
        </listitem>
      </itemizedlist> Each parent&nbsp;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(
    ).</para>

    <sect2 remap="h2">
      <title>A Few More Details</title>

      <para>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.</para>

      <para></para>

      <para>config_match( )</para>

      <para><itemizedlist>
          <listitem>
            <para>calls config_cfattach_lookup( )&nbsp;with device name as
            argument<itemizedlist>
                <listitem>
                  <para>calls config_cfdriver_lookup( )<itemizedlist>
                      <listitem>
                        <para>runs through allcfdrivers list</para>
                      </listitem>

                      <listitem>
                        <para>returns cfdriver with matching name</para>
                      </listitem>
                    </itemizedlist></para>
                </listitem>

                <listitem>
                  <para>returns config_cfattach_lookup_cd( ) with driver as
                  arg<itemizedlist>
                      <listitem>
                        <para>runs through cfdriver's cfattachlist field to
                        find name match</para>
                      </listitem>

                      <listitem>
                        <para>returns cfattach which matches name</para>
                      </listitem>
                    </itemizedlist></para>
                </listitem>
              </itemizedlist></para>
          </listitem>

          <listitem>
            <para>call the cfattach struct's field cf_match = match function
            of child</para>
          </listitem>
        </itemizedlist></para>

      <para>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</para>

      <para><screen>device foobus {[field1 = default1], ... [fieldn = defaultn]}</screen><itemizedlist>
          <listitem>
            <para>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</para>
          </listitem>

          <listitem>
            <para>defaults become defined values FOOBUSCF_FIELD1_DEFAULT, ...
            ,&nbsp;FOOBUSCF_FIELDn_DEFAULT</para>
          </listitem>
        </itemizedlist> For example, the files.vx115 file defines its internal
      APB with the text</para>

      <para><screen>device  vx115_apb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic</screen>
      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</para>

      <para>Device instances and their locator values for cfdata loc fields
      are specified in&nbsp;VX115_VEP. The format of the device instance
      specification is defined in the NetBSD man page <ulink
      url="http://netbsd.gw.com/cgi-bin/man-cgi?config+5+NetBSD-current">config(5)</ulink>.
      The&nbsp;declarations for the timer device attached to the Vx115's APB
      is</para>

      <para><screen>vx115_clk0   at vx115_apb? addr 0x700C5000 size 0x68  intr 9        </screen>
      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</para>

      <para><screen>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 */
};</screen> 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.</para>

      <para><screen>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&nbsp;&nbsp; = sc-&gt;sc_iot;
    aa.sa_addr&nbsp; = cf-&gt;cf_loc[VX115_APBCF_ADDR];
    aa.sa_size&nbsp; = cf-&gt;cf_loc[VX115_APBCF_SIZE];
    aa.sa_index = cf-&gt;cf_loc[VX115_APBCF_INDEX];
    aa.sa_intr&nbsp; = cf-&gt;cf_loc[VX115_APBCF_INTR];

    if (config_match(parent, cf, &amp;aa))
        config_attach(parent, cf, &amp;aa, vx115_apb_print);
    
    return 0;
}</screen></para>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="kernel_config" />Kernel Configuration</title>

    <para>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).</para>

    <sect2 remap="h2">
      <title>Directories</title>

      <para>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&nbsp;sys/arch/arm/vx115 in this case. The
      platform-specific directory is created in the&nbsp;sys/arch/evbarm
      directory; here, it's&nbsp;sys/arch/evbarm/vx115_vep.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Processor Configuration Files</title>

      <para>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/&lt;processor-name&gt; subdirectory (sys/arch/arm/vx115 in
      this case).</para>

      <sect3 remap="h3">
        <title><link linkend="files.vx115">files.vx115</link></title>

        <para>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</para>

        <para><itemizedlist>
            <listitem>
              <para>basic irq and bus operations</para>
            </listitem>

            <listitem>
              <para>system buses (we have an AHB and APB in this case)</para>
            </listitem>

            <listitem>
              <para>system timer (clock) used for the fundamental system time
              reference</para>
            </listitem>

            <listitem>
              <para>interrupt controller</para>
            </listitem>

            <listitem>
              <para>console serial interface (UART)</para>
            </listitem>
          </itemizedlist> 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, <link
        linkend="files.vx115_vep">files.vx115_vep</link> , described
        below).</para>

        <para>As can bee seen in&nbsp; <link
        linkend="files.vx115">files.vx115</link> , each bus or device has 3
        basic parts to its specification</para>

        <para><itemizedlist>
            <listitem>
              <para>device &lt;device-name&gt;<itemizedlist>
                  <listitem>
                    <para>Provides a name for the device, for use in further
                    references in the code and configuration
                    files.&nbsp;</para>
                  </listitem>

                  <listitem>
                    <para>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
                    <link linkend="VX115_VEP">VX115_VEP</link> , discussed
                    below)</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>attach &lt;device-name&gt; at
              &lt;bus-name&gt;<itemizedlist>
                  <listitem>
                    <para>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</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>file &lt;source-file-path&gt;<itemizedlist>
                  <listitem>
                    <para>Indicates the specified file should be included in
                    the compilation</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>
          </itemizedlist> 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.</para>

        <para>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).</para>
      </sect3>
    </sect2>

    <sect2 remap="h2">
      <title>Board Configuration Files</title>

      <para>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), &nbsp;as
      well as the exact specification of kernel features to be
      supported.</para>

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

      <sect3 remap="h3">
        <title><link linkend="files.vx115_vep">files.vx115_vep</link></title>

        <para>This specifies board-specific files to be included in the build,
        including:</para>

        <para><itemizedlist>
            <listitem>
              <para>CPU and integrated peripheral support, by including
              "arch/arm/vx115/files.vx115"</para>
            </listitem>

            <listitem>
              <para>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)</para>
            </listitem>

            <listitem>
              <para>softinterrupt support (just the generic ARM support is
              used here)</para>
            </listitem>
          </itemizedlist> &nbsp;Again, the particular file files.vx115_vep
        specifies the bare minimum to get the system going.</para>
      </sect3>

      <sect3 remap="h3">
        <title><link linkend="mk.vx115_vep">mk.vx115_vep</link></title>

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

        <para><itemizedlist>
            <listitem>
              <para>Indicate first file in image (start code)</para>

              <para>SYSTEM_FIRST_OBJ=vx115_vep_start.o</para>
            </listitem>

            <listitem>
              <para>&nbsp;Indicate any final steps to perform on image after
              main image made</para>

              <para>SYSTEM_LD_TAIL_EXTRA+=; \</para>
            </listitem>

            <listitem>
              <para>Specify make target additions to make above further kernel
              images</para>

              <para>EXTRA_KERNELS+=
              ${KERNELS:@.KERNEL.@${.KERNEL.}.bin@}</para>

              <para>SYSTEM_FIRST_SFILE=&nbsp;&nbsp;
              &nbsp;${THISARM}/vx115_vep/vx115_vep_start.S</para>

              <para>echo ${OBJCOPY} -S -O binary $@ $@.bin; \</para>

              <para>${OBJCOPY} -S -O binary $@ $@.bin; \</para>

              <para>echo gzip \&lt; $@.bin \&gt; $@.bin.gz; \</para>

              <para>gzip &lt; $@.bin &gt; $@.bin.gz</para>

              <para>EXTRA_KERNELS+=
              ${KERNELS:@.KERNEL.@${.KERNEL.}.bin.gz@}</para>
            </listitem>
          </itemizedlist></para>
      </sect3>

      <sect3 remap="h3">
        <title><link linkend="std.vx115_vep">std.vx115_vep</link></title>

        <para>This specifies general build options for board.</para>

        <para><itemizedlist>
            <listitem>
              <para>Specify the machine and processor architecture</para>

              <para><code>machine evbarm arm </code></para>
            </listitem>
          </itemizedlist><itemizedlist>
            <listitem>
              <para>Include files.vx115_vep</para>
            </listitem>
          </itemizedlist><itemizedlist>
            <listitem>
              <para>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)</para>

              <para><code>options HZ=64 </code></para>
            </listitem>
          </itemizedlist><itemizedlist>
            <listitem>
              <para>Specify kernel virtual and physical load locations, used
              in arch/evbarm/conf/Makefile.evbarm.inc</para>

              <para><code>makeoptions KERNEL_BASE_PHYS=0x24300000
              </code></para>
            </listitem>
          </itemizedlist><itemizedlist>
            <listitem>
              <para>Define board type and include makefile additions</para>

              <para><code>makeoptions BOARDTYPE="vx115_vep" makeoptions
              BOARDMKFRAG="${THISARM}/conf/mk.vx115_vep" </code></para>
            </listitem>
          </itemizedlist><itemizedlist>
            <listitem>
              <para>Specify interrupt implementation&nbsp;&nbsp;&nbsp;</para>

              <para><code>options
              ARM_INTR_IMPL="&lt;arch/arm/vx115/vx115_intr.h&gt;"
              </code></para>
            </listitem>
          </itemizedlist><itemizedlist>
            <listitem>
              <para>Additional defines</para>

              <para><code>makeoptions
              KERNEL_BASE_VIRT=0xc0200000</code></para>
            </listitem>
          </itemizedlist></para>
      </sect3>

      <sect3 remap="h3">
        <title><link linkend="VX115_VEP">VX115_VEP</link></title>

        <para>This is the&nbsp;main config file, passed as argument to
        nbconfig. It's similar to the Linux .config file.</para>

        <para><itemizedlist>
            <listitem>
              <para>Include std.vx115_vep (which includes files.vx115_vep and
              specifies mk.vx115_vep)</para>
            </listitem>

            <listitem>
              <para>All other main kernel options</para>
            </listitem>

            <listitem>
              <para>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 <link
              linkend="autoconfig">autoconfiguration section</link>, and in
              detail in the <ulink
              url="http://netbsd.gw.com/cgi-bin/man-cgi?config+5+NetBSD-current">NetBSD
              config(5) man page</ulink> .</para>
            </listitem>
          </itemizedlist></para>

        <blockquote>
          <para># The main bus device</para>

          <para>mainbus0 at root</para>

          <para># The boot cpu</para>

          <para>cpu0 at mainbus?</para>

          <para># Vx115 AHB and APB</para>

          <para>vx115_ahb0 at mainbus?</para>

          <para>vx115_apb0 at mainbus?</para>

          <para># On-chip interrupt controller</para>

          <para>vx115_pic0 at vx115_apb? addr 0x700C1000 size 0x14c</para>

          <para># On-chip timer</para>

          <para>vx115_clk0 at vx115_apb? addr 0x700C5000 size 0x68&nbsp; intr
          9</para>

          <para># On-chip serial UART</para>

          <para>vx115_com0 at vx115_apb? addr 0x700E2000 size 0x7c&nbsp; intr
          19</para>
        </blockquote>
      </sect3>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="initialization" />Initialization</title>

    <sect2 remap="h2">
      <title>Initial startup</title>

      <para>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 <link linkend="mk.vx115_vep">mk.vx115_vep</link>
      (discussed in the <link linkend="kernel_config">section on
      configuration</link> ); the source file is <link
      linkend="vx115_vep_start.S">vx115_vep_start.S</link> in this
      project.</para>

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

      <para><itemizedlist>
          <listitem>
            <para>Copy the kernel to RAM</para>
          </listitem>

          <listitem>
            <para>Set up initial MMU page tables, using all section (1 MB)
            maps to start. In particular:<itemizedlist>
                <listitem>
                  <para>Map all physical addresses to same virtual addresses
                  (VA == PA), for any peripherals that may need this</para>
                </listitem>

                <listitem>
                  <para>Map the physical peripheral space to the corresponding
                  virtual address range</para>
                </listitem>

                <listitem>
                  <para><itemizedlist>
                      <listitem>
                        <para>Do this so can use bus mapping functions during
                        setup in the init_arm( ) function</para>
                      </listitem>
                    </itemizedlist></para>
                </listitem>

                <listitem>
                  <para>Map the RAM with VA == PA, but set to be
                  cacheable</para>
                </listitem>

                <listitem>
                  <para>Map kernel virtual addresses to RAM physical
                  addresses</para>
                </listitem>
              </itemizedlist></para>
          </listitem>

          <listitem>
            <para>Enable the MMU</para>
          </listitem>
        </itemizedlist> 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.</para>
    </sect2>

    <sect2 remap="h2">
      <title>Machine startup</title>

      <para>The file <link
      linkend="vx115_vep_machdep.c">vx115_vep_machdep.c</link> 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 &nbsp;initarm function with minor changes.</para>

      <sect3 remap="h3">
        <title>struct pmap_devmap vx115_vep_devmap[ ]</title>

        <itemizedlist>
          <listitem>
            <para>array of specifications of virtual-physical memory mappings
            for peripherals</para>
          </listitem>
        </itemizedlist>
      </sect3>

      <sect3 remap="h3">
        <title>initarm( )</title>

        <para>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.</para>

        <para><itemizedlist>
            <listitem>
              <para>call set_cpufuncs to set up basic cpu functions</para>
            </listitem>

            <listitem>
              <para>call pmap_devmap_register(vx115_vep_devmap) so can use
              bus_space_map; the&nbsp;appropriate ranges must have been mapped
              in vx115_vep_start.S</para>
            </listitem>

            <listitem>
              <para>call vx115_intr_bootstrap(PIC1_BASE_PHYS, PIC1_SIZE) to do
              early setup of interrupts needed for spl functionality</para>
            </listitem>

            <listitem>
              <para>call consinit for early console output</para>
            </listitem>

            <listitem>
              <para>set up bootconfig struct</para>
            </listitem>

            <listitem>
              <para>set values of memory parameters<itemizedlist>
                  <listitem>
                    <para>physical_start, physical_end, physical_freestart,
                    physical_freeend, physmem</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>allocate pages for L1 page directory and L2 page
              tables</para>
            </listitem>

            <listitem>
              <para>allocate pages for vectors, stacks, msgbuf</para>
            </listitem>

            <listitem>
              <para>set up page tables<itemizedlist>
                  <listitem>
                    <para>call pmap_link_l2pt to link L2 page tables into L1
                    page table</para>
                  </listitem>

                  <listitem>
                    <para>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</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>call pmap_devmap_bootstrap to map static devices</para>
            </listitem>

            <listitem>
              <para>call cpu_domains to set cpu domains</para>
            </listitem>

            <listitem>
              <para>call setttb to set page table base</para>
            </listitem>

            <listitem>
              <para>call cpu_tlb_flushID to flush TLB for I and D</para>
            </listitem>

            <listitem>
              <para>call arm32_vector_init</para>
            </listitem>

            <listitem>
              <para>call set_stackptr to set stack pointers</para>
            </listitem>

            <listitem>
              <para>set data abort, prefetch abort, undefined instruction
              handlers</para>
            </listitem>

            <listitem>
              <para>call uvm_setpagesize to set up page size</para>
            </listitem>

            <listitem>
              <para>call uvm_page_physload to load physical pages into VM
              system, and add free pages to free page pool</para>
            </listitem>

            <listitem>
              <para>call pmap_bootstrap to initialize the BSD page map
              structure from the ARM-specific page tables built above</para>
            </listitem>

            <listitem>
              <para>return stack pointer</para>
            </listitem>
          </itemizedlist></para>
      </sect3>

      <sect3 remap="h3">
        <title>consinit( )</title>

        <para>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.</para>
      </sect3>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="bus" />Bus Operations</title>

    <para>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;&nbsp;NetBSD defines many
    generic operations, and&nbsp; any bus-specific operations can be defined
    as needed. The <link linkend="vx115_io.c">vx115_io.c</link> file defines a
    bus_space struct called vx115_bs_tag, as well as several bus-specific
    operations used in this struct.</para>

    <para>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.</para>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="interrupts" />Interrupt Handling</title>

    <para>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,&nbsp; <link
    linkend="vx115_intr.h">vx115_intr.h</link> and <link
    linkend="vx115_intr.c">vx115_intr.c</link> , 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.</para>

    <sect2 remap="h2">
      <title>System Priority Level (SPL) Handlers</title>

      <para>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.</para>

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

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

      <para><screen>
int oldSPL = splnet(newSPL);

/* protected code here */

splx(oldCode);
        </screen></para>

      <para>This functions somewhat like the Linux functions local_irq_save( )
      / local_irq_restore( ) and&nbsp;disable_irq( ) /&nbsp;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.</para>

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

      <para><screen>int _splraise(int);
int _spllower(int);
void splx(int);</screen>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.&nbsp;The splx( ) and
      _spllower( ) functions also check to see if any soft interrupts have
      been unmasked, as discussed below.)</para>

      <para>There are really only two issues for the spl functions:</para>

      <para><itemizedlist>
          <listitem>
            <para>how to mask interrupts</para>
          </listitem>

          <listitem>
            <para>which interrupts to mask for a specified priority
            level</para>
          </listitem>
        </itemizedlist> 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.</para>

      <para>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.</para>

      <para><screen>int vx115_pic_spl_mask[NIPL];
int vx115_pic_spl_soft_mask[NIPL];        </screen> 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:</para>

      <para><screen>
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]);
        
    ...
        
}
        </screen> (The Vx115 interrupt controller has separate registers for
      enabling and disabling interrupts, hence the two write
      operations.)</para>

      <para>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.&nbsp;</para>

      <para>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&nbsp;vx115_pic_spl_soft_mask array corresponding to the current
      system level:</para>

      <para><screen>
/* if there are software interrupts pending, process */
if (softint_pending &amp; vx115_pic_spl_soft_mask[current_spl_level])
    vx115_do_pending();
        </screen> 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&nbsp;"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).</para>

      <para>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.</para>

      <sect3 remap="h3">
        <title>vx115_pic_init_interrupt_masks( )</title>

        <itemizedlist>
          <listitem>
            <para>called at system init time (from the interrupt controller
            attach function)</para>
          </listitem>

          <listitem>
            <para>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)</para>
          </listitem>
        </itemizedlist>
      </sect3>

      <sect3 remap="h3">
        <title>vx115_update_intr_masks( )</title>

        <itemizedlist>
          <listitem>
            <para>called whenever a new hardware interrupt is registered, with
            the irq number and associated priority level as arguments</para>
          </listitem>

          <listitem>
            <para>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</para>
          </listitem>

          <listitem>
            <para>clear the&nbsp;bit corresponding to the supplied irq
            number&nbsp;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</para>
          </listitem>

          <listitem>
            <para>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...</para>
          </listitem>

          <listitem>
            <para>reset the interrupt controller mask register to reflect the
            change</para>
          </listitem>
        </itemizedlist>
      </sect3>
    </sect2>

    <sect2 remap="h2">
      <title>Interrupt Registration and Dispatching</title>

      <para>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&nbsp;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.</para>

      <sect3 remap="h3">
        <title>vx115_intr_establish( )</title>

        <itemizedlist>
          <listitem>
            <para>store supplied handler, cookie, and priority in handler
            array entry for specified irq number</para>
          </listitem>

          <listitem>
            <para>call vx115_update_intr_masks( ) so SPL masks will reflect
            new irq at specified priority level</para>
          </listitem>
        </itemizedlist>

        <para>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.</para>
      </sect3>

      <sect3 remap="h3">
        <title>vx115_irq_dispatcher( )</title>

        <itemizedlist>
          <listitem>
            <para>save current spl (note that interrupts are enabled - not
            globally disabled - when the interrupt handler is called)</para>
          </listitem>

          <listitem>
            <para>while interrupt status register is nonzero<itemizedlist>
                <listitem>
                  <para>select one of the asserted irqs</para>
                </listitem>

                <listitem>
                  <para>raise the spl to the level corresponding to the
                  selected irq</para>
                </listitem>

                <listitem>
                  <para>call the handler registered for that irq</para>
                </listitem>

                <listitem>
                  <para>clear the interrupt in the controller</para>
                </listitem>

                <listitem>
                  <para>restore the saved spl</para>
                </listitem>
              </itemizedlist></para>
          </listitem>

          <listitem>
            <para>if any soft interrupts are asserted, dispatch to associated
            soft interrupt handlers</para>
          </listitem>
        </itemizedlist>
      </sect3>
    </sect2>

    <sect2 remap="h2">
      <title>Soft Interrupt Registration and Dispatching</title>

      <para>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.</para>

      <para>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:</para>

      <para><itemizedlist>
          <listitem>
            <para>at the end of the hardware dispatcher, since a soft
            interrupt may have been scheduled by one of the hardware interrupt
            handlers</para>
          </listitem>

          <listitem>
            <para>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</para>
          </listitem>

          <listitem>
            <para>when a new soft interrupt is scheduled</para>
          </listitem>
        </itemizedlist> In each of these places, there's a call to
      vx115_do_pending( ) to execute any asserted unmasked soft
      interrupt(s).</para>

      <sect3 remap="h3">
        <title>_setsoftintr( )</title>

        <para>A&nbsp;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:</para>

        <para><itemizedlist>
            <listitem>
              <para>set the appropriate bit in a static variable indicating
              which soft interrupts are asserted</para>
            </listitem>

            <listitem>
              <para>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)</para>
            </listitem>
          </itemizedlist></para>
      </sect3>

      <sect3 remap="h3">
        <title>vx115_do_pending( )</title>

        <para>&nbsp;The&nbsp;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:</para>

        <para><itemizedlist>
            <listitem>
              <para>while&nbsp;softint_pending masked by the
              current&nbsp;vx115_pic_spl_soft_mask is nonzero (has bits
              set)<itemizedlist>
                  <listitem>
                    <para>check each of the four available soft interrupts; if
                    it's asserted and its priority is above the system
                    priority level<itemizedlist>
                        <listitem>
                          <para>clear the associated bit in
                          softint_pending</para>
                        </listitem>

                        <listitem>
                          <para>calls the handlers using the generic ARM soft
                          interrupt function softintr_dispatch( ).</para>
                        </listitem>
                      </itemizedlist></para>
                  </listitem>
                </itemizedlist></para>
            </listitem>
          </itemizedlist></para>
      </sect3>
    </sect2>

    <sect2 remap="h2">
      <title>Initialization</title>

      <para>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.</para>

      <para><screen>
CFATTACH_DECL(vx115_pic, sizeof(struct vx115_pic_softc), vx115_pic_match, vx115_pic_attach, NULL, NULL);
        </screen>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( ),&nbsp; 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.</para>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="timer" />System Timer</title>

    <para>The system timer source code is in the source file <link
    linkend="vx115_clk.c">arch/arm/vx115_clk.c</link> , 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.</para>

    <sect2 remap="h2">
      <title>Initialization</title>

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

    <sect2 remap="h2">
      <title>System clock</title>

      <para>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.</para>

      <para>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.</para>

      <sect3 remap="h3">
        <title>cpu_initclocks( )</title>

        <itemizedlist>
          <listitem>
            <para>register the timer interrupt handler</para>
          </listitem>

          <listitem>
            <para>configure the hardware timer to start generating interrupts
            at the rate HZ</para>
          </listitem>
        </itemizedlist>
      </sect3>

      <sect3 remap="h3">
        <title>vx115_clk_intr( )</title>

        <itemizedlist>
          <listitem>
            <para>handler for timer interrupts</para>
          </listitem>

          <listitem>
            <para>call hardclock( ) so system can update time</para>
          </listitem>
        </itemizedlist>
      </sect3>

      <sect3 remap="h3">
        <title>delay( )</title>

        <itemizedlist>
          <listitem>
            <para>loop until at least the specified number of microseconds has
            elapsed</para>
          </listitem>
        </itemizedlist>
      </sect3>

      <sect3 remap="h3">
        <title>microtime( )</title>

        <itemizedlist>
          <listitem>
            <para>return the time to the nearest microsecond</para>
          </listitem>
        </itemizedlist>
      </sect3>
    </sect2>

    <sect2 remap="h2">
      <title>Time-of-day functions</title>

      <sect3 remap="h3">
        <title>resettodr( )</title>

        <para></para>
      </sect3>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="serial" />Serial Driver</title>

    <para>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 <link
    linkend="vx115_com.c">vx115_com.c</link> , 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.</para>

    <sect2 remap="h2">
      <title>Initialization</title>

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

      <para><itemizedlist>
          <listitem>
            <para>vx115_com_match</para>
          </listitem>

          <listitem>
            <para>vx115_com_attach</para>
          </listitem>
        </itemizedlist> as well as the usual autoconfig attachment
      declaration:</para>

      <para><screen>CFATTACH_DECL(vx115_com, sizeof(struct vx115_com_softc), vx115_com_match, vx115_com_attach, NULL, NULL);</screen></para>
    </sect2>

    <sect2 remap="h2">
      <title>tty functions</title>

      <para>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.</para>

      <para><itemizedlist>
          <listitem>
            <para>vx115_com_start<itemizedlist>
                <listitem>
                  <para>used by the tty subsystem to start output<itemizedlist>
                      <listitem>
                        <para>assigned by the NetBSD tty subsystem during
                        initialization to be the t_linesw-&gt;l_start function
                        of the tty struct, which is called to transmit from
                        the tty buffer</para>
                      </listitem>
                    </itemizedlist></para>
                </listitem>

                <listitem>
                  <para>get output parameters for the softc struct (the byte
                  count and output position in the tty output queue), for use
                  by the interrupt routines&nbsp;</para>
                </listitem>

                <listitem>
                  <para>mark the driver as busy so the softc parameters won't
                  be changed until transmission is done</para>
                </listitem>

                <listitem>
                  <para>turn on transmit interrupts so output will
                  begin<itemizedlist>
                      <listitem>
                        <para>actual output is performed from within interrupt
                        routines</para>
                      </listitem>
                    </itemizedlist></para>
                </listitem>
              </itemizedlist></para>
          </listitem>
        </itemizedlist> <itemizedlist>
          <listitem>
            <para>vx115_com_hwiflow<itemizedlist>
                <listitem>
                  <para>used to provide HW flow control; not used on this
                  platform</para>
                </listitem>
              </itemizedlist></para>
          </listitem>
        </itemizedlist> <itemizedlist>
          <listitem>
            <para>vx115_com_param<itemizedlist>
                <listitem>
                  <para>used for termios parameter changes (baud rate,
                  etc.)</para>
                </listitem>
              </itemizedlist></para>
          </listitem>
        </itemizedlist></para>
    </sect2>

    <sect2 remap="h2">
      <title>cdevsw functions</title>

      <para>These are the basic interface functions for character drivers in
      NetBSD. <itemizedlist>
          <listitem>
            <para>vx115_com_open</para>
          </listitem>

          <listitem>
            <para>vx115_com_close</para>
          </listitem>

          <listitem>
            <para>vx115_com_read</para>
          </listitem>

          <listitem>
            <para>vx115_com_write</para>
          </listitem>

          <listitem>
            <para>vx115_com_ioctl</para>
          </listitem>

          <listitem>
            <para>vx115_com_stop</para>
          </listitem>

          <listitem>
            <para>vx115_com_tty</para>
          </listitem>

          <listitem>
            <para>vx115_com_poll</para>
          </listitem>
        </itemizedlist></para>
    </sect2>

    <sect2 remap="h2">
      <title>consdev functions</title>

      <para>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:</para>

      <para>printf -&gt; kprintf -&gt; KPRINTF_PUTCHAR -&gt; putchar -&gt;
      v_putc -&gt; cnputc -&gt; cn_tab.cn_putc = vx115_com_cnputc</para>

      <para>The functions are very low-level routines that just directly write
      to or read from the serial interface. <itemizedlist>
          <listitem>
            <para>vx115_com_cngetc<itemizedlist>
                <listitem>
                  <para>spin until there's a character in the receive FIFO,
                  then read and return the character</para>
                </listitem>
              </itemizedlist></para>
          </listitem>
        </itemizedlist> <itemizedlist>
          <listitem>
            <para>vx115_com_cnputc<itemizedlist>
                <listitem>
                  <para>spin until there's room in the transmit FIFO, then
                  write the supplied character to the FIFO</para>
                </listitem>
              </itemizedlist></para>
          </listitem>
        </itemizedlist> <itemizedlist>
          <listitem>
            <para>vx115_com_cnpollc</para>
          </listitem>
        </itemizedlist></para>
    </sect2>

    <sect2 remap="h2">
      <title>Interrupt routines</title>

      <para>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.</para>

      <sect3 remap="h3">
        <title>Transmit</title>

        <para>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.&nbsp;</para>

        <sect4 remap="h4">
          <title>vx115_tx_hard</title>

          <itemizedlist>
            <listitem>
              <para>while the transmit FIFO's not full<itemizedlist>
                  <listitem>
                    <para>copy data from the tty output buffer to the FIFO and
                    update the softc count and buffer pointer</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>if the count is 0, transmission's complete<itemizedlist>
                  <listitem>
                    <para>turn off the transmit interrupts</para>
                  </listitem>

                  <listitem>
                    <para>clear the sc_tx_busy flag, which will cause the soft
                    interrupt routine to run</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>
          </itemizedlist>
        </sect4>

        <sect4 remap="h4">
          <title>vx115_com_txsoft</title>

          <itemizedlist>
            <listitem>
              <para>if the sc_tx_busy flag is clear, transmission is
              complete<itemizedlist>
                  <listitem>
                    <para>call ndflush to update the tty buffer pointers to
                    account for the data transmitted</para>
                  </listitem>

                  <listitem>
                    <para>call the tty t_linesw-&gt;l_start method to initiate
                    a new transmission if new data is available<itemizedlist>
                        <listitem>
                          <para>this just directly calls the driver's
                          vx115_com_start method</para>
                        </listitem>
                      </itemizedlist></para>
                  </listitem>
                </itemizedlist></para>
            </listitem>
          </itemizedlist>
        </sect4>
      </sect3>

      <sect3 remap="h3">
        <title>Receive</title>

        <sect4 remap="h4">
          <title>vx115_rx_hard</title>

          <itemizedlist>
            <listitem>
              <para>while the receive FIFO's not empty and the driver receive
              buffer's not full<itemizedlist>
                  <listitem>
                    <para>copy data from the receive FIFO to the driver's
                    receive buffer and update the softc count and buffer
                    pointer</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>if the receive buffer's full, turn off receive
              interrupts</para>
            </listitem>
          </itemizedlist>
        </sect4>

        <sect4 remap="h4">
          <title>vx115_rx_soft</title>

          <itemizedlist>
            <listitem>
              <para>while the receive buffer has characters<itemizedlist>
                  <listitem>
                    <para>read next character from receive buffer, check for
                    framing, parity errors</para>
                  </listitem>

                  <listitem>
                    <para>call the tty method t_linesw-&gt;l_rint to deliver
                    the character to the tty layer</para>
                  </listitem>
                </itemizedlist></para>
            </listitem>

            <listitem>
              <para>update receive buffer pointers and count</para>
            </listitem>

            <listitem>
              <para>turn on receive interrupts if they're off and there's now
              room in buffer</para>
            </listitem>
          </itemizedlist>
        </sect4>
      </sect3>
    </sect2>

    <sect2 remap="h2">
      <title>Early initialization</title>

      <para>Because it's desirable to have serial output early in the boot
      process&nbsp;for debugging purposes&nbsp;during execution of the
      initarm( ) routine, there's an early initialization of the serial driver
      in the init_arm( ) function.</para>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="device_major" />Character and Block Device Major Number
    Registration</title>

    <para>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</para>

    <para><screen>device-major &lt;name&gt; char &lt;num&gt; [block &lt;num&gt;] [&lt;rule&gt;]</screen>
    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").</para>

    <para>For the vx115_com serial device here, the cdevsw struct is
    named</para>

    <para><screen>vx115_com_cdevsw</screen> in the source file vx115_com.c,
    and the corresponding line added to the majors.arm32 file is</para>

    <para><screen>device-major    vx115_com        char 110        vx115_com</screen>
    The major number 110 was selected arbitrarily as an available (unused)
    major number.</para>

    <para>This process is described in detail in the document <ulink
    url="http://www.daemon-systems.org/NetBSD/devsw.xml">"The Auto-Generation
    Block/Character Device Switch Tables by config(8)"</ulink> .</para>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="arm_variant" />Supporting the ARM926EJ-S
    Variant</title>

    <para>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.</para>

    <sect2 remap="h2">
      <title>arch/arm/arm32/cpu.c</title>

      <itemizedlist>
        <listitem>
          <para>an addition was made to the CPU identifier list
          cpuids[]:</para>
        </listitem>
      </itemizedlist>

      <para><screen>{ CPU_ID_ARM926EJS, CPU_CLASS_ARM9ES, "ARM926EJ-S", generic_steppings }</screen></para>

      <itemizedlist>
        <listitem>
          <para>a change was propagated from a more recent kernel version to
          wtnames[14]:</para>
        </listitem>
      </itemizedlist>

      <para><screen>"**unknown 14**" to "write-back-locking-C"</screen></para>
    </sect2>

    <sect2 remap="h2">
      <title>arch/arm/include/armreg.h</title>

      <itemizedlist>
        <listitem>
          <para>the identifier for this ARM9 variant was added to CPU arch
          type defines:</para>
        </listitem>
      </itemizedlist>

      <para><screen>CPU_ID_ARCH_V5TEJ   0x00060000</screen></para>

      <itemizedlist>
        <listitem>
          <para>the ID for this variant was added to CPU ID defines:</para>
        </listitem>
      </itemizedlist>

      <para><screen>#define CPU_ID_ARM926EJS    0x41069260</screen></para>
    </sect2>
  </sect1>

  <sect1 remap="h1">
    <title><anchor id="observations" />Observations and Comparison with Linux
    Porting Efforts</title>

    <para>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.</para>

    <para><itemizedlist>
        <listitem>
          <para>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.</para>
        </listitem>
      </itemizedlist> <itemizedlist>
        <listitem>
          <para>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.</para>
        </listitem>
      </itemizedlist> However, NetBSD has a few drawbacks (some significant),
    particularly for use in embedded systems.</para>

    <para><itemizedlist>
        <listitem>
          <para>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.)</para>
        </listitem>
      </itemizedlist><itemizedlist>
        <listitem>
          <para>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.)</para>
        </listitem>
      </itemizedlist></para>

    <para>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):</para>

    <para><computeroutput><code></code></computeroutput></para>

    <programlisting>NetBSD/evbarm (Vx115 VEP) booting...

physmemory: 3840 pages at 0x24100000 -&gt; 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 -&gt; 700fffff @ fd000000

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

S

devmap: fc000000 -&gt; 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: &lt;unknown&gt;

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

#</programlisting>

    <para><computeroutput><code></code></computeroutput></para>

    <para><screen>Comments/questions: <ulink url="mailto:jsevy@cs.drexel.edu">jsevy@cs.drexel.edu</ulink></screen></para>
  </sect1>

  <sect1>
    <title>Appendix: Source Files</title>

    <para></para>

    <sect2>
      <title>sys/arch/arm/vx115</title>

      <para></para>

      <sect3>
        <title><anchor id="files.vx115" />files.vx115</title>

        <programlisting>#
# 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</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_apb.c" />vx115_apb.c</title>

        <programlisting>/*
 * Copyright (c) 2006 Jon Sevy &lt;jsevy@cs.drexel.edu&gt;
 * All rights reserved.
 * 
 * Based on ixp12x0.c
 *
 * Copyright (c) 2002, 2003
 *    Ichiro FUKUHARA &lt;ichiro@ichiro.org&gt;.
 * 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 &lt;sys/cdefs.h&gt;
__KERNEL_RCSID(0, "$NetBSD: ixp12x0.c,v 1.13 2004/08/30 15:05:16 drochner Exp $");

#include &lt;sys/param.h&gt;
#include &lt;sys/systm.h&gt;
#include &lt;sys/device.h&gt;
#include &lt;uvm/uvm.h&gt;

#include &lt;machine/bus.h&gt;

#include &lt;arm/vx115/vx115_reg.h&gt;
#include &lt;arm/vx115/vx115_var.h&gt;

#include &lt;locators.h&gt;


#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-&gt;sa_size)
        aprint_normal(" addr 0x%lx", sa-&gt;sa_addr);
    if (sa-&gt;sa_size &gt; 1)
        aprint_normal("-0x%lx", sa-&gt;sa_addr + sa-&gt;sa_size - 1);
    if (sa-&gt;sa_intr != VX115_APBCF_INTR_DEFAULT)
        aprint_normal(" intr %d", sa-&gt;sa_intr);
    if (sa-&gt;sa_index != VX115_APBCF_INDEX_DEFAULT)
        aprint_normal(" unit %d", sa-&gt;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-&gt;sc_iot = &amp;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-&gt;sc_iot;
    aa.sa_addr  = cf-&gt;cf_loc[VX115_APBCF_ADDR];
    aa.sa_size  = cf-&gt;cf_loc[VX115_APBCF_SIZE];
    aa.sa_index = cf-&gt;cf_loc[VX115_APBCF_INDEX];
    aa.sa_intr  = cf-&gt;cf_loc[VX115_APBCF_INTR];

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

    return 0;
}

</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_clk.c" />vx115_clk.c</title>

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

/*
 * Agere Vx115 clock functions
 * Copyright (c) 2006, Jon Sevy &lt;jsevy@cs.drexel.edu&gt;
 * 
 * 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 &lt;sys/cdefs.h&gt;
__KERNEL_RCSID(0, "$NetBSD: vx115_clk.c,v 1.4 2005/02/26 12:00:52 simonb Exp $");

#include &lt;sys/types.h&gt;
#include &lt;sys/param.h&gt;
#include &lt;sys/systm.h&gt;
#include &lt;sys/kernel.h&gt;
#include &lt;sys/time.h&gt;
#include &lt;sys/device.h&gt;

#include &lt;dev/clock_subr.h&gt;

#include &lt;machine/bus.h&gt;
#include &lt;machine/intr.h&gt;

#include &lt;arm/cpufunc.h&gt;
#include &lt;arm/vx115/vx115_reg.h&gt;
#include &lt;arm/vx115/vx115_var.h&gt;

#include &lt;opt_hz.h&gt;     /* 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 &lt; 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-&gt;sc_iot, sc-&gt;sc_ioh, offset)
#define vx115_write_clk(offset, value)   bus_space_write_4(sc-&gt;sc_iot, sc-&gt;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-&gt;sc_iot = sa-&gt;sa_iot;
    sc-&gt;sc_intr = sa-&gt;sa_intr;
    
    if (vx115_clk_sc == NULL)
        vx115_clk_sc = sc;

    /* map bus space and get handle */
    if (bus_space_map(sc-&gt;sc_iot, sa-&gt;sa_addr, sa-&gt;sa_size, 0, &amp;sc-&gt;sc_ioh) != 0)
        panic("%s: Cannot map registers", self-&gt;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) &amp; 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-&gt;sc_intr, IPL_CLOCK, vx115_clk_intr, NULL);
    vx115_configure_irq(sc-&gt;sc_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH);
    vx115_enable_irq(sc-&gt;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-&gt;tv_sec = 0;
        tvp-&gt;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-&gt;tv_usec += vx115_timer_count_to_usec(VX115_TIMER_LATCH - current_count);
    
    /* Make sure microseconds doesn't overflow. */
    while (__predict_false(tvp-&gt;tv_usec &gt;= 1000000)) 
    {
        tvp-&gt;tv_usec -= 1000000;
        tvp-&gt;tv_sec++;
    }

    /* Make sure the time has advanced. */
    if (__predict_false(tvp-&gt;tv_sec == lasttv.tv_sec &amp;&amp; tvp-&gt;tv_usec &lt;= lasttv.tv_usec)) 
    {
        tvp-&gt;tv_usec = lasttv.tv_usec + 1;
        if (tvp-&gt;tv_usec &gt;= 1000000) 
        {
            tvp-&gt;tv_usec -= 1000000;
            tvp-&gt;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 &lt; 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 &lt;= 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 &lt;= end) &amp;&amp; (current &gt; start)))
            current = vx115_read_clk(TIM_ITCNT1);
    }
    else
    {
        /* just wait until count value is at or below end value */
        while (current &gt; 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 &gt;= 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 &lt; (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 *)&amp;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 &amp;&amp; !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 &lt; 0)
            deltat = -deltat;
        if (deltat &lt; 2 * SECDAY)
            return;/* all is well */
        printf("WARNING: clock %s %ld days\n", time.tv_sec &lt; 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) &amp;&amp; (todr_settime(todr_handle, (struct timeval *)&amp;time) != 0))
        printf("resettodr: failed to set time\n");
}
</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_com.c" />vx115_com.c</title>

        <programlisting>/*
 * Copyright (c) 2006 Jon Sevy &lt;jsevy@cs.drexel.edu&gt;
 * 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 &lt;sys/cdefs.h&gt;
__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 &gt; 0 &amp;&amp; defined(RND_COM)
#include &lt;sys/rnd.h&gt;
#endif

#include &lt;sys/param.h&gt;
#include &lt;sys/systm.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/conf.h&gt;
#include &lt;sys/file.h&gt;
#include &lt;sys/device.h&gt;
#include &lt;sys/kernel.h&gt;
#include &lt;sys/malloc.h&gt;
#include &lt;sys/tty.h&gt;
#include &lt;sys/uio.h&gt;
#include &lt;sys/vnode.h&gt;

#include &lt;machine/intr.h&gt;
#include &lt;machine/bus.h&gt;

#include &lt;arm/vx115/vx115_reg.h&gt;
#include &lt;arm/vx115/vx115_com.h&gt;
#include &lt;arm/vx115/vx115_com_var.h&gt;
#include &lt;arm/vx115/vx115_var.h&gt;

#include &lt;dev/cons.h&gt;


#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) &amp; COMUNIT_MASK)
#define COMDIALOUT(x)   (minor(x) &amp; COMDIALOUT_MASK)

#define COM_ISALIVE(sc)     ((sc)-&gt;enabled != 0 &amp;&amp; ISSET((sc)-&gt;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) &amp;= ~(f)
#define ISSET(t,f)  ((t) &amp; (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) &amp; 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-&gt;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-&gt;sc_iot = sa-&gt;sa_iot;
    sc-&gt;sc_addr = sa-&gt;sa_addr;

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

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

    vx115_com_attach_subr(sc);
    
    vx115_intr_establish(sa-&gt;sa_intr, IPL_SERIAL, vx115_com_intr, sc);
    vx115_configure_irq(sa-&gt;sa_intr, VX115_INT_SENSE_LEVEL, VX115_INT_POLARITY_HIGH);
    vx115_enable_irq(sa-&gt;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-&gt;sc_iot == vx115_com_cn_sc.sc_iot) &amp;&amp; (sc-&gt;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-&gt;sc_hwflags, COM_HW_CONSOLE);
        SET(sc-&gt;sc_swflags, TIOCFLAG_SOFTCAR);
    }

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

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

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

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

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

    sc-&gt;sc_softintr = softintr_establish(IPL_SOFTSERIAL, vx115_com_soft, sc);

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

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

    SET(sc-&gt;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(&amp;vx115_com_cd, COMUNIT(dev));
    if (sc == NULL || !ISSET(sc-&gt;sc_hwflags, COM_HW_DEV_OK) || sc-&gt;sc_rbuf == NULL)
        return (ENXIO);

    if (ISSET(sc-&gt;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-&gt;sc_hwflags, COM_HW_KGDB))
        return (EBUSY);
#endif

    tp = sc-&gt;sc_tty;

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

    s = spltty();

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

        tp-&gt;t_dev = dev;

        s2 = splserial();
        COM_LOCK(sc);

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

        /* Turn on receive interrupts. */
        sc-&gt;sc_rx_control |= RECEIVE_INTERRUPT_MASK;
        sc-&gt;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-&gt;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-&gt;sc_swflags, TIOCFLAG_CLOCAL))
            SET(t.c_cflag, CLOCAL);
        if (ISSET(sc-&gt;sc_swflags, TIOCFLAG_CRTSCTS))
            SET(t.c_cflag, CRTSCTS);
        if (ISSET(sc-&gt;sc_swflags, TIOCFLAG_MDMBUF))
            SET(t.c_cflag, MDMBUF);
        
        /* Make sure vx115_com_param() will do something. */
        tp-&gt;t_ospeed = 0;
        (void) vx115_com_param(tp, &amp;t);
        tp-&gt;t_iflag = TTYDEF_IFLAG;
        tp-&gt;t_oflag = TTYDEF_OFLAG;
        tp-&gt;t_lflag = TTYDEF_LFLAG;
        ttychars(tp);
        ttsetwater(tp);

        s2 = splserial();
        COM_LOCK(sc);

        /* Clear the input ring, and unblock. */
        sc-&gt;sc_rbput = sc-&gt;sc_rbget = sc-&gt;sc_rbuf;
        sc-&gt;sc_rbavail = VX115_COM_RING_SIZE;
        vx115_com_iflush(sc);
        CLR(sc-&gt;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-&gt;t_linesw-&gt;l_open)(dev, tp);
    if (error)
        goto bad;

    return (0);

bad:
    if (!ISSET(tp-&gt;t_state, TS_ISOPEN) &amp;&amp; tp-&gt;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(&amp;vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc-&gt;sc_tty;

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

    (*tp-&gt;t_linesw-&gt;l_close)(tp, flag);
    ttyclose(tp);

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

    if (!ISSET(tp-&gt;t_state, TS_ISOPEN) &amp;&amp; tp-&gt;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(&amp;vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc-&gt;sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_read\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp-&gt;t_linesw-&gt;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(&amp;vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc-&gt;sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_write\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp-&gt;t_linesw-&gt;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(&amp;vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc-&gt;sc_tty;

    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_poll\n");
    
    if (COM_ISALIVE(sc) == 0)
        return (EIO);
 
    return ((*tp-&gt;t_linesw-&gt;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(&amp;vx115_com_cd, COMUNIT(tp-&gt;t_dev));
    int s;

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

struct tty *
vx115_com_tty(dev_t dev)
{
    struct vx115_com_softc *sc = device_lookup(&amp;vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc-&gt;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(&amp;vx115_com_cd, COMUNIT(dev));
    struct tty *tp = sc-&gt;sc_tty;
    int error;
    int s;

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

    error = (*tp-&gt;t_linesw-&gt;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-&gt;sc_swflags;
        break;

    case TIOCSFLAGS:
        error = suser(p-&gt;p_ucred, &amp;p-&gt;p_acflag); 
        if (error)
            break;
        sc-&gt;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-&gt;sc_iot;
    bus_space_handle_t ioh = sc-&gt;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) &amp; ASCC_FIFO_STATUS_RFE) &amp;&amp; --timo )
    {
        REG_READ(ASCC_TX_RX_FIFO);
    }
    
#ifdef DIAGNOSTIC
    if (!timo)
        printf("%s: com_iflush timeout\n", sc-&gt;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-&gt;sc_tx_control |= ASCC_TX_CTRL_BREAK;
    else
        sc-&gt;sc_tx_control &amp;= ~ASCC_TX_CTRL_BREAK;
        
    if (!sc-&gt;sc_heldchange) 
    {
        if (sc-&gt;sc_tx_busy) 
        {
            sc-&gt;sc_heldtbc = sc-&gt;sc_tbc;
            sc-&gt;sc_tbc = 0;
            sc-&gt;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-&gt;sc_rx_control &amp;= ~RECEIVE_INTERRUPT_MASK;
    sc-&gt;sc_tx_control &amp;= ~TRANSMIT_INTERRUPT_MASK;
    sc-&gt;sc_char_counter_control &amp;= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
    /* set disable bit */
    sc-&gt;sc_rx_control |= ASCC_RX_CTRL_RXID;
    sc-&gt;sc_tx_control |= ASCC_TX_CTRL_TXID;
    
    vx115_com_set_cr(sc);

    if (sc-&gt;disable) {
#ifdef DIAGNOSTIC
        if (!sc-&gt;enabled)
            panic("vx115_com_shutdown: not enabled?");
#endif
        (*sc-&gt;disable)(sc);
        sc-&gt;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 &amp; PARENB)
        cr |= (cflag &amp; PARODD) ? ASCC_RX_CTRL_RXP_ODD : ASCC_RX_CTRL_RXP_EVEN;
    
    cr |= ((cflag &amp; CSIZE) == CS8) ? ASCC_RX_CTRL_RXCHSIZE_8 : ASCC_RX_CTRL_RXCHSIZE_7;
    cr |= (cflag &amp; 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 &amp; PARENB)
        cr |= (cflag &amp; PARODD) ? ASCC_TX_CTRL_TXP_ODD : ASCC_TX_CTRL_TXP_EVEN;
    
    cr |= ((cflag &amp; CSIZE) == CS8) ? ASCC_TX_CTRL_TXCHSIZE_8 : ASCC_TX_CTRL_TXCHSIZE_7;
    cr |= (cflag &amp; 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-&gt;sc_iot;
    bus_space_handle_t ioh = sc-&gt;sc_ioh;
    
    COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_set_cr\n");
    
    /* set control regs and baud divisor */
    REG_WRITE(ASCC_RX_CONTROL, sc-&gt;sc_rx_control);
    REG_WRITE(ASCC_TX_CONTROL, sc-&gt;sc_tx_control);
    REG_WRITE(ASCC_BAUD_DIVISOR, sc-&gt;sc_baud_divisor);
    REG_WRITE(ASCC_CHAR_COUNTER_CONTROL, sc-&gt;sc_char_counter_control);
}



/**************************************************************************
 * tty functions
 * Assigned to associated tty struct fields in vx115_com_open
 */
 
/* Assigned to standard termios line discipline t_linesw-&gt;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(&amp;vx115_com_cd, COMUNIT(tp-&gt;t_dev));
    int s;

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

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

    if (tp-&gt;t_outq.c_cc &lt;= tp-&gt;t_lowat) 
    {
        if (ISSET(tp-&gt;t_state, TS_ASLEEP)) 
        {
            CLR(tp-&gt;t_state, TS_ASLEEP);
            wakeup(&amp;tp-&gt;t_outq);
        }
        selwakeup(&amp;tp-&gt;t_wsel);
        if (tp-&gt;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-&gt;sc_tba = tp-&gt;t_outq.c_cf;
    sc-&gt;sc_tbc = ndqb(&amp;tp-&gt;t_outq, 0);
    
    if (sc-&gt;sc_tbc &gt; 0)
    {
        COM_LOWLEVEL_DEBUG_PRINT_STRING("vx115_com_start: sc-&gt;sc_tbc &gt; 0\n");
        
        SET(tp-&gt;t_state, TS_BUSY);
        sc-&gt;sc_tx_busy = 1;
    
        /* Enable transmit interrupts */
        sc-&gt;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(&amp;vx115_com_cd, COMUNIT(tp-&gt;t_dev));
    int s;

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

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

    sc-&gt;sc_baud_divisor = VX115_BAUD_DIVISOR(t-&gt;c_ospeed);

    /*
     * For the console, always force CLOCAL and !HUPCL, so that the port
     * is always active.
     */
    if (ISSET(sc-&gt;sc_swflags, TIOCFLAG_SOFTCAR) || ISSET(sc-&gt;sc_hwflags, COM_HW_CONSOLE)) 
    {
        SET(t-&gt;c_cflag, CLOCAL);
        CLR(t-&gt;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-&gt;t_ospeed == t-&gt;c_ospeed) &amp;&amp; (tp-&gt;t_cflag == t-&gt;c_cflag))
        return (0);

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

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

    if (!sc-&gt;sc_heldchange) 
    {
        if (sc-&gt;sc_tx_busy) 
        {
            sc-&gt;sc_heldtbc = sc-&gt;sc_tbc;
            sc-&gt;sc_tbc = 0;
            sc-&gt;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-&gt;t_linesw-&gt;l_modem)(tp, 1);

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

    if (!ISSET(t-&gt;c_cflag, CHWFLOW)) {
        if (sc-&gt;sc_tx_stopped) {
            sc-&gt;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 = &amp;vx115_com_cons;
    cn_init_magic(&amp;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 &amp;= ~ASCC_RX_CTRL_RXID;
    tx_control &amp;= ~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) &amp; 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) &amp; ASCC_FIFO_STATUS_RFE)
        ;

    /* get the char */
    c = REG_READ(ASCC_TX_RX_FIFO);
    c &amp;= 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-&gt;t_state, TS_BUSY);
    
    /* adjust tty buffer pointers to account for data that's been sent */
    if (ISSET(tp-&gt;t_state, TS_FLUSH))
        CLR(tp-&gt;t_state, TS_FLUSH);
    else
        ndflush(&amp;tp-&gt;t_outq, (int)(sc-&gt;sc_tba - tp-&gt;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-&gt;t_linesw-&gt;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-&gt;t_linesw-&gt;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-&gt;sc_ebuf;
    get = sc-&gt;sc_rbget;
    scc = cc = VX115_COM_RING_SIZE - sc-&gt;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 &gt;= end)
            get = sc-&gt;sc_rbuf;
        cc--;
    }

    if (cc != scc) 
    {
        sc-&gt;sc_rbget = get;
        s = splserial();
        COM_LOCK(sc);
        
        sc-&gt;sc_rbavail += scc - cc;
        
        if (sc-&gt;sc_rbavail &gt;= 1) 
        {
            /* buffers should be ok again, release possible block */
            if (ISSET(sc-&gt;sc_rx_flags, RX_IBUF_FULL)) 
            {
                CLR(sc-&gt;sc_rx_flags, RX_IBUF_FULL);
                
                /* enable receive interrupts */
                sc-&gt;sc_rx_control |= RECEIVE_INTERRUPT_MASK;
                sc-&gt;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-&gt;sc_rx_ready) 
    {
        sc-&gt;sc_rx_ready = 0;
        vx115_com_rx_soft(sc, sc-&gt;sc_tty);
    }
    
    if (!sc-&gt;sc_tx_busy) 
    {
        /* previous transmissions complete; see if have any new stuff to send */
        vx115_com_tx_soft(sc, sc-&gt;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-&gt;sc_rx_flags, RX_IBUF_FULL)) 
    {
        /* overflow - turn off interrupts */
        sc-&gt;sc_rx_control &amp;= ~RECEIVE_INTERRUPT_MASK;
        sc-&gt;sc_char_counter_control &amp;= ~CHAR_CNT_CNTL_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
        
        return;
    }
    
    /* get receive buffer parameters */
    end = sc-&gt;sc_ebuf;
    put = sc-&gt;sc_rbput;
    cc = sc-&gt;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 &gt; 0) &amp;&amp; !(REG_READ(ASCC_FIFO_STATUS) &amp; ASCC_FIFO_STATUS_RFE))
    {
        c = REG_READ(ASCC_TX_RX_FIFO);
        put[0] = c &amp; 0xff;
        put[1] = (c &gt;&gt; 8) &amp; 0xff;
        cn_check_magic(sc-&gt;sc_tty-&gt;t_dev, put[0], vx115_com_cnm_state);
        put += 2;
        if (put &gt;= end)
            put = sc-&gt;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-&gt;sc_rbput = put;
    sc-&gt;sc_rbavail = cc;
    sc-&gt;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-&gt;sc_rx_flags, RX_IBUF_FULL);
        sc-&gt;sc_rx_control &amp;= ~RECEIVE_INTERRUPT_MASK;
        sc-&gt;sc_char_counter_control &amp;= ~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-&gt;sc_heldchange) 
    {
        vx115_com_set_cr(sc);
        sc-&gt;sc_heldchange = 0;
        sc-&gt;sc_tbc = sc-&gt;sc_heldtbc;
        sc-&gt;sc_heldtbc = 0;
    }

    
    
    /* output the next chunk of the contiguous buffer, if any. */
    if (sc-&gt;sc_tbc &gt; 0) 
    {
        int n = 0;
        
        while ( !(REG_READ(ASCC_FIFO_STATUS) &amp; ASCC_FIFO_STATUS_TFF) )
        {
            if (n &gt;= sc-&gt;sc_tbc)
                break;
            REG_WRITE(ASCC_TX_RX_FIFO, 0xff &amp; *(sc-&gt;sc_tba + n));
            n++;
        }
        sc-&gt;sc_tbc -= n;
        sc-&gt;sc_tba += n;
    }
    
    if (sc-&gt;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-&gt;sc_tx_control &amp;= ~TRANSMIT_INTERRUPT_MASK;
        vx115_com_set_cr(sc);
        sc-&gt;sc_tx_busy = 0;
    }
}


static int
vx115_com_intr(void* arg)
{
    struct vx115_com_softc *sc = arg;
    bus_space_tag_t    iot = sc-&gt;sc_iot;
    bus_space_handle_t ioh = sc-&gt;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 &amp; ASCC_STATUS_ROE)
        {
            /* receive overrun */
            COM_LOWLEVEL_DEBUG_PRINT_STRING("o\n");
        }       

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

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

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

        if (status &amp; (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 &amp; 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-&gt;sc_softintr);

    return (1);
}
</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_intr.h" />vx115_intr.h</title>

        <programlisting>/*
 * Copyright (c) 2006 Jon Sevy &lt;jsevy@cs.drexel.edu&gt;
 * 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 &lt;arm/cpu.h&gt;
#include &lt;arm/armreg.h&gt;
#include &lt;arm/cpufunc.h&gt;
#include &lt;arm/softintr.h&gt;

#include &lt;machine/atomic.h&gt;
#include &lt;machine/bus.h&gt;
#include &lt;machine/intr.h&gt;

#include &lt;arm/vx115/vx115_reg.h&gt;

typedef int (* vx115_irq_handler_t)(void *);


#define NR_IRQS     32
#define VALID_IRQ(i)    ((i!=24) &amp;&amp; (i!=27) &amp;&amp; (i&lt;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&lt;&lt;(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-&gt;sc_iot, vx115_pic_sc-&gt;sc_ioh, offset)
#define vx115_write_pic(offset, value)   bus_space_write_4(vx115_pic_sc-&gt;sc_iot, vx115_pic_sc-&gt;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 &amp; 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 &gt; 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 &lt; old )
        vx115_setipl(ipl);
    
    restore_interrupts(psw);
    
    /* if there are software interrupts pending, process */
    if (softint_pending &amp; 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 *)&amp;softint_pending, SI_TO_IRQBIT(si) );

    /* Process unmasked pending soft interrupts. */
    if ( softint_pending &amp; 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_ */
</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_intr.c" />vx115_intr.c</title>

        <programlisting>/* Copyright (c) 2006 Jon Sevy &lt;jsevy@cs.drexel.edu&gt;
 * 
 * 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 &lt;sys/cdefs.h&gt;
__KERNEL_RCSID(0, "$NetBSD: vx115_intr.c,v 1.5 2003/07/15 00:24:55 lukem Exp $");

#include &lt;sys/param.h&gt;
#include &lt;sys/systm.h&gt;
#include &lt;sys/malloc.h&gt;

#include &lt;machine/bus.h&gt;
#include &lt;machine/intr.h&gt;
#include &lt;machine/lock.h&gt;

#include &lt;arm/vx115/vx115_reg.h&gt;
#include &lt;arm/vx115/vx115_var.h&gt;
#include &lt;arm/vx115/vx115_intr.h&gt;


#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 &lt; 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-&gt;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-&gt;sc_iot = sa-&gt;sa_iot;
    
    if (vx115_pic_sc == NULL)
        vx115_pic_sc = sc;

    /* map bus space and get handle */
    if (bus_space_map(sc-&gt;sc_iot, sa-&gt;sa_addr, sa-&gt;sa_size, 0, &amp;sc-&gt;sc_ioh) != 0)
        panic("%s: Cannot map registers", self-&gt;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 &lt; NR_IRQS; i++)
    {
        /* set the priority control register to the associate IRQ number */
        vx115_write_pic(PIC_IPCR_1 + ((i-1)&lt;&lt;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 &lt; 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 = &amp;vx115_bs_tag;
    
    /* map bus space and set handle */
    if (bus_space_map(vx115_pic_init_sc.sc_iot, addr, size, 0, &amp;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 = &amp; vx115_pic_init_sc;
}

static __inline void
__raise(int ipl)
{
    if (current_spl_level &lt; 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 &lt; 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 &lt; extirq_level[irqno])
            vx115_setipl(saved_spl_level);
        
        /* clear interrupt */
        vx115_write_pic(PIC_SOURCE_CLEAR, (1&lt;&lt;irqno));
        
    }
    
    /* do any pending soft interrutps */
    if(softint_pending &amp; 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 &lt; NR_IRQS) 
    {
        /* disable this interrupt */
        int save = disable_interrupts(I32_bit);
        vx115_write_pic(PIC_ENABLE_CLEAR, ~(1U&lt;&lt;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 &lt; 32)      */
    if (irqno &lt; 32)
    {
        /* hard interrupt */
        mask_array = vx115_pic_spl_mask;
        mask = 1U &lt;&lt; irqno;
    }
    else
    {
        mask_array = vx115_pic_spl_soft_mask;
        mask = 1U &lt;&lt; (irqno - 32);
    }

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

    /* Disable interrupt when at supplied or higher priority level */
    for( ; i &lt; NIPL-1; ++i)
        mask_array[i] &amp;= ~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]        &amp;= mask_array[IPL_SOFTNET];
    mask_array[IPL_NET]        &amp;= mask_array[IPL_BIO];
    mask_array[IPL_SOFTSERIAL] &amp;= mask_array[IPL_NET];
    mask_array[IPL_TTY]        &amp;= mask_array[IPL_SOFTSERIAL];
    
    /*
     * splvm() blocks all interrupts that use the kernel memory
     * allocation facilities.
     */
    mask_array[IPL_VM] &amp;= 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] &amp;= mask_array[IPL_VM];
    
    /*
     * splclock() must block anything that uses the scheduler.
     */
    mask_array[IPL_CLOCK] &amp;= mask_array[IPL_AUDIO];
    
    /*
     * splhigh() must block "everything".
     */
    mask_array[IPL_HIGH] &amp;= mask_array[IPL_STATCLOCK];
    
    /*
     * XXX We need serial drivers to run at the absolute highest priority
     * in order to avoid overruns, so serial &gt; high.
     */
    mask_array[IPL_SERIAL] &amp;= 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] &amp;= 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] &amp;= 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] &amp;= 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(&amp;processing) == 0)
        return;
    
    spl_save = current_spl_level;
    
    oldirqstate = disable_interrupts(I32_bit);
        
#define DO_SOFTINT(si,ipl)  \
        if ((softint_pending &amp; vx115_pic_spl_soft_mask[current_spl_level]) &amp; SI_TO_IRQBIT(si)) { \
            softint_pending &amp;= ~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 &amp; vx115_pic_spl_soft_mask[current_spl_level] );

    __cpu_simple_unlock(&amp;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 &lt; VX115_IRQ_MIN || irqno &gt;= 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 (&amp;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 &lt;&lt; 2;

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

    /* create mask for this irq */
    irq_mask = 1 &lt;&lt; irq;

    /* get current interrupt enabled state */
    irq_enabled = vx115_read_pic(PIC_ENABLE_SET) &amp; 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 &lt;&lt; irq);
}


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


</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_io.c" />vx115_io.c</title>

        <programlisting>/*
 * Copyright (c) 2006, Jon Sevy
 * 
 * Based on ixp12x0_io.c,
 * Copyright (c) 2002, 2003
 *    Ichiro FUKUHARA &lt;ichiro@ichiro.org&gt;.
 * 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 &lt;sys/cdefs.h&gt;
__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 &lt;sys/param.h&gt;
#include &lt;sys/systm.h&gt;
#include &lt;sys/queue.h&gt;

#include &lt;uvm/uvm.h&gt;

#include &lt;machine/bus.h&gt;

#include &lt;arm/vx115/vx115_reg.h&gt;
#include &lt;arm/vx115/vx115_var.h&gt;

/* 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-&gt;pd_va + (bpa - pd-&gt;pd_pa);
        return 0;
    }

    endpa = round_page(bpa + size);
    offset = bpa &amp; 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 &lt; endpa; pa += PAGE_SIZE, va += PAGE_SIZE) {
        pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE);
        pte = vtopte(va);
        *pte &amp;= ~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 */
</programlisting>
      </sect3>
    </sect2>

    <sect2>
      <title>sys/arch/evbarm/conf</title>

      <para></para>

      <sect3>
        <title><anchor id="files.vx115_vep" />files.vx115_vep</title>

        <programlisting>#
# 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</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="mk.vx115_vep" />mk.vx115_vep</title>

        <programlisting># 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 \&lt; $@.bin \&gt; $@.bin.gz; \
gzip &lt; $@.bin &gt; $@.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@}</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="std.vx115_vep" />std.vx115_vep</title>

        <programlisting>#
# 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="&lt;arch/arm/vx115/vx115_intr.h&gt;"</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="VX115_VEP" />VX115_VEP</title>

        <programlisting>#
#    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 Independant 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 &amp; keyboard multiplexor
#pseudo-device    wsfont</programlisting>
      </sect3>
    </sect2>

    <sect2>
      <title>sys/arch/evbarm/vx115_vep</title>

      <para></para>

      <sect3>
        <title><anchor id="vx115_vep_start.S" />vx115_vep_start.S</title>

        <programlisting>/*
 * vx115_vep_start.S
 * Copyright (c) 2007, J. Sevy &lt;jsevy@cs.drexel.edu&gt;
 *
 * 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 &lt;machine/asm.h&gt;
#include &lt;arm/armreg.h&gt;
#include &lt;arm/arm32/pte.h&gt;
#include &lt;arm/arm32/pmap.h&gt;        /* 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 &lt;&lt; (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)&gt;&gt;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&lt;&lt;(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 */</programlisting>
      </sect3>

      <sect3>
        <title><anchor id="vx115_vep_machdep.c" />vx115_vep_machdep.c</title>

        <programlisting>/*
 * Copyright (c) 2006 Jon Sevy &lt;jsevy@cs.drexel.edu&gt;
 * 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 dependant functions for kernel setup for integrator board
 *
 * Created      : 24/11/97
 */

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

#include &lt;sys/cdefs.h&gt;

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

#include &lt;sys/param.h&gt;
#include &lt;sys/device.h&gt;
#include &lt;sys/systm.h&gt;
#include &lt;sys/kernel.h&gt;
#include &lt;sys/exec.h&gt;
#include &lt;sys/proc.h&gt;
#include &lt;sys/msgbuf.h&gt;
#include &lt;sys/reboot.h&gt;
#include &lt;sys/termios.h&gt;
#include &lt;sys/ksyms.h&gt;

#include &lt;uvm/uvm_extern.h&gt;

#include &lt;dev/cons.h&gt;
#include &lt;dev/md.h&gt;

#include &lt;machine/db_machdep.h&gt;
#include &lt;ddb/db_sym.h&gt;
#include &lt;ddb/db_extern.h&gt;
#ifdef KGDB
#include &lt;sys/kgdb.h&gt;
#endif

#include &lt;machine/bootconfig.h&gt;
#include &lt;machine/bus.h&gt;
#include &lt;machine/cpu.h&gt;
#include &lt;machine/frame.h&gt;
#include &lt;machine/intr.h&gt;
#include &lt;arm/undefined.h&gt;

#include &lt;arm/arm32/machdep.h&gt;

#include &lt;arm/vx115/vx115_reg.h&gt;
#include &lt;arm/vx115/vx115_var.h&gt;
#include &lt;evbarm/vx115_vep/vx115_vep_var.h&gt;

#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 dependant 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 &lt;arm/vx115/vx115_com.h&gt;

/*
 * 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 &amp; ~(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 &amp; RB_NOSYNC))
        bootsync();

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

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

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

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

    if (howto &amp; 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 -&gt; 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 &lt; 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 &lt;= NUM_KERNEL_PTS; ++loop) {
        /* Are we 16KB aligned for an L1 ? */
        if (((physical_freeend - L1_TABLE_SIZE) &amp; (L1_TABLE_SIZE - 1)) == 0
            &amp;&amp; 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 &amp; (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, &amp;kernel_pt_table[KERNEL_PT_SYS]);
    
    for (loop = 0; loop &lt; KERNEL_PT_KERNEL_NUM; loop++)
        pmap_link_l2pt(l1pagetable, KERNEL_BASE + loop * 0x00400000, &amp;kernel_pt_table[KERNEL_PT_KERNEL + loop]);
    
    for (loop = 0; loop &lt; KERNEL_PT_VMDATA_NUM; loop++)
        pmap_link_l2pt(l1pagetable, KERNEL_VM_BASE + loop * 0x00400000, &amp;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)&amp;etext - KERNEL_TEXT_BASE;
        size_t totalsize = (uintptr_t)&amp;end - KERNEL_TEXT_BASE;
        u_int logical;

        textsize = (textsize + PGOFSET) &amp; ~PGOFSET;
        totalsize = (totalsize + PGOFSET) &amp; ~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 &lt; 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)&amp;end) + PGOFSET) &amp; ~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 &lt;&lt; (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT);
    setttb(kernel_l1pt.pv_pa);
    cpu_tlb_flushID();
    cpu_domains(DOMAIN_CLIENT &lt;&lt; (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 &amp; RB_KDB)
        ipkdb_connect(0);
#endif

#ifdef KGDB
    if (boothowto &amp; 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 &amp; 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(&amp;vx115_bs_tag, ASCC0_BASE_PHYS, ASCC0_SIZE, 0, &amp;ioh);

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

    consinit_done = 0;
}

</programlisting>
      </sect3>
    </sect2>
  </sect1>
</article>