diff options
Diffstat (limited to 'arch/arm/cpu/armv7/keystone')
-rw-r--r-- | arch/arm/cpu/armv7/keystone/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/clock-k2l.c | 138 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/clock.c | 17 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/cmd_clock.c | 24 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/cmd_ddr3.c | 248 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/ddr3.c | 244 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/init.c | 63 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/keystone_nav.c | 376 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/msmc.c | 26 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/keystone/spl.c | 53 |
11 files changed, 738 insertions, 459 deletions
diff --git a/arch/arm/cpu/armv7/keystone/Kconfig b/arch/arm/cpu/armv7/keystone/Kconfig index 8249b5e270..393885f710 100644 --- a/arch/arm/cpu/armv7/keystone/Kconfig +++ b/arch/arm/cpu/armv7/keystone/Kconfig @@ -9,6 +9,9 @@ config TARGET_K2HK_EVM config TARGET_K2E_EVM bool "TI Keystone 2 Edison EVM" +config TARGET_K2L_EVM + bool "TI Keystone 2 Lamar EVM" + endchoice config SYS_CPU diff --git a/arch/arm/cpu/armv7/keystone/Makefile b/arch/arm/cpu/armv7/keystone/Makefile index f8519c0403..ed030db2c8 100644 --- a/arch/arm/cpu/armv7/keystone/Makefile +++ b/arch/arm/cpu/armv7/keystone/Makefile @@ -10,10 +10,9 @@ obj-y += psc.o obj-y += clock.o obj-$(CONFIG_SOC_K2HK) += clock-k2hk.o obj-$(CONFIG_SOC_K2E) += clock-k2e.o +obj-$(CONFIG_SOC_K2L) += clock-k2l.o obj-y += cmd_clock.o obj-y += cmd_mon.o -obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_nav.o obj-y += msmc.o -obj-$(CONFIG_SPL_BUILD) += spl.o -obj-y += ddr3.o +obj-y += ddr3.o cmd_ddr3.o obj-y += keystone.o diff --git a/arch/arm/cpu/armv7/keystone/clock-k2l.c b/arch/arm/cpu/armv7/keystone/clock-k2l.c new file mode 100644 index 0000000000..1c5e4d54d8 --- /dev/null +++ b/arch/arm/cpu/armv7/keystone/clock-k2l.c @@ -0,0 +1,138 @@ +/* + * Keystone2: get clk rate for K2L + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/clock_defs.h> + +const struct keystone_pll_regs keystone_pll_regs[] = { + [CORE_PLL] = {KS2_MAINPLLCTL0, KS2_MAINPLLCTL1}, + [PASS_PLL] = {KS2_PASSPLLCTL0, KS2_PASSPLLCTL1}, + [TETRIS_PLL] = {KS2_ARMPLLCTL0, KS2_ARMPLLCTL1}, + [DDR3_PLL] = {KS2_DDR3APLLCTL0, KS2_DDR3APLLCTL1}, +}; + +int dev_speeds[] = { + SPD800, + SPD1000, + SPD1200, + SPD800, + SPD800, + SPD800, + SPD800, + SPD800, + SPD1200, + SPD1000, + SPD800, + SPD800, + SPD800, +}; + +int arm_speeds[] = { + SPD800, + SPD1000, + SPD1200, + SPD1350, + SPD1400, + SPD800, + SPD1400, + SPD1350, + SPD1200, + SPD1000, + SPD800, + SPD800, + SPD800, +}; + +/** + * pll_freq_get - get pll frequency + * Fout = Fref * NF(mult) / NR(prediv) / OD + * @pll: pll identifier + */ +static unsigned long pll_freq_get(int pll) +{ + unsigned long mult = 1, prediv = 1, output_div = 2; + unsigned long ret; + u32 tmp, reg; + + if (pll == CORE_PLL) { + ret = external_clk[sys_clk]; + if (pllctl_reg_read(pll, ctl) & PLLCTL_PLLEN) { + /* PLL mode */ + tmp = __raw_readl(KS2_MAINPLLCTL0); + prediv = (tmp & PLL_DIV_MASK) + 1; + mult = (((tmp & PLLM_MULT_HI_SMASK) >> 6) | + (pllctl_reg_read(pll, mult) & + PLLM_MULT_LO_MASK)) + 1; + output_div = ((pllctl_reg_read(pll, secctl) >> + PLL_CLKOD_SHIFT) & PLL_CLKOD_MASK) + 1; + + ret = ret / prediv / output_div * mult; + } + } else { + switch (pll) { + case PASS_PLL: + ret = external_clk[pa_clk]; + reg = KS2_PASSPLLCTL0; + break; + case TETRIS_PLL: + ret = external_clk[tetris_clk]; + reg = KS2_ARMPLLCTL0; + break; + case DDR3_PLL: + ret = external_clk[ddr3_clk]; + reg = KS2_DDR3APLLCTL0; + break; + default: + return 0; + } + + tmp = __raw_readl(reg); + if (!(tmp & PLLCTL_BYPASS)) { + /* Bypass disabled */ + prediv = (tmp & PLL_DIV_MASK) + 1; + mult = ((tmp >> PLL_MULT_SHIFT) & PLL_MULT_MASK) + 1; + output_div = ((tmp >> PLL_CLKOD_SHIFT) & + PLL_CLKOD_MASK) + 1; + ret = ((ret / prediv) * mult) / output_div; + } + } + + return ret; +} + +unsigned long clk_get_rate(unsigned int clk) +{ + switch (clk) { + case core_pll_clk: return pll_freq_get(CORE_PLL); + case pass_pll_clk: return pll_freq_get(PASS_PLL); + case tetris_pll_clk: return pll_freq_get(TETRIS_PLL); + case ddr3_pll_clk: return pll_freq_get(DDR3_PLL); + case sys_clk0_1_clk: + case sys_clk0_clk: return pll_freq_get(CORE_PLL) / pll0div_read(1); + case sys_clk1_clk: return pll_freq_get(CORE_PLL) / pll0div_read(2); + case sys_clk2_clk: return pll_freq_get(CORE_PLL) / pll0div_read(3); + case sys_clk3_clk: return pll_freq_get(CORE_PLL) / pll0div_read(4); + case sys_clk0_2_clk: return clk_get_rate(sys_clk0_clk) / 2; + case sys_clk0_3_clk: return clk_get_rate(sys_clk0_clk) / 3; + case sys_clk0_4_clk: return clk_get_rate(sys_clk0_clk) / 4; + case sys_clk0_6_clk: return clk_get_rate(sys_clk0_clk) / 6; + case sys_clk0_8_clk: return clk_get_rate(sys_clk0_clk) / 8; + case sys_clk0_12_clk: return clk_get_rate(sys_clk0_clk) / 12; + case sys_clk0_24_clk: return clk_get_rate(sys_clk0_clk) / 24; + case sys_clk1_3_clk: return clk_get_rate(sys_clk1_clk) / 3; + case sys_clk1_4_clk: return clk_get_rate(sys_clk1_clk) / 4; + case sys_clk1_6_clk: return clk_get_rate(sys_clk1_clk) / 6; + case sys_clk1_12_clk: return clk_get_rate(sys_clk1_clk) / 12; + default: + break; + } + + return 0; +} diff --git a/arch/arm/cpu/armv7/keystone/clock.c b/arch/arm/cpu/armv7/keystone/clock.c index 47fc89398d..d13fbc1a4b 100644 --- a/arch/arm/cpu/armv7/keystone/clock.c +++ b/arch/arm/cpu/armv7/keystone/clock.c @@ -185,10 +185,6 @@ void init_pll(const struct pll_init_data *data) tmp &= ~(PLL_BWADJ_HI_MASK); tmp |= ((bwadj >> 8) & PLL_BWADJ_HI_MASK); - /* set PLL Select (bit 13) for PASS PLL */ - if (data->pll == PASS_PLL) - tmp |= PLLCTL_PAPLL; - __raw_writel(tmp, keystone_pll_regs[data->pll].reg1); /* Reset bit: bit 14 for both DDR3 & PASS PLL */ @@ -261,3 +257,16 @@ inline int get_max_arm_speed(void) return get_max_speed((read_efuse_bootrom() >> 16) & 0xffff, arm_speeds); } #endif + +void pass_pll_pa_clk_enable(void) +{ + u32 reg; + + reg = readl(keystone_pll_regs[PASS_PLL].reg1); + + reg |= PLLCTL_PAPLL; + writel(reg, keystone_pll_regs[PASS_PLL].reg1); + + /* wait till clock is enabled */ + sdelay(15000); +} diff --git a/arch/arm/cpu/armv7/keystone/cmd_clock.c b/arch/arm/cpu/armv7/keystone/cmd_clock.c index d97c95be11..af1b701e82 100644 --- a/arch/arm/cpu/armv7/keystone/cmd_clock.c +++ b/arch/arm/cpu/armv7/keystone/cmd_clock.c @@ -58,20 +58,11 @@ pll_cmd_usage: return cmd_usage(cmdtp); } -#ifdef CONFIG_SOC_K2HK -U_BOOT_CMD( - pllset, 5, 0, do_pll_cmd, - "set pll multiplier and pre divider", - "<pa|arm|ddr3a|ddr3b> <mult> <div> <OD>\n" -); -#endif -#ifdef CONFIG_SOC_K2E U_BOOT_CMD( pllset, 5, 0, do_pll_cmd, "set pll multiplier and pre divider", - "<pa|ddr3> <mult> <div> <OD>\n" + PLLSET_CMD_LIST " <mult> <div> <OD>\n" ); -#endif int do_getclk_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -95,12 +86,8 @@ U_BOOT_CMD( getclk, 2, 0, do_getclk_cmd, "get clock rate", "<clk index>\n" -#ifdef CONFIG_SOC_K2HK - "See the 'enum clk_e' in the clock-k2hk.h for clk indexes\n" -#endif -#ifdef CONFIG_SOC_K2E - "See the 'enum clk_e' in the clock-k2e.h for clk indexes\n" -#endif + "The indexes for clocks:\n" + CLOCK_INDEXES_LIST ); int do_psc_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -141,5 +128,8 @@ U_BOOT_CMD( psc, 3, 0, do_psc_cmd, "<enable/disable psc module os disable domain>", "<mod/domain index> <en|di|domain>\n" - "See the hardware.h for Power and Sleep Controller (PSC) Domains\n" + "Intended to control Power and Sleep Controller (PSC) domains and\n" + "modules. The module or domain index exectly corresponds to ones\n" + "listed in official TRM. For instance, to enable MSMC RAM clock\n" + "domain use command: psc 14 en.\n" ); diff --git a/arch/arm/cpu/armv7/keystone/cmd_ddr3.c b/arch/arm/cpu/armv7/keystone/cmd_ddr3.c new file mode 100644 index 0000000000..ea78ad8fd5 --- /dev/null +++ b/arch/arm/cpu/armv7/keystone/cmd_ddr3.c @@ -0,0 +1,248 @@ +/* + * Keystone2: DDR3 test commands + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/hardware.h> +#include <asm/arch/ddr3.h> +#include <common.h> +#include <command.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define DDR_MIN_ADDR CONFIG_SYS_SDRAM_BASE + +#define DDR_REMAP_ADDR 0x80000000 +#define ECC_START_ADDR1 ((DDR_MIN_ADDR - DDR_REMAP_ADDR) >> 17) + +#define ECC_END_ADDR1 (((gd->start_addr_sp - DDR_REMAP_ADDR - \ + CONFIG_STACKSIZE) >> 17) - 2) + +#define DDR_TEST_BURST_SIZE 1024 + +static int ddr_memory_test(u32 start_address, u32 end_address, int quick) +{ + u32 index_start, value, index; + + index_start = start_address; + + while (1) { + /* Write a pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) + __raw_writel(index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) { + value = __raw_readl(index); + if (value != index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readl(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + + if (quick) + continue; + + /* Write a pattern for complementary values */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) + __raw_writel((u32)~index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 4) { + value = __raw_readl(index); + if (value != ~index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readl(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + + /* Write a pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 2) + __raw_writew((u16)index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 2) { + value = __raw_readw(index); + if (value != (u16)index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readw(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + + /* Write a pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 1) + __raw_writeb((u8)index, index); + + /* Read and check the pattern */ + for (index = index_start; + index < index_start + DDR_TEST_BURST_SIZE; + index += 1) { + value = __raw_readb(index); + if (value != (u8)index) { + printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", + index, value, __raw_readb(index)); + + return -1; + } + } + + index_start += DDR_TEST_BURST_SIZE; + if (index_start >= end_address) + break; + } + + puts("ddr memory test PASSED!\n"); + return 0; +} + +static int ddr_memory_compare(u32 address1, u32 address2, u32 size) +{ + u32 index, value, index2, value2; + + for (index = address1, index2 = address2; + index < address1 + size; + index += 4, index2 += 4) { + value = __raw_readl(index); + value2 = __raw_readl(index2); + + if (value != value2) { + printf("ddr_memory_test: Compare failed at address = 0x%x value = 0x%x, address2 = 0x%x value2 = 0x%x\n", + index, value, index2, value2); + + return -1; + } + } + + puts("ddr memory compare PASSED!\n"); + return 0; +} + +static int ddr_memory_ecc_err(u32 base, u32 address, u32 ecc_err) +{ + u32 value1, value2, value3; + + puts("Disabling DDR ECC ...\n"); + ddr3_disable_ecc(base); + + value1 = __raw_readl(address); + value2 = value1 ^ ecc_err; + __raw_writel(value2, address); + + value3 = __raw_readl(address); + printf("ECC err test, addr 0x%x, read data 0x%x, wrote data 0x%x, err pattern: 0x%x, read after write data 0x%x\n", + address, value1, value2, ecc_err, value3); + + __raw_writel(ECC_START_ADDR1 | (ECC_END_ADDR1 << 16), + base + KS2_DDR3_ECC_ADDR_RANGE1_OFFSET); + + puts("Enabling DDR ECC ...\n"); + ddr3_enable_ecc(base, 1); + + value1 = __raw_readl(address); + printf("ECC err test, addr 0x%x, read data 0x%x\n", address, value1); + + ddr3_check_ecc_int(base); + return 0; +} + +static int do_ddr_test(cmd_tbl_t *cmdtp, + int flag, int argc, char * const argv[]) +{ + u32 start_addr, end_addr, size, ecc_err; + + if ((argc == 4) && (strncmp(argv[1], "ecc_err", 8) == 0)) { + if (!ddr3_ecc_support_rmw(KS2_DDR3A_EMIF_CTRL_BASE)) { + puts("ECC RMW isn't supported for this SOC\n"); + return 1; + } + + start_addr = simple_strtoul(argv[2], NULL, 16); + ecc_err = simple_strtoul(argv[3], NULL, 16); + + if ((start_addr < CONFIG_SYS_SDRAM_BASE) || + (start_addr > (CONFIG_SYS_SDRAM_BASE + + CONFIG_MAX_RAM_BANK_SIZE - 1))) { + puts("Invalid address!\n"); + return cmd_usage(cmdtp); + } + + ddr_memory_ecc_err(KS2_DDR3A_EMIF_CTRL_BASE, + start_addr, ecc_err); + return 0; + } + + if (!(((argc == 4) && (strncmp(argv[1], "test", 5) == 0)) || + ((argc == 5) && (strncmp(argv[1], "compare", 8) == 0)))) + return cmd_usage(cmdtp); + + start_addr = simple_strtoul(argv[2], NULL, 16); + end_addr = simple_strtoul(argv[3], NULL, 16); + + if ((start_addr < CONFIG_SYS_SDRAM_BASE) || + (start_addr > (CONFIG_SYS_SDRAM_BASE + + CONFIG_MAX_RAM_BANK_SIZE - 1)) || + (end_addr < CONFIG_SYS_SDRAM_BASE) || + (end_addr > (CONFIG_SYS_SDRAM_BASE + + CONFIG_MAX_RAM_BANK_SIZE - 1)) || (start_addr >= end_addr)) { + puts("Invalid start or end address!\n"); + return cmd_usage(cmdtp); + } + + puts("Please wait ...\n"); + if (argc == 5) { + size = simple_strtoul(argv[4], NULL, 16); + ddr_memory_compare(start_addr, end_addr, size); + } else { + ddr_memory_test(start_addr, end_addr, 0); + } + + return 0; +} + +U_BOOT_CMD(ddr, 5, 1, do_ddr_test, + "DDR3 test", + "test <start_addr in hex> <end_addr in hex> - test DDR from start\n" + " address to end address\n" + "ddr compare <start_addr in hex> <end_addr in hex> <size in hex> -\n" + " compare DDR data of (size) bytes from start address to end\n" + " address\n" + "ddr ecc_err <addr in hex> <bit_err in hex> - generate bit errors\n" + " in DDR data at <addr>, the command will read a 32-bit data\n" + " from <addr>, and write (data ^ bit_err) back to <addr>\n" +); diff --git a/arch/arm/cpu/armv7/keystone/ddr3.c b/arch/arm/cpu/armv7/keystone/ddr3.c index 2eabec10f9..923906afb5 100644 --- a/arch/arm/cpu/armv7/keystone/ddr3.c +++ b/arch/arm/cpu/armv7/keystone/ddr3.c @@ -9,9 +9,19 @@ #include <asm/io.h> #include <common.h> +#include <asm/arch/msmc.h> #include <asm/arch/ddr3.h> #include <asm/arch/psc_defs.h> +#include <asm/ti-common/ti-edma3.h> + +#define DDR3_EDMA_BLK_SIZE_SHIFT 10 +#define DDR3_EDMA_BLK_SIZE (1 << DDR3_EDMA_BLK_SIZE_SHIFT) +#define DDR3_EDMA_BCNT 0x8000 +#define DDR3_EDMA_CCNT 1 +#define DDR3_EDMA_XF_SIZE (DDR3_EDMA_BLK_SIZE * DDR3_EDMA_BCNT) +#define DDR3_EDMA_SLOT_NUM 1 + void ddr3_init_ddrphy(u32 base, struct ddr3_phy_config *phy_cfg) { unsigned int tmp; @@ -70,6 +80,240 @@ void ddr3_init_ddremif(u32 base, struct ddr3_emif_config *emif_cfg) __raw_writel(emif_cfg->sdrfc, base + KS2_DDR3_SDRFC_OFFSET); } +int ddr3_ecc_support_rmw(u32 base) +{ + u32 value = __raw_readl(base + KS2_DDR3_MIDR_OFFSET); + + /* Check the DDR3 controller ID reg if the controllers + supports ECC RMW or not */ + if (value == 0x40461C02) + return 1; + + return 0; +} + +static void ddr3_ecc_config(u32 base, u32 value) +{ + u32 data; + + __raw_writel(value, base + KS2_DDR3_ECC_CTRL_OFFSET); + udelay(100000); /* delay required to synchronize across clock domains */ + + if (value & KS2_DDR3_ECC_EN) { + /* Clear the 1-bit error count */ + data = __raw_readl(base + KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET); + __raw_writel(data, base + KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET); + + /* enable the ECC interrupt */ + __raw_writel(KS2_DDR3_1B_ECC_ERR_SYS | KS2_DDR3_2B_ECC_ERR_SYS | + KS2_DDR3_WR_ECC_ERR_SYS, + base + KS2_DDR3_ECC_INT_ENABLE_SET_SYS_OFFSET); + + /* Clear the ECC error interrupt status */ + __raw_writel(KS2_DDR3_1B_ECC_ERR_SYS | KS2_DDR3_2B_ECC_ERR_SYS | + KS2_DDR3_WR_ECC_ERR_SYS, + base + KS2_DDR3_ECC_INT_STATUS_OFFSET); + } +} + +static void ddr3_reset_data(u32 base, u32 ddr3_size) +{ + u32 mpax[2]; + u32 seg_num; + u32 seg, blks, dst, edma_blks; + struct edma3_slot_config slot; + struct edma3_channel_config edma_channel; + u32 edma_src[DDR3_EDMA_BLK_SIZE/4] __aligned(16) = {0, }; + + /* Setup an edma to copy the 1k block to the entire DDR */ + puts("\nClear entire DDR3 memory to enable ECC\n"); + + /* save the SES MPAX regs */ + msmc_get_ses_mpax(8, 0, mpax); + + /* setup edma slot 1 configuration */ + slot.opt = EDMA3_SLOPT_TRANS_COMP_INT_ENB | + EDMA3_SLOPT_COMP_CODE(0) | + EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC; + slot.bcnt = DDR3_EDMA_BCNT; + slot.acnt = DDR3_EDMA_BLK_SIZE; + slot.ccnt = DDR3_EDMA_CCNT; + slot.src_bidx = 0; + slot.dst_bidx = DDR3_EDMA_BLK_SIZE; + slot.src_cidx = 0; + slot.dst_cidx = 0; + slot.link = EDMA3_PARSET_NULL_LINK; + slot.bcntrld = 0; + edma3_slot_configure(KS2_EDMA0_BASE, DDR3_EDMA_SLOT_NUM, &slot); + + /* configure quik edma channel */ + edma_channel.slot = DDR3_EDMA_SLOT_NUM; + edma_channel.chnum = 0; + edma_channel.complete_code = 0; + /* event trigger after dst update */ + edma_channel.trigger_slot_word = EDMA3_TWORD(dst); + qedma3_start(KS2_EDMA0_BASE, &edma_channel); + + /* DDR3 size in segments (4KB seg size) */ + seg_num = ddr3_size << (30 - KS2_MSMC_SEG_SIZE_SHIFT); + + for (seg = 0; seg < seg_num; seg += KS2_MSMC_MAP_SEG_NUM) { + /* map 2GB 36-bit DDR address to 32-bit DDR address in EMIF + access slave interface so that edma driver can access */ + msmc_map_ses_segment(8, 0, base >> KS2_MSMC_SEG_SIZE_SHIFT, + KS2_MSMC_DST_SEG_BASE + seg, MPAX_SEG_2G); + + if ((seg_num - seg) > KS2_MSMC_MAP_SEG_NUM) + edma_blks = KS2_MSMC_MAP_SEG_NUM << + (KS2_MSMC_SEG_SIZE_SHIFT + - DDR3_EDMA_BLK_SIZE_SHIFT); + else + edma_blks = (seg_num - seg) << (KS2_MSMC_SEG_SIZE_SHIFT + - DDR3_EDMA_BLK_SIZE_SHIFT); + + /* Use edma driver to scrub 2GB DDR memory */ + for (dst = base, blks = 0; blks < edma_blks; + blks += DDR3_EDMA_BCNT, dst += DDR3_EDMA_XF_SIZE) { + edma3_set_src_addr(KS2_EDMA0_BASE, + edma_channel.slot, (u32)edma_src); + edma3_set_dest_addr(KS2_EDMA0_BASE, + edma_channel.slot, (u32)dst); + + while (edma3_check_for_transfer(KS2_EDMA0_BASE, + &edma_channel)) + udelay(10); + } + } + + qedma3_stop(KS2_EDMA0_BASE, &edma_channel); + + /* restore the SES MPAX regs */ + msmc_set_ses_mpax(8, 0, mpax); +} + +static void ddr3_ecc_init_range(u32 base) +{ + u32 ecc_val = KS2_DDR3_ECC_EN; + u32 rmw = ddr3_ecc_support_rmw(base); + + if (rmw) + ecc_val |= KS2_DDR3_ECC_RMW_EN; + + __raw_writel(0, base + KS2_DDR3_ECC_ADDR_RANGE1_OFFSET); + + ddr3_ecc_config(base, ecc_val); +} + +void ddr3_enable_ecc(u32 base, int test) +{ + u32 ecc_val = KS2_DDR3_ECC_ENABLE; + u32 rmw = ddr3_ecc_support_rmw(base); + + if (test) + ecc_val |= KS2_DDR3_ECC_ADDR_RNG_1_EN; + + if (!rmw) { + if (!test) + /* by default, disable ecc when rmw = 0 and no + ecc test */ + ecc_val = 0; + } else { + ecc_val |= KS2_DDR3_ECC_RMW_EN; + } + + ddr3_ecc_config(base, ecc_val); +} + +void ddr3_disable_ecc(u32 base) +{ + ddr3_ecc_config(base, 0); +} + +#if defined(CONFIG_SOC_K2HK) || defined(CONFIG_SOC_K2L) +static void cic_init(u32 base) +{ + /* Disable CIC global interrupts */ + __raw_writel(0, base + KS2_CIC_GLOBAL_ENABLE); + + /* Set to normal mode, no nesting, no priority hold */ + __raw_writel(0, base + KS2_CIC_CTRL); + __raw_writel(0, base + KS2_CIC_HOST_CTRL); + + /* Enable CIC global interrupts */ + __raw_writel(1, base + KS2_CIC_GLOBAL_ENABLE); +} + +static void cic_map_cic_to_gic(u32 base, u32 chan_num, u32 irq_num) +{ + /* Map the system interrupt to a CIC channel */ + __raw_writeb(chan_num, base + KS2_CIC_CHAN_MAP(0) + irq_num); + + /* Enable CIC system interrupt */ + __raw_writel(irq_num, base + KS2_CIC_SYS_ENABLE_IDX_SET); + + /* Enable CIC Host interrupt */ + __raw_writel(chan_num, base + KS2_CIC_HOST_ENABLE_IDX_SET); +} + +static void ddr3_map_ecc_cic2_irq(u32 base) +{ + cic_init(base); + cic_map_cic_to_gic(base, KS2_CIC2_DDR3_ECC_CHAN_NUM, + KS2_CIC2_DDR3_ECC_IRQ_NUM); +} +#endif + +void ddr3_init_ecc(u32 base) +{ + u32 ddr3_size; + + if (!ddr3_ecc_support_rmw(base)) { + ddr3_disable_ecc(base); + return; + } + + ddr3_ecc_init_range(base); + ddr3_size = ddr3_get_size(); + ddr3_reset_data(CONFIG_SYS_SDRAM_BASE, ddr3_size); + + /* mapping DDR3 ECC system interrupt from CIC2 to GIC */ +#if defined(CONFIG_SOC_K2HK) || defined(CONFIG_SOC_K2L) + ddr3_map_ecc_cic2_irq(KS2_CIC2_BASE); +#endif + ddr3_enable_ecc(base, 0); +} + +void ddr3_check_ecc_int(u32 base) +{ + char *env; + int ecc_test = 0; + u32 value = __raw_readl(base + KS2_DDR3_ECC_INT_STATUS_OFFSET); + + env = getenv("ecc_test"); + if (env) + ecc_test = simple_strtol(env, NULL, 0); + + if (value & KS2_DDR3_WR_ECC_ERR_SYS) + puts("DDR3 ECC write error interrupted\n"); + + if (value & KS2_DDR3_2B_ECC_ERR_SYS) { + puts("DDR3 ECC 2-bit error interrupted\n"); + + if (!ecc_test) { + puts("Reseting the device ...\n"); + reset_cpu(0); + } + } + + value = __raw_readl(base + KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET); + if (value) { + printf("1-bit ECC err count: 0x%x\n", value); + value = __raw_readl(base + + KS2_DDR3_ONE_BIT_ECC_ERR_ADDR_LOG_OFFSET); + printf("1-bit ECC err address log: 0x%x\n", value); + } +} + void ddr3_reset_ddrphy(void) { u32 tmp; diff --git a/arch/arm/cpu/armv7/keystone/init.c b/arch/arm/cpu/armv7/keystone/init.c index a8f8aee8ab..c2b947839d 100644 --- a/arch/arm/cpu/armv7/keystone/init.c +++ b/arch/arm/cpu/armv7/keystone/init.c @@ -13,6 +13,7 @@ #include <asm/arch/msmc.h> #include <asm/arch/clock.h> #include <asm/arch/hardware.h> +#include <asm/arch/psc_defs.h> void chip_configuration_unlock(void) { @@ -20,17 +21,67 @@ void chip_configuration_unlock(void) __raw_writel(KS2_KICK1_MAGIC, KS2_KICK1); } +#ifdef CONFIG_SOC_K2L +void osr_init(void) +{ + u32 i; + u32 j; + u32 val; + u32 base = KS2_OSR_CFG_BASE; + u32 ecc_ctrl[KS2_OSR_NUM_RAM_BANKS]; + + /* Enable the OSR clock domain */ + psc_enable_module(KS2_LPSC_OSR); + + /* Disable OSR ECC check for all the ram banks */ + for (i = 0; i < KS2_OSR_NUM_RAM_BANKS; i++) { + val = i | KS2_OSR_ECC_VEC_TRIG_RD | + (KS2_OSR_ECC_CTRL << KS2_OSR_ECC_VEC_RD_ADDR_SH); + + writel(val , base + KS2_OSR_ECC_VEC); + + /** + * wait till read is done. + * Print should be added after earlyprintk support is added. + */ + for (j = 0; j < 10000; j++) { + val = readl(base + KS2_OSR_ECC_VEC); + if (val & KS2_OSR_ECC_VEC_RD_DONE) + break; + } + + ecc_ctrl[i] = readl(base + KS2_OSR_ECC_CTRL) ^ + KS2_OSR_ECC_CTRL_CHK; + + writel(ecc_ctrl[i], KS2_MSMC_DATA_BASE + i * 4); + writel(ecc_ctrl[i], base + KS2_OSR_ECC_CTRL); + } + + /* Reset OSR memory to all zeros */ + for (i = 0; i < KS2_OSR_SIZE; i += 4) + writel(0, KS2_OSR_DATA_BASE + i); + + /* Enable OSR ECC check for all the ram banks */ + for (i = 0; i < KS2_OSR_NUM_RAM_BANKS; i++) + writel(ecc_ctrl[i] | + KS2_OSR_ECC_CTRL_CHK, base + KS2_OSR_ECC_CTRL); +} +#endif + int arch_cpu_init(void) { chip_configuration_unlock(); icache_enable(); - msmc_share_all_segments(8); /* TETRIS */ - msmc_share_all_segments(9); /* NETCP */ - msmc_share_all_segments(10); /* QM PDSP */ - msmc_share_all_segments(11); /* PCIE 0 */ -#ifdef CONFIG_SOC_K2E - msmc_share_all_segments(13); /* PCIE 1 */ + msmc_share_all_segments(KS2_MSMC_SEGMENT_TETRIS); + msmc_share_all_segments(KS2_MSMC_SEGMENT_NETCP); + msmc_share_all_segments(KS2_MSMC_SEGMENT_QM_PDSP); + msmc_share_all_segments(KS2_MSMC_SEGMENT_PCIE0); +#if defined(CONFIG_SOC_K2E) || defined(CONFIG_SOC_K2L) + msmc_share_all_segments(KS2_MSMC_SEGMENT_PCIE1); +#endif +#ifdef CONFIG_SOC_K2L + osr_init(); #endif /* diff --git a/arch/arm/cpu/armv7/keystone/keystone_nav.c b/arch/arm/cpu/armv7/keystone/keystone_nav.c deleted file mode 100644 index 39d6f995f7..0000000000 --- a/arch/arm/cpu/armv7/keystone/keystone_nav.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Multicore Navigator driver for TI Keystone 2 devices. - * - * (C) Copyright 2012-2014 - * Texas Instruments Incorporated, <www.ti.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include <common.h> -#include <asm/io.h> -#include <asm/arch/keystone_nav.h> - -static int soc_type = -#ifdef CONFIG_SOC_K2HK - k2hk; -#endif - -struct qm_config k2hk_qm_memmap = { - .stat_cfg = 0x02a40000, - .queue = (struct qm_reg_queue *)0x02a80000, - .mngr_vbusm = 0x23a80000, - .i_lram = 0x00100000, - .proxy = (struct qm_reg_queue *)0x02ac0000, - .status_ram = 0x02a06000, - .mngr_cfg = (struct qm_cfg_reg *)0x02a02000, - .intd_cfg = 0x02a0c000, - .desc_mem = (struct descr_mem_setup_reg *)0x02a03000, - .region_num = 64, - .pdsp_cmd = 0x02a20000, - .pdsp_ctl = 0x02a0f000, - .pdsp_iram = 0x02a10000, - .qpool_num = 4000, -}; - -/* - * We are going to use only one type of descriptors - host packet - * descriptors. We staticaly allocate memory for them here - */ -struct qm_host_desc desc_pool[HDESC_NUM] __aligned(sizeof(struct qm_host_desc)); - -static struct qm_config *qm_cfg; - -inline int num_of_desc_to_reg(int num_descr) -{ - int j, num; - - for (j = 0, num = 32; j < 15; j++, num *= 2) { - if (num_descr <= num) - return j; - } - - return 15; -} - -static int _qm_init(struct qm_config *cfg) -{ - u32 j; - - if (cfg == NULL) - return QM_ERR; - - qm_cfg = cfg; - - qm_cfg->mngr_cfg->link_ram_base0 = qm_cfg->i_lram; - qm_cfg->mngr_cfg->link_ram_size0 = HDESC_NUM * 8; - qm_cfg->mngr_cfg->link_ram_base1 = 0; - qm_cfg->mngr_cfg->link_ram_size1 = 0; - qm_cfg->mngr_cfg->link_ram_base2 = 0; - - qm_cfg->desc_mem[0].base_addr = (u32)desc_pool; - qm_cfg->desc_mem[0].start_idx = 0; - qm_cfg->desc_mem[0].desc_reg_size = - (((sizeof(struct qm_host_desc) >> 4) - 1) << 16) | - num_of_desc_to_reg(HDESC_NUM); - - memset(desc_pool, 0, sizeof(desc_pool)); - for (j = 0; j < HDESC_NUM; j++) - qm_push(&desc_pool[j], qm_cfg->qpool_num); - - return QM_OK; -} - -int qm_init(void) -{ - switch (soc_type) { - case k2hk: - return _qm_init(&k2hk_qm_memmap); - } - - return QM_ERR; -} - -void qm_close(void) -{ - u32 j; - - if (qm_cfg == NULL) - return; - - queue_close(qm_cfg->qpool_num); - - qm_cfg->mngr_cfg->link_ram_base0 = 0; - qm_cfg->mngr_cfg->link_ram_size0 = 0; - qm_cfg->mngr_cfg->link_ram_base1 = 0; - qm_cfg->mngr_cfg->link_ram_size1 = 0; - qm_cfg->mngr_cfg->link_ram_base2 = 0; - - for (j = 0; j < qm_cfg->region_num; j++) { - qm_cfg->desc_mem[j].base_addr = 0; - qm_cfg->desc_mem[j].start_idx = 0; - qm_cfg->desc_mem[j].desc_reg_size = 0; - } - - qm_cfg = NULL; -} - -void qm_push(struct qm_host_desc *hd, u32 qnum) -{ - u32 regd; - - if (!qm_cfg) - return; - - cpu_to_bus((u32 *)hd, sizeof(struct qm_host_desc)/4); - regd = (u32)hd | ((sizeof(struct qm_host_desc) >> 4) - 1); - writel(regd, &qm_cfg->queue[qnum].ptr_size_thresh); -} - -void qm_buff_push(struct qm_host_desc *hd, u32 qnum, - void *buff_ptr, u32 buff_len) -{ - hd->orig_buff_len = buff_len; - hd->buff_len = buff_len; - hd->orig_buff_ptr = (u32)buff_ptr; - hd->buff_ptr = (u32)buff_ptr; - qm_push(hd, qnum); -} - -struct qm_host_desc *qm_pop(u32 qnum) -{ - u32 uhd; - - if (!qm_cfg) - return NULL; - - uhd = readl(&qm_cfg->queue[qnum].ptr_size_thresh) & ~0xf; - if (uhd) - cpu_to_bus((u32 *)uhd, sizeof(struct qm_host_desc)/4); - - return (struct qm_host_desc *)uhd; -} - -struct qm_host_desc *qm_pop_from_free_pool(void) -{ - if (!qm_cfg) - return NULL; - - return qm_pop(qm_cfg->qpool_num); -} - -void queue_close(u32 qnum) -{ - struct qm_host_desc *hd; - - while ((hd = qm_pop(qnum))) - ; -} - -/* - * DMA API - */ - -struct pktdma_cfg k2hk_netcp_pktdma = { - .global = (struct global_ctl_regs *)0x02004000, - .tx_ch = (struct tx_chan_regs *)0x02004400, - .tx_ch_num = 9, - .rx_ch = (struct rx_chan_regs *)0x02004800, - .rx_ch_num = 26, - .tx_sched = (u32 *)0x02004c00, - .rx_flows = (struct rx_flow_regs *)0x02005000, - .rx_flow_num = 32, - .rx_free_q = 4001, - .rx_rcv_q = 4002, - .tx_snd_q = 648, -}; - -struct pktdma_cfg *netcp; - -static int netcp_rx_disable(void) -{ - u32 j, v, k; - - for (j = 0; j < netcp->rx_ch_num; j++) { - v = readl(&netcp->rx_ch[j].cfg_a); - if (!(v & CPDMA_CHAN_A_ENABLE)) - continue; - - writel(v | CPDMA_CHAN_A_TDOWN, &netcp->rx_ch[j].cfg_a); - for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) { - udelay(100); - v = readl(&netcp->rx_ch[j].cfg_a); - if (!(v & CPDMA_CHAN_A_ENABLE)) - continue; - } - /* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */ - } - - /* Clear all of the flow registers */ - for (j = 0; j < netcp->rx_flow_num; j++) { - writel(0, &netcp->rx_flows[j].control); - writel(0, &netcp->rx_flows[j].tags); - writel(0, &netcp->rx_flows[j].tag_sel); - writel(0, &netcp->rx_flows[j].fdq_sel[0]); - writel(0, &netcp->rx_flows[j].fdq_sel[1]); - writel(0, &netcp->rx_flows[j].thresh[0]); - writel(0, &netcp->rx_flows[j].thresh[1]); - writel(0, &netcp->rx_flows[j].thresh[2]); - } - - return QM_OK; -} - -static int netcp_tx_disable(void) -{ - u32 j, v, k; - - for (j = 0; j < netcp->tx_ch_num; j++) { - v = readl(&netcp->tx_ch[j].cfg_a); - if (!(v & CPDMA_CHAN_A_ENABLE)) - continue; - - writel(v | CPDMA_CHAN_A_TDOWN, &netcp->tx_ch[j].cfg_a); - for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) { - udelay(100); - v = readl(&netcp->tx_ch[j].cfg_a); - if (!(v & CPDMA_CHAN_A_ENABLE)) - continue; - } - /* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */ - } - - return QM_OK; -} - -static int _netcp_init(struct pktdma_cfg *netcp_cfg, - struct rx_buff_desc *rx_buffers) -{ - u32 j, v; - struct qm_host_desc *hd; - u8 *rx_ptr; - - if (netcp_cfg == NULL || rx_buffers == NULL || - rx_buffers->buff_ptr == NULL || qm_cfg == NULL) - return QM_ERR; - - netcp = netcp_cfg; - netcp->rx_flow = rx_buffers->rx_flow; - - /* init rx queue */ - rx_ptr = rx_buffers->buff_ptr; - - for (j = 0; j < rx_buffers->num_buffs; j++) { - hd = qm_pop(qm_cfg->qpool_num); - if (hd == NULL) - return QM_ERR; - - qm_buff_push(hd, netcp->rx_free_q, - rx_ptr, rx_buffers->buff_len); - - rx_ptr += rx_buffers->buff_len; - } - - netcp_rx_disable(); - - /* configure rx channels */ - v = CPDMA_REG_VAL_MAKE_RX_FLOW_A(1, 1, 0, 0, 0, 0, 0, netcp->rx_rcv_q); - writel(v, &netcp->rx_flows[netcp->rx_flow].control); - writel(0, &netcp->rx_flows[netcp->rx_flow].tags); - writel(0, &netcp->rx_flows[netcp->rx_flow].tag_sel); - - v = CPDMA_REG_VAL_MAKE_RX_FLOW_D(0, netcp->rx_free_q, 0, - netcp->rx_free_q); - - writel(v, &netcp->rx_flows[netcp->rx_flow].fdq_sel[0]); - writel(v, &netcp->rx_flows[netcp->rx_flow].fdq_sel[1]); - writel(0, &netcp->rx_flows[netcp->rx_flow].thresh[0]); - writel(0, &netcp->rx_flows[netcp->rx_flow].thresh[1]); - writel(0, &netcp->rx_flows[netcp->rx_flow].thresh[2]); - - for (j = 0; j < netcp->rx_ch_num; j++) - writel(CPDMA_CHAN_A_ENABLE, &netcp->rx_ch[j].cfg_a); - - /* configure tx channels */ - /* Disable loopback in the tx direction */ - writel(0, &netcp->global->emulation_control); - -/* TODO: make it dependend on a soc type variable */ -#ifdef CONFIG_SOC_K2HK - /* Set QM base address, only for K2x devices */ - writel(0x23a80000, &netcp->global->qm_base_addr[0]); -#endif - - /* Enable all channels. The current state isn't important */ - for (j = 0; j < netcp->tx_ch_num; j++) { - writel(0, &netcp->tx_ch[j].cfg_b); - writel(CPDMA_CHAN_A_ENABLE, &netcp->tx_ch[j].cfg_a); - } - - return QM_OK; -} - -int netcp_init(struct rx_buff_desc *rx_buffers) -{ - switch (soc_type) { - case k2hk: - _netcp_init(&k2hk_netcp_pktdma, rx_buffers); - return QM_OK; - } - return QM_ERR; -} - -int netcp_close(void) -{ - if (!netcp) - return QM_ERR; - - netcp_tx_disable(); - netcp_rx_disable(); - - queue_close(netcp->rx_free_q); - queue_close(netcp->rx_rcv_q); - queue_close(netcp->tx_snd_q); - - return QM_OK; -} - -int netcp_send(u32 *pkt, int num_bytes, u32 swinfo2) -{ - struct qm_host_desc *hd; - - hd = qm_pop(qm_cfg->qpool_num); - if (hd == NULL) - return QM_ERR; - - hd->desc_info = num_bytes; - hd->swinfo[2] = swinfo2; - hd->packet_info = qm_cfg->qpool_num; - - qm_buff_push(hd, netcp->tx_snd_q, pkt, num_bytes); - - return QM_OK; -} - -void *netcp_recv(u32 **pkt, int *num_bytes) -{ - struct qm_host_desc *hd; - - hd = qm_pop(netcp->rx_rcv_q); - if (!hd) - return NULL; - - *pkt = (u32 *)hd->buff_ptr; - *num_bytes = hd->desc_info & 0x3fffff; - - return hd; -} - -void netcp_release_rxhd(void *hd) -{ - struct qm_host_desc *_hd = (struct qm_host_desc *)hd; - - _hd->buff_len = _hd->orig_buff_len; - _hd->buff_ptr = _hd->orig_buff_ptr; - - qm_push(_hd, netcp->rx_free_q); -} diff --git a/arch/arm/cpu/armv7/keystone/msmc.c b/arch/arm/cpu/armv7/keystone/msmc.c index 7d8e5978df..7899141d54 100644 --- a/arch/arm/cpu/armv7/keystone/msmc.c +++ b/arch/arm/cpu/armv7/keystone/msmc.c @@ -66,3 +66,29 @@ void msmc_share_all_segments(int priv_id) msmc->ses[priv_id][j].mpaxh &= 0xffffff7ful; } } + +void msmc_map_ses_segment(int priv_id, int ses_pair, + u32 src_pfn, u32 dst_pfn, enum mpax_seg_size size) +{ + struct msms_regs *msmc = (struct msms_regs *)KS2_MSMC_CTRL_BASE; + + msmc->ses[priv_id][ses_pair].mpaxh = src_pfn << 12 | + (size & 0x1f) | 0x80; + msmc->ses[priv_id][ses_pair].mpaxl = dst_pfn << 8 | 0x3f; +} + +void msmc_get_ses_mpax(int priv_id, int ses_pair, u32 *mpax) +{ + struct msms_regs *msmc = (struct msms_regs *)KS2_MSMC_CTRL_BASE; + + *mpax++ = msmc->ses[priv_id][ses_pair].mpaxl; + *mpax = msmc->ses[priv_id][ses_pair].mpaxh; +} + +void msmc_set_ses_mpax(int priv_id, int ses_pair, u32 *mpax) +{ + struct msms_regs *msmc = (struct msms_regs *)KS2_MSMC_CTRL_BASE; + + msmc->ses[priv_id][ses_pair].mpaxl = *mpax++; + msmc->ses[priv_id][ses_pair].mpaxh = *mpax; +} diff --git a/arch/arm/cpu/armv7/keystone/spl.c b/arch/arm/cpu/armv7/keystone/spl.c deleted file mode 100644 index d4b0e9b163..0000000000 --- a/arch/arm/cpu/armv7/keystone/spl.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * common spl init code - * - * (C) Copyright 2012-2014 - * Texas Instruments Incorporated, <www.ti.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include <common.h> -#include <config.h> -#include <ns16550.h> -#include <malloc.h> -#include <spl.h> -#include <spi_flash.h> - -#include <asm/u-boot.h> -#include <asm/utils.h> - -DECLARE_GLOBAL_DATA_PTR; - -#ifdef CONFIG_K2HK_EVM -static struct pll_init_data spl_pll_config[] = { - CORE_PLL_799, - TETRIS_PLL_500, -}; -#endif - -#ifdef CONFIG_K2E_EVM -static struct pll_init_data spl_pll_config[] = { - CORE_PLL_800, -}; -#endif - -void spl_init_keystone_plls(void) -{ - init_plls(ARRAY_SIZE(spl_pll_config), spl_pll_config); -} - -void spl_board_init(void) -{ - spl_init_keystone_plls(); - preloader_console_init(); -} - -u32 spl_boot_device(void) -{ -#if defined(CONFIG_SPL_SPI_LOAD) - return BOOT_DEVICE_SPI; -#else - puts("Unknown boot device\n"); - hang(); -#endif -} |