1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Hisilicon Reset Controller Driver |
4 | * |
5 | * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. |
6 | */ |
7 | |
8 | #include <linux/io.h> |
9 | #include <linux/of_address.h> |
10 | #include <linux/platform_device.h> |
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(addr: rstc->membase + offset); |
57 | writel(val: reg | BIT(bit), addr: rstc->membase + offset); |
58 | |
59 | spin_unlock_irqrestore(lock: &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(addr: rstc->membase + offset); |
78 | writel(val: reg & ~BIT(bit), addr: rstc->membase + offset); |
79 | |
80 | spin_unlock_irqrestore(lock: &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 | |
90 | struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) |
91 | { |
92 | struct hisi_reset_controller *rstc; |
93 | |
94 | rstc = devm_kmalloc(dev: &pdev->dev, size: sizeof(*rstc), GFP_KERNEL); |
95 | if (!rstc) |
96 | return NULL; |
97 | |
98 | rstc->membase = devm_platform_ioremap_resource(pdev, index: 0); |
99 | if (IS_ERR(ptr: rstc->membase)) |
100 | return NULL; |
101 | |
102 | spin_lock_init(&rstc->lock); |
103 | rstc->rcdev.owner = THIS_MODULE; |
104 | rstc->rcdev.ops = &hisi_reset_ops; |
105 | rstc->rcdev.of_node = pdev->dev.of_node; |
106 | rstc->rcdev.of_reset_n_cells = 2; |
107 | rstc->rcdev.of_xlate = hisi_reset_of_xlate; |
108 | reset_controller_register(rcdev: &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(rcdev: &rstc->rcdev); |
117 | } |
118 | EXPORT_SYMBOL_GPL(hisi_reset_exit); |
119 | |