aboutsummaryrefslogtreecommitdiff
path: root/drivers/memory
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/memory')
-rw-r--r--drivers/memory/Kconfig25
-rw-r--r--drivers/memory/Makefile6
-rw-r--r--drivers/memory/emif.c354
-rw-r--r--drivers/memory/fsl_ifc.c309
-rw-r--r--drivers/memory/mvebu-devbus.c362
-rw-r--r--drivers/memory/of_memory.c153
-rw-r--r--drivers/memory/of_memory.h36
-rw-r--r--drivers/memory/tegra20-mc.c28
-rw-r--r--drivers/memory/tegra30-mc.c46
-rw-r--r--drivers/memory/ti-aemif.c427
10 files changed, 1664 insertions, 82 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 067f31174a0..c59e9c96e86 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -7,6 +7,17 @@ menuconfig MEMORY
if MEMORY
+config TI_AEMIF
+ tristate "Texas Instruments AEMIF driver"
+ depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
+ help
+ This driver is for the AEMIF module available in Texas Instruments
+ SoCs. AEMIF stands for Asynchronous External Memory Interface and
+ is intended to provide a glue-less interface to a variety of
+ asynchronuous memory devices like ASRAM, NOR and NAND memory. A total
+ of 256M bytes of any of these memories can be accessed at a given
+ time via four chip selects with 64M byte access per chip select.
+
config TI_EMIF
tristate "Texas Instruments EMIF driver"
depends on ARCH_OMAP2PLUS
@@ -20,6 +31,16 @@ config TI_EMIF
parameters and other settings during frequency, voltage and
temperature changes
+config MVEBU_DEVBUS
+ bool "Marvell EBU Device Bus Controller"
+ default y
+ depends on PLAT_ORION && OF
+ help
+ This driver is for the Device Bus controller available in some
+ Marvell EBU SoCs such as Discovery (mv78xx0), Orion (88f5xxx) and
+ Armada 370 and Armada XP. This controller allows to handle flash
+ devices such as NOR, NAND, SRAM, and FPGA.
+
config TEGRA20_MC
bool "Tegra20 Memory Controller(MC) driver"
default y
@@ -40,4 +61,8 @@ config TEGRA30_MC
analysis, especially for IOMMU/SMMU(System Memory Management
Unit) module.
+config FSL_IFC
+ bool
+ depends on FSL_SOC
+
endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 42b3ce9d80f..71160a2b731 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -2,6 +2,12 @@
# Makefile for memory devices
#
+ifeq ($(CONFIG_DDR),y)
+obj-$(CONFIG_OF) += of_memory.o
+endif
+obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
+obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
+obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 33a4396b24c..04644e7b42b 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -10,6 +10,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/platform_data/emif_plat.h>
@@ -18,13 +19,16 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/spinlock.h>
+#include <linux/pm.h>
#include <memory/jedec_ddr.h>
#include "emif.h"
+#include "of_memory.h"
/**
* struct emif_data - Per device static data for driver's use
@@ -49,6 +53,7 @@
* frequency in effect at the moment)
* @plat_data: Pointer to saved platform data.
* @debugfs_root: dentry to the root folder for EMIF in debugfs
+ * @np_ddr: Pointer to ddr device tree node
*/
struct emif_data {
u8 duplicate;
@@ -63,6 +68,7 @@ struct emif_data {
struct emif_regs *curr_regs;
struct emif_platform_data *plat_data;
struct dentry *debugfs_root;
+ struct device_node *np_ddr;
};
static struct emif_data *emif1;
@@ -71,6 +77,7 @@ static unsigned long irq_state;
static u32 t_ck; /* DDR clock period in ps */
static LIST_HEAD(device_list);
+#ifdef CONFIG_DEBUG_FS
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
struct emif_regs *regs)
{
@@ -162,23 +169,23 @@ static int __init_or_module emif_debugfs_init(struct emif_data *emif)
int ret;
dentry = debugfs_create_dir(dev_name(emif->dev), NULL);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err0;
}
emif->debugfs_root = dentry;
dentry = debugfs_create_file("regcache_dump", S_IRUGO,
emif->debugfs_root, emif, &emif_regdump_fops);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err1;
}
dentry = debugfs_create_file("mr4", S_IRUGO,
emif->debugfs_root, emif, &emif_mr4_fops);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err1;
}
@@ -194,6 +201,16 @@ static void __exit emif_debugfs_exit(struct emif_data *emif)
debugfs_remove_recursive(emif->debugfs_root);
emif->debugfs_root = NULL;
}
+#else
+static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
+{
+ return 0;
+}
+
+static inline void __exit emif_debugfs_exit(struct emif_data *emif)
+{
+}
+#endif
/*
* Calculate the period of DDR clock from frequency value
@@ -240,6 +257,41 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode)
u32 temp;
void __iomem *base = emif->base;
+ /*
+ * Workaround for errata i743 - LPDDR2 Power-Down State is Not
+ * Efficient
+ *
+ * i743 DESCRIPTION:
+ * The EMIF supports power-down state for low power. The EMIF
+ * automatically puts the SDRAM into power-down after the memory is
+ * not accessed for a defined number of cycles and the
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set to 0x4.
+ * As the EMIF supports automatic output impedance calibration, a ZQ
+ * calibration long command is issued every time it exits active
+ * power-down and precharge power-down modes. The EMIF waits and
+ * blocks any other command during this calibration.
+ * The EMIF does not allow selective disabling of ZQ calibration upon
+ * exit of power-down mode. Due to very short periods of power-down
+ * cycles, ZQ calibration overhead creates bandwidth issues and
+ * increases overall system power consumption. On the other hand,
+ * issuing ZQ calibration long commands when exiting self-refresh is
+ * still required.
+ *
+ * WORKAROUND
+ * Because there is no power consumption benefit of the power-down due
+ * to the calibration and there is a performance risk, the guideline
+ * is to not allow power-down state and, therefore, to not have set
+ * the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4.
+ */
+ if ((emif->plat_data->ip_rev == EMIF_4D) &&
+ (EMIF_LP_MODE_PWR_DN == lpmode)) {
+ WARN_ONCE(1,
+ "REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by"
+ "erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n");
+ /* rollback LP_MODE to Self-refresh mode */
+ lpmode = EMIF_LP_MODE_SELF_REFRESH;
+ }
+
temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL);
temp &= ~LP_MODE_MASK;
temp |= (lpmode << LP_MODE_SHIFT);
@@ -699,6 +751,8 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
u32 timeout_perf = EMIF_LP_MODE_TIMEOUT_PERFORMANCE;
u32 timeout_pwr = EMIF_LP_MODE_TIMEOUT_POWER;
u32 freq_threshold = EMIF_LP_MODE_FREQ_THRESHOLD;
+ u32 mask;
+ u8 shift;
struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs;
@@ -712,38 +766,60 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
/* Timeout based on DDR frequency */
timeout = freq >= freq_threshold ? timeout_perf : timeout_pwr;
- /* The value to be set in register is "log2(timeout) - 3" */
+ /*
+ * The value to be set in register is "log2(timeout) - 3"
+ * if timeout < 16 load 0 in register
+ * if timeout is not a power of 2, round to next highest power of 2
+ */
if (timeout < 16) {
timeout = 0;
} else {
- timeout = __fls(timeout) - 3;
if (timeout & (timeout - 1))
- timeout++;
+ timeout <<= 1;
+ timeout = __fls(timeout) - 3;
}
switch (lpmode) {
case EMIF_LP_MODE_CLOCK_STOP:
- pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) |
- SR_TIM_MASK | PD_TIM_MASK;
+ shift = CS_TIM_SHIFT;
+ mask = CS_TIM_MASK;
break;
case EMIF_LP_MODE_SELF_REFRESH:
/* Workaround for errata i735 */
if (timeout < 6)
timeout = 6;
- pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) |
- CS_TIM_MASK | PD_TIM_MASK;
+ shift = SR_TIM_SHIFT;
+ mask = SR_TIM_MASK;
break;
case EMIF_LP_MODE_PWR_DN:
- pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) |
- CS_TIM_MASK | SR_TIM_MASK;
+ shift = PD_TIM_SHIFT;
+ mask = PD_TIM_MASK;
break;
case EMIF_LP_MODE_DISABLE:
default:
- pwr_mgmt_ctrl = CS_TIM_MASK |
- PD_TIM_MASK | SR_TIM_MASK;
+ mask = 0;
+ shift = 0;
+ break;
+ }
+ /* Round to maximum in case of overflow, BUT warn! */
+ if (lpmode != EMIF_LP_MODE_DISABLE && timeout > mask >> shift) {
+ pr_err("TIMEOUT Overflow - lpmode=%d perf=%d pwr=%d freq=%d\n",
+ lpmode,
+ timeout_perf,
+ timeout_pwr,
+ freq_threshold);
+ WARN(1, "timeout=0x%02x greater than 0x%02x. Using max\n",
+ timeout, mask >> shift);
+ timeout = mask >> shift;
}
+ /* Setup required timing */
+ pwr_mgmt_ctrl = (timeout << shift) & mask;
+ /* setup a default mask for rest of the modes */
+ pwr_mgmt_ctrl |= (SR_TIM_MASK | CS_TIM_MASK | PD_TIM_MASK) &
+ ~mask;
+
/* No CS_TIM in EMIF_4D5 */
if (ip_rev == EMIF_4D5)
pwr_mgmt_ctrl &= ~CS_TIM_MASK;
@@ -799,6 +875,8 @@ static void setup_registers(struct emif_data *emif, struct emif_regs *regs)
writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW);
writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW);
+ writel(regs->pwr_mgmt_ctrl_shdw,
+ base + EMIF_POWER_MANAGEMENT_CTRL_SHDW);
/* Settings specific for EMIF4D5 */
if (emif->plat_data->ip_rev != EMIF_4D5)
@@ -876,6 +954,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
{
u32 old_temp_level;
irqreturn_t ret = IRQ_HANDLED;
+ struct emif_custom_configs *custom_configs;
spin_lock_irqsave(&emif_lock, irq_state);
old_temp_level = emif->temperature_level;
@@ -888,6 +967,29 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
goto out;
}
+ custom_configs = emif->plat_data->custom_configs;
+
+ /*
+ * IF we detect higher than "nominal rating" from DDR sensor
+ * on an unsupported DDR part, shutdown system
+ */
+ if (custom_configs && !(custom_configs->mask &
+ EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) {
+ if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) {
+ dev_err(emif->dev,
+ "%s:NOT Extended temperature capable memory."
+ "Converting MR4=0x%02x as shutdown event\n",
+ __func__, emif->temperature_level);
+ /*
+ * Temperature far too high - do kernel_power_off()
+ * from thread context
+ */
+ emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN;
+ ret = IRQ_WAKE_THREAD;
+ goto out;
+ }
+ }
+
if (emif->temperature_level < old_temp_level ||
emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
/*
@@ -949,7 +1051,14 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
- kernel_power_off();
+
+ /* If we have Power OFF ability, use it, else try restarting */
+ if (pm_power_off) {
+ kernel_power_off();
+ } else {
+ WARN(1, "FIXME: NO pm_power_off!!! trying restart\n");
+ kernel_restart("SDRAM Over-temp Emergency restart");
+ }
return IRQ_HANDLED;
}
@@ -1148,6 +1257,172 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
return valid;
}
+#if defined(CONFIG_OF)
+static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
+ struct emif_data *emif)
+{
+ struct emif_custom_configs *cust_cfgs = NULL;
+ int len;
+ const __be32 *lpmode, *poll_intvl;
+
+ lpmode = of_get_property(np_emif, "low-power-mode", &len);
+ poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len);
+
+ if (lpmode || poll_intvl)
+ cust_cfgs = devm_kzalloc(emif->dev, sizeof(*cust_cfgs),
+ GFP_KERNEL);
+
+ if (!cust_cfgs)
+ return;
+
+ if (lpmode) {
+ cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE;
+ cust_cfgs->lpmode = be32_to_cpup(lpmode);
+ of_property_read_u32(np_emif,
+ "low-power-mode-timeout-performance",
+ &cust_cfgs->lpmode_timeout_performance);
+ of_property_read_u32(np_emif,
+ "low-power-mode-timeout-power",
+ &cust_cfgs->lpmode_timeout_power);
+ of_property_read_u32(np_emif,
+ "low-power-mode-freq-threshold",
+ &cust_cfgs->lpmode_freq_threshold);
+ }
+
+ if (poll_intvl) {
+ cust_cfgs->mask |=
+ EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL;
+ cust_cfgs->temp_alert_poll_interval_ms =
+ be32_to_cpup(poll_intvl);
+ }
+
+ if (of_find_property(np_emif, "extended-temp-part", &len))
+ cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART;
+
+ if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
+ devm_kfree(emif->dev, cust_cfgs);
+ return;
+ }
+
+ emif->plat_data->custom_configs = cust_cfgs;
+}
+
+static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
+ struct device_node *np_ddr,
+ struct ddr_device_info *dev_info)
+{
+ u32 density = 0, io_width = 0;
+ int len;
+
+ if (of_find_property(np_emif, "cs1-used", &len))
+ dev_info->cs1_used = true;
+
+ if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
+ dev_info->cal_resistors_per_cs = true;
+
+ if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s4"))
+ dev_info->type = DDR_TYPE_LPDDR2_S4;
+ else if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s2"))
+ dev_info->type = DDR_TYPE_LPDDR2_S2;
+
+ of_property_read_u32(np_ddr, "density", &density);
+ of_property_read_u32(np_ddr, "io-width", &io_width);
+
+ /* Convert from density in Mb to the density encoding in jedc_ddr.h */
+ if (density & (density - 1))
+ dev_info->density = 0;
+ else
+ dev_info->density = __fls(density) - 5;
+
+ /* Convert from io_width in bits to io_width encoding in jedc_ddr.h */
+ if (io_width & (io_width - 1))
+ dev_info->io_width = 0;
+ else
+ dev_info->io_width = __fls(io_width) - 1;
+}
+
+static struct emif_data * __init_or_module of_get_memory_device_details(
+ struct device_node *np_emif, struct device *dev)
+{
+ struct emif_data *emif = NULL;
+ struct ddr_device_info *dev_info = NULL;
+ struct emif_platform_data *pd = NULL;
+ struct device_node *np_ddr;
+ int len;
+
+ np_ddr = of_parse_phandle(np_emif, "device-handle", 0);
+ if (!np_ddr)
+ goto error;
+ emif = devm_kzalloc(dev, sizeof(struct emif_data), GFP_KERNEL);
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
+
+ if (!emif || !pd || !dev_info) {
+ dev_err(dev, "%s: Out of memory!!\n",
+ __func__);
+ goto error;
+ }
+
+ emif->plat_data = pd;
+ pd->device_info = dev_info;
+ emif->dev = dev;
+ emif->np_ddr = np_ddr;
+ emif->temperature_level = SDRAM_TEMP_NOMINAL;
+
+ if (of_device_is_compatible(np_emif, "ti,emif-4d"))
+ emif->plat_data->ip_rev = EMIF_4D;
+ else if (of_device_is_compatible(np_emif, "ti,emif-4d5"))
+ emif->plat_data->ip_rev = EMIF_4D5;
+
+ of_property_read_u32(np_emif, "phy-type", &pd->phy_type);
+
+ if (of_find_property(np_emif, "hw-caps-ll-interface", &len))
+ pd->hw_caps |= EMIF_HW_CAPS_LL_INTERFACE;
+
+ of_get_ddr_info(np_emif, np_ddr, dev_info);
+ if (!is_dev_data_valid(pd->device_info->type, pd->device_info->density,
+ pd->device_info->io_width, pd->phy_type, pd->ip_rev,
+ emif->dev)) {
+ dev_err(dev, "%s: invalid device data!!\n", __func__);
+ goto error;
+ }
+ /*
+ * For EMIF instances other than EMIF1 see if the devices connected
+ * are exactly same as on EMIF1(which is typically the case). If so,
+ * mark it as a duplicate of EMIF1. This will save some memory and
+ * computation.
+ */
+ if (emif1 && emif1->np_ddr == np_ddr) {
+ emif->duplicate = true;
+ goto out;
+ } else if (emif1) {
+ dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n",
+ __func__);
+ }
+
+ of_get_custom_configs(np_emif, emif);
+ emif->plat_data->timings = of_get_ddr_timings(np_ddr, emif->dev,
+ emif->plat_data->device_info->type,
+ &emif->plat_data->timings_arr_size);
+
+ emif->plat_data->min_tck = of_get_min_tck(np_ddr, emif->dev);
+ goto out;
+
+error:
+ return NULL;
+out:
+ return emif;
+}
+
+#else
+
+static struct emif_data * __init_or_module of_get_memory_device_details(
+ struct device_node *np_emif, struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static struct emif_data *__init_or_module get_device_details(
struct platform_device *pdev)
{
@@ -1229,7 +1504,7 @@ static struct emif_data *__init_or_module get_device_details(
if (pd->timings) {
temp = devm_kzalloc(dev, size, GFP_KERNEL);
if (temp) {
- memcpy(temp, pd->timings, sizeof(*pd->timings));
+ memcpy(temp, pd->timings, size);
pd->timings = temp;
} else {
dev_warn(dev, "%s:%d: allocation error\n", __func__,
@@ -1267,7 +1542,11 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
struct resource *res;
int irq;
- emif = get_device_details(pdev);
+ if (pdev->dev.of_node)
+ emif = of_get_memory_device_details(pdev->dev.of_node, &pdev->dev);
+ else
+ emif = get_device_details(pdev);
+
if (!emif) {
pr_err("%s: error getting device data\n", __func__);
goto error;
@@ -1281,18 +1560,9 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, emif);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(emif->dev, "%s: error getting memory resource\n",
- __func__);
+ emif->base = devm_ioremap_resource(emif->dev, res);
+ if (IS_ERR(emif->base))
goto error;
- }
-
- emif->base = devm_request_and_ioremap(emif->dev, res);
- if (!emif->base) {
- dev_err(emif->dev, "%s: devm_request_and_ioremap() failed\n",
- __func__);
- goto error;
- }
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -1644,26 +1914,26 @@ static void __attribute__((unused)) freq_post_notify_handling(void)
spin_unlock_irqrestore(&emif_lock, irq_state);
}
+#if defined(CONFIG_OF)
+static const struct of_device_id emif_of_match[] = {
+ { .compatible = "ti,emif-4d" },
+ { .compatible = "ti,emif-4d5" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, emif_of_match);
+#endif
+
static struct platform_driver emif_driver = {
.remove = __exit_p(emif_remove),
.shutdown = emif_shutdown,
.driver = {
.name = "emif",
+ .of_match_table = of_match_ptr(emif_of_match),
},
};
-static int __init_or_module emif_register(void)
-{
- return platform_driver_probe(&emif_driver, emif_probe);
-}
-
-static void __exit emif_unregister(void)
-{
- platform_driver_unregister(&emif_driver);
-}
+module_platform_driver_probe(emif_driver, emif_probe);
-module_init(emif_register);
-module_exit(emif_unregister);
MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:emif");
diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c
new file mode 100644
index 00000000000..3d5d792d5cb
--- /dev/null
+++ b/drivers/memory/fsl_ifc.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc
+ *
+ * Freescale Integrated Flash Controller
+ *
+ * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_ifc.h>
+#include <asm/prom.h>
+
+struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
+EXPORT_SYMBOL(fsl_ifc_ctrl_dev);
+
+/*
+ * convert_ifc_address - convert the base address
+ * @addr_base: base address of the memory bank
+ */
+unsigned int convert_ifc_address(phys_addr_t addr_base)
+{
+ return addr_base & CSPR_BA;
+}
+EXPORT_SYMBOL(convert_ifc_address);
+
+/*
+ * fsl_ifc_find - find IFC bank
+ * @addr_base: base address of the memory bank
+ *
+ * This function walks IFC banks comparing "Base address" field of the CSPR
+ * registers with the supplied addr_base argument. When bases match this
+ * function returns bank number (starting with 0), otherwise it returns
+ * appropriate errno value.
+ */
+int fsl_ifc_find(phys_addr_t addr_base)
+{
+ int i = 0;
+
+ if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) {
+ u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
+ if (cspr & CSPR_V && (cspr & CSPR_BA) ==
+ convert_ifc_address(addr_base))
+ return i;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(fsl_ifc_find);
+
+static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
+{
+ struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+
+ /*
+ * Clear all the common status and event registers
+ */
+ if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
+ out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
+
+ /* enable all error and events */
+ out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN);
+
+ /* enable all error and event interrupts */
+ out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN);
+ out_be32(&ifc->cm_erattr0, 0x0);
+ out_be32(&ifc->cm_erattr1, 0x0);
+
+ return 0;
+}
+
+static int fsl_ifc_ctrl_remove(struct platform_device *dev)
+{
+ struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(&dev->dev);
+
+ free_irq(ctrl->nand_irq, ctrl);
+ free_irq(ctrl->irq, ctrl);
+
+ irq_dispose_mapping(ctrl->nand_irq);
+ irq_dispose_mapping(ctrl->irq);
+
+ iounmap(ctrl->regs);
+
+ dev_set_drvdata(&dev->dev, NULL);
+ kfree(ctrl);
+
+ return 0;
+}
+
+/*
+ * NAND events are split between an operational interrupt which only
+ * receives OPC, and an error interrupt that receives everything else,
+ * including non-NAND errors. Whichever interrupt gets to it first
+ * records the status and wakes the wait queue.
+ */
+static DEFINE_SPINLOCK(nand_irq_lock);
+
+static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
+{
+ struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+ unsigned long flags;
+ u32 stat;
+
+ spin_lock_irqsave(&nand_irq_lock, flags);
+
+ stat = in_be32(&ifc->ifc_nand.nand_evter_stat);
+ if (stat) {
+ out_be32(&ifc->ifc_nand.nand_evter_stat, stat);
+ ctrl->nand_stat = stat;
+ wake_up(&ctrl->nand_wait);
+ }
+
+ spin_unlock_irqrestore(&nand_irq_lock, flags);
+
+ return stat;
+}
+
+static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data)
+{
+ struct fsl_ifc_ctrl *ctrl = data;
+
+ if (check_nand_stat(ctrl))
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+/*
+ * NOTE: This interrupt is used to report ifc events of various kinds,
+ * such as transaction errors on the chipselects.
+ */
+static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
+{
+ struct fsl_ifc_ctrl *ctrl = data;
+ struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+ u32 err_axiid, err_srcid, status, cs_err, err_addr;
+ irqreturn_t ret = IRQ_NONE;
+
+ /* read for chip select error */
+ cs_err = in_be32(&ifc->cm_evter_stat);
+ if (cs_err) {
+ dev_err(ctrl->dev, "transaction sent to IFC is not mapped to"
+ "any memory bank 0x%08X\n", cs_err);
+ /* clear the chip select error */
+ out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
+
+ /* read error attribute registers print the error information */
+ status = in_be32(&ifc->cm_erattr0);
+ err_addr = in_be32(&ifc->cm_erattr1);
+
+ if (status & IFC_CM_ERATTR0_ERTYP_READ)
+ dev_err(ctrl->dev, "Read transaction error"
+ "CM_ERATTR0 0x%08X\n", status);
+ else
+ dev_err(ctrl->dev, "Write transaction error"
+ "CM_ERATTR0 0x%08X\n", status);
+
+ err_axiid = (status & IFC_CM_ERATTR0_ERAID) >>
+ IFC_CM_ERATTR0_ERAID_SHIFT;
+ dev_err(ctrl->dev, "AXI ID of the error"
+ "transaction 0x%08X\n", err_axiid);
+
+ err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >>
+ IFC_CM_ERATTR0_ESRCID_SHIFT;
+ dev_err(ctrl->dev, "SRC ID of the error"
+ "transaction 0x%08X\n", err_srcid);
+
+ dev_err(ctrl->dev, "Transaction Address corresponding to error"
+ "ERADDR 0x%08X\n", err_addr);
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (check_nand_stat(ctrl))
+ ret = IRQ_HANDLED;
+
+ return ret;
+}
+
+/*
+ * fsl_ifc_ctrl_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code allocates all of
+ * the resources needed for the controller only. The
+ * resources for the NAND banks themselves are allocated
+ * in the chip probe function.
+*/
+static int fsl_ifc_ctrl_probe(struct platform_device *dev)
+{
+ int ret = 0;
+
+
+ dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
+
+ fsl_ifc_ctrl_dev = kzalloc(sizeof(*fsl_ifc_ctrl_dev), GFP_KERNEL);
+ if (!fsl_ifc_ctrl_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
+
+ /* IOMAP the entire IFC region */
+ fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
+ if (!fsl_ifc_ctrl_dev->regs) {
+ dev_err(&dev->dev, "failed to get memory region\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* get the Controller level irq */
+ fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
+ if (fsl_ifc_ctrl_dev->irq == NO_IRQ) {
+ dev_err(&dev->dev, "failed to get irq resource "
+ "for IFC\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* get the nand machine irq */
+ fsl_ifc_ctrl_dev->nand_irq =
+ irq_of_parse_and_map(dev->dev.of_node, 1);
+
+ fsl_ifc_ctrl_dev->dev = &dev->dev;
+
+ ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev);
+ if (ret < 0)
+ goto err;
+
+ init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait);
+
+ ret = request_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_irq, IRQF_SHARED,
+ "fsl-ifc", fsl_ifc_ctrl_dev);
+ if (ret != 0) {
+ dev_err(&dev->dev, "failed to install irq (%d)\n",
+ fsl_ifc_ctrl_dev->irq);
+ goto err_irq;
+ }
+
+ if (fsl_ifc_ctrl_dev->nand_irq) {
+ ret = request_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_nand_irq,
+ 0, "fsl-ifc-nand", fsl_ifc_ctrl_dev);
+ if (ret != 0) {
+ dev_err(&dev->dev, "failed to install irq (%d)\n",
+ fsl_ifc_ctrl_dev->nand_irq);
+ goto err_nandirq;
+ }
+ }
+
+ return 0;
+
+err_nandirq:
+ free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev);
+ irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
+err_irq:
+ free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev);
+ irq_dispose_mapping(fsl_ifc_ctrl_dev->irq);
+err:
+ return ret;
+}
+
+static const struct of_device_id fsl_ifc_match[] = {
+ {
+ .compatible = "fsl,ifc",
+ },
+ {},
+};
+
+static struct platform_driver fsl_ifc_ctrl_driver = {
+ .driver = {
+ .name = "fsl-ifc",
+ .of_match_table = fsl_ifc_match,
+ },
+ .probe = fsl_ifc_ctrl_probe,
+ .remove = fsl_ifc_ctrl_remove,
+};
+
+static int __init fsl_ifc_init(void)
+{
+ return platform_driver_register(&fsl_ifc_ctrl_driver);
+}
+subsys_initcall(fsl_ifc_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Freescale Integrated Flash Controller driver");
diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c
new file mode 100644
index 00000000000..ff7138fd66d
--- /dev/null
+++ b/drivers/memory/mvebu-devbus.c
@@ -0,0 +1,362 @@
+/*
+ * Marvell EBU SoC Device Bus Controller
+ * (memory controller for NOR/NAND/SRAM/FPGA devices)
+ *
+ * Copyright (C) 2013-2014 Marvell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mbus.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+/* Register definitions */
+#define ARMADA_DEV_WIDTH_SHIFT 30
+#define ARMADA_BADR_SKEW_SHIFT 28
+#define ARMADA_RD_HOLD_SHIFT 23
+#define ARMADA_ACC_NEXT_SHIFT 17
+#define ARMADA_RD_SETUP_SHIFT 12
+#define ARMADA_ACC_FIRST_SHIFT 6
+
+#define ARMADA_SYNC_ENABLE_SHIFT 24
+#define ARMADA_WR_HIGH_SHIFT 16
+#define ARMADA_WR_LOW_SHIFT 8
+
+#define ARMADA_READ_PARAM_OFFSET 0x0
+#define ARMADA_WRITE_PARAM_OFFSET 0x4
+
+#define ORION_RESERVED (0x2 << 30)
+#define ORION_BADR_SKEW_SHIFT 28
+#define ORION_WR_HIGH_EXT_BIT BIT(27)
+#define ORION_WR_HIGH_EXT_MASK 0x8
+#define ORION_WR_LOW_EXT_BIT BIT(26)
+#define ORION_WR_LOW_EXT_MASK 0x8
+#define ORION_ALE_WR_EXT_BIT BIT(25)
+#define ORION_ALE_WR_EXT_MASK 0x8
+#define ORION_ACC_NEXT_EXT_BIT BIT(24)
+#define ORION_ACC_NEXT_EXT_MASK 0x10
+#define ORION_ACC_FIRST_EXT_BIT BIT(23)
+#define ORION_ACC_FIRST_EXT_MASK 0x10
+#define ORION_TURN_OFF_EXT_BIT BIT(22)
+#define ORION_TURN_OFF_EXT_MASK 0x8
+#define ORION_DEV_WIDTH_SHIFT 20
+#define ORION_WR_HIGH_SHIFT 17
+#define ORION_WR_HIGH_MASK 0x7
+#define ORION_WR_LOW_SHIFT 14
+#define ORION_WR_LOW_MASK 0x7
+#define ORION_ALE_WR_SHIFT 11
+#define ORION_ALE_WR_MASK 0x7
+#define ORION_ACC_NEXT_SHIFT 7
+#define ORION_ACC_NEXT_MASK 0xF
+#define ORION_ACC_FIRST_SHIFT 3
+#define ORION_ACC_FIRST_MASK 0xF
+#define ORION_TURN_OFF_SHIFT 0
+#define ORION_TURN_OFF_MASK 0x7
+
+struct devbus_read_params {
+ u32 bus_width;
+ u32 badr_skew;
+ u32 turn_off;
+ u32 acc_first;
+ u32 acc_next;
+ u32 rd_setup;
+ u32 rd_hold;
+};
+
+struct devbus_write_params {
+ u32 sync_enable;
+ u32 wr_high;
+ u32 wr_low;
+ u32 ale_wr;
+};
+
+struct devbus {
+ struct device *dev;
+ void __iomem *base;
+ unsigned long tick_ps;
+};
+
+static int get_timing_param_ps(struct devbus *devbus,
+ struct device_node *node,
+ const char *name,
+ u32 *ticks)
+{
+ u32 time_ps;
+ int err;
+
+ err = of_property_read_u32(node, name, &time_ps);
+ if (err < 0) {
+ dev_err(devbus->dev, "%s has no '%s' property\n",
+ name, node->full_name);
+ return err;
+ }
+
+ *ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps;
+
+ dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n",
+ name, time_ps, *ticks);
+ return 0;
+}
+
+static int devbus_get_timing_params(struct devbus *devbus,
+ struct device_node *node,
+ struct devbus_read_params *r,
+ struct devbus_write_params *w)
+{
+ int err;
+
+ err = of_property_read_u32(node, "devbus,bus-width", &r->bus_width);
+ if (err < 0) {
+ dev_err(devbus->dev,
+ "%s has no 'devbus,bus-width' property\n",
+ node->full_name);
+ return err;
+ }
+
+ /*
+ * The bus width is encoded into the register as 0 for 8 bits,
+ * and 1 for 16 bits, so we do the necessary conversion here.
+ */
+ if (r->bus_width == 8)
+ r->bus_width = 0;
+ else if (r->bus_width == 16)
+ r->bus_width = 1;
+ else {
+ dev_err(devbus->dev, "invalid bus width %d\n", r->bus_width);
+ return -EINVAL;
+ }
+
+ err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
+ &r->badr_skew);
+ if (err < 0)
+ return err;
+
+ err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps",
+ &r->turn_off);
+ if (err < 0)
+ return err;
+
+ err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps",
+ &r->acc_first);
+ if (err < 0)
+ return err;
+
+ err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps",
+ &r->acc_next);
+ if (err < 0)
+ return err;
+
+ if (of_device_is_compatible(devbus->dev->of_node, "marvell,mvebu-devbus")) {
+ err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps",
+ &r->rd_setup);
+ if (err < 0)
+ return err;
+
+ err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps",
+ &r->rd_hold);
+ if (err < 0)
+ return err;
+
+ err = of_property_read_u32(node, "devbus,sync-enable",
+ &w->sync_enable);
+ if (err < 0) {
+ dev_err(devbus->dev,
+ "%s has no 'devbus,sync-enable' property\n",
+ node->full_name);
+ return err;
+ }
+ }
+
+ err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps",
+ &w->ale_wr);
+ if (err < 0)
+ return err;
+
+ err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps",
+ &w->wr_low);
+ if (err < 0)
+ return err;
+
+ err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps",
+ &w->wr_high);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void devbus_orion_set_timing_params(struct devbus *devbus,
+ struct device_node *node,
+ struct devbus_read_params *r,
+ struct devbus_write_params *w)
+{
+ u32 value;
+
+ /*
+ * The hardware designers found it would be a good idea to
+ * split most of the values in the register into two fields:
+ * one containing all the low-order bits, and another one
+ * containing just the high-order bit. For all of those
+ * fields, we have to split the value into these two parts.
+ */
+ value = (r->turn_off & ORION_TURN_OFF_MASK) << ORION_TURN_OFF_SHIFT |
+ (r->acc_first & ORION_ACC_FIRST_MASK) << ORION_ACC_FIRST_SHIFT |
+ (r->acc_next & ORION_ACC_NEXT_MASK) << ORION_ACC_NEXT_SHIFT |
+ (w->ale_wr & ORION_ALE_WR_MASK) << ORION_ALE_WR_SHIFT |
+ (w->wr_low & ORION_WR_LOW_MASK) << ORION_WR_LOW_SHIFT |
+ (w->wr_high & ORION_WR_HIGH_MASK) << ORION_WR_HIGH_SHIFT |
+ r->bus_width << ORION_DEV_WIDTH_SHIFT |
+ ((r->turn_off & ORION_TURN_OFF_EXT_MASK) ? ORION_TURN_OFF_EXT_BIT : 0) |
+ ((r->acc_first & ORION_ACC_FIRST_EXT_MASK) ? ORION_ACC_FIRST_EXT_BIT : 0) |
+ ((r->acc_next & ORION_ACC_NEXT_EXT_MASK) ? ORION_ACC_NEXT_EXT_BIT : 0) |
+ ((w->ale_wr & ORION_ALE_WR_EXT_MASK) ? ORION_ALE_WR_EXT_BIT : 0) |
+ ((w->wr_low & ORION_WR_LOW_EXT_MASK) ? ORION_WR_LOW_EXT_BIT : 0) |
+ ((w->wr_high & ORION_WR_HIGH_EXT_MASK) ? ORION_WR_HIGH_EXT_BIT : 0) |
+ (r->badr_skew << ORION_BADR_SKEW_SHIFT) |
+ ORION_RESERVED;
+
+ writel(value, devbus->base);
+}
+
+static void devbus_armada_set_timing_params(struct devbus *devbus,
+ struct device_node *node,
+ struct devbus_read_params *r,
+ struct devbus_write_params *w)
+{
+ u32 value;
+
+ /* Set read timings */
+ value = r->bus_width << ARMADA_DEV_WIDTH_SHIFT |
+ r->badr_skew << ARMADA_BADR_SKEW_SHIFT |
+ r->rd_hold << ARMADA_RD_HOLD_SHIFT |
+ r->acc_next << ARMADA_ACC_NEXT_SHIFT |
+ r->rd_setup << ARMADA_RD_SETUP_SHIFT |
+ r->acc_first << ARMADA_ACC_FIRST_SHIFT |
+ r->turn_off;
+
+ dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n",
+ devbus->base + ARMADA_READ_PARAM_OFFSET,
+ value);
+
+ writel(value, devbus->base + ARMADA_READ_PARAM_OFFSET);
+
+ /* Set write timings */
+ value = w->sync_enable << ARMADA_SYNC_ENABLE_SHIFT |
+ w->wr_low << ARMADA_WR_LOW_SHIFT |
+ w->wr_high << ARMADA_WR_HIGH_SHIFT |
+ w->ale_wr;
+
+ dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n",
+ devbus->base + ARMADA_WRITE_PARAM_OFFSET,
+ value);
+
+ writel(value, devbus->base + ARMADA_WRITE_PARAM_OFFSET);
+}
+
+static int mvebu_devbus_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct devbus_read_params r;
+ struct devbus_write_params w;
+ struct devbus *devbus;
+ struct resource *res;
+ struct clk *clk;
+ unsigned long rate;
+ int err;
+
+ devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL);
+ if (!devbus)
+ return -ENOMEM;
+
+ devbus->dev = dev;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ devbus->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(devbus->base))
+ return PTR_ERR(devbus->base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ clk_prepare_enable(clk);
+
+ /*
+ * Obtain clock period in picoseconds,
+ * we need this in order to convert timing
+ * parameters from cycles to picoseconds.
+ */
+ rate = clk_get_rate(clk) / 1000;
+ devbus->tick_ps = 1000000000 / rate;
+
+ dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n",
+ devbus->tick_ps);
+
+ if (!of_property_read_bool(node, "devbus,keep-config")) {
+ /* Read the Device Tree node */
+ err = devbus_get_timing_params(devbus, node, &r, &w);
+ if (err < 0)
+ return err;
+
+ /* Set the new timing parameters */
+ if (of_device_is_compatible(node, "marvell,orion-devbus"))
+ devbus_orion_set_timing_params(devbus, node, &r, &w);
+ else
+ devbus_armada_set_timing_params(devbus, node, &r, &w);
+ }
+
+ /*
+ * We need to create a child device explicitly from here to
+ * guarantee that the child will be probed after the timing
+ * parameters for the bus are written.
+ */
+ err = of_platform_populate(node, NULL, NULL, dev);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static const struct of_device_id mvebu_devbus_of_match[] = {
+ { .compatible = "marvell,mvebu-devbus" },
+ { .compatible = "marvell,orion-devbus" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match);
+
+static struct platform_driver mvebu_devbus_driver = {
+ .probe = mvebu_devbus_probe,
+ .driver = {
+ .name = "mvebu-devbus",
+ .owner = THIS_MODULE,
+ .of_match_table = mvebu_devbus_of_match,
+ },
+};
+
+static int __init mvebu_devbus_init(void)
+{
+ return platform_driver_register(&mvebu_devbus_driver);
+}
+module_init(mvebu_devbus_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller");
diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c
new file mode 100644
index 00000000000..60074351f17
--- /dev/null
+++ b/drivers/memory/of_memory.c
@@ -0,0 +1,153 @@
+/*
+ * OpenFirmware helpers for memory drivers
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/gfp.h>
+#include <memory/jedec_ddr.h>
+#include <linux/export.h>
+
+/**
+ * of_get_min_tck() - extract min timing values for ddr
+ * @np: pointer to ddr device tree node
+ * @device: device requesting for min timing values
+ *
+ * Populates the lpddr2_min_tck structure by extracting data
+ * from device tree node. Returns a pointer to the populated
+ * structure. If any error in populating the structure, returns
+ * default min timings provided by JEDEC.
+ */
+const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
+ struct device *dev)
+{
+ int ret = 0;
+ struct lpddr2_min_tck *min;
+
+ min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
+ if (!min)
+ goto default_min_tck;
+
+ ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
+ ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
+ ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
+ ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
+ ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
+ ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
+ ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
+ ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
+ ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
+ ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
+ ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
+
+ if (ret) {
+ devm_kfree(dev, min);
+ goto default_min_tck;
+ }
+
+ return min;
+
+default_min_tck:
+ dev_warn(dev, "%s: using default min-tck values\n", __func__);
+ return &lpddr2_jedec_min_tck;
+}
+EXPORT_SYMBOL(of_get_min_tck);
+
+static int of_do_get_timings(struct device_node *np,
+ struct lpddr2_timings *tim)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
+ ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
+ ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
+ ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
+ ret |= of_property_read_u32(np, "tWR", &tim->tWR);
+ ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
+ ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
+ ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
+ ret |= of_property_read_u32(np, "tXP", &tim->tXP);
+ ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
+ ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
+ ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
+ ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
+ ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
+ ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
+ ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
+ ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
+ ret |= of_property_read_u32(np, "tDQSCK-max-derated",
+ &tim->tDQSCK_max_derated);
+
+ return ret;
+}
+
+/**
+ * of_get_ddr_timings() - extracts the ddr timings and updates no of
+ * frequencies available.
+ * @np_ddr: Pointer to ddr device tree node
+ * @dev: Device requesting for ddr timings
+ * @device_type: Type of ddr(LPDDR2 S2/S4)
+ * @nr_frequencies: No of frequencies available for ddr
+ * (updated by this function)
+ *
+ * Populates lpddr2_timings structure by extracting data from device
+ * tree node. Returns pointer to populated structure. If any error
+ * while populating, returns default timings provided by JEDEC.
+ */
+const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
+ struct device *dev, u32 device_type, u32 *nr_frequencies)
+{
+ struct lpddr2_timings *timings = NULL;
+ u32 arr_sz = 0, i = 0;
+ struct device_node *np_tim;
+ char *tim_compat;
+
+ switch (device_type) {
+ case DDR_TYPE_LPDDR2_S2:
+ case DDR_TYPE_LPDDR2_S4:
+ tim_compat = "jedec,lpddr2-timings";
+ break;
+ default:
+ dev_warn(dev, "%s: un-supported memory type\n", __func__);
+ }
+
+ for_each_child_of_node(np_ddr, np_tim)
+ if (of_device_is_compatible(np_tim, tim_compat))
+ arr_sz++;
+
+ if (arr_sz)
+ timings = devm_kzalloc(dev, sizeof(*timings) * arr_sz,
+ GFP_KERNEL);
+
+ if (!timings)
+ goto default_timings;
+
+ for_each_child_of_node(np_ddr, np_tim) {
+ if (of_device_is_compatible(np_tim, tim_compat)) {
+ if (of_do_get_timings(np_tim, &timings[i])) {
+ devm_kfree(dev, timings);
+ goto default_timings;
+ }
+ i++;
+ }
+ }
+
+ *nr_frequencies = arr_sz;
+
+ return timings;
+
+default_timings:
+ dev_warn(dev, "%s: using default timings\n", __func__);
+ *nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
+ return lpddr2_jedec_timings;
+}
+EXPORT_SYMBOL(of_get_ddr_timings);
diff --git a/drivers/memory/of_memory.h b/drivers/memory/of_memory.h
new file mode 100644
index 00000000000..ef2514f553d
--- /dev/null
+++ b/drivers/memory/of_memory.h
@@ -0,0 +1,36 @@
+/*
+ * OpenFirmware helpers for memory drivers
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_MEMORY_OF_REG_H
+#define __LINUX_MEMORY_OF_REG_H
+
+#if defined(CONFIG_OF) && defined(CONFIG_DDR)
+extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
+ struct device *dev);
+extern const struct lpddr2_timings
+ *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
+ u32 device_type, u32 *nr_frequencies);
+#else
+static inline const struct lpddr2_min_tck
+ *of_get_min_tck(struct device_node *np, struct device *dev)
+{
+ return NULL;
+}
+
+static inline const struct lpddr2_timings
+ *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
+ u32 device_type, u32 *nr_frequencies)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF && CONFIG_DDR */
+
+#endif /* __LINUX_MEMORY_OF_REG_ */
diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c
index 3ed49c1c2b9..7cd82b874ab 100644
--- a/drivers/memory/tegra20-mc.c
+++ b/drivers/memory/tegra20-mc.c
@@ -17,6 +17,7 @@
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ratelimit.h>
@@ -57,7 +58,7 @@ static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
if (offs < 0x24)
val = readl(mc->regs[0] + offs);
- if (offs < 0x400)
+ else if (offs < 0x400)
val = readl(mc->regs[1] + offs - 0x3c);
return val;
@@ -65,14 +66,10 @@ static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
{
- if (offs < 0x24) {
+ if (offs < 0x24)
writel(val, mc->regs[0] + offs);
- return;
- }
- if (offs < 0x400) {
+ else if (offs < 0x400)
writel(val, mc->regs[1] + offs - 0x3c);
- return;
- }
}
static const char * const tegra20_mc_client[] = {
@@ -181,7 +178,7 @@ static void tegra20_mc_decode(struct tegra20_mc *mc, int n)
"carveout" : "trustzone") : "");
}
-static const struct of_device_id tegra20_mc_of_match[] __devinitconst = {
+static const struct of_device_id tegra20_mc_of_match[] = {
{ .compatible = "nvidia,tegra20-mc", },
{},
};
@@ -196,13 +193,16 @@ static irqreturn_t tegra20_mc_isr(int irq, void *data)
mask &= stat;
if (!mask)
return IRQ_NONE;
- while ((bit = ffs(mask)) != 0)
+ while ((bit = ffs(mask)) != 0) {
tegra20_mc_decode(mc, bit - 1);
+ mask &= ~BIT(bit - 1);
+ }
+
mc_writel(mc, stat, MC_INTSTATUS);
return IRQ_HANDLED;
}
-static int __devinit tegra20_mc_probe(struct platform_device *pdev)
+static int tegra20_mc_probe(struct platform_device *pdev)
{
struct resource *irq;
struct tegra20_mc *mc;
@@ -218,11 +218,9 @@ static int __devinit tegra20_mc_probe(struct platform_device *pdev)
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- if (!res)
- return -ENODEV;
- mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
- if (!mc->regs[i])
- return -EBUSY;
+ mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mc->regs[i]))
+ return PTR_ERR(mc->regs[i]);
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c
index e56ff04eb5c..ef7934535fd 100644
--- a/drivers/memory/tegra30-mc.c
+++ b/drivers/memory/tegra30-mc.c
@@ -17,6 +17,7 @@
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ratelimit.h>
@@ -95,11 +96,11 @@ static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
if (offs < 0x10)
val = readl(mc->regs[0] + offs);
- if (offs < 0x1f0)
+ else if (offs < 0x1f0)
val = readl(mc->regs[1] + offs - 0x3c);
- if (offs < 0x228)
+ else if (offs < 0x228)
val = readl(mc->regs[2] + offs - 0x200);
- if (offs < 0x400)
+ else if (offs < 0x400)
val = readl(mc->regs[3] + offs - 0x284);
return val;
@@ -107,22 +108,14 @@ static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
{
- if (offs < 0x10) {
+ if (offs < 0x10)
writel(val, mc->regs[0] + offs);
- return;
- }
- if (offs < 0x1f0) {
+ else if (offs < 0x1f0)
writel(val, mc->regs[1] + offs - 0x3c);
- return;
- }
- if (offs < 0x228) {
+ else if (offs < 0x228)
writel(val, mc->regs[2] + offs - 0x200);
- return;
- }
- if (offs < 0x400) {
+ else if (offs < 0x400)
writel(val, mc->regs[3] + offs - 0x284);
- return;
- }
}
static const char * const tegra30_mc_client[] = {
@@ -225,7 +218,7 @@ static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
return;
}
- err = readl(mc + MC_ERR_STATUS);
+ err = mc_readl(mc, MC_ERR_STATUS);
type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >>
@@ -242,7 +235,7 @@ static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
if (cid < ARRAY_SIZE(tegra30_mc_client))
client = tegra30_mc_client[cid];
- addr = readl(mc + MC_ERR_ADR);
+ addr = mc_readl(mc, MC_ERR_ADR);
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n",
mc_int_err[idx], err, addr, client,
@@ -275,6 +268,7 @@ static const u32 tegra30_mc_ctx[] = {
MC_INTMASK,
};
+#ifdef CONFIG_PM
static int tegra30_mc_suspend(struct device *dev)
{
int i;
@@ -298,12 +292,13 @@ static int tegra30_mc_resume(struct device *dev)
mc_readl(mc, MC_TIMING_CONTROL);
return 0;
}
+#endif
static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,
tegra30_mc_suspend,
tegra30_mc_resume, NULL);
-static const struct of_device_id tegra30_mc_of_match[] __devinitconst = {
+static const struct of_device_id tegra30_mc_of_match[] = {
{ .compatible = "nvidia,tegra30-mc", },
{},
};
@@ -318,13 +313,16 @@ static irqreturn_t tegra30_mc_isr(int irq, void *data)
mask &= stat;
if (!mask)
return IRQ_NONE;
- while ((bit = ffs(mask)) != 0)
+ while ((bit = ffs(mask)) != 0) {
tegra30_mc_decode(mc, bit - 1);
+ mask &= ~BIT(bit - 1);
+ }
+
mc_writel(mc, stat, MC_INTSTATUS);
return IRQ_HANDLED;
}
-static int __devinit tegra30_mc_probe(struct platform_device *pdev)
+static int tegra30_mc_probe(struct platform_device *pdev)
{
struct resource *irq;
struct tegra30_mc *mc;
@@ -342,11 +340,9 @@ static int __devinit tegra30_mc_probe(struct platform_device *pdev)
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- if (!res)
- return -ENODEV;
- mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
- if (!mc->regs[i])
- return -EBUSY;
+ mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mc->regs[i]))
+ return PTR_ERR(mc->regs[i]);
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c
new file mode 100644
index 00000000000..d3df7602f40
--- /dev/null
+++ b/drivers/memory/ti-aemif.c
@@ -0,0 +1,427 @@
+/*
+ * TI AEMIF driver
+ *
+ * Copyright (C) 2010 - 2013 Texas Instruments Incorporated. http://www.ti.com/
+ *
+ * Authors:
+ * Murali Karicheri <m-karicheri2@ti.com>
+ * Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#define TA_SHIFT 2
+#define RHOLD_SHIFT 4
+#define RSTROBE_SHIFT 7
+#define RSETUP_SHIFT 13
+#define WHOLD_SHIFT 17
+#define WSTROBE_SHIFT 20
+#define WSETUP_SHIFT 26
+#define EW_SHIFT 30
+#define SS_SHIFT 31
+
+#define TA(x) ((x) << TA_SHIFT)
+#define RHOLD(x) ((x) << RHOLD_SHIFT)
+#define RSTROBE(x) ((x) << RSTROBE_SHIFT)
+#define RSETUP(x) ((x) << RSETUP_SHIFT)
+#define WHOLD(x) ((x) << WHOLD_SHIFT)
+#define WSTROBE(x) ((x) << WSTROBE_SHIFT)
+#define WSETUP(x) ((x) << WSETUP_SHIFT)
+#define EW(x) ((x) << EW_SHIFT)
+#define SS(x) ((x) << SS_SHIFT)
+
+#define ASIZE_MAX 0x1
+#define TA_MAX 0x3
+#define RHOLD_MAX 0x7
+#define RSTROBE_MAX 0x3f
+#define RSETUP_MAX 0xf
+#define WHOLD_MAX 0x7
+#define WSTROBE_MAX 0x3f
+#define WSETUP_MAX 0xf
+#define EW_MAX 0x1
+#define SS_MAX 0x1
+#define NUM_CS 4
+
+#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT)
+#define RHOLD_VAL(x) (((x) & RHOLD(RHOLD_MAX)) >> RHOLD_SHIFT)
+#define RSTROBE_VAL(x) (((x) & RSTROBE(RSTROBE_MAX)) >> RSTROBE_SHIFT)
+#define RSETUP_VAL(x) (((x) & RSETUP(RSETUP_MAX)) >> RSETUP_SHIFT)
+#define WHOLD_VAL(x) (((x) & WHOLD(WHOLD_MAX)) >> WHOLD_SHIFT)
+#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT)
+#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT)
+#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT)
+#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT)
+
+#define NRCSR_OFFSET 0x00
+#define AWCCR_OFFSET 0x04
+#define A1CR_OFFSET 0x10
+
+#define ACR_ASIZE_MASK 0x3
+#define ACR_EW_MASK BIT(30)
+#define ACR_SS_MASK BIT(31)
+#define ASIZE_16BIT 1
+
+#define CONFIG_MASK (TA(TA_MAX) | \
+ RHOLD(RHOLD_MAX) | \
+ RSTROBE(RSTROBE_MAX) | \
+ RSETUP(RSETUP_MAX) | \
+ WHOLD(WHOLD_MAX) | \
+ WSTROBE(WSTROBE_MAX) | \
+ WSETUP(WSETUP_MAX) | \
+ EW(EW_MAX) | SS(SS_MAX) | \
+ ASIZE_MAX)
+
+/**
+ * struct aemif_cs_data: structure to hold cs parameters
+ * @cs: chip-select number
+ * @wstrobe: write strobe width, ns
+ * @rstrobe: read strobe width, ns
+ * @wsetup: write setup width, ns
+ * @whold: write hold width, ns
+ * @rsetup: read setup width, ns
+ * @rhold: read hold width, ns
+ * @ta: minimum turn around time, ns
+ * @enable_ss: enable/disable select strobe mode
+ * @enable_ew: enable/disable extended wait mode
+ * @asize: width of the asynchronous device's data bus
+ */
+struct aemif_cs_data {
+ u8 cs;
+ u16 wstrobe;
+ u16 rstrobe;
+ u8 wsetup;
+ u8 whold;
+ u8 rsetup;
+ u8 rhold;
+ u8 ta;
+ u8 enable_ss;
+ u8 enable_ew;
+ u8 asize;
+};
+
+/**
+ * struct aemif_device: structure to hold device data
+ * @base: base address of AEMIF registers
+ * @clk: source clock
+ * @clk_rate: clock's rate in kHz
+ * @num_cs: number of assigned chip-selects
+ * @cs_offset: start number of cs nodes
+ * @cs_data: array of chip-select settings
+ */
+struct aemif_device {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long clk_rate;
+ u8 num_cs;
+ int cs_offset;
+ struct aemif_cs_data cs_data[NUM_CS];
+};
+
+/**
+ * aemif_calc_rate - calculate timing data.
+ * @pdev: platform device to calculate for
+ * @wanted: The cycle time needed in nanoseconds.
+ * @clk: The input clock rate in kHz.
+ * @max: The maximum divider value that can be programmed.
+ *
+ * On success, returns the calculated timing value minus 1 for easy
+ * programming into AEMIF timing registers, else negative errno.
+ */
+static int aemif_calc_rate(struct platform_device *pdev, int wanted,
+ unsigned long clk, int max)
+{
+ int result;
+
+ result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
+
+ dev_dbg(&pdev->dev, "%s: result %d from %ld, %d\n", __func__, result,
+ clk, wanted);
+
+ /* It is generally OK to have a more relaxed timing than requested... */
+ if (result < 0)
+ result = 0;
+
+ /* ... But configuring tighter timings is not an option. */
+ else if (result > max)
+ result = -EINVAL;
+
+ return result;
+}
+
+/**
+ * aemif_config_abus - configure async bus parameters
+ * @pdev: platform device to configure for
+ * @csnum: aemif chip select number
+ *
+ * This function programs the given timing values (in real clock) into the
+ * AEMIF registers taking the AEMIF clock into account.
+ *
+ * This function does not use any locking while programming the AEMIF
+ * because it is expected that there is only one user of a given
+ * chip-select.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+static int aemif_config_abus(struct platform_device *pdev, int csnum)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ struct aemif_cs_data *data = &aemif->cs_data[csnum];
+ int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
+ unsigned long clk_rate = aemif->clk_rate;
+ unsigned offset;
+ u32 set, val;
+
+ offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
+
+ ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX);
+ rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX);
+ rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX);
+ rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX);
+ whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX);
+ wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX);
+ wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX);
+
+ if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
+ whold < 0 || wstrobe < 0 || wsetup < 0) {
+ dev_err(&pdev->dev, "%s: cannot get suitable timings\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
+ WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
+
+ set |= (data->asize & ACR_ASIZE_MASK);
+ if (data->enable_ew)
+ set |= ACR_EW_MASK;
+ if (data->enable_ss)
+ set |= ACR_SS_MASK;
+
+ val = readl(aemif->base + offset);
+ val &= ~CONFIG_MASK;
+ val |= set;
+ writel(val, aemif->base + offset);
+
+ return 0;
+}
+
+static inline int aemif_cycles_to_nsec(int val, unsigned long clk_rate)
+{
+ return ((val + 1) * NSEC_PER_MSEC) / clk_rate;
+}
+
+/**
+ * aemif_get_hw_params - function to read hw register values
+ * @pdev: platform device to read for
+ * @csnum: aemif chip select number
+ *
+ * This function reads the defaults from the registers and update
+ * the timing values. Required for get/set commands and also for
+ * the case when driver needs to use defaults in hardware.
+ */
+static void aemif_get_hw_params(struct platform_device *pdev, int csnum)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ struct aemif_cs_data *data = &aemif->cs_data[csnum];
+ unsigned long clk_rate = aemif->clk_rate;
+ u32 val, offset;
+
+ offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
+ val = readl(aemif->base + offset);
+
+ data->ta = aemif_cycles_to_nsec(TA_VAL(val), clk_rate);
+ data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val), clk_rate);
+ data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val), clk_rate);
+ data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val), clk_rate);
+ data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val), clk_rate);
+ data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate);
+ data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate);
+ data->enable_ew = EW_VAL(val);
+ data->enable_ss = SS_VAL(val);
+ data->asize = val & ASIZE_MAX;
+}
+
+/**
+ * of_aemif_parse_abus_config - parse CS configuration from DT
+ * @pdev: platform device to parse for
+ * @np: device node ptr
+ *
+ * This function update the emif async bus configuration based on the values
+ * configured in a cs device binding node.
+ */
+static int of_aemif_parse_abus_config(struct platform_device *pdev,
+ struct device_node *np)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ struct aemif_cs_data *data;
+ u32 cs;
+ u32 val;
+
+ if (of_property_read_u32(np, "ti,cs-chipselect", &cs)) {
+ dev_dbg(&pdev->dev, "cs property is required");
+ return -EINVAL;
+ }
+
+ if (cs - aemif->cs_offset >= NUM_CS || cs < aemif->cs_offset) {
+ dev_dbg(&pdev->dev, "cs number is incorrect %d", cs);
+ return -EINVAL;
+ }
+
+ if (aemif->num_cs >= NUM_CS) {
+ dev_dbg(&pdev->dev, "cs count is more than %d", NUM_CS);
+ return -EINVAL;
+ }
+
+ data = &aemif->cs_data[aemif->num_cs];
+ data->cs = cs;
+
+ /* read the current value in the hw register */
+ aemif_get_hw_params(pdev, aemif->num_cs++);
+
+ /* override the values from device node */
+ if (!of_property_read_u32(np, "ti,cs-min-turnaround-ns", &val))
+ data->ta = val;
+
+ if (!of_property_read_u32(np, "ti,cs-read-hold-ns", &val))
+ data->rhold = val;
+
+ if (!of_property_read_u32(np, "ti,cs-read-strobe-ns", &val))
+ data->rstrobe = val;
+
+ if (!of_property_read_u32(np, "ti,cs-read-setup-ns", &val))
+ data->rsetup = val;
+
+ if (!of_property_read_u32(np, "ti,cs-write-hold-ns", &val))
+ data->whold = val;
+
+ if (!of_property_read_u32(np, "ti,cs-write-strobe-ns", &val))
+ data->wstrobe = val;
+
+ if (!of_property_read_u32(np, "ti,cs-write-setup-ns", &val))
+ data->wsetup = val;
+
+ if (!of_property_read_u32(np, "ti,cs-bus-width", &val))
+ if (val == 16)
+ data->asize = 1;
+ data->enable_ew = of_property_read_bool(np, "ti,cs-extended-wait-mode");
+ data->enable_ss = of_property_read_bool(np, "ti,cs-select-strobe-mode");
+ return 0;
+}
+
+static const struct of_device_id aemif_of_match[] = {
+ { .compatible = "ti,davinci-aemif", },
+ { .compatible = "ti,da850-aemif", },
+ {},
+};
+
+static int aemif_probe(struct platform_device *pdev)
+{
+ int i;
+ int ret = -ENODEV;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child_np;
+ struct aemif_device *aemif;
+
+ if (np == NULL)
+ return 0;
+
+ aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
+ if (!aemif)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, aemif);
+
+ aemif->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(aemif->clk)) {
+ dev_err(dev, "cannot get clock 'aemif'\n");
+ return PTR_ERR(aemif->clk);
+ }
+
+ clk_prepare_enable(aemif->clk);
+ aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
+
+ if (of_device_is_compatible(np, "ti,da850-aemif"))
+ aemif->cs_offset = 2;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ aemif->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(aemif->base)) {
+ ret = PTR_ERR(aemif->base);
+ goto error;
+ }
+
+ /*
+ * For every controller device node, there is a cs device node that
+ * describe the bus configuration parameters. This functions iterate
+ * over these nodes and update the cs data array.
+ */
+ for_each_available_child_of_node(np, child_np) {
+ ret = of_aemif_parse_abus_config(pdev, child_np);
+ if (ret < 0)
+ goto error;
+ }
+
+ for (i = 0; i < aemif->num_cs; i++) {
+ ret = aemif_config_abus(pdev, i);
+ if (ret < 0) {
+ dev_err(dev, "Error configuring chip select %d\n",
+ aemif->cs_data[i].cs);
+ goto error;
+ }
+ }
+
+ /*
+ * Create a child devices explicitly from here to
+ * guarantee that the child will be probed after the AEMIF timing
+ * parameters are set.
+ */
+ for_each_available_child_of_node(np, child_np) {
+ ret = of_platform_populate(child_np, NULL, NULL, dev);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ clk_disable_unprepare(aemif->clk);
+ return ret;
+}
+
+static int aemif_remove(struct platform_device *pdev)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(aemif->clk);
+ return 0;
+}
+
+static struct platform_driver aemif_driver = {
+ .probe = aemif_probe,
+ .remove = aemif_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(aemif_of_match),
+ },
+};
+
+module_platform_driver(aemif_driver);
+
+MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
+MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments AEMIF driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);