summaryrefslogtreecommitdiff
path: root/drivers/memory
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/memory')
-rw-r--r--drivers/memory/Kconfig20
-rw-r--r--drivers/memory/Makefile4
-rw-r--r--drivers/memory/omap-gpmc.c2091
-rw-r--r--drivers/memory/tegra/Kconfig7
-rw-r--r--drivers/memory/tegra/Makefile7
-rw-r--r--drivers/memory/tegra/mc.c301
-rw-r--r--drivers/memory/tegra/mc.h40
-rw-r--r--drivers/memory/tegra/tegra114.c948
-rw-r--r--drivers/memory/tegra/tegra124.c995
-rw-r--r--drivers/memory/tegra/tegra30.c970
-rw-r--r--drivers/memory/tegra30-mc.c377
11 files changed, 5372 insertions, 388 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 6d91c27fd4c8..191383d8c94d 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -41,6 +41,14 @@ config TI_EMIF
parameters and other settings during frequency, voltage and
temperature changes
+config OMAP_GPMC
+ bool
+ help
+ This driver is for the General Purpose Memory Controller (GPMC)
+ present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows
+ interfacing to a variety of asynchronous as well as synchronous
+ memory drives like NOR, NAND, OneNAND, SRAM.
+
config MVEBU_DEVBUS
bool "Marvell EBU Device Bus Controller"
default y
@@ -61,16 +69,6 @@ config TEGRA20_MC
analysis, especially for IOMMU/GART(Graphics Address
Relocation Table) module.
-config TEGRA30_MC
- bool "Tegra30 Memory Controller(MC) driver"
- default y
- depends on ARCH_TEGRA_3x_SOC
- help
- This driver is for the Memory Controller(MC) module available
- in Tegra30 SoCs, mainly for a address translation fault
- analysis, especially for IOMMU/SMMU(System Memory Management
- Unit) module.
-
config FSL_CORENET_CF
tristate "Freescale CoreNet Error Reporting"
depends on FSL_SOC_BOOKE
@@ -85,4 +83,6 @@ config FSL_IFC
bool
depends on FSL_SOC
+source "drivers/memory/tegra/Kconfig"
+
endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index c32d31981be3..6b6548124473 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -8,8 +8,10 @@ endif
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
+obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.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
+
+obj-$(CONFIG_TEGRA_MC) += tegra/
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
new file mode 100644
index 000000000000..24696f59215b
--- /dev/null
+++ b/drivers/memory/omap-gpmc.c
@@ -0,0 +1,2091 @@
+/*
+ * GPMC support functions
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Author: Juha Yrjola
+ *
+ * Copyright (C) 2009 Texas Instruments
+ * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@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.
+ */
+#undef DEBUG
+
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_mtd.h>
+#include <linux/of_device.h>
+#include <linux/omap-gpmc.h>
+#include <linux/mtd/nand.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/platform_data/mtd-nand-omap2.h>
+#include <linux/platform_data/mtd-onenand-omap2.h>
+
+#include <asm/mach-types.h>
+
+#define DEVICE_NAME "omap-gpmc"
+
+/* GPMC register offsets */
+#define GPMC_REVISION 0x00
+#define GPMC_SYSCONFIG 0x10
+#define GPMC_SYSSTATUS 0x14
+#define GPMC_IRQSTATUS 0x18
+#define GPMC_IRQENABLE 0x1c
+#define GPMC_TIMEOUT_CONTROL 0x40
+#define GPMC_ERR_ADDRESS 0x44
+#define GPMC_ERR_TYPE 0x48
+#define GPMC_CONFIG 0x50
+#define GPMC_STATUS 0x54
+#define GPMC_PREFETCH_CONFIG1 0x1e0
+#define GPMC_PREFETCH_CONFIG2 0x1e4
+#define GPMC_PREFETCH_CONTROL 0x1ec
+#define GPMC_PREFETCH_STATUS 0x1f0
+#define GPMC_ECC_CONFIG 0x1f4
+#define GPMC_ECC_CONTROL 0x1f8
+#define GPMC_ECC_SIZE_CONFIG 0x1fc
+#define GPMC_ECC1_RESULT 0x200
+#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */
+#define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */
+#define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */
+#define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */
+#define GPMC_ECC_BCH_RESULT_4 0x300 /* not available on OMAP2 */
+#define GPMC_ECC_BCH_RESULT_5 0x304 /* not available on OMAP2 */
+#define GPMC_ECC_BCH_RESULT_6 0x308 /* not available on OMAP2 */
+
+/* GPMC ECC control settings */
+#define GPMC_ECC_CTRL_ECCCLEAR 0x100
+#define GPMC_ECC_CTRL_ECCDISABLE 0x000
+#define GPMC_ECC_CTRL_ECCREG1 0x001
+#define GPMC_ECC_CTRL_ECCREG2 0x002
+#define GPMC_ECC_CTRL_ECCREG3 0x003
+#define GPMC_ECC_CTRL_ECCREG4 0x004
+#define GPMC_ECC_CTRL_ECCREG5 0x005
+#define GPMC_ECC_CTRL_ECCREG6 0x006
+#define GPMC_ECC_CTRL_ECCREG7 0x007
+#define GPMC_ECC_CTRL_ECCREG8 0x008
+#define GPMC_ECC_CTRL_ECCREG9 0x009
+
+#define GPMC_CONFIG_LIMITEDADDRESS BIT(1)
+
+#define GPMC_CONFIG2_CSEXTRADELAY BIT(7)
+#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7)
+#define GPMC_CONFIG4_OEEXTRADELAY BIT(7)
+#define GPMC_CONFIG4_WEEXTRADELAY BIT(23)
+#define GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN BIT(6)
+#define GPMC_CONFIG6_CYCLE2CYCLESAMECSEN BIT(7)
+
+#define GPMC_CS0_OFFSET 0x60
+#define GPMC_CS_SIZE 0x30
+#define GPMC_BCH_SIZE 0x10
+
+#define GPMC_MEM_END 0x3FFFFFFF
+
+#define GPMC_CHUNK_SHIFT 24 /* 16 MB */
+#define GPMC_SECTION_SHIFT 28 /* 128 MB */
+
+#define CS_NUM_SHIFT 24
+#define ENABLE_PREFETCH (0x1 << 7)
+#define DMA_MPU_MODE 2
+
+#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf)
+#define GPMC_REVISION_MINOR(l) (l & 0xf)
+
+#define GPMC_HAS_WR_ACCESS 0x1
+#define GPMC_HAS_WR_DATA_MUX_BUS 0x2
+#define GPMC_HAS_MUX_AAD 0x4
+
+#define GPMC_NR_WAITPINS 4
+
+#define GPMC_CS_CONFIG1 0x00
+#define GPMC_CS_CONFIG2 0x04
+#define GPMC_CS_CONFIG3 0x08
+#define GPMC_CS_CONFIG4 0x0c
+#define GPMC_CS_CONFIG5 0x10
+#define GPMC_CS_CONFIG6 0x14
+#define GPMC_CS_CONFIG7 0x18
+#define GPMC_CS_NAND_COMMAND 0x1c
+#define GPMC_CS_NAND_ADDRESS 0x20
+#define GPMC_CS_NAND_DATA 0x24
+
+/* Control Commands */
+#define GPMC_CONFIG_RDY_BSY 0x00000001
+#define GPMC_CONFIG_DEV_SIZE 0x00000002
+#define GPMC_CONFIG_DEV_TYPE 0x00000003
+#define GPMC_SET_IRQ_STATUS 0x00000004
+
+#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31)
+#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30)
+#define GPMC_CONFIG1_READTYPE_ASYNC (0 << 29)
+#define GPMC_CONFIG1_READTYPE_SYNC (1 << 29)
+#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP (1 << 28)
+#define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27)
+#define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27)
+#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) ((val & 3) << 25)
+#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23)
+#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22)
+#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21)
+#define GPMC_CONFIG1_WAIT_MON_IIME(val) ((val & 3) << 18)
+#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16)
+#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12)
+#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1)
+#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10)
+#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0)
+#define GPMC_CONFIG1_MUXTYPE(val) ((val & 3) << 8)
+#define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4)
+#define GPMC_CONFIG1_FCLK_DIV(val) (val & 3)
+#define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1))
+#define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2))
+#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3))
+#define GPMC_CONFIG7_CSVALID (1 << 6)
+
+#define GPMC_DEVICETYPE_NOR 0
+#define GPMC_DEVICETYPE_NAND 2
+#define GPMC_CONFIG_WRITEPROTECT 0x00000010
+#define WR_RD_PIN_MONITORING 0x00600000
+
+#define GPMC_ENABLE_IRQ 0x0000000d
+
+/* ECC commands */
+#define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */
+#define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */
+#define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */
+
+/* XXX: Only NAND irq has been considered,currently these are the only ones used
+ */
+#define GPMC_NR_IRQ 2
+
+struct gpmc_cs_data {
+ const char *name;
+
+#define GPMC_CS_RESERVED (1 << 0)
+ u32 flags;
+
+ struct resource mem;
+};
+
+struct gpmc_client_irq {
+ unsigned irq;
+ u32 bitmask;
+};
+
+/* Structure to save gpmc cs context */
+struct gpmc_cs_config {
+ u32 config1;
+ u32 config2;
+ u32 config3;
+ u32 config4;
+ u32 config5;
+ u32 config6;
+ u32 config7;
+ int is_valid;
+};
+
+/*
+ * Structure to save/restore gpmc context
+ * to support core off on OMAP3
+ */
+struct omap3_gpmc_regs {
+ u32 sysconfig;
+ u32 irqenable;
+ u32 timeout_ctrl;
+ u32 config;
+ u32 prefetch_config1;
+ u32 prefetch_config2;
+ u32 prefetch_control;
+ struct gpmc_cs_config cs_context[GPMC_CS_NUM];
+};
+
+static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
+static struct irq_chip gpmc_irq_chip;
+static int gpmc_irq_start;
+
+static struct resource gpmc_mem_root;
+static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
+static DEFINE_SPINLOCK(gpmc_mem_lock);
+/* Define chip-selects as reserved by default until probe completes */
+static unsigned int gpmc_cs_num = GPMC_CS_NUM;
+static unsigned int gpmc_nr_waitpins;
+static struct device *gpmc_dev;
+static int gpmc_irq;
+static resource_size_t phys_base, mem_size;
+static unsigned gpmc_capability;
+static void __iomem *gpmc_base;
+
+static struct clk *gpmc_l3_clk;
+
+static irqreturn_t gpmc_handle_irq(int irq, void *dev);
+
+static void gpmc_write_reg(int idx, u32 val)
+{
+ writel_relaxed(val, gpmc_base + idx);
+}
+
+static u32 gpmc_read_reg(int idx)
+{
+ return readl_relaxed(gpmc_base + idx);
+}
+
+void gpmc_cs_write_reg(int cs, int idx, u32 val)
+{
+ void __iomem *reg_addr;
+
+ reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
+ writel_relaxed(val, reg_addr);
+}
+
+static u32 gpmc_cs_read_reg(int cs, int idx)
+{
+ void __iomem *reg_addr;
+
+ reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
+ return readl_relaxed(reg_addr);
+}
+
+/* TODO: Add support for gpmc_fck to clock framework and use it */
+static unsigned long gpmc_get_fclk_period(void)
+{
+ unsigned long rate = clk_get_rate(gpmc_l3_clk);
+
+ rate /= 1000;
+ rate = 1000000000 / rate; /* In picoseconds */
+
+ return rate;
+}
+
+static unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
+{
+ unsigned long tick_ps;
+
+ /* Calculate in picosecs to yield more exact results */
+ tick_ps = gpmc_get_fclk_period();
+
+ return (time_ns * 1000 + tick_ps - 1) / tick_ps;
+}
+
+static unsigned int gpmc_ps_to_ticks(unsigned int time_ps)
+{
+ unsigned long tick_ps;
+
+ /* Calculate in picosecs to yield more exact results */
+ tick_ps = gpmc_get_fclk_period();
+
+ return (time_ps + tick_ps - 1) / tick_ps;
+}
+
+unsigned int gpmc_ticks_to_ns(unsigned int ticks)
+{
+ return ticks * gpmc_get_fclk_period() / 1000;
+}
+
+static unsigned int gpmc_ticks_to_ps(unsigned int ticks)
+{
+ return ticks * gpmc_get_fclk_period();
+}
+
+static unsigned int gpmc_round_ps_to_ticks(unsigned int time_ps)
+{
+ unsigned long ticks = gpmc_ps_to_ticks(time_ps);
+
+ return ticks * gpmc_get_fclk_period();
+}
+
+static inline void gpmc_cs_modify_reg(int cs, int reg, u32 mask, bool value)
+{
+ u32 l;
+
+ l = gpmc_cs_read_reg(cs, reg);
+ if (value)
+ l |= mask;
+ else
+ l &= ~mask;
+ gpmc_cs_write_reg(cs, reg, l);
+}
+
+static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)
+{
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG1,
+ GPMC_CONFIG1_TIME_PARA_GRAN,
+ p->time_para_granularity);
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG2,
+ GPMC_CONFIG2_CSEXTRADELAY, p->cs_extra_delay);
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG3,
+ GPMC_CONFIG3_ADVEXTRADELAY, p->adv_extra_delay);
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4,
+ GPMC_CONFIG4_OEEXTRADELAY, p->oe_extra_delay);
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4,
+ GPMC_CONFIG4_OEEXTRADELAY, p->we_extra_delay);
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6,
+ GPMC_CONFIG6_CYCLE2CYCLESAMECSEN,
+ p->cycle2cyclesamecsen);
+ gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6,
+ GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN,
+ p->cycle2cyclediffcsen);
+}
+
+#ifdef DEBUG
+static int get_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
+ bool raw, bool noval, int shift,
+ const char *name)
+{
+ u32 l;
+ int nr_bits, max_value, mask;
+
+ l = gpmc_cs_read_reg(cs, reg);
+ nr_bits = end_bit - st_bit + 1;
+ max_value = (1 << nr_bits) - 1;
+ mask = max_value << st_bit;
+ l = (l & mask) >> st_bit;
+ if (shift)
+ l = (shift << l);
+ if (noval && (l == 0))
+ return 0;
+ if (!raw) {
+ unsigned int time_ns_min, time_ns, time_ns_max;
+
+ time_ns_min = gpmc_ticks_to_ns(l ? l - 1 : 0);
+ time_ns = gpmc_ticks_to_ns(l);
+ time_ns_max = gpmc_ticks_to_ns(l + 1 > max_value ?
+ max_value : l + 1);
+ pr_info("gpmc,%s = <%u> (%u - %u ns, %i ticks)\n",
+ name, time_ns, time_ns_min, time_ns_max, l);
+ } else {
+ pr_info("gpmc,%s = <%u>\n", name, l);
+ }
+
+ return l;
+}
+
+#define GPMC_PRINT_CONFIG(cs, config) \
+ pr_info("cs%i %s: 0x%08x\n", cs, #config, \
+ gpmc_cs_read_reg(cs, config))
+#define GPMC_GET_RAW(reg, st, end, field) \
+ get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 0, 0, field)
+#define GPMC_GET_RAW_BOOL(reg, st, end, field) \
+ get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, 0, field)
+#define GPMC_GET_RAW_SHIFT(reg, st, end, shift, field) \
+ get_gpmc_timing_reg(cs, (reg), (st), (end), 1, 1, (shift), field)
+#define GPMC_GET_TICKS(reg, st, end, field) \
+ get_gpmc_timing_reg(cs, (reg), (st), (end), 0, 0, 0, field)
+
+static void gpmc_show_regs(int cs, const char *desc)
+{
+ pr_info("gpmc cs%i %s:\n", cs, desc);
+ GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG1);
+ GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG2);
+ GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG3);
+ GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG4);
+ GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG5);
+ GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG6);
+}
+
+/*
+ * Note that gpmc,wait-pin handing wrongly assumes bit 8 is available,
+ * see commit c9fb809.
+ */
+static void gpmc_cs_show_timings(int cs, const char *desc)
+{
+ gpmc_show_regs(cs, desc);
+
+ pr_info("gpmc cs%i access configuration:\n", cs);
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity");
+ GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data");
+ GPMC_GET_RAW(GPMC_CS_CONFIG1, 12, 13, "device-width");
+ GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read");
+ GPMC_GET_RAW_SHIFT(GPMC_CS_CONFIG1, 23, 24, 4, "burst-length");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 27, 27, "sync-write");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 28, 28, "burst-write");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 29, 29, "gpmc,sync-read");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 30, 30, "burst-read");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 31, 31, "burst-wrap");
+
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG2, 7, 7, "cs-extra-delay");
+
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG3, 7, 7, "adv-extra-delay");
+
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 23, 23, "we-extra-delay");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 7, 7, "oe-extra-delay");
+
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 7, 7, "cycle2cycle-samecsen");
+ GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6, 6, 6, "cycle2cycle-diffcsen");
+
+ pr_info("gpmc cs%i timings configuration:\n", cs);
+ GPMC_GET_TICKS(GPMC_CS_CONFIG2, 0, 3, "cs-on-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG2, 8, 12, "cs-rd-off-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG2, 16, 20, "cs-wr-off-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG3, 0, 3, "adv-on-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG3, 8, 12, "adv-rd-off-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG3, 16, 20, "adv-wr-off-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG4, 0, 3, "oe-on-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG4, 8, 12, "oe-off-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG4, 16, 19, "we-on-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG4, 24, 28, "we-off-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG5, 0, 4, "rd-cycle-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG5, 8, 12, "wr-cycle-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG5, 16, 20, "access-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG5, 24, 27, "page-burst-access-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG1, 18, 19, "wait-monitoring-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG1, 25, 26, "clk-activation-ns");
+
+ GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns");
+ GPMC_GET_TICKS(GPMC_CS_CONFIG6, 24, 28, "wr-access-ns");
+}
+#else
+static inline void gpmc_cs_show_timings(int cs, const char *desc)
+{
+}
+#endif
+
+static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
+ int time, const char *name)
+{
+ u32 l;
+ int ticks, mask, nr_bits;
+
+ if (time == 0)
+ ticks = 0;
+ else
+ ticks = gpmc_ns_to_ticks(time);
+ nr_bits = end_bit - st_bit + 1;
+ mask = (1 << nr_bits) - 1;
+
+ if (ticks > mask) {
+ pr_err("%s: GPMC error! CS%d: %s: %d ns, %d ticks > %d\n",
+ __func__, cs, name, time, ticks, mask);
+
+ return -1;
+ }
+
+ l = gpmc_cs_read_reg(cs, reg);
+#ifdef DEBUG
+ printk(KERN_INFO
+ "GPMC CS%d: %-10s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
+ cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000,
+ (l >> st_bit) & mask, time);
+#endif
+ l &= ~(mask << st_bit);
+ l |= ticks << st_bit;
+ gpmc_cs_write_reg(cs, reg, l);
+
+ return 0;
+}
+
+#define GPMC_SET_ONE(reg, st, end, field) \
+ if (set_gpmc_timing_reg(cs, (reg), (st), (end), \
+ t->field, #field) < 0) \
+ return -1
+
+int gpmc_calc_divider(unsigned int sync_clk)
+{
+ int div;
+ u32 l;
+
+ l = sync_clk + (gpmc_get_fclk_period() - 1);
+ div = l / gpmc_get_fclk_period();
+ if (div > 4)
+ return -1;
+ if (div <= 0)
+ div = 1;
+
+ return div;
+}
+
+int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
+{
+ int div;
+ u32 l;
+
+ gpmc_cs_show_timings(cs, "before gpmc_cs_set_timings");
+ div = gpmc_calc_divider(t->sync_clk);
+ if (div < 0)
+ return div;
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG2, 0, 3, cs_on);
+ GPMC_SET_ONE(GPMC_CS_CONFIG2, 8, 12, cs_rd_off);
+ GPMC_SET_ONE(GPMC_CS_CONFIG2, 16, 20, cs_wr_off);
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG3, 0, 3, adv_on);
+ GPMC_SET_ONE(GPMC_CS_CONFIG3, 8, 12, adv_rd_off);
+ GPMC_SET_ONE(GPMC_CS_CONFIG3, 16, 20, adv_wr_off);
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG4, 0, 3, oe_on);
+ GPMC_SET_ONE(GPMC_CS_CONFIG4, 8, 12, oe_off);
+ GPMC_SET_ONE(GPMC_CS_CONFIG4, 16, 19, we_on);
+ GPMC_SET_ONE(GPMC_CS_CONFIG4, 24, 28, we_off);
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG5, 0, 4, rd_cycle);
+ GPMC_SET_ONE(GPMC_CS_CONFIG5, 8, 12, wr_cycle);
+ GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access);
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access);
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround);
+ GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay);
+
+ GPMC_SET_ONE(GPMC_CS_CONFIG1, 18, 19, wait_monitoring);
+ GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation);
+
+ if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
+ GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus);
+ if (gpmc_capability & GPMC_HAS_WR_ACCESS)
+ GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access);
+
+ /* caller is expected to have initialized CONFIG1 to cover
+ * at least sync vs async
+ */
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+ if (l & (GPMC_CONFIG1_READTYPE_SYNC | GPMC_CONFIG1_WRITETYPE_SYNC)) {
+#ifdef DEBUG
+ printk(KERN_INFO "GPMC CS%d CLK period is %lu ns (div %d)\n",
+ cs, (div * gpmc_get_fclk_period()) / 1000, div);
+#endif
+ l &= ~0x03;
+ l |= (div - 1);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
+ }
+
+ gpmc_cs_bool_timings(cs, &t->bool_timings);
+ gpmc_cs_show_timings(cs, "after gpmc_cs_set_timings");
+
+ return 0;
+}
+
+static int gpmc_cs_set_memconf(int cs, u32 base, u32 size)
+{
+ u32 l;
+ u32 mask;
+
+ /*
+ * Ensure that base address is aligned on a
+ * boundary equal to or greater than size.
+ */
+ if (base & (size - 1))
+ return -EINVAL;
+
+ mask = (1 << GPMC_SECTION_SHIFT) - size;
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+ l &= ~0x3f;
+ l = (base >> GPMC_CHUNK_SHIFT) & 0x3f;
+ l &= ~(0x0f << 8);
+ l |= ((mask >> GPMC_CHUNK_SHIFT) & 0x0f) << 8;
+ l |= GPMC_CONFIG7_CSVALID;
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
+
+ return 0;
+}
+
+static void gpmc_cs_enable_mem(int cs)
+{
+ u32 l;
+
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+ l |= GPMC_CONFIG7_CSVALID;
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
+}
+
+static void gpmc_cs_disable_mem(int cs)
+{
+ u32 l;
+
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+ l &= ~GPMC_CONFIG7_CSVALID;
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
+}
+
+static void gpmc_cs_get_memconf(int cs, u32 *base, u32 *size)
+{
+ u32 l;
+ u32 mask;
+
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+ *base = (l & 0x3f) << GPMC_CHUNK_SHIFT;
+ mask = (l >> 8) & 0x0f;
+ *size = (1 << GPMC_SECTION_SHIFT) - (mask << GPMC_CHUNK_SHIFT);
+}
+
+static int gpmc_cs_mem_enabled(int cs)
+{
+ u32 l;
+
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+ return l & GPMC_CONFIG7_CSVALID;
+}
+
+static void gpmc_cs_set_reserved(int cs, int reserved)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+
+ gpmc->flags |= GPMC_CS_RESERVED;
+}
+
+static bool gpmc_cs_reserved(int cs)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+
+ return gpmc->flags & GPMC_CS_RESERVED;
+}
+
+static void gpmc_cs_set_name(int cs, const char *name)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+
+ gpmc->name = name;
+}
+
+const char *gpmc_cs_get_name(int cs)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+
+ return gpmc->name;
+}
+
+static unsigned long gpmc_mem_align(unsigned long size)
+{
+ int order;
+
+ size = (size - 1) >> (GPMC_CHUNK_SHIFT - 1);
+ order = GPMC_CHUNK_SHIFT - 1;
+ do {
+ size >>= 1;
+ order++;
+ } while (size);
+ size = 1 << order;
+ return size;
+}
+
+static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+ struct resource *res = &gpmc->mem;
+ int r;
+
+ size = gpmc_mem_align(size);
+ spin_lock(&gpmc_mem_lock);
+ res->start = base;
+ res->end = base + size - 1;
+ r = request_resource(&gpmc_mem_root, res);
+ spin_unlock(&gpmc_mem_lock);
+
+ return r;
+}
+
+static int gpmc_cs_delete_mem(int cs)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+ struct resource *res = &gpmc->mem;
+ int r;
+
+ spin_lock(&gpmc_mem_lock);
+ r = release_resource(res);
+ res->start = 0;
+ res->end = 0;
+ spin_unlock(&gpmc_mem_lock);
+
+ return r;
+}
+
+/**
+ * gpmc_cs_remap - remaps a chip-select physical base address
+ * @cs: chip-select to remap
+ * @base: physical base address to re-map chip-select to
+ *
+ * Re-maps a chip-select to a new physical base address specified by
+ * "base". Returns 0 on success and appropriate negative error code
+ * on failure.
+ */
+static int gpmc_cs_remap(int cs, u32 base)
+{
+ int ret;
+ u32 old_base, size;
+
+ if (cs > gpmc_cs_num) {
+ pr_err("%s: requested chip-select is disabled\n", __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * Make sure we ignore any device offsets from the GPMC partition
+ * allocated for the chip select and that the new base confirms
+ * to the GPMC 16MB minimum granularity.
+ */
+ base &= ~(SZ_16M - 1);
+
+ gpmc_cs_get_memconf(cs, &old_base, &size);
+ if (base == old_base)
+ return 0;
+
+ ret = gpmc_cs_delete_mem(cs);
+ if (ret < 0)
+ return ret;
+
+ ret = gpmc_cs_insert_mem(cs, base, size);
+ if (ret < 0)
+ return ret;
+
+ ret = gpmc_cs_set_memconf(cs, base, size);
+
+ return ret;
+}
+
+int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+ struct resource *res = &gpmc->mem;
+ int r = -1;
+
+ if (cs > gpmc_cs_num) {
+ pr_err("%s: requested chip-select is disabled\n", __func__);
+ return -ENODEV;
+ }
+ size = gpmc_mem_align(size);
+ if (size > (1 << GPMC_SECTION_SHIFT))
+ return -ENOMEM;
+
+ spin_lock(&gpmc_mem_lock);
+ if (gpmc_cs_reserved(cs)) {
+ r = -EBUSY;
+ goto out;
+ }
+ if (gpmc_cs_mem_enabled(cs))
+ r = adjust_resource(res, res->start & ~(size - 1), size);
+ if (r < 0)
+ r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0,
+ size, NULL, NULL);
+ if (r < 0)
+ goto out;
+
+ /* Disable CS while changing base address and size mask */
+ gpmc_cs_disable_mem(cs);
+
+ r = gpmc_cs_set_memconf(cs, res->start, resource_size(res));
+ if (r < 0) {
+ release_resource(res);
+ goto out;
+ }
+
+ /* Enable CS */
+ gpmc_cs_enable_mem(cs);
+ *base = res->start;
+ gpmc_cs_set_reserved(cs, 1);
+out:
+ spin_unlock(&gpmc_mem_lock);
+ return r;
+}
+EXPORT_SYMBOL(gpmc_cs_request);
+
+void gpmc_cs_free(int cs)
+{
+ struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
+ struct resource *res = &gpmc->mem;
+
+ spin_lock(&gpmc_mem_lock);
+ if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
+ printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs);
+ BUG();
+ spin_unlock(&gpmc_mem_lock);
+ return;
+ }
+ gpmc_cs_disable_mem(cs);
+ if (res->flags)
+ release_resource(res);
+ gpmc_cs_set_reserved(cs, 0);
+ spin_unlock(&gpmc_mem_lock);
+}
+EXPORT_SYMBOL(gpmc_cs_free);
+
+/**
+ * gpmc_configure - write request to configure gpmc
+ * @cmd: command type
+ * @wval: value to write
+ * @return status of the operation
+ */
+int gpmc_configure(int cmd, int wval)
+{
+ u32 regval;
+
+ switch (cmd) {
+ case GPMC_ENABLE_IRQ:
+ gpmc_write_reg(GPMC_IRQENABLE, wval);
+ break;
+
+ case GPMC_SET_IRQ_STATUS:
+ gpmc_write_reg(GPMC_IRQSTATUS, wval);
+ break;
+
+ case GPMC_CONFIG_WP:
+ regval = gpmc_read_reg(GPMC_CONFIG);
+ if (wval)
+ regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */
+ else
+ regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */
+ gpmc_write_reg(GPMC_CONFIG, regval);
+ break;
+
+ default:
+ pr_err("%s: command not supported\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(gpmc_configure);
+
+void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
+{
+ int i;
+
+ reg->gpmc_status = gpmc_base + GPMC_STATUS;
+ reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
+ GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
+ reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
+ GPMC_CS_NAND_ADDRESS + GPMC_CS_SIZE * cs;
+ reg->gpmc_nand_data = gpmc_base + GPMC_CS0_OFFSET +
+ GPMC_CS_NAND_DATA + GPMC_CS_SIZE * cs;
+ reg->gpmc_prefetch_config1 = gpmc_base + GPMC_PREFETCH_CONFIG1;
+ reg->gpmc_prefetch_config2 = gpmc_base + GPMC_PREFETCH_CONFIG2;
+ reg->gpmc_prefetch_control = gpmc_base + GPMC_PREFETCH_CONTROL;
+ reg->gpmc_prefetch_status = gpmc_base + GPMC_PREFETCH_STATUS;
+ reg->gpmc_ecc_config = gpmc_base + GPMC_ECC_CONFIG;
+ reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL;
+ reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG;
+ reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT;
+
+ for (i = 0; i < GPMC_BCH_NUM_REMAINDER; i++) {
+ reg->gpmc_bch_result0[i] = gpmc_base + GPMC_ECC_BCH_RESULT_0 +
+ GPMC_BCH_SIZE * i;
+ reg->gpmc_bch_result1[i] = gpmc_base + GPMC_ECC_BCH_RESULT_1 +
+ GPMC_BCH_SIZE * i;
+ reg->gpmc_bch_result2[i] = gpmc_base + GPMC_ECC_BCH_RESULT_2 +
+ GPMC_BCH_SIZE * i;
+ reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
+ GPMC_BCH_SIZE * i;
+ reg->gpmc_bch_result4[i] = gpmc_base + GPMC_ECC_BCH_RESULT_4 +
+ i * GPMC_BCH_SIZE;
+ reg->gpmc_bch_result5[i] = gpmc_base + GPMC_ECC_BCH_RESULT_5 +
+ i * GPMC_BCH_SIZE;
+ reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 +
+ i * GPMC_BCH_SIZE;
+ }
+}
+
+int gpmc_get_client_irq(unsigned irq_config)
+{
+ int i;
+
+ if (hweight32(irq_config) > 1)
+ return 0;
+
+ for (i = 0; i < GPMC_NR_IRQ; i++)
+ if (gpmc_client_irq[i].bitmask & irq_config)
+ return gpmc_client_irq[i].irq;
+
+ return 0;
+}
+
+static int gpmc_irq_endis(unsigned irq, bool endis)
+{
+ int i;
+ u32 regval;
+
+ for (i = 0; i < GPMC_NR_IRQ; i++)
+ if (irq == gpmc_client_irq[i].irq) {
+ regval = gpmc_read_reg(GPMC_IRQENABLE);
+ if (endis)
+ regval |= gpmc_client_irq[i].bitmask;
+ else
+ regval &= ~gpmc_client_irq[i].bitmask;
+ gpmc_write_reg(GPMC_IRQENABLE, regval);
+ break;
+ }
+
+ return 0;
+}
+
+static void gpmc_irq_disable(struct irq_data *p)
+{
+ gpmc_irq_endis(p->irq, false);
+}
+
+static void gpmc_irq_enable(struct irq_data *p)
+{
+ gpmc_irq_endis(p->irq, true);
+}
+
+static void gpmc_irq_noop(struct irq_data *data) { }
+
+static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; }
+
+static int gpmc_setup_irq(void)
+{
+ int i;
+ u32 regval;
+
+ if (!gpmc_irq)
+ return -EINVAL;
+
+ gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0);
+ if (gpmc_irq_start < 0) {
+ pr_err("irq_alloc_descs failed\n");
+ return gpmc_irq_start;
+ }
+
+ gpmc_irq_chip.name = "gpmc";
+ gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret;
+ gpmc_irq_chip.irq_enable = gpmc_irq_enable;
+ gpmc_irq_chip.irq_disable = gpmc_irq_disable;
+ gpmc_irq_chip.irq_shutdown = gpmc_irq_noop;
+ gpmc_irq_chip.irq_ack = gpmc_irq_noop;
+ gpmc_irq_chip.irq_mask = gpmc_irq_noop;
+ gpmc_irq_chip.irq_unmask = gpmc_irq_noop;
+
+ gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE;
+ gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT;
+
+ for (i = 0; i < GPMC_NR_IRQ; i++) {
+ gpmc_client_irq[i].irq = gpmc_irq_start + i;
+ irq_set_chip_and_handler(gpmc_client_irq[i].irq,
+ &gpmc_irq_chip, handle_simple_irq);
+ set_irq_flags(gpmc_client_irq[i].irq,
+ IRQF_VALID | IRQF_NOAUTOEN);
+ }
+
+ /* Disable interrupts */
+ gpmc_write_reg(GPMC_IRQENABLE, 0);
+
+ /* clear interrupts */
+ regval = gpmc_read_reg(GPMC_IRQSTATUS);
+ gpmc_write_reg(GPMC_IRQSTATUS, regval);
+
+ return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL);
+}
+
+static int gpmc_free_irq(void)
+{
+ int i;
+
+ if (gpmc_irq)
+ free_irq(gpmc_irq, NULL);
+
+ for (i = 0; i < GPMC_NR_IRQ; i++) {
+ irq_set_handler(gpmc_client_irq[i].irq, NULL);
+ irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
+ irq_modify_status(gpmc_client_irq[i].irq, 0, 0);
+ }
+
+ irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ);
+
+ return 0;
+}
+
+static void gpmc_mem_exit(void)
+{
+ int cs;
+
+ for (cs = 0; cs < gpmc_cs_num; cs++) {
+ if (!gpmc_cs_mem_enabled(cs))
+ continue;
+ gpmc_cs_delete_mem(cs);
+ }
+
+}
+
+static void gpmc_mem_init(void)
+{
+ int cs;
+
+ /*
+ * The first 1MB of GPMC address space is typically mapped to
+ * the internal ROM. Never allocate the first page, to
+ * facilitate bug detection; even if we didn't boot from ROM.
+ */
+ gpmc_mem_root.start = SZ_1M;
+ gpmc_mem_root.end = GPMC_MEM_END;
+
+ /* Reserve all regions that has been set up by bootloader */
+ for (cs = 0; cs < gpmc_cs_num; cs++) {
+ u32 base, size;
+
+ if (!gpmc_cs_mem_enabled(cs))
+ continue;
+ gpmc_cs_get_memconf(cs, &base, &size);
+ if (gpmc_cs_insert_mem(cs, base, size)) {
+ pr_warn("%s: disabling cs %d mapped at 0x%x-0x%x\n",
+ __func__, cs, base, base + size);
+ gpmc_cs_disable_mem(cs);
+ }
+ }
+}
+
+static u32 gpmc_round_ps_to_sync_clk(u32 time_ps, u32 sync_clk)
+{
+ u32 temp;
+ int div;
+
+ div = gpmc_calc_divider(sync_clk);
+ temp = gpmc_ps_to_ticks(time_ps);
+ temp = (temp + div - 1) / div;
+ return gpmc_ticks_to_ps(temp * div);
+}
+
+/* XXX: can the cycles be avoided ? */
+static int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_device_timings *dev_t,
+ bool mux)
+{
+ u32 temp;
+
+ /* adv_rd_off */
+ temp = dev_t->t_avdp_r;
+ /* XXX: mux check required ? */
+ if (mux) {
+ /* XXX: t_avdp not to be required for sync, only added for tusb
+ * this indirectly necessitates requirement of t_avdp_r and
+ * t_avdp_w instead of having a single t_avdp
+ */
+ temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_avdh);
+ temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
+ }
+ gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp);
+
+ /* oe_on */
+ temp = dev_t->t_oeasu; /* XXX: remove this ? */
+ if (mux) {
+ temp = max_t(u32, temp, gpmc_t->clk_activation + dev_t->t_ach);
+ temp = max_t(u32, temp, gpmc_t->adv_rd_off +
+ gpmc_ticks_to_ps(dev_t->cyc_aavdh_oe));
+ }
+ gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp);
+
+ /* access */
+ /* XXX: any scope for improvement ?, by combining oe_on
+ * and clk_activation, need to check whether
+ * access = clk_activation + round to sync clk ?
+ */
+ temp = max_t(u32, dev_t->t_iaa, dev_t->cyc_iaa * gpmc_t->sync_clk);
+ temp += gpmc_t->clk_activation;
+ if (dev_t->cyc_oe)
+ temp = max_t(u32, temp, gpmc_t->oe_on +
+ gpmc_ticks_to_ps(dev_t->cyc_oe));
+ gpmc_t->access = gpmc_round_ps_to_ticks(temp);
+
+ gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1);
+ gpmc_t->cs_rd_off = gpmc_t->oe_off;
+
+ /* rd_cycle */
+ temp = max_t(u32, dev_t->t_cez_r, dev_t->t_oez);
+ temp = gpmc_round_ps_to_sync_clk(temp, gpmc_t->sync_clk) +
+ gpmc_t->access;
+ /* XXX: barter t_ce_rdyz with t_cez_r ? */
+ if (dev_t->t_ce_rdyz)
+ temp = max_t(u32, temp, gpmc_t->cs_rd_off + dev_t->t_ce_rdyz);
+ gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp);
+
+ return 0;
+}
+
+static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_device_timings *dev_t,
+ bool mux)
+{
+ u32 temp;
+
+ /* adv_wr_off */
+ temp = dev_t->t_avdp_w;
+ if (mux) {
+ temp = max_t(u32, temp,
+ gpmc_t->clk_activation + dev_t->t_avdh);
+ temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
+ }
+ gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp);
+
+ /* wr_data_mux_bus */
+ temp = max_t(u32, dev_t->t_weasu,
+ gpmc_t->clk_activation + dev_t->t_rdyo);
+ /* XXX: shouldn't mux be kept as a whole for wr_data_mux_bus ?,
+ * and in that case remember to handle we_on properly
+ */
+ if (mux) {
+ temp = max_t(u32, temp,
+ gpmc_t->adv_wr_off + dev_t->t_aavdh);
+ temp = max_t(u32, temp, gpmc_t->adv_wr_off +
+ gpmc_ticks_to_ps(dev_t->cyc_aavdh_we));
+ }
+ gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp);
+
+ /* we_on */
+ if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
+ gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu);
+ else
+ gpmc_t->we_on = gpmc_t->wr_data_mux_bus;
+
+ /* wr_access */
+ /* XXX: gpmc_capability check reqd ? , even if not, will not harm */
+ gpmc_t->wr_access = gpmc_t->access;
+
+ /* we_off */
+ temp = gpmc_t->we_on + dev_t->t_wpl;
+ temp = max_t(u32, temp,
+ gpmc_t->wr_access + gpmc_ticks_to_ps(1));
+ temp = max_t(u32, temp,
+ gpmc_t->we_on + gpmc_ticks_to_ps(dev_t->cyc_wpl));
+ gpmc_t->we_off = gpmc_round_ps_to_ticks(temp);
+
+ gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off +
+ dev_t->t_wph);
+
+ /* wr_cycle */
+ temp = gpmc_round_ps_to_sync_clk(dev_t->t_cez_w, gpmc_t->sync_clk);
+ temp += gpmc_t->wr_access;
+ /* XXX: barter t_ce_rdyz with t_cez_w ? */
+ if (dev_t->t_ce_rdyz)
+ temp = max_t(u32, temp,
+ gpmc_t->cs_wr_off + dev_t->t_ce_rdyz);
+ gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp);
+
+ return 0;
+}
+
+static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_device_timings *dev_t,
+ bool mux)
+{
+ u32 temp;
+
+ /* adv_rd_off */
+ temp = dev_t->t_avdp_r;
+ if (mux)
+ temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
+ gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp);
+
+ /* oe_on */
+ temp = dev_t->t_oeasu;
+ if (mux)
+ temp = max_t(u32, temp,
+ gpmc_t->adv_rd_off + dev_t->t_aavdh);
+ gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp);
+
+ /* access */
+ temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */
+ gpmc_t->oe_on + dev_t->t_oe);
+ temp = max_t(u32, temp,
+ gpmc_t->cs_on + dev_t->t_ce);
+ temp = max_t(u32, temp,
+ gpmc_t->adv_on + dev_t->t_aa);
+ gpmc_t->access = gpmc_round_ps_to_ticks(temp);
+
+ gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1);
+ gpmc_t->cs_rd_off = gpmc_t->oe_off;
+
+ /* rd_cycle */
+ temp = max_t(u32, dev_t->t_rd_cycle,
+ gpmc_t->cs_rd_off + dev_t->t_cez_r);
+ temp = max_t(u32, temp, gpmc_t->oe_off + dev_t->t_oez);
+ gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp);
+
+ return 0;
+}
+
+static int gpmc_calc_async_write_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_device_timings *dev_t,
+ bool mux)
+{
+ u32 temp;
+
+ /* adv_wr_off */
+ temp = dev_t->t_avdp_w;
+ if (mux)
+ temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
+ gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp);
+
+ /* wr_data_mux_bus */
+ temp = dev_t->t_weasu;
+ if (mux) {
+ temp = max_t(u32, temp, gpmc_t->adv_wr_off + dev_t->t_aavdh);
+ temp = max_t(u32, temp, gpmc_t->adv_wr_off +
+ gpmc_ticks_to_ps(dev_t->cyc_aavdh_we));
+ }
+ gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp);
+
+ /* we_on */
+ if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
+ gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu);
+ else
+ gpmc_t->we_on = gpmc_t->wr_data_mux_bus;
+
+ /* we_off */
+ temp = gpmc_t->we_on + dev_t->t_wpl;
+ gpmc_t->we_off = gpmc_round_ps_to_ticks(temp);
+
+ gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off +
+ dev_t->t_wph);
+
+ /* wr_cycle */
+ temp = max_t(u32, dev_t->t_wr_cycle,
+ gpmc_t->cs_wr_off + dev_t->t_cez_w);
+ gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp);
+
+ return 0;
+}
+
+static int gpmc_calc_sync_common_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_device_timings *dev_t)
+{
+ u32 temp;
+
+ gpmc_t->sync_clk = gpmc_calc_divider(dev_t->clk) *
+ gpmc_get_fclk_period();
+
+ gpmc_t->page_burst_access = gpmc_round_ps_to_sync_clk(
+ dev_t->t_bacc,
+ gpmc_t->sync_clk);
+
+ temp = max_t(u32, dev_t->t_ces, dev_t->t_avds);
+ gpmc_t->clk_activation = gpmc_round_ps_to_ticks(temp);
+
+ if (gpmc_calc_divider(gpmc_t->sync_clk) != 1)
+ return 0;
+
+ if (dev_t->ce_xdelay)
+ gpmc_t->bool_timings.cs_extra_delay = true;
+ if (dev_t->avd_xdelay)
+ gpmc_t->bool_timings.adv_extra_delay = true;
+ if (dev_t->oe_xdelay)
+ gpmc_t->bool_timings.oe_extra_delay = true;
+ if (dev_t->we_xdelay)
+ gpmc_t->bool_timings.we_extra_delay = true;
+
+ return 0;
+}
+
+static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_device_timings *dev_t,
+ bool sync)
+{
+ u32 temp;
+
+ /* cs_on */
+ gpmc_t->cs_on = gpmc_round_ps_to_ticks(dev_t->t_ceasu);
+
+ /* adv_on */
+ temp = dev_t->t_avdasu;
+ if (dev_t->t_ce_avd)
+ temp = max_t(u32, temp,
+ gpmc_t->cs_on + dev_t->t_ce_avd);
+ gpmc_t->adv_on = gpmc_round_ps_to_ticks(temp);
+
+ if (sync)
+ gpmc_calc_sync_common_timings(gpmc_t, dev_t);
+
+ return 0;
+}
+
+/* TODO: remove this function once all peripherals are confirmed to
+ * work with generic timing. Simultaneously gpmc_cs_set_timings()
+ * has to be modified to handle timings in ps instead of ns
+*/
+static void gpmc_convert_ps_to_ns(struct gpmc_timings *t)
+{
+ t->cs_on /= 1000;
+ t->cs_rd_off /= 1000;
+ t->cs_wr_off /= 1000;
+ t->adv_on /= 1000;
+ t->adv_rd_off /= 1000;
+ t->adv_wr_off /= 1000;
+ t->we_on /= 1000;
+ t->we_off /= 1000;
+ t->oe_on /= 1000;
+ t->oe_off /= 1000;
+ t->page_burst_access /= 1000;
+ t->access /= 1000;
+ t->rd_cycle /= 1000;
+ t->wr_cycle /= 1000;
+ t->bus_turnaround /= 1000;
+ t->cycle2cycle_delay /= 1000;
+ t->wait_monitoring /= 1000;
+ t->clk_activation /= 1000;
+ t->wr_access /= 1000;
+ t->wr_data_mux_bus /= 1000;
+}
+
+int gpmc_calc_timings(struct gpmc_timings *gpmc_t,
+ struct gpmc_settings *gpmc_s,
+ struct gpmc_device_timings *dev_t)
+{
+ bool mux = false, sync = false;
+
+ if (gpmc_s) {
+ mux = gpmc_s->mux_add_data ? true : false;
+ sync = (gpmc_s->sync_read || gpmc_s->sync_write);
+ }
+
+ memset(gpmc_t, 0, sizeof(*gpmc_t));
+
+ gpmc_calc_common_timings(gpmc_t, dev_t, sync);
+
+ if (gpmc_s && gpmc_s->sync_read)
+ gpmc_calc_sync_read_timings(gpmc_t, dev_t, mux);
+ else
+ gpmc_calc_async_read_timings(gpmc_t, dev_t, mux);
+
+ if (gpmc_s && gpmc_s->sync_write)
+ gpmc_calc_sync_write_timings(gpmc_t, dev_t, mux);
+ else
+ gpmc_calc_async_write_timings(gpmc_t, dev_t, mux);
+
+ /* TODO: remove, see function definition */
+ gpmc_convert_ps_to_ns(gpmc_t);
+
+ return 0;
+}
+
+/**
+ * gpmc_cs_program_settings - programs non-timing related settings
+ * @cs: GPMC chip-select to program
+ * @p: pointer to GPMC settings structure
+ *
+ * Programs non-timing related settings for a GPMC chip-select, such as
+ * bus-width, burst configuration, etc. Function should be called once
+ * for each chip-select that is being used and must be called before
+ * calling gpmc_cs_set_timings() as timing parameters in the CONFIG1
+ * register will be initialised to zero by this function. Returns 0 on
+ * success and appropriate negative error code on failure.
+ */
+int gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
+{
+ u32 config1;
+
+ if ((!p->device_width) || (p->device_width > GPMC_DEVWIDTH_16BIT)) {
+ pr_err("%s: invalid width %d!", __func__, p->device_width);
+ return -EINVAL;
+ }
+
+ /* Address-data multiplexing not supported for NAND devices */
+ if (p->device_nand && p->mux_add_data) {
+ pr_err("%s: invalid configuration!\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((p->mux_add_data > GPMC_MUX_AD) ||
+ ((p->mux_add_data == GPMC_MUX_AAD) &&
+ !(gpmc_capability & GPMC_HAS_MUX_AAD))) {
+ pr_err("%s: invalid multiplex configuration!\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Page/burst mode supports lengths of 4, 8 and 16 bytes */
+ if (p->burst_read || p->burst_write) {
+ switch (p->burst_len) {
+ case GPMC_BURST_4:
+ case GPMC_BURST_8:
+ case GPMC_BURST_16:
+ break;
+ default:
+ pr_err("%s: invalid page/burst-length (%d)\n",
+ __func__, p->burst_len);
+ return -EINVAL;
+ }
+ }
+
+ if (p->wait_pin > gpmc_nr_waitpins) {
+ pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin);
+ return -EINVAL;
+ }
+
+ config1 = GPMC_CONFIG1_DEVICESIZE((p->device_width - 1));
+
+ if (p->sync_read)
+ config1 |= GPMC_CONFIG1_READTYPE_SYNC;
+ if (p->sync_write)
+ config1 |= GPMC_CONFIG1_WRITETYPE_SYNC;
+ if (p->wait_on_read)
+ config1 |= GPMC_CONFIG1_WAIT_READ_MON;
+ if (p->wait_on_write)
+ config1 |= GPMC_CONFIG1_WAIT_WRITE_MON;
+ if (p->wait_on_read || p->wait_on_write)
+ config1 |= GPMC_CONFIG1_WAIT_PIN_SEL(p->wait_pin);
+ if (p->device_nand)
+ config1 |= GPMC_CONFIG1_DEVICETYPE(GPMC_DEVICETYPE_NAND);
+ if (p->mux_add_data)
+ config1 |= GPMC_CONFIG1_MUXTYPE(p->mux_add_data);
+ if (p->burst_read)
+ config1 |= GPMC_CONFIG1_READMULTIPLE_SUPP;
+ if (p->burst_write)
+ config1 |= GPMC_CONFIG1_WRITEMULTIPLE_SUPP;
+ if (p->burst_read || p->burst_write) {
+ config1 |= GPMC_CONFIG1_PAGE_LEN(p->burst_len >> 3);
+ config1 |= p->burst_wrap ? GPMC_CONFIG1_WRAPBURST_SUPP : 0;
+ }
+
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, config1);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpmc_dt_ids[] = {
+ { .compatible = "ti,omap2420-gpmc" },
+ { .compatible = "ti,omap2430-gpmc" },
+ { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */
+ { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */
+ { .compatible = "ti,am3352-gpmc" }, /* am335x devices */
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpmc_dt_ids);
+
+/**
+ * gpmc_read_settings_dt - read gpmc settings from device-tree
+ * @np: pointer to device-tree node for a gpmc child device
+ * @p: pointer to gpmc settings structure
+ *
+ * Reads the GPMC settings for a GPMC child device from device-tree and
+ * stores them in the GPMC settings structure passed. The GPMC settings
+ * structure is initialised to zero by this function and so any
+ * previously stored settings will be cleared.
+ */
+void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
+{
+ memset(p, 0, sizeof(struct gpmc_settings));
+
+ p->sync_read = of_property_read_bool(np, "gpmc,sync-read");
+ p->sync_write = of_property_read_bool(np, "gpmc,sync-write");
+ of_property_read_u32(np, "gpmc,device-width", &p->device_width);
+ of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data);
+
+ if (!of_property_read_u32(np, "gpmc,burst-length", &p->burst_len)) {
+ p->burst_wrap = of_property_read_bool(np, "gpmc,burst-wrap");
+ p->burst_read = of_property_read_bool(np, "gpmc,burst-read");
+ p->burst_write = of_property_read_bool(np, "gpmc,burst-write");
+ if (!p->burst_read && !p->burst_write)
+ pr_warn("%s: page/burst-length set but not used!\n",
+ __func__);
+ }
+
+ if (!of_property_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) {
+ p->wait_on_read = of_property_read_bool(np,
+ "gpmc,wait-on-read");
+ p->wait_on_write = of_property_read_bool(np,
+ "gpmc,wait-on-write");
+ if (!p->wait_on_read && !p->wait_on_write)
+ pr_debug("%s: rd/wr wait monitoring not enabled!\n",
+ __func__);
+ }
+}
+
+static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
+ struct gpmc_timings *gpmc_t)
+{
+ struct gpmc_bool_timings *p;
+
+ if (!np || !gpmc_t)
+ return;
+
+ memset(gpmc_t, 0, sizeof(*gpmc_t));
+
+ /* minimum clock period for syncronous mode */
+ of_property_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk);
+
+ /* chip select timtings */
+ of_property_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on);
+ of_property_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off);
+ of_property_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off);
+
+ /* ADV signal timings */
+ of_property_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on);
+ of_property_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off);
+ of_property_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off);
+
+ /* WE signal timings */
+ of_property_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on);
+ of_property_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off);
+
+ /* OE signal timings */
+ of_property_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on);
+ of_property_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off);
+
+ /* access and cycle timings */
+ of_property_read_u32(np, "gpmc,page-burst-access-ns",
+ &gpmc_t->page_burst_access);
+ of_property_read_u32(np, "gpmc,access-ns", &gpmc_t->access);
+ of_property_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle);
+ of_property_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle);
+ of_property_read_u32(np, "gpmc,bus-turnaround-ns",
+ &gpmc_t->bus_turnaround);
+ of_property_read_u32(np, "gpmc,cycle2cycle-delay-ns",
+ &gpmc_t->cycle2cycle_delay);
+ of_property_read_u32(np, "gpmc,wait-monitoring-ns",
+ &gpmc_t->wait_monitoring);
+ of_property_read_u32(np, "gpmc,clk-activation-ns",
+ &gpmc_t->clk_activation);
+
+ /* only applicable to OMAP3+ */
+ of_property_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access);
+ of_property_read_u32(np, "gpmc,wr-data-mux-bus-ns",
+ &gpmc_t->wr_data_mux_bus);
+
+ /* bool timing parameters */
+ p = &gpmc_t->bool_timings;
+
+ p->cycle2cyclediffcsen =
+ of_property_read_bool(np, "gpmc,cycle2cycle-diffcsen");
+ p->cycle2cyclesamecsen =
+ of_property_read_bool(np, "gpmc,cycle2cycle-samecsen");
+ p->we_extra_delay = of_property_read_bool(np, "gpmc,we-extra-delay");
+ p->oe_extra_delay = of_property_read_bool(np, "gpmc,oe-extra-delay");
+ p->adv_extra_delay = of_property_read_bool(np, "gpmc,adv-extra-delay");
+ p->cs_extra_delay = of_property_read_bool(np, "gpmc,cs-extra-delay");
+ p->time_para_granularity =
+ of_property_read_bool(np, "gpmc,time-para-granularity");
+}
+
+#if IS_ENABLED(CONFIG_MTD_NAND)
+
+static const char * const nand_xfer_types[] = {
+ [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
+ [NAND_OMAP_POLLED] = "polled",
+ [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
+ [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
+};
+
+static int gpmc_probe_nand_child(struct platform_device *pdev,
+ struct device_node *child)
+{
+ u32 val;
+ const char *s;
+ struct gpmc_timings gpmc_t;
+ struct omap_nand_platform_data *gpmc_nand_data;
+
+ if (of_property_read_u32(child, "reg", &val) < 0) {
+ dev_err(&pdev->dev, "%s has no 'reg' property\n",
+ child->full_name);
+ return -ENODEV;
+ }
+
+ gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data),
+ GFP_KERNEL);
+ if (!gpmc_nand_data)
+ return -ENOMEM;
+
+ gpmc_nand_data->cs = val;
+ gpmc_nand_data->of_node = child;
+
+ /* Detect availability of ELM module */
+ gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
+ if (gpmc_nand_data->elm_of_node == NULL)
+ gpmc_nand_data->elm_of_node =
+ of_parse_phandle(child, "elm_id", 0);
+
+ /* select ecc-scheme for NAND */
+ if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
+ pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!strcmp(s, "sw"))
+ gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
+ else if (!strcmp(s, "ham1") ||
+ !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_HAM1_CODE_HW;
+ else if (!strcmp(s, "bch4"))
+ if (gpmc_nand_data->elm_of_node)
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH4_CODE_HW;
+ else
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
+ else if (!strcmp(s, "bch8"))
+ if (gpmc_nand_data->elm_of_node)
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH8_CODE_HW;
+ else
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+ else if (!strcmp(s, "bch16"))
+ if (gpmc_nand_data->elm_of_node)
+ gpmc_nand_data->ecc_opt =
+ OMAP_ECC_BCH16_CODE_HW;
+ else
+ pr_err("%s: BCH16 requires ELM support\n", __func__);
+ else
+ pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
+
+ /* select data transfer mode for NAND controller */
+ if (!of_property_read_string(child, "ti,nand-xfer-type", &s))
+ for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++)
+ if (!strcasecmp(s, nand_xfer_types[val])) {
+ gpmc_nand_data->xfer_type = val;
+ break;
+ }
+
+ gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child);
+
+ val = of_get_nand_bus_width(child);
+ if (val == 16)
+ gpmc_nand_data->devsize = NAND_BUSWIDTH_16;
+
+ gpmc_read_timings_dt(child, &gpmc_t);
+ gpmc_nand_init(gpmc_nand_data, &gpmc_t);
+
+ return 0;
+}
+#else
+static int gpmc_probe_nand_child(struct platform_device *pdev,
+ struct device_node *child)
+{
+ return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_MTD_ONENAND)
+static int gpmc_probe_onenand_child(struct platform_device *pdev,
+ struct device_node *child)
+{
+ u32 val;
+ struct omap_onenand_platform_data *gpmc_onenand_data;
+
+ if (of_property_read_u32(child, "reg", &val) < 0) {
+ dev_err(&pdev->dev, "%s has no 'reg' property\n",
+ child->full_name);
+ return -ENODEV;
+ }
+
+ gpmc_onenand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_onenand_data),
+ GFP_KERNEL);
+ if (!gpmc_onenand_data)
+ return -ENOMEM;
+
+ gpmc_onenand_data->cs = val;
+ gpmc_onenand_data->of_node = child;
+ gpmc_onenand_data->dma_channel = -1;
+
+ if (!of_property_read_u32(child, "dma-channel", &val))
+ gpmc_onenand_data->dma_channel = val;
+
+ gpmc_onenand_init(gpmc_onenand_data);
+
+ return 0;
+}
+#else
+static int gpmc_probe_onenand_child(struct platform_device *pdev,
+ struct device_node *child)
+{
+ return 0;
+}
+#endif
+
+/**
+ * gpmc_probe_generic_child - configures the gpmc for a child device
+ * @pdev: pointer to gpmc platform device
+ * @child: pointer to device-tree node for child device
+ *
+ * Allocates and configures a GPMC chip-select for a child device.
+ * Returns 0 on success and appropriate negative error code on failure.
+ */
+static int gpmc_probe_generic_child(struct platform_device *pdev,
+ struct device_node *child)
+{
+ struct gpmc_settings gpmc_s;
+ struct gpmc_timings gpmc_t;
+ struct resource res;
+ unsigned long base;
+ const char *name;
+ int ret, cs;
+ u32 val;
+
+ if (of_property_read_u32(child, "reg", &cs) < 0) {
+ dev_err(&pdev->dev, "%s has no 'reg' property\n",
+ child->full_name);
+ return -ENODEV;
+ }
+
+ if (of_address_to_resource(child, 0, &res) < 0) {
+ dev_err(&pdev->dev, "%s has malformed 'reg' property\n",
+ child->full_name);
+ return -ENODEV;
+ }
+
+ /*
+ * Check if we have multiple instances of the same device
+ * on a single chip select. If so, use the already initialized
+ * timings.
+ */
+ name = gpmc_cs_get_name(cs);
+ if (name && child->name && of_node_cmp(child->name, name) == 0)
+ goto no_timings;
+
+ ret = gpmc_cs_request(cs, resource_size(&res), &base);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs);
+ return ret;
+ }
+ gpmc_cs_set_name(cs, child->name);
+
+ gpmc_read_settings_dt(child, &gpmc_s);
+ gpmc_read_timings_dt(child, &gpmc_t);
+
+ /*
+ * For some GPMC devices we still need to rely on the bootloader
+ * timings because the devices can be connected via FPGA.
+ * REVISIT: Add timing support from slls644g.pdf.
+ */
+ if (!gpmc_t.cs_rd_off) {
+ WARN(1, "enable GPMC debug to configure .dts timings for CS%i\n",
+ cs);
+ gpmc_cs_show_timings(cs,
+ "please add GPMC bootloader timings to .dts");
+ goto no_timings;
+ }
+
+ /* CS must be disabled while making changes to gpmc configuration */
+ gpmc_cs_disable_mem(cs);
+
+ /*
+ * FIXME: gpmc_cs_request() will map the CS to an arbitary
+ * location in the gpmc address space. When booting with
+ * device-tree we want the NOR flash to be mapped to the
+ * location specified in the device-tree blob. So remap the
+ * CS to this location. Once DT migration is complete should
+ * just make gpmc_cs_request() map a specific address.
+ */
+ ret = gpmc_cs_remap(cs, res.start);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
+ cs, &res.start);
+ goto err;
+ }
+
+ ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width);
+ if (ret < 0)
+ goto err;
+
+ ret = gpmc_cs_program_settings(cs, &gpmc_s);
+ if (ret < 0)
+ goto err;
+
+ ret = gpmc_cs_set_timings(cs, &gpmc_t);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n",
+ child->name);
+ goto err;
+ }
+
+ /* Clear limited address i.e. enable A26-A11 */
+ val = gpmc_read_reg(GPMC_CONFIG);
+ val &= ~GPMC_CONFIG_LIMITEDADDRESS;
+ gpmc_write_reg(GPMC_CONFIG, val);
+
+ /* Enable CS region */
+ gpmc_cs_enable_mem(cs);
+
+no_timings:
+ if (of_platform_device_create(child, NULL, &pdev->dev))
+ return 0;
+
+ dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
+ ret = -ENODEV;
+
+err:
+ gpmc_cs_free(cs);
+
+ return ret;
+}
+
+static int gpmc_probe_dt(struct platform_device *pdev)
+{
+ int ret;
+ struct device_node *child;
+ const struct of_device_id *of_id =
+ of_match_device(gpmc_dt_ids, &pdev->dev);
+
+ if (!of_id)
+ return 0;
+
+ ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-cs",
+ &gpmc_cs_num);
+ if (ret < 0) {
+ pr_err("%s: number of chip-selects not defined\n", __func__);
+ return ret;
+ } else if (gpmc_cs_num < 1) {
+ pr_err("%s: all chip-selects are disabled\n", __func__);
+ return -EINVAL;
+ } else if (gpmc_cs_num > GPMC_CS_NUM) {
+ pr_err("%s: number of supported chip-selects cannot be > %d\n",
+ __func__, GPMC_CS_NUM);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins",
+ &gpmc_nr_waitpins);
+ if (ret < 0) {
+ pr_err("%s: number of wait pins not found!\n", __func__);
+ return ret;
+ }
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+
+ if (!child->name)
+ continue;
+
+ if (of_node_cmp(child->name, "nand") == 0)
+ ret = gpmc_probe_nand_child(pdev, child);
+ else if (of_node_cmp(child->name, "onenand") == 0)
+ ret = gpmc_probe_onenand_child(pdev, child);
+ else if (of_node_cmp(child->name, "ethernet") == 0 ||
+ of_node_cmp(child->name, "nor") == 0 ||
+ of_node_cmp(child->name, "uart") == 0)
+ ret = gpmc_probe_generic_child(pdev, child);
+
+ if (WARN(ret < 0, "%s: probing gpmc child %s failed\n",
+ __func__, child->full_name))
+ of_node_put(child);
+ }
+
+ return 0;
+}
+#else
+static int gpmc_probe_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+static int gpmc_probe(struct platform_device *pdev)
+{
+ int rc;
+ u32 l;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -ENOENT;
+
+ phys_base = res->start;
+ mem_size = resource_size(res);
+
+ gpmc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpmc_base))
+ return PTR_ERR(gpmc_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL)
+ dev_warn(&pdev->dev, "Failed to get resource: irq\n");
+ else
+ gpmc_irq = res->start;
+
+ gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(gpmc_l3_clk)) {
+ dev_err(&pdev->dev, "Failed to get GPMC fck\n");
+ gpmc_irq = 0;
+ return PTR_ERR(gpmc_l3_clk);
+ }
+
+ if (!clk_get_rate(gpmc_l3_clk)) {
+ dev_err(&pdev->dev, "Invalid GPMC fck clock rate\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ gpmc_dev = &pdev->dev;
+
+ l = gpmc_read_reg(GPMC_REVISION);
+
+ /*
+ * FIXME: Once device-tree migration is complete the below flags
+ * should be populated based upon the device-tree compatible
+ * string. For now just use the IP revision. OMAP3+ devices have
+ * the wr_access and wr_data_mux_bus register fields. OMAP4+
+ * devices support the addr-addr-data multiplex protocol.
+ *
+ * GPMC IP revisions:
+ * - OMAP24xx = 2.0
+ * - OMAP3xxx = 5.0
+ * - OMAP44xx/54xx/AM335x = 6.0
+ */
+ if (GPMC_REVISION_MAJOR(l) > 0x4)
+ gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
+ if (GPMC_REVISION_MAJOR(l) > 0x5)
+ gpmc_capability |= GPMC_HAS_MUX_AAD;
+ dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
+ GPMC_REVISION_MINOR(l));
+
+ gpmc_mem_init();
+
+ if (gpmc_setup_irq() < 0)
+ dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
+
+ if (!pdev->dev.of_node) {
+ gpmc_cs_num = GPMC_CS_NUM;
+ gpmc_nr_waitpins = GPMC_NR_WAITPINS;
+ }
+
+ rc = gpmc_probe_dt(pdev);
+ if (rc < 0) {
+ pm_runtime_put_sync(&pdev->dev);
+ dev_err(gpmc_dev, "failed to probe DT parameters\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int gpmc_remove(struct platform_device *pdev)
+{
+ gpmc_free_irq();
+ gpmc_mem_exit();
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ gpmc_dev = NULL;
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int gpmc_suspend(struct device *dev)
+{
+ omap3_gpmc_save_context();
+ pm_runtime_put_sync(dev);
+ return 0;
+}
+
+static int gpmc_resume(struct device *dev)
+{
+ pm_runtime_get_sync(dev);
+ omap3_gpmc_restore_context();
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume);
+
+static struct platform_driver gpmc_driver = {
+ .probe = gpmc_probe,
+ .remove = gpmc_remove,
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = of_match_ptr(gpmc_dt_ids),
+ .pm = &gpmc_pm_ops,
+ },
+};
+
+static __init int gpmc_init(void)
+{
+ return platform_driver_register(&gpmc_driver);
+}
+
+static __exit void gpmc_exit(void)
+{
+ platform_driver_unregister(&gpmc_driver);
+
+}
+
+postcore_initcall(gpmc_init);
+module_exit(gpmc_exit);
+
+static irqreturn_t gpmc_handle_irq(int irq, void *dev)
+{
+ int i;
+ u32 regval;
+
+ regval = gpmc_read_reg(GPMC_IRQSTATUS);
+
+ if (!regval)
+ return IRQ_NONE;
+
+ for (i = 0; i < GPMC_NR_IRQ; i++)
+ if (regval & gpmc_client_irq[i].bitmask)
+ generic_handle_irq(gpmc_client_irq[i].irq);
+
+ gpmc_write_reg(GPMC_IRQSTATUS, regval);
+
+ return IRQ_HANDLED;
+}
+
+static struct omap3_gpmc_regs gpmc_context;
+
+void omap3_gpmc_save_context(void)
+{
+ int i;
+
+ gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
+ gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
+ gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
+ gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
+ gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
+ gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
+ gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
+ for (i = 0; i < gpmc_cs_num; i++) {
+ gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
+ if (gpmc_context.cs_context[i].is_valid) {
+ gpmc_context.cs_context[i].config1 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
+ gpmc_context.cs_context[i].config2 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
+ gpmc_context.cs_context[i].config3 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
+ gpmc_context.cs_context[i].config4 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
+ gpmc_context.cs_context[i].config5 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
+ gpmc_context.cs_context[i].config6 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
+ gpmc_context.cs_context[i].config7 =
+ gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
+ }
+ }
+}
+
+void omap3_gpmc_restore_context(void)
+{
+ int i;
+
+ gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
+ gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
+ gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
+ gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
+ gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
+ gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
+ for (i = 0; i < gpmc_cs_num; i++) {
+ if (gpmc_context.cs_context[i].is_valid) {
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
+ gpmc_context.cs_context[i].config1);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
+ gpmc_context.cs_context[i].config2);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
+ gpmc_context.cs_context[i].config3);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
+ gpmc_context.cs_context[i].config4);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
+ gpmc_context.cs_context[i].config5);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
+ gpmc_context.cs_context[i].config6);
+ gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
+ gpmc_context.cs_context[i].config7);
+ }
+ }
+}
diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
new file mode 100644
index 000000000000..571087621827
--- /dev/null
+++ b/drivers/memory/tegra/Kconfig
@@ -0,0 +1,7 @@
+config TEGRA_MC
+ bool "NVIDIA Tegra Memory Controller support"
+ default y
+ depends on ARCH_TEGRA
+ help
+ This driver supports the Memory Controller (MC) hardware found on
+ NVIDIA Tegra SoCs.
diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile
new file mode 100644
index 000000000000..0d9f497b786c
--- /dev/null
+++ b/drivers/memory/tegra/Makefile
@@ -0,0 +1,7 @@
+tegra-mc-y := mc.o
+
+tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30.o
+tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o
+tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o
+
+obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
new file mode 100644
index 000000000000..fe3c44e7e1d1
--- /dev/null
+++ b/drivers/memory/tegra/mc.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "mc.h"
+
+#define MC_INTSTATUS 0x000
+#define MC_INT_DECERR_MTS (1 << 16)
+#define MC_INT_SECERR_SEC (1 << 13)
+#define MC_INT_DECERR_VPR (1 << 12)
+#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
+#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
+#define MC_INT_ARBITRATION_EMEM (1 << 9)
+#define MC_INT_SECURITY_VIOLATION (1 << 8)
+#define MC_INT_DECERR_EMEM (1 << 6)
+
+#define MC_INTMASK 0x004
+
+#define MC_ERR_STATUS 0x08
+#define MC_ERR_STATUS_TYPE_SHIFT 28
+#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT)
+#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT)
+#define MC_ERR_STATUS_READABLE (1 << 27)
+#define MC_ERR_STATUS_WRITABLE (1 << 26)
+#define MC_ERR_STATUS_NONSECURE (1 << 25)
+#define MC_ERR_STATUS_ADR_HI_SHIFT 20
+#define MC_ERR_STATUS_ADR_HI_MASK 0x3
+#define MC_ERR_STATUS_SECURITY (1 << 17)
+#define MC_ERR_STATUS_RW (1 << 16)
+#define MC_ERR_STATUS_CLIENT_MASK 0x7f
+
+#define MC_ERR_ADR 0x0c
+
+#define MC_EMEM_ARB_CFG 0x90
+#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0)
+#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
+#define MC_EMEM_ARB_MISC0 0xd8
+
+static const struct of_device_id tegra_mc_of_match[] = {
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+ { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_124_SOC
+ { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc },
+#endif
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
+
+static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
+{
+ unsigned long long tick;
+ unsigned int i;
+ u32 value;
+
+ /* compute the number of MC clock cycles per tick */
+ tick = mc->tick * clk_get_rate(mc->clk);
+ do_div(tick, NSEC_PER_SEC);
+
+ value = readl(mc->regs + MC_EMEM_ARB_CFG);
+ value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK;
+ value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick);
+ writel(value, mc->regs + MC_EMEM_ARB_CFG);
+
+ /* write latency allowance defaults */
+ for (i = 0; i < mc->soc->num_clients; i++) {
+ const struct tegra_mc_la *la = &mc->soc->clients[i].la;
+ u32 value;
+
+ value = readl(mc->regs + la->reg);
+ value &= ~(la->mask << la->shift);
+ value |= (la->def & la->mask) << la->shift;
+ writel(value, mc->regs + la->reg);
+ }
+
+ return 0;
+}
+
+static const char *const status_names[32] = {
+ [ 1] = "External interrupt",
+ [ 6] = "EMEM address decode error",
+ [ 8] = "Security violation",
+ [ 9] = "EMEM arbitration error",
+ [10] = "Page fault",
+ [11] = "Invalid APB ASID update",
+ [12] = "VPR violation",
+ [13] = "Secure carveout violation",
+ [16] = "MTS carveout violation",
+};
+
+static const char *const error_names[8] = {
+ [2] = "EMEM decode error",
+ [3] = "TrustZone violation",
+ [4] = "Carveout violation",
+ [6] = "SMMU translation error",
+};
+
+static irqreturn_t tegra_mc_irq(int irq, void *data)
+{
+ struct tegra_mc *mc = data;
+ unsigned long status, mask;
+ unsigned int bit;
+
+ /* mask all interrupts to avoid flooding */
+ status = mc_readl(mc, MC_INTSTATUS);
+ mask = mc_readl(mc, MC_INTMASK);
+
+ for_each_set_bit(bit, &status, 32) {
+ const char *error = status_names[bit] ?: "unknown";
+ const char *client = "unknown", *desc;
+ const char *direction, *secure;
+ phys_addr_t addr = 0;
+ unsigned int i;
+ char perm[7];
+ u8 id, type;
+ u32 value;
+
+ value = mc_readl(mc, MC_ERR_STATUS);
+
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+ if (mc->soc->num_address_bits > 32) {
+ addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
+ MC_ERR_STATUS_ADR_HI_MASK);
+ addr <<= 32;
+ }
+#endif
+
+ if (value & MC_ERR_STATUS_RW)
+ direction = "write";
+ else
+ direction = "read";
+
+ if (value & MC_ERR_STATUS_SECURITY)
+ secure = "secure ";
+ else
+ secure = "";
+
+ id = value & MC_ERR_STATUS_CLIENT_MASK;
+
+ for (i = 0; i < mc->soc->num_clients; i++) {
+ if (mc->soc->clients[i].id == id) {
+ client = mc->soc->clients[i].name;
+ break;
+ }
+ }
+
+ type = (value & MC_ERR_STATUS_TYPE_MASK) >>
+ MC_ERR_STATUS_TYPE_SHIFT;
+ desc = error_names[type];
+
+ switch (value & MC_ERR_STATUS_TYPE_MASK) {
+ case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
+ perm[0] = ' ';
+ perm[1] = '[';
+
+ if (value & MC_ERR_STATUS_READABLE)
+ perm[2] = 'R';
+ else
+ perm[2] = '-';
+
+ if (value & MC_ERR_STATUS_WRITABLE)
+ perm[3] = 'W';
+ else
+ perm[3] = '-';
+
+ if (value & MC_ERR_STATUS_NONSECURE)
+ perm[4] = '-';
+ else
+ perm[4] = 'S';
+
+ perm[5] = ']';
+ perm[6] = '\0';
+ break;
+
+ default:
+ perm[0] = '\0';
+ break;
+ }
+
+ value = mc_readl(mc, MC_ERR_ADR);
+ addr |= value;
+
+ dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
+ client, secure, direction, &addr, error,
+ desc, perm);
+ }
+
+ /* clear interrupts */
+ mc_writel(mc, status, MC_INTSTATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int tegra_mc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct resource *res;
+ struct tegra_mc *mc;
+ u32 value;
+ int err;
+
+ match = of_match_node(tegra_mc_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENODEV;
+
+ mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mc);
+ mc->soc = match->data;
+ mc->dev = &pdev->dev;
+
+ /* length of MC tick in nanoseconds */
+ mc->tick = 30;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mc->regs))
+ return PTR_ERR(mc->regs);
+
+ mc->clk = devm_clk_get(&pdev->dev, "mc");
+ if (IS_ERR(mc->clk)) {
+ dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
+ PTR_ERR(mc->clk));
+ return PTR_ERR(mc->clk);
+ }
+
+ err = tegra_mc_setup_latency_allowance(mc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to setup latency allowance: %d\n",
+ err);
+ return err;
+ }
+
+ if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) {
+ mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
+ if (IS_ERR(mc->smmu)) {
+ dev_err(&pdev->dev, "failed to probe SMMU: %ld\n",
+ PTR_ERR(mc->smmu));
+ return PTR_ERR(mc->smmu);
+ }
+ }
+
+ mc->irq = platform_get_irq(pdev, 0);
+ if (mc->irq < 0) {
+ dev_err(&pdev->dev, "interrupt not specified\n");
+ return mc->irq;
+ }
+
+ err = devm_request_irq(&pdev->dev, mc->irq, tegra_mc_irq, IRQF_SHARED,
+ dev_name(&pdev->dev), mc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
+ err);
+ return err;
+ }
+
+ value = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
+ MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE |
+ MC_INT_ARBITRATION_EMEM | MC_INT_SECURITY_VIOLATION |
+ MC_INT_DECERR_EMEM;
+ mc_writel(mc, value, MC_INTMASK);
+
+ return 0;
+}
+
+static struct platform_driver tegra_mc_driver = {
+ .driver = {
+ .name = "tegra-mc",
+ .of_match_table = tegra_mc_of_match,
+ .suppress_bind_attrs = true,
+ },
+ .prevent_deferred_probe = true,
+ .probe = tegra_mc_probe,
+};
+
+static int tegra_mc_init(void)
+{
+ return platform_driver_register(&tegra_mc_driver);
+}
+arch_initcall(tegra_mc_init);
+
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
new file mode 100644
index 000000000000..d5d21147fc77
--- /dev/null
+++ b/drivers/memory/tegra/mc.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef MEMORY_TEGRA_MC_H
+#define MEMORY_TEGRA_MC_H
+
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include <soc/tegra/mc.h>
+
+static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
+{
+ return readl(mc->regs + offset);
+}
+
+static inline void mc_writel(struct tegra_mc *mc, u32 value,
+ unsigned long offset)
+{
+ writel(value, mc->regs + offset);
+}
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+extern const struct tegra_mc_soc tegra30_mc_soc;
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_114_SOC
+extern const struct tegra_mc_soc tegra114_mc_soc;
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_124_SOC
+extern const struct tegra_mc_soc tegra124_mc_soc;
+#endif
+
+#endif /* MEMORY_TEGRA_MC_H */
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
new file mode 100644
index 000000000000..511e9a25c151
--- /dev/null
+++ b/drivers/memory/tegra/tegra114.c
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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/of.h>
+#include <linux/mm.h>
+
+#include <asm/cacheflush.h>
+
+#include <dt-bindings/memory/tegra114-mc.h>
+
+#include "mc.h"
+
+static const struct tegra_mc_client tegra114_mc_clients[] = {
+ {
+ .id = 0x00,
+ .name = "ptcr",
+ .swgroup = TEGRA_SWGROUP_PTC,
+ }, {
+ .id = 0x01,
+ .name = "display0a",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x2e8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x02,
+ .name = "display0ab",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x2f4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x03,
+ .name = "display0b",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x2e8,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x04,
+ .name = "display0bb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x2f4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x05,
+ .name = "display0c",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x2ec,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x06,
+ .name = "display0cb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 6,
+ },
+ .la = {
+ .reg = 0x2f8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x09,
+ .name = "eppup",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 9,
+ },
+ .la = {
+ .reg = 0x300,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x33,
+ },
+ }, {
+ .id = 0x0a,
+ .name = "g2pr",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 10,
+ },
+ .la = {
+ .reg = 0x308,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x09,
+ },
+ }, {
+ .id = 0x0b,
+ .name = "g2sr",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x308,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x09,
+ },
+ }, {
+ .id = 0x0f,
+ .name = "avpcarm7r",
+ .swgroup = TEGRA_SWGROUP_AVPC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 15,
+ },
+ .la = {
+ .reg = 0x2e4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x10,
+ .name = "displayhc",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x2f0,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x68,
+ },
+ }, {
+ .id = 0x11,
+ .name = "displayhcb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x2fc,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x68,
+ },
+ }, {
+ .id = 0x12,
+ .name = "fdcdrd",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 18,
+ },
+ .la = {
+ .reg = 0x334,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0c,
+ },
+ }, {
+ .id = 0x13,
+ .name = "fdcdrd2",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 19,
+ },
+ .la = {
+ .reg = 0x33c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0c,
+ },
+ }, {
+ .id = 0x14,
+ .name = "g2dr",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 20,
+ },
+ .la = {
+ .reg = 0x30c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0a,
+ },
+ }, {
+ .id = 0x15,
+ .name = "hdar",
+ .swgroup = TEGRA_SWGROUP_HDA,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x318,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x16,
+ .name = "host1xdmar",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x310,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x17,
+ .name = "host1xr",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 23,
+ },
+ .la = {
+ .reg = 0x310,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x18,
+ .name = "idxsrd",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 24,
+ },
+ .la = {
+ .reg = 0x334,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0b,
+ },
+ }, {
+ .id = 0x1c,
+ .name = "msencsrd",
+ .swgroup = TEGRA_SWGROUP_MSENC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 28,
+ },
+ .la = {
+ .reg = 0x328,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x1d,
+ .name = "ppcsahbdmar",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 29,
+ },
+ .la = {
+ .reg = 0x344,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x1e,
+ .name = "ppcsahbslvr",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 30,
+ },
+ .la = {
+ .reg = 0x344,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xe8,
+ },
+ }, {
+ .id = 0x20,
+ .name = "texl2srd",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 0,
+ },
+ .la = {
+ .reg = 0x338,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0c,
+ },
+ }, {
+ .id = 0x22,
+ .name = "vdebsevr",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x354,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x23,
+ .name = "vdember",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x354,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x24,
+ .name = "vdemcer",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x358,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xb8,
+ },
+ }, {
+ .id = 0x25,
+ .name = "vdetper",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x358,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xee,
+ },
+ }, {
+ .id = 0x26,
+ .name = "mpcorelpr",
+ .swgroup = TEGRA_SWGROUP_MPCORELP,
+ .la = {
+ .reg = 0x324,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x27,
+ .name = "mpcorer",
+ .swgroup = TEGRA_SWGROUP_MPCORE,
+ .la = {
+ .reg = 0x320,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x28,
+ .name = "eppu",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 8,
+ },
+ .la = {
+ .reg = 0x300,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x33,
+ },
+ }, {
+ .id = 0x29,
+ .name = "eppv",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 9,
+ },
+ .la = {
+ .reg = 0x304,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x6c,
+ },
+ }, {
+ .id = 0x2a,
+ .name = "eppy",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 10,
+ },
+ .la = {
+ .reg = 0x304,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x6c,
+ },
+ }, {
+ .id = 0x2b,
+ .name = "msencswr",
+ .swgroup = TEGRA_SWGROUP_MSENC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x328,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x2c,
+ .name = "viwsb",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 12,
+ },
+ .la = {
+ .reg = 0x364,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x47,
+ },
+ }, {
+ .id = 0x2d,
+ .name = "viwu",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 13,
+ },
+ .la = {
+ .reg = 0x368,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x2e,
+ .name = "viwv",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 14,
+ },
+ .la = {
+ .reg = 0x368,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x2f,
+ .name = "viwy",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 15,
+ },
+ .la = {
+ .reg = 0x36c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x47,
+ },
+ }, {
+ .id = 0x30,
+ .name = "g2dw",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x30c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x9,
+ },
+ }, {
+ .id = 0x32,
+ .name = "avpcarm7w",
+ .swgroup = TEGRA_SWGROUP_AVPC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 18,
+ },
+ .la = {
+ .reg = 0x2e4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0e,
+ },
+ }, {
+ .id = 0x33,
+ .name = "fdcdwr",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 19,
+ },
+ .la = {
+ .reg = 0x338,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x34,
+ .name = "fdcwr2",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 20,
+ },
+ .la = {
+ .reg = 0x340,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x35,
+ .name = "hdaw",
+ .swgroup = TEGRA_SWGROUP_HDA,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x318,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x36,
+ .name = "host1xw",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x314,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x25,
+ },
+ }, {
+ .id = 0x37,
+ .name = "ispw",
+ .swgroup = TEGRA_SWGROUP_ISP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 23,
+ },
+ .la = {
+ .reg = 0x31c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x38,
+ .name = "mpcorelpw",
+ .swgroup = TEGRA_SWGROUP_MPCORELP,
+ .la = {
+ .reg = 0x324,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x39,
+ .name = "mpcorew",
+ .swgroup = TEGRA_SWGROUP_MPCORE,
+ .la = {
+ .reg = 0x320,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0e,
+ },
+ }, {
+ .id = 0x3b,
+ .name = "ppcsahbdmaw",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 27,
+ },
+ .la = {
+ .reg = 0x348,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x3c,
+ .name = "ppcsahbslvw",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 28,
+ },
+ .la = {
+ .reg = 0x348,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xe8,
+ },
+ }, {
+ .id = 0x3e,
+ .name = "vdebsevw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 30,
+ },
+ .la = {
+ .reg = 0x35c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x3f,
+ .name = "vdedbgw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 31,
+ },
+ .la = {
+ .reg = 0x35c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x40,
+ .name = "vdembew",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 0,
+ },
+ .la = {
+ .reg = 0x360,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x89,
+ },
+ }, {
+ .id = 0x41,
+ .name = "vdetpmw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x360,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x59,
+ },
+ }, {
+ .id = 0x4a,
+ .name = "xusb_hostr",
+ .swgroup = TEGRA_SWGROUP_XUSB_HOST,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 10,
+ },
+ .la = {
+ .reg = 0x37c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x4b,
+ .name = "xusb_hostw",
+ .swgroup = TEGRA_SWGROUP_XUSB_HOST,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x37c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x4c,
+ .name = "xusb_devr",
+ .swgroup = TEGRA_SWGROUP_XUSB_DEV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 12,
+ },
+ .la = {
+ .reg = 0x380,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x4d,
+ .name = "xusb_devw",
+ .swgroup = TEGRA_SWGROUP_XUSB_DEV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 13,
+ },
+ .la = {
+ .reg = 0x380,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x4e,
+ .name = "fdcdwr3",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 14,
+ },
+ .la = {
+ .reg = 0x388,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x4f,
+ .name = "fdcdrd3",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 15,
+ },
+ .la = {
+ .reg = 0x384,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0c,
+ },
+ }, {
+ .id = 0x50,
+ .name = "fdcwr4",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x388,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x51,
+ .name = "fdcrd4",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x384,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0c,
+ },
+ }, {
+ .id = 0x52,
+ .name = "emucifr",
+ .swgroup = TEGRA_SWGROUP_EMUCIF,
+ .la = {
+ .reg = 0x38c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x53,
+ .name = "emucifw",
+ .swgroup = TEGRA_SWGROUP_EMUCIF,
+ .la = {
+ .reg = 0x38c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0e,
+ },
+ }, {
+ .id = 0x54,
+ .name = "tsecsrd",
+ .swgroup = TEGRA_SWGROUP_TSEC,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 20,
+ },
+ .la = {
+ .reg = 0x390,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x55,
+ .name = "tsecswr",
+ .swgroup = TEGRA_SWGROUP_TSEC,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x390,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ },
+};
+
+static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
+ { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
+ { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
+ { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
+ { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
+ { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
+ { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
+ { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
+ { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
+ { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
+ { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
+ { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 },
+ { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c },
+ { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
+};
+
+static void tegra114_flush_dcache(struct page *page, unsigned long offset,
+ size_t size)
+{
+ phys_addr_t phys = page_to_phys(page) + offset;
+ void *virt = page_address(page) + offset;
+
+ __cpuc_flush_dcache_area(virt, size);
+ outer_flush_range(phys, phys + size);
+}
+
+static const struct tegra_smmu_ops tegra114_smmu_ops = {
+ .flush_dcache = tegra114_flush_dcache,
+};
+
+static const struct tegra_smmu_soc tegra114_smmu_soc = {
+ .clients = tegra114_mc_clients,
+ .num_clients = ARRAY_SIZE(tegra114_mc_clients),
+ .swgroups = tegra114_swgroups,
+ .num_swgroups = ARRAY_SIZE(tegra114_swgroups),
+ .supports_round_robin_arbitration = false,
+ .supports_request_limit = false,
+ .num_asids = 4,
+ .ops = &tegra114_smmu_ops,
+};
+
+const struct tegra_mc_soc tegra114_mc_soc = {
+ .clients = tegra114_mc_clients,
+ .num_clients = ARRAY_SIZE(tegra114_mc_clients),
+ .num_address_bits = 32,
+ .atom_size = 32,
+ .smmu = &tegra114_smmu_soc,
+};
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
new file mode 100644
index 000000000000..278d40b854c1
--- /dev/null
+++ b/drivers/memory/tegra/tegra124.c
@@ -0,0 +1,995 @@
+/*
+ * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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/of.h>
+#include <linux/mm.h>
+
+#include <asm/cacheflush.h>
+
+#include <dt-bindings/memory/tegra124-mc.h>
+
+#include "mc.h"
+
+static const struct tegra_mc_client tegra124_mc_clients[] = {
+ {
+ .id = 0x00,
+ .name = "ptcr",
+ .swgroup = TEGRA_SWGROUP_PTC,
+ }, {
+ .id = 0x01,
+ .name = "display0a",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x2e8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xc2,
+ },
+ }, {
+ .id = 0x02,
+ .name = "display0ab",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x2f4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xc6,
+ },
+ }, {
+ .id = 0x03,
+ .name = "display0b",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x2e8,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x04,
+ .name = "display0bb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x2f4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x05,
+ .name = "display0c",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x2ec,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x06,
+ .name = "display0cb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 6,
+ },
+ .la = {
+ .reg = 0x2f8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x0e,
+ .name = "afir",
+ .swgroup = TEGRA_SWGROUP_AFI,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 14,
+ },
+ .la = {
+ .reg = 0x2e0,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x13,
+ },
+ }, {
+ .id = 0x0f,
+ .name = "avpcarm7r",
+ .swgroup = TEGRA_SWGROUP_AVPC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 15,
+ },
+ .la = {
+ .reg = 0x2e4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x10,
+ .name = "displayhc",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x2f0,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x11,
+ .name = "displayhcb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x2fc,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x15,
+ .name = "hdar",
+ .swgroup = TEGRA_SWGROUP_HDA,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x318,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x24,
+ },
+ }, {
+ .id = 0x16,
+ .name = "host1xdmar",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x310,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x1e,
+ },
+ }, {
+ .id = 0x17,
+ .name = "host1xr",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 23,
+ },
+ .la = {
+ .reg = 0x310,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x1c,
+ .name = "msencsrd",
+ .swgroup = TEGRA_SWGROUP_MSENC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 28,
+ },
+ .la = {
+ .reg = 0x328,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x23,
+ },
+ }, {
+ .id = 0x1d,
+ .name = "ppcsahbdmar",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 29,
+ },
+ .la = {
+ .reg = 0x344,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x49,
+ },
+ }, {
+ .id = 0x1e,
+ .name = "ppcsahbslvr",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 30,
+ },
+ .la = {
+ .reg = 0x344,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x1a,
+ },
+ }, {
+ .id = 0x1f,
+ .name = "satar",
+ .swgroup = TEGRA_SWGROUP_SATA,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 31,
+ },
+ .la = {
+ .reg = 0x350,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x65,
+ },
+ }, {
+ .id = 0x22,
+ .name = "vdebsevr",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x354,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4f,
+ },
+ }, {
+ .id = 0x23,
+ .name = "vdember",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x354,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x3d,
+ },
+ }, {
+ .id = 0x24,
+ .name = "vdemcer",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x358,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x66,
+ },
+ }, {
+ .id = 0x25,
+ .name = "vdetper",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x358,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xa5,
+ },
+ }, {
+ .id = 0x26,
+ .name = "mpcorelpr",
+ .swgroup = TEGRA_SWGROUP_MPCORELP,
+ .la = {
+ .reg = 0x324,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x27,
+ .name = "mpcorer",
+ .swgroup = TEGRA_SWGROUP_MPCORE,
+ .la = {
+ .reg = 0x320,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x2b,
+ .name = "msencswr",
+ .swgroup = TEGRA_SWGROUP_MSENC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x328,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x31,
+ .name = "afiw",
+ .swgroup = TEGRA_SWGROUP_AFI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x2e0,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x32,
+ .name = "avpcarm7w",
+ .swgroup = TEGRA_SWGROUP_AVPC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 18,
+ },
+ .la = {
+ .reg = 0x2e4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x35,
+ .name = "hdaw",
+ .swgroup = TEGRA_SWGROUP_HDA,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x318,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x36,
+ .name = "host1xw",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x314,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x38,
+ .name = "mpcorelpw",
+ .swgroup = TEGRA_SWGROUP_MPCORELP,
+ .la = {
+ .reg = 0x324,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x39,
+ .name = "mpcorew",
+ .swgroup = TEGRA_SWGROUP_MPCORE,
+ .la = {
+ .reg = 0x320,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x3b,
+ .name = "ppcsahbdmaw",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 27,
+ },
+ .la = {
+ .reg = 0x348,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x3c,
+ .name = "ppcsahbslvw",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 28,
+ },
+ .la = {
+ .reg = 0x348,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x3d,
+ .name = "sataw",
+ .swgroup = TEGRA_SWGROUP_SATA,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 29,
+ },
+ .la = {
+ .reg = 0x350,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x65,
+ },
+ }, {
+ .id = 0x3e,
+ .name = "vdebsevw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 30,
+ },
+ .la = {
+ .reg = 0x35c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x3f,
+ .name = "vdedbgw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 31,
+ },
+ .la = {
+ .reg = 0x35c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x40,
+ .name = "vdembew",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 0,
+ },
+ .la = {
+ .reg = 0x360,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x41,
+ .name = "vdetpmw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x360,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x44,
+ .name = "ispra",
+ .swgroup = TEGRA_SWGROUP_ISP2,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x370,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x18,
+ },
+ }, {
+ .id = 0x46,
+ .name = "ispwa",
+ .swgroup = TEGRA_SWGROUP_ISP2,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 6,
+ },
+ .la = {
+ .reg = 0x374,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x47,
+ .name = "ispwb",
+ .swgroup = TEGRA_SWGROUP_ISP2,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 7,
+ },
+ .la = {
+ .reg = 0x374,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x4a,
+ .name = "xusb_hostr",
+ .swgroup = TEGRA_SWGROUP_XUSB_HOST,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 10,
+ },
+ .la = {
+ .reg = 0x37c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x39,
+ },
+ }, {
+ .id = 0x4b,
+ .name = "xusb_hostw",
+ .swgroup = TEGRA_SWGROUP_XUSB_HOST,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x37c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x4c,
+ .name = "xusb_devr",
+ .swgroup = TEGRA_SWGROUP_XUSB_DEV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 12,
+ },
+ .la = {
+ .reg = 0x380,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x39,
+ },
+ }, {
+ .id = 0x4d,
+ .name = "xusb_devw",
+ .swgroup = TEGRA_SWGROUP_XUSB_DEV,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 13,
+ },
+ .la = {
+ .reg = 0x380,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x4e,
+ .name = "isprab",
+ .swgroup = TEGRA_SWGROUP_ISP2B,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 14,
+ },
+ .la = {
+ .reg = 0x384,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x18,
+ },
+ }, {
+ .id = 0x50,
+ .name = "ispwab",
+ .swgroup = TEGRA_SWGROUP_ISP2B,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x388,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x51,
+ .name = "ispwbb",
+ .swgroup = TEGRA_SWGROUP_ISP2B,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x388,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x54,
+ .name = "tsecsrd",
+ .swgroup = TEGRA_SWGROUP_TSEC,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 20,
+ },
+ .la = {
+ .reg = 0x390,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x9b,
+ },
+ }, {
+ .id = 0x55,
+ .name = "tsecswr",
+ .swgroup = TEGRA_SWGROUP_TSEC,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x390,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x56,
+ .name = "a9avpscr",
+ .swgroup = TEGRA_SWGROUP_A9AVP,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x3a4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x57,
+ .name = "a9avpscw",
+ .swgroup = TEGRA_SWGROUP_A9AVP,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 23,
+ },
+ .la = {
+ .reg = 0x3a4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x58,
+ .name = "gpusrd",
+ .swgroup = TEGRA_SWGROUP_GPU,
+ .smmu = {
+ /* read-only */
+ .reg = 0x230,
+ .bit = 24,
+ },
+ .la = {
+ .reg = 0x3c8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x1a,
+ },
+ }, {
+ .id = 0x59,
+ .name = "gpuswr",
+ .swgroup = TEGRA_SWGROUP_GPU,
+ .smmu = {
+ /* read-only */
+ .reg = 0x230,
+ .bit = 25,
+ },
+ .la = {
+ .reg = 0x3c8,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x5a,
+ .name = "displayt",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 26,
+ },
+ .la = {
+ .reg = 0x2f0,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x60,
+ .name = "sdmmcra",
+ .swgroup = TEGRA_SWGROUP_SDMMC1A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 0,
+ },
+ .la = {
+ .reg = 0x3b8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x49,
+ },
+ }, {
+ .id = 0x61,
+ .name = "sdmmcraa",
+ .swgroup = TEGRA_SWGROUP_SDMMC2A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x3bc,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x49,
+ },
+ }, {
+ .id = 0x62,
+ .name = "sdmmcr",
+ .swgroup = TEGRA_SWGROUP_SDMMC3A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x3c0,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x49,
+ },
+ }, {
+ .id = 0x63,
+ .swgroup = TEGRA_SWGROUP_SDMMC4A,
+ .name = "sdmmcrab",
+ .smmu = {
+ .reg = 0x234,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x3c4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x49,
+ },
+ }, {
+ .id = 0x64,
+ .name = "sdmmcwa",
+ .swgroup = TEGRA_SWGROUP_SDMMC1A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x3b8,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x65,
+ .name = "sdmmcwaa",
+ .swgroup = TEGRA_SWGROUP_SDMMC2A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x3bc,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x66,
+ .name = "sdmmcw",
+ .swgroup = TEGRA_SWGROUP_SDMMC3A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 6,
+ },
+ .la = {
+ .reg = 0x3c0,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x67,
+ .name = "sdmmcwab",
+ .swgroup = TEGRA_SWGROUP_SDMMC4A,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 7,
+ },
+ .la = {
+ .reg = 0x3c4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x6c,
+ .name = "vicsrd",
+ .swgroup = TEGRA_SWGROUP_VIC,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 12,
+ },
+ .la = {
+ .reg = 0x394,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x1a,
+ },
+ }, {
+ .id = 0x6d,
+ .name = "vicswr",
+ .swgroup = TEGRA_SWGROUP_VIC,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 13,
+ },
+ .la = {
+ .reg = 0x394,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x72,
+ .name = "viw",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 18,
+ },
+ .la = {
+ .reg = 0x398,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x73,
+ .name = "displayd",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x234,
+ .bit = 19,
+ },
+ .la = {
+ .reg = 0x3c8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ },
+};
+
+static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
+ { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
+ { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
+ { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
+ { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
+ { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
+ { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
+ { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 },
+ { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
+ { .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 },
+ { .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 },
+ { .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c },
+ { .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 },
+ { .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
+ { .swgroup = TEGRA_SWGROUP_A9AVP, .reg = 0x290 },
+ { .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaac },
+ { .swgroup = TEGRA_SWGROUP_SDMMC1A, .reg = 0xa94 },
+ { .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 },
+ { .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c },
+ { .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 },
+ { .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
+ { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
+};
+
+#ifdef CONFIG_ARCH_TEGRA_124_SOC
+static void tegra124_flush_dcache(struct page *page, unsigned long offset,
+ size_t size)
+{
+ phys_addr_t phys = page_to_phys(page) + offset;
+ void *virt = page_address(page) + offset;
+
+ __cpuc_flush_dcache_area(virt, size);
+ outer_flush_range(phys, phys + size);
+}
+
+static const struct tegra_smmu_ops tegra124_smmu_ops = {
+ .flush_dcache = tegra124_flush_dcache,
+};
+
+static const struct tegra_smmu_soc tegra124_smmu_soc = {
+ .clients = tegra124_mc_clients,
+ .num_clients = ARRAY_SIZE(tegra124_mc_clients),
+ .swgroups = tegra124_swgroups,
+ .num_swgroups = ARRAY_SIZE(tegra124_swgroups),
+ .supports_round_robin_arbitration = true,
+ .supports_request_limit = true,
+ .num_asids = 128,
+ .ops = &tegra124_smmu_ops,
+};
+
+const struct tegra_mc_soc tegra124_mc_soc = {
+ .clients = tegra124_mc_clients,
+ .num_clients = ARRAY_SIZE(tegra124_mc_clients),
+ .num_address_bits = 34,
+ .atom_size = 32,
+ .smmu = &tegra124_smmu_soc,
+};
+#endif /* CONFIG_ARCH_TEGRA_124_SOC */
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
new file mode 100644
index 000000000000..71fe9376fe53
--- /dev/null
+++ b/drivers/memory/tegra/tegra30.c
@@ -0,0 +1,970 @@
+/*
+ * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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/of.h>
+#include <linux/mm.h>
+
+#include <asm/cacheflush.h>
+
+#include <dt-bindings/memory/tegra30-mc.h>
+
+#include "mc.h"
+
+static const struct tegra_mc_client tegra30_mc_clients[] = {
+ {
+ .id = 0x00,
+ .name = "ptcr",
+ .swgroup = TEGRA_SWGROUP_PTC,
+ }, {
+ .id = 0x01,
+ .name = "display0a",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x2e8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x02,
+ .name = "display0ab",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x2f4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x03,
+ .name = "display0b",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x2e8,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x04,
+ .name = "display0bb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x2f4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x05,
+ .name = "display0c",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x2ec,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x06,
+ .name = "display0cb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 6,
+ },
+ .la = {
+ .reg = 0x2f8,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x07,
+ .name = "display1b",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 7,
+ },
+ .la = {
+ .reg = 0x2ec,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x08,
+ .name = "display1bb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 8,
+ },
+ .la = {
+ .reg = 0x2f8,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x4e,
+ },
+ }, {
+ .id = 0x09,
+ .name = "eppup",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 9,
+ },
+ .la = {
+ .reg = 0x300,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x17,
+ },
+ }, {
+ .id = 0x0a,
+ .name = "g2pr",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 10,
+ },
+ .la = {
+ .reg = 0x308,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x09,
+ },
+ }, {
+ .id = 0x0b,
+ .name = "g2sr",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x308,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x09,
+ },
+ }, {
+ .id = 0x0c,
+ .name = "mpeunifbr",
+ .swgroup = TEGRA_SWGROUP_MPE,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 12,
+ },
+ .la = {
+ .reg = 0x328,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x0d,
+ .name = "viruv",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 13,
+ },
+ .la = {
+ .reg = 0x364,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x2c,
+ },
+ }, {
+ .id = 0x0e,
+ .name = "afir",
+ .swgroup = TEGRA_SWGROUP_AFI,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 14,
+ },
+ .la = {
+ .reg = 0x2e0,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x0f,
+ .name = "avpcarm7r",
+ .swgroup = TEGRA_SWGROUP_AVPC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 15,
+ },
+ .la = {
+ .reg = 0x2e4,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x10,
+ .name = "displayhc",
+ .swgroup = TEGRA_SWGROUP_DC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x2f0,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x11,
+ .name = "displayhcb",
+ .swgroup = TEGRA_SWGROUP_DCB,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x2fc,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x12,
+ .name = "fdcdrd",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 18,
+ },
+ .la = {
+ .reg = 0x334,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0a,
+ },
+ }, {
+ .id = 0x13,
+ .name = "fdcdrd2",
+ .swgroup = TEGRA_SWGROUP_NV2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 19,
+ },
+ .la = {
+ .reg = 0x33c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0a,
+ },
+ }, {
+ .id = 0x14,
+ .name = "g2dr",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 20,
+ },
+ .la = {
+ .reg = 0x30c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x0a,
+ },
+ }, {
+ .id = 0x15,
+ .name = "hdar",
+ .swgroup = TEGRA_SWGROUP_HDA,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x318,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x16,
+ .name = "host1xdmar",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x310,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x05,
+ },
+ }, {
+ .id = 0x17,
+ .name = "host1xr",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 23,
+ },
+ .la = {
+ .reg = 0x310,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x50,
+ },
+ }, {
+ .id = 0x18,
+ .name = "idxsrd",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 24,
+ },
+ .la = {
+ .reg = 0x334,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x13,
+ },
+ }, {
+ .id = 0x19,
+ .name = "idxsrd2",
+ .swgroup = TEGRA_SWGROUP_NV2,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 25,
+ },
+ .la = {
+ .reg = 0x33c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x13,
+ },
+ }, {
+ .id = 0x1a,
+ .name = "mpe_ipred",
+ .swgroup = TEGRA_SWGROUP_MPE,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 26,
+ },
+ .la = {
+ .reg = 0x328,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x80,
+ },
+ }, {
+ .id = 0x1b,
+ .name = "mpeamemrd",
+ .swgroup = TEGRA_SWGROUP_MPE,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 27,
+ },
+ .la = {
+ .reg = 0x32c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x42,
+ },
+ }, {
+ .id = 0x1c,
+ .name = "mpecsrd",
+ .swgroup = TEGRA_SWGROUP_MPE,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 28,
+ },
+ .la = {
+ .reg = 0x32c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x1d,
+ .name = "ppcsahbdmar",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 29,
+ },
+ .la = {
+ .reg = 0x344,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x1e,
+ .name = "ppcsahbslvr",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 30,
+ },
+ .la = {
+ .reg = 0x344,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x12,
+ },
+ }, {
+ .id = 0x1f,
+ .name = "satar",
+ .swgroup = TEGRA_SWGROUP_SATA,
+ .smmu = {
+ .reg = 0x228,
+ .bit = 31,
+ },
+ .la = {
+ .reg = 0x350,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x33,
+ },
+ }, {
+ .id = 0x20,
+ .name = "texsrd",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 0,
+ },
+ .la = {
+ .reg = 0x338,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x13,
+ },
+ }, {
+ .id = 0x21,
+ .name = "texsrd2",
+ .swgroup = TEGRA_SWGROUP_NV2,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x340,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x13,
+ },
+ }, {
+ .id = 0x22,
+ .name = "vdebsevr",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 2,
+ },
+ .la = {
+ .reg = 0x354,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x23,
+ .name = "vdember",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 3,
+ },
+ .la = {
+ .reg = 0x354,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xd0,
+ },
+ }, {
+ .id = 0x24,
+ .name = "vdemcer",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 4,
+ },
+ .la = {
+ .reg = 0x358,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x2a,
+ },
+ }, {
+ .id = 0x25,
+ .name = "vdetper",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 5,
+ },
+ .la = {
+ .reg = 0x358,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x74,
+ },
+ }, {
+ .id = 0x26,
+ .name = "mpcorelpr",
+ .swgroup = TEGRA_SWGROUP_MPCORELP,
+ .la = {
+ .reg = 0x324,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x27,
+ .name = "mpcorer",
+ .swgroup = TEGRA_SWGROUP_MPCORE,
+ .la = {
+ .reg = 0x320,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x04,
+ },
+ }, {
+ .id = 0x28,
+ .name = "eppu",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 8,
+ },
+ .la = {
+ .reg = 0x300,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x6c,
+ },
+ }, {
+ .id = 0x29,
+ .name = "eppv",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 9,
+ },
+ .la = {
+ .reg = 0x304,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x6c,
+ },
+ }, {
+ .id = 0x2a,
+ .name = "eppy",
+ .swgroup = TEGRA_SWGROUP_EPP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 10,
+ },
+ .la = {
+ .reg = 0x304,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x6c,
+ },
+ }, {
+ .id = 0x2b,
+ .name = "mpeunifbw",
+ .swgroup = TEGRA_SWGROUP_MPE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 11,
+ },
+ .la = {
+ .reg = 0x330,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x13,
+ },
+ }, {
+ .id = 0x2c,
+ .name = "viwsb",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 12,
+ },
+ .la = {
+ .reg = 0x364,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x12,
+ },
+ }, {
+ .id = 0x2d,
+ .name = "viwu",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 13,
+ },
+ .la = {
+ .reg = 0x368,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xb2,
+ },
+ }, {
+ .id = 0x2e,
+ .name = "viwv",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 14,
+ },
+ .la = {
+ .reg = 0x368,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xb2,
+ },
+ }, {
+ .id = 0x2f,
+ .name = "viwy",
+ .swgroup = TEGRA_SWGROUP_VI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 15,
+ },
+ .la = {
+ .reg = 0x36c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x12,
+ },
+ }, {
+ .id = 0x30,
+ .name = "g2dw",
+ .swgroup = TEGRA_SWGROUP_G2,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 16,
+ },
+ .la = {
+ .reg = 0x30c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x9,
+ },
+ }, {
+ .id = 0x31,
+ .name = "afiw",
+ .swgroup = TEGRA_SWGROUP_AFI,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 17,
+ },
+ .la = {
+ .reg = 0x2e0,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0c,
+ },
+ }, {
+ .id = 0x32,
+ .name = "avpcarm7w",
+ .swgroup = TEGRA_SWGROUP_AVPC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 18,
+ },
+ .la = {
+ .reg = 0x2e4,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0e,
+ },
+ }, {
+ .id = 0x33,
+ .name = "fdcdwr",
+ .swgroup = TEGRA_SWGROUP_NV,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 19,
+ },
+ .la = {
+ .reg = 0x338,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0a,
+ },
+ }, {
+ .id = 0x34,
+ .name = "fdcwr2",
+ .swgroup = TEGRA_SWGROUP_NV2,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 20,
+ },
+ .la = {
+ .reg = 0x340,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0a,
+ },
+ }, {
+ .id = 0x35,
+ .name = "hdaw",
+ .swgroup = TEGRA_SWGROUP_HDA,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 21,
+ },
+ .la = {
+ .reg = 0x318,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x36,
+ .name = "host1xw",
+ .swgroup = TEGRA_SWGROUP_HC,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 22,
+ },
+ .la = {
+ .reg = 0x314,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x37,
+ .name = "ispw",
+ .swgroup = TEGRA_SWGROUP_ISP,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 23,
+ },
+ .la = {
+ .reg = 0x31c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x38,
+ .name = "mpcorelpw",
+ .swgroup = TEGRA_SWGROUP_MPCORELP,
+ .la = {
+ .reg = 0x324,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0e,
+ },
+ }, {
+ .id = 0x39,
+ .name = "mpcorew",
+ .swgroup = TEGRA_SWGROUP_MPCORE,
+ .la = {
+ .reg = 0x320,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x0e,
+ },
+ }, {
+ .id = 0x3a,
+ .name = "mpecswr",
+ .swgroup = TEGRA_SWGROUP_MPE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 26,
+ },
+ .la = {
+ .reg = 0x330,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x3b,
+ .name = "ppcsahbdmaw",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 27,
+ },
+ .la = {
+ .reg = 0x348,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x10,
+ },
+ }, {
+ .id = 0x3c,
+ .name = "ppcsahbslvw",
+ .swgroup = TEGRA_SWGROUP_PPCS,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 28,
+ },
+ .la = {
+ .reg = 0x348,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x06,
+ },
+ }, {
+ .id = 0x3d,
+ .name = "sataw",
+ .swgroup = TEGRA_SWGROUP_SATA,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 29,
+ },
+ .la = {
+ .reg = 0x350,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x33,
+ },
+ }, {
+ .id = 0x3e,
+ .name = "vdebsevw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 30,
+ },
+ .la = {
+ .reg = 0x35c,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x3f,
+ .name = "vdedbgw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x22c,
+ .bit = 31,
+ },
+ .la = {
+ .reg = 0x35c,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0xff,
+ },
+ }, {
+ .id = 0x40,
+ .name = "vdembew",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 0,
+ },
+ .la = {
+ .reg = 0x360,
+ .shift = 0,
+ .mask = 0xff,
+ .def = 0x42,
+ },
+ }, {
+ .id = 0x41,
+ .name = "vdetpmw",
+ .swgroup = TEGRA_SWGROUP_VDE,
+ .smmu = {
+ .reg = 0x230,
+ .bit = 1,
+ },
+ .la = {
+ .reg = 0x360,
+ .shift = 16,
+ .mask = 0xff,
+ .def = 0x2a,
+ },
+ },
+};
+
+static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
+ { .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
+ { .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
+ { .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
+ { .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
+ { .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
+ { .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
+ { .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
+ { .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c },
+ { .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
+ { .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
+ { .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 },
+ { .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
+ { .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
+};
+
+static void tegra30_flush_dcache(struct page *page, unsigned long offset,
+ size_t size)
+{
+ phys_addr_t phys = page_to_phys(page) + offset;
+ void *virt = page_address(page) + offset;
+
+ __cpuc_flush_dcache_area(virt, size);
+ outer_flush_range(phys, phys + size);
+}
+
+static const struct tegra_smmu_ops tegra30_smmu_ops = {
+ .flush_dcache = tegra30_flush_dcache,
+};
+
+static const struct tegra_smmu_soc tegra30_smmu_soc = {
+ .clients = tegra30_mc_clients,
+ .num_clients = ARRAY_SIZE(tegra30_mc_clients),
+ .swgroups = tegra30_swgroups,
+ .num_swgroups = ARRAY_SIZE(tegra30_swgroups),
+ .supports_round_robin_arbitration = false,
+ .supports_request_limit = false,
+ .num_asids = 4,
+ .ops = &tegra30_smmu_ops,
+};
+
+const struct tegra_mc_soc tegra30_mc_soc = {
+ .clients = tegra30_mc_clients,
+ .num_clients = ARRAY_SIZE(tegra30_mc_clients),
+ .num_address_bits = 32,
+ .atom_size = 16,
+ .smmu = &tegra30_smmu_soc,
+};
diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c
deleted file mode 100644
index b3d59f88c278..000000000000
--- a/drivers/memory/tegra30-mc.c
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Tegra30 Memory Controller
- *
- * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.,
- * 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>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-
-#define DRV_NAME "tegra30-mc"
-
-#define MC_INTSTATUS 0x0
-#define MC_INTMASK 0x4
-
-#define MC_INT_ERR_SHIFT 6
-#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
-#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
-#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
-#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
-#define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4)
-
-#define MC_ERR_STATUS 0x8
-#define MC_ERR_ADR 0xc
-
-#define MC_ERR_TYPE_SHIFT 28
-#define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT)
-#define MC_ERR_TYPE_DECERR_EMEM 2
-#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3
-#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
-#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
-
-#define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25
-#define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT)
-#define MC_ERR_RW_SHIFT 16
-#define MC_ERR_RW BIT(MC_ERR_RW_SHIFT)
-#define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1)
-
-#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
-
-#define MC_EMEM_ARB_CFG 0x90
-#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
-#define MC_EMEM_ARB_TIMING_RCD 0x98
-#define MC_EMEM_ARB_TIMING_RP 0x9c
-#define MC_EMEM_ARB_TIMING_RC 0xa0
-#define MC_EMEM_ARB_TIMING_RAS 0xa4
-#define MC_EMEM_ARB_TIMING_FAW 0xa8
-#define MC_EMEM_ARB_TIMING_RRD 0xac
-#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
-#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
-#define MC_EMEM_ARB_TIMING_R2R 0xb8
-#define MC_EMEM_ARB_TIMING_W2W 0xbc
-#define MC_EMEM_ARB_TIMING_R2W 0xc0
-#define MC_EMEM_ARB_TIMING_W2R 0xc4
-
-#define MC_EMEM_ARB_DA_TURNS 0xd0
-#define MC_EMEM_ARB_DA_COVERS 0xd4
-#define MC_EMEM_ARB_MISC0 0xd8
-#define MC_EMEM_ARB_MISC1 0xdc
-
-#define MC_EMEM_ARB_RING3_THROTTLE 0xe4
-#define MC_EMEM_ARB_OVERRIDE 0xe8
-
-#define MC_TIMING_CONTROL 0xfc
-
-#define MC_CLIENT_ID_MASK 0x7f
-
-#define NUM_MC_REG_BANKS 4
-
-struct tegra30_mc {
- void __iomem *regs[NUM_MC_REG_BANKS];
- struct device *dev;
- u32 ctx[0];
-};
-
-static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
-{
- u32 val = 0;
-
- if (offs < 0x10)
- val = readl(mc->regs[0] + offs);
- else if (offs < 0x1f0)
- val = readl(mc->regs[1] + offs - 0x3c);
- else if (offs < 0x228)
- val = readl(mc->regs[2] + offs - 0x200);
- else if (offs < 0x400)
- val = readl(mc->regs[3] + offs - 0x284);
-
- return val;
-}
-
-static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
-{
- if (offs < 0x10)
- writel(val, mc->regs[0] + offs);
- else if (offs < 0x1f0)
- writel(val, mc->regs[1] + offs - 0x3c);
- else if (offs < 0x228)
- writel(val, mc->regs[2] + offs - 0x200);
- else if (offs < 0x400)
- writel(val, mc->regs[3] + offs - 0x284);
-}
-
-static const char * const tegra30_mc_client[] = {
- "csr_ptcr",
- "cbr_display0a",
- "cbr_display0ab",
- "cbr_display0b",
- "cbr_display0bb",
- "cbr_display0c",
- "cbr_display0cb",
- "cbr_display1b",
- "cbr_display1bb",
- "cbr_eppup",
- "cbr_g2pr",
- "cbr_g2sr",
- "cbr_mpeunifbr",
- "cbr_viruv",
- "csr_afir",
- "csr_avpcarm7r",
- "csr_displayhc",
- "csr_displayhcb",
- "csr_fdcdrd",
- "csr_fdcdrd2",
- "csr_g2dr",
- "csr_hdar",
- "csr_host1xdmar",
- "csr_host1xr",
- "csr_idxsrd",
- "csr_idxsrd2",
- "csr_mpe_ipred",
- "csr_mpeamemrd",
- "csr_mpecsrd",
- "csr_ppcsahbdmar",
- "csr_ppcsahbslvr",
- "csr_satar",
- "csr_texsrd",
- "csr_texsrd2",
- "csr_vdebsevr",
- "csr_vdember",
- "csr_vdemcer",
- "csr_vdetper",
- "csr_mpcorelpr",
- "csr_mpcorer",
- "cbw_eppu",
- "cbw_eppv",
- "cbw_eppy",
- "cbw_mpeunifbw",
- "cbw_viwsb",
- "cbw_viwu",
- "cbw_viwv",
- "cbw_viwy",
- "ccw_g2dw",
- "csw_afiw",
- "csw_avpcarm7w",
- "csw_fdcdwr",
- "csw_fdcdwr2",
- "csw_hdaw",
- "csw_host1xw",
- "csw_ispw",
- "csw_mpcorelpw",
- "csw_mpcorew",
- "csw_mpecswr",
- "csw_ppcsahbdmaw",
- "csw_ppcsahbslvw",
- "csw_sataw",
- "csw_vdebsevw",
- "csw_vdedbgw",
- "csw_vdembew",
- "csw_vdetpmw",
-};
-
-static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
-{
- u32 err, addr;
- const char * const mc_int_err[] = {
- "MC_DECERR",
- "Unknown",
- "MC_SECURITY_ERR",
- "MC_ARBITRATION_EMEM",
- "MC_SMMU_ERR",
- };
- const char * const err_type[] = {
- "Unknown",
- "Unknown",
- "DECERR_EMEM",
- "SECURITY_TRUSTZONE",
- "SECURITY_CARVEOUT",
- "Unknown",
- "INVALID_SMMU_PAGE",
- "Unknown",
- };
- char attr[6];
- int cid, perm, type, idx;
- const char *client = "Unknown";
-
- idx = n - MC_INT_ERR_SHIFT;
- if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) {
- dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
- BIT(n));
- return;
- }
-
- 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) >>
- MC_ERR_INVALID_SMMU_PAGE_SHIFT;
- if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE)
- sprintf(attr, "%c-%c-%c",
- (perm & BIT(2)) ? 'R' : '-',
- (perm & BIT(1)) ? 'W' : '-',
- (perm & BIT(0)) ? 'S' : '-');
- else
- attr[0] = '\0';
-
- cid = err & MC_CLIENT_ID_MASK;
- if (cid < ARRAY_SIZE(tegra30_mc_client))
- client = tegra30_mc_client[cid];
-
- 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,
- (err & MC_ERR_SECURITY) ? "secure" : "non-secure",
- (err & MC_ERR_RW) ? "write" : "read",
- err_type[type], attr);
-}
-
-static const u32 tegra30_mc_ctx[] = {
- MC_EMEM_ARB_CFG,
- MC_EMEM_ARB_OUTSTANDING_REQ,
- MC_EMEM_ARB_TIMING_RCD,
- MC_EMEM_ARB_TIMING_RP,
- MC_EMEM_ARB_TIMING_RC,
- MC_EMEM_ARB_TIMING_RAS,
- MC_EMEM_ARB_TIMING_FAW,
- MC_EMEM_ARB_TIMING_RRD,
- MC_EMEM_ARB_TIMING_RAP2PRE,
- MC_EMEM_ARB_TIMING_WAP2PRE,
- MC_EMEM_ARB_TIMING_R2R,
- MC_EMEM_ARB_TIMING_W2W,
- MC_EMEM_ARB_TIMING_R2W,
- MC_EMEM_ARB_TIMING_W2R,
- MC_EMEM_ARB_DA_TURNS,
- MC_EMEM_ARB_DA_COVERS,
- MC_EMEM_ARB_MISC0,
- MC_EMEM_ARB_MISC1,
- MC_EMEM_ARB_RING3_THROTTLE,
- MC_EMEM_ARB_OVERRIDE,
- MC_INTMASK,
-};
-
-#ifdef CONFIG_PM
-static int tegra30_mc_suspend(struct device *dev)
-{
- int i;
- struct tegra30_mc *mc = dev_get_drvdata(dev);
-
- for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
- mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]);
- return 0;
-}
-
-static int tegra30_mc_resume(struct device *dev)
-{
- int i;
- struct tegra30_mc *mc = dev_get_drvdata(dev);
-
- for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
- mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]);
-
- mc_writel(mc, 1, MC_TIMING_CONTROL);
- /* Read-back to ensure that write reached */
- 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[] = {
- { .compatible = "nvidia,tegra30-mc", },
- {},
-};
-
-static irqreturn_t tegra30_mc_isr(int irq, void *data)
-{
- u32 stat, mask, bit;
- struct tegra30_mc *mc = data;
-
- stat = mc_readl(mc, MC_INTSTATUS);
- mask = mc_readl(mc, MC_INTMASK);
- mask &= stat;
- if (!mask)
- return IRQ_NONE;
- 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 tegra30_mc_probe(struct platform_device *pdev)
-{
- struct resource *irq;
- struct tegra30_mc *mc;
- size_t bytes;
- int err, i;
- u32 intmask;
-
- bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx);
- mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
- if (!mc)
- return -ENOMEM;
- mc->dev = &pdev->dev;
-
- for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
- struct resource *res;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- 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);
- if (!irq)
- return -ENODEV;
- err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr,
- IRQF_SHARED, dev_name(&pdev->dev), mc);
- if (err)
- return -ENODEV;
-
- platform_set_drvdata(pdev, mc);
-
- intmask = MC_INT_INVALID_SMMU_PAGE |
- MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
- mc_writel(mc, intmask, MC_INTMASK);
- return 0;
-}
-
-static struct platform_driver tegra30_mc_driver = {
- .probe = tegra30_mc_probe,
- .driver = {
- .name = DRV_NAME,
- .of_match_table = tegra30_mc_of_match,
- .pm = &tegra30_mc_pm,
- },
-};
-module_platform_driver(tegra30_mc_driver);
-
-MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
-MODULE_DESCRIPTION("Tegra30 MC driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRV_NAME);