I'm writing my first linux device driver, and I have been reading Linux
Device Drivers (ORA, Rubini) and a pre-release copy of the next edition.
The next edition I have is missing the chapter on PCI.
I call ioremap to create maps to the PCI space pointed to by bar2, which
points at memory mapped IP modules.
After the ioremap, I try to use __pa() or virt_to_phys() to translate back
to physical and they return bogus info.
1. Am I wrong to use these calls for this purpose?
In my mmap file operation, I use remap_page_range(), which needs the
physical address. I CAN still get it from pci_resource_start(), but I'm
thinking I shouldn't.
The authors of the next edition say that you can remap virtual addresses
returned by ioremap, but didn't give an example, and I just can't figure it
out.
2. How do I use the virtual address returned from ioremap in a call to
remap_page_range?
Each of these IP modules is a *device*, but each only has 0x40 bytes of ID
info, and 0x80 bytes of i/o in the address space. And of course, the ID
spaces are contiguous, followed by the i/o spaces for each of the 5 modules.
I have captured some detail with the script command and then added some
comments (search for SEF)
# cat /proc/pci /proc/iomem
PCI devices found:
Bus 0, device 0, function 0:
Host bridge: Intel Corporation 430HX - 82439HX TXC [Triton II] (rev 1).
Master Capable. Latency=64.
Bus 0, device 7, function 0:
ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II] (rev
1).
Bus 0, device 7, function 1:
IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
(rev 0).
Master Capable. Latency=64.
I/O at 0xf450 [0xf45f].
Bus 0, device 15, function 0: SEF this is an Acromag 8620 IP
carrier with 5 IP modules installed
Bridge: PCI device 10b5:1024 (PLX Technology, Inc.) (rev 1).
IRQ 11.
Non-prefetchable 32 bit memory at 0xfdfff000 [0xfdffffff].
I/O at 0xf480 [0xf4ff].
Non-prefetchable 32 bit memory at 0x10000000 [0x10000fff].
Bus 0, device 16, function 0:
Ethernet controller: Intel Corporation 82557 [Ethernet Pro 100] (rev 5).
IRQ 10.
Master Capable. Latency=66. Min Gnt=8.Max Lat=56.
Prefetchable 32 bit memory at 0xfdffe000 [0xfdffefff].
I/O at 0xf460 [0xf47f].
Non-prefetchable 32 bit memory at 0xfde00000 [0xfdefffff].
Bus 0, device 18, function 0:
VGA compatible controller: S3 Inc. 86c325 [ViRGE] (rev 2).
IRQ 9.
Master Capable. Latency=64. Min Gnt=4.Max Lat=255.
Non-prefetchable 32 bit memory at 0xf8000000 [0xfbffffff].
00000000-0009fbff : System RAM
0009fc00-0009ffff : reserved
000a0000-000bffff : Video RAM area
000c0000-000c7fff : Video ROM
000f0000-000fffff : System ROM
00100000-01ffffff : System RAM
00100000-001e3e99 : Kernel code
001e3e9a-00237d5b : Kernel data
10000000-10000fff : PCI device 10b5:1024 (PLX Technology, Inc.) SEF this is
bar2, which points to memory mapped IP modules
f8000000-fbffffff : S3 Inc. 86c325 [ViRGE]
fde00000-fdefffff : Intel Corporation 82557 [Ethernet Pro 100]
fdffe000-fdffefff : Intel Corporation 82557 [Ethernet Pro 100]
fdffe000-fdffefff : eepro100
fdfff000-fdffffff : PCI device 10b5:1024 (PLX Technology, Inc.) SEF this is
bar0, which points to pci config space
fec00000-fec00fff : reserved
fee00000-fee00fff : reserved
ffff1cf4-ffffffff : reserved
# ./inscmd
SEF this is the loadable kernel module containing my device driver.
apc8620: ipModule Init
apc8620: ipModule Probe
apc8620: found Acromag 8620 (5 IP slots) at: bus=0, dev=15, func=0
apc8620: enabled device 'apc8620'
apc8620: requesting mem region referenced by bar0
apc8620: requesting mem region referenced by bar2
apc8620: carrierAdx(from ioremap)=0xc281f000
apc8620: virt_to_phys(carrierAdx)=0x0281f000 SEF note that this is NOT
the correct physical address
apc8620: __pa(carrierAdx)=0x0281f000
apc8620: bar2_phys_adx=0x10000000 SEF from pci_resource_start(dev,2)
apc8620: bar2_phys_length=0x00001000 SEF from pci_resource_len(dev,2)
apc8620: bar2_virtual=0xd0000000 SEF from phys_to_virt()
apc8620: __pa(bar2_virtual)=0x10000000 SEF happens to be correct, but
actually there's no mapping, linux code merely adds/subtracts 0xc0000000 to
convert between virtual and physical
apc8620: calculated ipID[0]=0xc281f040
apc8620: calculated ipID[1]=0xc281f080
apc8620: calculated ipID[2]=0xc281f0c0
apc8620: calculated ipID[3]=0xc281f100
apc8620: calculated ipID[4]=0xc281f140
apc8620: ipModule=apc8620 @ 0xc281da40
apc8620: rc=0x00000000
apc8620: registered driver 'apc8620'
apc8620: dev pointer passed to ipModuleShow=0xc10b0c00
apc8620: device is using IRQ11
apc8620: name PCI device 10b5:1024 (PLX Technology, Inc.)
apc8620: slot_name 00:0f.0
apc8620: vendor 0x10b5 device 0x1024 class 0x068000
apc8620: bar0 0xfdfff000 bar1 0x0000f480 bar2 0x10000000
apc8620: ipID[0]=IPAC Acromag IP480-6 @ 0x10000040 SEF I cheated on all
these except ipID[2], which shows my dilema
apc8620: ipID[1]=IPAC Acromag IP408
apc8620: ipID[2]=IPAC Acromag IP440 @ 0xc281f0c0 (0x0281f0c0)
apc8620: ipID[3]=IPAC Acromag IP445 @ 0x10000100
apc8620: ipID[4]=IPAC Acromag IP445 @ 0x10000140
apc8620: device driver apc8620
apc8620: registered char device 'apc8620' as major 254
apc8620: opened minor device 0
apc8620: offset 0x00000000 bytes
apc8620: physical adx 0x10000000 bytes
apc8620: pageAdjustment 0x00000000 bytes
apc8620: write 8 bytes to minor device 0
apc8620: read 8 bytes from minor device 0
# ps -ax
PID TTY STAT TIME COMMAND
...
900 tty3 S 0:00 ./ipDemo
...
# cat /proc/900/maps
08048000-08049000 r-xp 00000000 03:05 156190
/home/sfriedri/src/pci/apc8620/demo/ipDemo
08049000-0804a000 rw-p 00000000 03:05 156190
/home/sfriedri/src/pci/apc8620/demo/ipDemo
40000000-40015000 r-xp 00000000 03:05 31017 /lib/ld-2.2.2.so
40015000-40016000 rw-p 00014000 03:05 31017 /lib/ld-2.2.2.so
40016000-40018000 rw-p 00000000 00:00 0
40018000-40019000 rw-s 00000000 03:05 99027 /dev/apc8620 SEF I'm
guessing this is the result of my remap_page_range
40019000-4001a000 rw-p 00000000 00:00 0
4001b000-4013c000 r-xp 00000000 03:05 31026 /lib/libc-2.2.2.so
4013c000-40141000 rw-p 00120000 03:05 31026 /lib/libc-2.2.2.so
40141000-40145000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0
Here's code excerpts...
bar2_phys_adx = (void *)pci_resource_start(dev,2);
bar2_phys_length = pci_resource_len(dev,2);
carrierAdx = ioremap_nocache((int)bar2_phys_adx,
bar2_phys_length);
bar2_virtual = phys_to_virt((int)bar2_phys_adx);
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk("carrierAdx(from ioremap)=0x%08lx\n", carrierAdx);
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk("virt_to_phys(carrierAdx)=0x%08lx\n",
virt_to_phys(carrierAdx));
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk(" __pa(carrierAdx)=0x%08lx\n",
__pa(carrierAdx));
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk(" bar2_phys_adx=0x%08x\n", bar2_phys_adx);
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk(" bar2_phys_length=0x%08x\n", bar2_phys_length);
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk(" bar2_virtual=0x%08x\n", bar2_virtual);
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk("__pa(bar2_virtual)=0x%08x\n", __pa(bar2_virtual));
Here's the mmap file operation...
static int ipModuleMMap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long offset, physical, pageAdjustment;
physical = pci_resource_start(ipModuleDev,2);
pageAdjustment = (PAGE_SIZE - 1) & physical;
offset = vma->vm_pgoff << PAGE_SHIFT;
#ifdef DEBUG1
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk("offset 0x%08x bytes\n", offset);
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk("physical adx 0x%08x bytes\n", physical);
#endif
#ifdef DEBUG1
printk("%s%s%s", KERN_NOTICE, DRIVER_NAME, ": ");
printk("pageAdjustment 0x%08x bytes\n", pageAdjustment);
#endif
vma->vm_flags |= VM_RESERVED | VM_IO;
if (remap_page_range(vma->vm_start, physical,
vma->vm_end - vma->vm_start,
vma->vm_page_prot)) /* no man page */
return -EAGAIN;
return 0;
}
Steven Friedrich
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/