PCI For Mac Programmers
Volume Number: 15
Issue Number: 3
Column Tag: PCI For Mac Programmers
PCI for Mac Programmers
by Larry Barras
Understanding the PCI Expansion Bus
Introduction
In 1995, Apple introduced the PCI (Peripheral Component Interconnect) expansion
bus to the Power Macintosh line, replacing the older NuBus slots. PCI is now the
dominant bus standard for add-in cards; such as disk controllers, video cards, network
interface cards for desktop PC's and Power Macs. By adopting PCI as the standard for
expansion slots, Apple embraced the wider variety and lower cost hardware available
on the PC. Almost all PCI hardware functions on the Mac, provided there is software to
drive the device.
This article is designed to give a programmer's overview of the PCI bus, as
implemented in the Macintosh computer. PCI is a cross-platform standard and some
concepts of the PCI interface will be unfamiliar to Mac programmers. Developers who
have experience on PC's will be unfamiliar with some of the Mac's unique features as
well. Once the concepts and differences are cleared up, the actual programming is
fairly simple. Native Mac drivers usually require no assembly code, and many useful
routines are provided in the Mac OS. Hopefully, this article will encourage more
developers to produce Mac drivers for PCI devices.
Macintosh PCI Software
Take any PCI card and plug it in your Mac, and power-up. If the card wasn't
specifically made for the Macintosh, chances are not much happened. But this is not
necessarily a bad thing. Your Mac did boot after all. The card requires a software
driver to function on the Mac. There are two types of drivers for PCI Cards. Boot
driver is loaded from the PCI Cards firmware and executed when the Mac hardware in
initialized. A Mac specific device driver is loaded from the hard drive when the Mac OS
is initialized. Only cards needed when loading an operating system require firmware
based drivers. Usually, this means video cards and disk controllers for a bootable
device.
Firmware Driver
The common MS-DOS PC boots with native x86 binary code. Writing PC-Compatible
boot code for PCI cards is a difficult art. Many aspects of the low-level PC were never
well documented, and many variations exist today. Most expansion BIOS ROMS devote a
good portion of code to figuring out what kind of hardware it is running on, and how to
deal with it. Obviously, a PowerPC processor cannot directly interpret x86 code. If a
card requires boot time support, like disk controllers and video cards, the card will
need a ROM with boot code in it.
The Mac had the opportunity to start with a clean slate. The boot environment is well
defined, and boot-time code need not worry about machine-specific conditions. While
most PCs look the same at boot time, Macs may have tremendous differences in things
like video adapter addresses, disk controllers, network adapters and so on. The raw
hardware looks different, making it difficult to program in the traditional,
machine-specific manner.
Open Firmware
In order to ease the card developer's burden, allow for future changes in hardware and
provide a positive end-user experience, PCI Macs utilize Open Firmware at boot-time.
Open Firmware is based on the Forth language and provides a processor-independent
way to implement firmware on expansion cards. Instead of native binary PowerPC or
binary x86 code, Open Firmware uses processor independent Forth code. An expansion
ROM with Open Firmware code on a PCI card can supply information about the device to
the host computer and can even provide basic drivers for bootstrap devices, until an
operating system can load and provide OS-specific drivers for the device.
Open Firmware originated at Sun Microsystems. Sun needed a way to build computers
and expansion cards that worked on all the different processors they were working
with. Until Open Firmware and the Open Boot specification, computers booted via
firmware written for a specific processor family. Sun was building machines with
68K, Sparc and x86 processors. Previously, every card needed firmware coded for
each processor family and system design. Forth was a natural choice due to its
compactness and availability on nearly every known processor.
Open Firmware also has an interactive Forth console. The console is important for
developers because it provides a low-level interface before the Mac ROM is loaded.
Some Macintosh computers use the keyboard and display; other models may require a
terminal on a serial port to access the console. Using the console gives the developer a
chance to intercept the boot process and debug code before an operating system or
something like MacsBug can load.
When a PCI Mac is booted, Open Firmware loads from the Mac's ROM and initializes the
main board. Open Firmware then scans the PCI bus for any user-installed cards and
scans their configuration registers. The configuration registers indicate what
resources, such as interrupts and memory addresses, the card needs, and whether
there is a boot ROM present. When Open Firmware code is executed, unrecognized ROM
code is simply ignored.
During the scanning process OpenFirmware builds a table of devices and resources in
the system called the Device Tree. The information in the Device Tree is carried over
to the Mac OS in the Name Registry. Mac OS drivers and applications use the Name
Registry to find things like PCI cards, where they are located in memory, what
resources they require, where their interrupts are serviced, power requirements and
so on.
Apple defines three levels of firmware driver support; no firmware, minimal
firmware, and full firmware. A card with no firmware support on-board leaves
generic information in the name registry, and requires a disk-based driver. Limited
firmware includes some Open Firmware code to install specific properties in the name
registry, allowing your disk-based driver to positively identify your hardware. A Mac
OS runtime driver might also be included in partial support scenario, but such cards
will be limited to booting only the Mac OS. Full firmware support includes a Mac OS
driver and an Open Firmware driver in Forth to allow loading of an alternative
operating system like Mac OS X Server. An Open Firmware driver makes the device
available for the entire booting process, even before an operating system is loaded.
Again, anything that needs to participate in the boot process needs full Open Firmware
support.
Apple recommends developers implement minimal firmware support, and supply name
and resource information, but it is by no means required. Ordinary PCI cards, like
data acquisition cards, frame grabbers, and even the popular 3dfx Voodoo game cards
don't have any ROM at all.
The Name Registry
All the information that Open Firmware records in the Device Tree is passed along to
the Mac OS in the Name Registry. The Name Registry is a simple tree-like database,
which records system information. The Name Registry is unique to the Macintosh and
greatly eases driver development.
On most other platforms, a PCI driver developer needs to know lots of detail about how
the hardware works. Often one must resort to difficult techniques in order to configure
the hardware, reserve memory, determine the CPU speed and so on. Under the Mac OS,
things are a lot easier. Using the Name Registry, your device driver can easily
determine whether your hardware is present and what resources are assigned to it,
and other information like the PCI clock frequency.
The Name Registry provides functions to locate specific instances in the device tree,
retrieve properties such as address space and interrupt-tree location that are
allocated to your hardware. Your device driver will use the Name Registry to retrieve
the physical and logical addresses, interrupt resources and other information about
your device. The utility "Display Name Registry," part of Apple's PCI Driver SDK, is
useful for looking at the Name Registry.
PCI for Mac Programmers
The first step is to grasp what PCI offers on any computer platform. PCI devices
present well-defined means for identification, configuration and operation.
Identification and configuration are handled via a unique addressing mode. Since the PCI
standard is cross-platform, it offers two means for transfer of data, IO-mode
addressing, common for Intel x86 processors, and memory addressing, usually found
on other processor families like the PowerPC. Either IO or memory-mapped
addressing may be provided, sometimes both. However, due to the popularity of the
Intel platform, many devices only offer IO-mode addressing. Unlike the Intel family
processors, the PowerPC doesn't offer special IO addressing. But, as I will explain
later, some clever hardware built into the PowerMac takes care of this problem.
PCI Address Spaces
Under PCI, there are three relevant address spaces: Configuration Space, Memory
Space and IO Space. Configuration Space is how the card is identified and controlled.
Memory Space acts like any other physical memory address space. IO Space is a special
kind of address space, devoted to input/output on some processor types like the Intel
x86. To access these address spaces, the Mac provides helpful managers. The function
calls are documented in "Designing PCI Cards and Drivers" from Apple Computer.
Configuration Space
Configuration Space contains the registers that identify and control the card itself.
This space contains a total of 256 bytes for each card. The PCI standard defines part of
these addresses for control and identification registers. Registers above offset 0x3F
are device-specific and may be used by the card to control hardware on the card.
31 24 23 16 15 8 7 0 Offset
Device ID Vendor ID 00h
Status Command 04h
Class code Revision ID 08h
BIST Header type Latency Timer Cache Line Size 0Ch
Base Address 0 10h
Base Address 1 14h
Base Address 2 18h
Base Address 3 1Ch
Base Address 4 20h
Base Address 5 24h
Cardbus CIS pointer 28h
Subsytem ID Subsystem Vendor ID 2Ch
Expansion ROM base address 30h
Capability pointer 34h
Reserved 38h
Max_Lat Min_Gnt Interrupt pin Interrupt line 3Ch
The standard configuration registers.
Configuration registers are not mandatory. Vendors are free to implement optional
registers as they see fit. Registers above 3Fh are device-specific. Refer to the