|  | @@ -192,3 +192,162 @@ static struct irq_domain_ops vic_irqdomain_ops = {
 | 
	
		
			
				|  |  |   * @base: The base address of the VIC.
 | 
	
		
			
				|  |  |   * @irq: The base IRQ for the VIC.
 | 
	
		
			
				|  |  |   * @valid_sources: bitmask of valid interrupts
 | 
	
		
			
				|  |  | + * @resume_sources: bitmask of interrupts allowed for resume sources.
 | 
	
		
			
				|  |  | + * @node: The device tree node associated with the VIC.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Register the VIC with the system device tree so that it can be notified
 | 
	
		
			
				|  |  | + * of suspend and resume requests and ensure that the correct actions are
 | 
	
		
			
				|  |  | + * taken to re-instate the settings on resume.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * This also configures the IRQ domain for the VIC.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void __init vic_register(void __iomem *base, unsigned int irq,
 | 
	
		
			
				|  |  | +				u32 valid_sources, u32 resume_sources,
 | 
	
		
			
				|  |  | +				struct device_node *node)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct vic_device *v;
 | 
	
		
			
				|  |  | +	int i;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (vic_id >= ARRAY_SIZE(vic_devices)) {
 | 
	
		
			
				|  |  | +		printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__);
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	v = &vic_devices[vic_id];
 | 
	
		
			
				|  |  | +	v->base = base;
 | 
	
		
			
				|  |  | +	v->valid_sources = valid_sources;
 | 
	
		
			
				|  |  | +	v->resume_sources = resume_sources;
 | 
	
		
			
				|  |  | +	v->irq = irq;
 | 
	
		
			
				|  |  | +	vic_id++;
 | 
	
		
			
				|  |  | +	v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
 | 
	
		
			
				|  |  | +					  &vic_irqdomain_ops, v);
 | 
	
		
			
				|  |  | +	/* create an IRQ mapping for each valid IRQ */
 | 
	
		
			
				|  |  | +	for (i = 0; i < fls(valid_sources); i++)
 | 
	
		
			
				|  |  | +		if (valid_sources & (1 << i))
 | 
	
		
			
				|  |  | +			irq_create_mapping(v->domain, i);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void vic_ack_irq(struct irq_data *d)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	void __iomem *base = irq_data_get_irq_chip_data(d);
 | 
	
		
			
				|  |  | +	unsigned int irq = d->hwirq;
 | 
	
		
			
				|  |  | +	writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
 | 
	
		
			
				|  |  | +	/* moreover, clear the soft-triggered, in case it was the reason */
 | 
	
		
			
				|  |  | +	writel(1 << irq, base + VIC_INT_SOFT_CLEAR);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void vic_mask_irq(struct irq_data *d)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	void __iomem *base = irq_data_get_irq_chip_data(d);
 | 
	
		
			
				|  |  | +	unsigned int irq = d->hwirq;
 | 
	
		
			
				|  |  | +	writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void vic_unmask_irq(struct irq_data *d)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	void __iomem *base = irq_data_get_irq_chip_data(d);
 | 
	
		
			
				|  |  | +	unsigned int irq = d->hwirq;
 | 
	
		
			
				|  |  | +	writel(1 << irq, base + VIC_INT_ENABLE);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(CONFIG_PM)
 | 
	
		
			
				|  |  | +static struct vic_device *vic_from_irq(unsigned int irq)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +        struct vic_device *v = vic_devices;
 | 
	
		
			
				|  |  | +	unsigned int base_irq = irq & ~31;
 | 
	
		
			
				|  |  | +	int id;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	for (id = 0; id < vic_id; id++, v++) {
 | 
	
		
			
				|  |  | +		if (v->irq == base_irq)
 | 
	
		
			
				|  |  | +			return v;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return NULL;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int vic_set_wake(struct irq_data *d, unsigned int on)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct vic_device *v = vic_from_irq(d->irq);
 | 
	
		
			
				|  |  | +	unsigned int off = d->hwirq;
 | 
	
		
			
				|  |  | +	u32 bit = 1 << off;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!v)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!(bit & v->resume_sources))
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (on)
 | 
	
		
			
				|  |  | +		v->resume_irqs |= bit;
 | 
	
		
			
				|  |  | +	else
 | 
	
		
			
				|  |  | +		v->resume_irqs &= ~bit;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +#define vic_set_wake NULL
 | 
	
		
			
				|  |  | +#endif /* CONFIG_PM */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static struct irq_chip vic_chip = {
 | 
	
		
			
				|  |  | +	.name		= "VIC",
 | 
	
		
			
				|  |  | +	.irq_ack	= vic_ack_irq,
 | 
	
		
			
				|  |  | +	.irq_mask	= vic_mask_irq,
 | 
	
		
			
				|  |  | +	.irq_unmask	= vic_unmask_irq,
 | 
	
		
			
				|  |  | +	.irq_set_wake	= vic_set_wake,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void __init vic_disable(void __iomem *base)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	writel(0, base + VIC_INT_SELECT);
 | 
	
		
			
				|  |  | +	writel(0, base + VIC_INT_ENABLE);
 | 
	
		
			
				|  |  | +	writel(~0, base + VIC_INT_ENABLE_CLEAR);
 | 
	
		
			
				|  |  | +	writel(0, base + VIC_ITCR);
 | 
	
		
			
				|  |  | +	writel(~0, base + VIC_INT_SOFT_CLEAR);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void __init vic_clear_interrupts(void __iomem *base)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	unsigned int i;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	writel(0, base + VIC_PL190_VECT_ADDR);
 | 
	
		
			
				|  |  | +	for (i = 0; i < 19; i++) {
 | 
	
		
			
				|  |  | +		unsigned int value;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		value = readl(base + VIC_PL190_VECT_ADDR);
 | 
	
		
			
				|  |  | +		writel(value, base + VIC_PL190_VECT_ADDR);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * The PL190 cell from ARM has been modified by ST to handle 64 interrupts.
 | 
	
		
			
				|  |  | + * The original cell has 32 interrupts, while the modified one has 64,
 | 
	
		
			
				|  |  | + * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case
 | 
	
		
			
				|  |  | + * the probe function is called twice, with base set to offset 000
 | 
	
		
			
				|  |  | + *  and 020 within the page. We call this "second block".
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
 | 
	
		
			
				|  |  | +			       u32 vic_sources, struct device_node *node)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	unsigned int i;
 | 
	
		
			
				|  |  | +	int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Disable all interrupts initially. */
 | 
	
		
			
				|  |  | +	vic_disable(base);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * Make sure we clear all existing interrupts. The vector registers
 | 
	
		
			
				|  |  | +	 * in this cell are after the second block of general registers,
 | 
	
		
			
				|  |  | +	 * so we can address them using standard offsets, but only from
 | 
	
		
			
				|  |  | +	 * the second base address, which is 0x20 in the page
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	if (vic_2nd_block) {
 | 
	
		
			
				|  |  | +		vic_clear_interrupts(base);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		/* ST has 16 vectors as well, but we don't enable them by now */
 | 
	
		
			
				|  |  | +		for (i = 0; i < 16; i++) {
 | 
	
		
			
				|  |  | +			void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
 | 
	
		
			
				|  |  | +			writel(0, reg);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		writel(32, base + VIC_PL190_DEF_VECT_ADDR);
 | 
	
		
			
				|  |  | +	}
 |