1 | // SPDX-License-Identifier: GPL-2.0-only OR MIT |
2 | /* |
3 | * Apple SART device driver |
4 | * Copyright (C) The Asahi Linux Contributors |
5 | * |
6 | * Apple SART is a simple address filter for some DMA transactions. |
7 | * Regions of physical memory must be added to the SART's allow |
8 | * list before any DMA can target these. Unlike a proper |
9 | * IOMMU no remapping can be done and special support in the |
10 | * consumer driver is required since not all DMA transactions of |
11 | * a single device are subject to SART filtering. |
12 | */ |
13 | |
14 | #include <linux/soc/apple/sart.h> |
15 | #include <linux/atomic.h> |
16 | #include <linux/bits.h> |
17 | #include <linux/bitfield.h> |
18 | #include <linux/device.h> |
19 | #include <linux/io.h> |
20 | #include <linux/module.h> |
21 | #include <linux/of.h> |
22 | #include <linux/of_platform.h> |
23 | #include <linux/platform_device.h> |
24 | #include <linux/types.h> |
25 | |
26 | #define APPLE_SART_MAX_ENTRIES 16 |
27 | |
28 | /* This is probably a bitfield but the exact meaning of each bit is unknown. */ |
29 | #define APPLE_SART_FLAGS_ALLOW 0xff |
30 | |
31 | /* SARTv2 registers */ |
32 | #define APPLE_SART2_CONFIG(idx) (0x00 + 4 * (idx)) |
33 | #define APPLE_SART2_CONFIG_FLAGS GENMASK(31, 24) |
34 | #define APPLE_SART2_CONFIG_SIZE GENMASK(23, 0) |
35 | #define APPLE_SART2_CONFIG_SIZE_SHIFT 12 |
36 | #define APPLE_SART2_CONFIG_SIZE_MAX GENMASK(23, 0) |
37 | |
38 | #define APPLE_SART2_PADDR(idx) (0x40 + 4 * (idx)) |
39 | #define APPLE_SART2_PADDR_SHIFT 12 |
40 | |
41 | /* SARTv3 registers */ |
42 | #define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx)) |
43 | |
44 | #define APPLE_SART3_PADDR(idx) (0x40 + 4 * (idx)) |
45 | #define APPLE_SART3_PADDR_SHIFT 12 |
46 | |
47 | #define APPLE_SART3_SIZE(idx) (0x80 + 4 * (idx)) |
48 | #define APPLE_SART3_SIZE_SHIFT 12 |
49 | #define APPLE_SART3_SIZE_MAX GENMASK(29, 0) |
50 | |
51 | struct apple_sart_ops { |
52 | void (*get_entry)(struct apple_sart *sart, int index, u8 *flags, |
53 | phys_addr_t *paddr, size_t *size); |
54 | void (*set_entry)(struct apple_sart *sart, int index, u8 flags, |
55 | phys_addr_t paddr_shifted, size_t size_shifted); |
56 | unsigned int size_shift; |
57 | unsigned int paddr_shift; |
58 | size_t size_max; |
59 | }; |
60 | |
61 | struct apple_sart { |
62 | struct device *dev; |
63 | void __iomem *regs; |
64 | |
65 | const struct apple_sart_ops *ops; |
66 | |
67 | unsigned long protected_entries; |
68 | unsigned long used_entries; |
69 | }; |
70 | |
71 | static void sart2_get_entry(struct apple_sart *sart, int index, u8 *flags, |
72 | phys_addr_t *paddr, size_t *size) |
73 | { |
74 | u32 cfg = readl(addr: sart->regs + APPLE_SART2_CONFIG(index)); |
75 | phys_addr_t paddr_ = readl(addr: sart->regs + APPLE_SART2_PADDR(index)); |
76 | size_t size_ = FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg); |
77 | |
78 | *flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg); |
79 | *size = size_ << APPLE_SART2_CONFIG_SIZE_SHIFT; |
80 | *paddr = paddr_ << APPLE_SART2_PADDR_SHIFT; |
81 | } |
82 | |
83 | static void sart2_set_entry(struct apple_sart *sart, int index, u8 flags, |
84 | phys_addr_t paddr_shifted, size_t size_shifted) |
85 | { |
86 | u32 cfg; |
87 | |
88 | cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags); |
89 | cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size_shifted); |
90 | |
91 | writel(val: paddr_shifted, addr: sart->regs + APPLE_SART2_PADDR(index)); |
92 | writel(val: cfg, addr: sart->regs + APPLE_SART2_CONFIG(index)); |
93 | } |
94 | |
95 | static struct apple_sart_ops sart_ops_v2 = { |
96 | .get_entry = sart2_get_entry, |
97 | .set_entry = sart2_set_entry, |
98 | .size_shift = APPLE_SART2_CONFIG_SIZE_SHIFT, |
99 | .paddr_shift = APPLE_SART2_PADDR_SHIFT, |
100 | .size_max = APPLE_SART2_CONFIG_SIZE_MAX, |
101 | }; |
102 | |
103 | static void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags, |
104 | phys_addr_t *paddr, size_t *size) |
105 | { |
106 | phys_addr_t paddr_ = readl(addr: sart->regs + APPLE_SART3_PADDR(index)); |
107 | size_t size_ = readl(addr: sart->regs + APPLE_SART3_SIZE(index)); |
108 | |
109 | *flags = readl(addr: sart->regs + APPLE_SART3_CONFIG(index)); |
110 | *size = size_ << APPLE_SART3_SIZE_SHIFT; |
111 | *paddr = paddr_ << APPLE_SART3_PADDR_SHIFT; |
112 | } |
113 | |
114 | static void sart3_set_entry(struct apple_sart *sart, int index, u8 flags, |
115 | phys_addr_t paddr_shifted, size_t size_shifted) |
116 | { |
117 | writel(val: paddr_shifted, addr: sart->regs + APPLE_SART3_PADDR(index)); |
118 | writel(val: size_shifted, addr: sart->regs + APPLE_SART3_SIZE(index)); |
119 | writel(val: flags, addr: sart->regs + APPLE_SART3_CONFIG(index)); |
120 | } |
121 | |
122 | static struct apple_sart_ops sart_ops_v3 = { |
123 | .get_entry = sart3_get_entry, |
124 | .set_entry = sart3_set_entry, |
125 | .size_shift = APPLE_SART3_SIZE_SHIFT, |
126 | .paddr_shift = APPLE_SART3_PADDR_SHIFT, |
127 | .size_max = APPLE_SART3_SIZE_MAX, |
128 | }; |
129 | |
130 | static int apple_sart_probe(struct platform_device *pdev) |
131 | { |
132 | int i; |
133 | struct apple_sart *sart; |
134 | struct device *dev = &pdev->dev; |
135 | |
136 | sart = devm_kzalloc(dev, size: sizeof(*sart), GFP_KERNEL); |
137 | if (!sart) |
138 | return -ENOMEM; |
139 | |
140 | sart->dev = dev; |
141 | sart->ops = of_device_get_match_data(dev); |
142 | |
143 | sart->regs = devm_platform_ioremap_resource(pdev, index: 0); |
144 | if (IS_ERR(ptr: sart->regs)) |
145 | return PTR_ERR(ptr: sart->regs); |
146 | |
147 | for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { |
148 | u8 flags; |
149 | size_t size; |
150 | phys_addr_t paddr; |
151 | |
152 | sart->ops->get_entry(sart, i, &flags, &paddr, &size); |
153 | |
154 | if (!flags) |
155 | continue; |
156 | |
157 | dev_dbg(sart->dev, |
158 | "SART bootloader entry: index %02d; flags: 0x%02x; paddr: %pa; size: 0x%zx\n" , |
159 | i, flags, &paddr, size); |
160 | set_bit(nr: i, addr: &sart->protected_entries); |
161 | } |
162 | |
163 | platform_set_drvdata(pdev, data: sart); |
164 | return 0; |
165 | } |
166 | |
167 | static void apple_sart_put_device(void *dev) |
168 | { |
169 | put_device(dev); |
170 | } |
171 | |
172 | struct apple_sart *devm_apple_sart_get(struct device *dev) |
173 | { |
174 | struct device_node *sart_node; |
175 | struct platform_device *sart_pdev; |
176 | struct apple_sart *sart; |
177 | int ret; |
178 | |
179 | sart_node = of_parse_phandle(np: dev->of_node, phandle_name: "apple,sart" , index: 0); |
180 | if (!sart_node) |
181 | return ERR_PTR(error: -ENODEV); |
182 | |
183 | sart_pdev = of_find_device_by_node(np: sart_node); |
184 | of_node_put(node: sart_node); |
185 | |
186 | if (!sart_pdev) |
187 | return ERR_PTR(error: -ENODEV); |
188 | |
189 | sart = dev_get_drvdata(dev: &sart_pdev->dev); |
190 | if (!sart) { |
191 | put_device(dev: &sart_pdev->dev); |
192 | return ERR_PTR(error: -EPROBE_DEFER); |
193 | } |
194 | |
195 | ret = devm_add_action_or_reset(dev, apple_sart_put_device, |
196 | &sart_pdev->dev); |
197 | if (ret) |
198 | return ERR_PTR(error: ret); |
199 | |
200 | device_link_add(consumer: dev, supplier: &sart_pdev->dev, |
201 | DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); |
202 | |
203 | return sart; |
204 | } |
205 | EXPORT_SYMBOL_GPL(devm_apple_sart_get); |
206 | |
207 | static int sart_set_entry(struct apple_sart *sart, int index, u8 flags, |
208 | phys_addr_t paddr, size_t size) |
209 | { |
210 | if (size & ((1 << sart->ops->size_shift) - 1)) |
211 | return -EINVAL; |
212 | if (paddr & ((1 << sart->ops->paddr_shift) - 1)) |
213 | return -EINVAL; |
214 | |
215 | paddr >>= sart->ops->size_shift; |
216 | size >>= sart->ops->paddr_shift; |
217 | |
218 | if (size > sart->ops->size_max) |
219 | return -EINVAL; |
220 | |
221 | sart->ops->set_entry(sart, index, flags, paddr, size); |
222 | return 0; |
223 | } |
224 | |
225 | int apple_sart_add_allowed_region(struct apple_sart *sart, phys_addr_t paddr, |
226 | size_t size) |
227 | { |
228 | int i, ret; |
229 | |
230 | for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { |
231 | if (test_bit(i, &sart->protected_entries)) |
232 | continue; |
233 | if (test_and_set_bit(nr: i, addr: &sart->used_entries)) |
234 | continue; |
235 | |
236 | ret = sart_set_entry(sart, index: i, APPLE_SART_FLAGS_ALLOW, paddr, |
237 | size); |
238 | if (ret) { |
239 | dev_dbg(sart->dev, |
240 | "unable to set entry %d to [%pa, 0x%zx]\n" , |
241 | i, &paddr, size); |
242 | clear_bit(nr: i, addr: &sart->used_entries); |
243 | return ret; |
244 | } |
245 | |
246 | dev_dbg(sart->dev, "wrote [%pa, 0x%zx] to %d\n" , &paddr, size, |
247 | i); |
248 | return 0; |
249 | } |
250 | |
251 | dev_warn(sart->dev, |
252 | "no free entries left to add [paddr: 0x%pa, size: 0x%zx]\n" , |
253 | &paddr, size); |
254 | |
255 | return -EBUSY; |
256 | } |
257 | EXPORT_SYMBOL_GPL(apple_sart_add_allowed_region); |
258 | |
259 | int apple_sart_remove_allowed_region(struct apple_sart *sart, phys_addr_t paddr, |
260 | size_t size) |
261 | { |
262 | int i; |
263 | |
264 | dev_dbg(sart->dev, |
265 | "will remove [paddr: %pa, size: 0x%zx] from allowed regions\n" , |
266 | &paddr, size); |
267 | |
268 | for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { |
269 | u8 eflags; |
270 | size_t esize; |
271 | phys_addr_t epaddr; |
272 | |
273 | if (test_bit(i, &sart->protected_entries)) |
274 | continue; |
275 | |
276 | sart->ops->get_entry(sart, i, &eflags, &epaddr, &esize); |
277 | |
278 | if (epaddr != paddr || esize != size) |
279 | continue; |
280 | |
281 | sart->ops->set_entry(sart, i, 0, 0, 0); |
282 | |
283 | clear_bit(nr: i, addr: &sart->used_entries); |
284 | dev_dbg(sart->dev, "cleared entry %d\n" , i); |
285 | return 0; |
286 | } |
287 | |
288 | dev_warn(sart->dev, "entry [paddr: 0x%pa, size: 0x%zx] not found\n" , |
289 | &paddr, size); |
290 | |
291 | return -EINVAL; |
292 | } |
293 | EXPORT_SYMBOL_GPL(apple_sart_remove_allowed_region); |
294 | |
295 | static void apple_sart_shutdown(struct platform_device *pdev) |
296 | { |
297 | struct apple_sart *sart = dev_get_drvdata(dev: &pdev->dev); |
298 | int i; |
299 | |
300 | for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { |
301 | if (test_bit(i, &sart->protected_entries)) |
302 | continue; |
303 | |
304 | sart->ops->set_entry(sart, i, 0, 0, 0); |
305 | } |
306 | } |
307 | |
308 | static const struct of_device_id apple_sart_of_match[] = { |
309 | { |
310 | .compatible = "apple,t6000-sart" , |
311 | .data = &sart_ops_v3, |
312 | }, |
313 | { |
314 | .compatible = "apple,t8103-sart" , |
315 | .data = &sart_ops_v2, |
316 | }, |
317 | {} |
318 | }; |
319 | MODULE_DEVICE_TABLE(of, apple_sart_of_match); |
320 | |
321 | static struct platform_driver apple_sart_driver = { |
322 | .driver = { |
323 | .name = "apple-sart" , |
324 | .of_match_table = apple_sart_of_match, |
325 | }, |
326 | .probe = apple_sart_probe, |
327 | .shutdown = apple_sart_shutdown, |
328 | }; |
329 | module_platform_driver(apple_sart_driver); |
330 | |
331 | MODULE_LICENSE("Dual MIT/GPL" ); |
332 | MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>" ); |
333 | MODULE_DESCRIPTION("Apple SART driver" ); |
334 | |