/* * Copyright (c) 2011 The Chromium OS Authors. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include /* * The EMC registers have shadow registers. When the EMC clock is updated * in the clock controller, the shadow registers are copied to the active * registers, allowing glitchless memory bus frequency changes. * This function updates the shadow registers for a new clock frequency, * and relies on the clock lock on the emc clock to avoid races between * multiple frequency changes */ /* * This table defines the ordering of the registers provided to * tegra_set_mmc() * TODO: Convert to fdt version once available */ static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { 0x2c, /* RC */ 0x30, /* RFC */ 0x34, /* RAS */ 0x38, /* RP */ 0x3c, /* R2W */ 0x40, /* W2R */ 0x44, /* R2P */ 0x48, /* W2P */ 0x4c, /* RD_RCD */ 0x50, /* WR_RCD */ 0x54, /* RRD */ 0x58, /* REXT */ 0x5c, /* WDV */ 0x60, /* QUSE */ 0x64, /* QRST */ 0x68, /* QSAFE */ 0x6c, /* RDV */ 0x70, /* REFRESH */ 0x74, /* BURST_REFRESH_NUM */ 0x78, /* PDEX2WR */ 0x7c, /* PDEX2RD */ 0x80, /* PCHG2PDEN */ 0x84, /* ACT2PDEN */ 0x88, /* AR2PDEN */ 0x8c, /* RW2PDEN */ 0x90, /* TXSR */ 0x94, /* TCKE */ 0x98, /* TFAW */ 0x9c, /* TRPAB */ 0xa0, /* TCLKSTABLE */ 0xa4, /* TCLKSTOP */ 0xa8, /* TREFBW */ 0xac, /* QUSE_EXTRA */ 0x114, /* FBIO_CFG6 */ 0xb0, /* ODT_WRITE */ 0xb4, /* ODT_READ */ 0x104, /* FBIO_CFG5 */ 0x2bc, /* CFG_DIG_DLL */ 0x2c0, /* DLL_XFORM_DQS */ 0x2c4, /* DLL_XFORM_QUSE */ 0x2e0, /* ZCAL_REF_CNT */ 0x2e4, /* ZCAL_WAIT_CNT */ 0x2a8, /* AUTO_CAL_INTERVAL */ 0x2d0, /* CFG_CLKTRIM_0 */ 0x2d4, /* CFG_CLKTRIM_1 */ 0x2d8, /* CFG_CLKTRIM_2 */ }; struct emc_ctlr *emc_get_controller(const void *blob) { fdt_addr_t addr; int node; node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_EMC); if (node > 0) { addr = fdtdec_get_addr(blob, node, "reg"); if (addr != FDT_ADDR_T_NONE) return (struct emc_ctlr *)addr; } return NULL; } /* Error codes we use */ enum { ERR_NO_EMC_NODE = -10, ERR_NO_EMC_REG, ERR_NO_FREQ, ERR_FREQ_NOT_FOUND, ERR_BAD_REGS, ERR_NO_RAM_CODE, ERR_RAM_CODE_NOT_FOUND, }; /** * Find EMC tables for the given ram code. * * The tegra EMC binding has two options, one using the ram code and one not. * We detect which is in use by looking for the nvidia,use-ram-code property. * If this is not present, then the EMC tables are directly below 'node', * otherwise we select the correct emc-tables subnode based on the 'ram_code' * value. * * @param blob Device tree blob * @param node EMC node (nvidia,tegra20-emc compatible string) * @param ram_code RAM code to select (0-3, or -1 if unknown) * @return 0 if ok, otherwise a -ve ERR_ code (see enum above) */ static int find_emc_tables(const void *blob, int node, int ram_code) { int need_ram_code; int depth; int offset; /* If we are using RAM codes, scan through the tables for our code */ need_ram_code = fdtdec_get_bool(blob, node, "nvidia,use-ram-code"); if (!need_ram_code) return node; if (ram_code == -1) { debug("%s: RAM code required but not supplied\n", __func__); return ERR_NO_RAM_CODE; } offset = node; depth = 0; do { /* * Sadly there is no compatible string so we cannot use * fdtdec_next_compatible_subnode(). */ offset = fdt_next_node(blob, offset, &depth); if (depth <= 0) break; /* Make sure this is a direct subnode */ if (depth != 1) continue; if (strcmp("emc-tables", fdt_get_name(blob, offset, NULL))) continue; if (fdtdec_get_int(blob, offset, "nvidia,ram-code", -1) == ram_code) return offset; } while (1); debug("%s: Could not find tables for RAM code %d\n", __func__, ram_code); return ERR_RAM_CODE_NOT_FOUND; } /** * Decode the EMC node of the device tree, returning a pointer to the emc * controller and the table to be used for the given rate. * * @param blob Device tree blob * @param rate Clock speed of memory controller in Hz (=2x memory bus rate) * @param emcp Returns address of EMC controller registers * @param tablep Returns pointer to table to program into EMC. There are * TEGRA_EMC_NUM_REGS entries, destined for offsets as per the * emc_reg_addr array. * @return 0 if ok, otherwise a -ve error code which will allow someone to * figure out roughly what went wrong by looking at this code. */ static int decode_emc(const void *blob, unsigned rate, struct emc_ctlr **emcp, const u32 **tablep) { struct apb_misc_pp_ctlr *pp = (struct apb_misc_pp_ctlr *)NV_PA_APB_MISC_BASE; int ram_code; int depth; int node; ram_code = (readl(&pp->strapping_opt_a) & RAM_CODE_MASK) >> RAM_CODE_SHIFT; /* * The EMC clock rate is twice the bus rate, and the bus rate is * measured in kHz */ rate = rate / 2 / 1000; node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_EMC); if (node < 0) { debug("%s: No EMC node found in FDT\n", __func__); return ERR_NO_EMC_NODE; } *emcp = (struct emc_ctlr *)fdtdec_get_addr(blob, node, "reg"); if (*emcp == (struct emc_ctlr *)FDT_ADDR_T_NONE) { debug("%s: No EMC node reg property\n", __func__); return ERR_NO_EMC_REG; } /* Work out the parent node which contains our EMC tables */ node = find_emc_tables(blob, node, ram_code & 3); if (node < 0) return node; depth = 0; for (;;) { int node_rate; node = fdtdec_next_compatible_subnode(blob, node, COMPAT_NVIDIA_TEGRA20_EMC_TABLE, &depth); if (node < 0) break; node_rate = fdtdec_get_int(blob, node, "clock-frequency", -1); if (node_rate == -1) { debug("%s: Missing clock-frequency\n", __func__); return ERR_NO_FREQ; /* we expect this property */ } if (node_rate == rate) break; } if (node < 0) { debug("%s: No node found for clock frequency %d\n", __func__, rate); return ERR_FREQ_NOT_FOUND; } *tablep = fdtdec_locate_array(blob, node, "nvidia,emc-registers", TEGRA_EMC_NUM_REGS); if (!*tablep) { debug("%s: node '%s' array missing / wrong size\n", __func__, fdt_get_name(blob, node, NULL)); return ERR_BAD_REGS; } /* All seems well */ return 0; } int tegra_set_emc(const void *blob, unsigned rate) { struct emc_ctlr *emc; const u32 *table = NULL; int err, i; err = decode_emc(blob, rate, &emc, &table); if (err) { debug("Warning: no valid EMC (%d), memory timings unset\n", err); return err; } debug("%s: Table found, setting EMC values as follows:\n", __func__); for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) { u32 value = fdt32_to_cpu(table[i]); u32 addr = (uintptr_t)emc + emc_reg_addr[i]; debug(" %#x: %#x\n", addr, value); writel(value, addr); } /* trigger emc with new settings */ clock_adjust_periph_pll_div(PERIPH_ID_EMC, CLOCK_ID_MEMORY, clock_get_rate(CLOCK_ID_MEMORY), NULL); debug("EMC clock set to %lu\n", clock_get_periph_rate(PERIPH_ID_EMC, CLOCK_ID_MEMORY)); return 0; }