Latest News
!System updates

Development tools
C/C++ Tools

Writing 32-bit code
32-bit introduction
32-bit overview
32-bit technical
Memory issues
Developer tools
Paul Skirrow's Guide (PDF)
Download area

32-bit APIs
API changes
CallASWI
FileCore

RISC OS 5
Overview
BBC BASIC
CDFS driver
OS_ClaimDeviceVector behaviour
CDFS
Draw clipping
GraphicsV
HAL
Internet
UTF8 & Japanese support
International IME support
MIMEMap
Module PostInit/Final
PCI Manager
PDumper
Podules
Resource allocation
SCSI
Service Calls
UCS fonts
USB
WIMP API Changes
WIMP Flags

IYONIX pc
DDR memory
Help system
Keyboard layout
Miscellaneous
PCI slots

Opportunities

Iyonix Ltd
IYONIX home page
Contact details

   

PCI Manager

Document version 0.13a, Thursday the 30th of June, 2005

Introduction

RISC OS 5 supports 32-bit and 64-bit PCI cards via the new PCI Manager, which provides a number of common services to the device driver modules for specific PCI cards.

It is envisaged that third parties will supply PCI cards bundled with the necessary RISC OS device drivers and the information supplied here should help developers to write those drivers.

A PCI device driver will normally be supplied as a RISC OS module which provides a set of SWIs and other interfaces to the PCI card. Applications should access the PCI card using SWI calls provided by this device driver module.

Driver source code for many PCI devices which is publicly available on the Internet for NetBSD and Linux may be useful in developing the corresponding RISC OS device driver.

PCI Device Identification

In RISC OS, PCI devices can be identified in one of two ways. Users generally need only be aware of the PCI system when expansion cards are added to the machine, and will use the PCI slot number, in commands like *Unplug or *PCIDevices. Slot numbers are fixed for a given system.

Device drivers use a PCI handle - each function of each PCI device in the system is given a unique, permanent non-zero handle when it is discovered. Handles remain constant as long as the device remains present in the system, but can change when the system is rebooted, or if a card is removed and reinserted (if the system supports hot-plugging). Handles do not change if the PCI Manager is reloaded. Handles are assigned from 1 upwards, so can be searched linearly, much like Expansion Cards.


SWI PCI_ReadID

In:  R3 =  function handle
Out:  R0 =  vendor ID (bits 0-15)
    device ID (bits 16-31)
  R1 =  subsystem vendor ID (bits 0-15)
    subsystem ID (bits 16-31)

SWI PCI_ReadHeader

In:  R3 =  function handle
  R1 ->  buffer
  R2 =  number of bytes to read (4-256, must be multiple of 4)
Out:  Buffer  updated

The length of the buffer is assumed to be at least R2 bytes long

SWI PCI_ReturnNumber

In:  No arguments
Out:  R0 = number of function handles that have been used

This SWI returns the number of function handles that have been used. Device drivers may search handles 1 to R0 inclusive to find a device.

SWI PCI_EnumerateFunctions

In:  R3 =  0 to start enumeration
    or last value from EnumerateFunctions
Out:  R0 =  vendor ID (bits 0-15)
    device ID (bits 16-31)
  R1 =  subsystem vendor ID (bits 0-15)
    subsystem ID (bits 16-31)
  R2 =  class code
  R3 =  function handle

SWI PCI_IORead

In:  R0 =  PCI address (found with PCI_HardwareAddress)
  R2 =  access size (1, 2 or 4 for byte/half word/word)
Out:  R1 =  value read

SWI PCI_IOWrite

In:  R0 =  PCI address (found with PCI_HardwareAddress)
  R1 =  value to write
  R2 =  access size (1, 2 or 4 for byte/half word/word)

SWI PCI_MemoryRead

In:  R0 =  PCI address (found with PCI_HardwareAddress)
  R1 =  length (whole number of words)
  R2 ->  buffer
  R4 =  flags (bit 3 => prefetchable)
Out:  Buffer  updated

The length of the buffer is assumed to be at least R1 bytes long.

SWI PCI_MemoryWrite

In:  R0 =  PCI address (found with PCI_HardwareAddress)
  R1 =  length (whole number of words)
  R2 ->  buffer
  R4 =  flags

SWI PCI_ConfigurationRead

In:  R0 =  configuration space address (0-255) or bus/dev/fn/addr
  R2 =  access size (1, 2 or 4 for byte/half word/word)
  R3 =  function handle or 0
Out:  R1 =  value

SWI PCI_ConfigurationWrite

In:  R0 =  configuration space address (0-255) or bus/dev/fn/addr
  R1 =  value
  R2 =  access size (1, 2 or 4 for byte/half word/word)
  R3 =  function handle or 0

SWI PCI_HardwareAddress

In:  R0 = flags:
  bits 3..0 = access privileges (if bit 9 set)
  bit 4 => bufferable
  bit 5 => cacheable
  bits 8-6 => policy (see OS_DynamicArea)
  bit 9 => access privileges specified
  bit 31 => don't map, query PCI address only
  R1 = address index
  (0-5 correspond to first 6 Base Address Registers;
  a device with 3 64-bit addresses would use
  indexes 0,2,4 only. &100 corresponds to ROM)
  other values reserved
  R3 = function handle
Out:  error if address not available
  R0 = flags:
  bit 0 => IO if set, memory if clear
  bit 3 => prefetchable
  R1 = PCI address
  R2 = area size
  R4 = logical address (if R0
  bit 31 clear)

SWI PCI_ReadInfo

In:  R0 = bitmask of required results
  R1 = pointer to buffer
  R2 = length of buffer
  R3 = function handle
Out:  R2 = length of results
Bit 0:  Device/Function Number
1:  Bus number
2:  Function handle of parent bridge
3:  Slot number
4:  Vendor/device ID
5:  Revision ID
6:  Subsystem vendor/subsystem ID
7:  Class code (24 bits)
8:  CMOS address
9:  CMOS size in bytes
10:  Pointer to description (0 for none)
11:  Device vector number
12:  Ethernet address (low 32 bits) (?)
13:  Ethernet address (high 16 bits) (?)
14:  Logical DMA channel
15:  Pointer to vendor description (0 for none)

Anlogous to Podule_ReadInfo, for a given R3.

SWI PCI_SpecialCycle

In:  R0 =  bus
  R1 =  message/data

Issues a special cycle on the PCI bus.
The value in R1 will be call specific.

SWI PCI_FindByLocation

In: R0 =  bus
R1 =  dev/fn
Out: R3 =  function handle

Finds the function handle based on the topology passed in R0 and R1.This topology will be platform specific and could vary if backplanes with bridges are inserted into the system.

SWI PCI_FindByID

In:  R3 =  0 to start, or R3 last found
  R0 =  vendor ID (or -1)
  R1 =  device ID (or -1)
  R2 =  subsystem vendor ID (or -1)
  R4 =  subsystem ID (or -1)
Out:  R3 =  function handle

Finds the function handle based on the wildcarded information passed in.

SWI PCI_FindByClass

In:  R3 =  0 to start, or R3 last found
  R0 =  class code (24-bit)
  R1 =  mask (matches if (function_class AND R1) = R0)
Out:  R3 =  function handle

Finds the function handle based on the class passed.Only the bits in R1 of the class code will be checked so a mask of -1 will search for an exact match.

SWI PCI_RAMAlloc

In:  R0 =  size required
  R1 =  alignment required (eg &1000 if must be 4K aligned - 0 if none)
  R2 =  boundary limitation (eg &10000 if mustn't cross 64K boundary - 0 if none)
Out:  R0 =  logical address
  R1 =  PCI address

Allocates memory from a fixed, contiguous memory pool, suitable for access by other bus masters. This is provided for the convenience of drivers that only need a few simple data structures for communication with their PCI device, and don't want the complexity of dealing with cache coherency, memory fragmentation and physical page moving (Service_PagesUnsafe et al).

Memory will be uncachable but bufferable - you must ensure that any writes you perform have taken place before another bus master reads the memory, by calling OS_MMUControl 1 with bit 28 of R0 set. The memory is not accessible from user mode.

SWI PCI_RAMFree

In: R0 = logical address

Returns the block of memory pointed to by R0 (claimed earlier with PCI_RAMAlloc) to the PCI memory pool for others to use.

SWI PCI_LogicalAddress

In:  R0 =  flags: bit 4 => bufferable
    bit 5 => cacheable
    bits 8-6 => policy
    bit 30 => IO else Memory
  R1 =  PCI address
  R2 =  length
Out:  R4 =  logical address

This call maps in the given PCI address (found with PCI_HardwareAddress) into the logical memory map subject to the flags in bits 8 to 4 of R0. If bit 30 is set it will be marked as IO,else marked as Memory.

PCI manager service calls

Service_PCI is defined as &C3
R1 =  &C3 (the reason code)
R2 =  subreason
=>  0 - lookup description
  R3 = function handle to convert
  Return R1 = 0 to claim
  R2 = pointer to zero terminated string
  R3 = preserved
=>  1 - lookup vendor description
  R0 = vendor ID
  Return R0 = preserved
  R1 = 0 to claim
  R2 = pointer to zero terminated string
=>  others - reserved for future expansion

PCI Interrupts

Since PCI interrupt lines are shared by multiple devices, the interrupt handling mechanism is a little different from that used by traditional expansion cards. To find the device vector number that will be called for a particular card, use PCI_ReadInfo with bit 11 set in the R0 bitmask. To install a device driver, use OS_ClaimDeviceVector and pass in this vector number with bit 31 set to indicate that the call may be passed on to other vector claimants. You must also ensure that interrupts for this vector are enabled by calling the HAL routine HAL_IRQEnable (HAL entry number 1) with this vector number as the parameter. Note however that when you remove a device driver, you should not disable interrupts for this vector.

When your device driver routine is called, check whether your card is interrupting. If it is, perform the necessary interrupt processing and claim the vector by pulling the return address from the stack. If it is not, pass on the call by returning to the address in R14. If you are writing a device driver in C, use the CMHG 'vector-handlers' veneer and return 0 to claim the call or 1 to pass it on.

When the interrupt processing code needs to clear the interrupt on the PCI card, there are two things that must be done before interrupts are re-enabled or you return from the handler. Firstly, if clearing the interrupt involves writing to a register on the PCI card, you must ensure that the write has been flushed out to the card. This can be achieved by simply performing a read operation from a safe area or register on the card. Secondly, you should then call the HAL routine HAL_IRQClear (HAL entry number 3) with the vector number as the parameter. Note that this is not actually required for the Iyonix hardware, but should be included for compatibility with possible future hardware platforms.

If no handler claims a vector for a particular interrupt, the kernel will disable that interrupt line. For correct operation of the system it is vital that vector claiming and interrupt clearing are performed correctly.

© 2006 IYONIX Ltd 32-bit RISC OS