linux/drivers/power/reset/arm-versatile-reboot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Linaro Ltd.
   4 *
   5 * Author: Linus Walleij <linus.walleij@linaro.org>
   6 */
   7#include <linux/init.h>
   8#include <linux/mfd/syscon.h>
   9#include <linux/reboot.h>
  10#include <linux/regmap.h>
  11#include <linux/of.h>
  12
  13#define INTEGRATOR_HDR_CTRL_OFFSET      0x0C
  14#define INTEGRATOR_HDR_LOCK_OFFSET      0x14
  15#define INTEGRATOR_CM_CTRL_RESET        (1 << 3)
  16
  17#define VERSATILE_SYS_LOCK_OFFSET       0x20
  18#define VERSATILE_SYS_RESETCTL_OFFSET   0x40
  19
  20/* Magic unlocking token used on all Versatile boards */
  21#define VERSATILE_LOCK_VAL              0xA05F
  22
  23/*
  24 * We detect the different syscon types from the compatible strings.
  25 */
  26enum versatile_reboot {
  27        INTEGRATOR_REBOOT_CM,
  28        VERSATILE_REBOOT_CM,
  29        REALVIEW_REBOOT_EB,
  30        REALVIEW_REBOOT_PB1176,
  31        REALVIEW_REBOOT_PB11MP,
  32        REALVIEW_REBOOT_PBA8,
  33        REALVIEW_REBOOT_PBX,
  34};
  35
  36/* Pointer to the system controller */
  37static struct regmap *syscon_regmap;
  38static enum versatile_reboot versatile_reboot_type;
  39
  40static const struct of_device_id versatile_reboot_of_match[] = {
  41        {
  42                .compatible = "arm,core-module-integrator",
  43                .data = (void *)INTEGRATOR_REBOOT_CM
  44        },
  45        {
  46                .compatible = "arm,core-module-versatile",
  47                .data = (void *)VERSATILE_REBOOT_CM,
  48        },
  49        {
  50                .compatible = "arm,realview-eb-syscon",
  51                .data = (void *)REALVIEW_REBOOT_EB,
  52        },
  53        {
  54                .compatible = "arm,realview-pb1176-syscon",
  55                .data = (void *)REALVIEW_REBOOT_PB1176,
  56        },
  57        {
  58                .compatible = "arm,realview-pb11mp-syscon",
  59                .data = (void *)REALVIEW_REBOOT_PB11MP,
  60        },
  61        {
  62                .compatible = "arm,realview-pba8-syscon",
  63                .data = (void *)REALVIEW_REBOOT_PBA8,
  64        },
  65        {
  66                .compatible = "arm,realview-pbx-syscon",
  67                .data = (void *)REALVIEW_REBOOT_PBX,
  68        },
  69        {},
  70};
  71
  72static int versatile_reboot(struct notifier_block *this, unsigned long mode,
  73                            void *cmd)
  74{
  75        /* Unlock the reset register */
  76        /* Then hit reset on the different machines */
  77        switch (versatile_reboot_type) {
  78        case INTEGRATOR_REBOOT_CM:
  79                regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET,
  80                             VERSATILE_LOCK_VAL);
  81                regmap_update_bits(syscon_regmap,
  82                                   INTEGRATOR_HDR_CTRL_OFFSET,
  83                                   INTEGRATOR_CM_CTRL_RESET,
  84                                   INTEGRATOR_CM_CTRL_RESET);
  85                break;
  86        case VERSATILE_REBOOT_CM:
  87                regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
  88                             VERSATILE_LOCK_VAL);
  89                regmap_update_bits(syscon_regmap,
  90                                   VERSATILE_SYS_RESETCTL_OFFSET,
  91                                   0x0107,
  92                                   0x0105);
  93                regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
  94                             0);
  95                break;
  96        case REALVIEW_REBOOT_EB:
  97                regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
  98                             VERSATILE_LOCK_VAL);
  99                regmap_write(syscon_regmap,
 100                             VERSATILE_SYS_RESETCTL_OFFSET, 0x0008);
 101                break;
 102        case REALVIEW_REBOOT_PB1176:
 103                regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
 104                             VERSATILE_LOCK_VAL);
 105                regmap_write(syscon_regmap,
 106                             VERSATILE_SYS_RESETCTL_OFFSET, 0x0100);
 107                break;
 108        case REALVIEW_REBOOT_PB11MP:
 109        case REALVIEW_REBOOT_PBA8:
 110                regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
 111                             VERSATILE_LOCK_VAL);
 112                regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
 113                             0x0000);
 114                regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
 115                             0x0004);
 116                break;
 117        case REALVIEW_REBOOT_PBX:
 118                regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET,
 119                             VERSATILE_LOCK_VAL);
 120                regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
 121                             0x00f0);
 122                regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET,
 123                             0x00f4);
 124                break;
 125        }
 126        dsb();
 127
 128        return NOTIFY_DONE;
 129}
 130
 131static struct notifier_block versatile_reboot_nb = {
 132        .notifier_call = versatile_reboot,
 133        .priority = 192,
 134};
 135
 136static int __init versatile_reboot_probe(void)
 137{
 138        const struct of_device_id *reboot_id;
 139        struct device_node *np;
 140        int err;
 141
 142        np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
 143                                                 &reboot_id);
 144        if (!np)
 145                return -ENODEV;
 146        versatile_reboot_type = (enum versatile_reboot)reboot_id->data;
 147
 148        syscon_regmap = syscon_node_to_regmap(np);
 149        if (IS_ERR(syscon_regmap))
 150                return PTR_ERR(syscon_regmap);
 151
 152        err = register_restart_handler(&versatile_reboot_nb);
 153        if (err)
 154                return err;
 155
 156        pr_info("versatile reboot driver registered\n");
 157        return 0;
 158}
 159device_initcall(versatile_reboot_probe);
 160