Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: tegra: MBIST work around for Tegra210

Tegra210 has a hw bug which can cause IP blocks to lock up when ungating a
domain. The reason is that the logic responsible for resetting the memory
built-in self test mode can come up in an undefined state because its
clock is gated by a second level clock gate (SLCG). Work around this by
making sure the logic will get some clock edges by ensuring the relevant
clock is enabled and temporarily override the relevant SLCGs.
Unfortunately for some IP blocks, the control bits for overriding the
SLCGs are not in CAR, but in the IP block itself. This means we need to
map a few extra register banks in the clock code.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Hector Martin <marcan@marcan.st>
Tested-by: Andre Heider <a.heider@gmail.com>
Tested-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>

fixup mbist

authored by

Peter De Schrijver and committed by
Thierry Reding
e403d005 cbfc8d0a

+343 -2
+342 -2
drivers/clk/tegra/clk-tegra210.c
··· 22 22 #include <linux/of_address.h> 23 23 #include <linux/delay.h> 24 24 #include <linux/export.h> 25 + #include <linux/mutex.h> 25 26 #include <linux/clk/tegra.h> 26 27 #include <dt-bindings/clock/tegra210-car.h> 27 28 #include <dt-bindings/reset/tegra210-car.h> 28 29 #include <linux/iopoll.h> 30 + #include <soc/tegra/pmc.h> 29 31 30 32 #include "clk.h" 31 33 #include "clk-id.h" ··· 234 232 #define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8 235 233 #define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac 236 234 235 + #define LVL2_CLK_GATE_OVRA 0xf8 236 + #define LVL2_CLK_GATE_OVRC 0x3a0 237 + #define LVL2_CLK_GATE_OVRD 0x3a4 238 + #define LVL2_CLK_GATE_OVRE 0x554 239 + 240 + /* I2S registers to handle during APE MBIST WAR */ 241 + #define TEGRA210_I2S_BASE 0x1000 242 + #define TEGRA210_I2S_SIZE 0x100 243 + #define TEGRA210_I2S_CTRLS 5 244 + #define TEGRA210_I2S_CG 0x88 245 + #define TEGRA210_I2S_CTRL 0xa0 246 + 247 + /* DISPA registers to handle during MBIST WAR */ 248 + #define DC_CMD_DISPLAY_COMMAND 0xc8 249 + #define DC_COM_DSC_TOP_CTL 0xcf8 250 + 251 + /* VIC register to handle during MBIST WAR */ 252 + #define NV_PVIC_THI_SLCG_OVERRIDE_LOW 0x8c 253 + 254 + /* APE, DISPA and VIC base addesses needed for MBIST WAR */ 255 + #define TEGRA210_AHUB_BASE 0x702d0000 256 + #define TEGRA210_DISPA_BASE 0x54200000 257 + #define TEGRA210_VIC_BASE 0x54340000 258 + 237 259 /* 238 260 * SDM fractional divisor is 16-bit 2's complement signed number within 239 261 * (-2^12 ... 2^12-1) range. Represented in PLL data structure as unsigned ··· 282 256 } tegra210_cpu_clk_sctx; 283 257 #endif 284 258 259 + struct tegra210_domain_mbist_war { 260 + void (*handle_lvl2_ovr)(struct tegra210_domain_mbist_war *mbist); 261 + const u32 lvl2_offset; 262 + const u32 lvl2_mask; 263 + const unsigned int num_clks; 264 + const unsigned int *clk_init_data; 265 + struct clk_bulk_data *clks; 266 + }; 267 + 268 + static struct clk **clks; 269 + 285 270 static void __iomem *clk_base; 286 271 static void __iomem *pmc_base; 272 + static void __iomem *ahub_base; 273 + static void __iomem *dispa_base; 274 + static void __iomem *vic_base; 287 275 288 276 static unsigned long osc_freq; 289 277 static unsigned long pll_ref_freq; ··· 308 268 static DEFINE_SPINLOCK(pll_u_lock); 309 269 static DEFINE_SPINLOCK(sor1_lock); 310 270 static DEFINE_SPINLOCK(emc_lock); 271 + static DEFINE_MUTEX(lvl2_ovr_lock); 311 272 312 273 /* possible OSC frequencies in Hz */ 313 274 static unsigned long tegra210_input_freq[] = { ··· 352 311 #define PLLA_MISC2_WRITE_MASK 0x06ffffff 353 312 354 313 /* PLLD */ 314 + #define PLLD_BASE_CSI_CLKSOURCE (1 << 23) 315 + 355 316 #define PLLD_MISC0_EN_SDM (1 << 16) 356 317 #define PLLD_MISC0_LOCK_OVERRIDE (1 << 17) 357 318 #define PLLD_MISC0_LOCK_ENABLE (1 << 18) ··· 556 513 writel_relaxed(val, clk_base + SATA_PLL_CFG0); 557 514 } 558 515 EXPORT_SYMBOL_GPL(tegra210_set_sata_pll_seq_sw); 516 + 517 + static void tegra210_generic_mbist_war(struct tegra210_domain_mbist_war *mbist) 518 + { 519 + u32 val; 520 + 521 + val = readl_relaxed(clk_base + mbist->lvl2_offset); 522 + writel_relaxed(val | mbist->lvl2_mask, clk_base + mbist->lvl2_offset); 523 + fence_udelay(1, clk_base); 524 + writel_relaxed(val, clk_base + mbist->lvl2_offset); 525 + fence_udelay(1, clk_base); 526 + } 527 + 528 + static void tegra210_venc_mbist_war(struct tegra210_domain_mbist_war *mbist) 529 + { 530 + u32 csi_src, ovra, ovre; 531 + unsigned long flags = 0; 532 + 533 + spin_lock_irqsave(&pll_d_lock, flags); 534 + 535 + csi_src = readl_relaxed(clk_base + PLLD_BASE); 536 + writel_relaxed(csi_src | PLLD_BASE_CSI_CLKSOURCE, clk_base + PLLD_BASE); 537 + fence_udelay(1, clk_base); 538 + 539 + ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA); 540 + writel_relaxed(ovra | BIT(15), clk_base + LVL2_CLK_GATE_OVRA); 541 + ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE); 542 + writel_relaxed(ovre | BIT(3), clk_base + LVL2_CLK_GATE_OVRE); 543 + fence_udelay(1, clk_base); 544 + 545 + writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA); 546 + writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE); 547 + writel_relaxed(csi_src, clk_base + PLLD_BASE); 548 + fence_udelay(1, clk_base); 549 + 550 + spin_unlock_irqrestore(&pll_d_lock, flags); 551 + } 552 + 553 + static void tegra210_disp_mbist_war(struct tegra210_domain_mbist_war *mbist) 554 + { 555 + u32 ovra, dsc_top_ctrl; 556 + 557 + ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA); 558 + writel_relaxed(ovra | BIT(1), clk_base + LVL2_CLK_GATE_OVRA); 559 + fence_udelay(1, clk_base); 560 + 561 + dsc_top_ctrl = readl_relaxed(dispa_base + DC_COM_DSC_TOP_CTL); 562 + writel_relaxed(dsc_top_ctrl | BIT(2), dispa_base + DC_COM_DSC_TOP_CTL); 563 + readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND); 564 + writel_relaxed(dsc_top_ctrl, dispa_base + DC_COM_DSC_TOP_CTL); 565 + readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND); 566 + 567 + writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA); 568 + fence_udelay(1, clk_base); 569 + } 570 + 571 + static void tegra210_vic_mbist_war(struct tegra210_domain_mbist_war *mbist) 572 + { 573 + u32 ovre, val; 574 + 575 + ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE); 576 + writel_relaxed(ovre | BIT(5), clk_base + LVL2_CLK_GATE_OVRE); 577 + fence_udelay(1, clk_base); 578 + 579 + val = readl_relaxed(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW); 580 + writel_relaxed(val | BIT(0) | GENMASK(7, 2) | BIT(24), 581 + vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW); 582 + fence_udelay(1, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW); 583 + 584 + writel_relaxed(val, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW); 585 + readl(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW); 586 + 587 + writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE); 588 + fence_udelay(1, clk_base); 589 + } 590 + 591 + static void tegra210_ape_mbist_war(struct tegra210_domain_mbist_war *mbist) 592 + { 593 + void __iomem *i2s_base; 594 + unsigned int i; 595 + u32 ovrc, ovre; 596 + 597 + ovrc = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRC); 598 + ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE); 599 + writel_relaxed(ovrc | BIT(1), clk_base + LVL2_CLK_GATE_OVRC); 600 + writel_relaxed(ovre | BIT(10) | BIT(11), 601 + clk_base + LVL2_CLK_GATE_OVRE); 602 + fence_udelay(1, clk_base); 603 + 604 + i2s_base = ahub_base + TEGRA210_I2S_BASE; 605 + 606 + for (i = 0; i < TEGRA210_I2S_CTRLS; i++) { 607 + u32 i2s_ctrl; 608 + 609 + i2s_ctrl = readl_relaxed(i2s_base + TEGRA210_I2S_CTRL); 610 + writel_relaxed(i2s_ctrl | BIT(10), 611 + i2s_base + TEGRA210_I2S_CTRL); 612 + writel_relaxed(0, i2s_base + TEGRA210_I2S_CG); 613 + readl(i2s_base + TEGRA210_I2S_CG); 614 + writel_relaxed(1, i2s_base + TEGRA210_I2S_CG); 615 + writel_relaxed(i2s_ctrl, i2s_base + TEGRA210_I2S_CTRL); 616 + readl(i2s_base + TEGRA210_I2S_CTRL); 617 + 618 + i2s_base += TEGRA210_I2S_SIZE; 619 + } 620 + 621 + writel_relaxed(ovrc, clk_base + LVL2_CLK_GATE_OVRC); 622 + writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE); 623 + fence_udelay(1, clk_base); 624 + } 559 625 560 626 static inline void _pll_misc_chk_default(void __iomem *base, 561 627 struct tegra_clk_pll_params *params, ··· 2564 2412 { "pll_a1", &pll_a1_params, tegra_clk_pll_a1, "pll_ref" }, 2565 2413 }; 2566 2414 2567 - static struct clk **clks; 2568 - 2569 2415 static const char * const aclk_parents[] = { 2570 2416 "pll_a1", "pll_c", "pll_p", "pll_a_out0", "pll_c2", "pll_c3", 2571 2417 "clk_m" 2572 2418 }; 2419 + 2420 + static const unsigned int nvjpg_slcg_clkids[] = { TEGRA210_CLK_NVDEC }; 2421 + static const unsigned int nvdec_slcg_clkids[] = { TEGRA210_CLK_NVJPG }; 2422 + static const unsigned int sor_slcg_clkids[] = { TEGRA210_CLK_HDA2CODEC_2X, 2423 + TEGRA210_CLK_HDA2HDMI, TEGRA210_CLK_DISP1, TEGRA210_CLK_DISP2 }; 2424 + static const unsigned int disp_slcg_clkids[] = { TEGRA210_CLK_LA, 2425 + TEGRA210_CLK_HOST1X}; 2426 + static const unsigned int xusba_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST, 2427 + TEGRA210_CLK_XUSB_DEV }; 2428 + static const unsigned int xusbb_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST, 2429 + TEGRA210_CLK_XUSB_SS }; 2430 + static const unsigned int xusbc_slcg_clkids[] = { TEGRA210_CLK_XUSB_DEV, 2431 + TEGRA210_CLK_XUSB_SS }; 2432 + static const unsigned int venc_slcg_clkids[] = { TEGRA210_CLK_HOST1X, 2433 + TEGRA210_CLK_PLL_D }; 2434 + static const unsigned int ape_slcg_clkids[] = { TEGRA210_CLK_ACLK, 2435 + TEGRA210_CLK_I2S0, TEGRA210_CLK_I2S1, TEGRA210_CLK_I2S2, 2436 + TEGRA210_CLK_I2S3, TEGRA210_CLK_I2S4, TEGRA210_CLK_SPDIF_OUT, 2437 + TEGRA210_CLK_D_AUDIO }; 2438 + static const unsigned int vic_slcg_clkids[] = { TEGRA210_CLK_HOST1X }; 2439 + 2440 + static struct tegra210_domain_mbist_war tegra210_pg_mbist_war[] = { 2441 + [TEGRA_POWERGATE_VENC] = { 2442 + .handle_lvl2_ovr = tegra210_venc_mbist_war, 2443 + .num_clks = ARRAY_SIZE(venc_slcg_clkids), 2444 + .clk_init_data = venc_slcg_clkids, 2445 + }, 2446 + [TEGRA_POWERGATE_SATA] = { 2447 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2448 + .lvl2_offset = LVL2_CLK_GATE_OVRC, 2449 + .lvl2_mask = BIT(0) | BIT(17) | BIT(19), 2450 + }, 2451 + [TEGRA_POWERGATE_MPE] = { 2452 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2453 + .lvl2_offset = LVL2_CLK_GATE_OVRE, 2454 + .lvl2_mask = BIT(2), 2455 + }, 2456 + [TEGRA_POWERGATE_SOR] = { 2457 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2458 + .num_clks = ARRAY_SIZE(sor_slcg_clkids), 2459 + .clk_init_data = sor_slcg_clkids, 2460 + .lvl2_offset = LVL2_CLK_GATE_OVRA, 2461 + .lvl2_mask = BIT(1) | BIT(2), 2462 + }, 2463 + [TEGRA_POWERGATE_DIS] = { 2464 + .handle_lvl2_ovr = tegra210_disp_mbist_war, 2465 + .num_clks = ARRAY_SIZE(disp_slcg_clkids), 2466 + .clk_init_data = disp_slcg_clkids, 2467 + }, 2468 + [TEGRA_POWERGATE_DISB] = { 2469 + .num_clks = ARRAY_SIZE(disp_slcg_clkids), 2470 + .clk_init_data = disp_slcg_clkids, 2471 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2472 + .lvl2_offset = LVL2_CLK_GATE_OVRA, 2473 + .lvl2_mask = BIT(2), 2474 + }, 2475 + [TEGRA_POWERGATE_XUSBA] = { 2476 + .num_clks = ARRAY_SIZE(xusba_slcg_clkids), 2477 + .clk_init_data = xusba_slcg_clkids, 2478 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2479 + .lvl2_offset = LVL2_CLK_GATE_OVRC, 2480 + .lvl2_mask = BIT(30) | BIT(31), 2481 + }, 2482 + [TEGRA_POWERGATE_XUSBB] = { 2483 + .num_clks = ARRAY_SIZE(xusbb_slcg_clkids), 2484 + .clk_init_data = xusbb_slcg_clkids, 2485 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2486 + .lvl2_offset = LVL2_CLK_GATE_OVRC, 2487 + .lvl2_mask = BIT(30) | BIT(31), 2488 + }, 2489 + [TEGRA_POWERGATE_XUSBC] = { 2490 + .num_clks = ARRAY_SIZE(xusbc_slcg_clkids), 2491 + .clk_init_data = xusbc_slcg_clkids, 2492 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2493 + .lvl2_offset = LVL2_CLK_GATE_OVRC, 2494 + .lvl2_mask = BIT(30) | BIT(31), 2495 + }, 2496 + [TEGRA_POWERGATE_VIC] = { 2497 + .num_clks = ARRAY_SIZE(vic_slcg_clkids), 2498 + .clk_init_data = vic_slcg_clkids, 2499 + .handle_lvl2_ovr = tegra210_vic_mbist_war, 2500 + }, 2501 + [TEGRA_POWERGATE_NVDEC] = { 2502 + .num_clks = ARRAY_SIZE(nvdec_slcg_clkids), 2503 + .clk_init_data = nvdec_slcg_clkids, 2504 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2505 + .lvl2_offset = LVL2_CLK_GATE_OVRC, 2506 + .lvl2_mask = BIT(9) | BIT(31), 2507 + }, 2508 + [TEGRA_POWERGATE_NVJPG] = { 2509 + .num_clks = ARRAY_SIZE(nvjpg_slcg_clkids), 2510 + .clk_init_data = nvjpg_slcg_clkids, 2511 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2512 + .lvl2_offset = LVL2_CLK_GATE_OVRC, 2513 + .lvl2_mask = BIT(9) | BIT(31), 2514 + }, 2515 + [TEGRA_POWERGATE_AUD] = { 2516 + .num_clks = ARRAY_SIZE(ape_slcg_clkids), 2517 + .clk_init_data = ape_slcg_clkids, 2518 + .handle_lvl2_ovr = tegra210_ape_mbist_war, 2519 + }, 2520 + [TEGRA_POWERGATE_VE2] = { 2521 + .handle_lvl2_ovr = tegra210_generic_mbist_war, 2522 + .lvl2_offset = LVL2_CLK_GATE_OVRD, 2523 + .lvl2_mask = BIT(22), 2524 + }, 2525 + }; 2526 + 2527 + int tegra210_clk_handle_mbist_war(unsigned int id) 2528 + { 2529 + int err; 2530 + struct tegra210_domain_mbist_war *mbist_war; 2531 + 2532 + if (id >= ARRAY_SIZE(tegra210_pg_mbist_war)) { 2533 + WARN(1, "unknown domain id in MBIST WAR handler\n"); 2534 + return -EINVAL; 2535 + } 2536 + 2537 + mbist_war = &tegra210_pg_mbist_war[id]; 2538 + if (!mbist_war->handle_lvl2_ovr) 2539 + return 0; 2540 + 2541 + if (mbist_war->num_clks && !mbist_war->clks) 2542 + return -ENODEV; 2543 + 2544 + err = clk_bulk_prepare_enable(mbist_war->num_clks, mbist_war->clks); 2545 + if (err < 0) 2546 + return err; 2547 + 2548 + mutex_lock(&lvl2_ovr_lock); 2549 + 2550 + mbist_war->handle_lvl2_ovr(mbist_war); 2551 + 2552 + mutex_unlock(&lvl2_ovr_lock); 2553 + 2554 + clk_bulk_disable_unprepare(mbist_war->num_clks, mbist_war->clks); 2555 + 2556 + return 0; 2557 + } 2573 2558 2574 2559 void tegra210_put_utmipll_in_iddq(void) 2575 2560 { ··· 3452 3163 return 0; 3453 3164 } 3454 3165 3166 + static void tegra210_mbist_clk_init(void) 3167 + { 3168 + unsigned int i, j; 3169 + 3170 + for (i = 0; i < ARRAY_SIZE(tegra210_pg_mbist_war); i++) { 3171 + unsigned int num_clks = tegra210_pg_mbist_war[i].num_clks; 3172 + struct clk_bulk_data *clk_data; 3173 + 3174 + if (!num_clks) 3175 + continue; 3176 + 3177 + clk_data = kmalloc_array(num_clks, sizeof(*clk_data), 3178 + GFP_KERNEL); 3179 + if (WARN_ON(!clk_data)) 3180 + return; 3181 + 3182 + tegra210_pg_mbist_war[i].clks = clk_data; 3183 + for (j = 0; j < num_clks; j++) { 3184 + int clk_id = tegra210_pg_mbist_war[i].clk_init_data[j]; 3185 + struct clk *clk = clks[clk_id]; 3186 + 3187 + if (WARN(IS_ERR(clk), "clk_id: %d\n", clk_id)) { 3188 + kfree(clk_data); 3189 + tegra210_pg_mbist_war[i].clks = NULL; 3190 + break; 3191 + } 3192 + clk_data[j].clk = clk; 3193 + } 3194 + } 3195 + } 3196 + 3455 3197 /** 3456 3198 * tegra210_clock_init - Tegra210-specific clock initialization 3457 3199 * @np: struct device_node * of the DT node for the SoC CAR IP block ··· 3514 3194 if (!pmc_base) { 3515 3195 pr_err("Can't map pmc registers\n"); 3516 3196 WARN_ON(1); 3197 + return; 3198 + } 3199 + 3200 + ahub_base = ioremap(TEGRA210_AHUB_BASE, SZ_64K); 3201 + if (!ahub_base) { 3202 + pr_err("ioremap tegra210 APE failed\n"); 3203 + return; 3204 + } 3205 + 3206 + dispa_base = ioremap(TEGRA210_DISPA_BASE, SZ_256K); 3207 + if (!dispa_base) { 3208 + pr_err("ioremap tegra210 DISPA failed\n"); 3209 + return; 3210 + } 3211 + 3212 + vic_base = ioremap(TEGRA210_VIC_BASE, SZ_256K); 3213 + if (!vic_base) { 3214 + pr_err("ioremap tegra210 VIC failed\n"); 3517 3215 return; 3518 3216 } 3519 3217 ··· 3570 3232 3571 3233 tegra_add_of_provider(np); 3572 3234 tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); 3235 + 3236 + tegra210_mbist_clk_init(); 3573 3237 3574 3238 tegra_cpu_car_ops = &tegra210_cpu_car_ops; 3575 3239 }
+1
include/linux/clk/tegra.h
··· 128 128 extern void tegra210_set_sata_pll_seq_sw(bool state); 129 129 extern void tegra210_put_utmipll_in_iddq(void); 130 130 extern void tegra210_put_utmipll_out_iddq(void); 131 + extern int tegra210_clk_handle_mbist_war(unsigned int id); 131 132 132 133 #endif /* __LINUX_CLK_TEGRA_H_ */