chip.c 5.4 KB
Newer Older
P
Paul Mundt 已提交
1 2 3 4
/*
 * IRQ chip definitions for INTC IRQs.
 *
 * Copyright (C) 2007, 2008 Magnus Damm
P
Paul Mundt 已提交
5
 * Copyright (C) 2009 - 2012 Paul Mundt
P
Paul Mundt 已提交
6 7 8 9 10 11
 *
 * 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>
P
Paul Mundt 已提交
12
#include <linux/bsearch.h>
P
Paul Mundt 已提交
13 14 15
#include <linux/io.h>
#include "internals.h"

P
Paul Mundt 已提交
16
void _intc_enable(struct irq_data *data, unsigned long handle)
P
Paul Mundt 已提交
17
{
P
Paul Mundt 已提交
18
	unsigned int irq = data->irq;
P
Paul Mundt 已提交
19 20 21 22 23 24
	struct intc_desc_int *d = get_intc_desc(irq);
	unsigned long addr;
	unsigned int cpu;

	for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
#ifdef CONFIG_SMP
P
Paul Mundt 已提交
25
		if (!cpumask_test_cpu(cpu, data->affinity))
P
Paul Mundt 已提交
26 27 28 29 30 31 32 33 34 35
			continue;
#endif
		addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
		intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
						    [_INTC_FN(handle)], irq);
	}

	intc_balancing_enable(irq);
}

P
Paul Mundt 已提交
36
static void intc_enable(struct irq_data *data)
P
Paul Mundt 已提交
37
{
P
Paul Mundt 已提交
38
	_intc_enable(data, (unsigned long)irq_data_get_irq_chip_data(data));
P
Paul Mundt 已提交
39 40
}

P
Paul Mundt 已提交
41
static void intc_disable(struct irq_data *data)
P
Paul Mundt 已提交
42
{
P
Paul Mundt 已提交
43
	unsigned int irq = data->irq;
P
Paul Mundt 已提交
44
	struct intc_desc_int *d = get_intc_desc(irq);
P
Paul Mundt 已提交
45
	unsigned long handle = (unsigned long)irq_data_get_irq_chip_data(data);
P
Paul Mundt 已提交
46 47 48 49 50 51 52
	unsigned long addr;
	unsigned int cpu;

	intc_balancing_disable(irq);

	for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
#ifdef CONFIG_SMP
P
Paul Mundt 已提交
53
		if (!cpumask_test_cpu(cpu, data->affinity))
P
Paul Mundt 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67
			continue;
#endif
		addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
		intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
						     [_INTC_FN(handle)], irq);
	}
}

#ifdef CONFIG_SMP
/*
 * This is held with the irq desc lock held, so we don't require any
 * additional locking here at the intc desc level. The affinity mask is
 * later tested in the enable/disable paths.
 */
P
Paul Mundt 已提交
68 69 70
static int intc_set_affinity(struct irq_data *data,
			     const struct cpumask *cpumask,
			     bool force)
P
Paul Mundt 已提交
71 72 73 74
{
	if (!cpumask_intersects(cpumask, cpu_online_mask))
		return -1;

P
Paul Mundt 已提交
75
	cpumask_copy(data->affinity, cpumask);
P
Paul Mundt 已提交
76

77
	return IRQ_SET_MASK_OK_NOCOPY;
P
Paul Mundt 已提交
78 79 80
}
#endif

P
Paul Mundt 已提交
81
static void intc_mask_ack(struct irq_data *data)
P
Paul Mundt 已提交
82
{
P
Paul Mundt 已提交
83
	unsigned int irq = data->irq;
P
Paul Mundt 已提交
84 85
	struct intc_desc_int *d = get_intc_desc(irq);
	unsigned long handle = intc_get_ack_handle(irq);
86
	void __iomem *addr;
P
Paul Mundt 已提交
87

P
Paul Mundt 已提交
88
	intc_disable(data);
P
Paul Mundt 已提交
89 90 91 92 93

	/* read register and write zero only to the associated bit */
	if (handle) {
		unsigned int value;

94
		addr = (void __iomem *)INTC_REG(d, _INTC_ADDR_D(handle), 0);
P
Paul Mundt 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
		value = intc_set_field_from_handle(0, 1, handle);

		switch (_INTC_FN(handle)) {
		case REG_FN_MODIFY_BASE + 0:	/* 8bit */
			__raw_readb(addr);
			__raw_writeb(0xff ^ value, addr);
			break;
		case REG_FN_MODIFY_BASE + 1:	/* 16bit */
			__raw_readw(addr);
			__raw_writew(0xffff ^ value, addr);
			break;
		case REG_FN_MODIFY_BASE + 3:	/* 32bit */
			__raw_readl(addr);
			__raw_writel(0xffffffff ^ value, addr);
			break;
		default:
			BUG();
			break;
		}
	}
}

static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
					     unsigned int nr_hp,
					     unsigned int irq)
{
P
Paul Mundt 已提交
121
	struct intc_handle_int key;
P
Paul Mundt 已提交
122

P
Paul Mundt 已提交
123 124
	key.irq = irq;
	key.handle = 0;
P
Paul Mundt 已提交
125

P
Paul Mundt 已提交
126
	return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp);
P
Paul Mundt 已提交
127 128 129 130 131
}

int intc_set_priority(unsigned int irq, unsigned int prio)
{
	struct intc_desc_int *d = get_intc_desc(irq);
P
Paul Mundt 已提交
132
	struct irq_data *data = irq_get_irq_data(irq);
P
Paul Mundt 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
	struct intc_handle_int *ihp;

	if (!intc_get_prio_level(irq) || prio <= 1)
		return -EINVAL;

	ihp = intc_find_irq(d->prio, d->nr_prio, irq);
	if (ihp) {
		if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
			return -EINVAL;

		intc_set_prio_level(irq, prio);

		/*
		 * only set secondary masking method directly
		 * primary masking method is using intc_prio_level[irq]
		 * priority level will be set during next enable()
		 */
		if (_INTC_FN(ihp->handle) != REG_FN_ERR)
P
Paul Mundt 已提交
151
			_intc_enable(data, ihp->handle);
P
Paul Mundt 已提交
152 153 154 155
	}
	return 0;
}

156 157
#define SENSE_VALID_FLAG 0x80
#define VALID(x) (x | SENSE_VALID_FLAG)
P
Paul Mundt 已提交
158 159 160 161 162 163 164 165 166 167 168

static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
	[IRQ_TYPE_EDGE_FALLING] = VALID(0),
	[IRQ_TYPE_EDGE_RISING] = VALID(1),
	[IRQ_TYPE_LEVEL_LOW] = VALID(2),
	/* SH7706, SH7707 and SH7709 do not support high level triggered */
#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
    !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
    !defined(CONFIG_CPU_SUBTYPE_SH7709)
	[IRQ_TYPE_LEVEL_HIGH] = VALID(3),
#endif
169
#if defined(CONFIG_ARM) /* all recent SH-Mobile / R-Mobile ARM support this */
170 171
	[IRQ_TYPE_EDGE_BOTH] = VALID(4),
#endif
P
Paul Mundt 已提交
172 173
};

P
Paul Mundt 已提交
174
static int intc_set_type(struct irq_data *data, unsigned int type)
P
Paul Mundt 已提交
175
{
P
Paul Mundt 已提交
176
	unsigned int irq = data->irq;
P
Paul Mundt 已提交
177 178 179 180 181 182 183 184
	struct intc_desc_int *d = get_intc_desc(irq);
	unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
	struct intc_handle_int *ihp;
	unsigned long addr;

	if (!value)
		return -EINVAL;

185 186
	value &= ~SENSE_VALID_FLAG;

P
Paul Mundt 已提交
187 188
	ihp = intc_find_irq(d->sense, d->nr_sense, irq);
	if (ihp) {
189 190 191 192
		/* PINT has 2-bit sense registers, should fail on EDGE_BOTH */
		if (value >= (1 << _INTC_WIDTH(ihp->handle)))
			return -EINVAL;

P
Paul Mundt 已提交
193
		addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
194
		intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
P
Paul Mundt 已提交
195 196 197 198 199 200
	}

	return 0;
}

struct irq_chip intc_irq_chip	= {
P
Paul Mundt 已提交
201 202 203 204 205 206
	.irq_mask		= intc_disable,
	.irq_unmask		= intc_enable,
	.irq_mask_ack		= intc_mask_ack,
	.irq_enable		= intc_enable,
	.irq_disable		= intc_disable,
	.irq_set_type		= intc_set_type,
P
Paul Mundt 已提交
207
#ifdef CONFIG_SMP
P
Paul Mundt 已提交
208
	.irq_set_affinity	= intc_set_affinity,
P
Paul Mundt 已提交
209
#endif
210
	.flags			= IRQCHIP_SKIP_SET_WAKE,
P
Paul Mundt 已提交
211
};