- pci_mvebu: Minor cleanup (Pali)
- mvebu: turris_omnia: Enable ext4 write support (Marek)
- a37xx: Misc fixes in PCI and pinctrl (Pali & Marek)
- a38x/rtc: Fix null pointer access (Francios)
- mvebu: x530: clearfog: Fix ODT configuration (Chris)
- kwboot: Fix boot and terminal mode (Pali)
This commit is contained in:
Tom Rini 2022-03-04 08:27:32 -05:00
commit 55b5c426ae
10 changed files with 608 additions and 193 deletions

View File

@ -316,8 +316,8 @@ static int fdt_setprop_inplace_u32_partial(void *blob, int node,
int a3700_fdt_fix_pcie_regions(void *blob)
{
int acells, pacells, scells;
u32 base, fix_offset;
u32 base, lowest_cpu_addr, fix_offset;
int pci_cells, cpu_cells, size_cells;
const u32 *ranges;
int node, pnode;
int ret, i, len;
@ -331,51 +331,80 @@ int a3700_fdt_fix_pcie_regions(void *blob)
return node;
ranges = fdt_getprop(blob, node, "ranges", &len);
if (!ranges || len % sizeof(u32))
return -ENOENT;
if (!ranges || !len || len % sizeof(u32))
return -EINVAL;
/*
* The "ranges" property is an array of
* { <child address> <parent address> <size in child address space> }
* { <PCI address> <CPU address> <size in PCI address space> }
* where number of PCI address cells and size cells is stored in the
* "#address-cells" and "#size-cells" properties of the same node
* containing the "ranges" property and number of CPU address cells
* is stored in the parent's "#address-cells" property.
*
* All 3 elements can span a diffent number of cells. Fetch their sizes.
* All 3 elements can span a diffent number of cells. Fetch them.
*/
pnode = fdt_parent_offset(blob, node);
acells = fdt_address_cells(blob, node);
pacells = fdt_address_cells(blob, pnode);
scells = fdt_size_cells(blob, node);
pci_cells = fdt_address_cells(blob, node);
cpu_cells = fdt_address_cells(blob, pnode);
size_cells = fdt_size_cells(blob, node);
/* Child PCI addresses always use 3 cells */
if (acells != 3)
return -ENOENT;
/* PCI addresses always use 3 cells */
if (pci_cells != 3)
return -EINVAL;
/* Calculate fixup offset from first child address (in last cell) */
fix_offset = base - fdt32_to_cpu(ranges[2]);
/* CPU addresses on Armada 37xx always use 2 cells */
if (cpu_cells != 2)
return -EINVAL;
/* If fixup offset is zero then there is nothing to fix */
for (i = 0; i < len / sizeof(u32);
i += pci_cells + cpu_cells + size_cells) {
/*
* Parent CPU addresses on Armada 37xx are always 32-bit, so
* check that the high word is zero.
*/
if (fdt32_to_cpu(ranges[i + pci_cells]))
return -EINVAL;
if (i == 0 ||
fdt32_to_cpu(ranges[i + pci_cells + 1]) < lowest_cpu_addr)
lowest_cpu_addr = fdt32_to_cpu(ranges[i + pci_cells + 1]);
}
/* Calculate fixup offset from the lowest (first) CPU address */
fix_offset = base - lowest_cpu_addr;
/* If fixup offset is zero there is nothing to fix */
if (!fix_offset)
return 0;
/*
* Fix address (last cell) of each child address and each parent
* address
* Fix each CPU address and corresponding PCI address if PCI address
* is not already remapped (has the same value)
*/
for (i = 0; i < len / sizeof(u32); i += acells + pacells + scells) {
for (i = 0; i < len / sizeof(u32);
i += pci_cells + cpu_cells + size_cells) {
u32 cpu_addr;
u64 pci_addr;
int idx;
/* fix child address */
idx = i + acells - 1;
/* Fix CPU address */
idx = i + pci_cells + cpu_cells - 1;
cpu_addr = fdt32_to_cpu(ranges[idx]);
ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx,
fdt32_to_cpu(ranges[idx]) +
fix_offset);
cpu_addr + fix_offset);
if (ret)
return ret;
/* fix parent address */
idx = i + acells + pacells - 1;
/* Fix PCI address only if it isn't remapped (is same as CPU) */
idx = i + pci_cells - 1;
pci_addr = ((u64)fdt32_to_cpu(ranges[idx - 1]) << 32) |
fdt32_to_cpu(ranges[idx]);
if (cpu_addr != pci_addr)
continue;
ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx,
fdt32_to_cpu(ranges[idx]) +
fix_offset);
cpu_addr + fix_offset);
if (ret)
return ret;
}

View File

@ -73,6 +73,7 @@ static struct mv_ddr_topology_map board_topology_map = {
{0}, /* timing parameters */
{ {0} }, /* electrical configuration */
{0}, /* electrical parameters */
0, /* ODT configuration */
0, /* Clock enable mask */
160 /* Clock delay */
};

View File

@ -147,6 +147,7 @@ static struct mv_ddr_topology_map board_topology_map = {
{0}, /* timing parameters */
{ {0} }, /* electrical configuration */
{0,}, /* electrical parameters */
0, /* ODT configuration */
0x3, /* clock enable mask */
};

View File

@ -93,3 +93,4 @@ CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_WDT=y
CONFIG_WDT_ORION=y
CONFIG_EXT4_WRITE=y

View File

@ -1,4 +1,4 @@
.TH KWBOOT 1 "2021-08-25"
.TH KWBOOT 1 "2022-03-02"
.SH NAME
kwboot \- Boot Marvell Kirkwood (and others 32-bit) SoCs over a serial link.
@ -11,7 +11,7 @@ kwboot \- Boot Marvell Kirkwood (and others 32-bit) SoCs over a serial link.
.SH "DESCRIPTION"
The \fBkwboot\fP program boots boards based on Marvell's 32-bit
platforms including Kirkwood, Dove, A370, AXP, A375, A38x
platforms including Kirkwood, Dove, Avanta, A370, AXP, A375, A38x
and A39x over their integrated UART. Boot image files will typically
contain a second stage boot loader, such as U-Boot. The image file
must conform to Marvell's BootROM firmware image format
@ -47,6 +47,48 @@ code in it's header which may also print some output via UART (for
example U-Boot SPL does this). In such a case, this output is also
written to stdout after the header is sent.
.TP
.B "\-b"
Do only handshake on \fITTY\fP without uploading any file. File upload
could be done later via option \fB\-D\fP or via any other Xmodem
application, like \fBsx\fP(1).
.TP
.B "\-d"
Do special handshake on \fITTY\fP for console debug mode.
This will instruct BootROM to enter builtin simple console debug mode.
Should be combined with option \fB\-t\fP.
To get a BootROM help, type this command followed by ENTER key:
.RS 1.2i
.TP
.B ?
.RE
.IP
Armada 38x BootROM has a bug which cause that BootROM's standard output
is turned off on UART when SPI-NOR contains valid boot image. Nevertheless
BootROM's standard input and BootROM's terminal echo are active and working
fine. To workaround this BootROM bug with standard output, it is possible
to manually overwrite BootROM variables stored in SRAM which BootROM use
for checking if standard output is enabled or not. To enable BootROM
standard output on UART, type this command folled by ENTER key:
.RS 1.2i
.TP
.B w 0x40034100 1
.RE
.TP
.BI "\-D" " image"
Upload file \fIimage\fP over \fITTY\fP without initial handshake.
This method is used primary on Dove platforms, where BootROM does
not support initial handshake for entering UART upload mode and
strapping pins (exported via e.g. buttons) are used instead.
.TP
.BI "\-p"
Obsolete. Does nothing.
@ -55,13 +97,33 @@ In the past, when this option was used, the program patched the header
in the image prior upload, to "UART boot" type. This is now done by
default.
.TP
.B "\-q"
Obsolete. Does nothing.
It is unknown whether it did something in the past.
.TP
.BI "\-s" " response-timeout"
Specify custom response timeout when doing handshake. Default value is 50 ms.
It is the timeout between sending two consecutive handshake patterns, meaning
how long to wait for response from BootROM. Affects only option \fB\-b\fP with
image file and option \fB\-d\fP.
Option \fB-a\fP specify response timeout suitable for Armada XP BootROM and
currently it is 1000 ms.
Some testing showed that specifying 24 ms as response timeout make handshake
with Armada 385 BootROM more stable.
.TP
.BI "\-t"
Run a terminal program, connecting standard input and output to
.RB \fITTY\fP.
If used in combination with \fB-b\fP, terminal mode is entered
immediately following a successful image upload.
If used in combination with \fB\-b\fP, \fB\-D\fP or \fB\-d\fP option,
terminal mode is entered immediately following a successful image upload
or successful handshake (if not doing image upload).
If standard I/O streams connect to a console, this mode will terminate
after receiving \fBctrl-\e\fP followed by \fBc\fP from console input.
@ -85,9 +147,42 @@ Tested values for \fIbaudrate\fP for Armada 38x include: 115200,
230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000,
2000000, 2500000, 3125000, 4000000 and 5200000.
.SH "EXAMPLES"
Instruct BootROM to enter boot Xmodem boot mode, send \fIu-boot-spl.kwb\fP
kwbimage file via Xmodem on \fI/dev/ttyUSB0\fP at 115200 Bd and run terminal
program:
.IP
.B kwboot -b u-boot-spl.kwb -t /dev/ttyUSB0
.PP
Instruct BootROM to enter boot Xmodem boot mode, send header of
\fIu-boot-spl.kwb\fP kwbimage file via Xmodem at 115200 Bd, then instruct
BootROM to change baudrate to 5200000 Bd, send data part of the kwbimage
file via Xmodem at high speed and finally run terminal program:
.IP
.B kwboot -b u-boot-spl.kwb -B 5200000 -t /dev/ttyUSB0
.PP
Only send \fIu-boot-spl.kwb\fP kwbimage file via Xmodem on \fI/dev/ttyUSB0\fP
at 115200 Bd:
.IP
.B kwboot -D u-boot-spl.kwb /dev/ttyUSB0
.PP
Instruct BootROM to enter console debug mode and run terminal program on
\fI/dev/ttyUSB0\fP at 115200 Bd:
.IP
.B kwboot -d -t /dev/ttyUSB0
.PP
Only run terminal program on \fI/dev/ttyUSB0\fP at 115200 Bd:
.IP
.B kwboot -t /dev/ttyUSB0
.SH "SEE ALSO"
.PP
\fBmkimage\fP(1)
\fBmkimage\fP(1), \fBsx\fP(1)
.SH "AUTHORS"

View File

@ -30,37 +30,25 @@
#include <linux/sizes.h>
/* PCIe unit register offsets */
#define SELECT(x, n) ((x >> n) & 1UL)
#define PCIE_DEV_ID_OFF 0x0000
#define PCIE_CMD_OFF 0x0004
#define PCIE_DEV_REV_OFF 0x0008
#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
#define PCIE_EXP_ROM_BAR_OFF 0x0030
#define PCIE_CAPAB_OFF 0x0060
#define PCIE_CTRL_STAT_OFF 0x0068
#define PCIE_HEADER_LOG_4_OFF 0x0128
#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4))
#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
#define PCIE_WIN5_CTRL_OFF 0x1880
#define PCIE_WIN5_BASE_OFF 0x1884
#define PCIE_WIN5_REMAP_OFF 0x188c
#define PCIE_CONF_ADDR_OFF 0x18f8
#define PCIE_CONF_DATA_OFF 0x18fc
#define PCIE_MASK_OFF 0x1910
#define PCIE_MASK_ENABLE_INTS (0xf << 24)
#define PCIE_CTRL_OFF 0x1a00
#define PCIE_CTRL_X1_MODE BIT(0)
#define PCIE_CTRL_RC_MODE BIT(1)
#define PCIE_STAT_OFF 0x1a04
#define PCIE_STAT_BUS (0xff << 8)
#define PCIE_STAT_DEV (0x1f << 16)
#define PCIE_STAT_LINK_DOWN BIT(0)
#define PCIE_DEBUG_CTRL 0x1a60
#define PCIE_DEBUG_SOFT_RESET BIT(20)
#define MVPCIE_ROOT_PORT_PCI_CFG_OFF 0x0000
#define MVPCIE_ROOT_PORT_PCI_EXP_OFF 0x0060
#define MVPCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
#define MVPCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
#define MVPCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
#define MVPCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
#define MVPCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4))
#define MVPCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
#define MVPCIE_WIN5_CTRL_OFF 0x1880
#define MVPCIE_WIN5_BASE_OFF 0x1884
#define MVPCIE_WIN5_REMAP_OFF 0x188c
#define MVPCIE_CONF_ADDR_OFF 0x18f8
#define MVPCIE_CONF_DATA_OFF 0x18fc
#define MVPCIE_CTRL_OFF 0x1a00
#define MVPCIE_CTRL_RC_MODE BIT(1)
#define MVPCIE_STAT_OFF 0x1a04
#define MVPCIE_STAT_BUS (0xff << 8)
#define MVPCIE_STAT_DEV (0x1f << 16)
#define MVPCIE_STAT_LINK_DOWN BIT(0)
#define LINK_WAIT_RETRIES 100
#define LINK_WAIT_TIMEOUT 1000
@ -77,7 +65,6 @@ struct mvebu_pcie {
u32 lane;
bool is_x4;
int devfn;
u32 lane_mask;
int sec_busno;
char name[16];
unsigned int mem_target;
@ -90,8 +77,8 @@ struct mvebu_pcie {
static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie)
{
u32 val;
val = readl(pcie->base + PCIE_STAT_OFF);
return !(val & PCIE_STAT_LINK_DOWN);
val = readl(pcie->base + MVPCIE_STAT_OFF);
return !(val & MVPCIE_STAT_LINK_DOWN);
}
static void mvebu_pcie_wait_for_link(struct mvebu_pcie *pcie)
@ -115,20 +102,20 @@ static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie *pcie, int busno)
{
u32 stat;
stat = readl(pcie->base + PCIE_STAT_OFF);
stat &= ~PCIE_STAT_BUS;
stat = readl(pcie->base + MVPCIE_STAT_OFF);
stat &= ~MVPCIE_STAT_BUS;
stat |= busno << 8;
writel(stat, pcie->base + PCIE_STAT_OFF);
writel(stat, pcie->base + MVPCIE_STAT_OFF);
}
static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie *pcie, int devno)
{
u32 stat;
stat = readl(pcie->base + PCIE_STAT_OFF);
stat &= ~PCIE_STAT_DEV;
stat = readl(pcie->base + MVPCIE_STAT_OFF);
stat &= ~MVPCIE_STAT_DEV;
stat |= devno << 16;
writel(stat, pcie->base + PCIE_STAT_OFF);
writel(stat, pcie->base + MVPCIE_STAT_OFF);
}
static inline struct mvebu_pcie *hose_to_pcie(struct pci_controller *hose)
@ -198,18 +185,18 @@ static int mvebu_pcie_read_config(const struct udevice *bus, pci_dev_t bdf,
addr = PCI_CONF1_EXT_ADDRESS(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset);
/* write address */
writel(addr, pcie->base + PCIE_CONF_ADDR_OFF);
writel(addr, pcie->base + MVPCIE_CONF_ADDR_OFF);
/* read data */
switch (size) {
case PCI_SIZE_8:
data = readb(pcie->base + PCIE_CONF_DATA_OFF + (offset & 3));
data = readb(pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 3));
break;
case PCI_SIZE_16:
data = readw(pcie->base + PCIE_CONF_DATA_OFF + (offset & 2));
data = readw(pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 2));
break;
case PCI_SIZE_32:
data = readl(pcie->base + PCIE_CONF_DATA_OFF);
data = readl(pcie->base + MVPCIE_CONF_DATA_OFF);
break;
default:
return -EINVAL;
@ -289,18 +276,18 @@ static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf,
addr = PCI_CONF1_EXT_ADDRESS(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset);
/* write address */
writel(addr, pcie->base + PCIE_CONF_ADDR_OFF);
writel(addr, pcie->base + MVPCIE_CONF_ADDR_OFF);
/* write data */
switch (size) {
case PCI_SIZE_8:
writeb(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 3));
writeb(value, pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 3));
break;
case PCI_SIZE_16:
writew(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 2));
writew(value, pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 2));
break;
case PCI_SIZE_32:
writel(value, pcie->base + PCIE_CONF_DATA_OFF);
writel(value, pcie->base + MVPCIE_CONF_DATA_OFF);
break;
default:
return -EINVAL;
@ -324,20 +311,20 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie)
/* First, disable and clear BARs and windows. */
for (i = 1; i < 3; i++) {
writel(0, pcie->base + PCIE_BAR_CTRL_OFF(i));
writel(0, pcie->base + PCIE_BAR_LO_OFF(i));
writel(0, pcie->base + PCIE_BAR_HI_OFF(i));
writel(0, pcie->base + MVPCIE_BAR_CTRL_OFF(i));
writel(0, pcie->base + MVPCIE_BAR_LO_OFF(i));
writel(0, pcie->base + MVPCIE_BAR_HI_OFF(i));
}
for (i = 0; i < 5; i++) {
writel(0, pcie->base + PCIE_WIN04_CTRL_OFF(i));
writel(0, pcie->base + PCIE_WIN04_BASE_OFF(i));
writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i));
writel(0, pcie->base + MVPCIE_WIN04_CTRL_OFF(i));
writel(0, pcie->base + MVPCIE_WIN04_BASE_OFF(i));
writel(0, pcie->base + MVPCIE_WIN04_REMAP_OFF(i));
}
writel(0, pcie->base + PCIE_WIN5_CTRL_OFF);
writel(0, pcie->base + PCIE_WIN5_BASE_OFF);
writel(0, pcie->base + PCIE_WIN5_REMAP_OFF);
writel(0, pcie->base + MVPCIE_WIN5_CTRL_OFF);
writel(0, pcie->base + MVPCIE_WIN5_BASE_OFF);
writel(0, pcie->base + MVPCIE_WIN5_REMAP_OFF);
/* Setup windows for DDR banks. Count total DDR size on the fly. */
size = 0;
@ -345,12 +332,12 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie)
const struct mbus_dram_window *cs = dram->cs + i;
writel(cs->base & 0xffff0000,
pcie->base + PCIE_WIN04_BASE_OFF(i));
writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i));
pcie->base + MVPCIE_WIN04_BASE_OFF(i));
writel(0, pcie->base + MVPCIE_WIN04_REMAP_OFF(i));
writel(((cs->size - 1) & 0xffff0000) |
(cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
pcie->base + PCIE_WIN04_CTRL_OFF(i));
pcie->base + MVPCIE_WIN04_CTRL_OFF(i));
size += cs->size;
}
@ -360,14 +347,14 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie)
size = 1 << fls(size);
/* Setup BAR[1] to all DRAM banks. */
writel(dram->cs[0].base | 0xc, pcie->base + PCIE_BAR_LO_OFF(1));
writel(0, pcie->base + PCIE_BAR_HI_OFF(1));
writel(dram->cs[0].base | 0xc, pcie->base + MVPCIE_BAR_LO_OFF(1));
writel(0, pcie->base + MVPCIE_BAR_HI_OFF(1));
writel(((size - 1) & 0xffff0000) | 0x1,
pcie->base + PCIE_BAR_CTRL_OFF(1));
pcie->base + MVPCIE_BAR_CTRL_OFF(1));
/* Setup BAR[0] to internal registers. */
writel(pcie->intregs, pcie->base + PCIE_BAR_LO_OFF(0));
writel(0, pcie->base + PCIE_BAR_HI_OFF(0));
writel(pcie->intregs, pcie->base + MVPCIE_BAR_LO_OFF(0));
writel(0, pcie->base + MVPCIE_BAR_HI_OFF(0));
}
/* Only enable PCIe link, do not setup it */
@ -406,9 +393,9 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie)
u32 reg;
/* Setup PCIe controller to Root Complex mode */
reg = readl(pcie->base + PCIE_CTRL_OFF);
reg |= PCIE_CTRL_RC_MODE;
writel(reg, pcie->base + PCIE_CTRL_OFF);
reg = readl(pcie->base + MVPCIE_CTRL_OFF);
reg |= MVPCIE_CTRL_RC_MODE;
writel(reg, pcie->base + MVPCIE_CTRL_OFF);
/*
* Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link
@ -417,10 +404,10 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie)
* be set to number of SerDes PCIe lanes (1 or 4). If this register is
* not set correctly then link with endpoint card is not established.
*/
reg = readl(pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP);
reg = readl(pcie->base + MVPCIE_ROOT_PORT_PCI_EXP_OFF + PCI_EXP_LNKCAP);
reg &= ~PCI_EXP_LNKCAP_MLW;
reg |= (pcie->is_x4 ? 4 : 1) << 4;
writel(reg, pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP);
writel(reg, pcie->base + MVPCIE_ROOT_PORT_PCI_EXP_OFF + PCI_EXP_LNKCAP);
}
static int mvebu_pcie_probe(struct udevice *dev)
@ -443,7 +430,7 @@ static int mvebu_pcie_probe(struct udevice *dev)
* have the same format in Marvell's specification as in PCIe
* specification, but their meaning is totally different and they do
* different things: they are aliased into internal mvebu registers
* (e.g. PCIE_BAR_LO_OFF) and these should not be changed or
* (e.g. MVPCIE_BAR_LO_OFF) and these should not be changed or
* reconfigured by pci device drivers.
*
* So our driver converts Type 0 config space to Type 1 and reports
@ -451,10 +438,10 @@ static int mvebu_pcie_probe(struct udevice *dev)
* Type 1 registers is redirected to the virtual cfgcache[] buffer,
* which avoids changing unrelated registers.
*/
reg = readl(pcie->base + PCIE_DEV_REV_OFF);
reg = readl(pcie->base + MVPCIE_ROOT_PORT_PCI_CFG_OFF + PCI_CLASS_REVISION);
reg &= ~0xffffff00;
reg |= (PCI_CLASS_BRIDGE_PCI << 8) << 8;
writel(reg, pcie->base + PCIE_DEV_REV_OFF);
writel(reg, pcie->base + MVPCIE_ROOT_PORT_PCI_CFG_OFF + PCI_CLASS_REVISION);
/*
* mvebu uses local bus number and local device number to determinate

View File

@ -162,11 +162,11 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = {
PIN_GRP_GPIO("emmc_nb", 27, 9, BIT(2), "emmc"),
PIN_GRP_GPIO_3("pwm0", 11, 1, BIT(3) | BIT(20), 0, BIT(20), BIT(3),
"pwm", "led"),
PIN_GRP_GPIO_3("pwm1", 11, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4),
PIN_GRP_GPIO_3("pwm1", 12, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4),
"pwm", "led"),
PIN_GRP_GPIO_3("pwm2", 11, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5),
PIN_GRP_GPIO_3("pwm2", 13, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5),
"pwm", "led"),
PIN_GRP_GPIO_3("pwm3", 11, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6),
PIN_GRP_GPIO_3("pwm3", 14, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6),
"pwm", "led"),
PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"),
PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"),

View File

@ -121,7 +121,7 @@ static int armada38x_rtc_reset(struct udevice *dev)
armada38x_rtc_write(0, rtc, RTC_CONF_TEST);
mdelay(500);
armada38x_rtc_write(0, rtc, RTC_TIME);
armada38x_rtc_write(BIT(0) | BIT(1), 0, RTC_STATUS);
armada38x_rtc_write(BIT(0) | BIT(1), rtc, RTC_STATUS);
}
return 0;

View File

@ -196,6 +196,9 @@ hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl
hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl
HOSTCFLAGS_mkexynosspl.o := -pedantic
HOSTCFLAGS_kwboot.o += -pthread
HOSTLDLIBS_kwboot += -pthread -ltinfo
ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o
hostprogs-$(CONFIG_X86) += ifdtool

View File

@ -1,15 +1,40 @@
/*
* Boot a Marvell SoC, with Xmodem over UART0.
* supports Kirkwood, Dove, Armada 370, Armada XP, Armada 375, Armada 38x and
* Armada 39x
* supports Kirkwood, Dove, Avanta, Armada 370, Armada XP, Armada 375,
* Armada 38x and Armada 39x.
*
* (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
* (c) 2021 Pali Rohár <pali@kernel.org>
* (c) 2021 Marek Behún <marek.behun@nic.cz>
*
* References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
* Integrated Controller: Functional Specifications" December 2,
* 2008. Chapter 24.2 "BootROM Firmware".
* References:
* - "88F6180, 88F6190, 88F6192, and 88F6281: Integrated Controller: Functional
* Specifications" December 2, 2008. Chapter 24.2 "BootROM Firmware".
* https://web.archive.org/web/20130730091033/https://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf
* - "88AP510: High-Performance SoC with Integrated CPU, 2D/3D Graphics
* Processor, and High-Definition Video Decoder: Functional Specifications"
* August 3, 2011. Chapter 5 "BootROM Firmware"
* https://web.archive.org/web/20120130172443/https://www.marvell.com/application-processors/armada-500/assets/Armada-510-Functional-Spec.pdf
* - "88F6710, 88F6707, and 88F6W11: ARMADA(R) 370 SoC: Functional Specifications"
* May 26, 2014. Chapter 6 "BootROM Firmware".
* https://web.archive.org/web/20140617183701/https://www.marvell.com/embedded-processors/armada-300/assets/ARMADA370-FunctionalSpec-datasheet.pdf
* - "MV78230, MV78260, and MV78460: ARMADA(R) XP Family of Highly Integrated
* Multi-Core ARMv7 Based SoC Processors: Functional Specifications"
* May 29, 2014. Chapter 6 "BootROM Firmware".
* https://web.archive.org/web/20180829171131/https://www.marvell.com/embedded-processors/armada-xp/assets/ARMADA-XP-Functional-SpecDatasheet.pdf
* - "ARMADA(R) 375 Value-Performance Dual Core CPU System on Chip: Functional
* Specifications" Doc. No. MV-S109377-00, Rev. A. September 18, 2013.
* Chapter 7 "Boot Sequence"
* CONFIDENTIAL, no public documentation available
* - "88F6810, 88F6811, 88F6821, 88F6W21, 88F6820, and 88F6828: ARMADA(R) 38x
* Family High-Performance Single/Dual CPU System on Chip: Functional
* Specifications" Doc. No. MV-S109094-00, Rev. C. August 2, 2015.
* Chapter 7 "Boot Flow"
* CONFIDENTIAL, no public documentation available
* - "88F6920, 88F6925 and 88F6928: ARMADA(R) 39x High-Performance Dual Core CPU
* System on Chip Functional Specifications" Doc. No. MV-S109896-00, Rev. B.
* December 22, 2015. Chapter 7 "Boot Flow"
* CONFIDENTIAL, no public documentation available
*/
#include "kwbimage.h"
@ -28,6 +53,7 @@
#include <stdint.h>
#include <time.h>
#include <sys/stat.h>
#include <pthread.h>
#ifdef __linux__
#include "termios_linux.h"
@ -35,6 +61,13 @@
#include <termios.h>
#endif
/*
* These functions are in <term.h> header file, but this header file conflicts
* with "termios_linux.h" header file. So declare these functions manually.
*/
extern int setupterm(const char *, int, int *);
extern char *tigetstr(const char *);
/*
* Marvell BootROM UART Sensing
*/
@ -48,11 +81,9 @@ static unsigned char kwboot_msg_debug[] = {
};
/* Defines known to work on Kirkwood */
#define KWBOOT_MSG_REQ_DELAY 10 /* ms */
#define KWBOOT_MSG_RSP_TIMEO 50 /* ms */
/* Defines known to work on Armada XP */
#define KWBOOT_MSG_REQ_DELAY_AXP 1000 /* ms */
#define KWBOOT_MSG_RSP_TIMEO_AXP 1000 /* ms */
/*
@ -285,7 +316,6 @@ static const char kwb_baud_magic[16] = "$baudratechange";
static int kwboot_verbose;
static int msg_req_delay = KWBOOT_MSG_REQ_DELAY;
static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
@ -720,42 +750,120 @@ out:
return rc;
}
static int
kwboot_bootmsg(int tty, void *msg)
static void *
kwboot_msg_write_handler(void *arg)
{
struct kwboot_block block;
int rc;
char c;
int count;
int tty = *(int *)((void **)arg)[0];
const void *msg = ((void **)arg)[1];
int rsp_timeo = msg_rsp_timeo;
int i, dummy_oldtype;
if (msg == NULL)
kwboot_printv("Please reboot the target into UART boot mode...");
else
kwboot_printv("Sending boot message. Please reboot the target...");
/* allow to cancel this thread at any time */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &dummy_oldtype);
do {
rc = tcflush(tty, TCIOFLUSH);
if (rc)
break;
for (count = 0; count < 128; count++) {
rc = kwboot_tty_send(tty, msg, 8, 0);
if (rc) {
usleep(msg_req_delay * 1000);
continue;
while (1) {
/* write 128 samples of message pattern into the output queue without waiting */
for (i = 0; i < 128; i++) {
if (kwboot_tty_send(tty, msg, 8, 1) < 0) {
perror("\nFailed to send message pattern");
exit(1);
}
}
/* wait until output queue is transmitted and then make pause */
if (tcdrain(tty) < 0) {
perror("\nFailed to send message pattern");
exit(1);
}
/* BootROM requires pause on UART after it detects message pattern */
usleep(rsp_timeo * 1000);
}
}
rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
static int
kwboot_msg_start_thread(pthread_t *thread, int *tty, void *msg)
{
void *arg[2];
int rc;
arg[0] = tty;
arg[1] = msg;
rc = pthread_create(thread, NULL, kwboot_msg_write_handler, arg);
if (rc) {
errno = rc;
return -1;
}
return 0;
}
static int
kwboot_msg_stop_thread(pthread_t thread)
{
int rc;
rc = pthread_cancel(thread);
if (rc) {
errno = rc;
return -1;
}
rc = pthread_join(thread, NULL);
if (rc) {
errno = rc;
return -1;
}
return 0;
}
static int
kwboot_bootmsg(int tty)
{
struct kwboot_block block;
pthread_t write_thread;
int rc, err;
char c;
/* flush input and output queue */
tcflush(tty, TCIOFLUSH);
rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_boot);
if (rc) {
perror("Failed to start write thread");
return rc;
}
kwboot_printv("Sending boot message. Please reboot the target...");
err = 0;
while (1) {
kwboot_spinner();
} while (rc || c != NAK);
rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
if (rc && errno == ETIMEDOUT) {
continue;
} else if (rc) {
err = errno;
break;
}
if (c == NAK)
break;
}
kwboot_printv("\n");
if (rc)
rc = kwboot_msg_stop_thread(write_thread);
if (rc) {
perror("Failed to stop write thread");
return rc;
}
if (err) {
errno = err;
perror("Failed to read response for boot message pattern");
return -1;
}
/*
* At this stage we have sent more boot message patterns and BootROM
@ -772,11 +880,19 @@ kwboot_bootmsg(int tty, void *msg)
*/
/* flush output queue with remaining boot message patterns */
tcflush(tty, TCOFLUSH);
rc = tcflush(tty, TCOFLUSH);
if (rc) {
perror("Failed to flush output queue");
return rc;
}
/* send one xmodem packet with 0xff bytes to force BootROM to re-sync */
memset(&block, 0xff, sizeof(block));
kwboot_tty_send(tty, &block, sizeof(block), 0);
rc = kwboot_tty_send(tty, &block, sizeof(block), 0);
if (rc) {
perror("Failed to send sync sequence");
return rc;
}
/*
* Sending 132 bytes via 115200B/8-N-1 takes 11.45 ms, reading 132 bytes
@ -785,40 +901,151 @@ kwboot_bootmsg(int tty, void *msg)
usleep(30 * 1000);
/* flush remaining NAK replies from input queue */
tcflush(tty, TCIFLUSH);
rc = tcflush(tty, TCIFLUSH);
if (rc) {
perror("Failed to flush input queue");
return rc;
}
return 0;
}
static int
kwboot_debugmsg(int tty, void *msg)
kwboot_debugmsg(int tty)
{
int rc;
unsigned char buf[8192];
pthread_t write_thread;
int rc, err, i, pos;
size_t off;
/* flush input and output queue */
tcflush(tty, TCIOFLUSH);
rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_debug);
if (rc) {
perror("Failed to start write thread");
return rc;
}
kwboot_printv("Sending debug message. Please reboot the target...");
kwboot_spinner();
do {
char buf[16];
rc = tcflush(tty, TCIOFLUSH);
if (rc)
break;
rc = kwboot_tty_send(tty, msg, 8, 0);
if (rc) {
usleep(msg_req_delay * 1000);
err = 0;
off = 0;
while (1) {
/* Read immediately all bytes in queue without waiting */
rc = read(tty, buf + off, sizeof(buf) - off);
if ((rc < 0 && errno == EINTR) || rc == 0) {
continue;
} else if (rc < 0) {
err = errno;
break;
}
rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo);
off += rc - 1;
kwboot_spinner();
} while (rc);
/*
* Check if we received at least 4 debug message patterns
* (console echo from BootROM) in cyclic buffer
*/
for (pos = 0; pos < sizeof(kwboot_msg_debug); pos++)
if (buf[off] == kwboot_msg_debug[(pos + off) % sizeof(kwboot_msg_debug)])
break;
for (i = off; i >= 0; i--)
if (buf[i] != kwboot_msg_debug[(pos + i) % sizeof(kwboot_msg_debug)])
break;
off -= i;
if (off >= 4 * sizeof(kwboot_msg_debug))
break;
/* If not move valid suffix from end of the buffer to the beginning of buffer */
memmove(buf, buf + i + 1, off);
}
kwboot_printv("\n");
return rc;
rc = kwboot_msg_stop_thread(write_thread);
if (rc) {
perror("Failed to stop write thread");
return rc;
}
if (err) {
errno = err;
perror("Failed to read response for debug message pattern");
return -1;
}
/* flush output queue with remaining debug message patterns */
rc = tcflush(tty, TCOFLUSH);
if (rc) {
perror("Failed to flush output queue");
return rc;
}
kwboot_printv("Clearing input buffer...\n");
/*
* Wait until BootROM transmit all remaining echo characters.
* Experimentally it was measured that for Armada 385 BootROM
* it is required to wait at least 0.415s. So wait 0.5s.
*/
usleep(500 * 1000);
/*
* In off variable is stored number of characters received after the
* successful detection of echo reply. So these characters are console
* echo for other following debug message patterns. BootROM may have in
* its output queue other echo characters which were being transmitting
* before above sleep call. So read remaining number of echo characters
* sent by the BootROM now.
*/
while ((rc = kwboot_tty_recv(tty, &buf[0], 1, 0)) == 0)
off++;
if (errno != ETIMEDOUT) {
perror("Failed to read response");
return rc;
}
/*
* Clear every echo character set by the BootROM by backspace byte.
* This is required prior writing any command to the BootROM debug
* because BootROM command line buffer has limited size. If length
* of the command is larger than buffer size then it looks like
* that Armada 385 BootROM crashes after sending ENTER. So erase it.
* Experimentally it was measured that for Armada 385 BootROM it is
* required to send at least 3 backspace bytes for one echo character.
* This is unknown why. But lets do it.
*/
off *= 3;
memset(buf, '\x08', sizeof(buf));
while (off > sizeof(buf)) {
rc = kwboot_tty_send(tty, buf, sizeof(buf), 1);
if (rc) {
perror("Failed to send clear sequence");
return rc;
}
off -= sizeof(buf);
}
rc = kwboot_tty_send(tty, buf, off, 0);
if (rc) {
perror("Failed to send clear sequence");
return rc;
}
usleep(msg_rsp_timeo * 1000);
rc = tcflush(tty, TCIFLUSH);
if (rc) {
perror("Failed to flush input queue");
return rc;
}
return 0;
}
static size_t
@ -1181,37 +1408,84 @@ kwboot_xmodem(int tty, const void *_img, size_t size, int baudrate)
}
static int
kwboot_term_pipe(int in, int out, const char *quit, int *s)
kwboot_term_pipe(int in, int out, const char *quit, int *s, const char *kbs, int *k)
{
char buf[128];
ssize_t nin;
ssize_t nin, noff;
nin = read(in, buf, sizeof(buf));
if (nin <= 0)
return -1;
if (quit) {
noff = 0;
if (quit || kbs) {
int i;
for (i = 0; i < nin; i++) {
if (buf[i] == quit[*s]) {
if ((quit || kbs) &&
(!quit || buf[i] != quit[*s]) &&
(!kbs || buf[i] != kbs[*k])) {
const char *prefix;
int plen;
if (quit && kbs) {
prefix = (*s >= *k) ? quit : kbs;
plen = (*s >= *k) ? *s : *k;
} else if (quit) {
prefix = quit;
plen = *s;
} else {
prefix = kbs;
plen = *k;
}
if (plen > i && kwboot_write(out, prefix, plen - i) < 0)
return -1;
}
if (quit && buf[i] == quit[*s]) {
(*s)++;
if (!quit[*s]) {
nin = i - *s;
nin = (i > *s) ? (i - *s) : 0;
break;
}
} else {
if (*s > i && kwboot_write(out, quit, *s - i) < 0)
return -1;
} else if (quit) {
*s = 0;
}
if (kbs && buf[i] == kbs[*k]) {
(*k)++;
if (!kbs[*k]) {
if (i > *k + noff &&
kwboot_write(out, buf + noff, i - *k - noff) < 0)
return -1;
/*
* Replace backspace key by '\b' (0x08)
* byte which is the only recognized
* backspace byte by Marvell BootROM.
*/
if (write(out, "\x08", 1) < 0)
return -1;
noff = i + 1;
*k = 0;
}
} else if (kbs) {
*k = 0;
}
}
if (i == nin)
nin -= *s;
if (i == nin) {
i = 0;
if (quit && i < *s)
i = *s;
if (kbs && i < *k)
i = *k;
nin -= (nin > i) ? i : nin;
}
}
if (kwboot_write(out, buf, nin) < 0)
if (nin > noff && kwboot_write(out, buf + noff, nin - noff) < 0)
return -1;
return 0;
@ -1220,7 +1494,8 @@ kwboot_term_pipe(int in, int out, const char *quit, int *s)
static int
kwboot_terminal(int tty)
{
int rc, in, s;
int rc, in, s, k;
const char *kbs = NULL;
const char *quit = "\34c";
struct termios otio, tio;
@ -1239,6 +1514,33 @@ kwboot_terminal(int tty)
goto out;
}
/*
* Get sequence for backspace key used by the current
* terminal. Every occurrence of this sequence will be
* replaced by '\b' byte which is the only recognized
* backspace byte by Marvell BootROM.
*
* Note that we cannot read this sequence from termios
* c_cc[VERASE] as VERASE is valid only when ICANON is
* set in termios c_lflag, which is not case for us.
*
* Also most terminals do not set termios c_cc[VERASE]
* as c_cc[VERASE] can specify only one-byte sequence
* and instead let applications to read (possible
* multi-byte) sequence for backspace key from "kbs"
* terminfo database based on $TERM env variable.
*
* So read "kbs" from terminfo database via tigetstr()
* call after successful setupterm(). Most terminals
* use byte 0x7F for backspace key, so replacement with
* '\b' is required.
*/
if (setupterm(NULL, STDOUT_FILENO, &rc) == 0) {
kbs = tigetstr("kbs");
if (kbs == (char *)-1)
kbs = NULL;
}
kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
quit[0] | 0100, quit[1]);
} else
@ -1246,6 +1548,7 @@ kwboot_terminal(int tty)
rc = 0;
s = 0;
k = 0;
do {
fd_set rfds;
@ -1265,13 +1568,13 @@ kwboot_terminal(int tty)
break;
if (FD_ISSET(tty, &rfds)) {
rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL, NULL, NULL);
if (rc)
break;
}
if (in >= 0 && FD_ISSET(in, &rfds)) {
rc = kwboot_term_pipe(in, tty, quit, &s);
rc = kwboot_term_pipe(in, tty, quit, &s, kbs, &k);
if (rc)
break;
}
@ -1708,16 +2011,16 @@ static void
kwboot_usage(FILE *stream, char *progname)
{
fprintf(stream,
"Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n",
"Usage: %s [OPTIONS] [-b <image> | -D <image> | -b | -d ] [-B <baud> ] [-t] <TTY>\n",
progname);
fprintf(stream, "\n");
fprintf(stream,
" -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n");
" -b <image>: boot <image> with preamble (Kirkwood, Avanta, Armada 370/XP/375/38x/39x)\n");
fprintf(stream,
" -D <image>: boot <image> without preamble (Dove)\n");
fprintf(stream, " -d: enter debug mode\n");
fprintf(stream, " -b: enter xmodem boot mode\n");
fprintf(stream, " -d: enter console debug mode\n");
fprintf(stream, " -a: use timings for Armada XP\n");
fprintf(stream, " -q <req-delay>: use specific request-delay\n");
fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n");
fprintf(stream,
" -o <block-timeo>: use specific xmodem block timeout\n");
@ -1733,8 +2036,8 @@ main(int argc, char **argv)
{
const char *ttypath, *imgpath;
int rv, rc, tty, term;
void *bootmsg;
void *debugmsg;
int bootmsg;
int debugmsg;
void *img;
size_t size;
size_t after_img_rsv;
@ -1744,8 +2047,8 @@ main(int argc, char **argv)
rv = 1;
tty = -1;
bootmsg = NULL;
debugmsg = NULL;
bootmsg = 0;
debugmsg = 0;
imgpath = NULL;
img = NULL;
term = 0;
@ -1767,7 +2070,7 @@ main(int argc, char **argv)
case 'b':
if (imgpath || bootmsg || debugmsg)
goto usage;
bootmsg = kwboot_msg_boot;
bootmsg = 1;
if (prev_optind == optind)
goto usage;
if (optind < argc - 1 && argv[optind] && argv[optind][0] != '-')
@ -1777,14 +2080,14 @@ main(int argc, char **argv)
case 'D':
if (imgpath || bootmsg || debugmsg)
goto usage;
bootmsg = NULL;
bootmsg = 0;
imgpath = optarg;
break;
case 'd':
if (imgpath || bootmsg || debugmsg)
goto usage;
debugmsg = kwboot_msg_debug;
debugmsg = 1;
break;
case 'p':
@ -1796,12 +2099,11 @@ main(int argc, char **argv)
break;
case 'a':
msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP;
msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
break;
case 'q':
msg_req_delay = atoi(optarg);
/* nop, for backward compatibility */
break;
case 's':
@ -1866,17 +2168,13 @@ main(int argc, char **argv)
}
if (debugmsg) {
rc = kwboot_debugmsg(tty, debugmsg);
if (rc) {
perror("debugmsg");
rc = kwboot_debugmsg(tty);
if (rc)
goto out;
}
} else if (bootmsg) {
rc = kwboot_bootmsg(tty, bootmsg);
if (rc) {
perror("bootmsg");
rc = kwboot_bootmsg(tty);
if (rc)
goto out;
}
}
if (img) {