mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-15 06:33:45 +09:00
![Tom Rini](/assets/img/avatar_default.png)
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
270 lines
7.0 KiB
C
270 lines
7.0 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2011 The Chromium OS Authors.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <fdtdec.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch-tegra/ap.h>
|
|
#include <asm/arch-tegra/apb_misc.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/arch/emc.h>
|
|
#include <asm/arch/tegra.h>
|
|
|
|
/*
|
|
* 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;
|
|
}
|