From: Matt Porter <mporter@kernel.crashing.org> Date: 2006-02-01 17:44:08
On Wed, Feb 01, 2006 at 09:02:31AM -0800, David Hawkins wrote:
Jenkins, Clive wrote:
quoted
quoted
quoted
quoted
readl() and ioread32() read the registers in little-endian format!
Correct. That's how it is implemented on all platforms. Think for
example of an pci device driver. Using these IO functions, the
driver will become platform independent, running without
modifications on little- and big-endian machines.
I just stumbled across the section in Rubini 3rd Ed that mislead
me into believing that the readl()/writel() were machine endianness
dependent, i.e., LE on x86, BE on PPC.
quoted
p453 of his book has a PCI DMA example, where he uses the
cpu_to_le32() macros inside calls writel().
quoted
However, since these functions are internally implemented to
perform LE operations, this example appears to be incorrect.
quoted
Would you agree?
No, I think it is correct.
On most architectures readl() and writel() assume you are
accessing MMIO on the PCI bus, which is little-endian.
So these macros convert between the endian-ness of the CPU
and the endian-ness of the PCI bus.
Clive
Hey Clive,
Right, but in the 440EP source, and probably on other PowerPC
ports, readl() and writel() perform the endian conversion for
you, so if you wrote an operation as
writel(register_address, cpu_to_le32(data));
you would get *two* little endian conversions, i.e., you
would write the bytes in the wrong order.
I haven't looked in the source for other big-endian architecures
yet. I wonder if the ARM stuff is big, or little endian?
Both
The original question came about while I was trying to read
back PLX-9054 registers (a PCI-to-local bus bridge on a PCI
adapter board).
Those regs are little endian since it's a PCI device. You
use the old read*/write* or new ioread*/iowrite* since it's
a PCI device.
I search through the other drivers for the 440EP peripherals
that are not on the PCI bus showed that the authors used
the in_be32() and out_be32() calls. The comment at the top
of this email indicates that the readl() and writel() are
effectively 'reserved' for PCI accesses. I didn't get this
impression from reading Rubini.
The book implicitly focuses on x86 driver developers, that's
why you don't get an explicit statement about this...
"everything" is PCI in that world.
read*/write* and ioread*/iowrite* generate outbound little
endian cycles on ALL arches, period. They are intended
only for PCI use and have generic names only because of
the assumption that "all the world is a PC".
Now, what it takes to to generate outbound little endian cycles
varies. On some arches, it's just a store (native LE) on
other arches, it's a reversed store (PPC), others still configure
their PCI bridge hardware to do byte swapping in hardware (typically
if their arch doesn't have a simple byte-swapping store like PPC).
The example you cite on pg. 453 of Rubini looks broken for BE
systems. It works on LE systems since cpu_to_le32() does nothing
and writel is a simply dereference. That's pure luck. On PPC,
for example, that would write a big endian bus_addr to the fictitious
PCI device which is not what they want.
-Matt
From: David Hawkins <hidden> Date: 2006-02-01 17:51:52
The book implicitly focuses on x86 driver developers, that's
why you don't get an explicit statement about this...
"everything" is PCI in that world.
read*/write* and ioread*/iowrite* generate outbound little
endian cycles on ALL arches, period. They are intended
only for PCI use and have generic names only because of
the assumption that "all the world is a PC".
Now, what it takes to to generate outbound little endian cycles
varies. On some arches, it's just a store (native LE) on
other arches, it's a reversed store (PPC), others still configure
their PCI bridge hardware to do byte swapping in hardware (typically
if their arch doesn't have a simple byte-swapping store like PPC).
The example you cite on pg. 453 of Rubini looks broken for BE
systems. It works on LE systems since cpu_to_le32() does nothing
and writel is a simply dereference. That's pure luck. On PPC,
for example, that would write a big endian bus_addr to the fictitious
PCI device which is not what they want.
Great! An authoritive answer!
Re: endianness, even cooler on the 440EP, in your mmap()
implementation you can set the _PAGE_ENDIAN flag, and
user-space will see the PCI device in little endian
format. Fun stuff!
Thanks Matt.
Dave
From: David Hawkins <hidden> Date: 2006-02-01 18:02:21
Matt,
In the same vein as the readl()/writel() question, what
are the assumptions regarding memcpy_toio and memcpy_fromio?
If the memcpy_to/fromio operations are intended only
for access to PCI devices, then they should also inherently
perform little-endianness conversion. For the test driver
I was working on, I did *not* find this the case, eg.
I implemented the test driver read() and write() using the
memcpy_to/fromio calls, and the data transfers occur
in big-endian (well, 'native' mode, since I also test the
same test driver with the PCI adapter in an x86 system).
If memcpy_to/fromio can be used in a more general context,
then I can see why they operate in native mode.
Just looking for enlightenment.
Cheers
Dave
On Wed, Feb 01, 2006 at 10:04:15AM -0800, David Hawkins wrote:
Matt,
In the same vein as the readl()/writel() question, what
are the assumptions regarding memcpy_toio and memcpy_fromio?
If the memcpy_to/fromio operations are intended only
for access to PCI devices, then they should also inherently
perform little-endianness conversion. For the test driver
I was working on, I did *not* find this the case, eg.
I implemented the test driver read() and write() using the
memcpy_to/fromio calls, and the data transfers occur
in big-endian (well, 'native' mode, since I also test the
same test driver with the PCI adapter in an x86 system).
If memcpy_to/fromio can be used in a more general context,
then I can see why they operate in native mode.
Just looking for enlightenment.
This commands IIRC are intended for copying chunk of _bytes_. There
are no issues with endianess for bytes, e.g. they work just like
ordinary memcpy.
--
Eugene
From: David Hawkins <hidden> Date: 2006-02-01 18:18:44
Eugene Surovegin wrote:
On Wed, Feb 01, 2006 at 10:04:15AM -0800, David Hawkins wrote:
quoted
Matt,
In the same vein as the readl()/writel() question, what
are the assumptions regarding memcpy_toio and memcpy_fromio?
If the memcpy_to/fromio operations are intended only
for access to PCI devices, then they should also inherently
perform little-endianness conversion. For the test driver
I was working on, I did *not* find this the case, eg.
I implemented the test driver read() and write() using the
memcpy_to/fromio calls, and the data transfers occur
in big-endian (well, 'native' mode, since I also test the
same test driver with the PCI adapter in an x86 system).
If memcpy_to/fromio can be used in a more general context,
then I can see why they operate in native mode.
Just looking for enlightenment.
This commands IIRC are intended for copying chunk of _bytes_. There
are no issues with endianess for bytes, e.g. they work just like
ordinary memcpy.
True, good point.
I quite often implement a 'control' device to read/write/mmap PCI
device registers. In that case, the registers are usually 32-bit, so
if I wanted endian neutrality, I could either let the user-space
app determine the endianness and act accordingly, or force the
user-space app to always see little-endian registers by replacing
memcpy_to/fromio calls with a loop over read;/writel, and in mmap
making sure to set the _PAGE_ENDIAN flag. Of course, making mmap
endian-neutral depends on the 440EP page flags, which say an
ARM might not have.
Thanks for the valuable feedback guys.
Dave
On Wed, Feb 01, 2006 at 10:20:42AM -0800, David Hawkins wrote:
Eugene Surovegin wrote:
quoted
On Wed, Feb 01, 2006 at 10:04:15AM -0800, David Hawkins wrote:
quoted
Matt,
In the same vein as the readl()/writel() question, what
are the assumptions regarding memcpy_toio and memcpy_fromio?
If the memcpy_to/fromio operations are intended only
for access to PCI devices, then they should also inherently
perform little-endianness conversion. For the test driver
I was working on, I did *not* find this the case, eg.
I implemented the test driver read() and write() using the
memcpy_to/fromio calls, and the data transfers occur
in big-endian (well, 'native' mode, since I also test the
same test driver with the PCI adapter in an x86 system).
If memcpy_to/fromio can be used in a more general context,
then I can see why they operate in native mode.
Just looking for enlightenment.
This commands IIRC are intended for copying chunk of _bytes_. There
are no issues with endianess for bytes, e.g. they work just like
ordinary memcpy.
True, good point.
I quite often implement a 'control' device to read/write/mmap PCI
device registers. In that case, the registers are usually 32-bit, so
if I wanted endian neutrality, I could either let the user-space
app determine the endianness and act accordingly, or force the
user-space app to always see little-endian registers by replacing
memcpy_to/fromio calls with a loop over read;/writel,
You seem to assume that memcpy should do 32-bit reads/writes. Why not
16-bit ones? That's why memcpy cannot do any byte swapping, because it
can "theoretically" do 2 different types of it (16-bit and 32-bit),
which is obviously not specified in memcpy interface.
--
Eugene
From: Peter Korsgaard <jacmet@sunsite.dk> Date: 2006-02-01 21:12:44
quoted
quoted
quoted
quoted
"Matt" == Matt Porter [off-list ref] writes:
Matt> read*/write* and ioread*/iowrite* generate outbound little
Matt> endian cycles on ALL arches, period. They are intended
Matt> only for PCI use and have generic names only because of
Matt> the assumption that "all the world is a PC".
What is the preferred way of accessing non-PCI devices then? Direct
pointer access?
--
Bye, Peter Korsgaard
From: Kumar Gala <hidden> Date: 2006-02-02 01:02:06
On Wed, 1 Feb 2006, Peter Korsgaard wrote:
quoted
quoted
quoted
quoted
quoted
"Matt" == Matt Porter [off-list ref] writes:
Matt> read*/write* and ioread*/iowrite* generate outbound little
Matt> endian cycles on ALL arches, period. They are intended
Matt> only for PCI use and have generic names only because of
Matt> the assumption that "all the world is a PC".
What is the preferred way of accessing non-PCI devices then? Direct
pointer access?
No direct pointer access is bad. On PPC You can use
in_be{8,16,32}/out_be{8,16,32}
- kumar
From: Peter Korsgaard <jacmet@sunsite.dk> Date: 2006-02-02 08:09:19
On 2/2/06, Kumar Gala [off-list ref] wrote:
quoted
What is the preferred way of accessing non-PCI devices then? Direct
pointer access?
No direct pointer access is bad. On PPC You can use
in_be{8,16,32}/out_be{8,16,32}
What about arch independent drivers? Are there any generic approach
for this or do you have to stick to ugly #ifdefs to decide between
in_be32/inl ?
--
Bye, Peter Korsgaard