diff options
Diffstat (limited to 'drivers/sh/intc')
| -rw-r--r-- | drivers/sh/intc/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/sh/intc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/sh/intc/access.c | 45 | ||||
| -rw-r--r-- | drivers/sh/intc/balancing.c | 2 | ||||
| -rw-r--r-- | drivers/sh/intc/chip.c | 53 | ||||
| -rw-r--r-- | drivers/sh/intc/core.c | 193 | ||||
| -rw-r--r-- | drivers/sh/intc/dynamic.c | 64 | ||||
| -rw-r--r-- | drivers/sh/intc/handle.c | 7 | ||||
| -rw-r--r-- | drivers/sh/intc/internals.h | 26 | ||||
| -rw-r--r-- | drivers/sh/intc/irqdomain.c | 68 | ||||
| -rw-r--r-- | drivers/sh/intc/userimask.c | 17 | ||||
| -rw-r--r-- | drivers/sh/intc/virq.c | 26 | 
12 files changed, 282 insertions, 227 deletions
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig index c88cbccc62b..60228fae943 100644 --- a/drivers/sh/intc/Kconfig +++ b/drivers/sh/intc/Kconfig @@ -1,8 +1,12 @@ +config SH_INTC +	def_bool y +	select IRQ_DOMAIN +  comment "Interrupt controller options"  config INTC_USERIMASK  	bool "Userspace interrupt masking support" -	depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) +	depends on (SUPERH && CPU_SH4A) || COMPILE_TEST  	help  	  This enables support for hardware-assisted userspace hardirq  	  masking. diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile index bb5df868d77..54ec2a0643d 100644 --- a/drivers/sh/intc/Makefile +++ b/drivers/sh/intc/Makefile @@ -1,4 +1,4 @@ -obj-y 	:= access.o chip.o core.o dynamic.o handle.o virq.o +obj-y 	:= access.o chip.o core.o handle.o irqdomain.o virq.o  obj-$(CONFIG_INTC_BALANCING)		+= balancing.o  obj-$(CONFIG_INTC_USERIMASK)		+= userimask.o diff --git a/drivers/sh/intc/access.c b/drivers/sh/intc/access.c index f892ae1d212..114390f967d 100644 --- a/drivers/sh/intc/access.c +++ b/drivers/sh/intc/access.c @@ -75,54 +75,61 @@ unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle  static unsigned long test_8(unsigned long addr, unsigned long h,  			    unsigned long ignore)  { -	return intc_get_field_from_handle(__raw_readb(addr), h); +	void __iomem *ptr = (void __iomem *)addr; +	return intc_get_field_from_handle(__raw_readb(ptr), h);  }  static unsigned long test_16(unsigned long addr, unsigned long h,  			     unsigned long ignore)  { -	return intc_get_field_from_handle(__raw_readw(addr), h); +	void __iomem *ptr = (void __iomem *)addr; +	return intc_get_field_from_handle(__raw_readw(ptr), h);  }  static unsigned long test_32(unsigned long addr, unsigned long h,  			     unsigned long ignore)  { -	return intc_get_field_from_handle(__raw_readl(addr), h); +	void __iomem *ptr = (void __iomem *)addr; +	return intc_get_field_from_handle(__raw_readl(ptr), h);  }  static unsigned long write_8(unsigned long addr, unsigned long h,  			     unsigned long data)  { -	__raw_writeb(intc_set_field_from_handle(0, data, h), addr); -	(void)__raw_readb(addr);	/* Defeat write posting */ +	void __iomem *ptr = (void __iomem *)addr; +	__raw_writeb(intc_set_field_from_handle(0, data, h), ptr); +	(void)__raw_readb(ptr);	/* Defeat write posting */  	return 0;  }  static unsigned long write_16(unsigned long addr, unsigned long h,  			      unsigned long data)  { -	__raw_writew(intc_set_field_from_handle(0, data, h), addr); -	(void)__raw_readw(addr);	/* Defeat write posting */ +	void __iomem *ptr = (void __iomem *)addr; +	__raw_writew(intc_set_field_from_handle(0, data, h), ptr); +	(void)__raw_readw(ptr);	/* Defeat write posting */  	return 0;  }  static unsigned long write_32(unsigned long addr, unsigned long h,  			      unsigned long data)  { -	__raw_writel(intc_set_field_from_handle(0, data, h), addr); -	(void)__raw_readl(addr);	/* Defeat write posting */ +	void __iomem *ptr = (void __iomem *)addr; +	__raw_writel(intc_set_field_from_handle(0, data, h), ptr); +	(void)__raw_readl(ptr);	/* Defeat write posting */  	return 0;  }  static unsigned long modify_8(unsigned long addr, unsigned long h,  			      unsigned long data)  { +	void __iomem *ptr = (void __iomem *)addr;  	unsigned long flags;  	unsigned int value;  	local_irq_save(flags); -	value = intc_set_field_from_handle(__raw_readb(addr), data, h); -	__raw_writeb(value, addr); -	(void)__raw_readb(addr);	/* Defeat write posting */ +	value = intc_set_field_from_handle(__raw_readb(ptr), data, h); +	__raw_writeb(value, ptr); +	(void)__raw_readb(ptr);	/* Defeat write posting */  	local_irq_restore(flags);  	return 0;  } @@ -130,12 +137,13 @@ static unsigned long modify_8(unsigned long addr, unsigned long h,  static unsigned long modify_16(unsigned long addr, unsigned long h,  			       unsigned long data)  { +	void __iomem *ptr = (void __iomem *)addr;  	unsigned long flags;  	unsigned int value;  	local_irq_save(flags); -	value = intc_set_field_from_handle(__raw_readw(addr), data, h); -	__raw_writew(value, addr); -	(void)__raw_readw(addr);	/* Defeat write posting */ +	value = intc_set_field_from_handle(__raw_readw(ptr), data, h); +	__raw_writew(value, ptr); +	(void)__raw_readw(ptr);	/* Defeat write posting */  	local_irq_restore(flags);  	return 0;  } @@ -143,12 +151,13 @@ static unsigned long modify_16(unsigned long addr, unsigned long h,  static unsigned long modify_32(unsigned long addr, unsigned long h,  			       unsigned long data)  { +	void __iomem *ptr = (void __iomem *)addr;  	unsigned long flags;  	unsigned int value;  	local_irq_save(flags); -	value = intc_set_field_from_handle(__raw_readl(addr), data, h); -	__raw_writel(value, addr); -	(void)__raw_readl(addr);	/* Defeat write posting */ +	value = intc_set_field_from_handle(__raw_readl(ptr), data, h); +	__raw_writel(value, ptr); +	(void)__raw_readl(ptr);	/* Defeat write posting */  	local_irq_restore(flags);  	return 0;  } diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c index cec7a96f2c0..bc780807ccb 100644 --- a/drivers/sh/intc/balancing.c +++ b/drivers/sh/intc/balancing.c @@ -9,7 +9,7 @@   */  #include "internals.h" -static unsigned long dist_handle[NR_IRQS]; +static unsigned long dist_handle[INTC_NR_IRQS];  void intc_balancing_enable(unsigned int irq)  { diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c index de885a0f917..46427b48e2f 100644 --- a/drivers/sh/intc/chip.c +++ b/drivers/sh/intc/chip.c @@ -2,13 +2,14 @@   * IRQ chip definitions for INTC IRQs.   *   * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt + * Copyright (C) 2009 - 2012 Paul Mundt   *   * This file is subject to the terms and conditions of the GNU General Public   * License.  See the file "COPYING" in the main directory of this archive   * for more details.   */  #include <linux/cpumask.h> +#include <linux/bsearch.h>  #include <linux/io.h>  #include "internals.h" @@ -58,11 +59,6 @@ static void intc_disable(struct irq_data *data)  	}  } -static int intc_set_wake(struct irq_data *data, unsigned int on) -{ -	return 0; /* allow wakeup, but setup hardware in intc_suspend() */ -} -  #ifdef CONFIG_SMP  /*   * This is held with the irq desc lock held, so we don't require any @@ -78,7 +74,7 @@ static int intc_set_affinity(struct irq_data *data,  	cpumask_copy(data->affinity, cpumask); -	return 0; +	return IRQ_SET_MASK_OK_NOCOPY;  }  #endif @@ -87,7 +83,7 @@ static void intc_mask_ack(struct irq_data *data)  	unsigned int irq = data->irq;  	struct intc_desc_int *d = get_intc_desc(irq);  	unsigned long handle = intc_get_ack_handle(irq); -	unsigned long addr; +	void __iomem *addr;  	intc_disable(data); @@ -95,7 +91,7 @@ static void intc_mask_ack(struct irq_data *data)  	if (handle) {  		unsigned int value; -		addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); +		addr = (void __iomem *)INTC_REG(d, _INTC_ADDR_D(handle), 0);  		value = intc_set_field_from_handle(0, 1, handle);  		switch (_INTC_FN(handle)) { @@ -122,28 +118,12 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,  					     unsigned int nr_hp,  					     unsigned int irq)  { -	int i; - -	/* -	 * this doesn't scale well, but... -	 * -	 * this function should only be used for cerain uncommon -	 * operations such as intc_set_priority() and intc_set_type() -	 * and in those rare cases performance doesn't matter that much. -	 * keeping the memory footprint low is more important. -	 * -	 * one rather simple way to speed this up and still keep the -	 * memory footprint down is to make sure the array is sorted -	 * and then perform a bisect to lookup the irq. -	 */ -	for (i = 0; i < nr_hp; i++) { -		if ((hp + i)->irq != irq) -			continue; +	struct intc_handle_int key; -		return hp + i; -	} +	key.irq = irq; +	key.handle = 0; -	return NULL; +	return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp);  }  int intc_set_priority(unsigned int irq, unsigned int prio) @@ -173,7 +153,8 @@ int intc_set_priority(unsigned int irq, unsigned int prio)  	return 0;  } -#define VALID(x) (x | 0x80) +#define SENSE_VALID_FLAG 0x80 +#define VALID(x) (x | SENSE_VALID_FLAG)  static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {  	[IRQ_TYPE_EDGE_FALLING] = VALID(0), @@ -185,6 +166,9 @@ static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {      !defined(CONFIG_CPU_SUBTYPE_SH7709)  	[IRQ_TYPE_LEVEL_HIGH] = VALID(3),  #endif +#if defined(CONFIG_ARM) /* all recent SH-Mobile / R-Mobile ARM support this */ +	[IRQ_TYPE_EDGE_BOTH] = VALID(4), +#endif  };  static int intc_set_type(struct irq_data *data, unsigned int type) @@ -198,8 +182,14 @@ static int intc_set_type(struct irq_data *data, unsigned int type)  	if (!value)  		return -EINVAL; +	value &= ~SENSE_VALID_FLAG; +  	ihp = intc_find_irq(d->sense, d->nr_sense, irq);  	if (ihp) { +		/* PINT has 2-bit sense registers, should fail on EDGE_BOTH */ +		if (value >= (1 << _INTC_WIDTH(ihp->handle))) +			return -EINVAL; +  		addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);  		intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);  	} @@ -213,10 +203,9 @@ struct irq_chip intc_irq_chip	= {  	.irq_mask_ack		= intc_mask_ack,  	.irq_enable		= intc_enable,  	.irq_disable		= intc_disable, -	.irq_shutdown		= intc_disable,  	.irq_set_type		= intc_set_type, -	.irq_set_wake		= intc_set_wake,  #ifdef CONFIG_SMP  	.irq_set_affinity	= intc_set_affinity,  #endif +	.flags			= IRQCHIP_SKIP_SET_WAKE,  }; diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index e5e9e6735f7..81f22980b2d 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -2,7 +2,7 @@   * Shared interrupt handling code for IPR and INTC2 types of IRQs.   *   * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt + * Copyright (C) 2009 - 2012 Paul Mundt   *   * Based on intc2.c and ipr.c   * @@ -22,24 +22,29 @@  #include <linux/irq.h>  #include <linux/io.h>  #include <linux/slab.h> +#include <linux/stat.h>  #include <linux/interrupt.h>  #include <linux/sh_intc.h> -#include <linux/sysdev.h> +#include <linux/irqdomain.h> +#include <linux/device.h> +#include <linux/syscore_ops.h>  #include <linux/list.h>  #include <linux/spinlock.h>  #include <linux/radix-tree.h> +#include <linux/export.h> +#include <linux/sort.h>  #include "internals.h"  LIST_HEAD(intc_list);  DEFINE_RAW_SPINLOCK(intc_big_lock); -unsigned int nr_intc_controllers; +static unsigned int nr_intc_controllers;  /*   * Default priority level   * - this needs to be at least 2 for 5-bit priorities on 7780   */  static unsigned int default_prio_level = 2;	/* 2 - 16 */ -static unsigned int intc_prio_level[NR_IRQS];	/* for now */ +static unsigned int intc_prio_level[INTC_NR_IRQS];	/* for now */  unsigned int intc_get_dfl_prio_level(void)  { @@ -62,7 +67,7 @@ void intc_set_prio_level(unsigned int irq, unsigned int level)  static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)  { -	generic_handle_irq((unsigned int)get_irq_data(irq)); +	generic_handle_irq((unsigned int)irq_get_handler_data(irq));  }  static void __init intc_register_irq(struct intc_desc *desc, @@ -75,12 +80,6 @@ static void __init intc_register_irq(struct intc_desc *desc,  	unsigned int data[2], primary;  	unsigned long flags; -	/* -	 * Register the IRQ position with the global IRQ map, then insert -	 * it in to the radix tree. -	 */ -	irq_reserve_irq(irq); -  	raw_spin_lock_irqsave(&intc_big_lock, flags);  	radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq));  	raw_spin_unlock_irqrestore(&intc_big_lock, flags); @@ -115,9 +114,9 @@ static void __init intc_register_irq(struct intc_desc *desc,  	irq_data = irq_get_irq_data(irq);  	disable_irq_nosync(irq); -	set_irq_chip_and_handler_name(irq, &d->chip, -				      handle_level_irq, "level"); -	set_irq_chip_data(irq, (void *)data[primary]); +	irq_set_chip_and_handler_name(irq, &d->chip, handle_level_irq, +				      "level"); +	irq_set_chip_data(irq, (void *)data[primary]);  	/*  	 * set priority level @@ -198,6 +197,7 @@ int __init register_intc_controller(struct intc_desc *desc)  	list_add_tail(&d->list, &intc_list);  	raw_spin_lock_init(&d->lock); +	INIT_RADIX_TREE(&d->tree, GFP_ATOMIC);  	d->index = nr_intc_controllers; @@ -263,6 +263,9 @@ int __init register_intc_controller(struct intc_desc *desc)  			k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);  			k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);  		} + +		sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio), +		     intc_handle_int_cmp, NULL);  	}  	if (hw->sense_regs) { @@ -273,6 +276,9 @@ int __init register_intc_controller(struct intc_desc *desc)  		for (i = 0; i < hw->nr_sense_regs; i++)  			k += save_reg(d, k, hw->sense_regs[i].reg, 0); + +		sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense), +		     intc_handle_int_cmp, NULL);  	}  	if (hw->subgroups) @@ -299,6 +305,8 @@ int __init register_intc_controller(struct intc_desc *desc)  	BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ +	intc_irq_domain_init(d, hw); +  	/* register the vectors one by one */  	for (i = 0; i < hw->nr_vectors; i++) {  		struct intc_vect *vect = hw->vectors + i; @@ -308,10 +316,18 @@ int __init register_intc_controller(struct intc_desc *desc)  		if (!vect->enum_id)  			continue; -		res = irq_alloc_desc_at(irq, numa_node_id()); -		if (res != irq && res != -EEXIST) { -			pr_err("can't get irq_desc for %d\n", irq); -			continue; +		res = irq_create_identity_mapping(d->domain, irq); +		if (unlikely(res)) { +			if (res == -EEXIST) { +				res = irq_domain_associate(d->domain, irq, irq); +				if (unlikely(res)) { +					pr_err("domain association failure\n"); +					continue; +				} +			} else { +				pr_err("can't identity map IRQ %d\n", irq); +				continue; +			}  		}  		intc_irq_xlate_set(irq, vect->enum_id, d); @@ -329,18 +345,29 @@ int __init register_intc_controller(struct intc_desc *desc)  			 * IRQ support, each vector still needs to have  			 * its own backing irq_desc.  			 */ -			res = irq_alloc_desc_at(irq2, numa_node_id()); -			if (res != irq2 && res != -EEXIST) { -				pr_err("can't get irq_desc for %d\n", irq2); -				continue; +			res = irq_create_identity_mapping(d->domain, irq2); +			if (unlikely(res)) { +				if (res == -EEXIST) { +					res = irq_domain_associate(d->domain, +								   irq2, irq2); +					if (unlikely(res)) { +						pr_err("domain association " +						       "failure\n"); +						continue; +					} +				} else { +					pr_err("can't identity map IRQ %d\n", +					       irq); +					continue; +				}  			}  			vect2->enum_id = 0;  			/* redirect this interrupts to the first one */ -			set_irq_chip(irq2, &dummy_irq_chip); -			set_irq_chained_handler(irq2, intc_redirect_irq); -			set_irq_data(irq2, (void *)irq); +			irq_set_chip(irq2, &dummy_irq_chip); +			irq_set_chained_handler(irq2, intc_redirect_irq); +			irq_set_handler_data(irq2, (void *)irq);  		}  	} @@ -350,6 +377,8 @@ int __init register_intc_controller(struct intc_desc *desc)  	if (desc->force_enable)  		intc_enable_disable_enum(desc, d, desc->force_enable, 1); +	d->skip_suspend = desc->skip_syscore_suspend; +  	nr_intc_controllers++;  	return 0; @@ -375,108 +404,108 @@ err0:  	return -ENOMEM;  } -static ssize_t -show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +static int intc_suspend(void)  {  	struct intc_desc_int *d; -	d = container_of(dev, struct intc_desc_int, sysdev); +	list_for_each_entry(d, &intc_list, list) { +		int irq; -	return sprintf(buf, "%s\n", d->chip.name); -} +		if (d->skip_suspend) +			continue; -static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); +		/* enable wakeup irqs belonging to this intc controller */ +		for_each_active_irq(irq) { +			struct irq_data *data; +			struct irq_chip *chip; + +			data = irq_get_irq_data(irq); +			chip = irq_data_get_irq_chip(data); +			if (chip != &d->chip) +				continue; +			if (irqd_is_wakeup_set(data)) +				chip->irq_enable(data); +		} +	} +	return 0; +} -static int intc_suspend(struct sys_device *dev, pm_message_t state) +static void intc_resume(void)  {  	struct intc_desc_int *d; -	struct irq_data *data; -	struct irq_desc *desc; -	struct irq_chip *chip; -	int irq; -	/* get intc controller associated with this sysdev */ -	d = container_of(dev, struct intc_desc_int, sysdev); +	list_for_each_entry(d, &intc_list, list) { +		int irq; -	switch (state.event) { -	case PM_EVENT_ON: -		if (d->state.event != PM_EVENT_FREEZE) -			break; +		if (d->skip_suspend) +			continue;  		for_each_active_irq(irq) { -			desc = irq_to_desc(irq); +			struct irq_data *data; +			struct irq_chip *chip; +  			data = irq_get_irq_data(irq);  			chip = irq_data_get_irq_chip(data); -  			/*  			 * This will catch the redirect and VIRQ cases  			 * due to the dummy_irq_chip being inserted.  			 */  			if (chip != &d->chip)  				continue; -			if (desc->status & IRQ_DISABLED) +			if (irqd_irq_disabled(data))  				chip->irq_disable(data);  			else  				chip->irq_enable(data);  		} -		break; -	case PM_EVENT_FREEZE: -		/* nothing has to be done */ -		break; -	case PM_EVENT_SUSPEND: -		/* enable wakeup irqs belonging to this intc controller */ -		for_each_active_irq(irq) { -			desc = irq_to_desc(irq); -			data = irq_get_irq_data(irq); -			chip = irq_data_get_irq_chip(data); - -			if (chip != &d->chip) -				continue; -			if ((desc->status & IRQ_WAKEUP)) -				chip->irq_enable(data); -		} -		break;  	} +} -	d->state = state; +struct syscore_ops intc_syscore_ops = { +	.suspend	= intc_suspend, +	.resume		= intc_resume, +}; -	return 0; -} +struct bus_type intc_subsys = { +	.name		= "intc", +	.dev_name	= "intc", +}; -static int intc_resume(struct sys_device *dev) +static ssize_t +show_intc_name(struct device *dev, struct device_attribute *attr, char *buf)  { -	return intc_suspend(dev, PMSG_ON); +	struct intc_desc_int *d; + +	d = container_of(dev, struct intc_desc_int, dev); + +	return sprintf(buf, "%s\n", d->chip.name);  } -struct sysdev_class intc_sysdev_class = { -	.name		= "intc", -	.suspend	= intc_suspend, -	.resume		= intc_resume, -}; +static DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL); -/* register this intc as sysdev to allow suspend/resume */ -static int __init register_intc_sysdevs(void) +static int __init register_intc_devs(void)  {  	struct intc_desc_int *d;  	int error; -	error = sysdev_class_register(&intc_sysdev_class); +	register_syscore_ops(&intc_syscore_ops); + +	error = subsys_system_register(&intc_subsys, NULL);  	if (!error) {  		list_for_each_entry(d, &intc_list, list) { -			d->sysdev.id = d->index; -			d->sysdev.cls = &intc_sysdev_class; -			error = sysdev_register(&d->sysdev); +			d->dev.id = d->index; +			d->dev.bus = &intc_subsys; +			error = device_register(&d->dev);  			if (error == 0) -				error = sysdev_create_file(&d->sysdev, -							   &attr_name); +				error = device_create_file(&d->dev, +							   &dev_attr_name);  			if (error)  				break;  		}  	}  	if (error) -		pr_err("sysdev registration error\n"); +		pr_err("device registration error\n");  	return error;  } -device_initcall(register_intc_sysdevs); +device_initcall(register_intc_devs); diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c deleted file mode 100644 index a3677c9dfe3..00000000000 --- a/drivers/sh/intc/dynamic.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Dynamic IRQ management - * - * Copyright (C) 2010  Paul Mundt - * - * Modelled after arch/x86/kernel/apic/io_apic.c - * - * This file is subject to the terms and conditions of the GNU General Public - * License.  See the file "COPYING" in the main directory of this archive - * for more details. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include <linux/irq.h> -#include <linux/bitmap.h> -#include <linux/spinlock.h> -#include "internals.h" /* only for activate_irq() damage.. */ - -/* - * The IRQ bitmap provides a global map of bound IRQ vectors for a - * given platform. Allocation of IRQs are either static through the CPU - * vector map, or dynamic in the case of board mux vectors or MSI. - * - * As this is a central point for all IRQ controllers on the system, - * each of the available sources are mapped out here. This combined with - * sparseirq makes it quite trivial to keep the vector map tightly packed - * when dynamically creating IRQs, as well as tying in to otherwise - * unused irq_desc positions in the sparse array. - */ - -/* - * Dynamic IRQ allocation and deallocation - */ -unsigned int create_irq_nr(unsigned int irq_want, int node) -{ -	int irq = irq_alloc_desc_at(irq_want, node); -	if (irq < 0) -		return 0; - -	activate_irq(irq); -	return irq; -} - -int create_irq(void) -{ -	int irq = irq_alloc_desc(numa_node_id()); -	if (irq >= 0) -		activate_irq(irq); - -	return irq; -} - -void destroy_irq(unsigned int irq) -{ -	irq_free_desc(irq); -} - -void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) -{ -	int i; - -	for (i = 0; i < nr_vecs; i++) -		irq_reserve_irq(evt2irq(vectors[i].vect)); -} diff --git a/drivers/sh/intc/handle.c b/drivers/sh/intc/handle.c index 057ce56829b..7863a44918a 100644 --- a/drivers/sh/intc/handle.c +++ b/drivers/sh/intc/handle.c @@ -13,7 +13,7 @@  #include <linux/spinlock.h>  #include "internals.h" -static unsigned long ack_handle[NR_IRQS]; +static unsigned long ack_handle[INTC_NR_IRQS];  static intc_enum __init intc_grp_id(struct intc_desc *desc,  				    intc_enum enum_id) @@ -172,9 +172,8 @@ intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,  	return 0;  } -static unsigned int __init intc_ack_data(struct intc_desc *desc, -					  struct intc_desc_int *d, -					  intc_enum enum_id) +static unsigned int intc_ack_data(struct intc_desc *desc, +				  struct intc_desc_int *d, intc_enum enum_id)  {  	struct intc_mask_reg *mr = desc->hw.ack_regs;  	unsigned int i, j, fn, mode; diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h index 0cf8260971d..7dff08e2a07 100644 --- a/drivers/sh/intc/internals.h +++ b/drivers/sh/intc/internals.h @@ -1,10 +1,11 @@  #include <linux/sh_intc.h>  #include <linux/irq.h> +#include <linux/irqdomain.h>  #include <linux/list.h>  #include <linux/kernel.h>  #include <linux/types.h>  #include <linux/radix-tree.h> -#include <linux/sysdev.h> +#include <linux/device.h>  #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \  	((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -51,9 +52,8 @@ struct intc_subgroup_entry {  struct intc_desc_int {  	struct list_head list; -	struct sys_device sysdev; +	struct device dev;  	struct radix_tree_root tree; -	pm_message_t state;  	raw_spinlock_t lock;  	unsigned int index;  	unsigned long *reg; @@ -67,7 +67,9 @@ struct intc_desc_int {  	unsigned int nr_sense;  	struct intc_window *window;  	unsigned int nr_windows; +	struct irq_domain *domain;  	struct irq_chip chip; +	bool skip_suspend;  }; @@ -87,7 +89,7 @@ enum {	MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */  static inline struct intc_desc_int *get_intc_desc(unsigned int irq)  { -	struct irq_chip *chip = get_irq_chip(irq); +	struct irq_chip *chip = irq_get_chip(irq);  	return container_of(chip, struct intc_desc_int, chip);  } @@ -104,10 +106,18 @@ static inline void activate_irq(int irq)  	set_irq_flags(irq, IRQF_VALID);  #else  	/* same effect on other architectures */ -	set_irq_noprobe(irq); +	irq_set_noprobe(irq);  #endif  } +static inline int intc_handle_int_cmp(const void *a, const void *b) +{ +	const struct intc_handle_int *_a = a; +	const struct intc_handle_int *_b = b; + +	return _a->irq - _b->irq; +} +  /* access.c */  extern unsigned long  (*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data); @@ -157,8 +167,7 @@ void _intc_enable(struct irq_data *data, unsigned long handle);  /* core.c */  extern struct list_head intc_list;  extern raw_spinlock_t intc_big_lock; -extern unsigned int nr_intc_controllers; -extern struct sysdev_class intc_sysdev_class; +extern struct bus_type intc_subsys;  unsigned int intc_get_dfl_prio_level(void);  unsigned int intc_get_prio_level(unsigned int irq); @@ -180,6 +189,9 @@ unsigned long intc_get_ack_handle(unsigned int irq);  void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d,  			      intc_enum enum_id, int enable); +/* irqdomain.c */ +void intc_irq_domain_init(struct intc_desc_int *d, struct intc_hw_desc *hw); +  /* virq.c */  void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d);  void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d); diff --git a/drivers/sh/intc/irqdomain.c b/drivers/sh/intc/irqdomain.c new file mode 100644 index 00000000000..3968f1c3c5c --- /dev/null +++ b/drivers/sh/intc/irqdomain.c @@ -0,0 +1,68 @@ +/* + * IRQ domain support for SH INTC subsystem + * + * Copyright (C) 2012  Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) "intc: " fmt + +#include <linux/irqdomain.h> +#include <linux/sh_intc.h> +#include <linux/export.h> +#include "internals.h" + +/** + * intc_irq_domain_evt_xlate() - Generic xlate for vectored IRQs. + * + * This takes care of exception vector to hwirq translation through + * by way of evt2irq() translation. + * + * Note: For platforms that use a flat vector space without INTEVT this + * basically just mimics irq_domain_xlate_onecell() by way of a nopped + * out evt2irq() implementation. + */ +static int intc_evt_xlate(struct irq_domain *d, struct device_node *ctrlr, +			  const u32 *intspec, unsigned int intsize, +			  unsigned long *out_hwirq, unsigned int *out_type) +{ +	if (WARN_ON(intsize < 1)) +		return -EINVAL; + +	*out_hwirq = evt2irq(intspec[0]); +	*out_type = IRQ_TYPE_NONE; + +	return 0; +} + +static const struct irq_domain_ops intc_evt_ops = { +	.xlate		= intc_evt_xlate, +}; + +void __init intc_irq_domain_init(struct intc_desc_int *d, +				 struct intc_hw_desc *hw) +{ +	unsigned int irq_base, irq_end; + +	/* +	 * Quick linear revmap check +	 */ +	irq_base = evt2irq(hw->vectors[0].vect); +	irq_end = evt2irq(hw->vectors[hw->nr_vectors - 1].vect); + +	/* +	 * Linear domains have a hard-wired assertion that IRQs start at +	 * 0 in order to make some performance optimizations. Lamely +	 * restrict the linear case to these conditions here, taking the +	 * tree penalty for linear cases with non-zero hwirq bases. +	 */ +	if (irq_base == 0 && irq_end == (irq_base + hw->nr_vectors - 1)) +		d->domain = irq_domain_add_linear(NULL, hw->nr_vectors, +						  &intc_evt_ops, NULL); +	else +		d->domain = irq_domain_add_tree(NULL, &intc_evt_ops, NULL); + +	BUG_ON(!d->domain); +} diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c index e32304b66cf..e649ceaaa41 100644 --- a/drivers/sh/intc/userimask.c +++ b/drivers/sh/intc/userimask.c @@ -10,24 +10,25 @@  #define pr_fmt(fmt) "intc: " fmt  #include <linux/errno.h> -#include <linux/sysdev.h> +#include <linux/device.h>  #include <linux/init.h>  #include <linux/io.h> +#include <linux/stat.h>  #include <asm/sizes.h>  #include "internals.h"  static void __iomem *uimask;  static ssize_t -show_intc_userimask(struct sysdev_class *cls, -		    struct sysdev_class_attribute *attr, char *buf) +show_intc_userimask(struct device *dev, +		    struct device_attribute *attr, char *buf)  {  	return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);  }  static ssize_t -store_intc_userimask(struct sysdev_class *cls, -		     struct sysdev_class_attribute *attr, +store_intc_userimask(struct device *dev, +		     struct device_attribute *attr,  		     const char *buf, size_t count)  {  	unsigned long level; @@ -54,8 +55,8 @@ store_intc_userimask(struct sysdev_class *cls,  	return count;  } -static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, -			 show_intc_userimask, store_intc_userimask); +static DEVICE_ATTR(userimask, S_IRUSR | S_IWUSR, +		   show_intc_userimask, store_intc_userimask);  static int __init userimask_sysdev_init(void) @@ -63,7 +64,7 @@ static int __init userimask_sysdev_init(void)  	if (unlikely(!uimask))  		return -ENXIO; -	return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask); +	return device_create_file(intc_subsys.dev_root, &dev_attr_userimask);  }  late_initcall(userimask_sysdev_init); diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c index e5bf5d3c698..f30ac9354ff 100644 --- a/drivers/sh/intc/virq.c +++ b/drivers/sh/intc/virq.c @@ -14,9 +14,10 @@  #include <linux/list.h>  #include <linux/radix-tree.h>  #include <linux/spinlock.h> +#include <linux/export.h>  #include "internals.h" -static struct intc_map_entry intc_irq_xlate[NR_IRQS]; +static struct intc_map_entry intc_irq_xlate[INTC_NR_IRQS];  struct intc_virq_list {  	unsigned int irq; @@ -110,7 +111,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc)  {  	struct irq_data *data = irq_get_irq_data(irq);  	struct irq_chip *chip = irq_data_get_irq_chip(data); -	struct intc_virq_list *entry, *vlist = irq_data_get_irq_data(data); +	struct intc_virq_list *entry, *vlist = irq_data_get_irq_handler_data(data);  	struct intc_desc_int *d = get_intc_desc(irq);  	chip->irq_mask_ack(data); @@ -118,7 +119,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc)  	for_each_virq(entry, vlist) {  		unsigned long addr, handle; -		handle = (unsigned long)get_irq_data(entry->irq); +		handle = (unsigned long)irq_get_handler_data(entry->irq);  		addr = INTC_REG(d, _INTC_ADDR_E(handle), 0);  		if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) @@ -215,27 +216,34 @@ restart:  		entry = radix_tree_deref_slot((void **)entries[i]);  		if (unlikely(!entry))  			continue; -		if (unlikely(entry == RADIX_TREE_RETRY)) +		if (radix_tree_deref_retry(entry))  			goto restart; -		irq = create_irq(); +		irq = irq_alloc_desc(numa_node_id());  		if (unlikely(irq < 0)) {  			pr_err("no more free IRQs, bailing..\n");  			break;  		} +		activate_irq(irq); +  		pr_info("Setting up a chained VIRQ from %d -> %d\n",  			irq, entry->pirq);  		intc_irq_xlate_set(irq, entry->enum_id, d); -		set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq), +		irq_set_chip_and_handler_name(irq, irq_get_chip(entry->pirq),  					      handle_simple_irq, "virq"); -		set_irq_chip_data(irq, get_irq_chip_data(entry->pirq)); +		irq_set_chip_data(irq, irq_get_chip_data(entry->pirq)); + +		irq_set_handler_data(irq, (void *)entry->handle); -		set_irq_data(irq, (void *)entry->handle); +		/* +		 * Set the virtual IRQ as non-threadable. +		 */ +		irq_set_nothread(irq); -		set_irq_chained_handler(entry->pirq, intc_virq_handler); +		irq_set_chained_handler(entry->pirq, intc_virq_handler);  		add_virq_to_pirq(entry->pirq, irq);  		radix_tree_tag_clear(&d->tree, entry->enum_id,  | 
