1 /* 2 * Hisilicon Reset Controller Driver 3 * 4 * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/io.h> 21 #include <linux/of_address.h> 22 #include <linux/reset-controller.h> 23 #include <linux/slab.h> 24 #include <linux/spinlock.h> 25 #include "reset.h" 26 27 #define HISI_RESET_BIT_MASK 0x1f 28 #define HISI_RESET_OFFSET_SHIFT 8 29 #define HISI_RESET_OFFSET_MASK 0xffff00 30 31 struct hisi_reset_controller { 32 spinlock_t lock; 33 void __iomem *membase; 34 struct reset_controller_dev rcdev; 35 }; 36 37 38 #define to_hisi_reset_controller(rcdev) \ 39 container_of(rcdev, struct hisi_reset_controller, rcdev) 40 41 static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev, 42 const struct of_phandle_args *reset_spec) 43 { 44 u32 offset; 45 u8 bit; 46 47 offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT) 48 & HISI_RESET_OFFSET_MASK; 49 bit = reset_spec->args[1] & HISI_RESET_BIT_MASK; 50 51 return (offset | bit); 52 } 53 54 static int hisi_reset_assert(struct reset_controller_dev *rcdev, 55 unsigned long id) 56 { 57 struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); 58 unsigned long flags; 59 u32 offset, reg; 60 u8 bit; 61 62 offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; 63 bit = id & HISI_RESET_BIT_MASK; 64 65 spin_lock_irqsave(&rstc->lock, flags); 66 67 reg = readl(rstc->membase + offset); 68 writel(reg | BIT(bit), rstc->membase + offset); 69 70 spin_unlock_irqrestore(&rstc->lock, flags); 71 72 return 0; 73 } 74 75 static int hisi_reset_deassert(struct reset_controller_dev *rcdev, 76 unsigned long id) 77 { 78 struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); 79 unsigned long flags; 80 u32 offset, reg; 81 u8 bit; 82 83 offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; 84 bit = id & HISI_RESET_BIT_MASK; 85 86 spin_lock_irqsave(&rstc->lock, flags); 87 88 reg = readl(rstc->membase + offset); 89 writel(reg & ~BIT(bit), rstc->membase + offset); 90 91 spin_unlock_irqrestore(&rstc->lock, flags); 92 93 return 0; 94 } 95 96 static const struct reset_control_ops hisi_reset_ops = { 97 .assert = hisi_reset_assert, 98 .deassert = hisi_reset_deassert, 99 }; 100 101 struct hisi_reset_controller *hisi_reset_init(struct device_node *np) 102 { 103 struct hisi_reset_controller *rstc; 104 105 rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); 106 if (!rstc) 107 return NULL; 108 109 rstc->membase = of_iomap(np, 0); 110 if (!rstc->membase) { 111 kfree(rstc); 112 return NULL; 113 } 114 115 spin_lock_init(&rstc->lock); 116 117 rstc->rcdev.owner = THIS_MODULE; 118 rstc->rcdev.ops = &hisi_reset_ops; 119 rstc->rcdev.of_node = np; 120 rstc->rcdev.of_reset_n_cells = 2; 121 rstc->rcdev.of_xlate = hisi_reset_of_xlate; 122 reset_controller_register(&rstc->rcdev); 123 124 return rstc; 125 } 126 EXPORT_SYMBOL_GPL(hisi_reset_init); 127 128 void hisi_reset_exit(struct hisi_reset_controller *rstc) 129 { 130 reset_controller_unregister(&rstc->rcdev); 131 iounmap(rstc->membase); 132 kfree(rstc); 133 } 134 EXPORT_SYMBOL_GPL(hisi_reset_exit); 135