]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright 2003 PMC-Sierra | |
3 | * Author: Manish Lachwani (lachwani@pmc-sierra.com) | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. | |
9 | * | |
10 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
11 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
13 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
14 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
15 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
16 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
17 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
18 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
19 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along | |
22 | * with this program; if not, write to the Free Software Foundation, Inc., | |
23 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | */ | |
25 | ||
26 | #include <linux/config.h> | |
27 | #include <linux/types.h> | |
28 | #include <linux/pci.h> | |
29 | #include <linux/kernel.h> | |
30 | #include <linux/slab.h> | |
31 | #include <linux/version.h> | |
32 | #include <asm/pci.h> | |
33 | #include <asm/io.h> | |
34 | ||
35 | #include <linux/init.h> | |
36 | #include <asm/titan_dep.h> | |
37 | ||
38 | #ifdef CONFIG_HYPERTRANSPORT | |
39 | ||
40 | ||
41 | /* | |
42 | * This function check if the Hypertransport Link Initialization completed. If | |
43 | * it did, then proceed further with scanning bus #2 | |
44 | */ | |
45 | static __inline__ int check_titan_htlink(void) | |
46 | { | |
47 | u32 val; | |
48 | ||
49 | val = *(volatile uint32_t *)(RM9000x2_HTLINK_REG); | |
50 | if (val & 0x00000020) | |
51 | /* HT Link Initialization completed */ | |
52 | return 1; | |
53 | else | |
54 | return 0; | |
55 | } | |
56 | ||
57 | static int titan_ht_config_read_dword(struct pci_dev *device, | |
58 | int offset, u32* val) | |
59 | { | |
60 | int dev, bus, func; | |
61 | uint32_t address_reg, data_reg; | |
62 | uint32_t address; | |
63 | ||
64 | bus = device->bus->number; | |
65 | dev = PCI_SLOT(device->devfn); | |
66 | func = PCI_FUNC(device->devfn); | |
67 | ||
68 | /* XXX Need to change the Bus # */ | |
69 | if (bus > 2) | |
70 | address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) | | |
71 | 0x80000000 | 0x1; | |
72 | else | |
73 | address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000; | |
74 | ||
75 | address_reg = RM9000x2_OCD_HTCFGA; | |
76 | data_reg = RM9000x2_OCD_HTCFGD; | |
77 | ||
78 | RM9K_WRITE(address_reg, address); | |
79 | RM9K_READ(data_reg, val); | |
80 | ||
81 | return PCIBIOS_SUCCESSFUL; | |
82 | } | |
83 | ||
84 | ||
85 | static int titan_ht_config_read_word(struct pci_dev *device, | |
86 | int offset, u16* val) | |
87 | { | |
88 | int dev, bus, func; | |
89 | uint32_t address_reg, data_reg; | |
90 | uint32_t address; | |
91 | ||
92 | bus = device->bus->number; | |
93 | dev = PCI_SLOT(device->devfn); | |
94 | func = PCI_FUNC(device->devfn); | |
95 | ||
96 | /* XXX Need to change the Bus # */ | |
97 | if (bus > 2) | |
98 | address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) | | |
99 | 0x80000000 | 0x1; | |
100 | else | |
101 | address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000; | |
102 | ||
103 | address_reg = RM9000x2_OCD_HTCFGA; | |
104 | data_reg = RM9000x2_OCD_HTCFGD; | |
105 | ||
106 | if ((offset & 0x3) == 0) | |
107 | offset = 0x2; | |
108 | else | |
109 | offset = 0x0; | |
110 | ||
111 | RM9K_WRITE(address_reg, address); | |
112 | RM9K_READ_16(data_reg + offset, val); | |
113 | ||
114 | return PCIBIOS_SUCCESSFUL; | |
115 | } | |
116 | ||
117 | ||
118 | u32 longswap(unsigned long l) | |
119 | { | |
120 | unsigned char b1,b2,b3,b4; | |
121 | ||
122 | b1 = l&255; | |
123 | b2 = (l>>8)&255; | |
124 | b3 = (l>>16)&255; | |
125 | b4 = (l>>24)&255; | |
126 | ||
127 | return ((b1<<24) + (b2<<16) + (b3<<8) + b4); | |
128 | } | |
129 | ||
130 | ||
131 | static int titan_ht_config_read_byte(struct pci_dev *device, | |
132 | int offset, u8* val) | |
133 | { | |
134 | int dev, bus, func; | |
135 | uint32_t address_reg, data_reg; | |
136 | uint32_t address; | |
137 | int offset1; | |
138 | ||
139 | bus = device->bus->number; | |
140 | dev = PCI_SLOT(device->devfn); | |
141 | func = PCI_FUNC(device->devfn); | |
142 | ||
143 | /* XXX Need to change the Bus # */ | |
144 | if (bus > 2) | |
145 | address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) | | |
146 | 0x80000000 | 0x1; | |
147 | else | |
148 | address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000; | |
149 | ||
150 | address_reg = RM9000x2_OCD_HTCFGA; | |
151 | data_reg = RM9000x2_OCD_HTCFGD; | |
152 | ||
153 | RM9K_WRITE(address_reg, address); | |
154 | ||
155 | if ((offset & 0x3) == 0) { | |
156 | offset1 = 0x3; | |
157 | } | |
158 | if ((offset & 0x3) == 1) { | |
159 | offset1 = 0x2; | |
160 | } | |
161 | if ((offset & 0x3) == 2) { | |
162 | offset1 = 0x1; | |
163 | } | |
164 | if ((offset & 0x3) == 3) { | |
165 | offset1 = 0x0; | |
166 | } | |
167 | RM9K_READ_8(data_reg + offset1, val); | |
168 | ||
169 | return PCIBIOS_SUCCESSFUL; | |
170 | } | |
171 | ||
172 | ||
173 | static int titan_ht_config_write_dword(struct pci_dev *device, | |
174 | int offset, u8 val) | |
175 | { | |
176 | int dev, bus, func; | |
177 | uint32_t address_reg, data_reg; | |
178 | uint32_t address; | |
179 | ||
180 | bus = device->bus->number; | |
181 | dev = PCI_SLOT(device->devfn); | |
182 | func = PCI_FUNC(device->devfn); | |
183 | ||
184 | /* XXX Need to change the Bus # */ | |
185 | if (bus > 2) | |
186 | address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) | | |
187 | 0x80000000 | 0x1; | |
188 | else | |
189 | address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000; | |
190 | ||
191 | address_reg = RM9000x2_OCD_HTCFGA; | |
192 | data_reg = RM9000x2_OCD_HTCFGD; | |
193 | ||
194 | RM9K_WRITE(address_reg, address); | |
195 | RM9K_WRITE(data_reg, val); | |
196 | ||
197 | return PCIBIOS_SUCCESSFUL; | |
198 | } | |
199 | ||
200 | static int titan_ht_config_write_word(struct pci_dev *device, | |
201 | int offset, u8 val) | |
202 | { | |
203 | int dev, bus, func; | |
204 | uint32_t address_reg, data_reg; | |
205 | uint32_t address; | |
206 | ||
207 | bus = device->bus->number; | |
208 | dev = PCI_SLOT(device->devfn); | |
209 | func = PCI_FUNC(device->devfn); | |
210 | ||
211 | /* XXX Need to change the Bus # */ | |
212 | if (bus > 2) | |
213 | address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) | | |
214 | 0x80000000 | 0x1; | |
215 | else | |
216 | address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000; | |
217 | ||
218 | address_reg = RM9000x2_OCD_HTCFGA; | |
219 | data_reg = RM9000x2_OCD_HTCFGD; | |
220 | ||
221 | if ((offset & 0x3) == 0) | |
222 | offset = 0x2; | |
223 | else | |
224 | offset = 0x0; | |
225 | ||
226 | RM9K_WRITE(address_reg, address); | |
227 | RM9K_WRITE_16(data_reg + offset, val); | |
228 | ||
229 | return PCIBIOS_SUCCESSFUL; | |
230 | } | |
231 | ||
232 | static int titan_ht_config_write_byte(struct pci_dev *device, | |
233 | int offset, u8 val) | |
234 | { | |
235 | int dev, bus, func; | |
236 | uint32_t address_reg, data_reg; | |
237 | uint32_t address; | |
238 | int offset1; | |
239 | ||
240 | bus = device->bus->number; | |
241 | dev = PCI_SLOT(device->devfn); | |
242 | func = PCI_FUNC(device->devfn); | |
243 | ||
244 | /* XXX Need to change the Bus # */ | |
245 | if (bus > 2) | |
246 | address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) | | |
247 | 0x80000000 | 0x1; | |
248 | else | |
249 | address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000; | |
250 | ||
251 | address_reg = RM9000x2_OCD_HTCFGA; | |
252 | data_reg = RM9000x2_OCD_HTCFGD; | |
253 | ||
254 | RM9K_WRITE(address_reg, address); | |
255 | ||
256 | if ((offset & 0x3) == 0) { | |
257 | offset1 = 0x3; | |
258 | } | |
259 | if ((offset & 0x3) == 1) { | |
260 | offset1 = 0x2; | |
261 | } | |
262 | if ((offset & 0x3) == 2) { | |
263 | offset1 = 0x1; | |
264 | } | |
265 | if ((offset & 0x3) == 3) { | |
266 | offset1 = 0x0; | |
267 | } | |
268 | ||
269 | RM9K_WRITE_8(data_reg + offset1, val); | |
270 | return PCIBIOS_SUCCESSFUL; | |
271 | } | |
272 | ||
273 | ||
274 | static void titan_pcibios_set_master(struct pci_dev *dev) | |
275 | { | |
276 | u16 cmd; | |
277 | int bus = dev->bus->number; | |
278 | ||
279 | if (check_titan_htlink()) | |
280 | titan_ht_config_read_word(dev, PCI_COMMAND, &cmd); | |
281 | ||
282 | cmd |= PCI_COMMAND_MASTER; | |
283 | ||
284 | if (check_titan_htlink()) | |
285 | titan_ht_config_write_word(dev, PCI_COMMAND, cmd); | |
286 | } | |
287 | ||
288 | ||
289 | int pcibios_enable_resources(struct pci_dev *dev) | |
290 | { | |
291 | u16 cmd, old_cmd; | |
292 | u8 tmp1; | |
293 | int idx; | |
294 | struct resource *r; | |
295 | int bus = dev->bus->number; | |
296 | ||
297 | if (check_titan_htlink()) | |
298 | titan_ht_config_read_word(dev, PCI_COMMAND, &cmd); | |
299 | ||
300 | old_cmd = cmd; | |
301 | for (idx = 0; idx < 6; idx++) { | |
302 | r = &dev->resource[idx]; | |
303 | if (!r->start && r->end) { | |
304 | printk(KERN_ERR | |
305 | "PCI: Device %s not available because of " | |
306 | "resource collisions\n", pci_name(dev)); | |
307 | return -EINVAL; | |
308 | } | |
309 | if (r->flags & IORESOURCE_IO) | |
310 | cmd |= PCI_COMMAND_IO; | |
311 | if (r->flags & IORESOURCE_MEM) | |
312 | cmd |= PCI_COMMAND_MEMORY; | |
313 | } | |
314 | if (cmd != old_cmd) { | |
315 | if (check_titan_htlink()) | |
316 | titan_ht_config_write_word(dev, PCI_COMMAND, cmd); | |
317 | } | |
318 | ||
319 | if (check_titan_htlink()) | |
320 | titan_ht_config_read_byte(dev, PCI_CACHE_LINE_SIZE, &tmp1); | |
321 | ||
322 | if (tmp1 != 8) { | |
323 | printk(KERN_WARNING "PCI setting cache line size to 8 from " | |
324 | "%d\n", tmp1); | |
325 | } | |
326 | ||
327 | if (check_titan_htlink()) | |
328 | titan_ht_config_write_byte(dev, PCI_CACHE_LINE_SIZE, 8); | |
329 | ||
330 | if (check_titan_htlink()) | |
331 | titan_ht_config_read_byte(dev, PCI_LATENCY_TIMER, &tmp1); | |
332 | ||
333 | if (tmp1 < 32 || tmp1 == 0xff) { | |
334 | printk(KERN_WARNING "PCI setting latency timer to 32 from %d\n", | |
335 | tmp1); | |
336 | } | |
337 | ||
338 | if (check_titan_htlink()) | |
339 | titan_ht_config_write_byte(dev, PCI_LATENCY_TIMER, 32); | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | ||
345 | int pcibios_enable_device(struct pci_dev *dev, int mask) | |
346 | { | |
347 | return pcibios_enable_resources(dev); | |
348 | } | |
349 | ||
350 | ||
351 | ||
352 | void pcibios_update_resource(struct pci_dev *dev, struct resource *root, | |
353 | struct resource *res, int resource) | |
354 | { | |
355 | u32 new, check; | |
356 | int reg; | |
357 | ||
358 | return; | |
359 | ||
360 | new = res->start | (res->flags & PCI_REGION_FLAG_MASK); | |
361 | if (resource < 6) { | |
362 | reg = PCI_BASE_ADDRESS_0 + 4 * resource; | |
363 | } else if (resource == PCI_ROM_RESOURCE) { | |
364 | res->flags |= IORESOURCE_ROM_ENABLE; | |
365 | reg = dev->rom_base_reg; | |
366 | } else { | |
367 | /* | |
368 | * Somebody might have asked allocation of a non-standard | |
369 | * resource | |
370 | */ | |
371 | return; | |
372 | } | |
373 | ||
374 | pci_write_config_dword(dev, reg, new); | |
375 | pci_read_config_dword(dev, reg, &check); | |
376 | if ((new ^ check) & | |
377 | ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : | |
378 | PCI_BASE_ADDRESS_MEM_MASK)) { | |
379 | printk(KERN_ERR "PCI: Error while updating region " | |
380 | "%s/%d (%08x != %08x)\n", pci_name(dev), resource, | |
381 | new, check); | |
382 | } | |
383 | } | |
384 | ||
385 | ||
386 | void pcibios_align_resource(void *data, struct resource *res, | |
387 | unsigned long size, unsigned long align) | |
388 | { | |
389 | struct pci_dev *dev = data; | |
390 | ||
391 | if (res->flags & IORESOURCE_IO) { | |
392 | unsigned long start = res->start; | |
393 | ||
394 | /* We need to avoid collisions with `mirrored' VGA ports | |
395 | and other strange ISA hardware, so we always want the | |
396 | addresses kilobyte aligned. */ | |
397 | if (size > 0x100) { | |
398 | printk(KERN_ERR "PCI: I/O Region %s/%d too large" | |
399 | " (%ld bytes)\n", pci_name(dev), | |
400 | dev->resource - res, size); | |
401 | } | |
402 | ||
403 | start = (start + 1024 - 1) & ~(1024 - 1); | |
404 | res->start = start; | |
405 | } | |
406 | } | |
407 | ||
408 | struct pci_ops titan_pci_ops = { | |
409 | titan_ht_config_read_byte, | |
410 | titan_ht_config_read_word, | |
411 | titan_ht_config_read_dword, | |
412 | titan_ht_config_write_byte, | |
413 | titan_ht_config_write_word, | |
414 | titan_ht_config_write_dword | |
415 | }; | |
416 | ||
417 | void __init pcibios_fixup_bus(struct pci_bus *c) | |
418 | { | |
419 | titan_ht_pcibios_fixup_bus(c); | |
420 | } | |
421 | ||
422 | void __init pcibios_init(void) | |
423 | { | |
424 | ||
425 | /* Reset PCI I/O and PCI MEM values */ | |
426 | /* XXX Need to add the proper values here */ | |
427 | ioport_resource.start = 0xe0000000; | |
428 | ioport_resource.end = 0xe0000000 + 0x20000000 - 1; | |
429 | iomem_resource.start = 0xc0000000; | |
430 | iomem_resource.end = 0xc0000000 + 0x20000000 - 1; | |
431 | ||
432 | /* XXX Need to add bus values */ | |
433 | pci_scan_bus(2, &titan_pci_ops, NULL); | |
434 | pci_scan_bus(3, &titan_pci_ops, NULL); | |
435 | } | |
436 | ||
437 | /* | |
438 | * for parsing "pci=" kernel boot arguments. | |
439 | */ | |
440 | char *pcibios_setup(char *str) | |
441 | { | |
442 | printk(KERN_INFO "rr: pcibios_setup\n"); | |
443 | /* Nothing to do for now. */ | |
444 | ||
445 | return str; | |
446 | } | |
447 | ||
448 | unsigned __init int pcibios_assign_all_busses(void) | |
449 | { | |
450 | /* We want to use the PCI bus detection done by PMON */ | |
451 | return 0; | |
452 | } | |
453 | ||
454 | #endif /* CONFIG_HYPERTRANSPORT */ |