diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_emc.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra2_emc.c | 224 |
1 files changed, 201 insertions, 23 deletions
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 0f7ae6e90b55..5070d833bdd1 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -16,14 +16,19 @@ */ #include <linux/kernel.h> +#include <linux/device.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/platform_data/tegra_emc.h> #include <mach/iomap.h> #include "tegra2_emc.h" +#include "fuse.h" #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE static bool emc_enable = true; @@ -32,18 +37,17 @@ static bool emc_enable; #endif module_param(emc_enable, bool, 0644); -static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE); -static const struct tegra_emc_table *tegra_emc_table; -static int tegra_emc_table_size; +static struct platform_device *emc_pdev; +static void __iomem *emc_regbase; static inline void emc_writel(u32 val, unsigned long addr) { - writel(val, emc + addr); + writel(val, emc_regbase + addr); } static inline u32 emc_readl(unsigned long addr) { - return readl(emc + addr); + return readl(emc_regbase + addr); } static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { @@ -98,15 +102,15 @@ static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { /* Select the closest EMC rate that is higher than the requested rate */ long tegra_emc_round_rate(unsigned long rate) { + struct tegra_emc_pdata *pdata; int i; int best = -1; unsigned long distance = ULONG_MAX; - if (!tegra_emc_table) + if (!emc_pdev) return -EINVAL; - if (!emc_enable) - return -EINVAL; + pdata = emc_pdev->dev.platform_data; pr_debug("%s: %lu\n", __func__, rate); @@ -116,10 +120,10 @@ long tegra_emc_round_rate(unsigned long rate) */ rate = rate / 2 / 1000; - for (i = 0; i < tegra_emc_table_size; i++) { - if (tegra_emc_table[i].rate >= rate && - (tegra_emc_table[i].rate - rate) < distance) { - distance = tegra_emc_table[i].rate - rate; + for (i = 0; i < pdata->num_tables; i++) { + if (pdata->tables[i].rate >= rate && + (pdata->tables[i].rate - rate) < distance) { + distance = pdata->tables[i].rate - rate; best = i; } } @@ -127,9 +131,9 @@ long tegra_emc_round_rate(unsigned long rate) if (best < 0) return -EINVAL; - pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate); + pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate); - return tegra_emc_table[best].rate * 2 * 1000; + return pdata->tables[best].rate * 2 * 1000; } /* @@ -142,37 +146,211 @@ long tegra_emc_round_rate(unsigned long rate) */ int tegra_emc_set_rate(unsigned long rate) { + struct tegra_emc_pdata *pdata; int i; int j; - if (!tegra_emc_table) + if (!emc_pdev) return -EINVAL; + pdata = emc_pdev->dev.platform_data; + /* * The EMC clock rate is twice the bus rate, and the bus rate is * measured in kHz */ rate = rate / 2 / 1000; - for (i = 0; i < tegra_emc_table_size; i++) - if (tegra_emc_table[i].rate == rate) + for (i = 0; i < pdata->num_tables; i++) + if (pdata->tables[i].rate == rate) break; - if (i >= tegra_emc_table_size) + if (i >= pdata->num_tables) return -EINVAL; pr_debug("%s: setting to %lu\n", __func__, rate); for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) - emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]); + emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]); - emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]); + emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]); return 0; } -void tegra_init_emc(const struct tegra_emc_table *table, int table_size) +#ifdef CONFIG_OF +static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np) +{ + struct device_node *iter; + u32 reg; + + for_each_child_of_node(np, iter) { + if (of_property_read_u32(np, "nvidia,ram-code", ®)) + continue; + if (reg == tegra_bct_strapping) + return of_node_get(iter); + } + + return NULL; +} + +static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *tnp, *iter; + struct tegra_emc_pdata *pdata; + int ret, i, num_tables; + + if (!np) + return NULL; + + if (of_find_property(np, "nvidia,use-ram-code", NULL)) { + tnp = tegra_emc_ramcode_devnode(np); + if (!tnp) + dev_warn(&pdev->dev, + "can't find emc table for ram-code 0x%02x\n", + tegra_bct_strapping); + } else + tnp = of_node_get(np); + + if (!tnp) + return NULL; + + num_tables = 0; + for_each_child_of_node(tnp, iter) + if (of_device_is_compatible(iter, "nvidia,tegra20-emc-table")) + num_tables++; + + if (!num_tables) { + pdata = NULL; + goto out; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata->tables = devm_kzalloc(&pdev->dev, + sizeof(*pdata->tables) * num_tables, + GFP_KERNEL); + + i = 0; + for_each_child_of_node(tnp, iter) { + u32 prop; + + ret = of_property_read_u32(iter, "clock-frequency", &prop); + if (ret) { + dev_err(&pdev->dev, "no clock-frequency in %s\n", + iter->full_name); + continue; + } + pdata->tables[i].rate = prop; + + ret = of_property_read_u32_array(iter, "nvidia,emc-registers", + pdata->tables[i].regs, + TEGRA_EMC_NUM_REGS); + if (ret) { + dev_err(&pdev->dev, + "malformed emc-registers property in %s\n", + iter->full_name); + continue; + } + + i++; + } + pdata->num_tables = i; + +out: + of_node_put(tnp); + return pdata; +} +#else +static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( + struct platform_device *pdev) +{ + return NULL; +} +#endif + +static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct platform_device *pdev) +{ + struct clk *c = clk_get_sys(NULL, "emc"); + struct tegra_emc_pdata *pdata; + unsigned long khz; + int i; + + WARN_ON(pdev->dev.platform_data); + BUG_ON(IS_ERR_OR_NULL(c)); + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), + GFP_KERNEL); + + pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000; + + for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) + pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]); + + pdata->num_tables = 1; + + khz = pdata->tables[0].rate; + dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, " + "%ld kHz mem\n", khz * 2, khz); + + return pdata; +} + +static int __devinit tegra_emc_probe(struct platform_device *pdev) +{ + struct tegra_emc_pdata *pdata; + struct resource *res; + + if (!emc_enable) { + dev_err(&pdev->dev, "disabled per module parameter\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing register base\n"); + return -ENOMEM; + } + + emc_regbase = devm_request_and_ioremap(&pdev->dev, res); + if (!emc_regbase) { + dev_err(&pdev->dev, "failed to remap registers\n"); + return -ENOMEM; + } + + pdata = pdev->dev.platform_data; + + if (!pdata) + pdata = tegra_emc_dt_parse_pdata(pdev); + + if (!pdata) + pdata = tegra_emc_fill_pdata(pdev); + + pdev->dev.platform_data = pdata; + + emc_pdev = pdev; + + return 0; +} + +static struct of_device_id tegra_emc_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra20-emc", }, + { }, +}; + +static struct platform_driver tegra_emc_driver = { + .driver = { + .name = "tegra-emc", + .owner = THIS_MODULE, + .of_match_table = tegra_emc_of_match, + }, + .probe = tegra_emc_probe, +}; + +static int __init tegra_emc_init(void) { - tegra_emc_table = table; - tegra_emc_table_size = table_size; + return platform_driver_register(&tegra_emc_driver); } +device_initcall(tegra_emc_init); |