2.6.x kernels & PCI-to-PCMCIA bridges [again]

Russell King rmk+pcmcia at arm.linux.org.uk
Tue Jun 29 09:14:42 EDT 2004

On Tue, Jun 29, 2004 at 09:53:14PM +0900, Komuro wrote:
> >> I've fixed card_start problem.
> >> Most client driver set card_start=0,
> >> So
> >> mem->card_start - mem->sys_start equals -mem->sys_start.  wrong!    
> >
> >Actually, its right.  You've programmed the memory start/stop registers
> >with sys_start and sys_stop.  Since card_start = 0, you want accesses
> >to physical address "sys_start" to correspond with card address
> >"card_start".  So, if card_start = 0, you need to program the offset
> >register (I365_W_OFF) with -sys_start to achieve the required address
> >on the PCMCIA bus.
> I think I365_W_OFF value is greater than or equal to 0.
> I365_W_OFF specifies the offset in PCMCIA Memory Address Space(MAX 64Mbytes).
> I365_W_START specifies the address in PCI Memory Address Space(MAX 4Gbytes)
> Please see "Figure 3-1 Memory Window Organization" in the PD6729 datasheet
> (page 18).
> You can dowonload the PD6729 datasheet from the pcmcia-cs site.
> http://pcmcia-cs.sourceforge.net/specs/index.html

Take a look at page 57 and 58 of that same data sheet.

"Bits 7-0: Offset address 19-12
"This register contains the least-significant byte of the quantity that
will be added to the system memory address that determines where in the
PCMCIA card's memory map the memory access will occur."

"Bits 5-0: Offset address 25-20
"This field contains the most-significant six bits of the Offset Address.
See the description of the Offset Address field associated with bits 7-0
of the Card Memory Map 0-4 Offset Address Low Register."

What this is saying is that:

	Card Address = System Address + Offset Address

System Address is mem->sys_start
Card Address is mem->card_start

Also note that the card address is only 26 bits in size, and therefore
the above equation only has 26 bits of precision.

So, we have:

	mem->card_start = mem->sys_start + Offset Address

Rearranging this equation gives:

	Offset Address = mem->card_start - mem->sys_start

Therefore, the code as it currently stands in even your pd6729 driver
is correct.

Now, as noted above, we only have 26 bits of precision for the card
address, and also note that the offset address is also 26 bits.
Remember your 2's complement arithmetic and you'll realise that
having a large 26-bit number (eg, 0x3ffffff) is the same as a
negative number (-1).

To give an example, lets assume that mem->sys_start is 0xe00000
and we want mem->card_start = 0.  Let's work it out:

	Offset Address = 0 - 0xe0000

This obviously gives Offset Address = -0xe0000, or 0xfff20000
(assuming 32-bit arithmetic).  Limiting this to the 26-bit number
gives 0x3f20000.  Now, lets plug this back in to the original
equation and work out what the actual card address is:

	Card Address = 0xe0000 + 0x3f20000

which gives 0x4000000.  Remember that card addresses are cropped
to 26 bits, and this gives a card address of 0 - which is what
we wanted all along.

Repeat this for another card address and you'll see that it all
works out correctly, and hopefully you'll prove to yourself that
the code as it stands is actually 100% correct.

You may also like to try plugging your proposed "fix" into the
equation and see what happens.

Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 PCMCIA      - http://pcmcia.arm.linux.org.uk/
                 2.6 Serial core

More information about the linux-pcmcia mailing list