Extend the pl011 small-fifos fix to other SoCs that indicate rev 5

hardware but lack the larger fifos rev 5 hardware should have.

The linux world (where our FDT data comes from) solved this by adding
a new property to pl011 nodes, "arm,primecell-periphid".  When this
property is present, its values override the values in the hardware
periphid registers.  For pl011 rev 5 hardware with small fifos, they
override the id so that it appears to be rev 4 hardware.

The driver now uses the new property when present.  It also continues
to check the device compat string, to handle older fdt data that may
still be in use on existing systems (on RPi systems it is common to
update system software without updating fdt data which is part of the
boot firmware).

Reviewed by:	imp
This commit is contained in:
Ian Lepore 2017-03-11 22:34:02 +00:00
parent 1ba0d51a7b
commit bf8bdd6762

View File

@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <dev/uart/uart_cpu.h>
#ifdef FDT
#include <dev/uart/uart_cpu_fdt.h>
#include <dev/ofw/ofw_bus.h>
#endif
#include <dev/uart/uart_bus.h>
#include "uart_if.h"
@ -449,25 +450,36 @@ static int
uart_pl011_bus_probe(struct uart_softc *sc)
{
uint8_t hwrev;
bool is_bcm2835;
device_set_desc(sc->sc_dev, "PrimeCell UART (PL011)");
#ifdef FDT
pcell_t node;
uint32_t periphid;
/*
* The FIFO sizes vary depending on hardware; rev 2 and below have 16
* byte FIFOs, rev 3 and up are 32 byte. We get a bit of drama, as
* always, with the bcm2835 (rpi), which claims to be rev 3, but has 16
* byte FIFOs. We check for both the old freebsd-historic and the
* proper bindings-defined compatible strings for bcm2835.
* byte FIFOs, rev 3 and up are 32 byte. The hardware rev is in the
* primecell periphid register, but we get a bit of drama, as always,
* with the bcm2835 (rpi), which claims to be rev 3, but has 16 byte
* FIFOs. We check for both the old freebsd-historic and the proper
* bindings-defined compatible strings for bcm2835, and also check the
* workaround the linux drivers use for rpi3, which is to override the
* primecell periphid register value with a property.
*/
#ifdef FDT
is_bcm2835 = ofw_bus_is_compatible(sc->sc_dev, "brcm,bcm2835-pl011") ||
ofw_bus_is_compatible(sc->sc_dev, "broadcom,bcm2835-uart");
if (ofw_bus_is_compatible(sc->sc_dev, "brcm,bcm2835-pl011") ||
ofw_bus_is_compatible(sc->sc_dev, "broadcom,bcm2835-uart")) {
hwrev = 2;
} else {
node = ofw_bus_get_node(sc->sc_dev);
if (OF_getencprop(node, "arm,primecell-periphid", &periphid,
sizeof(periphid)) > 0) {
hwrev = (periphid >> 20) & 0x0f;
} else {
hwrev = __uart_getreg(&sc->sc_bas, UART_PIDREG_2) >> 4;
}
}
#else
is_bcm2835 = false;
#endif
hwrev = __uart_getreg(&sc->sc_bas, UART_PIDREG_2) >> 4;
if (hwrev <= 2 || is_bcm2835) {
#endif
if (hwrev <= 2) {
sc->sc_rxfifosz = FIFO_RX_SIZE_R2;
sc->sc_txfifosz = FIFO_TX_SIZE_R2;
} else {
@ -475,6 +487,8 @@ uart_pl011_bus_probe(struct uart_softc *sc)
sc->sc_txfifosz = FIFO_TX_SIZE_R3;
}
device_set_desc(sc->sc_dev, "PrimeCell UART (PL011)");
return (0);
}