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 86 87
	struct intc_desc_int *d = get_intc_desc(irq);
	unsigned long handle = intc_get_ack_handle(irq);
	unsigned long addr;

P
Paul Mundt 已提交
88
	intc_disable(data);
P
Paul Mundt 已提交
89 90 91 92 93 94 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

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

		addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
		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
};