Accessing MMIO PCI space - crossplatform
Petr Vandrovec Ing. VTEI (VANDROVE@vc.cvut.cz)
Thu, 12 Nov 1998 18:59:02 MET-1
Hello Linus, hello everyone,
I asked few days ago about correct way to crossplatform accesses to
PCI MMIO - specifically how to access MMIO with same code on Alpha and
PPC. Unfortunately, there was only one reply - whether I'm porting
matroxfb to Alpha. Yes, I'm.
So again, currently I'm using
char* virtual = ioremap(pcidev->base_address[x], 8MB);
virtual[y*size + x] = pixel;
for writting or
pixel = virtual[y*size + x];
for reading and
at the end for release resources.
But I was pointed to that this does not work on Alpha - driver must use
writex and readx macros. Unfortunately, these macros DO NOT work for me:
1) they probably works on Alpha as expected, so no problem there
2) they do not work at all on PPC because of they swap bytes to little
endian format. While it is (I'm not too much sure) probably correct
for accesses to other hardware, it is not correct for access
to Matrox devices. They (Matroxes) know about PPC and expect bytes
in big-endian ordering. So I cannot use writel/readl on PPC.
Except that, it is not possible to tell to memcpy_toio whether
it should swap nothing (byte stream), words or dwords while copying...
On __arm__ (Arm does not have PCI, so no problem) it copies memory
byte-by-byte, so it is completely unusable if hardware expects only
32-bit (or 64-bit) accesses :-(
3) writel/readl even DOES NOT work on ia32 with 2GB of memory.
When someone sets PAGE_OFFSET to 0x70000000, and then has machine with
(f.e.) 512MB of memory, physical memory is mapped from 0x70000000 to
0x8FFFFFFF. So ioremap() returns some value around 0x90000000. After
oring it with 0x70000000, driver gets 0xF0000000 and dies
very quickly :-(
Whole concept of PAGE_OFFSET (and or-ing whith PAGE_OFFSET) works only
if you restrict PAGE_OFFSET that value must be valid as "ipv4 netmask"
(0x80000000, 0xC0000000, 0xE0000000 ...), so it is a bit useless
(0x80000000 is not enough for 2GB of memory).
I think, that, at least on Intel:
a) every driver should be fixed to use ioremap
b) readl/writel should not do anything with address
c) virt_to_phys and virt_to_bus should walk through pagetables instead
of anding/oring with something with something
(or explicitly say that they cannot be used for shared (adaptor)
memory... after reading up and down through IO-mapping it looks
clear to me, but...)
(after this change, you can do DMA I/O directly to/from userspace
d) phys_to_virt, bus_to_virt should be removed, it cannot be done
in linear time (I'm not aware of such solution (on ia32)) and
ioremap is already here and should be used instead of this one-time
translation) (or explicitly say that for shared (adaptor) memory
it cannot be used)
e) that means:
1) clear readl/writel interface that passed address must
be obtained from ioremap (so first example fro IO-mapping.txt -
- readl(0xC0000) - is wrong). On ia32, ioremap can return
virtual address and readl/writel are simple reading/writting (no
playing with PAGE_OFFSET), on alpha, ioremap can return bus
address and readl/writel are complicated function as they are
2) same for memcpy_toio, memcpy_fromio, memset_io, so examples for
these functions in IO-mapping.txt are no longer valid.
f) it must be done before 2.2, because of otherwise it is not possible
to have more than 1.8 GB of memory in an Intel box (you can have
it... but you cannot use it as system memory) (I know that no one
real user uses ia32 this time, but there are such)
So unless someone has more correct solution (please, please), I have to use
#define mga_readl(x) readl(x)
#define mga_readl(x) (*(volatile u_int32_t*)(x))
But I do not want to have this crappy code in my driver because of I think
that it should not be there (I've found only four #ifdef __alpha__ in
drivers subtree of kernel, so I think that it is not right way to go).
P.S.: I cannot believe to (3f), so I'm probably wrong... But I'm searching
solution for more than week and I still have nothing.
P.P.S.: For Dave and/or Jakub (and others): On __sparc__, __sparc64__
(one PCI sparc is probably coming here next year) and other non-listed
ports (__m68k__), what is better, safe and working -
(*(volatile u_int32_t*)(x)) or readl(x) ?
P.P.P.S.: Yes, I read Documentation/IO-mapping.txt couple of times.
P.P.P.P.S.: It (IO-mapping.txt) is a bit outdated, isn't it?
unsigned long signature = *(unsigned int*)0xC0000;
(at the end of doc) never worked in 2.1, isn't it?
P.P.P.P.P.S.: While I'm talking about ioremap, is there any chance that
ioremap_nocache or __ioremap will exist on non-ia32? Otherwise I have
to use #ifdef __i386__ ... Can I achieve same (similar) effect by MTRR
on non-Intel or non-PII (non-P6)? I do not believe that every PC hardware
around can handle this correctly without _nocache.
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to firstname.lastname@example.org
Please read the FAQ at http://www.tux.org/lkml/