]>
Commit | Line | Data |
---|---|---|
25824d52 JX |
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> | |
97b7129c | 22 | #include <linux/platform_device.h> |
25824d52 JX |
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 | ||
97b7129c | 102 | struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) |
25824d52 JX |
103 | { |
104 | struct hisi_reset_controller *rstc; | |
97b7129c | 105 | struct resource *res; |
25824d52 | 106 | |
97b7129c | 107 | rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); |
25824d52 JX |
108 | if (!rstc) |
109 | return NULL; | |
110 | ||
97b7129c JX |
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) | |
25824d52 | 115 | return NULL; |
25824d52 JX |
116 | |
117 | spin_lock_init(&rstc->lock); | |
25824d52 JX |
118 | rstc->rcdev.owner = THIS_MODULE; |
119 | rstc->rcdev.ops = &hisi_reset_ops; | |
97b7129c | 120 | rstc->rcdev.of_node = pdev->dev.of_node; |
25824d52 JX |
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); | |
25824d52 JX |
132 | } |
133 | EXPORT_SYMBOL_GPL(hisi_reset_exit); |