- 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:
commit
55b5c426ae
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
105
doc/kwboot.1
105
doc/kwboot.1
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
468
tools/kwboot.c
468
tools/kwboot.c
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user