diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 0f7ae6e90b55..e6229bbbb83a 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -16,10 +16,13 @@ */ #include +#include #include #include #include #include +#include +#include #include @@ -32,18 +35,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 +100,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 +118,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 +129,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 +144,81 @@ 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) +static int __devinit tegra_emc_probe(struct platform_device *pdev) { - tegra_emc_table = table; - tegra_emc_table_size = table_size; + struct tegra_emc_pdata *pdata; + struct resource *res; + + if (!emc_enable) { + dev_err(&pdev->dev, "disabled per module parameter\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENXIO; + } + + 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; + } + emc_pdev = pdev; + + return 0; } + +static struct platform_driver tegra_emc_driver = { + .driver = { + .name = "tegra-emc", + .owner = THIS_MODULE, + }, + .probe = tegra_emc_probe, +}; + +static int __init tegra_emc_init(void) +{ + return platform_driver_register(&tegra_emc_driver); +} +device_initcall(tegra_emc_init); diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h index 19f08cb31603..f61409b54cb7 100644 --- a/arch/arm/mach-tegra/tegra2_emc.h +++ b/arch/arm/mach-tegra/tegra2_emc.h @@ -15,13 +15,10 @@ * */ -#define TEGRA_EMC_NUM_REGS 46 - -struct tegra_emc_table { - unsigned long rate; - u32 regs[TEGRA_EMC_NUM_REGS]; -}; +#ifndef __MACH_TEGRA_TEGRA2_EMC_H_ +#define __MACH_TEGRA_TEGRA2_EMC_H int tegra_emc_set_rate(unsigned long rate); long tegra_emc_round_rate(unsigned long rate); -void tegra_init_emc(const struct tegra_emc_table *table, int table_size); + +#endif diff --git a/include/linux/platform_data/tegra_emc.h b/include/linux/platform_data/tegra_emc.h new file mode 100644 index 000000000000..df67505e98f8 --- /dev/null +++ b/include/linux/platform_data/tegra_emc.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * Author: + * Colin Cross + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __TEGRA_EMC_H_ +#define __TEGRA_EMC_H_ + +#define TEGRA_EMC_NUM_REGS 46 + +struct tegra_emc_table { + unsigned long rate; + u32 regs[TEGRA_EMC_NUM_REGS]; +}; + +struct tegra_emc_pdata { + int num_tables; + struct tegra_emc_table *tables; +}; + +#endif