Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Jan 2009 13:31:13 -0700 (MST)
From:      "M. Warner Losh" <imp@bsdimp.com>
To:        gonzo@bluezbox.com
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: uart and big-endian targets
Message-ID:  <20090120.133113.-1630673491.imp@bsdimp.com>
In-Reply-To: <4976297C.7020405@bluezbox.com>
References:  <4976297C.7020405@bluezbox.com>

next in thread | previous in thread | raw e-mail | index | archive | help
In message: <4976297C.7020405@bluezbox.com>
            Oleksandr Tymoshenko <gonzo@bluezbox.com> writes:
:      Yesterday I ran into a "problem" with uart(4) on big-endian MIPS
: board. uart code treats registers as bytes and reads/writes them using
: bus_space_read_1/bus_space_write_1. To handle word-aligned registers we
: have regshft in uart_bas structure. It works for little-endian flags
: where lowest byte resides at uart_base + (regnum << regshft) address
: but for big endian targets actual data resides at
: uart_base + ((regnum + 1) << regshft) - 1.

That's not the problem.  The problem is that we're trying to do byte
accesses to word registers.  That's why we see a disconnect between
the addresses since we're reading the wrong byte.  also, we may be
getting lucky with this access, but many chips have issues when you
access word registers as bytes.

: One way to solve it is to increase uart_base when setting uart_bas,
: but it's not obvious and requires knowledge of uart(4) internals.
: I think better solution would be to take into account endianess
: when defining uart_regofs. Or if other BE devices have data in
: highest byte new field should be added to uart_bas (defaulted to 0)
: 
: Any thoughts?

The base problem here is:

uart.h:

#define	uart_getreg(bas, reg)		\
	bus_space_read_1((bas)->bst, (bas)->bsh, uart_regofs(bas, reg))
#define	uart_setreg(bas, reg, value)	\
	bus_space_write_1((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value)

These should be, for the platform you are using:

#define	uart_getreg(bas, reg)		\
	bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg))
#define	uart_setreg(bas, reg, value)	\
	bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value)

There's no easy way to swap these out, nor is there a way to have
variants for different kinds of hardware attached to the same machine
(the UART on the SoC will have different access patterns than the UART
on the modem on the PC Card that's plugged in, for example).  This is
a short-coming in the design of UART.  One that's relatively easy to
fix, mind you, and one that could easily be fixed.  There's so many
twists like this that it can be hard to anticipate them all.

The Octeon port basically copies uart_dev_ns8250.c and provides its
own set of accessors.  This is right from a accessing the hardware
correctly point of view, but a pita from a code reuse point of view.
Were it not for the other quirks in Cavium's serial ports, there'd be
little reason to go this route...

Warner



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20090120.133113.-1630673491.imp>