Commit | Line | Data |
---|---|---|
b6c02715 JR |
1 | /* |
2 | * Copyright (C) 2007-2008 Advanced Micro Devices, Inc. | |
3 | * Author: Joerg Roedel <joerg.roedel@amd.com> | |
4 | * Leo Duran <leo.duran@amd.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published | |
8 | * by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | ||
20 | #include <linux/pci.h> | |
21 | #include <linux/gfp.h> | |
22 | #include <linux/bitops.h> | |
23 | #include <linux/scatterlist.h> | |
24 | #include <linux/iommu-helper.h> | |
25 | #include <asm/proto.h> | |
26 | #include <asm/gart.h> | |
27 | #include <asm/amd_iommu_types.h> | |
28 | ||
29 | #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) | |
30 | ||
31 | #define to_pages(addr, size) \ | |
32 | (round_up(((addr) & ~PAGE_MASK) + (size), PAGE_SIZE) >> PAGE_SHIFT) | |
33 | ||
34 | static DEFINE_RWLOCK(amd_iommu_devtable_lock); | |
35 | ||
36 | struct command { | |
37 | u32 data[4]; | |
38 | }; | |
39 | ||
a19ae1ec JR |
40 | static int __iommu_queue_command(struct amd_iommu *iommu, struct command *cmd) |
41 | { | |
42 | u32 tail, head; | |
43 | u8 *target; | |
44 | ||
45 | tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); | |
46 | target = (iommu->cmd_buf + tail); | |
47 | memcpy_toio(target, cmd, sizeof(*cmd)); | |
48 | tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size; | |
49 | head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET); | |
50 | if (tail == head) | |
51 | return -ENOMEM; | |
52 | writel(tail, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | static int iommu_queue_command(struct amd_iommu *iommu, struct command *cmd) | |
58 | { | |
59 | unsigned long flags; | |
60 | int ret; | |
61 | ||
62 | spin_lock_irqsave(&iommu->lock, flags); | |
63 | ret = __iommu_queue_command(iommu, cmd); | |
64 | spin_unlock_irqrestore(&iommu->lock, flags); | |
65 | ||
66 | return ret; | |
67 | } | |
68 | ||
69 | static int iommu_completion_wait(struct amd_iommu *iommu) | |
70 | { | |
71 | int ret; | |
72 | struct command cmd; | |
73 | volatile u64 ready = 0; | |
74 | unsigned long ready_phys = virt_to_phys(&ready); | |
75 | ||
76 | memset(&cmd, 0, sizeof(cmd)); | |
77 | cmd.data[0] = LOW_U32(ready_phys) | CMD_COMPL_WAIT_STORE_MASK; | |
78 | cmd.data[1] = HIGH_U32(ready_phys); | |
79 | cmd.data[2] = 1; /* value written to 'ready' */ | |
80 | CMD_SET_TYPE(&cmd, CMD_COMPL_WAIT); | |
81 | ||
82 | iommu->need_sync = 0; | |
83 | ||
84 | ret = iommu_queue_command(iommu, &cmd); | |
85 | ||
86 | if (ret) | |
87 | return ret; | |
88 | ||
89 | while (!ready) | |
90 | cpu_relax(); | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static int iommu_queue_inv_dev_entry(struct amd_iommu *iommu, u16 devid) | |
96 | { | |
97 | struct command cmd; | |
98 | ||
99 | BUG_ON(iommu == NULL); | |
100 | ||
101 | memset(&cmd, 0, sizeof(cmd)); | |
102 | CMD_SET_TYPE(&cmd, CMD_INV_DEV_ENTRY); | |
103 | cmd.data[0] = devid; | |
104 | ||
105 | iommu->need_sync = 1; | |
106 | ||
107 | return iommu_queue_command(iommu, &cmd); | |
108 | } | |
109 | ||
110 | static int iommu_queue_inv_iommu_pages(struct amd_iommu *iommu, | |
111 | u64 address, u16 domid, int pde, int s) | |
112 | { | |
113 | struct command cmd; | |
114 | ||
115 | memset(&cmd, 0, sizeof(cmd)); | |
116 | address &= PAGE_MASK; | |
117 | CMD_SET_TYPE(&cmd, CMD_INV_IOMMU_PAGES); | |
118 | cmd.data[1] |= domid; | |
119 | cmd.data[2] = LOW_U32(address); | |
120 | cmd.data[3] = HIGH_U32(address); | |
121 | if (s) | |
122 | cmd.data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK; | |
123 | if (pde) | |
124 | cmd.data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK; | |
125 | ||
126 | iommu->need_sync = 1; | |
127 | ||
128 | return iommu_queue_command(iommu, &cmd); | |
129 | } | |
130 | ||
131 | static int iommu_flush_pages(struct amd_iommu *iommu, u16 domid, | |
132 | u64 address, size_t size) | |
133 | { | |
134 | int i; | |
135 | unsigned pages = to_pages(address, size); | |
136 | ||
137 | address &= PAGE_MASK; | |
138 | ||
139 | for (i = 0; i < pages; ++i) { | |
140 | iommu_queue_inv_iommu_pages(iommu, address, domid, 0, 0); | |
141 | address += PAGE_SIZE; | |
142 | } | |
143 | ||
144 | return 0; | |
145 | } | |
b6c02715 | 146 |