Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/ppc/platforms/pmac_feature.c | |
3 | * | |
4 | * Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au) | |
5 | * Ben. Herrenschmidt (benh@kernel.crashing.org) | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation; either version | |
10 | * 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * TODO: | |
13 | * | |
14 | * - Replace mdelay with some schedule loop if possible | |
15 | * - Shorten some obfuscated delays on some routines (like modem | |
16 | * power) | |
17 | * - Refcount some clocks (see darwin) | |
18 | * - Split split split... | |
19 | * | |
20 | */ | |
21 | #include <linux/config.h> | |
22 | #include <linux/types.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/delay.h> | |
25 | #include <linux/kernel.h> | |
26 | #include <linux/sched.h> | |
27 | #include <linux/spinlock.h> | |
28 | #include <linux/adb.h> | |
29 | #include <linux/pmu.h> | |
30 | #include <linux/ioport.h> | |
31 | #include <linux/pci.h> | |
32 | #include <asm/sections.h> | |
33 | #include <asm/errno.h> | |
34 | #include <asm/keylargo.h> | |
35 | #include <asm/uninorth.h> | |
36 | #include <asm/io.h> | |
37 | #include <asm/prom.h> | |
38 | #include <asm/machdep.h> | |
39 | #include <asm/pmac_feature.h> | |
40 | #include <asm/dbdma.h> | |
41 | #include <asm/pci-bridge.h> | |
42 | #include <asm/pmac_low_i2c.h> | |
43 | ||
44 | #undef DEBUG_FEATURE | |
45 | ||
46 | #ifdef DEBUG_FEATURE | |
47 | #define DBG(fmt...) printk(KERN_DEBUG fmt) | |
48 | #else | |
49 | #define DBG(fmt...) | |
50 | #endif | |
51 | ||
52 | /* | |
53 | * We use a single global lock to protect accesses. Each driver has | |
54 | * to take care of its own locking | |
55 | */ | |
56 | static DEFINE_SPINLOCK(feature_lock __pmacdata); | |
57 | ||
58 | #define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); | |
59 | #define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); | |
60 | ||
61 | ||
62 | /* | |
63 | * Instance of some macio stuffs | |
64 | */ | |
65 | struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata; | |
66 | ||
7bbd8277 | 67 | struct macio_chip* __pmac macio_find(struct device_node* child, int type) |
1da177e4 LT |
68 | { |
69 | while(child) { | |
70 | int i; | |
71 | ||
72 | for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++) | |
73 | if (child == macio_chips[i].of_node && | |
74 | (!type || macio_chips[i].type == type)) | |
75 | return &macio_chips[i]; | |
76 | child = child->parent; | |
77 | } | |
78 | return NULL; | |
79 | } | |
7bbd8277 | 80 | EXPORT_SYMBOL_GPL(macio_find); |
1da177e4 LT |
81 | |
82 | static const char* macio_names[] __pmacdata = | |
83 | { | |
84 | "Unknown", | |
85 | "Grand Central", | |
86 | "OHare", | |
87 | "OHareII", | |
88 | "Heathrow", | |
89 | "Gatwick", | |
90 | "Paddington", | |
91 | "Keylargo", | |
92 | "Pangea", | |
93 | "Intrepid", | |
94 | "K2" | |
95 | }; | |
96 | ||
97 | ||
98 | ||
99 | /* | |
100 | * Uninorth reg. access. Note that Uni-N regs are big endian | |
101 | */ | |
102 | ||
103 | #define UN_REG(r) (uninorth_base + ((r) >> 2)) | |
104 | #define UN_IN(r) (in_be32(UN_REG(r))) | |
105 | #define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) | |
106 | #define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) | |
107 | #define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) | |
108 | ||
109 | static struct device_node* uninorth_node __pmacdata; | |
110 | static u32* uninorth_base __pmacdata; | |
111 | static u32 uninorth_rev __pmacdata; | |
112 | static void *u3_ht; | |
113 | ||
114 | extern struct device_node *k2_skiplist[2]; | |
115 | ||
116 | /* | |
117 | * For each motherboard family, we have a table of functions pointers | |
118 | * that handle the various features. | |
119 | */ | |
120 | ||
121 | typedef long (*feature_call)(struct device_node* node, long param, long value); | |
122 | ||
123 | struct feature_table_entry { | |
124 | unsigned int selector; | |
125 | feature_call function; | |
126 | }; | |
127 | ||
128 | struct pmac_mb_def | |
129 | { | |
130 | const char* model_string; | |
131 | const char* model_name; | |
132 | int model_id; | |
133 | struct feature_table_entry* features; | |
134 | unsigned long board_flags; | |
135 | }; | |
136 | static struct pmac_mb_def pmac_mb __pmacdata; | |
137 | ||
138 | /* | |
139 | * Here are the chip specific feature functions | |
140 | */ | |
141 | ||
142 | ||
143 | static long __pmac g5_read_gpio(struct device_node* node, long param, long value) | |
144 | { | |
145 | struct macio_chip* macio = &macio_chips[0]; | |
146 | ||
147 | return MACIO_IN8(param); | |
148 | } | |
149 | ||
150 | ||
151 | static long __pmac g5_write_gpio(struct device_node* node, long param, long value) | |
152 | { | |
153 | struct macio_chip* macio = &macio_chips[0]; | |
154 | ||
155 | MACIO_OUT8(param, (u8)(value & 0xff)); | |
156 | return 0; | |
157 | } | |
158 | ||
159 | static long __pmac g5_gmac_enable(struct device_node* node, long param, long value) | |
160 | { | |
161 | struct macio_chip* macio = &macio_chips[0]; | |
162 | unsigned long flags; | |
163 | ||
164 | if (node == NULL) | |
165 | return -ENODEV; | |
166 | ||
167 | LOCK(flags); | |
168 | if (value) { | |
169 | MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); | |
170 | mb(); | |
171 | k2_skiplist[0] = NULL; | |
172 | } else { | |
173 | k2_skiplist[0] = node; | |
174 | mb(); | |
175 | MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); | |
176 | } | |
177 | ||
178 | UNLOCK(flags); | |
179 | mdelay(1); | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | static long __pmac g5_fw_enable(struct device_node* node, long param, long value) | |
185 | { | |
186 | struct macio_chip* macio = &macio_chips[0]; | |
187 | unsigned long flags; | |
188 | ||
189 | if (node == NULL) | |
190 | return -ENODEV; | |
191 | ||
192 | LOCK(flags); | |
193 | if (value) { | |
194 | MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); | |
195 | mb(); | |
196 | k2_skiplist[1] = NULL; | |
197 | } else { | |
198 | k2_skiplist[1] = node; | |
199 | mb(); | |
200 | MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); | |
201 | } | |
202 | ||
203 | UNLOCK(flags); | |
204 | mdelay(1); | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
209 | static long __pmac g5_mpic_enable(struct device_node* node, long param, long value) | |
210 | { | |
211 | unsigned long flags; | |
212 | ||
213 | if (node->parent == NULL || strcmp(node->parent->name, "u3")) | |
214 | return 0; | |
215 | ||
216 | LOCK(flags); | |
217 | UN_BIS(U3_TOGGLE_REG, U3_MPIC_RESET | U3_MPIC_OUTPUT_ENABLE); | |
218 | UNLOCK(flags); | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static long __pmac g5_eth_phy_reset(struct device_node* node, long param, long value) | |
224 | { | |
225 | struct macio_chip* macio = &macio_chips[0]; | |
226 | struct device_node *phy; | |
227 | int need_reset; | |
228 | ||
229 | /* | |
230 | * We must not reset the combo PHYs, only the BCM5221 found in | |
231 | * the iMac G5. | |
232 | */ | |
233 | phy = of_get_next_child(node, NULL); | |
234 | if (!phy) | |
235 | return -ENODEV; | |
236 | need_reset = device_is_compatible(phy, "B5221"); | |
237 | of_node_put(phy); | |
238 | if (!need_reset) | |
239 | return 0; | |
240 | ||
241 | /* PHY reset is GPIO 29, not in device-tree unfortunately */ | |
242 | MACIO_OUT8(K2_GPIO_EXTINT_0 + 29, | |
243 | KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); | |
244 | /* Thankfully, this is now always called at a time when we can | |
245 | * schedule by sungem. | |
246 | */ | |
247 | msleep(10); | |
248 | MACIO_OUT8(K2_GPIO_EXTINT_0 + 29, 0); | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
7bbd8277 BH |
253 | static long __pmac g5_i2s_enable(struct device_node *node, long param, long value) |
254 | { | |
255 | /* Very crude implementation for now */ | |
256 | struct macio_chip* macio = &macio_chips[0]; | |
257 | unsigned long flags; | |
258 | ||
259 | if (value == 0) | |
260 | return 0; /* don't disable yet */ | |
261 | ||
262 | LOCK(flags); | |
263 | MACIO_BIS(KEYLARGO_FCR3, KL3_CLK45_ENABLE | KL3_CLK49_ENABLE | | |
264 | KL3_I2S0_CLK18_ENABLE); | |
265 | udelay(10); | |
266 | MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_I2S0_CELL_ENABLE | | |
267 | K2_FCR1_I2S0_CLK_ENABLE_BIT | K2_FCR1_I2S0_ENABLE); | |
268 | udelay(10); | |
269 | MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_I2S0_RESET); | |
270 | UNLOCK(flags); | |
271 | udelay(10); | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | ||
1da177e4 LT |
277 | #ifdef CONFIG_SMP |
278 | static long __pmac g5_reset_cpu(struct device_node* node, long param, long value) | |
279 | { | |
280 | unsigned int reset_io = 0; | |
281 | unsigned long flags; | |
282 | struct macio_chip* macio; | |
283 | struct device_node* np; | |
284 | ||
285 | macio = &macio_chips[0]; | |
286 | if (macio->type != macio_keylargo2) | |
287 | return -ENODEV; | |
288 | ||
289 | np = find_path_device("/cpus"); | |
290 | if (np == NULL) | |
291 | return -ENODEV; | |
292 | for (np = np->child; np != NULL; np = np->sibling) { | |
293 | u32* num = (u32 *)get_property(np, "reg", NULL); | |
294 | u32* rst = (u32 *)get_property(np, "soft-reset", NULL); | |
295 | if (num == NULL || rst == NULL) | |
296 | continue; | |
297 | if (param == *num) { | |
298 | reset_io = *rst; | |
299 | break; | |
300 | } | |
301 | } | |
302 | if (np == NULL || reset_io == 0) | |
303 | return -ENODEV; | |
304 | ||
305 | LOCK(flags); | |
306 | MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); | |
307 | (void)MACIO_IN8(reset_io); | |
308 | udelay(1); | |
309 | MACIO_OUT8(reset_io, 0); | |
310 | (void)MACIO_IN8(reset_io); | |
311 | UNLOCK(flags); | |
312 | ||
313 | return 0; | |
314 | } | |
315 | #endif /* CONFIG_SMP */ | |
316 | ||
317 | /* | |
318 | * This can be called from pmac_smp so isn't static | |
319 | * | |
320 | * This takes the second CPU off the bus on dual CPU machines | |
321 | * running UP | |
322 | */ | |
323 | void __pmac g5_phy_disable_cpu1(void) | |
324 | { | |
325 | UN_OUT(U3_API_PHY_CONFIG_1, 0); | |
326 | } | |
327 | ||
328 | static long __pmac generic_get_mb_info(struct device_node* node, long param, long value) | |
329 | { | |
330 | switch(param) { | |
331 | case PMAC_MB_INFO_MODEL: | |
332 | return pmac_mb.model_id; | |
333 | case PMAC_MB_INFO_FLAGS: | |
334 | return pmac_mb.board_flags; | |
335 | case PMAC_MB_INFO_NAME: | |
336 | /* hack hack hack... but should work */ | |
337 | *((const char **)value) = pmac_mb.model_name; | |
338 | return 0; | |
339 | } | |
340 | return -EINVAL; | |
341 | } | |
342 | ||
343 | ||
344 | /* | |
345 | * Table definitions | |
346 | */ | |
347 | ||
348 | /* Used on any machine | |
349 | */ | |
350 | static struct feature_table_entry any_features[] __pmacdata = { | |
351 | { PMAC_FTR_GET_MB_INFO, generic_get_mb_info }, | |
352 | { 0, NULL } | |
353 | }; | |
354 | ||
355 | /* G5 features | |
356 | */ | |
357 | static struct feature_table_entry g5_features[] __pmacdata = { | |
358 | { PMAC_FTR_GMAC_ENABLE, g5_gmac_enable }, | |
359 | { PMAC_FTR_1394_ENABLE, g5_fw_enable }, | |
360 | { PMAC_FTR_ENABLE_MPIC, g5_mpic_enable }, | |
361 | { PMAC_FTR_READ_GPIO, g5_read_gpio }, | |
362 | { PMAC_FTR_WRITE_GPIO, g5_write_gpio }, | |
363 | { PMAC_FTR_GMAC_PHY_RESET, g5_eth_phy_reset }, | |
7bbd8277 | 364 | { PMAC_FTR_SOUND_CHIP_ENABLE, g5_i2s_enable }, |
1da177e4 LT |
365 | #ifdef CONFIG_SMP |
366 | { PMAC_FTR_RESET_CPU, g5_reset_cpu }, | |
367 | #endif /* CONFIG_SMP */ | |
368 | { 0, NULL } | |
369 | }; | |
370 | ||
371 | static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { | |
372 | { "PowerMac7,2", "PowerMac G5", | |
373 | PMAC_TYPE_POWERMAC_G5, g5_features, | |
374 | 0, | |
375 | }, | |
376 | { "PowerMac7,3", "PowerMac G5", | |
377 | PMAC_TYPE_POWERMAC_G5, g5_features, | |
378 | 0, | |
379 | }, | |
380 | { "PowerMac8,1", "iMac G5", | |
381 | PMAC_TYPE_IMAC_G5, g5_features, | |
382 | 0, | |
383 | }, | |
384 | { "PowerMac9,1", "PowerMac G5", | |
385 | PMAC_TYPE_POWERMAC_G5_U3L, g5_features, | |
386 | 0, | |
387 | }, | |
388 | { "RackMac3,1", "XServe G5", | |
389 | PMAC_TYPE_XSERVE_G5, g5_features, | |
390 | 0, | |
391 | }, | |
392 | }; | |
393 | ||
394 | /* | |
395 | * The toplevel feature_call callback | |
396 | */ | |
397 | long __pmac pmac_do_feature_call(unsigned int selector, ...) | |
398 | { | |
399 | struct device_node* node; | |
400 | long param, value; | |
401 | int i; | |
402 | feature_call func = NULL; | |
403 | va_list args; | |
404 | ||
405 | if (pmac_mb.features) | |
406 | for (i=0; pmac_mb.features[i].function; i++) | |
407 | if (pmac_mb.features[i].selector == selector) { | |
408 | func = pmac_mb.features[i].function; | |
409 | break; | |
410 | } | |
411 | if (!func) | |
412 | for (i=0; any_features[i].function; i++) | |
413 | if (any_features[i].selector == selector) { | |
414 | func = any_features[i].function; | |
415 | break; | |
416 | } | |
417 | if (!func) | |
418 | return -ENODEV; | |
419 | ||
420 | va_start(args, selector); | |
421 | node = (struct device_node*)va_arg(args, void*); | |
422 | param = va_arg(args, long); | |
423 | value = va_arg(args, long); | |
424 | va_end(args); | |
425 | ||
426 | return func(node, param, value); | |
427 | } | |
428 | ||
429 | static int __init probe_motherboard(void) | |
430 | { | |
431 | int i; | |
432 | struct macio_chip* macio = &macio_chips[0]; | |
433 | const char* model = NULL; | |
434 | struct device_node *dt; | |
435 | ||
436 | /* Lookup known motherboard type in device-tree. First try an | |
437 | * exact match on the "model" property, then try a "compatible" | |
438 | * match is none is found. | |
439 | */ | |
440 | dt = find_devices("device-tree"); | |
441 | if (dt != NULL) | |
442 | model = (const char *) get_property(dt, "model", NULL); | |
443 | for(i=0; model && i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { | |
444 | if (strcmp(model, pmac_mb_defs[i].model_string) == 0) { | |
445 | pmac_mb = pmac_mb_defs[i]; | |
446 | goto found; | |
447 | } | |
448 | } | |
449 | for(i=0; i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { | |
450 | if (machine_is_compatible(pmac_mb_defs[i].model_string)) { | |
451 | pmac_mb = pmac_mb_defs[i]; | |
452 | goto found; | |
453 | } | |
454 | } | |
455 | ||
456 | /* Fallback to selection depending on mac-io chip type */ | |
457 | switch(macio->type) { | |
458 | case macio_keylargo2: | |
459 | pmac_mb.model_id = PMAC_TYPE_UNKNOWN_K2; | |
460 | pmac_mb.model_name = "Unknown K2-based"; | |
461 | pmac_mb.features = g5_features; | |
462 | ||
463 | default: | |
464 | return -ENODEV; | |
465 | } | |
466 | found: | |
467 | /* Check for "mobile" machine */ | |
468 | if (model && (strncmp(model, "PowerBook", 9) == 0 | |
469 | || strncmp(model, "iBook", 5) == 0)) | |
470 | pmac_mb.board_flags |= PMAC_MB_MOBILE; | |
471 | ||
472 | ||
473 | printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name); | |
474 | return 0; | |
475 | } | |
476 | ||
477 | /* Initialize the Core99 UniNorth host bridge and memory controller | |
478 | */ | |
479 | static void __init probe_uninorth(void) | |
480 | { | |
481 | uninorth_node = of_find_node_by_name(NULL, "u3"); | |
482 | if (uninorth_node && uninorth_node->n_addrs > 0) { | |
483 | /* Small hack until I figure out if parsing in prom.c is correct. I should | |
484 | * get rid of those pre-parsed junk anyway | |
485 | */ | |
486 | unsigned long address = uninorth_node->addrs[0].address; | |
487 | uninorth_base = ioremap(address, 0x40000); | |
488 | uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); | |
489 | u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); | |
490 | } else | |
491 | uninorth_node = NULL; | |
492 | ||
493 | if (!uninorth_node) | |
494 | return; | |
495 | ||
496 | printk(KERN_INFO "Found U3 memory controller & host bridge, revision: %d\n", | |
497 | uninorth_rev); | |
498 | printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); | |
499 | ||
500 | } | |
501 | ||
502 | static void __init probe_one_macio(const char* name, const char* compat, int type) | |
503 | { | |
504 | struct device_node* node; | |
505 | int i; | |
506 | volatile u32* base; | |
507 | u32* revp; | |
508 | ||
509 | node = find_devices(name); | |
510 | if (!node || !node->n_addrs) | |
511 | return; | |
512 | if (compat) | |
513 | do { | |
514 | if (device_is_compatible(node, compat)) | |
515 | break; | |
516 | node = node->next; | |
517 | } while (node); | |
518 | if (!node) | |
519 | return; | |
520 | for(i=0; i<MAX_MACIO_CHIPS; i++) { | |
521 | if (!macio_chips[i].of_node) | |
522 | break; | |
523 | if (macio_chips[i].of_node == node) | |
524 | return; | |
525 | } | |
526 | if (i >= MAX_MACIO_CHIPS) { | |
527 | printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n"); | |
528 | printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name); | |
529 | return; | |
530 | } | |
531 | base = (volatile u32*)ioremap(node->addrs[0].address, node->addrs[0].size); | |
532 | if (!base) { | |
533 | printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n"); | |
534 | return; | |
535 | } | |
536 | if (type == macio_keylargo) { | |
537 | u32* did = (u32 *)get_property(node, "device-id", NULL); | |
538 | if (*did == 0x00000025) | |
539 | type = macio_pangea; | |
540 | if (*did == 0x0000003e) | |
541 | type = macio_intrepid; | |
542 | } | |
543 | macio_chips[i].of_node = node; | |
544 | macio_chips[i].type = type; | |
545 | macio_chips[i].base = base; | |
546 | macio_chips[i].flags = MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON; | |
547 | macio_chips[i].name = macio_names[type]; | |
548 | revp = (u32 *)get_property(node, "revision-id", NULL); | |
549 | if (revp) | |
550 | macio_chips[i].rev = *revp; | |
551 | printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n", | |
552 | macio_names[type], macio_chips[i].rev, macio_chips[i].base); | |
553 | } | |
554 | ||
555 | static int __init | |
556 | probe_macios(void) | |
557 | { | |
558 | probe_one_macio("mac-io", "K2-Keylargo", macio_keylargo2); | |
559 | ||
560 | macio_chips[0].lbus.index = 0; | |
561 | macio_chips[1].lbus.index = 1; | |
562 | ||
563 | return (macio_chips[0].of_node == NULL) ? -ENODEV : 0; | |
564 | } | |
565 | ||
566 | static void __init | |
567 | set_initial_features(void) | |
568 | { | |
569 | struct device_node *np; | |
570 | ||
571 | if (macio_chips[0].type == macio_keylargo2) { | |
572 | #ifndef CONFIG_SMP | |
573 | /* On SMP machines running UP, we have the second CPU eating | |
574 | * bus cycles. We need to take it off the bus. This is done | |
575 | * from pmac_smp for SMP kernels running on one CPU | |
576 | */ | |
577 | np = of_find_node_by_type(NULL, "cpu"); | |
578 | if (np != NULL) | |
579 | np = of_find_node_by_type(np, "cpu"); | |
580 | if (np != NULL) { | |
581 | g5_phy_disable_cpu1(); | |
582 | of_node_put(np); | |
583 | } | |
584 | #endif /* CONFIG_SMP */ | |
585 | /* Enable GMAC for now for PCI probing. It will be disabled | |
586 | * later on after PCI probe | |
587 | */ | |
588 | np = of_find_node_by_name(NULL, "ethernet"); | |
589 | while(np) { | |
590 | if (device_is_compatible(np, "K2-GMAC")) | |
591 | g5_gmac_enable(np, 0, 1); | |
592 | np = of_find_node_by_name(np, "ethernet"); | |
593 | } | |
594 | ||
595 | /* Enable FW before PCI probe. Will be disabled later on | |
596 | * Note: We should have a batter way to check that we are | |
597 | * dealing with uninorth internal cell and not a PCI cell | |
598 | * on the external PCI. The code below works though. | |
599 | */ | |
600 | np = of_find_node_by_name(NULL, "firewire"); | |
601 | while(np) { | |
602 | if (device_is_compatible(np, "pci106b,5811")) { | |
603 | macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; | |
604 | g5_fw_enable(np, 0, 1); | |
605 | } | |
606 | np = of_find_node_by_name(np, "firewire"); | |
607 | } | |
608 | } | |
609 | } | |
610 | ||
611 | void __init | |
612 | pmac_feature_init(void) | |
613 | { | |
614 | /* Detect the UniNorth memory controller */ | |
615 | probe_uninorth(); | |
616 | ||
617 | /* Probe mac-io controllers */ | |
618 | if (probe_macios()) { | |
619 | printk(KERN_WARNING "No mac-io chip found\n"); | |
620 | return; | |
621 | } | |
622 | ||
623 | /* Setup low-level i2c stuffs */ | |
624 | pmac_init_low_i2c(); | |
625 | ||
626 | /* Probe machine type */ | |
627 | if (probe_motherboard()) | |
628 | printk(KERN_WARNING "Unknown PowerMac !\n"); | |
629 | ||
630 | /* Set some initial features (turn off some chips that will | |
631 | * be later turned on) | |
632 | */ | |
633 | set_initial_features(); | |
634 | } | |
635 | ||
636 | int __init pmac_feature_late_init(void) | |
637 | { | |
638 | #if 0 | |
639 | struct device_node* np; | |
640 | ||
641 | /* Request some resources late */ | |
642 | if (uninorth_node) | |
643 | request_OF_resource(uninorth_node, 0, NULL); | |
644 | np = find_devices("hammerhead"); | |
645 | if (np) | |
646 | request_OF_resource(np, 0, NULL); | |
647 | np = find_devices("interrupt-controller"); | |
648 | if (np) | |
649 | request_OF_resource(np, 0, NULL); | |
650 | #endif | |
651 | return 0; | |
652 | } | |
653 | ||
654 | device_initcall(pmac_feature_late_init); | |
655 | ||
656 | #if 0 | |
657 | static void dump_HT_speeds(char *name, u32 cfg, u32 frq) | |
658 | { | |
659 | int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; | |
660 | int bits[8] = { 8,16,0,32,2,4,0,0 }; | |
661 | int freq = (frq >> 8) & 0xf; | |
662 | ||
663 | if (freqs[freq] == 0) | |
664 | printk("%s: Unknown HT link frequency %x\n", name, freq); | |
665 | else | |
666 | printk("%s: %d MHz on main link, (%d in / %d out) bits width\n", | |
667 | name, freqs[freq], | |
668 | bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); | |
669 | } | |
670 | #endif | |
671 | ||
672 | void __init pmac_check_ht_link(void) | |
673 | { | |
674 | #if 0 /* Disabled for now */ | |
675 | u32 ufreq, freq, ucfg, cfg; | |
676 | struct device_node *pcix_node; | |
1635317f | 677 | struct pci_dn *pdn; |
1da177e4 LT |
678 | u8 px_bus, px_devfn; |
679 | struct pci_controller *px_hose; | |
680 | ||
681 | (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); | |
682 | ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); | |
683 | ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); | |
684 | dump_HT_speeds("U3 HyperTransport", cfg, freq); | |
685 | ||
686 | pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); | |
687 | if (pcix_node == NULL) { | |
688 | printk("No PCI-X bridge found\n"); | |
689 | return; | |
690 | } | |
1635317f PM |
691 | pdn = pcix_node->data; |
692 | px_hose = pdn->phb; | |
693 | px_bus = pdn->busno; | |
694 | px_devfn = pdn->devfn; | |
1da177e4 LT |
695 | |
696 | early_read_config_dword(px_hose, px_bus, px_devfn, 0xc4, &cfg); | |
697 | early_read_config_dword(px_hose, px_bus, px_devfn, 0xcc, &freq); | |
698 | dump_HT_speeds("PCI-X HT Uplink", cfg, freq); | |
699 | early_read_config_dword(px_hose, px_bus, px_devfn, 0xc8, &cfg); | |
700 | early_read_config_dword(px_hose, px_bus, px_devfn, 0xd0, &freq); | |
701 | dump_HT_speeds("PCI-X HT Downlink", cfg, freq); | |
702 | #endif | |
703 | } | |
0c541b44 BH |
704 | |
705 | /* | |
706 | * Early video resume hook | |
707 | */ | |
708 | ||
709 | static void (*pmac_early_vresume_proc)(void *data) __pmacdata; | |
710 | static void *pmac_early_vresume_data __pmacdata; | |
711 | ||
712 | void pmac_set_early_video_resume(void (*proc)(void *data), void *data) | |
713 | { | |
714 | if (_machine != _MACH_Pmac) | |
715 | return; | |
716 | preempt_disable(); | |
717 | pmac_early_vresume_proc = proc; | |
718 | pmac_early_vresume_data = data; | |
719 | preempt_enable(); | |
720 | } | |
721 | EXPORT_SYMBOL(pmac_set_early_video_resume); | |
722 | ||
723 | ||
724 | /* | |
725 | * AGP related suspend/resume code | |
726 | */ | |
727 | ||
728 | static struct pci_dev *pmac_agp_bridge __pmacdata; | |
729 | static int (*pmac_agp_suspend)(struct pci_dev *bridge) __pmacdata; | |
730 | static int (*pmac_agp_resume)(struct pci_dev *bridge) __pmacdata; | |
731 | ||
732 | void __pmac pmac_register_agp_pm(struct pci_dev *bridge, | |
733 | int (*suspend)(struct pci_dev *bridge), | |
734 | int (*resume)(struct pci_dev *bridge)) | |
735 | { | |
736 | if (suspend || resume) { | |
737 | pmac_agp_bridge = bridge; | |
738 | pmac_agp_suspend = suspend; | |
739 | pmac_agp_resume = resume; | |
740 | return; | |
741 | } | |
742 | if (bridge != pmac_agp_bridge) | |
743 | return; | |
744 | pmac_agp_suspend = pmac_agp_resume = NULL; | |
745 | return; | |
746 | } | |
747 | EXPORT_SYMBOL(pmac_register_agp_pm); | |
748 | ||
749 | void __pmac pmac_suspend_agp_for_card(struct pci_dev *dev) | |
750 | { | |
751 | if (pmac_agp_bridge == NULL || pmac_agp_suspend == NULL) | |
752 | return; | |
753 | if (pmac_agp_bridge->bus != dev->bus) | |
754 | return; | |
755 | pmac_agp_suspend(pmac_agp_bridge); | |
756 | } | |
757 | EXPORT_SYMBOL(pmac_suspend_agp_for_card); | |
758 | ||
759 | void __pmac pmac_resume_agp_for_card(struct pci_dev *dev) | |
760 | { | |
761 | if (pmac_agp_bridge == NULL || pmac_agp_resume == NULL) | |
762 | return; | |
763 | if (pmac_agp_bridge->bus != dev->bus) | |
764 | return; | |
765 | pmac_agp_resume(pmac_agp_bridge); | |
766 | } | |
767 | EXPORT_SYMBOL(pmac_resume_agp_for_card); |