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 syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node); 57 if (IS_ERR(syscon_rbm->map)) 58 return PTR_ERR(syscon_rbm->map); 59 60 if (of_property_read_u32(pdev->dev.of_node, "offset", 61 &syscon_rbm->offset)) 62 return -EINVAL; 63 64 of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask); 65 66 ret = devm_reboot_mode_register(&pdev->dev, &syscon_rbm->reboot); 67 if (ret) 68 dev_err(&pdev->dev, "can't register reboot mode\n"); 69 70 return ret; 71 } 72 73 static const struct of_device_id syscon_reboot_mode_of_match[] = { 74 { .compatible = "syscon-reboot-mode" }, 75 {} 76 }; 77 MODULE_DEVICE_TABLE(of, syscon_reboot_mode_of_match); 78 79 static struct platform_driver syscon_reboot_mode_driver = { 80 .probe = syscon_reboot_mode_probe, 81 .driver = { 82 .name = "syscon-reboot-mode", 83 .of_match_table = syscon_reboot_mode_of_match, 84 }, 85 }; 86 module_platform_driver(syscon_reboot_mode_driver); 87 88 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); 89 MODULE_DESCRIPTION("SYSCON reboot mode driver"); 90 MODULE_LICENSE("GPL v2"); 91