<?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 */