1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2014 MundoReader S.L. |
4 | * Author: Heiko Stuebner <heiko@sntech.de> |
5 | */ |
6 | |
7 | #include <linux/slab.h> |
8 | #include <linux/io.h> |
9 | #include <linux/reset-controller.h> |
10 | #include <linux/spinlock.h> |
11 | #include "clk.h" |
12 | |
13 | struct rockchip_softrst { |
14 | struct reset_controller_dev rcdev; |
15 | const int *lut; |
16 | void __iomem *reg_base; |
17 | int num_regs; |
18 | int num_per_reg; |
19 | u8 flags; |
20 | spinlock_t lock; |
21 | }; |
22 | |
23 | static int rockchip_softrst_assert(struct reset_controller_dev *rcdev, |
24 | unsigned long id) |
25 | { |
26 | struct rockchip_softrst *softrst = container_of(rcdev, |
27 | struct rockchip_softrst, |
28 | rcdev); |
29 | int bank, offset; |
30 | |
31 | if (softrst->lut) |
32 | id = softrst->lut[id]; |
33 | |
34 | bank = id / softrst->num_per_reg; |
35 | offset = id % softrst->num_per_reg; |
36 | |
37 | if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) { |
38 | writel(BIT(offset) | (BIT(offset) << 16), |
39 | addr: softrst->reg_base + (bank * 4)); |
40 | } else { |
41 | unsigned long flags; |
42 | u32 reg; |
43 | |
44 | spin_lock_irqsave(&softrst->lock, flags); |
45 | |
46 | reg = readl(addr: softrst->reg_base + (bank * 4)); |
47 | writel(val: reg | BIT(offset), addr: softrst->reg_base + (bank * 4)); |
48 | |
49 | spin_unlock_irqrestore(lock: &softrst->lock, flags); |
50 | } |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | static int rockchip_softrst_deassert(struct reset_controller_dev *rcdev, |
56 | unsigned long id) |
57 | { |
58 | struct rockchip_softrst *softrst = container_of(rcdev, |
59 | struct rockchip_softrst, |
60 | rcdev); |
61 | int bank, offset; |
62 | |
63 | if (softrst->lut) |
64 | id = softrst->lut[id]; |
65 | |
66 | bank = id / softrst->num_per_reg; |
67 | offset = id % softrst->num_per_reg; |
68 | |
69 | if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) { |
70 | writel(val: (BIT(offset) << 16), addr: softrst->reg_base + (bank * 4)); |
71 | } else { |
72 | unsigned long flags; |
73 | u32 reg; |
74 | |
75 | spin_lock_irqsave(&softrst->lock, flags); |
76 | |
77 | reg = readl(addr: softrst->reg_base + (bank * 4)); |
78 | writel(val: reg & ~BIT(offset), addr: softrst->reg_base + (bank * 4)); |
79 | |
80 | spin_unlock_irqrestore(lock: &softrst->lock, flags); |
81 | } |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static const struct reset_control_ops rockchip_softrst_ops = { |
87 | .assert = rockchip_softrst_assert, |
88 | .deassert = rockchip_softrst_deassert, |
89 | }; |
90 | |
91 | void rockchip_register_softrst_lut(struct device_node *np, |
92 | const int *lookup_table, |
93 | unsigned int num_regs, |
94 | void __iomem *base, u8 flags) |
95 | { |
96 | struct rockchip_softrst *softrst; |
97 | int ret; |
98 | |
99 | softrst = kzalloc(size: sizeof(*softrst), GFP_KERNEL); |
100 | if (!softrst) |
101 | return; |
102 | |
103 | spin_lock_init(&softrst->lock); |
104 | |
105 | softrst->reg_base = base; |
106 | softrst->lut = lookup_table; |
107 | softrst->flags = flags; |
108 | softrst->num_regs = num_regs; |
109 | softrst->num_per_reg = (flags & ROCKCHIP_SOFTRST_HIWORD_MASK) ? 16 |
110 | : 32; |
111 | |
112 | softrst->rcdev.owner = THIS_MODULE; |
113 | if (lookup_table) |
114 | softrst->rcdev.nr_resets = num_regs; |
115 | else |
116 | softrst->rcdev.nr_resets = num_regs * softrst->num_per_reg; |
117 | softrst->rcdev.ops = &rockchip_softrst_ops; |
118 | softrst->rcdev.of_node = np; |
119 | ret = reset_controller_register(rcdev: &softrst->rcdev); |
120 | if (ret) { |
121 | pr_err("%s: could not register reset controller, %d\n" , |
122 | __func__, ret); |
123 | kfree(objp: softrst); |
124 | } |
125 | }; |
126 | EXPORT_SYMBOL_GPL(rockchip_register_softrst_lut); |
127 | |