146d1fb07SSven Peter // SPDX-License-Identifier: GPL-2.0-only
246d1fb07SSven Peter /*
346d1fb07SSven Peter * Apple DART (Device Address Resolution Table) IOMMU driver
446d1fb07SSven Peter *
546d1fb07SSven Peter * Copyright (C) 2021 The Asahi Linux Contributors
646d1fb07SSven Peter *
746d1fb07SSven Peter * Based on arm/arm-smmu/arm-ssmu.c and arm/arm-smmu-v3/arm-smmu-v3.c
846d1fb07SSven Peter * Copyright (C) 2013 ARM Limited
946d1fb07SSven Peter * Copyright (C) 2015 ARM Limited
1046d1fb07SSven Peter * and on exynos-iommu.c
1146d1fb07SSven Peter * Copyright (c) 2011,2016 Samsung Electronics Co., Ltd.
1246d1fb07SSven Peter */
1346d1fb07SSven Peter
1446d1fb07SSven Peter #include <linux/atomic.h>
1546d1fb07SSven Peter #include <linux/bitfield.h>
1646d1fb07SSven Peter #include <linux/clk.h>
1746d1fb07SSven Peter #include <linux/dev_printk.h>
1846d1fb07SSven Peter #include <linux/dma-mapping.h>
1946d1fb07SSven Peter #include <linux/err.h>
2046d1fb07SSven Peter #include <linux/interrupt.h>
2146d1fb07SSven Peter #include <linux/io-pgtable.h>
2246d1fb07SSven Peter #include <linux/iommu.h>
2346d1fb07SSven Peter #include <linux/iopoll.h>
2446d1fb07SSven Peter #include <linux/module.h>
2546d1fb07SSven Peter #include <linux/of.h>
2646d1fb07SSven Peter #include <linux/of_address.h>
2746d1fb07SSven Peter #include <linux/of_iommu.h>
2846d1fb07SSven Peter #include <linux/of_platform.h>
2946d1fb07SSven Peter #include <linux/pci.h>
3046d1fb07SSven Peter #include <linux/platform_device.h>
3146d1fb07SSven Peter #include <linux/slab.h>
3246d1fb07SSven Peter #include <linux/swab.h>
3346d1fb07SSven Peter #include <linux/types.h>
3446d1fb07SSven Peter
35f2042ed2SRobin Murphy #include "dma-iommu.h"
36f2042ed2SRobin Murphy
37510d4072SHector Martin #define DART_MAX_STREAMS 256
3846d1fb07SSven Peter #define DART_MAX_TTBR 4
3946d1fb07SSven Peter #define MAX_DARTS_PER_DEVICE 2
4046d1fb07SSven Peter
41b76c68fcSHector Martin /* Common registers */
4246d1fb07SSven Peter
4346d1fb07SSven Peter #define DART_PARAMS1 0x00
44a772a02cSHector Martin #define DART_PARAMS1_PAGE_SHIFT GENMASK(27, 24)
4546d1fb07SSven Peter
4646d1fb07SSven Peter #define DART_PARAMS2 0x04
47a772a02cSHector Martin #define DART_PARAMS2_BYPASS_SUPPORT BIT(0)
4846d1fb07SSven Peter
49b76c68fcSHector Martin /* T8020/T6000 registers */
5046d1fb07SSven Peter
51b76c68fcSHector Martin #define DART_T8020_STREAM_COMMAND 0x20
52b76c68fcSHector Martin #define DART_T8020_STREAM_COMMAND_BUSY BIT(2)
53b76c68fcSHector Martin #define DART_T8020_STREAM_COMMAND_INVALIDATE BIT(20)
5446d1fb07SSven Peter
55b76c68fcSHector Martin #define DART_T8020_STREAM_SELECT 0x34
5646d1fb07SSven Peter
57b76c68fcSHector Martin #define DART_T8020_ERROR 0x40
58b76c68fcSHector Martin #define DART_T8020_ERROR_STREAM GENMASK(27, 24)
59b76c68fcSHector Martin #define DART_T8020_ERROR_CODE GENMASK(11, 0)
60b76c68fcSHector Martin #define DART_T8020_ERROR_FLAG BIT(31)
6146d1fb07SSven Peter
62b76c68fcSHector Martin #define DART_T8020_ERROR_READ_FAULT BIT(4)
63b76c68fcSHector Martin #define DART_T8020_ERROR_WRITE_FAULT BIT(3)
64b76c68fcSHector Martin #define DART_T8020_ERROR_NO_PTE BIT(2)
65b76c68fcSHector Martin #define DART_T8020_ERROR_NO_PMD BIT(1)
66b76c68fcSHector Martin #define DART_T8020_ERROR_NO_TTBR BIT(0)
67b76c68fcSHector Martin
68b76c68fcSHector Martin #define DART_T8020_CONFIG 0x60
69b76c68fcSHector Martin #define DART_T8020_CONFIG_LOCK BIT(15)
7046d1fb07SSven Peter
7146d1fb07SSven Peter #define DART_STREAM_COMMAND_BUSY_TIMEOUT 100
7246d1fb07SSven Peter
73b76c68fcSHector Martin #define DART_T8020_ERROR_ADDR_HI 0x54
74b76c68fcSHector Martin #define DART_T8020_ERROR_ADDR_LO 0x50
7546d1fb07SSven Peter
76b76c68fcSHector Martin #define DART_T8020_STREAMS_ENABLE 0xfc
775a009fc1SSven Peter
78b76c68fcSHector Martin #define DART_T8020_TCR 0x100
79b76c68fcSHector Martin #define DART_T8020_TCR_TRANSLATE_ENABLE BIT(7)
80b76c68fcSHector Martin #define DART_T8020_TCR_BYPASS_DART BIT(8)
81b76c68fcSHector Martin #define DART_T8020_TCR_BYPASS_DAPF BIT(12)
8246d1fb07SSven Peter
83b76c68fcSHector Martin #define DART_T8020_TTBR 0x200
84b76c68fcSHector Martin #define DART_T8020_TTBR_VALID BIT(31)
85b76c68fcSHector Martin #define DART_T8020_TTBR_ADDR_FIELD_SHIFT 0
86b76c68fcSHector Martin #define DART_T8020_TTBR_SHIFT 12
8746d1fb07SSven Peter
88d8bcc870SHector Martin /* T8110 registers */
89d8bcc870SHector Martin
90d8bcc870SHector Martin #define DART_T8110_PARAMS3 0x08
91d8bcc870SHector Martin #define DART_T8110_PARAMS3_PA_WIDTH GENMASK(29, 24)
92d8bcc870SHector Martin #define DART_T8110_PARAMS3_VA_WIDTH GENMASK(21, 16)
93d8bcc870SHector Martin #define DART_T8110_PARAMS3_VER_MAJ GENMASK(15, 8)
94d8bcc870SHector Martin #define DART_T8110_PARAMS3_VER_MIN GENMASK(7, 0)
95d8bcc870SHector Martin
96d8bcc870SHector Martin #define DART_T8110_PARAMS4 0x0c
97d8bcc870SHector Martin #define DART_T8110_PARAMS4_NUM_CLIENTS GENMASK(24, 16)
98d8bcc870SHector Martin #define DART_T8110_PARAMS4_NUM_SIDS GENMASK(8, 0)
99d8bcc870SHector Martin
100d8bcc870SHector Martin #define DART_T8110_TLB_CMD 0x80
101d8bcc870SHector Martin #define DART_T8110_TLB_CMD_BUSY BIT(31)
102d8bcc870SHector Martin #define DART_T8110_TLB_CMD_OP GENMASK(10, 8)
103d8bcc870SHector Martin #define DART_T8110_TLB_CMD_OP_FLUSH_ALL 0
104d8bcc870SHector Martin #define DART_T8110_TLB_CMD_OP_FLUSH_SID 1
105d8bcc870SHector Martin #define DART_T8110_TLB_CMD_STREAM GENMASK(7, 0)
106d8bcc870SHector Martin
107d8bcc870SHector Martin #define DART_T8110_ERROR 0x100
108d8bcc870SHector Martin #define DART_T8110_ERROR_STREAM GENMASK(27, 20)
109d8bcc870SHector Martin #define DART_T8110_ERROR_CODE GENMASK(14, 0)
110d8bcc870SHector Martin #define DART_T8110_ERROR_FLAG BIT(31)
111d8bcc870SHector Martin
112d8bcc870SHector Martin #define DART_T8110_ERROR_MASK 0x104
113d8bcc870SHector Martin
1149e6a1825SEric Curtin #define DART_T8110_ERROR_READ_FAULT BIT(5)
1159e6a1825SEric Curtin #define DART_T8110_ERROR_WRITE_FAULT BIT(4)
116d8bcc870SHector Martin #define DART_T8110_ERROR_NO_PTE BIT(3)
117d8bcc870SHector Martin #define DART_T8110_ERROR_NO_PMD BIT(2)
118d8bcc870SHector Martin #define DART_T8110_ERROR_NO_PGD BIT(1)
119d8bcc870SHector Martin #define DART_T8110_ERROR_NO_TTBR BIT(0)
120d8bcc870SHector Martin
121d8bcc870SHector Martin #define DART_T8110_ERROR_ADDR_LO 0x170
122d8bcc870SHector Martin #define DART_T8110_ERROR_ADDR_HI 0x174
123d8bcc870SHector Martin
124d8bcc870SHector Martin #define DART_T8110_PROTECT 0x200
125d8bcc870SHector Martin #define DART_T8110_UNPROTECT 0x204
126d8bcc870SHector Martin #define DART_T8110_PROTECT_LOCK 0x208
127d8bcc870SHector Martin #define DART_T8110_PROTECT_TTBR_TCR BIT(0)
128d8bcc870SHector Martin
129d8bcc870SHector Martin #define DART_T8110_ENABLE_STREAMS 0xc00
130d8bcc870SHector Martin #define DART_T8110_DISABLE_STREAMS 0xc20
131d8bcc870SHector Martin
132d8bcc870SHector Martin #define DART_T8110_TCR 0x1000
133d8bcc870SHector Martin #define DART_T8110_TCR_REMAP GENMASK(11, 8)
134d8bcc870SHector Martin #define DART_T8110_TCR_REMAP_EN BIT(7)
135d8bcc870SHector Martin #define DART_T8110_TCR_BYPASS_DAPF BIT(2)
136d8bcc870SHector Martin #define DART_T8110_TCR_BYPASS_DART BIT(1)
137d8bcc870SHector Martin #define DART_T8110_TCR_TRANSLATE_ENABLE BIT(0)
138d8bcc870SHector Martin
139d8bcc870SHector Martin #define DART_T8110_TTBR 0x1400
140d8bcc870SHector Martin #define DART_T8110_TTBR_VALID BIT(0)
141d8bcc870SHector Martin #define DART_T8110_TTBR_ADDR_FIELD_SHIFT 2
142d8bcc870SHector Martin #define DART_T8110_TTBR_SHIFT 14
143d8bcc870SHector Martin
144b76c68fcSHector Martin #define DART_TCR(dart, sid) ((dart)->hw->tcr + ((sid) << 2))
145b76c68fcSHector Martin
146b76c68fcSHector Martin #define DART_TTBR(dart, sid, idx) ((dart)->hw->ttbr + \
1470b459bcdSHector Martin (((dart)->hw->ttbr_count * (sid)) << 2) + \
1480b459bcdSHector Martin ((idx) << 2))
1490b459bcdSHector Martin
150b76c68fcSHector Martin struct apple_dart_stream_map;
1510b459bcdSHector Martin
152d8bcc870SHector Martin enum dart_type {
153d8bcc870SHector Martin DART_T8020,
154d8bcc870SHector Martin DART_T6000,
155d8bcc870SHector Martin DART_T8110,
156d8bcc870SHector Martin };
15746d1fb07SSven Peter
158a380b8dcSSven Peter struct apple_dart_hw {
159d8bcc870SHector Martin enum dart_type type;
160b76c68fcSHector Martin irqreturn_t (*irq_handler)(int irq, void *dev);
161b76c68fcSHector Martin int (*invalidate_tlb)(struct apple_dart_stream_map *stream_map);
162b76c68fcSHector Martin
163a380b8dcSSven Peter u32 oas;
164a380b8dcSSven Peter enum io_pgtable_fmt fmt;
165510d4072SHector Martin
166510d4072SHector Martin int max_sid_count;
1670b459bcdSHector Martin
168b76c68fcSHector Martin u64 lock;
169b76c68fcSHector Martin u64 lock_bit;
170b76c68fcSHector Martin
171b76c68fcSHector Martin u64 error;
172b76c68fcSHector Martin
173b76c68fcSHector Martin u64 enable_streams;
174b76c68fcSHector Martin
175b76c68fcSHector Martin u64 tcr;
176b76c68fcSHector Martin u64 tcr_enabled;
177b76c68fcSHector Martin u64 tcr_disabled;
178b76c68fcSHector Martin u64 tcr_bypass;
179b76c68fcSHector Martin
180b76c68fcSHector Martin u64 ttbr;
181b76c68fcSHector Martin u64 ttbr_valid;
182b76c68fcSHector Martin u64 ttbr_addr_field_shift;
183b76c68fcSHector Martin u64 ttbr_shift;
1840b459bcdSHector Martin int ttbr_count;
185a380b8dcSSven Peter };
186a380b8dcSSven Peter
18746d1fb07SSven Peter /*
18846d1fb07SSven Peter * Private structure associated with each DART device.
18946d1fb07SSven Peter *
19046d1fb07SSven Peter * @dev: device struct
191a380b8dcSSven Peter * @hw: SoC-specific hardware data
19246d1fb07SSven Peter * @regs: mapped MMIO region
19346d1fb07SSven Peter * @irq: interrupt number, can be shared with other DARTs
19446d1fb07SSven Peter * @clks: clocks associated with this DART
19546d1fb07SSven Peter * @num_clks: number of @clks
19646d1fb07SSven Peter * @lock: lock for hardware operations involving this dart
19746d1fb07SSven Peter * @pgsize: pagesize supported by this DART
19846d1fb07SSven Peter * @supports_bypass: indicates if this DART supports bypass mode
19946d1fb07SSven Peter * @force_bypass: force bypass mode due to pagesize mismatch?
20046d1fb07SSven Peter * @sid2group: maps stream ids to iommu_groups
20146d1fb07SSven Peter * @iommu: iommu core device
20246d1fb07SSven Peter */
20346d1fb07SSven Peter struct apple_dart {
20446d1fb07SSven Peter struct device *dev;
205a380b8dcSSven Peter const struct apple_dart_hw *hw;
20646d1fb07SSven Peter
20746d1fb07SSven Peter void __iomem *regs;
20846d1fb07SSven Peter
20946d1fb07SSven Peter int irq;
21046d1fb07SSven Peter struct clk_bulk_data *clks;
21146d1fb07SSven Peter int num_clks;
21246d1fb07SSven Peter
21346d1fb07SSven Peter spinlock_t lock;
21446d1fb07SSven Peter
215d8bcc870SHector Martin u32 ias;
216d8bcc870SHector Martin u32 oas;
21746d1fb07SSven Peter u32 pgsize;
218510d4072SHector Martin u32 num_streams;
21946d1fb07SSven Peter u32 supports_bypass : 1;
22046d1fb07SSven Peter u32 force_bypass : 1;
22146d1fb07SSven Peter
22246d1fb07SSven Peter struct iommu_group *sid2group[DART_MAX_STREAMS];
22346d1fb07SSven Peter struct iommu_device iommu;
2243d68bbb8SHector Martin
2253d68bbb8SHector Martin u32 save_tcr[DART_MAX_STREAMS];
2263d68bbb8SHector Martin u32 save_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
22746d1fb07SSven Peter };
22846d1fb07SSven Peter
22946d1fb07SSven Peter /*
23046d1fb07SSven Peter * Convenience struct to identify streams.
23146d1fb07SSven Peter *
23246d1fb07SSven Peter * The normal variant is used inside apple_dart_master_cfg which isn't written
23346d1fb07SSven Peter * to concurrently.
23446d1fb07SSven Peter * The atomic variant is used inside apple_dart_domain where we have to guard
23546d1fb07SSven Peter * against races from potential parallel calls to attach/detach_device.
23646d1fb07SSven Peter * Note that even inside the atomic variant the apple_dart pointer is not
23746d1fb07SSven Peter * protected: This pointer is initialized once under the domain init mutex
23846d1fb07SSven Peter * and never changed again afterwards. Devices with different dart pointers
23946d1fb07SSven Peter * cannot be attached to the same domain.
24046d1fb07SSven Peter *
24146d1fb07SSven Peter * @dart dart pointer
24246d1fb07SSven Peter * @sid stream id bitmap
24346d1fb07SSven Peter */
24446d1fb07SSven Peter struct apple_dart_stream_map {
24546d1fb07SSven Peter struct apple_dart *dart;
246510d4072SHector Martin DECLARE_BITMAP(sidmap, DART_MAX_STREAMS);
24746d1fb07SSven Peter };
24846d1fb07SSven Peter struct apple_dart_atomic_stream_map {
24946d1fb07SSven Peter struct apple_dart *dart;
250510d4072SHector Martin atomic_long_t sidmap[BITS_TO_LONGS(DART_MAX_STREAMS)];
25146d1fb07SSven Peter };
25246d1fb07SSven Peter
25346d1fb07SSven Peter /*
25446d1fb07SSven Peter * This structure is attached to each iommu domain handled by a DART.
25546d1fb07SSven Peter *
25646d1fb07SSven Peter * @pgtbl_ops: pagetable ops allocated by io-pgtable
25746d1fb07SSven Peter * @finalized: true if the domain has been completely initialized
25846d1fb07SSven Peter * @init_lock: protects domain initialization
25946d1fb07SSven Peter * @stream_maps: streams attached to this domain (valid for DMA/UNMANAGED only)
26046d1fb07SSven Peter * @domain: core iommu domain pointer
26146d1fb07SSven Peter */
26246d1fb07SSven Peter struct apple_dart_domain {
26346d1fb07SSven Peter struct io_pgtable_ops *pgtbl_ops;
26446d1fb07SSven Peter
26546d1fb07SSven Peter bool finalized;
26646d1fb07SSven Peter struct mutex init_lock;
26746d1fb07SSven Peter struct apple_dart_atomic_stream_map stream_maps[MAX_DARTS_PER_DEVICE];
26846d1fb07SSven Peter
26946d1fb07SSven Peter struct iommu_domain domain;
27046d1fb07SSven Peter };
27146d1fb07SSven Peter
27246d1fb07SSven Peter /*
27346d1fb07SSven Peter * This structure is attached to devices with dev_iommu_priv_set() on of_xlate
27446d1fb07SSven Peter * and contains a list of streams bound to this device.
27546d1fb07SSven Peter * So far the worst case seen is a single device with two streams
27646d1fb07SSven Peter * from different darts, such that this simple static array is enough.
27746d1fb07SSven Peter *
27846d1fb07SSven Peter * @streams: streams for this device
27946d1fb07SSven Peter */
28046d1fb07SSven Peter struct apple_dart_master_cfg {
28146d1fb07SSven Peter struct apple_dart_stream_map stream_maps[MAX_DARTS_PER_DEVICE];
28246d1fb07SSven Peter };
28346d1fb07SSven Peter
28446d1fb07SSven Peter /*
28546d1fb07SSven Peter * Helper macro to iterate over apple_dart_master_cfg.stream_maps and
28646d1fb07SSven Peter * apple_dart_domain.stream_maps
28746d1fb07SSven Peter *
28846d1fb07SSven Peter * @i int used as loop variable
28946d1fb07SSven Peter * @base pointer to base struct (apple_dart_master_cfg or apple_dart_domain)
29046d1fb07SSven Peter * @stream pointer to the apple_dart_streams struct for each loop iteration
29146d1fb07SSven Peter */
29246d1fb07SSven Peter #define for_each_stream_map(i, base, stream_map) \
29346d1fb07SSven Peter for (i = 0, stream_map = &(base)->stream_maps[0]; \
29446d1fb07SSven Peter i < MAX_DARTS_PER_DEVICE && stream_map->dart; \
29546d1fb07SSven Peter stream_map = &(base)->stream_maps[++i])
29646d1fb07SSven Peter
29746d1fb07SSven Peter static struct platform_driver apple_dart_driver;
29846d1fb07SSven Peter static const struct iommu_ops apple_dart_iommu_ops;
29946d1fb07SSven Peter
to_dart_domain(struct iommu_domain * dom)30046d1fb07SSven Peter static struct apple_dart_domain *to_dart_domain(struct iommu_domain *dom)
30146d1fb07SSven Peter {
30246d1fb07SSven Peter return container_of(dom, struct apple_dart_domain, domain);
30346d1fb07SSven Peter }
30446d1fb07SSven Peter
30546d1fb07SSven Peter static void
apple_dart_hw_enable_translation(struct apple_dart_stream_map * stream_map)30646d1fb07SSven Peter apple_dart_hw_enable_translation(struct apple_dart_stream_map *stream_map)
30746d1fb07SSven Peter {
308510d4072SHector Martin struct apple_dart *dart = stream_map->dart;
30946d1fb07SSven Peter int sid;
31046d1fb07SSven Peter
311510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, dart->num_streams)
312b76c68fcSHector Martin writel(dart->hw->tcr_enabled, dart->regs + DART_TCR(dart, sid));
31346d1fb07SSven Peter }
31446d1fb07SSven Peter
apple_dart_hw_disable_dma(struct apple_dart_stream_map * stream_map)31546d1fb07SSven Peter static void apple_dart_hw_disable_dma(struct apple_dart_stream_map *stream_map)
31646d1fb07SSven Peter {
317510d4072SHector Martin struct apple_dart *dart = stream_map->dart;
31846d1fb07SSven Peter int sid;
31946d1fb07SSven Peter
320510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, dart->num_streams)
321b76c68fcSHector Martin writel(dart->hw->tcr_disabled, dart->regs + DART_TCR(dart, sid));
32246d1fb07SSven Peter }
32346d1fb07SSven Peter
32446d1fb07SSven Peter static void
apple_dart_hw_enable_bypass(struct apple_dart_stream_map * stream_map)32546d1fb07SSven Peter apple_dart_hw_enable_bypass(struct apple_dart_stream_map *stream_map)
32646d1fb07SSven Peter {
327510d4072SHector Martin struct apple_dart *dart = stream_map->dart;
32846d1fb07SSven Peter int sid;
32946d1fb07SSven Peter
33046d1fb07SSven Peter WARN_ON(!stream_map->dart->supports_bypass);
331510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, dart->num_streams)
332b76c68fcSHector Martin writel(dart->hw->tcr_bypass,
333b76c68fcSHector Martin dart->regs + DART_TCR(dart, sid));
33446d1fb07SSven Peter }
33546d1fb07SSven Peter
apple_dart_hw_set_ttbr(struct apple_dart_stream_map * stream_map,u8 idx,phys_addr_t paddr)33646d1fb07SSven Peter static void apple_dart_hw_set_ttbr(struct apple_dart_stream_map *stream_map,
33746d1fb07SSven Peter u8 idx, phys_addr_t paddr)
33846d1fb07SSven Peter {
339510d4072SHector Martin struct apple_dart *dart = stream_map->dart;
34046d1fb07SSven Peter int sid;
34146d1fb07SSven Peter
342b76c68fcSHector Martin WARN_ON(paddr & ((1 << dart->hw->ttbr_shift) - 1));
343510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, dart->num_streams)
344b76c68fcSHector Martin writel(dart->hw->ttbr_valid |
345b76c68fcSHector Martin (paddr >> dart->hw->ttbr_shift) << dart->hw->ttbr_addr_field_shift,
3460b459bcdSHector Martin dart->regs + DART_TTBR(dart, sid, idx));
34746d1fb07SSven Peter }
34846d1fb07SSven Peter
apple_dart_hw_clear_ttbr(struct apple_dart_stream_map * stream_map,u8 idx)34946d1fb07SSven Peter static void apple_dart_hw_clear_ttbr(struct apple_dart_stream_map *stream_map,
35046d1fb07SSven Peter u8 idx)
35146d1fb07SSven Peter {
352510d4072SHector Martin struct apple_dart *dart = stream_map->dart;
35346d1fb07SSven Peter int sid;
35446d1fb07SSven Peter
355510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, dart->num_streams)
3560b459bcdSHector Martin writel(0, dart->regs + DART_TTBR(dart, sid, idx));
35746d1fb07SSven Peter }
35846d1fb07SSven Peter
35946d1fb07SSven Peter static void
apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map * stream_map)36046d1fb07SSven Peter apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map)
36146d1fb07SSven Peter {
36246d1fb07SSven Peter int i;
36346d1fb07SSven Peter
3640b459bcdSHector Martin for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
36546d1fb07SSven Peter apple_dart_hw_clear_ttbr(stream_map, i);
36646d1fb07SSven Peter }
36746d1fb07SSven Peter
36846d1fb07SSven Peter static int
apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map * stream_map,u32 command)369b76c68fcSHector Martin apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map *stream_map,
37046d1fb07SSven Peter u32 command)
37146d1fb07SSven Peter {
37246d1fb07SSven Peter unsigned long flags;
37346d1fb07SSven Peter int ret;
37446d1fb07SSven Peter u32 command_reg;
37546d1fb07SSven Peter
37646d1fb07SSven Peter spin_lock_irqsave(&stream_map->dart->lock, flags);
37746d1fb07SSven Peter
378b76c68fcSHector Martin writel(stream_map->sidmap[0], stream_map->dart->regs + DART_T8020_STREAM_SELECT);
379b76c68fcSHector Martin writel(command, stream_map->dart->regs + DART_T8020_STREAM_COMMAND);
38046d1fb07SSven Peter
38146d1fb07SSven Peter ret = readl_poll_timeout_atomic(
382b76c68fcSHector Martin stream_map->dart->regs + DART_T8020_STREAM_COMMAND, command_reg,
383b76c68fcSHector Martin !(command_reg & DART_T8020_STREAM_COMMAND_BUSY), 1,
38446d1fb07SSven Peter DART_STREAM_COMMAND_BUSY_TIMEOUT);
38546d1fb07SSven Peter
38646d1fb07SSven Peter spin_unlock_irqrestore(&stream_map->dart->lock, flags);
38746d1fb07SSven Peter
38846d1fb07SSven Peter if (ret) {
38946d1fb07SSven Peter dev_err(stream_map->dart->dev,
39046d1fb07SSven Peter "busy bit did not clear after command %x for streams %lx\n",
391510d4072SHector Martin command, stream_map->sidmap[0]);
39246d1fb07SSven Peter return ret;
39346d1fb07SSven Peter }
39446d1fb07SSven Peter
39546d1fb07SSven Peter return 0;
39646d1fb07SSven Peter }
39746d1fb07SSven Peter
39846d1fb07SSven Peter static int
apple_dart_t8110_hw_tlb_command(struct apple_dart_stream_map * stream_map,u32 command)399d8bcc870SHector Martin apple_dart_t8110_hw_tlb_command(struct apple_dart_stream_map *stream_map,
400d8bcc870SHector Martin u32 command)
40146d1fb07SSven Peter {
402d8bcc870SHector Martin struct apple_dart *dart = stream_map->dart;
403d8bcc870SHector Martin unsigned long flags;
404d8bcc870SHector Martin int ret = 0;
405d8bcc870SHector Martin int sid;
406d8bcc870SHector Martin
407d8bcc870SHector Martin spin_lock_irqsave(&dart->lock, flags);
408d8bcc870SHector Martin
409d8bcc870SHector Martin for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
410d8bcc870SHector Martin u32 val = FIELD_PREP(DART_T8110_TLB_CMD_OP, command) |
411d8bcc870SHector Martin FIELD_PREP(DART_T8110_TLB_CMD_STREAM, sid);
412d8bcc870SHector Martin writel(val, dart->regs + DART_T8110_TLB_CMD);
413d8bcc870SHector Martin
414d8bcc870SHector Martin ret = readl_poll_timeout_atomic(
415d8bcc870SHector Martin dart->regs + DART_T8110_TLB_CMD, val,
416d8bcc870SHector Martin !(val & DART_T8110_TLB_CMD_BUSY), 1,
417d8bcc870SHector Martin DART_STREAM_COMMAND_BUSY_TIMEOUT);
418d8bcc870SHector Martin
419d8bcc870SHector Martin if (ret)
420d8bcc870SHector Martin break;
421d8bcc870SHector Martin
422d8bcc870SHector Martin }
423d8bcc870SHector Martin
424d8bcc870SHector Martin spin_unlock_irqrestore(&dart->lock, flags);
425d8bcc870SHector Martin
426d8bcc870SHector Martin if (ret) {
427d8bcc870SHector Martin dev_err(stream_map->dart->dev,
428d8bcc870SHector Martin "busy bit did not clear after command %x for stream %d\n",
429d8bcc870SHector Martin command, sid);
430d8bcc870SHector Martin return ret;
431d8bcc870SHector Martin }
432d8bcc870SHector Martin
433d8bcc870SHector Martin return 0;
434d8bcc870SHector Martin }
435d8bcc870SHector Martin
436d8bcc870SHector Martin static int
apple_dart_t8020_hw_invalidate_tlb(struct apple_dart_stream_map * stream_map)437b76c68fcSHector Martin apple_dart_t8020_hw_invalidate_tlb(struct apple_dart_stream_map *stream_map)
43846d1fb07SSven Peter {
439b76c68fcSHector Martin return apple_dart_t8020_hw_stream_command(
440b76c68fcSHector Martin stream_map, DART_T8020_STREAM_COMMAND_INVALIDATE);
44146d1fb07SSven Peter }
44246d1fb07SSven Peter
443d8bcc870SHector Martin static int
apple_dart_t8110_hw_invalidate_tlb(struct apple_dart_stream_map * stream_map)444d8bcc870SHector Martin apple_dart_t8110_hw_invalidate_tlb(struct apple_dart_stream_map *stream_map)
445d8bcc870SHector Martin {
446d8bcc870SHector Martin return apple_dart_t8110_hw_tlb_command(
447d8bcc870SHector Martin stream_map, DART_T8110_TLB_CMD_OP_FLUSH_SID);
44846d1fb07SSven Peter }
44946d1fb07SSven Peter
apple_dart_hw_reset(struct apple_dart * dart)45046d1fb07SSven Peter static int apple_dart_hw_reset(struct apple_dart *dart)
45146d1fb07SSven Peter {
45246d1fb07SSven Peter u32 config;
45346d1fb07SSven Peter struct apple_dart_stream_map stream_map;
454510d4072SHector Martin int i;
45546d1fb07SSven Peter
456b76c68fcSHector Martin config = readl(dart->regs + dart->hw->lock);
457b76c68fcSHector Martin if (config & dart->hw->lock_bit) {
45846d1fb07SSven Peter dev_err(dart->dev, "DART is locked down until reboot: %08x\n",
45946d1fb07SSven Peter config);
46046d1fb07SSven Peter return -EINVAL;
46146d1fb07SSven Peter }
46246d1fb07SSven Peter
46346d1fb07SSven Peter stream_map.dart = dart;
464510d4072SHector Martin bitmap_zero(stream_map.sidmap, DART_MAX_STREAMS);
465510d4072SHector Martin bitmap_set(stream_map.sidmap, 0, dart->num_streams);
46646d1fb07SSven Peter apple_dart_hw_disable_dma(&stream_map);
46746d1fb07SSven Peter apple_dart_hw_clear_all_ttbrs(&stream_map);
46846d1fb07SSven Peter
4695a009fc1SSven Peter /* enable all streams globally since TCR is used to control isolation */
470510d4072SHector Martin for (i = 0; i < BITS_TO_U32(dart->num_streams); i++)
471b76c68fcSHector Martin writel(U32_MAX, dart->regs + dart->hw->enable_streams + 4 * i);
4725a009fc1SSven Peter
47346d1fb07SSven Peter /* clear any pending errors before the interrupt is unmasked */
474b76c68fcSHector Martin writel(readl(dart->regs + dart->hw->error), dart->regs + dart->hw->error);
47546d1fb07SSven Peter
476d8bcc870SHector Martin if (dart->hw->type == DART_T8110)
477d8bcc870SHector Martin writel(0, dart->regs + DART_T8110_ERROR_MASK);
478d8bcc870SHector Martin
479b76c68fcSHector Martin return dart->hw->invalidate_tlb(&stream_map);
48046d1fb07SSven Peter }
48146d1fb07SSven Peter
apple_dart_domain_flush_tlb(struct apple_dart_domain * domain)48246d1fb07SSven Peter static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain)
48346d1fb07SSven Peter {
484510d4072SHector Martin int i, j;
48546d1fb07SSven Peter struct apple_dart_atomic_stream_map *domain_stream_map;
48646d1fb07SSven Peter struct apple_dart_stream_map stream_map;
48746d1fb07SSven Peter
48846d1fb07SSven Peter for_each_stream_map(i, domain, domain_stream_map) {
48946d1fb07SSven Peter stream_map.dart = domain_stream_map->dart;
490510d4072SHector Martin
491510d4072SHector Martin for (j = 0; j < BITS_TO_LONGS(stream_map.dart->num_streams); j++)
492510d4072SHector Martin stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]);
493510d4072SHector Martin
494b76c68fcSHector Martin stream_map.dart->hw->invalidate_tlb(&stream_map);
49546d1fb07SSven Peter }
49646d1fb07SSven Peter }
49746d1fb07SSven Peter
apple_dart_flush_iotlb_all(struct iommu_domain * domain)49846d1fb07SSven Peter static void apple_dart_flush_iotlb_all(struct iommu_domain *domain)
49946d1fb07SSven Peter {
50046d1fb07SSven Peter apple_dart_domain_flush_tlb(to_dart_domain(domain));
50146d1fb07SSven Peter }
50246d1fb07SSven Peter
apple_dart_iotlb_sync(struct iommu_domain * domain,struct iommu_iotlb_gather * gather)50346d1fb07SSven Peter static void apple_dart_iotlb_sync(struct iommu_domain *domain,
50446d1fb07SSven Peter struct iommu_iotlb_gather *gather)
50546d1fb07SSven Peter {
50646d1fb07SSven Peter apple_dart_domain_flush_tlb(to_dart_domain(domain));
50746d1fb07SSven Peter }
50846d1fb07SSven Peter
apple_dart_iotlb_sync_map(struct iommu_domain * domain,unsigned long iova,size_t size)50946d1fb07SSven Peter static void apple_dart_iotlb_sync_map(struct iommu_domain *domain,
51046d1fb07SSven Peter unsigned long iova, size_t size)
51146d1fb07SSven Peter {
51246d1fb07SSven Peter apple_dart_domain_flush_tlb(to_dart_domain(domain));
51346d1fb07SSven Peter }
51446d1fb07SSven Peter
apple_dart_iova_to_phys(struct iommu_domain * domain,dma_addr_t iova)51546d1fb07SSven Peter static phys_addr_t apple_dart_iova_to_phys(struct iommu_domain *domain,
51646d1fb07SSven Peter dma_addr_t iova)
51746d1fb07SSven Peter {
51846d1fb07SSven Peter struct apple_dart_domain *dart_domain = to_dart_domain(domain);
51946d1fb07SSven Peter struct io_pgtable_ops *ops = dart_domain->pgtbl_ops;
52046d1fb07SSven Peter
52146d1fb07SSven Peter if (!ops)
52246d1fb07SSven Peter return 0;
52346d1fb07SSven Peter
52446d1fb07SSven Peter return ops->iova_to_phys(ops, iova);
52546d1fb07SSven Peter }
52646d1fb07SSven Peter
apple_dart_map_pages(struct iommu_domain * domain,unsigned long iova,phys_addr_t paddr,size_t pgsize,size_t pgcount,int prot,gfp_t gfp,size_t * mapped)52746d1fb07SSven Peter static int apple_dart_map_pages(struct iommu_domain *domain, unsigned long iova,
52846d1fb07SSven Peter phys_addr_t paddr, size_t pgsize,
52946d1fb07SSven Peter size_t pgcount, int prot, gfp_t gfp,
53046d1fb07SSven Peter size_t *mapped)
53146d1fb07SSven Peter {
53246d1fb07SSven Peter struct apple_dart_domain *dart_domain = to_dart_domain(domain);
53346d1fb07SSven Peter struct io_pgtable_ops *ops = dart_domain->pgtbl_ops;
53446d1fb07SSven Peter
53546d1fb07SSven Peter if (!ops)
53646d1fb07SSven Peter return -ENODEV;
53746d1fb07SSven Peter
53846d1fb07SSven Peter return ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot, gfp,
53946d1fb07SSven Peter mapped);
54046d1fb07SSven Peter }
54146d1fb07SSven Peter
apple_dart_unmap_pages(struct iommu_domain * domain,unsigned long iova,size_t pgsize,size_t pgcount,struct iommu_iotlb_gather * gather)54246d1fb07SSven Peter static size_t apple_dart_unmap_pages(struct iommu_domain *domain,
54346d1fb07SSven Peter unsigned long iova, size_t pgsize,
54446d1fb07SSven Peter size_t pgcount,
54546d1fb07SSven Peter struct iommu_iotlb_gather *gather)
54646d1fb07SSven Peter {
54746d1fb07SSven Peter struct apple_dart_domain *dart_domain = to_dart_domain(domain);
54846d1fb07SSven Peter struct io_pgtable_ops *ops = dart_domain->pgtbl_ops;
54946d1fb07SSven Peter
55046d1fb07SSven Peter return ops->unmap_pages(ops, iova, pgsize, pgcount, gather);
55146d1fb07SSven Peter }
55246d1fb07SSven Peter
55346d1fb07SSven Peter static void
apple_dart_setup_translation(struct apple_dart_domain * domain,struct apple_dart_stream_map * stream_map)55446d1fb07SSven Peter apple_dart_setup_translation(struct apple_dart_domain *domain,
55546d1fb07SSven Peter struct apple_dart_stream_map *stream_map)
55646d1fb07SSven Peter {
55746d1fb07SSven Peter int i;
55846d1fb07SSven Peter struct io_pgtable_cfg *pgtbl_cfg =
55946d1fb07SSven Peter &io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
56046d1fb07SSven Peter
56146d1fb07SSven Peter for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
56246d1fb07SSven Peter apple_dart_hw_set_ttbr(stream_map, i,
56346d1fb07SSven Peter pgtbl_cfg->apple_dart_cfg.ttbr[i]);
5640b459bcdSHector Martin for (; i < stream_map->dart->hw->ttbr_count; ++i)
56546d1fb07SSven Peter apple_dart_hw_clear_ttbr(stream_map, i);
56646d1fb07SSven Peter
56746d1fb07SSven Peter apple_dart_hw_enable_translation(stream_map);
568b76c68fcSHector Martin stream_map->dart->hw->invalidate_tlb(stream_map);
56946d1fb07SSven Peter }
57046d1fb07SSven Peter
apple_dart_finalize_domain(struct iommu_domain * domain,struct apple_dart_master_cfg * cfg)57146d1fb07SSven Peter static int apple_dart_finalize_domain(struct iommu_domain *domain,
57246d1fb07SSven Peter struct apple_dart_master_cfg *cfg)
57346d1fb07SSven Peter {
57446d1fb07SSven Peter struct apple_dart_domain *dart_domain = to_dart_domain(domain);
57546d1fb07SSven Peter struct apple_dart *dart = cfg->stream_maps[0].dart;
57646d1fb07SSven Peter struct io_pgtable_cfg pgtbl_cfg;
57746d1fb07SSven Peter int ret = 0;
578510d4072SHector Martin int i, j;
57946d1fb07SSven Peter
58046d1fb07SSven Peter mutex_lock(&dart_domain->init_lock);
58146d1fb07SSven Peter
58246d1fb07SSven Peter if (dart_domain->finalized)
58346d1fb07SSven Peter goto done;
58446d1fb07SSven Peter
58546d1fb07SSven Peter for (i = 0; i < MAX_DARTS_PER_DEVICE; ++i) {
58646d1fb07SSven Peter dart_domain->stream_maps[i].dart = cfg->stream_maps[i].dart;
587510d4072SHector Martin for (j = 0; j < BITS_TO_LONGS(dart->num_streams); j++)
588510d4072SHector Martin atomic_long_set(&dart_domain->stream_maps[i].sidmap[j],
589510d4072SHector Martin cfg->stream_maps[i].sidmap[j]);
59046d1fb07SSven Peter }
59146d1fb07SSven Peter
59246d1fb07SSven Peter pgtbl_cfg = (struct io_pgtable_cfg){
59346d1fb07SSven Peter .pgsize_bitmap = dart->pgsize,
594d8bcc870SHector Martin .ias = dart->ias,
595d8bcc870SHector Martin .oas = dart->oas,
59646d1fb07SSven Peter .coherent_walk = 1,
59746d1fb07SSven Peter .iommu_dev = dart->dev,
59846d1fb07SSven Peter };
59946d1fb07SSven Peter
60046d1fb07SSven Peter dart_domain->pgtbl_ops =
601a380b8dcSSven Peter alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg, domain);
60246d1fb07SSven Peter if (!dart_domain->pgtbl_ops) {
60346d1fb07SSven Peter ret = -ENOMEM;
60446d1fb07SSven Peter goto done;
60546d1fb07SSven Peter }
60646d1fb07SSven Peter
60746d1fb07SSven Peter domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
60846d1fb07SSven Peter domain->geometry.aperture_start = 0;
609d8bcc870SHector Martin domain->geometry.aperture_end = (dma_addr_t)DMA_BIT_MASK(dart->ias);
61046d1fb07SSven Peter domain->geometry.force_aperture = true;
61146d1fb07SSven Peter
61246d1fb07SSven Peter dart_domain->finalized = true;
61346d1fb07SSven Peter
61446d1fb07SSven Peter done:
61546d1fb07SSven Peter mutex_unlock(&dart_domain->init_lock);
61646d1fb07SSven Peter return ret;
61746d1fb07SSven Peter }
61846d1fb07SSven Peter
61946d1fb07SSven Peter static int
apple_dart_mod_streams(struct apple_dart_atomic_stream_map * domain_maps,struct apple_dart_stream_map * master_maps,bool add_streams)62046d1fb07SSven Peter apple_dart_mod_streams(struct apple_dart_atomic_stream_map *domain_maps,
62146d1fb07SSven Peter struct apple_dart_stream_map *master_maps,
62246d1fb07SSven Peter bool add_streams)
62346d1fb07SSven Peter {
624510d4072SHector Martin int i, j;
62546d1fb07SSven Peter
62646d1fb07SSven Peter for (i = 0; i < MAX_DARTS_PER_DEVICE; ++i) {
62746d1fb07SSven Peter if (domain_maps[i].dart != master_maps[i].dart)
62846d1fb07SSven Peter return -EINVAL;
62946d1fb07SSven Peter }
63046d1fb07SSven Peter
63146d1fb07SSven Peter for (i = 0; i < MAX_DARTS_PER_DEVICE; ++i) {
63246d1fb07SSven Peter if (!domain_maps[i].dart)
63346d1fb07SSven Peter break;
634510d4072SHector Martin for (j = 0; j < BITS_TO_LONGS(domain_maps[i].dart->num_streams); j++) {
63546d1fb07SSven Peter if (add_streams)
636510d4072SHector Martin atomic_long_or(master_maps[i].sidmap[j],
637510d4072SHector Martin &domain_maps[i].sidmap[j]);
63846d1fb07SSven Peter else
639510d4072SHector Martin atomic_long_and(~master_maps[i].sidmap[j],
640510d4072SHector Martin &domain_maps[i].sidmap[j]);
641510d4072SHector Martin }
64246d1fb07SSven Peter }
64346d1fb07SSven Peter
64446d1fb07SSven Peter return 0;
64546d1fb07SSven Peter }
64646d1fb07SSven Peter
apple_dart_domain_add_streams(struct apple_dart_domain * domain,struct apple_dart_master_cfg * cfg)64746d1fb07SSven Peter static int apple_dart_domain_add_streams(struct apple_dart_domain *domain,
64846d1fb07SSven Peter struct apple_dart_master_cfg *cfg)
64946d1fb07SSven Peter {
65046d1fb07SSven Peter return apple_dart_mod_streams(domain->stream_maps, cfg->stream_maps,
65146d1fb07SSven Peter true);
65246d1fb07SSven Peter }
65346d1fb07SSven Peter
apple_dart_attach_dev(struct iommu_domain * domain,struct device * dev)65446d1fb07SSven Peter static int apple_dart_attach_dev(struct iommu_domain *domain,
65546d1fb07SSven Peter struct device *dev)
65646d1fb07SSven Peter {
65746d1fb07SSven Peter int ret, i;
65846d1fb07SSven Peter struct apple_dart_stream_map *stream_map;
65946d1fb07SSven Peter struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
66046d1fb07SSven Peter struct apple_dart_domain *dart_domain = to_dart_domain(domain);
66146d1fb07SSven Peter
66246d1fb07SSven Peter if (cfg->stream_maps[0].dart->force_bypass &&
66346d1fb07SSven Peter domain->type != IOMMU_DOMAIN_IDENTITY)
66446d1fb07SSven Peter return -EINVAL;
66546d1fb07SSven Peter if (!cfg->stream_maps[0].dart->supports_bypass &&
66646d1fb07SSven Peter domain->type == IOMMU_DOMAIN_IDENTITY)
66746d1fb07SSven Peter return -EINVAL;
66846d1fb07SSven Peter
66946d1fb07SSven Peter ret = apple_dart_finalize_domain(domain, cfg);
67046d1fb07SSven Peter if (ret)
67146d1fb07SSven Peter return ret;
67246d1fb07SSven Peter
67346d1fb07SSven Peter switch (domain->type) {
674*c7bd8a1fSHector Martin default:
67546d1fb07SSven Peter ret = apple_dart_domain_add_streams(dart_domain, cfg);
67646d1fb07SSven Peter if (ret)
67746d1fb07SSven Peter return ret;
67846d1fb07SSven Peter
67946d1fb07SSven Peter for_each_stream_map(i, cfg, stream_map)
68046d1fb07SSven Peter apple_dart_setup_translation(dart_domain, stream_map);
68146d1fb07SSven Peter break;
68246d1fb07SSven Peter case IOMMU_DOMAIN_BLOCKED:
68346d1fb07SSven Peter for_each_stream_map(i, cfg, stream_map)
68446d1fb07SSven Peter apple_dart_hw_disable_dma(stream_map);
68546d1fb07SSven Peter break;
68646d1fb07SSven Peter case IOMMU_DOMAIN_IDENTITY:
68746d1fb07SSven Peter for_each_stream_map(i, cfg, stream_map)
68846d1fb07SSven Peter apple_dart_hw_enable_bypass(stream_map);
68946d1fb07SSven Peter break;
69046d1fb07SSven Peter }
69146d1fb07SSven Peter
69246d1fb07SSven Peter return ret;
69346d1fb07SSven Peter }
69446d1fb07SSven Peter
apple_dart_probe_device(struct device * dev)69546d1fb07SSven Peter static struct iommu_device *apple_dart_probe_device(struct device *dev)
69646d1fb07SSven Peter {
69746d1fb07SSven Peter struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
69846d1fb07SSven Peter struct apple_dart_stream_map *stream_map;
69946d1fb07SSven Peter int i;
70046d1fb07SSven Peter
70146d1fb07SSven Peter if (!cfg)
70246d1fb07SSven Peter return ERR_PTR(-ENODEV);
70346d1fb07SSven Peter
70446d1fb07SSven Peter for_each_stream_map(i, cfg, stream_map)
70546d1fb07SSven Peter device_link_add(
70646d1fb07SSven Peter dev, stream_map->dart->dev,
70746d1fb07SSven Peter DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER);
70846d1fb07SSven Peter
70946d1fb07SSven Peter return &cfg->stream_maps[0].dart->iommu;
71046d1fb07SSven Peter }
71146d1fb07SSven Peter
apple_dart_release_device(struct device * dev)71246d1fb07SSven Peter static void apple_dart_release_device(struct device *dev)
71346d1fb07SSven Peter {
71446d1fb07SSven Peter struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
71546d1fb07SSven Peter
71646d1fb07SSven Peter dev_iommu_priv_set(dev, NULL);
71746d1fb07SSven Peter kfree(cfg);
71846d1fb07SSven Peter }
71946d1fb07SSven Peter
apple_dart_domain_alloc(unsigned int type)72046d1fb07SSven Peter static struct iommu_domain *apple_dart_domain_alloc(unsigned int type)
72146d1fb07SSven Peter {
72246d1fb07SSven Peter struct apple_dart_domain *dart_domain;
72346d1fb07SSven Peter
72446d1fb07SSven Peter if (type != IOMMU_DOMAIN_DMA && type != IOMMU_DOMAIN_UNMANAGED &&
72546d1fb07SSven Peter type != IOMMU_DOMAIN_IDENTITY && type != IOMMU_DOMAIN_BLOCKED)
72646d1fb07SSven Peter return NULL;
72746d1fb07SSven Peter
72846d1fb07SSven Peter dart_domain = kzalloc(sizeof(*dart_domain), GFP_KERNEL);
72946d1fb07SSven Peter if (!dart_domain)
73046d1fb07SSven Peter return NULL;
73146d1fb07SSven Peter
73246d1fb07SSven Peter mutex_init(&dart_domain->init_lock);
73346d1fb07SSven Peter
73446d1fb07SSven Peter /* no need to allocate pgtbl_ops or do any other finalization steps */
73546d1fb07SSven Peter if (type == IOMMU_DOMAIN_IDENTITY || type == IOMMU_DOMAIN_BLOCKED)
73646d1fb07SSven Peter dart_domain->finalized = true;
73746d1fb07SSven Peter
73846d1fb07SSven Peter return &dart_domain->domain;
73946d1fb07SSven Peter }
74046d1fb07SSven Peter
apple_dart_domain_free(struct iommu_domain * domain)74146d1fb07SSven Peter static void apple_dart_domain_free(struct iommu_domain *domain)
74246d1fb07SSven Peter {
74346d1fb07SSven Peter struct apple_dart_domain *dart_domain = to_dart_domain(domain);
74446d1fb07SSven Peter
74546d1fb07SSven Peter if (dart_domain->pgtbl_ops)
74646d1fb07SSven Peter free_io_pgtable_ops(dart_domain->pgtbl_ops);
74746d1fb07SSven Peter
74846d1fb07SSven Peter kfree(dart_domain);
74946d1fb07SSven Peter }
75046d1fb07SSven Peter
apple_dart_of_xlate(struct device * dev,struct of_phandle_args * args)75146d1fb07SSven Peter static int apple_dart_of_xlate(struct device *dev, struct of_phandle_args *args)
75246d1fb07SSven Peter {
75346d1fb07SSven Peter struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
75446d1fb07SSven Peter struct platform_device *iommu_pdev = of_find_device_by_node(args->np);
75546d1fb07SSven Peter struct apple_dart *dart = platform_get_drvdata(iommu_pdev);
75646d1fb07SSven Peter struct apple_dart *cfg_dart;
75746d1fb07SSven Peter int i, sid;
75846d1fb07SSven Peter
75946d1fb07SSven Peter if (args->args_count != 1)
76046d1fb07SSven Peter return -EINVAL;
76146d1fb07SSven Peter sid = args->args[0];
76246d1fb07SSven Peter
76346d1fb07SSven Peter if (!cfg)
76446d1fb07SSven Peter cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
76546d1fb07SSven Peter if (!cfg)
76646d1fb07SSven Peter return -ENOMEM;
76746d1fb07SSven Peter dev_iommu_priv_set(dev, cfg);
76846d1fb07SSven Peter
76946d1fb07SSven Peter cfg_dart = cfg->stream_maps[0].dart;
77046d1fb07SSven Peter if (cfg_dart) {
77146d1fb07SSven Peter if (cfg_dart->supports_bypass != dart->supports_bypass)
77246d1fb07SSven Peter return -EINVAL;
77346d1fb07SSven Peter if (cfg_dart->force_bypass != dart->force_bypass)
77446d1fb07SSven Peter return -EINVAL;
77546d1fb07SSven Peter if (cfg_dart->pgsize != dart->pgsize)
77646d1fb07SSven Peter return -EINVAL;
77746d1fb07SSven Peter }
77846d1fb07SSven Peter
77946d1fb07SSven Peter for (i = 0; i < MAX_DARTS_PER_DEVICE; ++i) {
78046d1fb07SSven Peter if (cfg->stream_maps[i].dart == dart) {
781510d4072SHector Martin set_bit(sid, cfg->stream_maps[i].sidmap);
78246d1fb07SSven Peter return 0;
78346d1fb07SSven Peter }
78446d1fb07SSven Peter }
78546d1fb07SSven Peter for (i = 0; i < MAX_DARTS_PER_DEVICE; ++i) {
78646d1fb07SSven Peter if (!cfg->stream_maps[i].dart) {
78746d1fb07SSven Peter cfg->stream_maps[i].dart = dart;
788510d4072SHector Martin set_bit(sid, cfg->stream_maps[i].sidmap);
78946d1fb07SSven Peter return 0;
79046d1fb07SSven Peter }
79146d1fb07SSven Peter }
79246d1fb07SSven Peter
79346d1fb07SSven Peter return -EINVAL;
79446d1fb07SSven Peter }
79546d1fb07SSven Peter
796f0b63680SSven Peter static DEFINE_MUTEX(apple_dart_groups_lock);
797f0b63680SSven Peter
apple_dart_release_group(void * iommu_data)798f0b63680SSven Peter static void apple_dart_release_group(void *iommu_data)
799f0b63680SSven Peter {
800f0b63680SSven Peter int i, sid;
801f0b63680SSven Peter struct apple_dart_stream_map *stream_map;
802f0b63680SSven Peter struct apple_dart_master_cfg *group_master_cfg = iommu_data;
803f0b63680SSven Peter
804f0b63680SSven Peter mutex_lock(&apple_dart_groups_lock);
805f0b63680SSven Peter
806f0b63680SSven Peter for_each_stream_map(i, group_master_cfg, stream_map)
807510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, stream_map->dart->num_streams)
808f0b63680SSven Peter stream_map->dart->sid2group[sid] = NULL;
809f0b63680SSven Peter
810f0b63680SSven Peter kfree(iommu_data);
811f0b63680SSven Peter mutex_unlock(&apple_dart_groups_lock);
812f0b63680SSven Peter }
813f0b63680SSven Peter
apple_dart_merge_master_cfg(struct apple_dart_master_cfg * dst,struct apple_dart_master_cfg * src)814cf5c1c87SSven Peter static int apple_dart_merge_master_cfg(struct apple_dart_master_cfg *dst,
815cf5c1c87SSven Peter struct apple_dart_master_cfg *src)
816cf5c1c87SSven Peter {
817cf5c1c87SSven Peter /*
818cf5c1c87SSven Peter * We know that this function is only called for groups returned from
819cf5c1c87SSven Peter * pci_device_group and that all Apple Silicon platforms never spread
820cf5c1c87SSven Peter * PCIe devices from the same bus across multiple DARTs such that we can
821cf5c1c87SSven Peter * just assume that both src and dst only have the same single DART.
822cf5c1c87SSven Peter */
823cf5c1c87SSven Peter if (src->stream_maps[1].dart)
824cf5c1c87SSven Peter return -EINVAL;
825cf5c1c87SSven Peter if (dst->stream_maps[1].dart)
826cf5c1c87SSven Peter return -EINVAL;
827cf5c1c87SSven Peter if (src->stream_maps[0].dart != dst->stream_maps[0].dart)
828cf5c1c87SSven Peter return -EINVAL;
829cf5c1c87SSven Peter
830cf5c1c87SSven Peter bitmap_or(dst->stream_maps[0].sidmap,
831cf5c1c87SSven Peter dst->stream_maps[0].sidmap,
832cf5c1c87SSven Peter src->stream_maps[0].sidmap,
833cf5c1c87SSven Peter dst->stream_maps[0].dart->num_streams);
834cf5c1c87SSven Peter return 0;
835cf5c1c87SSven Peter }
836cf5c1c87SSven Peter
apple_dart_device_group(struct device * dev)83746d1fb07SSven Peter static struct iommu_group *apple_dart_device_group(struct device *dev)
83846d1fb07SSven Peter {
83946d1fb07SSven Peter int i, sid;
84046d1fb07SSven Peter struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
84146d1fb07SSven Peter struct apple_dart_stream_map *stream_map;
842f0b63680SSven Peter struct apple_dart_master_cfg *group_master_cfg;
84346d1fb07SSven Peter struct iommu_group *group = NULL;
84446d1fb07SSven Peter struct iommu_group *res = ERR_PTR(-EINVAL);
84546d1fb07SSven Peter
846f0b63680SSven Peter mutex_lock(&apple_dart_groups_lock);
84746d1fb07SSven Peter
84846d1fb07SSven Peter for_each_stream_map(i, cfg, stream_map) {
849510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, stream_map->dart->num_streams) {
85046d1fb07SSven Peter struct iommu_group *stream_group =
85146d1fb07SSven Peter stream_map->dart->sid2group[sid];
85246d1fb07SSven Peter
85346d1fb07SSven Peter if (group && group != stream_group) {
85446d1fb07SSven Peter res = ERR_PTR(-EINVAL);
85546d1fb07SSven Peter goto out;
85646d1fb07SSven Peter }
85746d1fb07SSven Peter
85846d1fb07SSven Peter group = stream_group;
85946d1fb07SSven Peter }
86046d1fb07SSven Peter }
86146d1fb07SSven Peter
86246d1fb07SSven Peter if (group) {
86346d1fb07SSven Peter res = iommu_group_ref_get(group);
86446d1fb07SSven Peter goto out;
86546d1fb07SSven Peter }
86646d1fb07SSven Peter
86746d1fb07SSven Peter #ifdef CONFIG_PCI
86846d1fb07SSven Peter if (dev_is_pci(dev))
86946d1fb07SSven Peter group = pci_device_group(dev);
87046d1fb07SSven Peter else
87146d1fb07SSven Peter #endif
87246d1fb07SSven Peter group = generic_device_group(dev);
87346d1fb07SSven Peter
874f0b63680SSven Peter res = ERR_PTR(-ENOMEM);
875f0b63680SSven Peter if (!group)
876f0b63680SSven Peter goto out;
877f0b63680SSven Peter
878cf5c1c87SSven Peter group_master_cfg = iommu_group_get_iommudata(group);
879cf5c1c87SSven Peter if (group_master_cfg) {
880cf5c1c87SSven Peter int ret;
881cf5c1c87SSven Peter
882cf5c1c87SSven Peter ret = apple_dart_merge_master_cfg(group_master_cfg, cfg);
883cf5c1c87SSven Peter if (ret) {
884cf5c1c87SSven Peter dev_err(dev, "Failed to merge DART IOMMU grups.\n");
885cf5c1c87SSven Peter iommu_group_put(group);
886cf5c1c87SSven Peter res = ERR_PTR(ret);
887cf5c1c87SSven Peter goto out;
888cf5c1c87SSven Peter }
889cf5c1c87SSven Peter } else {
890cf5c1c87SSven Peter group_master_cfg = kmemdup(cfg, sizeof(*group_master_cfg),
891cf5c1c87SSven Peter GFP_KERNEL);
892f0b63680SSven Peter if (!group_master_cfg) {
893f0b63680SSven Peter iommu_group_put(group);
894f0b63680SSven Peter goto out;
895f0b63680SSven Peter }
896f0b63680SSven Peter
897f0b63680SSven Peter iommu_group_set_iommudata(group, group_master_cfg,
898f0b63680SSven Peter apple_dart_release_group);
899cf5c1c87SSven Peter }
900f0b63680SSven Peter
90146d1fb07SSven Peter for_each_stream_map(i, cfg, stream_map)
902510d4072SHector Martin for_each_set_bit(sid, stream_map->sidmap, stream_map->dart->num_streams)
90346d1fb07SSven Peter stream_map->dart->sid2group[sid] = group;
90446d1fb07SSven Peter
90546d1fb07SSven Peter res = group;
90646d1fb07SSven Peter
90746d1fb07SSven Peter out:
908f0b63680SSven Peter mutex_unlock(&apple_dart_groups_lock);
90946d1fb07SSven Peter return res;
91046d1fb07SSven Peter }
91146d1fb07SSven Peter
apple_dart_def_domain_type(struct device * dev)91246d1fb07SSven Peter static int apple_dart_def_domain_type(struct device *dev)
91346d1fb07SSven Peter {
91446d1fb07SSven Peter struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
91546d1fb07SSven Peter
91646d1fb07SSven Peter if (cfg->stream_maps[0].dart->force_bypass)
91746d1fb07SSven Peter return IOMMU_DOMAIN_IDENTITY;
91846d1fb07SSven Peter if (!cfg->stream_maps[0].dart->supports_bypass)
91946d1fb07SSven Peter return IOMMU_DOMAIN_DMA;
92046d1fb07SSven Peter
92146d1fb07SSven Peter return 0;
92246d1fb07SSven Peter }
92346d1fb07SSven Peter
924946d619fSMarc Zyngier #ifndef CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR
925946d619fSMarc Zyngier /* Keep things compiling when CONFIG_PCI_APPLE isn't selected */
926946d619fSMarc Zyngier #define CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR 0
927946d619fSMarc Zyngier #endif
928946d619fSMarc Zyngier #define DOORBELL_ADDR (CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR & PAGE_MASK)
929946d619fSMarc Zyngier
apple_dart_get_resv_regions(struct device * dev,struct list_head * head)930946d619fSMarc Zyngier static void apple_dart_get_resv_regions(struct device *dev,
931946d619fSMarc Zyngier struct list_head *head)
932946d619fSMarc Zyngier {
933946d619fSMarc Zyngier if (IS_ENABLED(CONFIG_PCIE_APPLE) && dev_is_pci(dev)) {
934946d619fSMarc Zyngier struct iommu_resv_region *region;
935946d619fSMarc Zyngier int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
936946d619fSMarc Zyngier
937946d619fSMarc Zyngier region = iommu_alloc_resv_region(DOORBELL_ADDR,
938946d619fSMarc Zyngier PAGE_SIZE, prot,
9390251d010SLu Baolu IOMMU_RESV_MSI, GFP_KERNEL);
940946d619fSMarc Zyngier if (!region)
941946d619fSMarc Zyngier return;
942946d619fSMarc Zyngier
943946d619fSMarc Zyngier list_add_tail(®ion->list, head);
944946d619fSMarc Zyngier }
945946d619fSMarc Zyngier
946946d619fSMarc Zyngier iommu_dma_get_resv_regions(dev, head);
947946d619fSMarc Zyngier }
948946d619fSMarc Zyngier
94946d1fb07SSven Peter static const struct iommu_ops apple_dart_iommu_ops = {
95046d1fb07SSven Peter .domain_alloc = apple_dart_domain_alloc,
95146d1fb07SSven Peter .probe_device = apple_dart_probe_device,
95246d1fb07SSven Peter .release_device = apple_dart_release_device,
95346d1fb07SSven Peter .device_group = apple_dart_device_group,
95446d1fb07SSven Peter .of_xlate = apple_dart_of_xlate,
95546d1fb07SSven Peter .def_domain_type = apple_dart_def_domain_type,
956946d619fSMarc Zyngier .get_resv_regions = apple_dart_get_resv_regions,
95746d1fb07SSven Peter .pgsize_bitmap = -1UL, /* Restricted during dart probe */
9582ac2fab5SHector Martin .owner = THIS_MODULE,
9599a630a4bSLu Baolu .default_domain_ops = &(const struct iommu_domain_ops) {
9609a630a4bSLu Baolu .attach_dev = apple_dart_attach_dev,
9619a630a4bSLu Baolu .map_pages = apple_dart_map_pages,
9629a630a4bSLu Baolu .unmap_pages = apple_dart_unmap_pages,
9639a630a4bSLu Baolu .flush_iotlb_all = apple_dart_flush_iotlb_all,
9649a630a4bSLu Baolu .iotlb_sync = apple_dart_iotlb_sync,
9659a630a4bSLu Baolu .iotlb_sync_map = apple_dart_iotlb_sync_map,
9669a630a4bSLu Baolu .iova_to_phys = apple_dart_iova_to_phys,
9679a630a4bSLu Baolu .free = apple_dart_domain_free,
9689a630a4bSLu Baolu }
96946d1fb07SSven Peter };
97046d1fb07SSven Peter
apple_dart_t8020_irq(int irq,void * dev)971b76c68fcSHector Martin static irqreturn_t apple_dart_t8020_irq(int irq, void *dev)
97246d1fb07SSven Peter {
97346d1fb07SSven Peter struct apple_dart *dart = dev;
97446d1fb07SSven Peter const char *fault_name = NULL;
975b76c68fcSHector Martin u32 error = readl(dart->regs + DART_T8020_ERROR);
976b76c68fcSHector Martin u32 error_code = FIELD_GET(DART_T8020_ERROR_CODE, error);
977b76c68fcSHector Martin u32 addr_lo = readl(dart->regs + DART_T8020_ERROR_ADDR_LO);
978b76c68fcSHector Martin u32 addr_hi = readl(dart->regs + DART_T8020_ERROR_ADDR_HI);
97946d1fb07SSven Peter u64 addr = addr_lo | (((u64)addr_hi) << 32);
980b76c68fcSHector Martin u8 stream_idx = FIELD_GET(DART_T8020_ERROR_STREAM, error);
98146d1fb07SSven Peter
982b76c68fcSHector Martin if (!(error & DART_T8020_ERROR_FLAG))
98346d1fb07SSven Peter return IRQ_NONE;
98446d1fb07SSven Peter
98546d1fb07SSven Peter /* there should only be a single bit set but let's use == to be sure */
986b76c68fcSHector Martin if (error_code == DART_T8020_ERROR_READ_FAULT)
98746d1fb07SSven Peter fault_name = "READ FAULT";
988b76c68fcSHector Martin else if (error_code == DART_T8020_ERROR_WRITE_FAULT)
98946d1fb07SSven Peter fault_name = "WRITE FAULT";
990b76c68fcSHector Martin else if (error_code == DART_T8020_ERROR_NO_PTE)
99146d1fb07SSven Peter fault_name = "NO PTE FOR IOVA";
992b76c68fcSHector Martin else if (error_code == DART_T8020_ERROR_NO_PMD)
99346d1fb07SSven Peter fault_name = "NO PMD FOR IOVA";
994b76c68fcSHector Martin else if (error_code == DART_T8020_ERROR_NO_TTBR)
99546d1fb07SSven Peter fault_name = "NO TTBR FOR IOVA";
99646d1fb07SSven Peter else
99746d1fb07SSven Peter fault_name = "unknown";
99846d1fb07SSven Peter
99946d1fb07SSven Peter dev_err_ratelimited(
100046d1fb07SSven Peter dart->dev,
100146d1fb07SSven Peter "translation fault: status:0x%x stream:%d code:0x%x (%s) at 0x%llx",
100246d1fb07SSven Peter error, stream_idx, error_code, fault_name, addr);
100346d1fb07SSven Peter
1004b76c68fcSHector Martin writel(error, dart->regs + DART_T8020_ERROR);
100546d1fb07SSven Peter return IRQ_HANDLED;
100646d1fb07SSven Peter }
100746d1fb07SSven Peter
apple_dart_t8110_irq(int irq,void * dev)1008d8bcc870SHector Martin static irqreturn_t apple_dart_t8110_irq(int irq, void *dev)
1009d8bcc870SHector Martin {
1010d8bcc870SHector Martin struct apple_dart *dart = dev;
1011d8bcc870SHector Martin const char *fault_name = NULL;
1012d8bcc870SHector Martin u32 error = readl(dart->regs + DART_T8110_ERROR);
1013d8bcc870SHector Martin u32 error_code = FIELD_GET(DART_T8110_ERROR_CODE, error);
1014d8bcc870SHector Martin u32 addr_lo = readl(dart->regs + DART_T8110_ERROR_ADDR_LO);
1015d8bcc870SHector Martin u32 addr_hi = readl(dart->regs + DART_T8110_ERROR_ADDR_HI);
1016d8bcc870SHector Martin u64 addr = addr_lo | (((u64)addr_hi) << 32);
1017d8bcc870SHector Martin u8 stream_idx = FIELD_GET(DART_T8110_ERROR_STREAM, error);
1018d8bcc870SHector Martin
1019d8bcc870SHector Martin if (!(error & DART_T8110_ERROR_FLAG))
1020d8bcc870SHector Martin return IRQ_NONE;
1021d8bcc870SHector Martin
1022d8bcc870SHector Martin /* there should only be a single bit set but let's use == to be sure */
1023d8bcc870SHector Martin if (error_code == DART_T8110_ERROR_READ_FAULT)
1024d8bcc870SHector Martin fault_name = "READ FAULT";
1025d8bcc870SHector Martin else if (error_code == DART_T8110_ERROR_WRITE_FAULT)
1026d8bcc870SHector Martin fault_name = "WRITE FAULT";
1027d8bcc870SHector Martin else if (error_code == DART_T8110_ERROR_NO_PTE)
1028d8bcc870SHector Martin fault_name = "NO PTE FOR IOVA";
1029d8bcc870SHector Martin else if (error_code == DART_T8110_ERROR_NO_PMD)
1030d8bcc870SHector Martin fault_name = "NO PMD FOR IOVA";
1031d8bcc870SHector Martin else if (error_code == DART_T8110_ERROR_NO_PGD)
1032d8bcc870SHector Martin fault_name = "NO PGD FOR IOVA";
1033d8bcc870SHector Martin else if (error_code == DART_T8110_ERROR_NO_TTBR)
1034d8bcc870SHector Martin fault_name = "NO TTBR FOR IOVA";
1035d8bcc870SHector Martin else
1036d8bcc870SHector Martin fault_name = "unknown";
1037d8bcc870SHector Martin
1038d8bcc870SHector Martin dev_err_ratelimited(
1039d8bcc870SHector Martin dart->dev,
1040d8bcc870SHector Martin "translation fault: status:0x%x stream:%d code:0x%x (%s) at 0x%llx",
1041d8bcc870SHector Martin error, stream_idx, error_code, fault_name, addr);
1042d8bcc870SHector Martin
1043d8bcc870SHector Martin writel(error, dart->regs + DART_T8110_ERROR);
104446d1fb07SSven Peter return IRQ_HANDLED;
104546d1fb07SSven Peter }
104646d1fb07SSven Peter
apple_dart_probe(struct platform_device * pdev)104746d1fb07SSven Peter static int apple_dart_probe(struct platform_device *pdev)
104846d1fb07SSven Peter {
104946d1fb07SSven Peter int ret;
1050d8bcc870SHector Martin u32 dart_params[4];
105146d1fb07SSven Peter struct resource *res;
105246d1fb07SSven Peter struct apple_dart *dart;
105346d1fb07SSven Peter struct device *dev = &pdev->dev;
105446d1fb07SSven Peter
105546d1fb07SSven Peter dart = devm_kzalloc(dev, sizeof(*dart), GFP_KERNEL);
105646d1fb07SSven Peter if (!dart)
105746d1fb07SSven Peter return -ENOMEM;
105846d1fb07SSven Peter
105946d1fb07SSven Peter dart->dev = dev;
1060a380b8dcSSven Peter dart->hw = of_device_get_match_data(dev);
106146d1fb07SSven Peter spin_lock_init(&dart->lock);
106246d1fb07SSven Peter
1063a15932f4SYang Yingliang dart->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
1064a15932f4SYang Yingliang if (IS_ERR(dart->regs))
1065a15932f4SYang Yingliang return PTR_ERR(dart->regs);
1066a15932f4SYang Yingliang
106746d1fb07SSven Peter if (resource_size(res) < 0x4000) {
106846d1fb07SSven Peter dev_err(dev, "MMIO region too small (%pr)\n", res);
106946d1fb07SSven Peter return -EINVAL;
107046d1fb07SSven Peter }
107146d1fb07SSven Peter
107246d1fb07SSven Peter dart->irq = platform_get_irq(pdev, 0);
107346d1fb07SSven Peter if (dart->irq < 0)
107446d1fb07SSven Peter return -ENODEV;
107546d1fb07SSven Peter
107646d1fb07SSven Peter ret = devm_clk_bulk_get_all(dev, &dart->clks);
107746d1fb07SSven Peter if (ret < 0)
107846d1fb07SSven Peter return ret;
107946d1fb07SSven Peter dart->num_clks = ret;
108046d1fb07SSven Peter
108146d1fb07SSven Peter ret = clk_bulk_prepare_enable(dart->num_clks, dart->clks);
108246d1fb07SSven Peter if (ret)
108346d1fb07SSven Peter return ret;
108446d1fb07SSven Peter
108546d1fb07SSven Peter dart_params[0] = readl(dart->regs + DART_PARAMS1);
108646d1fb07SSven Peter dart_params[1] = readl(dart->regs + DART_PARAMS2);
1087a772a02cSHector Martin dart->pgsize = 1 << FIELD_GET(DART_PARAMS1_PAGE_SHIFT, dart_params[0]);
1088a772a02cSHector Martin dart->supports_bypass = dart_params[1] & DART_PARAMS2_BYPASS_SUPPORT;
1089510d4072SHector Martin
1090d8bcc870SHector Martin switch (dart->hw->type) {
1091d8bcc870SHector Martin case DART_T8020:
1092d8bcc870SHector Martin case DART_T6000:
1093d8bcc870SHector Martin dart->ias = 32;
1094d8bcc870SHector Martin dart->oas = dart->hw->oas;
1095510d4072SHector Martin dart->num_streams = dart->hw->max_sid_count;
1096d8bcc870SHector Martin break;
1097d8bcc870SHector Martin
1098d8bcc870SHector Martin case DART_T8110:
1099d8bcc870SHector Martin dart_params[2] = readl(dart->regs + DART_T8110_PARAMS3);
1100d8bcc870SHector Martin dart_params[3] = readl(dart->regs + DART_T8110_PARAMS4);
1101d8bcc870SHector Martin dart->ias = FIELD_GET(DART_T8110_PARAMS3_VA_WIDTH, dart_params[2]);
1102d8bcc870SHector Martin dart->oas = FIELD_GET(DART_T8110_PARAMS3_PA_WIDTH, dart_params[2]);
1103d8bcc870SHector Martin dart->num_streams = FIELD_GET(DART_T8110_PARAMS4_NUM_SIDS, dart_params[3]);
1104d8bcc870SHector Martin break;
1105d8bcc870SHector Martin }
1106510d4072SHector Martin
1107510d4072SHector Martin if (dart->num_streams > DART_MAX_STREAMS) {
1108510d4072SHector Martin dev_err(&pdev->dev, "Too many streams (%d > %d)\n",
1109510d4072SHector Martin dart->num_streams, DART_MAX_STREAMS);
1110510d4072SHector Martin ret = -EINVAL;
1111510d4072SHector Martin goto err_clk_disable;
1112510d4072SHector Martin }
1113510d4072SHector Martin
111446d1fb07SSven Peter dart->force_bypass = dart->pgsize > PAGE_SIZE;
111546d1fb07SSven Peter
111646d1fb07SSven Peter ret = apple_dart_hw_reset(dart);
111746d1fb07SSven Peter if (ret)
111846d1fb07SSven Peter goto err_clk_disable;
111946d1fb07SSven Peter
1120b76c68fcSHector Martin ret = request_irq(dart->irq, dart->hw->irq_handler, IRQF_SHARED,
112146d1fb07SSven Peter "apple-dart fault handler", dart);
112246d1fb07SSven Peter if (ret)
112346d1fb07SSven Peter goto err_clk_disable;
112446d1fb07SSven Peter
112546d1fb07SSven Peter platform_set_drvdata(pdev, dart);
112646d1fb07SSven Peter
112746d1fb07SSven Peter ret = iommu_device_sysfs_add(&dart->iommu, dev, NULL, "apple-dart.%s",
112846d1fb07SSven Peter dev_name(&pdev->dev));
112946d1fb07SSven Peter if (ret)
1130006abbe3SRobin Murphy goto err_free_irq;
113146d1fb07SSven Peter
113246d1fb07SSven Peter ret = iommu_device_register(&dart->iommu, &apple_dart_iommu_ops, dev);
113346d1fb07SSven Peter if (ret)
113446d1fb07SSven Peter goto err_sysfs_remove;
113546d1fb07SSven Peter
113646d1fb07SSven Peter dev_info(
113746d1fb07SSven Peter &pdev->dev,
1138510d4072SHector Martin "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d] initialized\n",
1139510d4072SHector Martin dart->pgsize, dart->num_streams, dart->supports_bypass, dart->force_bypass);
114046d1fb07SSven Peter return 0;
114146d1fb07SSven Peter
114246d1fb07SSven Peter err_sysfs_remove:
114346d1fb07SSven Peter iommu_device_sysfs_remove(&dart->iommu);
114446d1fb07SSven Peter err_free_irq:
114546d1fb07SSven Peter free_irq(dart->irq, dart);
114646d1fb07SSven Peter err_clk_disable:
114746d1fb07SSven Peter clk_bulk_disable_unprepare(dart->num_clks, dart->clks);
114846d1fb07SSven Peter
114946d1fb07SSven Peter return ret;
115046d1fb07SSven Peter }
115146d1fb07SSven Peter
apple_dart_remove(struct platform_device * pdev)1152f8047318SUwe Kleine-König static void apple_dart_remove(struct platform_device *pdev)
115346d1fb07SSven Peter {
115446d1fb07SSven Peter struct apple_dart *dart = platform_get_drvdata(pdev);
115546d1fb07SSven Peter
115646d1fb07SSven Peter apple_dart_hw_reset(dart);
115746d1fb07SSven Peter free_irq(dart->irq, dart);
115846d1fb07SSven Peter
115946d1fb07SSven Peter iommu_device_unregister(&dart->iommu);
116046d1fb07SSven Peter iommu_device_sysfs_remove(&dart->iommu);
116146d1fb07SSven Peter
116246d1fb07SSven Peter clk_bulk_disable_unprepare(dart->num_clks, dart->clks);
116346d1fb07SSven Peter }
116446d1fb07SSven Peter
1165a380b8dcSSven Peter static const struct apple_dart_hw apple_dart_hw_t8103 = {
1166d8bcc870SHector Martin .type = DART_T8020,
1167b76c68fcSHector Martin .irq_handler = apple_dart_t8020_irq,
1168b76c68fcSHector Martin .invalidate_tlb = apple_dart_t8020_hw_invalidate_tlb,
1169a380b8dcSSven Peter .oas = 36,
1170a380b8dcSSven Peter .fmt = APPLE_DART,
1171510d4072SHector Martin .max_sid_count = 16,
11720b459bcdSHector Martin
1173b76c68fcSHector Martin .enable_streams = DART_T8020_STREAMS_ENABLE,
1174b76c68fcSHector Martin .lock = DART_T8020_CONFIG,
1175b76c68fcSHector Martin .lock_bit = DART_T8020_CONFIG_LOCK,
1176b76c68fcSHector Martin
1177b76c68fcSHector Martin .error = DART_T8020_ERROR,
1178b76c68fcSHector Martin
1179b76c68fcSHector Martin .tcr = DART_T8020_TCR,
1180b76c68fcSHector Martin .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE,
1181b76c68fcSHector Martin .tcr_disabled = 0,
1182b76c68fcSHector Martin .tcr_bypass = DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART,
1183b76c68fcSHector Martin
1184b76c68fcSHector Martin .ttbr = DART_T8020_TTBR,
1185b76c68fcSHector Martin .ttbr_valid = DART_T8020_TTBR_VALID,
1186b76c68fcSHector Martin .ttbr_addr_field_shift = DART_T8020_TTBR_ADDR_FIELD_SHIFT,
1187b76c68fcSHector Martin .ttbr_shift = DART_T8020_TTBR_SHIFT,
11880b459bcdSHector Martin .ttbr_count = 4,
1189a380b8dcSSven Peter };
1190a380b8dcSSven Peter static const struct apple_dart_hw apple_dart_hw_t6000 = {
1191d8bcc870SHector Martin .type = DART_T6000,
1192b76c68fcSHector Martin .irq_handler = apple_dart_t8020_irq,
1193b76c68fcSHector Martin .invalidate_tlb = apple_dart_t8020_hw_invalidate_tlb,
1194a380b8dcSSven Peter .oas = 42,
1195a380b8dcSSven Peter .fmt = APPLE_DART2,
1196510d4072SHector Martin .max_sid_count = 16,
11970b459bcdSHector Martin
1198b76c68fcSHector Martin .enable_streams = DART_T8020_STREAMS_ENABLE,
1199b76c68fcSHector Martin .lock = DART_T8020_CONFIG,
1200b76c68fcSHector Martin .lock_bit = DART_T8020_CONFIG_LOCK,
1201b76c68fcSHector Martin
1202b76c68fcSHector Martin .error = DART_T8020_ERROR,
1203b76c68fcSHector Martin
1204b76c68fcSHector Martin .tcr = DART_T8020_TCR,
1205b76c68fcSHector Martin .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE,
1206b76c68fcSHector Martin .tcr_disabled = 0,
1207b76c68fcSHector Martin .tcr_bypass = DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART,
1208b76c68fcSHector Martin
1209b76c68fcSHector Martin .ttbr = DART_T8020_TTBR,
1210b76c68fcSHector Martin .ttbr_valid = DART_T8020_TTBR_VALID,
1211b76c68fcSHector Martin .ttbr_addr_field_shift = DART_T8020_TTBR_ADDR_FIELD_SHIFT,
1212b76c68fcSHector Martin .ttbr_shift = DART_T8020_TTBR_SHIFT,
12130b459bcdSHector Martin .ttbr_count = 4,
1214a380b8dcSSven Peter };
1215a380b8dcSSven Peter
1216d8bcc870SHector Martin static const struct apple_dart_hw apple_dart_hw_t8110 = {
1217d8bcc870SHector Martin .type = DART_T8110,
1218d8bcc870SHector Martin .irq_handler = apple_dart_t8110_irq,
1219d8bcc870SHector Martin .invalidate_tlb = apple_dart_t8110_hw_invalidate_tlb,
1220d8bcc870SHector Martin .fmt = APPLE_DART2,
1221d8bcc870SHector Martin .max_sid_count = 256,
1222d8bcc870SHector Martin
1223d8bcc870SHector Martin .enable_streams = DART_T8110_ENABLE_STREAMS,
1224d8bcc870SHector Martin .lock = DART_T8110_PROTECT,
1225d8bcc870SHector Martin .lock_bit = DART_T8110_PROTECT_TTBR_TCR,
1226d8bcc870SHector Martin
1227d8bcc870SHector Martin .error = DART_T8110_ERROR,
1228d8bcc870SHector Martin
1229d8bcc870SHector Martin .tcr = DART_T8110_TCR,
1230d8bcc870SHector Martin .tcr_enabled = DART_T8110_TCR_TRANSLATE_ENABLE,
1231d8bcc870SHector Martin .tcr_disabled = 0,
1232d8bcc870SHector Martin .tcr_bypass = DART_T8110_TCR_BYPASS_DAPF | DART_T8110_TCR_BYPASS_DART,
1233d8bcc870SHector Martin
1234d8bcc870SHector Martin .ttbr = DART_T8110_TTBR,
1235d8bcc870SHector Martin .ttbr_valid = DART_T8110_TTBR_VALID,
1236d8bcc870SHector Martin .ttbr_addr_field_shift = DART_T8110_TTBR_ADDR_FIELD_SHIFT,
1237d8bcc870SHector Martin .ttbr_shift = DART_T8110_TTBR_SHIFT,
1238d8bcc870SHector Martin .ttbr_count = 1,
1239d8bcc870SHector Martin };
1240d8bcc870SHector Martin
apple_dart_suspend(struct device * dev)12413d68bbb8SHector Martin static __maybe_unused int apple_dart_suspend(struct device *dev)
12423d68bbb8SHector Martin {
12433d68bbb8SHector Martin struct apple_dart *dart = dev_get_drvdata(dev);
12443d68bbb8SHector Martin unsigned int sid, idx;
12453d68bbb8SHector Martin
1246510d4072SHector Martin for (sid = 0; sid < dart->num_streams; sid++) {
1247b76c68fcSHector Martin dart->save_tcr[sid] = readl_relaxed(dart->regs + DART_TCR(dart, sid));
12480b459bcdSHector Martin for (idx = 0; idx < dart->hw->ttbr_count; idx++)
12493d68bbb8SHector Martin dart->save_ttbr[sid][idx] =
12500b459bcdSHector Martin readl(dart->regs + DART_TTBR(dart, sid, idx));
12513d68bbb8SHector Martin }
12523d68bbb8SHector Martin
12533d68bbb8SHector Martin return 0;
12543d68bbb8SHector Martin }
12553d68bbb8SHector Martin
apple_dart_resume(struct device * dev)12563d68bbb8SHector Martin static __maybe_unused int apple_dart_resume(struct device *dev)
12573d68bbb8SHector Martin {
12583d68bbb8SHector Martin struct apple_dart *dart = dev_get_drvdata(dev);
12593d68bbb8SHector Martin unsigned int sid, idx;
12603d68bbb8SHector Martin int ret;
12613d68bbb8SHector Martin
12623d68bbb8SHector Martin ret = apple_dart_hw_reset(dart);
12633d68bbb8SHector Martin if (ret) {
12643d68bbb8SHector Martin dev_err(dev, "Failed to reset DART on resume\n");
12653d68bbb8SHector Martin return ret;
12663d68bbb8SHector Martin }
12673d68bbb8SHector Martin
1268510d4072SHector Martin for (sid = 0; sid < dart->num_streams; sid++) {
12690b459bcdSHector Martin for (idx = 0; idx < dart->hw->ttbr_count; idx++)
12703d68bbb8SHector Martin writel(dart->save_ttbr[sid][idx],
12710b459bcdSHector Martin dart->regs + DART_TTBR(dart, sid, idx));
1272b76c68fcSHector Martin writel(dart->save_tcr[sid], dart->regs + DART_TCR(dart, sid));
12733d68bbb8SHector Martin }
12743d68bbb8SHector Martin
12753d68bbb8SHector Martin return 0;
12763d68bbb8SHector Martin }
12773d68bbb8SHector Martin
1278ed8c975bSMin-Hua Chen static DEFINE_SIMPLE_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dart_resume);
12793d68bbb8SHector Martin
128046d1fb07SSven Peter static const struct of_device_id apple_dart_of_match[] = {
1281a380b8dcSSven Peter { .compatible = "apple,t8103-dart", .data = &apple_dart_hw_t8103 },
1282d8bcc870SHector Martin { .compatible = "apple,t8110-dart", .data = &apple_dart_hw_t8110 },
1283a380b8dcSSven Peter { .compatible = "apple,t6000-dart", .data = &apple_dart_hw_t6000 },
128446d1fb07SSven Peter {},
128546d1fb07SSven Peter };
128646d1fb07SSven Peter MODULE_DEVICE_TABLE(of, apple_dart_of_match);
128746d1fb07SSven Peter
128846d1fb07SSven Peter static struct platform_driver apple_dart_driver = {
128946d1fb07SSven Peter .driver = {
129046d1fb07SSven Peter .name = "apple-dart",
129146d1fb07SSven Peter .of_match_table = apple_dart_of_match,
129246d1fb07SSven Peter .suppress_bind_attrs = true,
12933d68bbb8SHector Martin .pm = pm_sleep_ptr(&apple_dart_pm_ops),
129446d1fb07SSven Peter },
129546d1fb07SSven Peter .probe = apple_dart_probe,
1296f8047318SUwe Kleine-König .remove_new = apple_dart_remove,
129746d1fb07SSven Peter };
129846d1fb07SSven Peter
129946d1fb07SSven Peter module_platform_driver(apple_dart_driver);
130046d1fb07SSven Peter
130146d1fb07SSven Peter MODULE_DESCRIPTION("IOMMU API for Apple's DART");
130246d1fb07SSven Peter MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
130346d1fb07SSven Peter MODULE_LICENSE("GPL v2");
1304