linux/drivers/power/reset/vexpress-poweroff.c
<<
>>
Prefs
   1/*
   2 * This program is free software; you can redistribute it and/or modify
   3 * it under the terms of the GNU General Public License version 2 as
   4 * published by the Free Software Foundation.
   5 *
   6 * This program is distributed in the hope that it will be useful,
   7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   9 * GNU General Public License for more details.
  10 *
  11 * Copyright (C) 2012 ARM Limited
  12 */
  13
  14#include <linux/delay.h>
  15#include <linux/notifier.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/reboot.h>
  20#include <linux/stat.h>
  21#include <linux/vexpress.h>
  22
  23static void vexpress_reset_do(struct device *dev, const char *what)
  24{
  25        int err = -ENOENT;
  26        struct regmap *reg = dev_get_drvdata(dev);
  27
  28        if (reg) {
  29                err = regmap_write(reg, 0, 0);
  30                if (!err)
  31                        mdelay(1000);
  32        }
  33
  34        dev_emerg(dev, "Unable to %s (%d)\n", what, err);
  35}
  36
  37static struct device *vexpress_power_off_device;
  38static atomic_t vexpress_restart_nb_refcnt = ATOMIC_INIT(0);
  39
  40static void vexpress_power_off(void)
  41{
  42        vexpress_reset_do(vexpress_power_off_device, "power off");
  43}
  44
  45static struct device *vexpress_restart_device;
  46
  47static int vexpress_restart(struct notifier_block *this, unsigned long mode,
  48                             void *cmd)
  49{
  50        vexpress_reset_do(vexpress_restart_device, "restart");
  51
  52        return NOTIFY_DONE;
  53}
  54
  55static struct notifier_block vexpress_restart_nb = {
  56        .notifier_call = vexpress_restart,
  57        .priority = 128,
  58};
  59
  60static ssize_t vexpress_reset_active_show(struct device *dev,
  61                struct device_attribute *attr, char *buf)
  62{
  63        return sprintf(buf, "%d\n", vexpress_restart_device == dev);
  64}
  65
  66static ssize_t vexpress_reset_active_store(struct device *dev,
  67                struct device_attribute *attr, const char *buf, size_t count)
  68{
  69        long value;
  70        int err = kstrtol(buf, 0, &value);
  71
  72        if (!err && value)
  73                vexpress_restart_device = dev;
  74
  75        return err ? err : count;
  76}
  77
  78static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show,
  79                   vexpress_reset_active_store);
  80
  81
  82enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT };
  83
  84static const struct of_device_id vexpress_reset_of_match[] = {
  85        {
  86                .compatible = "arm,vexpress-reset",
  87                .data = (void *)FUNC_RESET,
  88        }, {
  89                .compatible = "arm,vexpress-shutdown",
  90                .data = (void *)FUNC_SHUTDOWN
  91        }, {
  92                .compatible = "arm,vexpress-reboot",
  93                .data = (void *)FUNC_REBOOT
  94        },
  95        {}
  96};
  97
  98static int _vexpress_register_restart_handler(struct device *dev)
  99{
 100        int err;
 101
 102        vexpress_restart_device = dev;
 103        if (atomic_inc_return(&vexpress_restart_nb_refcnt) == 1) {
 104                err = register_restart_handler(&vexpress_restart_nb);
 105                if (err) {
 106                        dev_err(dev, "cannot register restart handler (err=%d)\n", err);
 107                        atomic_dec(&vexpress_restart_nb_refcnt);
 108                        return err;
 109                }
 110        }
 111        device_create_file(dev, &dev_attr_active);
 112
 113        return 0;
 114}
 115
 116static int vexpress_reset_probe(struct platform_device *pdev)
 117{
 118        const struct of_device_id *match =
 119                        of_match_device(vexpress_reset_of_match, &pdev->dev);
 120        struct regmap *regmap;
 121        int ret = 0;
 122
 123        if (!match)
 124                return -EINVAL;
 125
 126        regmap = devm_regmap_init_vexpress_config(&pdev->dev);
 127        if (IS_ERR(regmap))
 128                return PTR_ERR(regmap);
 129        dev_set_drvdata(&pdev->dev, regmap);
 130
 131        switch ((enum vexpress_reset_func)match->data) {
 132        case FUNC_SHUTDOWN:
 133                vexpress_power_off_device = &pdev->dev;
 134                pm_power_off = vexpress_power_off;
 135                break;
 136        case FUNC_RESET:
 137                if (!vexpress_restart_device)
 138                        ret = _vexpress_register_restart_handler(&pdev->dev);
 139                break;
 140        case FUNC_REBOOT:
 141                ret = _vexpress_register_restart_handler(&pdev->dev);
 142                break;
 143        };
 144
 145        return ret;
 146}
 147
 148static struct platform_driver vexpress_reset_driver = {
 149        .probe = vexpress_reset_probe,
 150        .driver = {
 151                .name = "vexpress-reset",
 152                .of_match_table = vexpress_reset_of_match,
 153        },
 154};
 155
 156static int __init vexpress_reset_init(void)
 157{
 158        return platform_driver_register(&vexpress_reset_driver);
 159}
 160device_initcall(vexpress_reset_init);
 161