1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC |
4 | * |
5 | * Authors: |
6 | * Serge Semin <Sergey.Semin@baikalelectronics.ru> |
7 | * |
8 | * Baikal-T1 CCU Resets interface driver |
9 | */ |
10 | |
11 | #define pr_fmt(fmt) "bt1-ccu-rst: " fmt |
12 | |
13 | #include <linux/bits.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/of.h> |
17 | #include <linux/printk.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/reset-controller.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include <dt-bindings/reset/bt1-ccu.h> |
23 | |
24 | #include "ccu-rst.h" |
25 | |
26 | #define CCU_AXI_MAIN_BASE 0x030 |
27 | #define CCU_AXI_DDR_BASE 0x034 |
28 | #define CCU_AXI_SATA_BASE 0x038 |
29 | #define CCU_AXI_GMAC0_BASE 0x03C |
30 | #define CCU_AXI_GMAC1_BASE 0x040 |
31 | #define CCU_AXI_XGMAC_BASE 0x044 |
32 | #define CCU_AXI_PCIE_M_BASE 0x048 |
33 | #define CCU_AXI_PCIE_S_BASE 0x04C |
34 | #define CCU_AXI_USB_BASE 0x050 |
35 | #define CCU_AXI_HWA_BASE 0x054 |
36 | #define CCU_AXI_SRAM_BASE 0x058 |
37 | |
38 | #define CCU_SYS_DDR_BASE 0x02c |
39 | #define CCU_SYS_SATA_REF_BASE 0x060 |
40 | #define CCU_SYS_APB_BASE 0x064 |
41 | #define CCU_SYS_PCIE_BASE 0x144 |
42 | |
43 | #define CCU_RST_DELAY_US 1 |
44 | |
45 | #define CCU_RST_TRIG(_base, _ofs) \ |
46 | { \ |
47 | .type = CCU_RST_TRIG, \ |
48 | .base = _base, \ |
49 | .mask = BIT(_ofs), \ |
50 | } |
51 | |
52 | #define CCU_RST_DIR(_base, _ofs) \ |
53 | { \ |
54 | .type = CCU_RST_DIR, \ |
55 | .base = _base, \ |
56 | .mask = BIT(_ofs), \ |
57 | } |
58 | |
59 | struct ccu_rst_info { |
60 | enum ccu_rst_type type; |
61 | unsigned int base; |
62 | unsigned int mask; |
63 | }; |
64 | |
65 | /* |
66 | * Each AXI-bus clock divider is equipped with the corresponding clock-consumer |
67 | * domain reset (it's self-deasserted reset control). |
68 | */ |
69 | static const struct ccu_rst_info axi_rst_info[] = { |
70 | [CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1), |
71 | [CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1), |
72 | [CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1), |
73 | [CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1), |
74 | [CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1), |
75 | [CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1), |
76 | [CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1), |
77 | [CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1), |
78 | [CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1), |
79 | [CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1), |
80 | [CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1), |
81 | }; |
82 | |
83 | /* |
84 | * SATA reference clock domain and APB-bus domain are connected with the |
85 | * sefl-deasserted reset control, which can be activated via the corresponding |
86 | * clock divider register. DDR and PCIe sub-domains can be reset with directly |
87 | * controlled reset signals. Resetting the DDR controller though won't end up |
88 | * well while the Linux kernel is working. |
89 | */ |
90 | static const struct ccu_rst_info sys_rst_info[] = { |
91 | [CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1), |
92 | [CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1), |
93 | [CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1), |
94 | [CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2), |
95 | [CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0), |
96 | [CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4), |
97 | [CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8), |
98 | [CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9), |
99 | [CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10), |
100 | [CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11), |
101 | [CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12), |
102 | }; |
103 | |
104 | static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx) |
105 | { |
106 | struct ccu_rst *rst = to_ccu_rst(rcdev); |
107 | const struct ccu_rst_info *info = &rst->rsts_info[idx]; |
108 | |
109 | if (info->type != CCU_RST_TRIG) |
110 | return -EOPNOTSUPP; |
111 | |
112 | regmap_update_bits(map: rst->sys_regs, reg: info->base, mask: info->mask, val: info->mask); |
113 | |
114 | /* The next delay must be enough to cover all the resets. */ |
115 | udelay(CCU_RST_DELAY_US); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static int ccu_rst_set(struct reset_controller_dev *rcdev, |
121 | unsigned long idx, bool high) |
122 | { |
123 | struct ccu_rst *rst = to_ccu_rst(rcdev); |
124 | const struct ccu_rst_info *info = &rst->rsts_info[idx]; |
125 | |
126 | if (info->type != CCU_RST_DIR) |
127 | return high ? -EOPNOTSUPP : 0; |
128 | |
129 | return regmap_update_bits(map: rst->sys_regs, reg: info->base, |
130 | mask: info->mask, val: high ? info->mask : 0); |
131 | } |
132 | |
133 | static int ccu_rst_assert(struct reset_controller_dev *rcdev, |
134 | unsigned long idx) |
135 | { |
136 | return ccu_rst_set(rcdev, idx, high: true); |
137 | } |
138 | |
139 | static int ccu_rst_deassert(struct reset_controller_dev *rcdev, |
140 | unsigned long idx) |
141 | { |
142 | return ccu_rst_set(rcdev, idx, high: false); |
143 | } |
144 | |
145 | static int ccu_rst_status(struct reset_controller_dev *rcdev, |
146 | unsigned long idx) |
147 | { |
148 | struct ccu_rst *rst = to_ccu_rst(rcdev); |
149 | const struct ccu_rst_info *info = &rst->rsts_info[idx]; |
150 | u32 val; |
151 | |
152 | if (info->type != CCU_RST_DIR) |
153 | return -EOPNOTSUPP; |
154 | |
155 | regmap_read(map: rst->sys_regs, reg: info->base, val: &val); |
156 | |
157 | return !!(val & info->mask); |
158 | } |
159 | |
160 | static const struct reset_control_ops ccu_rst_ops = { |
161 | .reset = ccu_rst_reset, |
162 | .assert = ccu_rst_assert, |
163 | .deassert = ccu_rst_deassert, |
164 | .status = ccu_rst_status, |
165 | }; |
166 | |
167 | struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init) |
168 | { |
169 | struct ccu_rst *rst; |
170 | int ret; |
171 | |
172 | if (!rst_init) |
173 | return ERR_PTR(error: -EINVAL); |
174 | |
175 | rst = kzalloc(size: sizeof(*rst), GFP_KERNEL); |
176 | if (!rst) |
177 | return ERR_PTR(error: -ENOMEM); |
178 | |
179 | rst->sys_regs = rst_init->sys_regs; |
180 | if (of_device_is_compatible(device: rst_init->np, "baikal,bt1-ccu-axi" )) { |
181 | rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info); |
182 | rst->rsts_info = axi_rst_info; |
183 | } else if (of_device_is_compatible(device: rst_init->np, "baikal,bt1-ccu-sys" )) { |
184 | rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info); |
185 | rst->rsts_info = sys_rst_info; |
186 | } else { |
187 | pr_err("Incompatible DT node '%s' specified\n" , |
188 | of_node_full_name(rst_init->np)); |
189 | ret = -EINVAL; |
190 | goto err_kfree_rst; |
191 | } |
192 | |
193 | rst->rcdev.owner = THIS_MODULE; |
194 | rst->rcdev.ops = &ccu_rst_ops; |
195 | rst->rcdev.of_node = rst_init->np; |
196 | |
197 | ret = reset_controller_register(rcdev: &rst->rcdev); |
198 | if (ret) { |
199 | pr_err("Couldn't register '%s' reset controller\n" , |
200 | of_node_full_name(rst_init->np)); |
201 | goto err_kfree_rst; |
202 | } |
203 | |
204 | return rst; |
205 | |
206 | err_kfree_rst: |
207 | kfree(objp: rst); |
208 | |
209 | return ERR_PTR(error: ret); |
210 | } |
211 | |
212 | void ccu_rst_hw_unregister(struct ccu_rst *rst) |
213 | { |
214 | reset_controller_unregister(rcdev: &rst->rcdev); |
215 | |
216 | kfree(objp: rst); |
217 | } |
218 | |