1  /*
2   * intc.c  -- support for the old ColdFire interrupt controller
3   *
4   * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com>
5   *
6   * This file is subject to the terms and conditions of the GNU General Public
7   * License.  See the file COPYING in the main directory of this archive
8   * for more details.
9   */
10  
11  #include <linux/types.h>
12  #include <linux/init.h>
13  #include <linux/kernel.h>
14  #include <linux/interrupt.h>
15  #include <linux/irq.h>
16  #include <linux/io.h>
17  #include <asm/traps.h>
18  #include <asm/coldfire.h>
19  #include <asm/mcfsim.h>
20  
21  /*
22   * The mapping of irq number to a mask register bit is not one-to-one.
23   * The irq numbers are either based on "level" of interrupt or fixed
24   * for an autovector-able interrupt. So we keep a local data structure
25   * that maps from irq to mask register. Not all interrupts will have
26   * an IMR bit.
27   */
28  unsigned char mcf_irq2imr[NR_IRQS];
29  
30  /*
31   * Define the miniumun and maximum external interrupt numbers.
32   * This is also used as the "level" interrupt numbers.
33   */
34  #define	EIRQ1	25
35  #define	EIRQ7	31
36  
37  /*
38   * In the early version 2 core ColdFire parts the IMR register was 16 bits
39   * in size. Version 3 (and later version 2) core parts have a 32 bit
40   * sized IMR register. Provide some size independent methods to access the
41   * IMR register.
42   */
43  #ifdef MCFSIM_IMR_IS_16BITS
44  
mcf_setimr(int index)45  void mcf_setimr(int index)
46  {
47  	u16 imr;
48  	imr = __raw_readw(MCFSIM_IMR);
49  	__raw_writew(imr | (0x1 << index), MCFSIM_IMR);
50  }
51  
mcf_clrimr(int index)52  void mcf_clrimr(int index)
53  {
54  	u16 imr;
55  	imr = __raw_readw(MCFSIM_IMR);
56  	__raw_writew(imr & ~(0x1 << index), MCFSIM_IMR);
57  }
58  
mcf_maskimr(unsigned int mask)59  void mcf_maskimr(unsigned int mask)
60  {
61  	u16 imr;
62  	imr = __raw_readw(MCFSIM_IMR);
63  	imr |= mask;
64  	__raw_writew(imr, MCFSIM_IMR);
65  }
66  
67  #else
68  
mcf_setimr(int index)69  void mcf_setimr(int index)
70  {
71  	u32 imr;
72  	imr = __raw_readl(MCFSIM_IMR);
73  	__raw_writel(imr | (0x1 << index), MCFSIM_IMR);
74  }
75  
mcf_clrimr(int index)76  void mcf_clrimr(int index)
77  {
78  	u32 imr;
79  	imr = __raw_readl(MCFSIM_IMR);
80  	__raw_writel(imr & ~(0x1 << index), MCFSIM_IMR);
81  }
82  
mcf_maskimr(unsigned int mask)83  void mcf_maskimr(unsigned int mask)
84  {
85  	u32 imr;
86  	imr = __raw_readl(MCFSIM_IMR);
87  	imr |= mask;
88  	__raw_writel(imr, MCFSIM_IMR);
89  }
90  
91  #endif
92  
93  /*
94   * Interrupts can be "vectored" on the ColdFire cores that support this old
95   * interrupt controller. That is, the device raising the interrupt can also
96   * supply the vector number to interrupt through. The AVR register of the
97   * interrupt controller enables or disables this for each external interrupt,
98   * so provide generic support for this. Setting this up is out-of-band for
99   * the interrupt system API's, and needs to be done by the driver that
100   * supports this device. Very few devices actually use this.
101   */
mcf_autovector(int irq)102  void mcf_autovector(int irq)
103  {
104  #ifdef MCFSIM_AVR
105  	if ((irq >= EIRQ1) && (irq <= EIRQ7)) {
106  		u8 avec;
107  		avec = __raw_readb(MCFSIM_AVR);
108  		avec |= (0x1 << (irq - EIRQ1 + 1));
109  		__raw_writeb(avec, MCFSIM_AVR);
110  	}
111  #endif
112  }
113  
intc_irq_mask(struct irq_data * d)114  static void intc_irq_mask(struct irq_data *d)
115  {
116  	if (mcf_irq2imr[d->irq])
117  		mcf_setimr(mcf_irq2imr[d->irq]);
118  }
119  
intc_irq_unmask(struct irq_data * d)120  static void intc_irq_unmask(struct irq_data *d)
121  {
122  	if (mcf_irq2imr[d->irq])
123  		mcf_clrimr(mcf_irq2imr[d->irq]);
124  }
125  
intc_irq_set_type(struct irq_data * d,unsigned int type)126  static int intc_irq_set_type(struct irq_data *d, unsigned int type)
127  {
128  	return 0;
129  }
130  
131  static struct irq_chip intc_irq_chip = {
132  	.name		= "CF-INTC",
133  	.irq_mask	= intc_irq_mask,
134  	.irq_unmask	= intc_irq_unmask,
135  	.irq_set_type	= intc_irq_set_type,
136  };
137  
init_IRQ(void)138  void __init init_IRQ(void)
139  {
140  	int irq;
141  
142  	mcf_maskimr(0xffffffff);
143  
144  	for (irq = 0; (irq < NR_IRQS); irq++) {
145  		irq_set_chip(irq, &intc_irq_chip);
146  		irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
147  		irq_set_handler(irq, handle_level_irq);
148  	}
149  }
150  
151