|
Device drivers use ddi_regs_map_setup(9F) to
establish mappings between kernel virtual address (kvaddr)
and device address space for PIO. A device address space may
consist of multiple addressable memory regions. Each region is
assigned a register set number, rnumber. ddi_dev_nregs(9F)
gives the total number of register sets that a device has.
The "reg" property shows the
number of addressable regions exported by the device to the
system. ddi_regs_map_setup(9F) uses rnumber
to index into the array of registers in the "reg"
property and returns a kvaddr mapped to the region
corresponding to rnumber.
As mentioned in "IEEE 1275 PCI Binding,"
IEEE 1275 PCI binding makes no specific connection between the
entries in the "reg" property and the configuration
registers in PCI configuration space. Driver developers should
check the "reg" property to ensure that the correct rnumber
is used in ddi_regs_map_setup(9F).
For example, the "reg" property
of /pci@1f,4000/scsi@3 shown in the example of the
PCI SCSI device node properties contains four register
sets: one for configuration space, one for I/O space, and two for
memory space.
The "assigned-addresses"
property has three register sets: one for I/O space, and two for
memory space, as shown below.
| assigned-addresses 81001810 00000000
00000400 00000000 00000100
82001814
00000000 00018000 00000000 00000100
82001818 00000000 00019000
00000000 00001000
reg 00001800 00000000 00000000
00000000 00000000 <-- rnumber 0
01001810 00000000 00000000
00000000 00000100 <-- rnumber 1
02001814 00000000 00000000
00000000 00000100 <-- rnumber 2
02001818 00000000 00000000
00000000 00001000 <-- rnumber 3
|
If you type show-pci-config
at the ok prompt of OpenBoot, you get the values of
the device's configuration registers, as shown in this example of
the configuration register values for an on-board SCSI device. As
shown below, base address 0 is for I/O space and base address 1
and 2 are for memory space.
ok show-pci-config
/pci@1f,4000/scsi@3
PCI Header for
Bus # 0, Dev # 3, Func # 0
Vendor ID = 1000
Device ID = f
Command Reg = 46
Status Reg = 200
Revision ID = 1
Class Code = 10000
Cache Line Size = 10
Latency Timer = 40
Header Type = 0
BIST = 0
Base Address 0 = 401
Base Address 1 = 18000
Base Address 2 = 19000
Base Address 3 = 0
Base Address 4 = 0
Base Address 5 = 0
Base Address ROM = 0
Interrupt Line = 0
Interrupt Pin = 1
Minimum Grant = 8
Maximum Latency = 40
|
If you write a routine to map into register
set 0, as shown below, you should be able to read the device's
configuration space and get the same value of device ID, vendor
ID, and Class Code as shown in the example of on-board SCSI configuration register values.
#define CONFIG_VENDOR_ID_OFFSET 0x0
#define CONFIG_DEVICE_ID_OFFSET 0x2
#define CONFIG_CLASS_CODE_OFFSET
0x8
#define CONFIG_BASE_ADDR_2_OFFSET
0x18
#define CONFIG_REGS_SIZE 0x40
static struct ddi_device_acc_attr
endian_attr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
xxx_map_regs(dev_info_t *dip) {
caddr_t configp; /* pointer to
configuration space */
caddr_t mem_reg1; /* pointer to
memory space 1 */
size_t mem_size; /* size of memory
space as shown in "reg" */
ddi_acc_handle_t handle0; /* Access
handle for configuration space */
ddi_acc_handle_t handle2; /* Access
handle for memory space 1 */
ulong_t base_addr2; /* Base address
1 value (rnumber = 2) */
ushort_t vendor_id, device_id;
uint_t class_code;
......
/*
* configp is the kernel virtual
address that maps to the device
* configuration space, rnumber is
0.
*/
ddi_regs_map_setup(dip, 0,
&configp, 0, CONFIG_REGS_SIZE,
&endian_attr, &handlep0);
/*
* Comparing with the corresponding
properties,
*
* vendor_id should be 0x00001000.
* device_id should be 0x0000000f.
* class_code should be 0x00010000.
* baddr1 should be 18000.
*/
|
vendor_id = ddi_getw(handle0, configp
+ CONFIG_VENDOR_ID_OFFSET);
device_id = ddi_getw(handle0, configp +
CONFIG_DEVICE_ID_OFFSET);
base_addr2 = ddi_getl(handle0,
configp + CONFIG_BASE_ADDR_2_OFFSET);
class_code =
(uint_t)ddi_getl(handle0, configp +
CONFIG_CLASS_CODE_OFFSET);
class_code = (uint_t)(class_code
>> 8);
......
> /*
* memp is the kernel virtual
address that maps to the device memory space
* register number 2, rnumber = 2.
*/
ddi_dev_regsize(dip, 2,
&mem_size);
ddi_regs_map_setup(dip, 2,
&mem_reg1, 0, mem_size, &endian_attr,
&handlep2);
......
}
|
In the example above, mem_reg1
and base_addr2 are referring to the same memory
object on the device. mem_reg1 is the kvaddr mapped to the memory object and can be used by the
driver to access that memory object.
mem_reg1 is allocated from the
kernel resources map (kernelmap) and has the value
between SYSBASE and SYSEND, as shown in
the kernel address map below.
base_addr2 contains the value
of the base address register for memory space 1. In this example,
the value of base_addr2 is 18000. base_addr2
is the physical address of the memory
object in the PCI bus address domain.
The system uses base_addr2 in
ddi_regs_map_setup(9F) to establish mapping between mem_reg1 and rnumber.
In the ddi_regs_map_setup(9F) call, the
system traverses the device tree all the way up to the root nexus
driver to establish mapping between kernel virtual address (for
example, mem_reg1) and the device register
address space denoted by rnumber. On the way to the
root nexus driver, the system calls the PCI nexus driver, the
device's parent node, to convert rnumber to a
generic register structure that can be used by the root nexus
driver. The PCI nexus driver reads the values of the
"reg" property of the device (if the address in the
"reg" property is relocatable, the PCI nexus driver
gets the values from the "assigned-addresses" property)
and uses rnumber to index into the array to
construct the register structure. Inside the register structure
is a field that contains the physical address of the device
register in the PCI bus address domain (for example, the value of
base_addr2).
The system passes the register structure to
the root nexus driver. The root nexus driver does three things:
- Converts the address in the register structure into a
physical address (
paddr) in the system bus
domain
- Allocates a
kvaddr from kernelmap
- Establishes a mapping between
kvaddr and paddr
The root nexus driver then returns the kvaddr to
the device driver. When the kvaddr is accessed, the
system generates a memory fault to load the system MMU with the kvaddr->paddr
translations. It should be noted that the IOMMU of the HPB is not
involved in the translations.
<<Previous |
Contents |
Next>>
|