summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/at91/clk-system.c8
-rw-r--r--drivers/clocksource/Kconfig4
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/timer-atmel-pit.c296
-rw-r--r--drivers/edac/Kconfig9
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/altera_edac.c410
-rw-r--r--drivers/memory/Kconfig10
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/atmel-sdramc.c98
-rw-r--r--drivers/power/reset/Kconfig33
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/at91-poweroff.c156
-rw-r--r--drivers/power/reset/at91-reset.c252
14 files changed, 1267 insertions, 15 deletions
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 8c96307d7363..a76d03fd577b 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -119,13 +119,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
init.ops = &system_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
- /*
- * CLK_IGNORE_UNUSED is used to avoid ddrck switch off.
- * TODO : we should implement a driver supporting at91 ddr controller
- * (see drivers/memory) which would request and enable the ddrck clock.
- * When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
- */
- init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
+ init.flags = CLK_SET_RATE_PARENT;
sys->id = id;
sys->hw.init = &init;
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cfd6519df661..82a2ebe41e27 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -120,6 +120,10 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
help
Use ARM global timer clock source as sched_clock
+config ATMEL_PIT
+ select CLKSRC_OF if OF
+ def_bool SOC_AT91SAM9 || SOC_SAMA5
+
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 7fd9fd1dff42..e566f6c7ded4 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o
+obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
new file mode 100644
index 000000000000..d5289098b3df
--- /dev/null
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -0,0 +1,296 @@
+/*
+ * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x
+ *
+ * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France
+ * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France
+ * Converted to ClockSource/ClockEvents by David Brownell.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "AT91: PIT: " fmt
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+#define AT91_PIT_MR 0x00 /* Mode Register */
+#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */
+#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */
+#define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */
+
+#define AT91_PIT_SR 0x04 /* Status Register */
+#define AT91_PIT_PITS BIT(0) /* Timer Status */
+
+#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */
+#define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */
+#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */
+#define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */
+
+#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV)
+#define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20)
+
+struct pit_data {
+ struct clock_event_device clkevt;
+ struct clocksource clksrc;
+
+ void __iomem *base;
+ u32 cycle;
+ u32 cnt;
+ unsigned int irq;
+ struct clk *mck;
+};
+
+static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc)
+{
+ return container_of(clksrc, struct pit_data, clksrc);
+}
+
+static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt)
+{
+ return container_of(clkevt, struct pit_data, clkevt);
+}
+
+static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset)
+{
+ return __raw_readl(base + reg_offset);
+}
+
+static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value)
+{
+ __raw_writel(value, base + reg_offset);
+}
+
+/*
+ * Clocksource: just a monotonic counter of MCK/16 cycles.
+ * We don't care whether or not PIT irqs are enabled.
+ */
+static cycle_t read_pit_clk(struct clocksource *cs)
+{
+ struct pit_data *data = clksrc_to_pit_data(cs);
+ unsigned long flags;
+ u32 elapsed;
+ u32 t;
+
+ raw_local_irq_save(flags);
+ elapsed = data->cnt;
+ t = pit_read(data->base, AT91_PIT_PIIR);
+ raw_local_irq_restore(flags);
+
+ elapsed += PIT_PICNT(t) * data->cycle;
+ elapsed += PIT_CPIV(t);
+ return elapsed;
+}
+
+/*
+ * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16)
+ */
+static void
+pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+{
+ struct pit_data *data = clkevt_to_pit_data(dev);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ /* update clocksource counter */
+ data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
+ pit_write(data->base, AT91_PIT_MR,
+ (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ BUG();
+ /* FALLTHROUGH */
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ /* disable irq, leaving the clocksource active */
+ pit_write(data->base, AT91_PIT_MR,
+ (data->cycle - 1) | AT91_PIT_PITEN);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static void at91sam926x_pit_suspend(struct clock_event_device *cedev)
+{
+ struct pit_data *data = clkevt_to_pit_data(cedev);
+
+ /* Disable timer */
+ pit_write(data->base, AT91_PIT_MR, 0);
+}
+
+static void at91sam926x_pit_reset(struct pit_data *data)
+{
+ /* Disable timer and irqs */
+ pit_write(data->base, AT91_PIT_MR, 0);
+
+ /* Clear any pending interrupts, wait for PIT to stop counting */
+ while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0)
+ cpu_relax();
+
+ /* Start PIT but don't enable IRQ */
+ pit_write(data->base, AT91_PIT_MR,
+ (data->cycle - 1) | AT91_PIT_PITEN);
+}
+
+static void at91sam926x_pit_resume(struct clock_event_device *cedev)
+{
+ struct pit_data *data = clkevt_to_pit_data(cedev);
+
+ at91sam926x_pit_reset(data);
+}
+
+/*
+ * IRQ handler for the timer.
+ */
+static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
+{
+ struct pit_data *data = dev_id;
+
+ /*
+ * irqs should be disabled here, but as the irq is shared they are only
+ * guaranteed to be off if the timer irq is registered first.
+ */
+ WARN_ON_ONCE(!irqs_disabled());
+
+ /* The PIT interrupt may be disabled, and is shared */
+ if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) &&
+ (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
+ unsigned nr_ticks;
+
+ /* Get number of ticks performed before irq, and ack it */
+ nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
+ do {
+ data->cnt += data->cycle;
+ data->clkevt.event_handler(&data->clkevt);
+ nr_ticks--;
+ } while (nr_ticks);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/*
+ * Set up both clocksource and clockevent support.
+ */
+static void __init at91sam926x_pit_common_init(struct pit_data *data)
+{
+ unsigned long pit_rate;
+ unsigned bits;
+ int ret;
+
+ /*
+ * Use our actual MCK to figure out how many MCK/16 ticks per
+ * 1/HZ period (instead of a compile-time constant LATCH).
+ */
+ pit_rate = clk_get_rate(data->mck) / 16;
+ data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ);
+ WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0);
+
+ /* Initialize and enable the timer */
+ at91sam926x_pit_reset(data);
+
+ /*
+ * Register clocksource. The high order bits of PIV are unused,
+ * so this isn't a 32-bit counter unless we get clockevent irqs.
+ */
+ bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */;
+ data->clksrc.mask = CLOCKSOURCE_MASK(bits);
+ data->clksrc.name = "pit";
+ data->clksrc.rating = 175;
+ data->clksrc.read = read_pit_clk,
+ data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ clocksource_register_hz(&data->clksrc, pit_rate);
+
+ /* Set up irq handler */
+ ret = request_irq(data->irq, at91sam926x_pit_interrupt,
+ IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
+ "at91_tick", data);
+ if (ret)
+ panic(pr_fmt("Unable to setup IRQ\n"));
+
+ /* Set up and register clockevents */
+ data->clkevt.name = "pit";
+ data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
+ data->clkevt.shift = 32;
+ data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
+ data->clkevt.rating = 100;
+ data->clkevt.cpumask = cpumask_of(0);
+
+ data->clkevt.set_mode = pit_clkevt_mode;
+ data->clkevt.resume = at91sam926x_pit_resume;
+ data->clkevt.suspend = at91sam926x_pit_suspend;
+ clockevents_register_device(&data->clkevt);
+}
+
+static void __init at91sam926x_pit_dt_init(struct device_node *node)
+{
+ struct pit_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ panic(pr_fmt("Unable to allocate memory\n"));
+
+ data->base = of_iomap(node, 0);
+ if (!data->base)
+ panic(pr_fmt("Could not map PIT address\n"));
+
+ data->mck = of_clk_get(node, 0);
+ if (IS_ERR(data->mck))
+ /* Fallback on clkdev for !CCF-based boards */
+ data->mck = clk_get(NULL, "mck");
+
+ if (IS_ERR(data->mck))
+ panic(pr_fmt("Unable to get mck clk\n"));
+
+ /* Get the interrupts property */
+ data->irq = irq_of_parse_and_map(node, 0);
+ if (!data->irq)
+ panic(pr_fmt("Unable to get IRQ from DT\n"));
+
+ at91sam926x_pit_common_init(data);
+}
+CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
+ at91sam926x_pit_dt_init);
+
+static void __iomem *pit_base_addr;
+
+void __init at91sam926x_pit_init(int irq)
+{
+ struct pit_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ panic(pr_fmt("Unable to allocate memory\n"));
+
+ data->base = pit_base_addr;
+
+ data->mck = clk_get(NULL, "mck");
+ if (IS_ERR(data->mck))
+ panic(pr_fmt("Unable to get mck clk\n"));
+
+ data->irq = irq;
+
+ at91sam926x_pit_common_init(data);
+}
+
+void __init at91sam926x_ioremap_pit(u32 addr)
+{
+ if (of_have_populated_dt())
+ return;
+
+ pit_base_addr = ioremap(addr, 16);
+
+ if (!pit_base_addr)
+ panic(pr_fmt("Impossible to ioremap PIT\n"));
+}
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index fd89ca982748..7072c2892d63 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -376,4 +376,13 @@ config EDAC_OCTEON_PCI
Support for error detection and correction on the
Cavium Octeon family of SOCs.
+config EDAC_ALTERA_MC
+ tristate "Altera SDRAM Memory Controller EDAC"
+ depends on EDAC_MM_EDAC && ARCH_SOCFPGA
+ help
+ Support for error detection and correction on the
+ Altera SDRAM memory controller. Note that the
+ preloader must initialize the SDRAM before loading
+ the kernel.
+
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index c479a24d8f77..359aa499b200 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -65,3 +65,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o
obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o
obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o
obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o
+
+obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
new file mode 100644
index 000000000000..3c4929fda9d5
--- /dev/null
+++ b/drivers/edac/altera_edac.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright Altera Corporation (C) 2014. All rights reserved.
+ * Copyright 2011-2012 Calxeda, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Adapted from the highbank_mc_edac driver.
+ */
+
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define EDAC_MOD_STR "altera_edac"
+#define EDAC_VERSION "1"
+
+/* SDRAM Controller CtrlCfg Register */
+#define CTLCFG_OFST 0x00
+
+/* SDRAM Controller CtrlCfg Register Bit Masks */
+#define CTLCFG_ECC_EN 0x400
+#define CTLCFG_ECC_CORR_EN 0x800
+#define CTLCFG_GEN_SB_ERR 0x2000
+#define CTLCFG_GEN_DB_ERR 0x4000
+
+#define CTLCFG_ECC_AUTO_EN (CTLCFG_ECC_EN | \
+ CTLCFG_ECC_CORR_EN)
+
+/* SDRAM Controller Address Width Register */
+#define DRAMADDRW_OFST 0x2C
+
+/* SDRAM Controller Address Widths Field Register */
+#define DRAMADDRW_COLBIT_MASK 0x001F
+#define DRAMADDRW_COLBIT_SHIFT 0
+#define DRAMADDRW_ROWBIT_MASK 0x03E0
+#define DRAMADDRW_ROWBIT_SHIFT 5
+#define DRAMADDRW_BANKBIT_MASK 0x1C00
+#define DRAMADDRW_BANKBIT_SHIFT 10
+#define DRAMADDRW_CSBIT_MASK 0xE000
+#define DRAMADDRW_CSBIT_SHIFT 13
+
+/* SDRAM Controller Interface Data Width Register */
+#define DRAMIFWIDTH_OFST 0x30
+
+/* SDRAM Controller Interface Data Width Defines */
+#define DRAMIFWIDTH_16B_ECC 24
+#define DRAMIFWIDTH_32B_ECC 40
+
+/* SDRAM Controller DRAM Status Register */
+#define DRAMSTS_OFST 0x38
+
+/* SDRAM Controller DRAM Status Register Bit Masks */
+#define DRAMSTS_SBEERR 0x04
+#define DRAMSTS_DBEERR 0x08
+#define DRAMSTS_CORR_DROP 0x10
+
+/* SDRAM Controller DRAM IRQ Register */
+#define DRAMINTR_OFST 0x3C
+
+/* SDRAM Controller DRAM IRQ Register Bit Masks */
+#define DRAMINTR_INTREN 0x01
+#define DRAMINTR_SBEMASK 0x02
+#define DRAMINTR_DBEMASK 0x04
+#define DRAMINTR_CORRDROPMASK 0x08
+#define DRAMINTR_INTRCLR 0x10
+
+/* SDRAM Controller Single Bit Error Count Register */
+#define SBECOUNT_OFST 0x40
+
+/* SDRAM Controller Single Bit Error Count Register Bit Masks */
+#define SBECOUNT_MASK 0x0F
+
+/* SDRAM Controller Double Bit Error Count Register */
+#define DBECOUNT_OFST 0x44
+
+/* SDRAM Controller Double Bit Error Count Register Bit Masks */
+#define DBECOUNT_MASK 0x0F
+
+/* SDRAM Controller ECC Error Address Register */
+#define ERRADDR_OFST 0x48
+
+/* SDRAM Controller ECC Error Address Register Bit Masks */
+#define ERRADDR_MASK 0xFFFFFFFF
+
+/* Altera SDRAM Memory Controller data */
+struct altr_sdram_mc_data {
+ struct regmap *mc_vbase;
+};
+
+static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct altr_sdram_mc_data *drvdata = mci->pvt_info;
+ u32 status, err_count, err_addr;
+
+ /* Error Address is shared by both SBE & DBE */
+ regmap_read(drvdata->mc_vbase, ERRADDR_OFST, &err_addr);
+
+ regmap_read(drvdata->mc_vbase, DRAMSTS_OFST, &status);
+
+ if (status & DRAMSTS_DBEERR) {
+ regmap_read(drvdata->mc_vbase, DBECOUNT_OFST, &err_count);
+ panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n",
+ err_count, err_addr);
+ }
+ if (status & DRAMSTS_SBEERR) {
+ regmap_read(drvdata->mc_vbase, SBECOUNT_OFST, &err_count);
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count,
+ err_addr >> PAGE_SHIFT,
+ err_addr & ~PAGE_MASK, 0,
+ 0, 0, -1, mci->ctl_name, "");
+ }
+
+ regmap_write(drvdata->mc_vbase, DRAMINTR_OFST,
+ (DRAMINTR_INTRCLR | DRAMINTR_INTREN));
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t altr_sdr_mc_err_inject_write(struct file *file,
+ const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct mem_ctl_info *mci = file->private_data;
+ struct altr_sdram_mc_data *drvdata = mci->pvt_info;
+ u32 *ptemp;
+ dma_addr_t dma_handle;
+ u32 reg, read_reg;
+
+ ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL);
+ if (!ptemp) {
+ dma_free_coherent(mci->pdev, 16, ptemp, dma_handle);
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Inject: Buffer Allocation error\n");
+ return -ENOMEM;
+ }
+
+ regmap_read(drvdata->mc_vbase, CTLCFG_OFST, &read_reg);
+ read_reg &= ~(CTLCFG_GEN_SB_ERR | CTLCFG_GEN_DB_ERR);
+
+ /* Error are injected by writing a word while the SBE or DBE
+ * bit in the CTLCFG register is set. Reading the word will
+ * trigger the SBE or DBE error and the corresponding IRQ.
+ */
+ if (count == 3) {
+ edac_printk(KERN_ALERT, EDAC_MC,
+ "Inject Double bit error\n");
+ regmap_write(drvdata->mc_vbase, CTLCFG_OFST,
+ (read_reg | CTLCFG_GEN_DB_ERR));
+ } else {
+ edac_printk(KERN_ALERT, EDAC_MC,
+ "Inject Single bit error\n");
+ regmap_write(drvdata->mc_vbase, CTLCFG_OFST,
+ (read_reg | CTLCFG_GEN_SB_ERR));
+ }
+
+ ptemp[0] = 0x5A5A5A5A;
+ ptemp[1] = 0xA5A5A5A5;
+
+ /* Clear the error injection bits */
+ regmap_write(drvdata->mc_vbase, CTLCFG_OFST, read_reg);
+ /* Ensure it has been written out */
+ wmb();
+
+ /*
+ * To trigger the error, we need to read the data back
+ * (the data was written with errors above).
+ * The ACCESS_ONCE macros and printk are used to prevent the
+ * the compiler optimizing these reads out.
+ */
+ reg = ACCESS_ONCE(ptemp[0]);
+ read_reg = ACCESS_ONCE(ptemp[1]);
+ /* Force Read */
+ rmb();
+
+ edac_printk(KERN_ALERT, EDAC_MC, "Read Data [0x%X, 0x%X]\n",
+ reg, read_reg);
+
+ dma_free_coherent(mci->pdev, 16, ptemp, dma_handle);
+
+ return count;
+}
+
+static const struct file_operations altr_sdr_mc_debug_inject_fops = {
+ .open = simple_open,
+ .write = altr_sdr_mc_err_inject_write,
+ .llseek = generic_file_llseek,
+};
+
+static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
+{
+ if (mci->debugfs)
+ debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+ &altr_sdr_mc_debug_inject_fops);
+}
+#else
+static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
+{}
+#endif
+
+/* Get total memory size in bytes */
+static u32 altr_sdram_get_total_mem_size(struct regmap *mc_vbase)
+{
+ u32 size, read_reg, row, bank, col, cs, width;
+
+ if (regmap_read(mc_vbase, DRAMADDRW_OFST, &read_reg) < 0)
+ return 0;
+
+ if (regmap_read(mc_vbase, DRAMIFWIDTH_OFST, &width) < 0)
+ return 0;
+
+ col = (read_reg & DRAMADDRW_COLBIT_MASK) >>
+ DRAMADDRW_COLBIT_SHIFT;
+ row = (read_reg & DRAMADDRW_ROWBIT_MASK) >>
+ DRAMADDRW_ROWBIT_SHIFT;
+ bank = (read_reg & DRAMADDRW_BANKBIT_MASK) >>
+ DRAMADDRW_BANKBIT_SHIFT;
+ cs = (read_reg & DRAMADDRW_CSBIT_MASK) >>
+ DRAMADDRW_CSBIT_SHIFT;
+
+ /* Correct for ECC as its not addressible */
+ if (width == DRAMIFWIDTH_32B_ECC)
+ width = 32;
+ if (width == DRAMIFWIDTH_16B_ECC)
+ width = 16;
+
+ /* calculate the SDRAM size base on this info */
+ size = 1 << (row + bank + col);
+ size = size * cs * (width / 8);
+ return size;
+}
+
+static int altr_sdram_probe(struct platform_device *pdev)
+{
+ struct edac_mc_layer layers[2];
+ struct mem_ctl_info *mci;
+ struct altr_sdram_mc_data *drvdata;
+ struct regmap *mc_vbase;
+ struct dimm_info *dimm;
+ u32 read_reg, mem_size;
+ int irq;
+ int res = 0;
+
+ /* Validate the SDRAM controller has ECC enabled */
+ /* Grab the register range from the sdr controller in device tree */
+ mc_vbase = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "altr,sdr-syscon");
+ if (IS_ERR(mc_vbase)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "regmap for altr,sdr-syscon lookup failed.\n");
+ return -ENODEV;
+ }
+
+ if (regmap_read(mc_vbase, CTLCFG_OFST, &read_reg) ||
+ ((read_reg & CTLCFG_ECC_AUTO_EN) != CTLCFG_ECC_AUTO_EN)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "No ECC/ECC disabled [0x%08X]\n", read_reg);
+ return -ENODEV;
+ }
+
+ /* Grab memory size from device tree. */
+ mem_size = altr_sdram_get_total_mem_size(mc_vbase);
+ if (!mem_size) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Unable to calculate memory size\n");
+ return -ENODEV;
+ }
+
+ /* Ensure the SDRAM Interrupt is disabled and cleared */
+ if (regmap_write(mc_vbase, DRAMINTR_OFST, DRAMINTR_INTRCLR)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Error clearing SDRAM ECC IRQ\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "No irq %d in DT\n", irq);
+ return -ENODEV;
+ }
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = 1;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = 1;
+ layers[1].is_virt_csrow = false;
+ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+ sizeof(struct altr_sdram_mc_data));
+ if (!mci)
+ return -ENOMEM;
+
+ mci->pdev = &pdev->dev;
+ drvdata = mci->pvt_info;
+ drvdata->mc_vbase = mc_vbase;
+ platform_set_drvdata(pdev, mci);
+
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
+ res = -ENOMEM;
+ goto free;
+ }
+
+ mci->mtype_cap = MEM_FLAG_DDR3;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->mod_name = EDAC_MOD_STR;
+ mci->mod_ver = EDAC_VERSION;
+ mci->ctl_name = dev_name(&pdev->dev);
+ mci->scrub_mode = SCRUB_SW_SRC;
+ mci->dev_name = dev_name(&pdev->dev);
+
+ dimm = *mci->dimms;
+ dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1;
+ dimm->grain = 8;
+ dimm->dtype = DEV_X8;
+ dimm->mtype = MEM_DDR3;
+ dimm->edac_mode = EDAC_SECDED;
+
+ res = edac_mc_add_mc(mci);
+ if (res < 0)
+ goto err;
+
+ res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler,
+ 0, dev_name(&pdev->dev), mci);
+ if (res < 0) {
+ edac_mc_printk(mci, KERN_ERR,
+ "Unable to request irq %d\n", irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ if (regmap_write(drvdata->mc_vbase, DRAMINTR_OFST,
+ (DRAMINTR_INTRCLR | DRAMINTR_INTREN))) {
+ edac_mc_printk(mci, KERN_ERR,
+ "Error enabling SDRAM ECC IRQ\n");
+ res = -ENODEV;
+ goto err2;
+ }
+
+ altr_sdr_mc_create_debugfs_nodes(mci);
+
+ devres_close_group(&pdev->dev, NULL);
+
+ return 0;
+
+err2:
+ edac_mc_del_mc(&pdev->dev);
+err:
+ devres_release_group(&pdev->dev, NULL);
+free:
+ edac_mc_free(mci);
+ edac_printk(KERN_ERR, EDAC_MC,
+ "EDAC Probe Failed; Error %d\n", res);
+
+ return res;
+}
+
+static int altr_sdram_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id altr_sdram_ctrl_of_match[] = {
+ { .compatible = "altr,sdram-edac", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
+
+static struct platform_driver altr_sdram_edac_driver = {
+ .probe = altr_sdram_probe,
+ .remove = altr_sdram_remove,
+ .driver = {
+ .name = "altr_sdram_edac",
+ .of_match_table = altr_sdram_ctrl_of_match,
+ },
+};
+
+module_platform_driver(altr_sdram_edac_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Thor Thayer");
+MODULE_DESCRIPTION("EDAC Driver for Altera SDRAM Controller");
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index fab81a143bd7..6d91c27fd4c8 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -7,6 +7,16 @@ menuconfig MEMORY
if MEMORY
+config ATMEL_SDRAMC
+ bool "Atmel (Multi-port DDR-)SDRAM Controller"
+ default y
+ depends on ARCH_AT91 && OF
+ help
+ This driver is for Atmel SDRAM Controller or Atmel Multi-port
+ DDR-SDRAM Controller available on Atmel AT91SAM9 and SAMA5 SoCs.
+ Starting with the at91sam9g45, this controller supports SDR, DDR and
+ LP-DDR memories.
+
config TI_AEMIF
tristate "Texas Instruments AEMIF driver"
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 4055c47f45ab..c32d31981be3 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -5,6 +5,7 @@
ifeq ($(CONFIG_DDR),y)
obj-$(CONFIG_OF) += of_memory.o
endif
+obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o
diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c
new file mode 100644
index 000000000000..fed04e8efe75
--- /dev/null
+++ b/drivers/memory/atmel-sdramc.c
@@ -0,0 +1,98 @@
+/*
+ * Atmel (Multi-port DDR-)SDRAM Controller driver
+ *
+ * Copyright (C) 2014 Atmel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+struct at91_ramc_caps {
+ bool has_ddrck;
+ bool has_mpddr_clk;
+};
+
+static const struct at91_ramc_caps at91rm9200_caps = { };
+
+static const struct at91_ramc_caps at91sam9g45_caps = {
+ .has_ddrck = 1,
+ .has_mpddr_clk = 0,
+};
+
+static const struct at91_ramc_caps sama5d3_caps = {
+ .has_ddrck = 1,
+ .has_mpddr_clk = 1,
+};
+
+static const struct of_device_id atmel_ramc_of_match[] = {
+ { .compatible = "atmel,at91rm9200-sdramc", .data = &at91rm9200_caps, },
+ { .compatible = "atmel,at91sam9260-sdramc", .data = &at91rm9200_caps, },
+ { .compatible = "atmel,at91sam9g45-ddramc", .data = &at91sam9g45_caps, },
+ { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, atmel_ramc_of_match);
+
+static int atmel_ramc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct at91_ramc_caps *caps;
+ struct clk *clk;
+
+ match = of_match_device(atmel_ramc_of_match, &pdev->dev);
+ caps = match->data;
+
+ if (caps->has_ddrck) {
+ clk = devm_clk_get(&pdev->dev, "ddrck");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ clk_prepare_enable(clk);
+ }
+
+ if (caps->has_mpddr_clk) {
+ clk = devm_clk_get(&pdev->dev, "mpddr");
+ if (IS_ERR(clk)) {
+ pr_err("AT91 RAMC: couldn't get mpddr clock\n");
+ return PTR_ERR(clk);
+ }
+ clk_prepare_enable(clk);
+ }
+
+ return 0;
+}
+
+static struct platform_driver atmel_ramc_driver = {
+ .probe = atmel_ramc_probe,
+ .driver = {
+ .name = "atmel-ramc",
+ .owner = THIS_MODULE,
+ .of_match_table = atmel_ramc_of_match,
+ },
+};
+
+static int __init atmel_ramc_init(void)
+{
+ return platform_driver_register(&atmel_ramc_driver);
+}
+module_init(atmel_ramc_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller");
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index ca41523bbebf..81b445122eea 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -6,15 +6,33 @@ menuconfig POWER_RESET
Say Y here to enable board reset and power off
+if POWER_RESET
+
config POWER_RESET_AS3722
bool "ams AS3722 power-off driver"
- depends on MFD_AS3722 && POWER_RESET
+ depends on MFD_AS3722
help
This driver supports turning off board via a ams AS3722 power-off.
+config POWER_RESET_AT91_POWEROFF
+ bool "Atmel AT91 poweroff driver"
+ depends on ARCH_AT91
+ default SOC_AT91SAM9 || SOC_SAMA5
+ help
+ This driver supports poweroff for Atmel AT91SAM9 and SAMA5
+ SoCs
+
+config POWER_RESET_AT91_RESET
+ bool "Atmel AT91 reset driver"
+ depends on ARCH_AT91
+ default SOC_AT91SAM9 || SOC_SAMA5
+ help
+ This driver supports restart for Atmel AT91SAM9 and SAMA5
+ SoCs
+
config POWER_RESET_AXXIA
bool "LSI Axxia reset driver"
- depends on POWER_RESET && ARCH_AXXIA
+ depends on ARCH_AXXIA
help
This driver supports restart for Axxia SoC.
@@ -33,7 +51,7 @@ config POWER_RESET_BRCMSTB
config POWER_RESET_GPIO
bool "GPIO power-off driver"
- depends on OF_GPIO && POWER_RESET
+ depends on OF_GPIO
help
This driver supports turning off your board via a GPIO line.
If your board needs a GPIO high/low to power down, say Y and
@@ -47,13 +65,13 @@ config POWER_RESET_HISI
config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver"
- depends on POWER_RESET && ARCH_QCOM
+ depends on ARCH_QCOM
help
Power off and restart support for Qualcomm boards.
config POWER_RESET_QNAP
bool "QNAP power-off driver"
- depends on OF_GPIO && POWER_RESET && PLAT_ORION
+ depends on OF_GPIO && PLAT_ORION
help
This driver supports turning off QNAP NAS devices by sending
commands to the microcontroller which controls the main power.
@@ -71,14 +89,13 @@ config POWER_RESET_RESTART
config POWER_RESET_SUN6I
bool "Allwinner A31 SoC reset driver"
depends on ARCH_SUNXI
- depends on POWER_RESET
help
Reboot support for the Allwinner A31 SoCs.
config POWER_RESET_VEXPRESS
bool "ARM Versatile Express power-off and reset driver"
depends on ARM || ARM64
- depends on POWER_RESET && VEXPRESS_CONFIG
+ depends on VEXPRESS_CONFIG
help
Power off and reset support for the ARM Ltd. Versatile
Express boards.
@@ -86,7 +103,6 @@ config POWER_RESET_VEXPRESS
config POWER_RESET_XGENE
bool "APM SoC X-Gene reset driver"
depends on ARM64
- depends on POWER_RESET
help
Reboot support for the APM SoC X-Gene Eval boards.
@@ -97,3 +113,4 @@ config POWER_RESET_KEYSTONE
help
Reboot support for the KEYSTONE SoCs.
+endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index a42e70edd037..4433a753fd93 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -1,4 +1,6 @@
obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
+obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
+obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
new file mode 100644
index 000000000000..c61000333bb9
--- /dev/null
+++ b/drivers/power/reset/at91-poweroff.c
@@ -0,0 +1,156 @@
+/*
+ * Atmel AT91 SAM9 SoCs reset code
+ *
+ * Copyright (C) 2007 Atmel Corporation.
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ * Copyright (C) 2014 Free Electrons
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+
+#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
+#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
+#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
+
+#define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */
+#define AT91_SHDW_WKMODE0 GENMASK(2, 0) /* Wake-up 0 Mode Selection */
+#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */
+#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
+#define AT91_SHDW_CPTWK0_(x) ((x) << 4)
+#define AT91_SHDW_RTTWKEN BIT(16) /* Real Time Timer Wake-up Enable */
+#define AT91_SHDW_RTCWKEN BIT(17) /* Real Time Clock Wake-up Enable */
+
+#define AT91_SHDW_SR 0x08 /* Shut Down Status Register */
+#define AT91_SHDW_WAKEUP0 BIT(0) /* Wake-up 0 Status */
+#define AT91_SHDW_RTTWK BIT(16) /* Real-time Timer Wake-up */
+#define AT91_SHDW_RTCWK BIT(17) /* Real-time Clock Wake-up [SAM9RL] */
+
+enum wakeup_type {
+ AT91_SHDW_WKMODE0_NONE = 0,
+ AT91_SHDW_WKMODE0_HIGH = 1,
+ AT91_SHDW_WKMODE0_LOW = 2,
+ AT91_SHDW_WKMODE0_ANYLEVEL = 3,
+};
+
+static const char *shdwc_wakeup_modes[] = {
+ [AT91_SHDW_WKMODE0_NONE] = "none",
+ [AT91_SHDW_WKMODE0_HIGH] = "high",
+ [AT91_SHDW_WKMODE0_LOW] = "low",
+ [AT91_SHDW_WKMODE0_ANYLEVEL] = "any",
+};
+
+static void __iomem *at91_shdwc_base;
+
+static void __init at91_wakeup_status(void)
+{
+ u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
+ char *reason = "unknown";
+
+ /* Simple power-on, just bail out */
+ if (!reg)
+ return;
+
+ if (reg & AT91_SHDW_RTTWK)
+ reason = "RTT";
+ else if (reg & AT91_SHDW_RTCWK)
+ reason = "RTC";
+
+ pr_info("AT91: Wake-Up source: %s\n", reason);
+}
+
+static void at91_poweroff(void)
+{
+ writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
+}
+
+const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
+{
+ const char *pm;
+ int err, i;
+
+ err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
+ if (err < 0)
+ return AT91_SHDW_WKMODE0_ANYLEVEL;
+
+ for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
+ if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
+ return i;
+
+ return -ENODEV;
+}
+
+static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ enum wakeup_type wakeup_mode;
+ u32 mode = 0, tmp;
+
+ wakeup_mode = at91_poweroff_get_wakeup_mode(np);
+ if (wakeup_mode < 0) {
+ dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n");
+ return;
+ }
+
+ if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) {
+ if (tmp > AT91_SHDW_CPTWK0_MAX) {
+ dev_warn(&pdev->dev,
+ "shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n",
+ tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
+ tmp = AT91_SHDW_CPTWK0_MAX;
+ }
+ mode |= AT91_SHDW_CPTWK0_(tmp);
+ }
+
+ if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
+ mode |= AT91_SHDW_RTCWKEN;
+
+ if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
+ mode |= AT91_SHDW_RTTWKEN;
+
+ writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR);
+}
+
+static int at91_poweroff_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(at91_shdwc_base)) {
+ dev_err(&pdev->dev, "Could not map reset controller address\n");
+ return PTR_ERR(at91_shdwc_base);
+ }
+
+ at91_wakeup_status();
+
+ if (pdev->dev.of_node)
+ at91_poweroff_dt_set_wakeup_mode(pdev);
+
+ pm_power_off = at91_poweroff;
+
+ return 0;
+}
+
+static struct of_device_id at91_poweroff_of_match[] = {
+ { .compatible = "atmel,at91sam9260-shdwc", },
+ { .compatible = "atmel,at91sam9rl-shdwc", },
+ { .compatible = "atmel,at91sam9x5-shdwc", },
+ { /*sentinel*/ }
+};
+
+static struct platform_driver at91_poweroff_driver = {
+ .probe = at91_poweroff_probe,
+ .driver = {
+ .name = "at91-poweroff",
+ .of_match_table = at91_poweroff_of_match,
+ },
+};
+module_platform_driver(at91_poweroff_driver);
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
new file mode 100644
index 000000000000..3611806c9cfd
--- /dev/null
+++ b/drivers/power/reset/at91-reset.c
@@ -0,0 +1,252 @@
+/*
+ * Atmel AT91 SAM9 SoCs reset code
+ *
+ * Copyright (C) 2007 Atmel Corporation.
+ * Copyright (C) BitBox Ltd 2010
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com>
+ * Copyright (C) 2014 Free Electrons
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+#include <asm/system_misc.h>
+
+#include <mach/at91sam9_ddrsdr.h>
+#include <mach/at91sam9_sdramc.h>
+
+#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */
+#define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */
+#define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */
+#define AT91_RSTC_EXTRST BIT(3) /* External Reset */
+#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */
+
+#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */
+#define AT91_RSTC_URSTS BIT(0) /* User Reset Status */
+#define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */
+#define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */
+#define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */
+
+#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */
+#define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */
+#define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */
+#define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */
+
+enum reset_type {
+ RESET_TYPE_GENERAL = 0,
+ RESET_TYPE_WAKEUP = 1,
+ RESET_TYPE_WATCHDOG = 2,
+ RESET_TYPE_SOFTWARE = 3,
+ RESET_TYPE_USER = 4,
+};
+
+static void __iomem *at91_ramc_base[2], *at91_rstc_base;
+
+/*
+* unless the SDRAM is cleanly shutdown before we hit the
+* reset register it can be left driving the data bus and
+* killing the chance of a subsequent boot from NAND
+*/
+static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
+{
+ asm volatile(
+ /* Align to cache lines */
+ ".balign 32\n\t"
+
+ /* Disable SDRAM accesses */
+ "str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t"
+
+ /* Power down SDRAM */
+ "str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t"
+
+ /* Reset CPU */
+ "str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t"
+
+ "b .\n\t"
+ :
+ : "r" (at91_ramc_base[0]),
+ "r" (at91_rstc_base),
+ "r" (1),
+ "r" (AT91_SDRAMC_LPCB_POWER_DOWN),
+ "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
+}
+
+static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
+{
+ asm volatile(
+ /*
+ * Test wether we have a second RAM controller to care
+ * about.
+ *
+ * First, test that we can dereference the virtual address.
+ */
+ "cmp %1, #0\n\t"
+ "beq 1f\n\t"
+
+ /* Then, test that the RAM controller is enabled */
+ "ldr r0, [%1]\n\t"
+ "cmp r0, #0\n\t"
+
+ /* Align to cache lines */
+ ".balign 32\n\t"
+
+ /* Disable SDRAM0 accesses */
+ "1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+ /* Power down SDRAM0 */
+ " str %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+ /* Disable SDRAM1 accesses */
+ " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+ /* Power down SDRAM1 */
+ " strne %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+ /* Reset CPU */
+ " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
+
+ " b .\n\t"
+ :
+ : "r" (at91_ramc_base[0]),
+ "r" (at91_ramc_base[1]),
+ "r" (at91_rstc_base),
+ "r" (1),
+ "r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
+ "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
+ : "r0");
+}
+
+static void __init at91_reset_status(struct platform_device *pdev)
+{
+ u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
+ char *reason;
+
+ switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
+ case RESET_TYPE_GENERAL:
+ reason = "general reset";
+ break;
+ case RESET_TYPE_WAKEUP:
+ reason = "wakeup";
+ break;
+ case RESET_TYPE_WATCHDOG:
+ reason = "watchdog reset";
+ break;
+ case RESET_TYPE_SOFTWARE:
+ reason = "software reset";
+ break;
+ case RESET_TYPE_USER:
+ reason = "user reset";
+ break;
+ default:
+ reason = "unknown reset";
+ break;
+ }
+
+ pr_info("AT91: Starting after %s\n", reason);
+}
+
+static struct of_device_id at91_ramc_of_match[] = {
+ { .compatible = "atmel,at91sam9260-sdramc", },
+ { .compatible = "atmel,at91sam9g45-ddramc", },
+ { .compatible = "atmel,sama5d3-ddramc", },
+ { /* sentinel */ }
+};
+
+static struct of_device_id at91_reset_of_match[] = {
+ { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
+ { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
+ { /* sentinel */ }
+};
+
+static int at91_reset_of_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *np;
+ int idx = 0;
+
+ at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
+ if (!at91_rstc_base) {
+ dev_err(&pdev->dev, "Could not map reset controller address\n");
+ return -ENODEV;
+ }
+
+ for_each_matching_node(np, at91_ramc_of_match) {
+ at91_ramc_base[idx] = of_iomap(np, 0);
+ if (!at91_ramc_base[idx]) {
+ dev_err(&pdev->dev, "Could not map ram controller address\n");
+ return -ENODEV;
+ }
+ idx++;
+ }
+
+ match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
+ arm_pm_restart = match->data;
+
+ return 0;
+}
+
+static int at91_reset_platform_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *match;
+ struct resource *res;
+ int idx = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ at91_rstc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(at91_rstc_base)) {
+ dev_err(&pdev->dev, "Could not map reset controller address\n");
+ return PTR_ERR(at91_rstc_base);
+ }
+
+ for (idx = 0; idx < 2; idx++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
+ at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(at91_ramc_base[idx])) {
+ dev_err(&pdev->dev, "Could not map ram controller address\n");
+ return PTR_ERR(at91_ramc_base[idx]);
+ }
+ }
+
+ match = platform_get_device_id(pdev);
+ arm_pm_restart = (void (*)(enum reboot_mode, const char*))
+ match->driver_data;
+
+ return 0;
+}
+
+static int at91_reset_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (pdev->dev.of_node)
+ ret = at91_reset_of_probe(pdev);
+ else
+ ret = at91_reset_platform_probe(pdev);
+
+ if (ret)
+ return ret;
+
+ at91_reset_status(pdev);
+
+ return 0;
+}
+
+static struct platform_device_id at91_reset_plat_match[] = {
+ { "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
+ { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
+ { /* sentinel */ }
+};
+
+static struct platform_driver at91_reset_driver = {
+ .probe = at91_reset_probe,
+ .driver = {
+ .name = "at91-reset",
+ .of_match_table = at91_reset_of_match,
+ },
+ .id_table = at91_reset_plat_match,
+};
+module_platform_driver(at91_reset_driver);