1 /* 2 * Copyright (c) 2016-2017 Linaro Ltd. 3 * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 */ 10 #include <linux/kernel.h> 11 #include <linux/mfd/syscon.h> 12 #include <linux/module.h> 13 #include <linux/of_device.h> 14 #include <linux/platform_device.h> 15 #include <linux/regmap.h> 16 #include <linux/reset-controller.h> 17 18 struct hi3660_reset_controller { 19 struct reset_controller_dev rst; 20 struct regmap *map; 21 }; 22 23 #define to_hi3660_reset_controller(_rst) \ 24 container_of(_rst, struct hi3660_reset_controller, rst) 25 26 static int hi3660_reset_program_hw(struct reset_controller_dev *rcdev, 27 unsigned long idx, bool assert) 28 { 29 struct hi3660_reset_controller *rc = to_hi3660_reset_controller(rcdev); 30 unsigned int offset = idx >> 8; 31 unsigned int mask = BIT(idx & 0x1f); 32 33 if (assert) 34 return regmap_write(rc->map, offset, mask); 35 else 36 return regmap_write(rc->map, offset + 4, mask); 37 } 38 39 static int hi3660_reset_assert(struct reset_controller_dev *rcdev, 40 unsigned long idx) 41 { 42 return hi3660_reset_program_hw(rcdev, idx, true); 43 } 44 45 static int hi3660_reset_deassert(struct reset_controller_dev *rcdev, 46 unsigned long idx) 47 { 48 return hi3660_reset_program_hw(rcdev, idx, false); 49 } 50 51 static int hi3660_reset_dev(struct reset_controller_dev *rcdev, 52 unsigned long idx) 53 { 54 int err; 55 56 err = hi3660_reset_assert(rcdev, idx); 57 if (err) 58 return err; 59 60 return hi3660_reset_deassert(rcdev, idx); 61 } 62 63 static struct reset_control_ops hi3660_reset_ops = { 64 .reset = hi3660_reset_dev, 65 .assert = hi3660_reset_assert, 66 .deassert = hi3660_reset_deassert, 67 }; 68 69 static int hi3660_reset_xlate(struct reset_controller_dev *rcdev, 70 const struct of_phandle_args *reset_spec) 71 { 72 unsigned int offset, bit; 73 74 offset = reset_spec->args[0]; 75 bit = reset_spec->args[1]; 76 77 return (offset << 8) | bit; 78 } 79 80 static int hi3660_reset_probe(struct platform_device *pdev) 81 { 82 struct hi3660_reset_controller *rc; 83 struct device_node *np = pdev->dev.of_node; 84 struct device *dev = &pdev->dev; 85 86 rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); 87 if (!rc) 88 return -ENOMEM; 89 90 rc->map = syscon_regmap_lookup_by_phandle(np, "hisi,rst-syscon"); 91 if (IS_ERR(rc->map)) { 92 dev_err(dev, "failed to get hi3660,rst-syscon\n"); 93 return PTR_ERR(rc->map); 94 } 95 96 rc->rst.ops = &hi3660_reset_ops, 97 rc->rst.of_node = np; 98 rc->rst.of_reset_n_cells = 2; 99 rc->rst.of_xlate = hi3660_reset_xlate; 100 101 return reset_controller_register(&rc->rst); 102 } 103 104 static const struct of_device_id hi3660_reset_match[] = { 105 { .compatible = "hisilicon,hi3660-reset", }, 106 {}, 107 }; 108 MODULE_DEVICE_TABLE(of, hi3660_reset_match); 109 110 static struct platform_driver hi3660_reset_driver = { 111 .probe = hi3660_reset_probe, 112 .driver = { 113 .name = "hi3660-reset", 114 .of_match_table = hi3660_reset_match, 115 }, 116 }; 117 118 static int __init hi3660_reset_init(void) 119 { 120 return platform_driver_register(&hi3660_reset_driver); 121 } 122 arch_initcall(hi3660_reset_init); 123 124 MODULE_LICENSE("GPL"); 125 MODULE_ALIAS("platform:hi3660-reset"); 126 MODULE_DESCRIPTION("HiSilicon Hi3660 Reset Driver"); 127