aboutsummaryrefslogtreecommitdiff
path: root/drivers/power/reset/at91-reset.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/reset/at91-reset.c')
-rw-r--r--drivers/power/reset/at91-reset.c184
1 files changed, 169 insertions, 15 deletions
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index 64def79d557a..741e44a017c3 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -17,10 +17,13 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
+#include <linux/reset-controller.h>
#include <soc/at91/at91sam9_ddrsdr.h>
#include <soc/at91/at91sam9_sdramc.h>
+#include <dt-bindings/reset/sama7g5-reset.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 */
@@ -39,6 +42,17 @@
#define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */
#define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */
+/**
+ * enum reset_type - reset types
+ * @RESET_TYPE_GENERAL: first power-up reset
+ * @RESET_TYPE_WAKEUP: return from backup mode
+ * @RESET_TYPE_WATCHDOG: watchdog fault
+ * @RESET_TYPE_SOFTWARE: processor reset required by software
+ * @RESET_TYPE_USER: NRST pin detected low
+ * @RESET_TYPE_CPU_FAIL: CPU clock failure detection
+ * @RESET_TYPE_XTAL_FAIL: 32KHz crystal failure dectection fault
+ * @RESET_TYPE_ULP2: ULP2 reset
+ */
enum reset_type {
RESET_TYPE_GENERAL = 0,
RESET_TYPE_WAKEUP = 1,
@@ -50,15 +64,48 @@ enum reset_type {
RESET_TYPE_ULP2 = 8,
};
+/**
+ * struct at91_reset - AT91 reset specific data structure
+ * @rstc_base: base address for system reset
+ * @ramc_base: array with base addresses of RAM controllers
+ * @dev_base: base address for devices reset
+ * @sclk: slow clock
+ * @data: platform specific reset data
+ * @rcdev: reset controller device
+ * @lock: lock for devices reset register access
+ * @nb: reset notifier block
+ * @args: SoC specific system reset arguments
+ * @ramc_lpr: SDRAM Controller Low Power Register
+ */
struct at91_reset {
void __iomem *rstc_base;
void __iomem *ramc_base[2];
+ void __iomem *dev_base;
struct clk *sclk;
+ const struct at91_reset_data *data;
+ struct reset_controller_dev rcdev;
+ spinlock_t lock;
struct notifier_block nb;
u32 args;
u32 ramc_lpr;
};
+#define to_at91_reset(r) container_of(r, struct at91_reset, rcdev)
+
+/**
+ * struct at91_reset_data - AT91 reset data
+ * @reset_args: SoC specific system reset arguments
+ * @n_device_reset: number of device resets
+ * @device_reset_min_id: min id for device reset
+ * @device_reset_max_id: max id for device reset
+ */
+struct at91_reset_data {
+ u32 reset_args;
+ u32 n_device_reset;
+ u8 device_reset_min_id;
+ u8 device_reset_max_id;
+};
+
/*
* unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and
@@ -95,7 +142,7 @@ static int at91_reset(struct notifier_block *this, unsigned long mode,
"r" (reset->rstc_base),
"r" (1),
"r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN),
- "r" (reset->args),
+ "r" (reset->data->reset_args),
"r" (reset->ramc_lpr)
: "r4");
@@ -153,34 +200,133 @@ static const struct of_device_id at91_ramc_of_match[] = {
{ /* sentinel */ }
};
+static const struct at91_reset_data sam9260 = {
+ .reset_args = AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST,
+};
+
+static const struct at91_reset_data samx7 = {
+ .reset_args = AT91_RSTC_KEY | AT91_RSTC_PROCRST,
+};
+
+static const struct at91_reset_data sama7g5 = {
+ .reset_args = AT91_RSTC_KEY | AT91_RSTC_PROCRST,
+ .n_device_reset = 3,
+ .device_reset_min_id = SAMA7G5_RESET_USB_PHY1,
+ .device_reset_max_id = SAMA7G5_RESET_USB_PHY3,
+};
+
static const struct of_device_id at91_reset_of_match[] = {
{
.compatible = "atmel,at91sam9260-rstc",
- .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST |
- AT91_RSTC_PROCRST),
+ .data = &sam9260,
},
{
.compatible = "atmel,at91sam9g45-rstc",
- .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST |
- AT91_RSTC_PROCRST)
+ .data = &sam9260,
},
{
.compatible = "atmel,sama5d3-rstc",
- .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST |
- AT91_RSTC_PROCRST)
+ .data = &sam9260,
},
{
.compatible = "atmel,samx7-rstc",
- .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST)
+ .data = &samx7,
},
{
.compatible = "microchip,sam9x60-rstc",
- .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST)
+ .data = &samx7,
+ },
+ {
+ .compatible = "microchip,sama7g5-rstc",
+ .data = &sama7g5,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_reset_of_match);
+static int at91_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct at91_reset *reset = to_at91_reset(rcdev);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&reset->lock, flags);
+ val = readl_relaxed(reset->dev_base);
+ if (assert)
+ val |= BIT(id);
+ else
+ val &= ~BIT(id);
+ writel_relaxed(val, reset->dev_base);
+ spin_unlock_irqrestore(&reset->lock, flags);
+
+ return 0;
+}
+
+static int at91_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return at91_reset_update(rcdev, id, true);
+}
+
+static int at91_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return at91_reset_update(rcdev, id, false);
+}
+
+static int at91_reset_dev_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct at91_reset *reset = to_at91_reset(rcdev);
+ u32 val;
+
+ val = readl_relaxed(reset->dev_base);
+
+ return !!(val & BIT(id));
+}
+
+static const struct reset_control_ops at91_reset_ops = {
+ .assert = at91_reset_assert,
+ .deassert = at91_reset_deassert,
+ .status = at91_reset_dev_status,
+};
+
+static int at91_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct at91_reset *reset = to_at91_reset(rcdev);
+
+ if (!reset->data->n_device_reset ||
+ (reset_spec->args[0] < reset->data->device_reset_min_id ||
+ reset_spec->args[0] > reset->data->device_reset_max_id))
+ return -EINVAL;
+
+ return reset_spec->args[0];
+}
+
+static int at91_rcdev_init(struct at91_reset *reset,
+ struct platform_device *pdev)
+{
+ if (!reset->data->n_device_reset)
+ return 0;
+
+ reset->dev_base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 1,
+ NULL);
+ if (IS_ERR(reset->dev_base))
+ return -ENODEV;
+
+ spin_lock_init(&reset->lock);
+ reset->rcdev.ops = &at91_reset_ops;
+ reset->rcdev.owner = THIS_MODULE;
+ reset->rcdev.of_node = pdev->dev.of_node;
+ reset->rcdev.nr_resets = reset->data->n_device_reset;
+ reset->rcdev.of_reset_n_cells = 1;
+ reset->rcdev.of_xlate = at91_reset_of_xlate;
+
+ return devm_reset_controller_register(&pdev->dev, &reset->rcdev);
+}
+
static int __init at91_reset_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -212,10 +358,12 @@ static int __init at91_reset_probe(struct platform_device *pdev)
}
}
- match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
+ reset->data = device_get_match_data(&pdev->dev);
+ if (!reset->data)
+ return -ENODEV;
+
reset->nb.notifier_call = at91_reset;
reset->nb.priority = 192;
- reset->args = (u32)match->data;
reset->sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(reset->sclk))
@@ -229,6 +377,10 @@ static int __init at91_reset_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, reset);
+ ret = at91_rcdev_init(reset, pdev);
+ if (ret)
+ goto disable_clk;
+
if (of_device_is_compatible(pdev->dev.of_node, "microchip,sam9x60-rstc")) {
u32 val = readl(reset->rstc_base + AT91_RSTC_MR);
@@ -237,14 +389,16 @@ static int __init at91_reset_probe(struct platform_device *pdev)
}
ret = register_restart_handler(&reset->nb);
- if (ret) {
- clk_disable_unprepare(reset->sclk);
- return ret;
- }
+ if (ret)
+ goto disable_clk;
at91_reset_status(pdev, reset->rstc_base);
return 0;
+
+disable_clk:
+ clk_disable_unprepare(reset->sclk);
+ return ret;
}
static int __exit at91_reset_remove(struct platform_device *pdev)