164 lines
4.2 KiB
C
164 lines
4.2 KiB
C
|
/*
|
||
|
* Copyright (C) 2014 Linaro Ltd.
|
||
|
*
|
||
|
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||
|
*
|
||
|
* 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/init.h>
|
||
|
#include <linux/mfd/syscon.h>
|
||
|
#include <linux/reboot.h>
|
||
|
#include <linux/regmap.h>
|
||
|
#include <linux/of.h>
|
||
|
|
||
|
#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
|
||
|
#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
|
||
|
#define INTEGRATOR_CM_CTRL_RESET (1 << 3)
|
||
|
|
||
|
#define VERSATILE_SYS_LOCK_OFFSET 0x20
|
||
|
#define VERSATILE_SYS_RESETCTL_OFFSET 0x40
|
||
|
|
||
|
/* Magic unlocking token used on all Versatile boards */
|
||
|
#define VERSATILE_LOCK_VAL 0xA05F
|
||
|
|
||
|
/*
|
||
|
* We detect the different syscon types from the compatible strings.
|
||
|
*/
|
||
|
enum versatile_reboot {
|
||
|
INTEGRATOR_REBOOT_CM,
|
||
|
VERSATILE_REBOOT_CM,
|
||
|
REALVIEW_REBOOT_EB,
|
||
|
REALVIEW_REBOOT_PB1176,
|
||
|
REALVIEW_REBOOT_PB11MP,
|
||
|
REALVIEW_REBOOT_PBA8,
|
||
|
REALVIEW_REBOOT_PBX,
|
||
|
};
|
||
|
|
||
|
/* Pointer to the system controller */
|
||
|
static struct regmap *syscon_regmap;
|
||
|
static enum versatile_reboot versatile_reboot_type;
|
||
|
|
||
|
static const struct of_device_id versatile_reboot_of_match[] = {
|
||
|
{
|
||
|
.compatible = "arm,core-module-integrator",
|
||
|
.data = (void *)INTEGRATOR_REBOOT_CM
|
||
|
},
|
||
|
{
|
||
|
.compatible = "arm,core-module-versatile",
|
||
|
.data = (void *)VERSATILE_REBOOT_CM,
|
||
|
},
|
||
|
{
|
||
|
.compatible = "arm,realview-eb-syscon",
|
||
|
.data = (void *)REALVIEW_REBOOT_EB,
|
||
|
},
|
||
|
{
|
||
|
.compatible = "arm,realview-pb1176-syscon",
|
||
|
.data = (void *)REALVIEW_REBOOT_PB1176,
|
||
|
},
|
||
|
{
|
||
|
.compatible = "arm,realview-pb11mp-syscon",
|
||
|
.data = (void *)REALVIEW_REBOOT_PB11MP,
|
||
|
},
|
||
|
{
|
||
|
.compatible = "arm,realview-pba8-syscon",
|
||
|
.data = (void *)REALVIEW_REBOOT_PBA8,
|
||
|
},
|
||
|
{
|
||
|
.compatible = "arm,realview-pbx-syscon",
|
||
|
.data = (void *)REALVIEW_REBOOT_PBX,
|
||
|
},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
static int versatile_reboot(struct notifier_block *this, unsigned long mode,
|
||
|
void *cmd)
|
||
|
{
|
||
|
/* Unlock the reset register */
|
||
|
/* Then hit reset on the different machines */
|
||
|
switch (versatile_reboot_type) {
|
||
|
case INTEGRATOR_REBOOT_CM:
|
||
|
regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET,
|
||
|
VERSATILE_LOCK_VAL);
|
||
|
regmap_update_bits(syscon_regmap,
|
||
|
INTEGRATOR_HDR_CTRL_OFFSET,
|
||
|
INTEGRATOR_CM_CTRL_RESET,
|
||
|
INTEGRATOR_CM_CTRL_RESET);
|
||
|
break;
|
||
|
case VERSATILE_REBOOT_CM:
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
|
||
|
VERSATILE_LOCK_VAL);
|
||
|
regmap_update_bits(syscon_regmap,
|
||
|
VERSATILE_SYS_RESETCTL_OFFSET,
|
||
|
0x0107,
|
||
|
0x0105);
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
|
||
|
0);
|
||
|
break;
|
||
|
case REALVIEW_REBOOT_EB:
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
|
||
|
VERSATILE_LOCK_VAL);
|
||
|
regmap_write(syscon_regmap,
|
||
|
VERSATILE_SYS_RESETCTL_OFFSET, 0x0008);
|
||
|
break;
|
||
|
case REALVIEW_REBOOT_PB1176:
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
|
||
|
VERSATILE_LOCK_VAL);
|
||
|
regmap_write(syscon_regmap,
|
||
|
VERSATILE_SYS_RESETCTL_OFFSET, 0x0100);
|
||
|
break;
|
||
|
case REALVIEW_REBOOT_PB11MP:
|
||
|
case REALVIEW_REBOOT_PBA8:
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
|
||
|
VERSATILE_LOCK_VAL);
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
|
||
|
0x0000);
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
|
||
|
0x0004);
|
||
|
break;
|
||
|
case REALVIEW_REBOOT_PBX:
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
|
||
|
VERSATILE_LOCK_VAL);
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
|
||
|
0x00f0);
|
||
|
regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
|
||
|
0x00f4);
|
||
|
break;
|
||
|
}
|
||
|
dsb();
|
||
|
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
|
||
|
static struct notifier_block versatile_reboot_nb = {
|
||
|
.notifier_call = versatile_reboot,
|
||
|
.priority = 192,
|
||
|
};
|
||
|
|
||
|
static int __init versatile_reboot_probe(void)
|
||
|
{
|
||
|
const struct of_device_id *reboot_id;
|
||
|
struct device_node *np;
|
||
|
int err;
|
||
|
|
||
|
np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
|
||
|
&reboot_id);
|
||
|
if (!np)
|
||
|
return -ENODEV;
|
||
|
versatile_reboot_type = (enum versatile_reboot)reboot_id->data;
|
||
|
|
||
|
syscon_regmap = syscon_node_to_regmap(np);
|
||
|
if (IS_ERR(syscon_regmap))
|
||
|
return PTR_ERR(syscon_regmap);
|
||
|
|
||
|
err = register_restart_handler(&versatile_reboot_nb);
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
pr_info("versatile reboot driver registered\n");
|
||
|
return 0;
|
||
|
}
|
||
|
device_initcall(versatile_reboot_probe);
|