⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xics.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	/*	 * Put the calling processor into the GIQ.  This is really only	 * necessary from a secondary thread as the OF start-cpu interface	 * performs this function for us on primary threads.	 *	 * XXX: undo of teardown on kexec needs this too, as may hotplug	 */	rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,		(1UL << interrupt_server_size) - 1 - default_distrib_server, 1);}static struct irq_chip xics_pic_direct = {	.typename = " XICS     ",	.startup = xics_startup,	.mask = xics_mask_irq,	.unmask = xics_unmask_irq,	.eoi = xics_eoi_direct,	.set_affinity = xics_set_affinity};static struct irq_chip xics_pic_lpar = {	.typename = " XICS     ",	.startup = xics_startup,	.mask = xics_mask_irq,	.unmask = xics_unmask_irq,	.eoi = xics_eoi_lpar,	.set_affinity = xics_set_affinity};static int xics_host_match(struct irq_host *h, struct device_node *node){	/* IBM machines have interrupt parents of various funky types for things	 * like vdevices, events, etc... The trick we use here is to match	 * everything here except the legacy 8259 which is compatible "chrp,iic"	 */	return !of_device_is_compatible(node, "chrp,iic");}static int xics_host_map_direct(struct irq_host *h, unsigned int virq,				irq_hw_number_t hw){	pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw);	get_irq_desc(virq)->status |= IRQ_LEVEL;	set_irq_chip_and_handler(virq, &xics_pic_direct, handle_fasteoi_irq);	return 0;}static int xics_host_map_lpar(struct irq_host *h, unsigned int virq,			      irq_hw_number_t hw){	pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw);	get_irq_desc(virq)->status |= IRQ_LEVEL;	set_irq_chip_and_handler(virq, &xics_pic_lpar, handle_fasteoi_irq);	return 0;}static int xics_host_xlate(struct irq_host *h, struct device_node *ct,			   u32 *intspec, unsigned int intsize,			   irq_hw_number_t *out_hwirq, unsigned int *out_flags){	/* Current xics implementation translates everything	 * to level. It is not technically right for MSIs but this	 * is irrelevant at this point. We might get smarter in the future	 */	*out_hwirq = intspec[0];	*out_flags = IRQ_TYPE_LEVEL_LOW;	return 0;}static struct irq_host_ops xics_host_direct_ops = {	.match = xics_host_match,	.map = xics_host_map_direct,	.xlate = xics_host_xlate,};static struct irq_host_ops xics_host_lpar_ops = {	.match = xics_host_match,	.map = xics_host_map_lpar,	.xlate = xics_host_xlate,};static void __init xics_init_host(void){	struct irq_host_ops *ops;	if (firmware_has_feature(FW_FEATURE_LPAR))		ops = &xics_host_lpar_ops;	else		ops = &xics_host_direct_ops;	xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, ops,				   XICS_IRQ_SPURIOUS);	BUG_ON(xics_host == NULL);	irq_set_default_host(xics_host);}static void __init xics_map_one_cpu(int hw_id, unsigned long addr,				     unsigned long size){#ifdef CONFIG_SMP	int i;	/* This may look gross but it's good enough for now, we don't quite	 * have a hard -> linux processor id matching.	 */	for_each_possible_cpu(i) {		if (!cpu_present(i))			continue;		if (hw_id == get_hard_smp_processor_id(i)) {			xics_per_cpu[i] = ioremap(addr, size);			return;		}	}#else	if (hw_id != 0)		return;	xics_per_cpu[0] = ioremap(addr, size);#endif /* CONFIG_SMP */}static void __init xics_init_one_node(struct device_node *np,				      unsigned int *indx){	unsigned int ilen;	const u32 *ireg;	/* This code does the theorically broken assumption that the interrupt	 * server numbers are the same as the hard CPU numbers.	 * This happens to be the case so far but we are playing with fire...	 * should be fixed one of these days. -BenH.	 */	ireg = of_get_property(np, "ibm,interrupt-server-ranges", NULL);	/* Do that ever happen ? we'll know soon enough... but even good'old	 * f80 does have that property ..	 */	WARN_ON(ireg == NULL);	if (ireg) {		/*		 * set node starting index for this node		 */		*indx = *ireg;	}	ireg = of_get_property(np, "reg", &ilen);	if (!ireg)		panic("xics_init_IRQ: can't find interrupt reg property");	while (ilen >= (4 * sizeof(u32))) {		unsigned long addr, size;		/* XXX Use proper OF parsing code here !!! */		addr = (unsigned long)*ireg++ << 32;		ilen -= sizeof(u32);		addr |= *ireg++;		ilen -= sizeof(u32);		size = (unsigned long)*ireg++ << 32;		ilen -= sizeof(u32);		size |= *ireg++;		ilen -= sizeof(u32);		xics_map_one_cpu(*indx, addr, size);		(*indx)++;	}}static void __init xics_setup_8259_cascade(void){	struct device_node *np, *old, *found = NULL;	int cascade, naddr;	const u32 *addrp;	unsigned long intack = 0;	for_each_node_by_type(np, "interrupt-controller")		if (of_device_is_compatible(np, "chrp,iic")) {			found = np;			break;		}	if (found == NULL) {		printk(KERN_DEBUG "xics: no ISA interrupt controller\n");		return;	}	cascade = irq_of_parse_and_map(found, 0);	if (cascade == NO_IRQ) {		printk(KERN_ERR "xics: failed to map cascade interrupt");		return;	}	pr_debug("xics: cascade mapped to irq %d\n", cascade);	for (old = of_node_get(found); old != NULL ; old = np) {		np = of_get_parent(old);		of_node_put(old);		if (np == NULL)			break;		if (strcmp(np->name, "pci") != 0)			continue;		addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL);		if (addrp == NULL)			continue;		naddr = of_n_addr_cells(np);		intack = addrp[naddr-1];		if (naddr > 1)			intack |= ((unsigned long)addrp[naddr-2]) << 32;	}	if (intack)		printk(KERN_DEBUG "xics: PCI 8259 intack at 0x%016lx\n", intack);	i8259_init(found, intack);	of_node_put(found);	set_irq_chained_handler(cascade, pseries_8259_cascade);}static struct device_node *cpuid_to_of_node(int cpu){	struct device_node *np;	u32 hcpuid = get_hard_smp_processor_id(cpu);	for_each_node_by_type(np, "cpu") {		int i, len;		const u32 *intserv;		intserv = of_get_property(np, "ibm,ppc-interrupt-server#s",					&len);		if (!intserv)			intserv = of_get_property(np, "reg", &len);		i = len / sizeof(u32);		while (i--)			if (intserv[i] == hcpuid)				return np;	}	return NULL;}void __init xics_init_IRQ(void){	int i, j;	struct device_node *np;	u32 ilen, indx = 0;	const u32 *ireg, *isize;	int found = 0;	u32 hcpuid;	ppc64_boot_msg(0x20, "XICS Init");	ibm_get_xive = rtas_token("ibm,get-xive");	ibm_set_xive = rtas_token("ibm,set-xive");	ibm_int_on  = rtas_token("ibm,int-on");	ibm_int_off = rtas_token("ibm,int-off");	for_each_node_by_type(np, "PowerPC-External-Interrupt-Presentation") {		found = 1;		if (firmware_has_feature(FW_FEATURE_LPAR))			break;		xics_init_one_node(np, &indx);	}	if (found == 0)		return;	xics_init_host();	/* Find the server numbers for the boot cpu. */	np = cpuid_to_of_node(boot_cpuid);	BUG_ON(!np);	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);	if (!ireg)		goto skip_gserver_check;	i = ilen / sizeof(int);	hcpuid = get_hard_smp_processor_id(boot_cpuid);	/* Global interrupt distribution server is specified in the last	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last	 * entry fom this property for current boot cpu id and use it as	 * default distribution server	 */	for (j = 0; j < i; j += 2) {		if (ireg[j] == hcpuid) {			default_server = hcpuid;			default_distrib_server = ireg[j+1];			isize = of_get_property(np,					"ibm,interrupt-server#-size", NULL);			if (isize)				interrupt_server_size = *isize;		}	}skip_gserver_check:	of_node_put(np);	if (firmware_has_feature(FW_FEATURE_LPAR))		ppc_md.get_irq = xics_get_irq_lpar;	else		ppc_md.get_irq = xics_get_irq_direct;	xics_setup_cpu();	xics_setup_8259_cascade();	ppc64_boot_msg(0x21, "XICS Done");}#ifdef CONFIG_SMPvoid xics_request_IPIs(void){	unsigned int ipi;	int rc;	ipi = irq_create_mapping(xics_host, XICS_IPI);	BUG_ON(ipi == NO_IRQ);	/*	 * IPIs are marked IRQF_DISABLED as they must run with irqs	 * disabled	 */	set_irq_handler(ipi, handle_percpu_irq);	if (firmware_has_feature(FW_FEATURE_LPAR))		rc = request_irq(ipi, xics_ipi_action_lpar, IRQF_DISABLED,				"IPI", NULL);	else		rc = request_irq(ipi, xics_ipi_action_direct, IRQF_DISABLED,				"IPI", NULL);	BUG_ON(rc);}#endif /* CONFIG_SMP */void xics_teardown_cpu(int secondary){	int cpu = smp_processor_id();	unsigned int ipi;	struct irq_desc *desc;	xics_set_cpu_priority(cpu, 0);	/*	 * Clear IPI	 */	if (firmware_has_feature(FW_FEATURE_LPAR))		lpar_qirr_info(cpu, 0xff);	else		direct_qirr_info(cpu, 0xff);	/*	 * we need to EOI the IPI if we got here from kexec down IPI	 *	 * probably need to check all the other interrupts too	 * should we be flagging idle loop instead?	 * or creating some task to be scheduled?	 */	ipi = irq_find_mapping(xics_host, XICS_IPI);	if (ipi == XICS_IRQ_SPURIOUS)		return;	desc = get_irq_desc(ipi);	if (desc->chip && desc->chip->eoi)		desc->chip->eoi(ipi);	/*	 * Some machines need to have at least one cpu in the GIQ,	 * so leave the master cpu in the group.	 */	if (secondary)		rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,				   (1UL << interrupt_server_size) - 1 -				   default_distrib_server, 0);}#ifdef CONFIG_HOTPLUG_CPU/* Interrupts are disabled. */void xics_migrate_irqs_away(void){	int status;	unsigned int irq, virq, cpu = smp_processor_id();	/* Reject any interrupt that was queued to us... */	xics_set_cpu_priority(cpu, 0);	/* remove ourselves from the global interrupt queue */	status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,		(1UL << interrupt_server_size) - 1 - default_distrib_server, 0);	WARN_ON(status < 0);	/* Allow IPIs again... */	xics_set_cpu_priority(cpu, DEFAULT_PRIORITY);	for_each_irq(virq) {		struct irq_desc *desc;		int xics_status[2];		unsigned long flags;		/* We cant set affinity on ISA interrupts */		if (virq < NUM_ISA_INTERRUPTS)			continue;		if (irq_map[virq].host != xics_host)			continue;		irq = (unsigned int)irq_map[virq].hwirq;		/* We need to get IPIs still. */		if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)			continue;		desc = get_irq_desc(virq);		/* We only need to migrate enabled IRQS */		if (desc == NULL || desc->chip == NULL		    || desc->action == NULL		    || desc->chip->set_affinity == NULL)			continue;		spin_lock_irqsave(&desc->lock, flags);		status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);		if (status) {			printk(KERN_ERR "migrate_irqs_away: irq=%u "					"ibm,get-xive returns %d\n",					virq, status);			goto unlock;		}		/*		 * We only support delivery to all cpus or to one cpu.		 * The irq has to be migrated only in the single cpu		 * case.		 */		if (xics_status[0] != get_hard_smp_processor_id(cpu))			goto unlock;		printk(KERN_WARNING "IRQ %u affinity broken off cpu %u\n",		       virq, cpu);		/* Reset affinity to all cpus */		desc->chip->set_affinity(virq, CPU_MASK_ALL);		irq_desc[irq].affinity = CPU_MASK_ALL;unlock:		spin_unlock_irqrestore(&desc->lock, flags);	}}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -