Commit | Line | Data |
---|---|---|
1ccea77e | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
25824d52 JX |
2 | /* |
3 | * Hisilicon Reset Controller Driver | |
4 | * | |
5 | * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. | |
25824d52 JX |
6 | */ |
7 | ||
8 | #include <linux/io.h> | |
9 | #include <linux/of_address.h> | |
97b7129c | 10 | #include <linux/platform_device.h> |
25824d52 JX |
11 | #include <linux/reset-controller.h> |
12 | #include <linux/slab.h> | |
13 | #include <linux/spinlock.h> | |
14 | #include "reset.h" | |
15 | ||
16 | #define HISI_RESET_BIT_MASK 0x1f | |
17 | #define HISI_RESET_OFFSET_SHIFT 8 | |
18 | #define HISI_RESET_OFFSET_MASK 0xffff00 | |
19 | ||
20 | struct hisi_reset_controller { | |
21 | spinlock_t lock; | |
22 | void __iomem *membase; | |
23 | struct reset_controller_dev rcdev; | |
24 | }; | |
25 | ||
26 | ||
27 | #define to_hisi_reset_controller(rcdev) \ | |
28 | container_of(rcdev, struct hisi_reset_controller, rcdev) | |
29 | ||
30 | static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev, | |
31 | const struct of_phandle_args *reset_spec) | |
32 | { | |
33 | u32 offset; | |
34 | u8 bit; | |
35 | ||
36 | offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT) | |
37 | & HISI_RESET_OFFSET_MASK; | |
38 | bit = reset_spec->args[1] & HISI_RESET_BIT_MASK; | |
39 | ||
40 | return (offset | bit); | |
41 | } | |
42 | ||
43 | static int hisi_reset_assert(struct reset_controller_dev *rcdev, | |
44 | unsigned long id) | |
45 | { | |
46 | struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); | |
47 | unsigned long flags; | |
48 | u32 offset, reg; | |
49 | u8 bit; | |
50 | ||
51 | offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; | |
52 | bit = id & HISI_RESET_BIT_MASK; | |
53 | ||
54 | spin_lock_irqsave(&rstc->lock, flags); | |
55 | ||
56 | reg = readl(rstc->membase + offset); | |
57 | writel(reg | BIT(bit), rstc->membase + offset); | |
58 | ||
59 | spin_unlock_irqrestore(&rstc->lock, flags); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | static int hisi_reset_deassert(struct reset_controller_dev *rcdev, | |
65 | unsigned long id) | |
66 | { | |
67 | struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); | |
68 | unsigned long flags; | |
69 | u32 offset, reg; | |
70 | u8 bit; | |
71 | ||
72 | offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT; | |
73 | bit = id & HISI_RESET_BIT_MASK; | |
74 | ||
75 | spin_lock_irqsave(&rstc->lock, flags); | |
76 | ||
77 | reg = readl(rstc->membase + offset); | |
78 | writel(reg & ~BIT(bit), rstc->membase + offset); | |
79 | ||
80 | spin_unlock_irqrestore(&rstc->lock, flags); | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
85 | static const struct reset_control_ops hisi_reset_ops = { | |
86 | .assert = hisi_reset_assert, | |
87 | .deassert = hisi_reset_deassert, | |
88 | }; | |
89 | ||
97b7129c | 90 | struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) |
25824d52 JX |
91 | { |
92 | struct hisi_reset_controller *rstc; | |
93 | ||
97b7129c | 94 | rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); |
25824d52 JX |
95 | if (!rstc) |
96 | return NULL; | |
97 | ||
75cc0a12 | 98 | rstc->membase = devm_platform_ioremap_resource(pdev, 0); |
e9a2310f | 99 | if (IS_ERR(rstc->membase)) |
25824d52 | 100 | return NULL; |
25824d52 JX |
101 | |
102 | spin_lock_init(&rstc->lock); | |
25824d52 JX |
103 | rstc->rcdev.owner = THIS_MODULE; |
104 | rstc->rcdev.ops = &hisi_reset_ops; | |
97b7129c | 105 | rstc->rcdev.of_node = pdev->dev.of_node; |
25824d52 JX |
106 | rstc->rcdev.of_reset_n_cells = 2; |
107 | rstc->rcdev.of_xlate = hisi_reset_of_xlate; | |
108 | reset_controller_register(&rstc->rcdev); | |
109 | ||
110 | return rstc; | |
111 | } | |
112 | EXPORT_SYMBOL_GPL(hisi_reset_init); | |
113 | ||
114 | void hisi_reset_exit(struct hisi_reset_controller *rstc) | |
115 | { | |
116 | reset_controller_unregister(&rstc->rcdev); | |
25824d52 JX |
117 | } |
118 | EXPORT_SYMBOL_GPL(hisi_reset_exit); |