18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for the interrupt controllers found on Power Macintosh, 48c2ecf20Sopenharmony_ci * currently Apple's "Grand Central" interrupt controller in all 58c2ecf20Sopenharmony_ci * it's incarnations. OpenPIC support used on newer machines is 68c2ecf20Sopenharmony_ci * in a separate file 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 1997 Paul Mackerras (paulus@samba.org) 98c2ecf20Sopenharmony_ci * Copyright (C) 2005 Benjamin Herrenschmidt (benh@kernel.crashing.org) 108c2ecf20Sopenharmony_ci * IBM, Corp. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/stddef.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/signal.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 208c2ecf20Sopenharmony_ci#include <linux/adb.h> 218c2ecf20Sopenharmony_ci#include <linux/pmu.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <asm/sections.h> 248c2ecf20Sopenharmony_ci#include <asm/io.h> 258c2ecf20Sopenharmony_ci#include <asm/smp.h> 268c2ecf20Sopenharmony_ci#include <asm/prom.h> 278c2ecf20Sopenharmony_ci#include <asm/pci-bridge.h> 288c2ecf20Sopenharmony_ci#include <asm/time.h> 298c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h> 308c2ecf20Sopenharmony_ci#include <asm/mpic.h> 318c2ecf20Sopenharmony_ci#include <asm/xmon.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "pmac.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32 368c2ecf20Sopenharmony_cistruct pmac_irq_hw { 378c2ecf20Sopenharmony_ci unsigned int event; 388c2ecf20Sopenharmony_ci unsigned int enable; 398c2ecf20Sopenharmony_ci unsigned int ack; 408c2ecf20Sopenharmony_ci unsigned int level; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Workaround flags for 32bit powermac machines */ 448c2ecf20Sopenharmony_ciunsigned int of_irq_workarounds; 458c2ecf20Sopenharmony_cistruct device_node *of_irq_dflt_pic; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Default addresses */ 488c2ecf20Sopenharmony_cistatic volatile struct pmac_irq_hw __iomem *pmac_irq_hw[4]; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int max_irqs; 518c2ecf20Sopenharmony_cistatic int max_real_irqs; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(pmac_pic_lock); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* The max irq number this driver deals with is 128; see max_irqs */ 568c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(ppc_lost_interrupts, 128); 578c2ecf20Sopenharmony_cistatic DECLARE_BITMAP(ppc_cached_irq_mask, 128); 588c2ecf20Sopenharmony_cistatic int pmac_irq_cascade = -1; 598c2ecf20Sopenharmony_cistatic struct irq_domain *pmac_pic_host; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void __pmac_retrigger(unsigned int irq_nr) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci if (irq_nr >= max_real_irqs && pmac_irq_cascade > 0) { 648c2ecf20Sopenharmony_ci __set_bit(irq_nr, ppc_lost_interrupts); 658c2ecf20Sopenharmony_ci irq_nr = pmac_irq_cascade; 668c2ecf20Sopenharmony_ci mb(); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci if (!__test_and_set_bit(irq_nr, ppc_lost_interrupts)) { 698c2ecf20Sopenharmony_ci atomic_inc(&ppc_n_lost_interrupts); 708c2ecf20Sopenharmony_ci set_dec(1); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void pmac_mask_and_ack_irq(struct irq_data *d) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 778c2ecf20Sopenharmony_ci unsigned long bit = 1UL << (src & 0x1f); 788c2ecf20Sopenharmony_ci int i = src >> 5; 798c2ecf20Sopenharmony_ci unsigned long flags; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 828c2ecf20Sopenharmony_ci __clear_bit(src, ppc_cached_irq_mask); 838c2ecf20Sopenharmony_ci if (__test_and_clear_bit(src, ppc_lost_interrupts)) 848c2ecf20Sopenharmony_ci atomic_dec(&ppc_n_lost_interrupts); 858c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); 868c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[i]->ack, bit); 878c2ecf20Sopenharmony_ci do { 888c2ecf20Sopenharmony_ci /* make sure ack gets to controller before we enable 898c2ecf20Sopenharmony_ci interrupts */ 908c2ecf20Sopenharmony_ci mb(); 918c2ecf20Sopenharmony_ci } while((in_le32(&pmac_irq_hw[i]->enable) & bit) 928c2ecf20Sopenharmony_ci != (ppc_cached_irq_mask[i] & bit)); 938c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void pmac_ack_irq(struct irq_data *d) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 998c2ecf20Sopenharmony_ci unsigned long bit = 1UL << (src & 0x1f); 1008c2ecf20Sopenharmony_ci int i = src >> 5; 1018c2ecf20Sopenharmony_ci unsigned long flags; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 1048c2ecf20Sopenharmony_ci if (__test_and_clear_bit(src, ppc_lost_interrupts)) 1058c2ecf20Sopenharmony_ci atomic_dec(&ppc_n_lost_interrupts); 1068c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[i]->ack, bit); 1078c2ecf20Sopenharmony_ci (void)in_le32(&pmac_irq_hw[i]->ack); 1088c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci unsigned long bit = 1UL << (irq_nr & 0x1f); 1148c2ecf20Sopenharmony_ci int i = irq_nr >> 5; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if ((unsigned)irq_nr >= max_irqs) 1178c2ecf20Sopenharmony_ci return; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* enable unmasked interrupts */ 1208c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci do { 1238c2ecf20Sopenharmony_ci /* make sure mask gets to controller before we 1248c2ecf20Sopenharmony_ci return to user */ 1258c2ecf20Sopenharmony_ci mb(); 1268c2ecf20Sopenharmony_ci } while((in_le32(&pmac_irq_hw[i]->enable) & bit) 1278c2ecf20Sopenharmony_ci != (ppc_cached_irq_mask[i] & bit)); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Unfortunately, setting the bit in the enable register 1318c2ecf20Sopenharmony_ci * when the device interrupt is already on *doesn't* set 1328c2ecf20Sopenharmony_ci * the bit in the flag register or request another interrupt. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level)) 1358c2ecf20Sopenharmony_ci __pmac_retrigger(irq_nr); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* When an irq gets requested for the first client, if it's an 1398c2ecf20Sopenharmony_ci * edge interrupt, we clear any previous one on the controller 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cistatic unsigned int pmac_startup_irq(struct irq_data *d) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned long flags; 1448c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 1458c2ecf20Sopenharmony_ci unsigned long bit = 1UL << (src & 0x1f); 1468c2ecf20Sopenharmony_ci int i = src >> 5; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 1498c2ecf20Sopenharmony_ci if (!irqd_is_level_type(d)) 1508c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[i]->ack, bit); 1518c2ecf20Sopenharmony_ci __set_bit(src, ppc_cached_irq_mask); 1528c2ecf20Sopenharmony_ci __pmac_set_irq_mask(src, 0); 1538c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void pmac_mask_irq(struct irq_data *d) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci unsigned long flags; 1618c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 1648c2ecf20Sopenharmony_ci __clear_bit(src, ppc_cached_irq_mask); 1658c2ecf20Sopenharmony_ci __pmac_set_irq_mask(src, 1); 1668c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void pmac_unmask_irq(struct irq_data *d) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci unsigned long flags; 1728c2ecf20Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 1758c2ecf20Sopenharmony_ci __set_bit(src, ppc_cached_irq_mask); 1768c2ecf20Sopenharmony_ci __pmac_set_irq_mask(src, 0); 1778c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int pmac_retrigger(struct irq_data *d) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci unsigned long flags; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 1858c2ecf20Sopenharmony_ci __pmac_retrigger(irqd_to_hwirq(d)); 1868c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 1878c2ecf20Sopenharmony_ci return 1; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic struct irq_chip pmac_pic = { 1918c2ecf20Sopenharmony_ci .name = "PMAC-PIC", 1928c2ecf20Sopenharmony_ci .irq_startup = pmac_startup_irq, 1938c2ecf20Sopenharmony_ci .irq_mask = pmac_mask_irq, 1948c2ecf20Sopenharmony_ci .irq_ack = pmac_ack_irq, 1958c2ecf20Sopenharmony_ci .irq_mask_ack = pmac_mask_and_ack_irq, 1968c2ecf20Sopenharmony_ci .irq_unmask = pmac_unmask_irq, 1978c2ecf20Sopenharmony_ci .irq_retrigger = pmac_retrigger, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic irqreturn_t gatwick_action(int cpl, void *dev_id) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci unsigned long flags; 2038c2ecf20Sopenharmony_ci int irq, bits; 2048c2ecf20Sopenharmony_ci int rc = IRQ_NONE; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 2078c2ecf20Sopenharmony_ci for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) { 2088c2ecf20Sopenharmony_ci int i = irq >> 5; 2098c2ecf20Sopenharmony_ci bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; 2108c2ecf20Sopenharmony_ci bits |= in_le32(&pmac_irq_hw[i]->level); 2118c2ecf20Sopenharmony_ci bits &= ppc_cached_irq_mask[i]; 2128c2ecf20Sopenharmony_ci if (bits == 0) 2138c2ecf20Sopenharmony_ci continue; 2148c2ecf20Sopenharmony_ci irq += __ilog2(bits); 2158c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 2168c2ecf20Sopenharmony_ci generic_handle_irq(irq); 2178c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 2188c2ecf20Sopenharmony_ci rc = IRQ_HANDLED; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 2218c2ecf20Sopenharmony_ci return rc; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic unsigned int pmac_pic_get_irq(void) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int irq; 2278c2ecf20Sopenharmony_ci unsigned long bits = 0; 2288c2ecf20Sopenharmony_ci unsigned long flags; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC32_PSURGE 2318c2ecf20Sopenharmony_ci /* IPI's are a hack on the powersurge -- Cort */ 2328c2ecf20Sopenharmony_ci if (smp_processor_id() != 0) { 2338c2ecf20Sopenharmony_ci return psurge_secondary_virq; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC32_PSURGE */ 2368c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 2378c2ecf20Sopenharmony_ci for (irq = max_real_irqs; (irq -= 32) >= 0; ) { 2388c2ecf20Sopenharmony_ci int i = irq >> 5; 2398c2ecf20Sopenharmony_ci bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; 2408c2ecf20Sopenharmony_ci bits |= in_le32(&pmac_irq_hw[i]->level); 2418c2ecf20Sopenharmony_ci bits &= ppc_cached_irq_mask[i]; 2428c2ecf20Sopenharmony_ci if (bits == 0) 2438c2ecf20Sopenharmony_ci continue; 2448c2ecf20Sopenharmony_ci irq += __ilog2(bits); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 2488c2ecf20Sopenharmony_ci if (unlikely(irq < 0)) 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci return irq_linear_revmap(pmac_pic_host, irq); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int pmac_pic_host_match(struct irq_domain *h, struct device_node *node, 2548c2ecf20Sopenharmony_ci enum irq_domain_bus_token bus_token) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci /* We match all, we don't always have a node anyway */ 2578c2ecf20Sopenharmony_ci return 1; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int pmac_pic_host_map(struct irq_domain *h, unsigned int virq, 2618c2ecf20Sopenharmony_ci irq_hw_number_t hw) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci if (hw >= max_irqs) 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Mark level interrupts, set delayed disable for edge ones and set 2678c2ecf20Sopenharmony_ci * handlers 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci irq_set_status_flags(virq, IRQ_LEVEL); 2708c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &pmac_pic, handle_level_irq); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic const struct irq_domain_ops pmac_pic_host_ops = { 2758c2ecf20Sopenharmony_ci .match = pmac_pic_host_match, 2768c2ecf20Sopenharmony_ci .map = pmac_pic_host_map, 2778c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void __init pmac_pic_probe_oldstyle(void) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int i; 2838c2ecf20Sopenharmony_ci struct device_node *master = NULL; 2848c2ecf20Sopenharmony_ci struct device_node *slave = NULL; 2858c2ecf20Sopenharmony_ci u8 __iomem *addr; 2868c2ecf20Sopenharmony_ci struct resource r; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Set our get_irq function */ 2898c2ecf20Sopenharmony_ci ppc_md.get_irq = pmac_pic_get_irq; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * Find the interrupt controller type & node 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if ((master = of_find_node_by_name(NULL, "gc")) != NULL) { 2968c2ecf20Sopenharmony_ci max_irqs = max_real_irqs = 32; 2978c2ecf20Sopenharmony_ci } else if ((master = of_find_node_by_name(NULL, "ohare")) != NULL) { 2988c2ecf20Sopenharmony_ci max_irqs = max_real_irqs = 32; 2998c2ecf20Sopenharmony_ci /* We might have a second cascaded ohare */ 3008c2ecf20Sopenharmony_ci slave = of_find_node_by_name(NULL, "pci106b,7"); 3018c2ecf20Sopenharmony_ci if (slave) 3028c2ecf20Sopenharmony_ci max_irqs = 64; 3038c2ecf20Sopenharmony_ci } else if ((master = of_find_node_by_name(NULL, "mac-io")) != NULL) { 3048c2ecf20Sopenharmony_ci max_irqs = max_real_irqs = 64; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* We might have a second cascaded heathrow */ 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Compensate for of_node_put() in of_find_node_by_name() */ 3098c2ecf20Sopenharmony_ci of_node_get(master); 3108c2ecf20Sopenharmony_ci slave = of_find_node_by_name(master, "mac-io"); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Check ordering of master & slave */ 3138c2ecf20Sopenharmony_ci if (of_device_is_compatible(master, "gatwick")) { 3148c2ecf20Sopenharmony_ci struct device_node *tmp; 3158c2ecf20Sopenharmony_ci BUG_ON(slave == NULL); 3168c2ecf20Sopenharmony_ci tmp = master; 3178c2ecf20Sopenharmony_ci master = slave; 3188c2ecf20Sopenharmony_ci slave = tmp; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* We found a slave */ 3228c2ecf20Sopenharmony_ci if (slave) 3238c2ecf20Sopenharmony_ci max_irqs = 128; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci BUG_ON(master == NULL); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * Allocate an irq host 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci pmac_pic_host = irq_domain_add_linear(master, max_irqs, 3318c2ecf20Sopenharmony_ci &pmac_pic_host_ops, NULL); 3328c2ecf20Sopenharmony_ci BUG_ON(pmac_pic_host == NULL); 3338c2ecf20Sopenharmony_ci irq_set_default_host(pmac_pic_host); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Get addresses of first controller if we have a node for it */ 3368c2ecf20Sopenharmony_ci BUG_ON(of_address_to_resource(master, 0, &r)); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Map interrupts of primary controller */ 3398c2ecf20Sopenharmony_ci addr = (u8 __iomem *) ioremap(r.start, 0x40); 3408c2ecf20Sopenharmony_ci i = 0; 3418c2ecf20Sopenharmony_ci pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) 3428c2ecf20Sopenharmony_ci (addr + 0x20); 3438c2ecf20Sopenharmony_ci if (max_real_irqs > 32) 3448c2ecf20Sopenharmony_ci pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) 3458c2ecf20Sopenharmony_ci (addr + 0x10); 3468c2ecf20Sopenharmony_ci of_node_put(master); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci printk(KERN_INFO "irq: Found primary Apple PIC %pOF for %d irqs\n", 3498c2ecf20Sopenharmony_ci master, max_real_irqs); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Map interrupts of cascaded controller */ 3528c2ecf20Sopenharmony_ci if (slave && !of_address_to_resource(slave, 0, &r)) { 3538c2ecf20Sopenharmony_ci addr = (u8 __iomem *)ioremap(r.start, 0x40); 3548c2ecf20Sopenharmony_ci pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) 3558c2ecf20Sopenharmony_ci (addr + 0x20); 3568c2ecf20Sopenharmony_ci if (max_irqs > 64) 3578c2ecf20Sopenharmony_ci pmac_irq_hw[i++] = 3588c2ecf20Sopenharmony_ci (volatile struct pmac_irq_hw __iomem *) 3598c2ecf20Sopenharmony_ci (addr + 0x10); 3608c2ecf20Sopenharmony_ci pmac_irq_cascade = irq_of_parse_and_map(slave, 0); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci printk(KERN_INFO "irq: Found slave Apple PIC %pOF for %d irqs" 3638c2ecf20Sopenharmony_ci " cascade: %d\n", slave, 3648c2ecf20Sopenharmony_ci max_irqs - max_real_irqs, pmac_irq_cascade); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci of_node_put(slave); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Disable all interrupts in all controllers */ 3698c2ecf20Sopenharmony_ci for (i = 0; i * 32 < max_irqs; ++i) 3708c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[i]->enable, 0); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Hookup cascade irq */ 3738c2ecf20Sopenharmony_ci if (slave && pmac_irq_cascade) { 3748c2ecf20Sopenharmony_ci if (request_irq(pmac_irq_cascade, gatwick_action, 3758c2ecf20Sopenharmony_ci IRQF_NO_THREAD, "cascade", NULL)) 3768c2ecf20Sopenharmony_ci pr_err("Failed to register cascade interrupt\n"); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs); 3808c2ecf20Sopenharmony_ci#ifdef CONFIG_XMON 3818c2ecf20Sopenharmony_ci i = irq_create_mapping(NULL, 20); 3828c2ecf20Sopenharmony_ci if (request_irq(i, xmon_irq, IRQF_NO_THREAD, "NMI - XMON", NULL)) 3838c2ecf20Sopenharmony_ci pr_err("Failed to register NMI-XMON interrupt\n"); 3848c2ecf20Sopenharmony_ci#endif 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciint of_irq_parse_oldworld(struct device_node *device, int index, 3888c2ecf20Sopenharmony_ci struct of_phandle_args *out_irq) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci const u32 *ints = NULL; 3918c2ecf20Sopenharmony_ci int intlen; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Old machines just have a list of interrupt numbers 3958c2ecf20Sopenharmony_ci * and no interrupt-controller nodes. We also have dodgy 3968c2ecf20Sopenharmony_ci * cases where the APPL,interrupts property is completely 3978c2ecf20Sopenharmony_ci * missing behind pci-pci bridges and we have to get it 3988c2ecf20Sopenharmony_ci * from the parent (the bridge itself, as apple just wired 3998c2ecf20Sopenharmony_ci * everything together on these) 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci while (device) { 4028c2ecf20Sopenharmony_ci ints = of_get_property(device, "AAPL,interrupts", &intlen); 4038c2ecf20Sopenharmony_ci if (ints != NULL) 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci device = device->parent; 4068c2ecf20Sopenharmony_ci if (!of_node_is_type(device, "pci")) 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci if (ints == NULL) 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci intlen /= sizeof(u32); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (index >= intlen) 4148c2ecf20Sopenharmony_ci return -EINVAL; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci out_irq->np = NULL; 4178c2ecf20Sopenharmony_ci out_irq->args[0] = ints[index]; 4188c2ecf20Sopenharmony_ci out_irq->args_count = 1; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC32 */ 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci#if defined(CONFIG_XMON) && defined(CONFIG_PPC32) 4278c2ecf20Sopenharmony_ci struct device_node* pswitch; 4288c2ecf20Sopenharmony_ci int nmi_irq; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci pswitch = of_find_node_by_name(NULL, "programmer-switch"); 4318c2ecf20Sopenharmony_ci if (pswitch) { 4328c2ecf20Sopenharmony_ci nmi_irq = irq_of_parse_and_map(pswitch, 0); 4338c2ecf20Sopenharmony_ci if (nmi_irq) { 4348c2ecf20Sopenharmony_ci mpic_irq_set_priority(nmi_irq, 9); 4358c2ecf20Sopenharmony_ci if (request_irq(nmi_irq, xmon_irq, IRQF_NO_THREAD, 4368c2ecf20Sopenharmony_ci "NMI - XMON", NULL)) 4378c2ecf20Sopenharmony_ci pr_err("Failed to register NMI-XMON interrupt\n"); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci of_node_put(pswitch); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct mpic * __init pmac_setup_one_mpic(struct device_node *np, 4458c2ecf20Sopenharmony_ci int master) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci const char *name = master ? " MPIC 1 " : " MPIC 2 "; 4488c2ecf20Sopenharmony_ci struct mpic *mpic; 4498c2ecf20Sopenharmony_ci unsigned int flags = master ? 0 : MPIC_SECONDARY; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (of_get_property(np, "big-endian", NULL)) 4548c2ecf20Sopenharmony_ci flags |= MPIC_BIG_ENDIAN; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Primary Big Endian means HT interrupts. This is quite dodgy 4578c2ecf20Sopenharmony_ci * but works until I find a better way 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci if (master && (flags & MPIC_BIG_ENDIAN)) 4608c2ecf20Sopenharmony_ci flags |= MPIC_U3_HT_IRQS; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci mpic = mpic_alloc(np, 0, flags, 0, 0, name); 4638c2ecf20Sopenharmony_ci if (mpic == NULL) 4648c2ecf20Sopenharmony_ci return NULL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci mpic_init(mpic); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return mpic; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int __init pmac_pic_probe_mpic(void) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct mpic *mpic1, *mpic2; 4748c2ecf20Sopenharmony_ci struct device_node *np, *master = NULL, *slave = NULL; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* We can have up to 2 MPICs cascaded */ 4778c2ecf20Sopenharmony_ci for_each_node_by_type(np, "open-pic") { 4788c2ecf20Sopenharmony_ci if (master == NULL && 4798c2ecf20Sopenharmony_ci of_get_property(np, "interrupts", NULL) == NULL) 4808c2ecf20Sopenharmony_ci master = of_node_get(np); 4818c2ecf20Sopenharmony_ci else if (slave == NULL) 4828c2ecf20Sopenharmony_ci slave = of_node_get(np); 4838c2ecf20Sopenharmony_ci if (master && slave) { 4848c2ecf20Sopenharmony_ci of_node_put(np); 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Check for bogus setups */ 4908c2ecf20Sopenharmony_ci if (master == NULL && slave != NULL) { 4918c2ecf20Sopenharmony_ci master = slave; 4928c2ecf20Sopenharmony_ci slave = NULL; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Not found, default to good old pmac pic */ 4968c2ecf20Sopenharmony_ci if (master == NULL) 4978c2ecf20Sopenharmony_ci return -ENODEV; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* Set master handler */ 5008c2ecf20Sopenharmony_ci ppc_md.get_irq = mpic_get_irq; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Setup master */ 5038c2ecf20Sopenharmony_ci mpic1 = pmac_setup_one_mpic(master, 1); 5048c2ecf20Sopenharmony_ci BUG_ON(mpic1 == NULL); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* Install NMI if any */ 5078c2ecf20Sopenharmony_ci pmac_pic_setup_mpic_nmi(mpic1); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci of_node_put(master); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Set up a cascaded controller, if present */ 5128c2ecf20Sopenharmony_ci if (slave) { 5138c2ecf20Sopenharmony_ci mpic2 = pmac_setup_one_mpic(slave, 0); 5148c2ecf20Sopenharmony_ci if (mpic2 == NULL) 5158c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to setup slave MPIC\n"); 5168c2ecf20Sopenharmony_ci of_node_put(slave); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_civoid __init pmac_pic_init(void) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci /* We configure the OF parsing based on our oldworld vs. newworld 5268c2ecf20Sopenharmony_ci * platform type and whether we were booted by BootX. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32 5298c2ecf20Sopenharmony_ci if (!pmac_newworld) 5308c2ecf20Sopenharmony_ci of_irq_workarounds |= OF_IMAP_OLDWORLD_MAC; 5318c2ecf20Sopenharmony_ci if (of_get_property(of_chosen, "linux,bootx", NULL) != NULL) 5328c2ecf20Sopenharmony_ci of_irq_workarounds |= OF_IMAP_NO_PHANDLE; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* If we don't have phandles on a newworld, then try to locate a 5358c2ecf20Sopenharmony_ci * default interrupt controller (happens when booting with BootX). 5368c2ecf20Sopenharmony_ci * We do a first match here, hopefully, that only ever happens on 5378c2ecf20Sopenharmony_ci * machines with one controller. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci if (pmac_newworld && (of_irq_workarounds & OF_IMAP_NO_PHANDLE)) { 5408c2ecf20Sopenharmony_ci struct device_node *np; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci for_each_node_with_property(np, "interrupt-controller") { 5438c2ecf20Sopenharmony_ci /* Skip /chosen/interrupt-controller */ 5448c2ecf20Sopenharmony_ci if (of_node_name_eq(np, "chosen")) 5458c2ecf20Sopenharmony_ci continue; 5468c2ecf20Sopenharmony_ci /* It seems like at least one person wants 5478c2ecf20Sopenharmony_ci * to use BootX on a machine with an AppleKiwi 5488c2ecf20Sopenharmony_ci * controller which happens to pretend to be an 5498c2ecf20Sopenharmony_ci * interrupt controller too. */ 5508c2ecf20Sopenharmony_ci if (of_node_name_eq(np, "AppleKiwi")) 5518c2ecf20Sopenharmony_ci continue; 5528c2ecf20Sopenharmony_ci /* I think we found one ! */ 5538c2ecf20Sopenharmony_ci of_irq_dflt_pic = np; 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC32 */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* We first try to detect Apple's new Core99 chipset, since mac-io 5608c2ecf20Sopenharmony_ci * is quite different on those machines and contains an IBM MPIC2. 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci if (pmac_pic_probe_mpic() == 0) 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32 5668c2ecf20Sopenharmony_ci pmac_pic_probe_oldstyle(); 5678c2ecf20Sopenharmony_ci#endif 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_PPC32) 5718c2ecf20Sopenharmony_ci/* 5728c2ecf20Sopenharmony_ci * These procedures are used in implementing sleep on the powerbooks. 5738c2ecf20Sopenharmony_ci * sleep_save_intrs() saves the states of all interrupt enables 5748c2ecf20Sopenharmony_ci * and disables all interrupts except for the nominated one. 5758c2ecf20Sopenharmony_ci * sleep_restore_intrs() restores the states of all interrupt enables. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ciunsigned long sleep_save_mask[2]; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci/* This used to be passed by the PMU driver but that link got 5808c2ecf20Sopenharmony_ci * broken with the new driver model. We use this tweak for now... 5818c2ecf20Sopenharmony_ci * We really want to do things differently though... 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic int pmacpic_find_viaint(void) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci int viaint = -1; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_PMU 5888c2ecf20Sopenharmony_ci struct device_node *np; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (pmu_get_model() != PMU_OHARE_BASED) 5918c2ecf20Sopenharmony_ci goto not_found; 5928c2ecf20Sopenharmony_ci np = of_find_node_by_name(NULL, "via-pmu"); 5938c2ecf20Sopenharmony_ci if (np == NULL) 5948c2ecf20Sopenharmony_ci goto not_found; 5958c2ecf20Sopenharmony_ci viaint = irq_of_parse_and_map(np, 0); 5968c2ecf20Sopenharmony_ci of_node_put(np); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cinot_found: 5998c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB_PMU */ 6008c2ecf20Sopenharmony_ci return viaint; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int pmacpic_suspend(void) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int viaint = pmacpic_find_viaint(); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci sleep_save_mask[0] = ppc_cached_irq_mask[0]; 6088c2ecf20Sopenharmony_ci sleep_save_mask[1] = ppc_cached_irq_mask[1]; 6098c2ecf20Sopenharmony_ci ppc_cached_irq_mask[0] = 0; 6108c2ecf20Sopenharmony_ci ppc_cached_irq_mask[1] = 0; 6118c2ecf20Sopenharmony_ci if (viaint > 0) 6128c2ecf20Sopenharmony_ci set_bit(viaint, ppc_cached_irq_mask); 6138c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); 6148c2ecf20Sopenharmony_ci if (max_real_irqs > 32) 6158c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); 6168c2ecf20Sopenharmony_ci (void)in_le32(&pmac_irq_hw[0]->event); 6178c2ecf20Sopenharmony_ci /* make sure mask gets to controller before we return to caller */ 6188c2ecf20Sopenharmony_ci mb(); 6198c2ecf20Sopenharmony_ci (void)in_le32(&pmac_irq_hw[0]->enable); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic void pmacpic_resume(void) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci int i; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[0]->enable, 0); 6298c2ecf20Sopenharmony_ci if (max_real_irqs > 32) 6308c2ecf20Sopenharmony_ci out_le32(&pmac_irq_hw[1]->enable, 0); 6318c2ecf20Sopenharmony_ci mb(); 6328c2ecf20Sopenharmony_ci for (i = 0; i < max_real_irqs; ++i) 6338c2ecf20Sopenharmony_ci if (test_bit(i, sleep_save_mask)) 6348c2ecf20Sopenharmony_ci pmac_unmask_irq(irq_get_irq_data(i)); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic struct syscore_ops pmacpic_syscore_ops = { 6388c2ecf20Sopenharmony_ci .suspend = pmacpic_suspend, 6398c2ecf20Sopenharmony_ci .resume = pmacpic_resume, 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic int __init init_pmacpic_syscore(void) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci if (pmac_irq_hw[0]) 6458c2ecf20Sopenharmony_ci register_syscore_ops(&pmacpic_syscore_ops); 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cimachine_subsys_initcall(powermac, init_pmacpic_syscore); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci#endif /* CONFIG_PM && CONFIG_PPC32 */ 652