diff options
Diffstat (limited to 'arch/arm/mach-layerscape/errata.c')
-rw-r--r-- | arch/arm/mach-layerscape/errata.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/arch/arm/mach-layerscape/errata.c b/arch/arm/mach-layerscape/errata.c new file mode 100644 index 0000000000..4f4b759ddb --- /dev/null +++ b/arch/arm/mach-layerscape/errata.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <common.h> +#include <io.h> +#include <soc/fsl/immap_lsch2.h> +#include <soc/fsl/fsl_ddr_sdram.h> +#include <asm/system.h> +#include <mach/errata.h> +#include <mach/lowlevel.h> + +#define scfg_clrsetbits32(addr, clear, set) clrsetbits_be32(addr, clear, set) +#define scfg_clrbits32(addr, clear) clrbits_be32(addr, clear) + +static inline void set_usb_pcstxswingfull(u32 __iomem *scfg, u32 offset) +{ + scfg_clrsetbits32(scfg + offset / 4, + 0x7f << 9, + SCFG_USB_PCSTXSWINGFULL << 9); +} + +static void erratum_a008997_ls1046a(void) +{ + u32 __iomem *scfg = (u32 __iomem *)LSCH2_SCFG_ADDR; + + set_usb_pcstxswingfull(scfg, SCFG_USB3PRM2CR_USB1); + set_usb_pcstxswingfull(scfg, SCFG_USB3PRM2CR_USB2); + set_usb_pcstxswingfull(scfg, SCFG_USB3PRM2CR_USB3); +} + +#define PROGRAM_USB_PHY_RX_OVRD_IN_HI(phy) \ + out_be16((phy) + SCFG_USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_1); \ + out_be16((phy) + SCFG_USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_2); \ + out_be16((phy) + SCFG_USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_3); \ + out_be16((phy) + SCFG_USB_PHY_RX_OVRD_IN_HI, USB_PHY_RX_EQ_VAL_4) + +static void erratum_a009007_ls1046a(void) +{ + void __iomem *usb_phy = IOMEM(SCFG_USB_PHY1); + + PROGRAM_USB_PHY_RX_OVRD_IN_HI(usb_phy); + usb_phy = (void __iomem *)SCFG_USB_PHY2; + PROGRAM_USB_PHY_RX_OVRD_IN_HI(usb_phy); + + usb_phy = (void __iomem *)SCFG_USB_PHY3; + PROGRAM_USB_PHY_RX_OVRD_IN_HI(usb_phy); +} + +static inline void set_usb_txvreftune(u32 __iomem *scfg, u32 offset) +{ + scfg_clrsetbits32(scfg + offset / 4, 0xf << 6, SCFG_USB_TXVREFTUNE << 6); +} + +static void erratum_a009008_ls1046a(void) +{ + u32 __iomem *scfg = IOMEM(LSCH2_SCFG_ADDR); + + set_usb_txvreftune(scfg, SCFG_USB3PRM1CR_USB1); + set_usb_txvreftune(scfg, SCFG_USB3PRM1CR_USB2); + set_usb_txvreftune(scfg, SCFG_USB3PRM1CR_USB3); +} + +static inline void set_usb_sqrxtune(u32 __iomem *scfg, u32 offset) +{ + scfg_clrbits32(scfg + offset / 4, SCFG_USB_SQRXTUNE_MASK << 23); +} + +static void erratum_a009798_ls1046a(void) +{ + u32 __iomem *scfg = IOMEM(LSCH2_SCFG_ADDR); + + set_usb_sqrxtune(scfg, SCFG_USB3PRM1CR_USB1); + set_usb_sqrxtune(scfg, SCFG_USB3PRM1CR_USB2); + set_usb_sqrxtune(scfg, SCFG_USB3PRM1CR_USB3); +} + +static void erratum_a008850_early(void) +{ + /* part 1 of 2 */ + struct ccsr_cci400 __iomem *cci = IOMEM(LSCH2_CCI400_ADDR); + struct ccsr_ddr __iomem *ddr = IOMEM(LSCH2_DDR_ADDR); + + /* Skip if running at lower exception level */ + if (current_el() < 3) + return; + + /* disables propagation of barrier transactions to DDRC from CCI400 */ + out_le32(&cci->ctrl_ord, CCI400_CTRLORD_TERM_BARRIER); + + /* disable the re-ordering in DDRC */ + ddr_out32(&ddr->eor, DDR_EOR_RD_REOD_DIS | DDR_EOR_WD_REOD_DIS); +} + +/* erratum_a009942_check_cpo */ + +void ls1046a_errata(void) +{ + erratum_a008850_early(); + erratum_a009008_ls1046a(); + erratum_a009798_ls1046a(); + erratum_a008997_ls1046a(); + erratum_a009007_ls1046a(); +} + +static void erratum_a008850_post(void) +{ + /* part 2 of 2 */ + struct ccsr_cci400 __iomem *cci = IOMEM(LSCH2_CCI400_ADDR); + struct ccsr_ddr __iomem *ddr = IOMEM(LSCH2_DDR_ADDR); + u32 tmp; + + /* Skip if running at lower exception level */ + if (current_el() < 3) + return; + + /* enable propagation of barrier transactions to DDRC from CCI400 */ + out_le32(&cci->ctrl_ord, CCI400_CTRLORD_EN_BARRIER); + + /* enable the re-ordering in DDRC */ + tmp = ddr_in32(&ddr->eor); + tmp &= ~(DDR_EOR_RD_REOD_DIS | DDR_EOR_WD_REOD_DIS); + ddr_out32(&ddr->eor, tmp); +} + +/* + * This additional workaround of A009942 checks the condition to determine if + * the CPO value set by the existing A009942 workaround needs to be updated. + * If need, print a warning to prompt user reconfigure DDR debug_29[24:31] with + * expected optimal value, the optimal value is highly board dependent. + */ +static void erratum_a009942_check_cpo(void) +{ + struct ccsr_ddr __iomem *ddr = + (struct ccsr_ddr __iomem *)(LSCH2_DDR_ADDR); + u32 cpo, cpo_e, cpo_o, cpo_target, cpo_optimal; + u32 cpo_min = ddr_in32(&ddr->debug[9]) >> 24; + u32 cpo_max = cpo_min; + u32 sdram_cfg, i, tmp, lanes, ddr_type; + bool update_cpo = false, has_ecc = false; + + sdram_cfg = ddr_in32(&ddr->sdram_cfg); + if (sdram_cfg & SDRAM_CFG_32_BE) + lanes = 4; + else if (sdram_cfg & SDRAM_CFG_16_BE) + lanes = 2; + else + lanes = 8; + + if (sdram_cfg & SDRAM_CFG_ECC_EN) + has_ecc = true; + + /* determine the maximum and minimum CPO values */ + for (i = 9; i < 9 + lanes / 2; i++) { + cpo = ddr_in32(&ddr->debug[i]); + cpo_e = cpo >> 24; + cpo_o = (cpo >> 8) & 0xff; + tmp = min(cpo_e, cpo_o); + if (tmp < cpo_min) + cpo_min = tmp; + tmp = max(cpo_e, cpo_o); + if (tmp > cpo_max) + cpo_max = tmp; + } + + if (has_ecc) { + cpo = ddr_in32(&ddr->debug[13]); + cpo = cpo >> 24; + if (cpo < cpo_min) + cpo_min = cpo; + if (cpo > cpo_max) + cpo_max = cpo; + } + + cpo_target = ddr_in32(&ddr->debug[28]) & 0xff; + cpo_optimal = ((cpo_max + cpo_min) >> 1) + 0x27; + debug("cpo_optimal = 0x%x, cpo_target = 0x%x\n", cpo_optimal, + cpo_target); + debug("cpo_max = 0x%x, cpo_min = 0x%x\n", cpo_max, cpo_min); + + ddr_type = (sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >> + SDRAM_CFG_SDRAM_TYPE_SHIFT; + if (ddr_type == SDRAM_TYPE_DDR4) + update_cpo = (cpo_min + 0x3b) < cpo_target ? true : false; + else if (ddr_type == SDRAM_TYPE_DDR3) + update_cpo = (cpo_min + 0x3f) < cpo_target ? true : false; + + if (update_cpo) { + printf("WARN: pls set popts->cpo_sample = 0x%x ", cpo_optimal); + printf("in <board>/ddr.c to optimize cpo\n"); + } +} + +void ls1046a_errata_post_ddr(void) +{ + erratum_a008850_post(); + erratum_a009942_check_cpo(); +} |