Commit | Line | Data |
---|---|---|
a61127c2 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
8c8f1c95 AC |
2 | /* |
3 | * Copyright (c) 2007, Intel Corporation. | |
4 | * All Rights Reserved. | |
5 | * | |
8c8f1c95 AC |
6 | * Authors: Thomas Hellstrom <thomas-at-tungstengraphics.com> |
7 | * Alan Cox <alan@linux.intel.com> | |
8 | */ | |
9 | ||
f2d061ed | 10 | #include "gem.h" /* TODO: for struct psb_gem_object, see psb_gtt_restore() */ |
0c7b178a | 11 | #include "psb_drv.h" |
8c8f1c95 AC |
12 | |
13 | ||
14 | /* | |
15 | * GTT resource allocator - manage page mappings in GTT space | |
16 | */ | |
17 | ||
3c101135 TZ |
18 | int psb_gtt_allocate_resource(struct drm_psb_private *pdev, struct resource *res, |
19 | const char *name, resource_size_t size, resource_size_t align, | |
20 | bool stolen, u32 *offset) | |
21 | { | |
22 | struct resource *root = pdev->gtt_mem; | |
23 | resource_size_t start, end; | |
24 | int ret; | |
25 | ||
26 | if (stolen) { | |
27 | /* The start of the GTT is backed by stolen pages. */ | |
28 | start = root->start; | |
29 | end = root->start + pdev->gtt.stolen_size - 1; | |
30 | } else { | |
31 | /* The rest is backed by system pages. */ | |
32 | start = root->start + pdev->gtt.stolen_size; | |
33 | end = root->end; | |
34 | } | |
35 | ||
36 | res->name = name; | |
37 | ret = allocate_resource(root, res, size, start, end, align, NULL, NULL); | |
38 | if (ret) | |
39 | return ret; | |
40 | *offset = res->start - root->start; | |
41 | ||
42 | return 0; | |
43 | } | |
44 | ||
8c8f1c95 AC |
45 | /** |
46 | * psb_gtt_mask_pte - generate GTT pte entry | |
47 | * @pfn: page number to encode | |
48 | * @type: type of memory in the GTT | |
49 | * | |
50 | * Set the GTT entry for the appropriate memory type. | |
51 | */ | |
d339386c | 52 | uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) |
8c8f1c95 AC |
53 | { |
54 | uint32_t mask = PSB_PTE_VALID; | |
55 | ||
398b4706 AC |
56 | /* Ensure we explode rather than put an invalid low mapping of |
57 | a high mapping page into the gtt */ | |
58 | BUG_ON(pfn & ~(0xFFFFFFFF >> PAGE_SHIFT)); | |
59 | ||
8c8f1c95 AC |
60 | if (type & PSB_MMU_CACHED_MEMORY) |
61 | mask |= PSB_PTE_CACHED; | |
62 | if (type & PSB_MMU_RO_MEMORY) | |
63 | mask |= PSB_PTE_RO; | |
64 | if (type & PSB_MMU_WO_MEMORY) | |
65 | mask |= PSB_PTE_WO; | |
66 | ||
67 | return (pfn << PAGE_SHIFT) | mask; | |
68 | } | |
69 | ||
e1f80341 | 70 | static u32 __iomem *psb_gtt_entry(struct drm_psb_private *pdev, const struct resource *res) |
8c8f1c95 | 71 | { |
e1f80341 | 72 | unsigned long offset = res->start - pdev->gtt_mem->start; |
8c8f1c95 | 73 | |
e1f80341 | 74 | return pdev->gtt_map + (offset >> PAGE_SHIFT); |
8c8f1c95 AC |
75 | } |
76 | ||
14e92dd1 | 77 | /* Acquires GTT mutex internally. */ |
e1f80341 TZ |
78 | void psb_gtt_insert_pages(struct drm_psb_private *pdev, const struct resource *res, |
79 | struct page **pages) | |
8c8f1c95 | 80 | { |
e1f80341 | 81 | resource_size_t npages, i; |
eab37607 KS |
82 | u32 __iomem *gtt_slot; |
83 | u32 pte; | |
8c8f1c95 | 84 | |
14e92dd1 TZ |
85 | mutex_lock(&pdev->gtt_mutex); |
86 | ||
e1f80341 | 87 | /* Write our page entries into the GTT itself */ |
8c8f1c95 | 88 | |
e1f80341 TZ |
89 | npages = resource_size(res) >> PAGE_SHIFT; |
90 | gtt_slot = psb_gtt_entry(pdev, res); | |
8c8f1c95 | 91 | |
e1f80341 TZ |
92 | for (i = 0; i < npages; ++i, ++gtt_slot) { |
93 | pte = psb_gtt_mask_pte(page_to_pfn(pages[i]), PSB_MMU_CACHED_MEMORY); | |
94 | iowrite32(pte, gtt_slot); | |
8c8f1c95 | 95 | } |
ebc7d647 | 96 | |
8c8f1c95 AC |
97 | /* Make sure all the entries are set before we return */ |
98 | ioread32(gtt_slot - 1); | |
14e92dd1 TZ |
99 | |
100 | mutex_unlock(&pdev->gtt_mutex); | |
8c8f1c95 AC |
101 | } |
102 | ||
14e92dd1 | 103 | /* Acquires GTT mutex internally. */ |
e1f80341 | 104 | void psb_gtt_remove_pages(struct drm_psb_private *pdev, const struct resource *res) |
8c8f1c95 | 105 | { |
e1f80341 | 106 | resource_size_t npages, i; |
eab37607 KS |
107 | u32 __iomem *gtt_slot; |
108 | u32 pte; | |
8c8f1c95 | 109 | |
14e92dd1 TZ |
110 | mutex_lock(&pdev->gtt_mutex); |
111 | ||
e1f80341 TZ |
112 | /* Install scratch page for the resource */ |
113 | ||
114 | pte = psb_gtt_mask_pte(page_to_pfn(pdev->scratch_page), PSB_MMU_CACHED_MEMORY); | |
8c8f1c95 | 115 | |
e1f80341 TZ |
116 | npages = resource_size(res) >> PAGE_SHIFT; |
117 | gtt_slot = psb_gtt_entry(pdev, res); | |
8c8f1c95 | 118 | |
e1f80341 TZ |
119 | for (i = 0; i < npages; ++i, ++gtt_slot) |
120 | iowrite32(pte, gtt_slot); | |
121 | ||
122 | /* Make sure all the entries are set before we return */ | |
8c8f1c95 | 123 | ioread32(gtt_slot - 1); |
14e92dd1 TZ |
124 | |
125 | mutex_unlock(&pdev->gtt_mutex); | |
8c8f1c95 AC |
126 | } |
127 | ||
5169f359 | 128 | static int psb_gtt_enable(struct drm_psb_private *dev_priv) |
8c8f1c95 | 129 | { |
5169f359 | 130 | struct drm_device *dev = &dev_priv->dev; |
a2c68495 | 131 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
5169f359 | 132 | int ret; |
8c8f1c95 | 133 | |
5169f359 TZ |
134 | ret = pci_read_config_word(pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); |
135 | if (ret) | |
136 | return pcibios_err_to_errno(ret); | |
137 | ret = pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); | |
138 | if (ret) | |
139 | return pcibios_err_to_errno(ret); | |
140 | ||
141 | dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); | |
142 | PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); | |
143 | ||
144 | (void)PSB_RVDC32(PSB_PGETBL_CTL); | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | static void psb_gtt_disable(struct drm_psb_private *dev_priv) | |
150 | { | |
151 | struct drm_device *dev = &dev_priv->dev; | |
152 | struct pci_dev *pdev = to_pci_dev(dev->dev); | |
6069fd81 TZ |
153 | |
154 | pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl); | |
155 | PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); | |
5169f359 | 156 | |
6069fd81 | 157 | (void)PSB_RVDC32(PSB_PGETBL_CTL); |
5169f359 | 158 | } |
6069fd81 | 159 | |
5169f359 TZ |
160 | void psb_gtt_fini(struct drm_device *dev) |
161 | { | |
162 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); | |
163 | ||
164 | iounmap(dev_priv->gtt_map); | |
165 | psb_gtt_disable(dev_priv); | |
6069fd81 | 166 | mutex_destroy(&dev_priv->gtt_mutex); |
8c8f1c95 AC |
167 | } |
168 | ||
60a78f9e TZ |
169 | /* Clear GTT. Use a scratch page to avoid accidents or scribbles. */ |
170 | static void psb_gtt_clear(struct drm_psb_private *pdev) | |
171 | { | |
172 | resource_size_t pfn_base; | |
173 | unsigned long i; | |
174 | uint32_t pte; | |
175 | ||
176 | pfn_base = page_to_pfn(pdev->scratch_page); | |
177 | pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY); | |
178 | ||
179 | for (i = 0; i < pdev->gtt.gtt_pages; ++i) | |
180 | iowrite32(pte, pdev->gtt_map + i); | |
181 | ||
182 | (void)ioread32(pdev->gtt_map + i - 1); | |
183 | } | |
184 | ||
07739597 | 185 | static void psb_gtt_init_ranges(struct drm_psb_private *dev_priv) |
8c8f1c95 | 186 | { |
07739597 | 187 | struct drm_device *dev = &dev_priv->dev; |
a2c68495 | 188 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
5169f359 | 189 | struct psb_gtt *pg = &dev_priv->gtt; |
07739597 TZ |
190 | resource_size_t gtt_phys_start, mmu_gatt_start, gtt_start, gtt_pages, |
191 | gatt_start, gatt_pages; | |
192 | struct resource *gtt_mem; | |
8c8f1c95 AC |
193 | |
194 | /* The root resource we allocate address space from */ | |
07739597 | 195 | gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; |
8c8f1c95 AC |
196 | |
197 | /* | |
07739597 TZ |
198 | * The video MMU has a HW bug when accessing 0x0d0000000. Make |
199 | * GATT start at 0x0e0000000. This doesn't actually matter for | |
200 | * us now, but maybe will if the video acceleration ever gets | |
201 | * opened up. | |
8c8f1c95 | 202 | */ |
07739597 TZ |
203 | mmu_gatt_start = 0xe0000000; |
204 | ||
205 | gtt_start = pci_resource_start(pdev, PSB_GTT_RESOURCE); | |
206 | gtt_pages = pci_resource_len(pdev, PSB_GTT_RESOURCE) >> PAGE_SHIFT; | |
8c8f1c95 | 207 | |
055bf38d | 208 | /* CDV doesn't report this. In which case the system has 64 gtt pages */ |
07739597 | 209 | if (!gtt_start || !gtt_pages) { |
055bf38d | 210 | dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n"); |
8c8f1c95 | 211 | gtt_pages = 64; |
07739597 | 212 | gtt_start = dev_priv->pge_ctl; |
8c8f1c95 AC |
213 | } |
214 | ||
07739597 TZ |
215 | gatt_start = pci_resource_start(pdev, PSB_GATT_RESOURCE); |
216 | gatt_pages = pci_resource_len(pdev, PSB_GATT_RESOURCE) >> PAGE_SHIFT; | |
8c8f1c95 | 217 | |
07739597 | 218 | if (!gatt_pages || !gatt_start) { |
8c8f1c95 | 219 | static struct resource fudge; /* Preferably peppermint */ |
07739597 TZ |
220 | |
221 | /* | |
222 | * This can occur on CDV systems. Fudge it in this case. We | |
223 | * really don't care what imaginary space is being allocated | |
224 | * at this point. | |
225 | */ | |
055bf38d | 226 | dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n"); |
07739597 TZ |
227 | gatt_start = 0x40000000; |
228 | gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; | |
229 | ||
230 | /* | |
231 | * This is a little confusing but in fact the GTT is providing | |
232 | * a view from the GPU into memory and not vice versa. As such | |
233 | * this is really allocating space that is not the same as the | |
234 | * CPU address space on CDV. | |
235 | */ | |
8c8f1c95 AC |
236 | fudge.start = 0x40000000; |
237 | fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; | |
238 | fudge.name = "fudge"; | |
239 | fudge.flags = IORESOURCE_MEM; | |
07739597 TZ |
240 | |
241 | gtt_mem = &fudge; | |
242 | } else { | |
243 | gtt_mem = &pdev->resource[PSB_GATT_RESOURCE]; | |
8c8f1c95 AC |
244 | } |
245 | ||
07739597 TZ |
246 | pg->gtt_phys_start = gtt_phys_start; |
247 | pg->mmu_gatt_start = mmu_gatt_start; | |
248 | pg->gtt_start = gtt_start; | |
8c8f1c95 | 249 | pg->gtt_pages = gtt_pages; |
07739597 TZ |
250 | pg->gatt_start = gatt_start; |
251 | pg->gatt_pages = gatt_pages; | |
252 | dev_priv->gtt_mem = gtt_mem; | |
253 | } | |
254 | ||
255 | int psb_gtt_init(struct drm_device *dev) | |
256 | { | |
257 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); | |
258 | struct psb_gtt *pg = &dev_priv->gtt; | |
259 | int ret; | |
260 | ||
261 | mutex_init(&dev_priv->gtt_mutex); | |
262 | ||
263 | ret = psb_gtt_enable(dev_priv); | |
264 | if (ret) | |
265 | goto err_mutex_destroy; | |
8c8f1c95 | 266 | |
07739597 TZ |
267 | psb_gtt_init_ranges(dev_priv); |
268 | ||
269 | dev_priv->gtt_map = ioremap(pg->gtt_phys_start, pg->gtt_pages << PAGE_SHIFT); | |
8c8f1c95 AC |
270 | if (!dev_priv->gtt_map) { |
271 | dev_err(dev->dev, "Failure to map gtt.\n"); | |
272 | ret = -ENOMEM; | |
5169f359 | 273 | goto err_psb_gtt_disable; |
8c8f1c95 AC |
274 | } |
275 | ||
d00f44dd TZ |
276 | psb_gtt_clear(dev_priv); |
277 | ||
278 | return 0; | |
279 | ||
5169f359 TZ |
280 | err_psb_gtt_disable: |
281 | psb_gtt_disable(dev_priv); | |
282 | err_mutex_destroy: | |
d00f44dd TZ |
283 | mutex_destroy(&dev_priv->gtt_mutex); |
284 | return ret; | |
285 | } | |
286 | ||
42ceddb6 | 287 | int psb_gtt_resume(struct drm_device *dev) |
97bd66c4 TZ |
288 | { |
289 | struct drm_psb_private *dev_priv = to_drm_psb_private(dev); | |
5169f359 | 290 | struct psb_gtt *pg = &dev_priv->gtt; |
07739597 | 291 | unsigned int old_gtt_pages = pg->gtt_pages; |
d00f44dd | 292 | int ret; |
97bd66c4 | 293 | |
97bd66c4 | 294 | /* Enable the GTT */ |
5169f359 TZ |
295 | ret = psb_gtt_enable(dev_priv); |
296 | if (ret) | |
297 | return ret; | |
97bd66c4 | 298 | |
07739597 | 299 | psb_gtt_init_ranges(dev_priv); |
97bd66c4 | 300 | |
07739597 | 301 | if (old_gtt_pages != pg->gtt_pages) { |
97bd66c4 | 302 | dev_err(dev->dev, "GTT resume error.\n"); |
07739597 | 303 | ret = -ENODEV; |
5169f359 | 304 | goto err_psb_gtt_disable; |
97bd66c4 TZ |
305 | } |
306 | ||
97bd66c4 | 307 | psb_gtt_clear(dev_priv); |
97bd66c4 | 308 | |
5169f359 TZ |
309 | err_psb_gtt_disable: |
310 | psb_gtt_disable(dev_priv); | |
97bd66c4 TZ |
311 | return ret; |
312 | } |