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/platform_device.h> 23 #include <linux/reset-controller.h> 24 #include <linux/slab.h> 25 #include <linux/spinlock.h> 26 #include "reset.h" 27 28 #define HISI_RESET_BIT_MASK 0x1f 29 #define HISI_RESET_OFFSET_SHIFT 8 30 #define HISI_RESET_OFFSET_MASK 0xffff00 31 32 struct hisi_reset_controller { 33 spinlock_t lock; 34 void __iomem *membase; 35 struct reset_controller_dev rcdev; 36 }; 37 38 39 #define to_hisi_reset_controller(rcdev) \ 40 container_of(rcdev, struct hisi_reset_controller, rcdev) 41 42 static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev, 43 const struct of_phandle_args *reset_spec) 44 { 45 u32 offset; 46 u8 bit; 47 48 offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT) 49 & HISI_RESET_OFFSET_MASK; 50 bit = reset_spec->args[1] & HISI_RESET_BIT_MASK; 51 52 return (offset | bit); 53 } 54 55 static int hisi_reset_assert(struct reset_controller_dev *rcdev, 56 unsigned long id) 57 { 58 struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); 59 unsigned long flags; 60 u32 offset, reg; 61 u8 bit; 62 63 offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; 64 bit = id & HISI_RESET_BIT_MASK; 65 66 spin_lock_irqsave(&rstc->lock, flags); 67 68 reg = readl(rstc->membase + offset); 69 writel(reg | BIT(bit), rstc->membase + offset); 70 71 spin_unlock_irqrestore(&rstc->lock, flags); 72 73 return 0; 74 } 75 76 static int hisi_reset_deassert(struct reset_controller_dev *rcdev, 77 unsigned long id) 78 { 79 struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); 80 unsigned long flags; 81 u32 offset, reg; 82 u8 bit; 83 84 offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; 85 bit = id & HISI_RESET_BIT_MASK; 86 87 spin_lock_irqsave(&rstc->lock, flags); 88 89 reg = readl(rstc->membase + offset); 90 writel(reg & ~BIT(bit), rstc->membase + offset); 91 92 spin_unlock_irqrestore(&rstc->lock, flags); 93 94 return 0; 95 } 96 97 static const struct reset_control_ops hisi_reset_ops = { 98 .assert = hisi_reset_assert, 99 .deassert = hisi_reset_deassert, 100 }; 101 102 struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) 103 { 104 struct hisi_reset_controller *rstc; 105 struct resource *res; 106 107 rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); 108 if (!rstc) 109 return NULL; 110 111 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 112 rstc->membase = devm_ioremap(&pdev->dev, 113 res->start, resource_size(res)); 114 if (!rstc->membase) 115 return NULL; 116 117 spin_lock_init(&rstc->lock); 118 rstc->rcdev.owner = THIS_MODULE; 119 rstc->rcdev.ops = &hisi_reset_ops; 120 rstc->rcdev.of_node = pdev->dev.of_node; 121 rstc->rcdev.of_reset_n_cells = 2; 122 rstc->rcdev.of_xlate = hisi_reset_of_xlate; 123 reset_controller_register(&rstc->rcdev); 124 125 return rstc; 126 } 127 EXPORT_SYMBOL_GPL(hisi_reset_init); 128 129 void hisi_reset_exit(struct hisi_reset_controller *rstc) 130 { 131 reset_controller_unregister(&rstc->rcdev); 132 } 133 EXPORT_SYMBOL_GPL(hisi_reset_exit); 134