1 /* 2 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 */ 9 10 #include <linux/init.h> 11 #include <linux/module.h> 12 #include <linux/kernel.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/reboot.h> 16 #include <linux/regmap.h> 17 #include <linux/mfd/syscon.h> 18 #include "reboot-mode.h" 19 20 struct syscon_reboot_mode { 21 struct regmap *map; 22 struct reboot_mode_driver reboot; 23 u32 offset; 24 u32 mask; 25 }; 26 27 static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot, 28 unsigned int magic) 29 { 30 struct syscon_reboot_mode *syscon_rbm; 31 int ret; 32 33 syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot); 34 35 ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset, 36 syscon_rbm->mask, magic); 37 if (ret < 0) 38 dev_err(reboot->dev, "update reboot mode bits failed\n"); 39 40 return ret; 41 } 42 43 static int syscon_reboot_mode_probe(struct platform_device *pdev) 44 { 45 int ret; 46 struct syscon_reboot_mode *syscon_rbm; 47 48 syscon_rbm = devm_kzalloc(&pdev->dev, sizeof(*syscon_rbm), GFP_KERNEL); 49 if (!syscon_rbm) 50 return -ENOMEM; 51 52 syscon_rbm->reboot.dev = &pdev->dev; 53 syscon_rbm->reboot.write = syscon_reboot_mode_write; 54 syscon_rbm->mask = 0xffffffff; 55 56 dev_set_drvdata(&pdev->dev, syscon_rbm); 57 58 syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node); 59 if (IS_ERR(syscon_rbm->map)) 60 return PTR_ERR(syscon_rbm->map); 61 62 if (of_property_read_u32(pdev->dev.of_node, "offset", 63 &syscon_rbm->offset)) 64 return -EINVAL; 65 66 of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask); 67 68 ret = reboot_mode_register(&syscon_rbm->reboot); 69 if (ret) 70 dev_err(&pdev->dev, "can't register reboot mode\n"); 71 72 return ret; 73 } 74 75 static int syscon_reboot_mode_remove(struct platform_device *pdev) 76 { 77 struct syscon_reboot_mode *syscon_rbm = dev_get_drvdata(&pdev->dev); 78 79 return reboot_mode_unregister(&syscon_rbm->reboot); 80 } 81 82 static const struct of_device_id syscon_reboot_mode_of_match[] = { 83 { .compatible = "syscon-reboot-mode" }, 84 {} 85 }; 86 87 static struct platform_driver syscon_reboot_mode_driver = { 88 .probe = syscon_reboot_mode_probe, 89 .remove = syscon_reboot_mode_remove, 90 .driver = { 91 .name = "syscon-reboot-mode", 92 .of_match_table = syscon_reboot_mode_of_match, 93 }, 94 }; 95 module_platform_driver(syscon_reboot_mode_driver); 96 97 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); 98 MODULE_DESCRIPTION("SYSCON reboot mode driver"); 99 MODULE_LICENSE("GPL v2"); 100