aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-06-08 08:51:59 -0400
committerTom Rini <trini@konsulko.com>2020-06-08 08:51:59 -0400
commit08649c3d50b1ca014a9e0bf87c724f99f88a20ce (patch)
tree55a144349e0fcd807f94f44594623c8c890ae6cb /drivers
parent325b83e04072e22c19d6395f69264bf6bf800146 (diff)
parent164a5af43635c63ea2459d4c87a1a4b16acd689a (diff)
downloadu-boot-08649c3d50b1ca014a9e0bf87c724f99f88a20ce.zip
u-boot-08649c3d50b1ca014a9e0bf87c724f99f88a20ce.tar.gz
u-boot-08649c3d50b1ca014a9e0bf87c724f99f88a20ce.tar.bz2
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-mpc85xx
- DM_ETH support for P2041RDB, T1024RDB, P5040DS, P3041DS, P4080DS, bug fixes - Add TBI PHY access through MII - DDR: Rework errata workaround for A008109, A008378, 009942
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ddr/fsl/Kconfig3
-rw-r--r--drivers/ddr/fsl/ctrl_regs.c58
-rw-r--r--drivers/ddr/fsl/fsl_ddr_gen4.c54
-rw-r--r--drivers/ddr/fsl/mpc85xx_ddr_gen3.c54
-rw-r--r--drivers/net/Kconfig1
-rw-r--r--drivers/net/fsl_mdio.c140
-rw-r--r--drivers/net/tsec.c32
-rw-r--r--drivers/qe/Kconfig2
8 files changed, 273 insertions, 71 deletions
diff --git a/drivers/ddr/fsl/Kconfig b/drivers/ddr/fsl/Kconfig
index 1b73df8..f75d97b 100644
--- a/drivers/ddr/fsl/Kconfig
+++ b/drivers/ddr/fsl/Kconfig
@@ -151,6 +151,9 @@ endmenu
config SYS_FSL_ERRATUM_A008378
bool
+config SYS_FSL_ERRATUM_A008109
+ bool
+
config SYS_FSL_ERRATUM_A008511
bool
diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c
index 2d3fb19..c849ef3 100644
--- a/drivers/ddr/fsl/ctrl_regs.c
+++ b/drivers/ddr/fsl/ctrl_regs.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2008-2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018 NXP Semiconductor
+ * Copyright 2017-2020 NXP Semiconductor
*/
/*
@@ -2363,38 +2363,6 @@ compute_fsl_memctl_config_regs(const unsigned int ctrl_num,
unsigned int ip_rev = 0;
unsigned int unq_mrs_en = 0;
int cs_en = 1;
-#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
- unsigned int ddr_freq;
-#endif
-#if (defined(CONFIG_SYS_FSL_ERRATUM_A008378) && \
- defined(CONFIG_SYS_FSL_DDRC_GEN4)) || \
- defined(CONFIG_SYS_FSL_ERRATUM_A009942)
- struct ccsr_ddr __iomem *ddrc;
-
- switch (ctrl_num) {
- case 0:
- ddrc = (void *)CONFIG_SYS_FSL_DDR_ADDR;
- break;
-#if defined(CONFIG_SYS_FSL_DDR2_ADDR) && (CONFIG_SYS_NUM_DDR_CTLRS > 1)
- case 1:
- ddrc = (void *)CONFIG_SYS_FSL_DDR2_ADDR;
- break;
-#endif
-#if defined(CONFIG_SYS_FSL_DDR3_ADDR) && (CONFIG_SYS_NUM_DDR_CTLRS > 2)
- case 2:
- ddrc = (void *)CONFIG_SYS_FSL_DDR3_ADDR;
- break;
-#endif
-#if defined(CONFIG_SYS_FSL_DDR4_ADDR) && (CONFIG_SYS_NUM_DDR_CTLRS > 3)
- case 3:
- ddrc = (void *)CONFIG_SYS_FSL_DDR4_ADDR;
- break;
-#endif
- default:
- printf("%s unexpected ctrl_num = %u\n", __func__, ctrl_num);
- return 1;
- }
-#endif
memset(ddr, 0, sizeof(fsl_ddr_cfg_regs_t));
@@ -2615,31 +2583,7 @@ compute_fsl_memctl_config_regs(const unsigned int ctrl_num,
ddr->debug[2] |= 0x00000200; /* set bit 22 */
#endif
-#if defined(CONFIG_SYS_FSL_ERRATUM_A008378) && defined(CONFIG_SYS_FSL_DDRC_GEN4)
- /* Erratum applies when accumulated ECC is used, or DBI is enabled */
-#define IS_ACC_ECC_EN(v) ((v) & 0x4)
-#define IS_DBI(v) ((((v) >> 12) & 0x3) == 0x2)
- if (has_erratum_a008378()) {
- if (IS_ACC_ECC_EN(ddr->ddr_sdram_cfg) ||
- IS_DBI(ddr->ddr_sdram_cfg_3)) {
- ddr->debug[28] = ddr_in32(&ddrc->debug[28]);
- ddr->debug[28] |= (0x9 << 20);
- }
- }
-#endif
-
#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
- ddr_freq = get_ddr_freq(ctrl_num) / 1000000;
- ddr->debug[28] |= ddr_in32(&ddrc->debug[28]);
- ddr->debug[28] &= 0xff0fff00;
- if (ddr_freq <= 1333)
- ddr->debug[28] |= 0x0080006a;
- else if (ddr_freq <= 1600)
- ddr->debug[28] |= 0x0070006f;
- else if (ddr_freq <= 1867)
- ddr->debug[28] |= 0x00700076;
- else if (ddr_freq <= 2133)
- ddr->debug[28] |= 0x0060007b;
if (popts->cpo_sample)
ddr->debug[28] = (ddr->debug[28] & 0xffffff00) |
popts->cpo_sample;
diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c
index eab5b82..e43c680 100644
--- a/drivers/ddr/fsl/fsl_ddr_gen4.c
+++ b/drivers/ddr/fsl/fsl_ddr_gen4.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright 2014-2015 Freescale Semiconductor, Inc.
+ * Copyright 2014-2020 Freescale Semiconductor, Inc.
*/
#include <common.h>
@@ -73,6 +73,15 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs,
#ifdef CONFIG_FSL_DDR_BIST
char buffer[CONFIG_SYS_CBSIZE];
#endif
+#if defined(CONFIG_SYS_FSL_ERRATUM_A009942) || \
+ (defined(CONFIG_SYS_FSL_ERRATUM_A008378) && \
+ defined(CONFIG_SYS_FSL_DDRC_GEN4)) || \
+ defined(CONFIG_SYS_FSL_ERRATUM_A008109)
+ u32 val32;
+#endif
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
+ unsigned int ddr_freq;
+#endif
switch (ctrl_num) {
case 0:
ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR;
@@ -437,6 +446,49 @@ step2:
ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2);
#endif
+#if defined(CONFIG_SYS_FSL_ERRATUM_A008378) && defined(CONFIG_SYS_FSL_DDRC_GEN4)
+ /* Erratum applies when accumulated ECC is used, or DBI is enabled */
+#define IS_ACC_ECC_EN(v) ((v) & 0x4)
+#define IS_DBI(v) ((((v) >> 12) & 0x3) == 0x2)
+ if (has_erratum_a008378()) {
+ if (IS_ACC_ECC_EN(regs->ddr_sdram_cfg) ||
+ IS_DBI(regs->ddr_sdram_cfg_3)) {
+ val32 = ddr_in32(&ddr->debug[28]);
+ val32 |= (0x9 << 20);
+ ddr_out32(&ddr->debug[28], val32);
+ }
+ debug("Applied errata CONFIG_SYS_FSL_ERRATUM_A008378\n");
+ }
+#endif
+
+#if defined(CONFIG_SYS_FSL_ERRATUM_A008109)
+ val32 = ddr_in32(&ddr->sdram_cfg_2) | 0x800; /* DDR_SLOW */
+ ddr_out32(&ddr->sdram_cfg_2, val32);
+
+ val32 = ddr_in32(&ddr->debug[18]) | 0x2;
+ ddr_out32(&ddr->debug[18], val32);
+
+ ddr_out32(&ddr->debug[28], 0x30000000);
+ debug("Applied errta CONFIG_SYS_FSL_ERRATUM_A008109\n");
+#endif
+
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
+ ddr_freq = get_ddr_freq(ctrl_num) / 1000000;
+ val32 = ddr_in32(&ddr->debug[28]);
+ val32 &= 0xff0fff00;
+ if (ddr_freq <= 1333)
+ val32 |= 0x0080006a;
+ else if (ddr_freq <= 1600)
+ val32 |= 0x0070006f;
+ else if (ddr_freq <= 1867)
+ val32 |= 0x00700076;
+ else if (ddr_freq <= 2133)
+ val32 |= 0x0060007b;
+
+ ddr_out32(&ddr->debug[28], val32);
+ debug("Applied errata CONFIG_SYS_FSL_ERRATUM_A009942\n");
+#endif
+
total_gb_size_per_controller = 0;
for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
if (!(regs->cs[i].config & 0x80000000))
diff --git a/drivers/ddr/fsl/mpc85xx_ddr_gen3.c b/drivers/ddr/fsl/mpc85xx_ddr_gen3.c
index ab8d2de..1ed4d50 100644
--- a/drivers/ddr/fsl/mpc85xx_ddr_gen3.c
+++ b/drivers/ddr/fsl/mpc85xx_ddr_gen3.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ * Copyright 2008-2020 Freescale Semiconductor, Inc.
*/
#include <common.h>
@@ -40,6 +40,15 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs,
#ifdef CONFIG_SYS_FSL_ERRATUM_DDR_A003
u32 save1, save2;
#endif
+#if defined(CONFIG_SYS_FSL_ERRATUM_A009942) || \
+ (defined(CONFIG_SYS_FSL_ERRATUM_A008378) && \
+ defined(CONFIG_SYS_FSL_DDRC_GEN4)) || \
+ defined(CONFIG_SYS_FSL_ERRATUM_A008109)
+ u32 val32;
+#endif
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
+ unsigned int ddr_freq;
+#endif
switch (ctrl_num) {
case 0:
@@ -338,6 +347,49 @@ step2:
}
#endif
+
+#if defined(CONFIG_SYS_FSL_ERRATUM_A008378) && defined(CONFIG_SYS_FSL_DDRC_GEN4)
+ /* Erratum applies when accumulated ECC is used, or DBI is enabled */
+#define IS_ACC_ECC_EN(v) ((v) & 0x4)
+#define IS_DBI(v) ((((v) >> 12) & 0x3) == 0x2)
+ if (has_erratum_a008378()) {
+ if (IS_ACC_ECC_EN(regs->ddr_sdram_cfg) ||
+ IS_DBI(regs->ddr_sdram_cfg_3)) {
+ val32 = ddr_in32(&ddr->debug[28]);
+ val32 |= (0x9 << 20);
+ ddr_out32(&ddr->debug[28], val32);
+ }
+ debug("Applied errata CONFIG_SYS_FSL_ERRATUM_A008378\n");
+ }
+#endif
+
+#if defined(CONFIG_SYS_FSL_ERRATUM_A008109)
+ val32 = in_be32(&ddr->sdram_cfg_2) | 0x800; /* DDR_SLOW */
+ out_be32(&ddr->sdram_cfg_2, val32);
+
+ val32 = in_be32(&ddr->debug[18]) | 0x2;
+ out_be32(&ddr->debug[18], val32);
+
+ out_be32(&ddr->debug[28], 0x30000000);
+ debug("Applied errta CONFIG_SYS_FSL_ERRATUM_A008109\n");
+#endif
+
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
+ ddr_freq = get_ddr_freq(ctrl_num) / 1000000;
+ val32 = in_be32(&ddr->debug[28]);
+ val32 &= 0xff0fff00;
+ if (ddr_freq <= 1333)
+ val32 |= 0x0080006a;
+ else if (ddr_freq <= 1600)
+ val32 |= 0x0070006f;
+ else if (ddr_freq <= 1867)
+ val32 |= 0x00700076;
+ else if (ddr_freq <= 2133)
+ val32 |= 0x0060007b;
+
+ out_be32(&ddr->debug[28], val32);
+ debug("Applied errata CONFIG_SYS_FSL_ERRATUM_A009942\n");
+#endif
/*
* For 8572 DDR1 erratum - DDR controller may enter illegal state
* when operatiing in 32-bit bus mode with 4-beat bursts,
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 0b08de0..bb23f73 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -162,6 +162,7 @@ config DWC_ETH_QOS
config E1000
bool "Intel PRO/1000 Gigabit Ethernet support"
+ depends on (DM_ETH && DM_PCI) || !DM_ETH
help
This driver supports Intel(R) PRO/1000 gigabit ethernet family of
adapters. For more information on how to identify your adapter, go
diff --git a/drivers/net/fsl_mdio.c b/drivers/net/fsl_mdio.c
index 894b52e..43040d4 100644
--- a/drivers/net/fsl_mdio.c
+++ b/drivers/net/fsl_mdio.c
@@ -12,6 +12,12 @@
#include <asm/io.h>
#include <linux/errno.h>
+#ifdef CONFIG_DM_MDIO
+struct tsec_mdio_priv {
+ struct tsec_mii_mng __iomem *regs;
+};
+#endif
+
void tsec_local_mdio_write(struct tsec_mii_mng __iomem *phyregs, int port_addr,
int dev_addr, int regnum, int value)
{
@@ -56,10 +62,21 @@ int tsec_local_mdio_read(struct tsec_mii_mng __iomem *phyregs, int port_addr,
return value;
}
+#if defined(CONFIG_PHYLIB)
static int fsl_pq_mdio_reset(struct mii_dev *bus)
{
- struct tsec_mii_mng __iomem *regs =
- (struct tsec_mii_mng __iomem *)bus->priv;
+ struct tsec_mii_mng __iomem *regs;
+#ifndef CONFIG_DM_MDIO
+ regs = (struct tsec_mii_mng __iomem *)bus->priv;
+#else
+ struct tsec_mdio_priv *priv;
+
+ if (!bus->priv)
+ return -EINVAL;
+
+ priv = dev_get_priv(bus->priv);
+ regs = priv->regs;
+#endif
/* Reset MII (due to new addresses) */
out_be32(&regs->miimcfg, MIIMCFG_RESET_MGMT);
@@ -71,11 +88,22 @@ static int fsl_pq_mdio_reset(struct mii_dev *bus)
return 0;
}
+#endif
int tsec_phy_read(struct mii_dev *bus, int addr, int dev_addr, int regnum)
{
- struct tsec_mii_mng __iomem *phyregs =
- (struct tsec_mii_mng __iomem *)bus->priv;
+ struct tsec_mii_mng __iomem *phyregs;
+#ifndef CONFIG_DM_MDIO
+ phyregs = (struct tsec_mii_mng __iomem *)bus->priv;
+#else
+ struct tsec_mdio_priv *priv;
+
+ if (!bus->priv)
+ return -EINVAL;
+
+ priv = dev_get_priv(bus->priv);
+ phyregs = priv->regs;
+#endif
return tsec_local_mdio_read(phyregs, addr, dev_addr, regnum);
}
@@ -83,14 +111,25 @@ int tsec_phy_read(struct mii_dev *bus, int addr, int dev_addr, int regnum)
int tsec_phy_write(struct mii_dev *bus, int addr, int dev_addr, int regnum,
u16 value)
{
- struct tsec_mii_mng __iomem *phyregs =
- (struct tsec_mii_mng __iomem *)bus->priv;
+ struct tsec_mii_mng __iomem *phyregs;
+#ifndef CONFIG_DM_MDIO
+ phyregs = (struct tsec_mii_mng __iomem *)bus->priv;
+#else
+ struct tsec_mdio_priv *priv;
+
+ if (!bus->priv)
+ return -EINVAL;
+
+ priv = dev_get_priv(bus->priv);
+ phyregs = priv->regs;
+#endif
tsec_local_mdio_write(phyregs, addr, dev_addr, regnum, value);
return 0;
}
+#ifndef CONFIG_DM_MDIO
int fsl_pq_mdio_init(bd_t *bis, struct fsl_pq_mdio_info *info)
{
struct mii_dev *bus = mdio_alloc();
@@ -109,3 +148,92 @@ int fsl_pq_mdio_init(bd_t *bis, struct fsl_pq_mdio_info *info)
return mdio_register(bus);
}
+#else /* CONFIG_DM_MDIO */
+#if defined(CONFIG_PHYLIB)
+static int tsec_mdio_read(struct udevice *dev, int addr, int devad, int reg)
+{
+ struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
+ NULL;
+
+ if (pdata && pdata->mii_bus)
+ return tsec_phy_read(pdata->mii_bus, addr, devad, reg);
+
+ return -1;
+}
+
+static int tsec_mdio_write(struct udevice *dev, int addr, int devad, int reg,
+ u16 val)
+{
+ struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
+ NULL;
+
+ if (pdata && pdata->mii_bus)
+ return tsec_phy_write(pdata->mii_bus, addr, devad, reg, val);
+
+ return -1;
+}
+
+static int tsec_mdio_reset(struct udevice *dev)
+{
+ struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
+ NULL;
+
+ if (pdata && pdata->mii_bus)
+ return fsl_pq_mdio_reset(pdata->mii_bus);
+
+ return -1;
+}
+
+static const struct mdio_ops tsec_mdio_ops = {
+ .read = tsec_mdio_read,
+ .write = tsec_mdio_write,
+ .reset = tsec_mdio_reset,
+};
+
+static const struct udevice_id tsec_mdio_ids[] = {
+ { .compatible = "fsl,gianfar-tbi" },
+ { .compatible = "fsl,gianfar-mdio" },
+ { .compatible = "fsl,etsec2-tbi" },
+ { .compatible = "fsl,etsec2-mdio" },
+ { .compatible = "fsl,fman-mdio" },
+ {}
+};
+
+static int tsec_mdio_probe(struct udevice *dev)
+{
+ struct tsec_mdio_priv *priv = (dev) ? dev_get_priv(dev) : NULL;
+ struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
+ NULL;
+
+ if (!dev) {
+ printf("%s dev = NULL\n", __func__);
+ return -1;
+ }
+ if (!priv) {
+ printf("dev_get_priv(dev %p) = NULL\n", dev);
+ return -1;
+ }
+ priv->regs = (void *)(uintptr_t)dev_read_addr(dev);
+ debug("%s priv %p @ regs %p, pdata %p\n", __func__,
+ priv, priv->regs, pdata);
+
+ return 0;
+}
+
+static int tsec_mdio_remove(struct udevice *dev)
+{
+ return 0;
+}
+
+U_BOOT_DRIVER(tsec_mdio) = {
+ .name = "tsec_mdio",
+ .id = UCLASS_MDIO,
+ .of_match = tsec_mdio_ids,
+ .probe = tsec_mdio_probe,
+ .remove = tsec_mdio_remove,
+ .ops = &tsec_mdio_ops,
+ .priv_auto_alloc_size = sizeof(struct tsec_mdio_priv),
+ .platdata_auto_alloc_size = sizeof(struct mdio_perdev_priv),
+};
+#endif /* CONFIG_PHYLIB */
+#endif /* CONFIG_DM_MDIO */
diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c
index c2e755f..3d75acb 100644
--- a/drivers/net/tsec.c
+++ b/drivers/net/tsec.c
@@ -791,6 +791,7 @@ int tsec_probe(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct tsec_private *priv = dev_get_priv(dev);
+ struct tsec_mii_mng __iomem *ext_phyregs_mii;
struct ofnode_phandle_args phandle_args;
u32 tbiaddr = CONFIG_SYS_TBIPA_VALUE;
struct fsl_pq_mdio_info mdio_info;
@@ -800,7 +801,7 @@ int tsec_probe(struct udevice *dev)
int ret;
pdata->iobase = (phys_addr_t)dev_read_addr(dev);
- priv->regs = (struct tsec *)pdata->iobase;
+ priv->regs = dev_remap_addr(dev);
if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
&phandle_args)) {
@@ -819,14 +820,35 @@ int tsec_probe(struct udevice *dev)
}
reg = ofnode_get_addr_index(parent, 0);
- priv->phyregs_sgmii = (struct tsec_mii_mng *)
- (reg + TSEC_MDIO_REGS_OFFSET);
+ if (reg == FDT_ADDR_T_NONE) {
+ printf("No 'reg' property of MII for external PHY\n");
+ return -ENOENT;
+ }
+
+ ext_phyregs_mii = map_physmem(reg + TSEC_MDIO_REGS_OFFSET, 0,
+ MAP_NOCACHE);
ret = dev_read_phandle_with_args(dev, "tbi-handle", NULL, 0, 0,
&phandle_args);
- if (ret == 0)
+ if (ret == 0) {
ofnode_read_u32(phandle_args.node, "reg", &tbiaddr);
+ parent = ofnode_get_parent(phandle_args.node);
+ if (!ofnode_valid(parent)) {
+ printf("No parent node for TBI PHY?\n");
+ return -ENOENT;
+ }
+
+ reg = ofnode_get_addr_index(parent, 0);
+ if (reg == FDT_ADDR_T_NONE) {
+ printf("No 'reg' property of MII for TBI PHY\n");
+ return -ENOENT;
+ }
+
+ priv->phyregs_sgmii = map_physmem(reg + TSEC_MDIO_REGS_OFFSET,
+ 0, MAP_NOCACHE);
+ }
+
priv->tbiaddr = tbiaddr;
phy_mode = dev_read_prop(dev, "phy-connection-type", NULL);
@@ -843,7 +865,7 @@ int tsec_probe(struct udevice *dev)
if (priv->interface == PHY_INTERFACE_MODE_SGMII)
priv->flags |= TSEC_SGMII;
- mdio_info.regs = priv->phyregs_sgmii;
+ mdio_info.regs = ext_phyregs_mii;
mdio_info.name = (char *)dev->name;
ret = fsl_pq_mdio_init(NULL, &mdio_info);
if (ret)
diff --git a/drivers/qe/Kconfig b/drivers/qe/Kconfig
index 864b36b..44c9f01 100644
--- a/drivers/qe/Kconfig
+++ b/drivers/qe/Kconfig
@@ -3,7 +3,7 @@
#
config QE
bool "Enable support for QUICC Engine"
- depends on PPC
+ depends on PPC && !DM_ETH
default y if ARCH_T1040 || ARCH_T1042 || ARCH_T1024 || ARCH_P1021 \
|| ARCH_P1025
help