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