diff options
Diffstat (limited to 'arch/powerpc/platforms/86xx')
-rw-r--r-- | arch/powerpc/platforms/86xx/gef_gpio.c | 170 | ||||
-rw-r--r-- | arch/powerpc/platforms/86xx/gef_pic.c | 252 | ||||
-rw-r--r-- | arch/powerpc/platforms/86xx/gef_pic.h | 11 |
3 files changed, 433 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/86xx/gef_gpio.c b/arch/powerpc/platforms/86xx/gef_gpio.c new file mode 100644 index 00000000000..4ff7b1e7bba --- /dev/null +++ b/arch/powerpc/platforms/86xx/gef_gpio.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Driver for GE FPGA based GPIO | ||
3 | * | ||
4 | * Author: Martyn Welch <martyn.welch@ge.com> | ||
5 | * | ||
6 | * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | /* TODO | ||
14 | * | ||
15 | * Configuration of output modes (totem-pole/open-drain) | ||
16 | * Interrupt configuration - interrupts are always generated the FPGA relies on | ||
17 | * the I/O interrupt controllers mask to stop them propergating | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/compiler.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_device.h> | ||
26 | #include <linux/of_platform.h> | ||
27 | #include <linux/of_gpio.h> | ||
28 | #include <linux/gpio.h> | ||
29 | #include <linux/slab.h> | ||
30 | |||
31 | #define GEF_GPIO_DIRECT 0x00 | ||
32 | #define GEF_GPIO_IN 0x04 | ||
33 | #define GEF_GPIO_OUT 0x08 | ||
34 | #define GEF_GPIO_TRIG 0x0C | ||
35 | #define GEF_GPIO_POLAR_A 0x10 | ||
36 | #define GEF_GPIO_POLAR_B 0x14 | ||
37 | #define GEF_GPIO_INT_STAT 0x18 | ||
38 | #define GEF_GPIO_OVERRUN 0x1C | ||
39 | #define GEF_GPIO_MODE 0x20 | ||
40 | |||
41 | static void _gef_gpio_set(void __iomem *reg, unsigned int offset, int value) | ||
42 | { | ||
43 | unsigned int data; | ||
44 | |||
45 | data = ioread32be(reg); | ||
46 | /* value: 0=low; 1=high */ | ||
47 | if (value & 0x1) | ||
48 | data = data | (0x1 << offset); | ||
49 | else | ||
50 | data = data & ~(0x1 << offset); | ||
51 | |||
52 | iowrite32be(data, reg); | ||
53 | } | ||
54 | |||
55 | |||
56 | static int gef_gpio_dir_in(struct gpio_chip *chip, unsigned offset) | ||
57 | { | ||
58 | unsigned int data; | ||
59 | struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); | ||
60 | |||
61 | data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); | ||
62 | data = data | (0x1 << offset); | ||
63 | iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int gef_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value) | ||
69 | { | ||
70 | unsigned int data; | ||
71 | struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); | ||
72 | |||
73 | /* Set direction before switching to input */ | ||
74 | _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); | ||
75 | |||
76 | data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); | ||
77 | data = data & ~(0x1 << offset); | ||
78 | iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int gef_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
84 | { | ||
85 | unsigned int data; | ||
86 | int state = 0; | ||
87 | struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); | ||
88 | |||
89 | data = ioread32be(mmchip->regs + GEF_GPIO_IN); | ||
90 | state = (int)((data >> offset) & 0x1); | ||
91 | |||
92 | return state; | ||
93 | } | ||
94 | |||
95 | static void gef_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
96 | { | ||
97 | struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); | ||
98 | |||
99 | _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); | ||
100 | } | ||
101 | |||
102 | static int __init gef_gpio_init(void) | ||
103 | { | ||
104 | struct device_node *np; | ||
105 | int retval; | ||
106 | struct of_mm_gpio_chip *gef_gpio_chip; | ||
107 | |||
108 | for_each_compatible_node(np, NULL, "gef,sbc610-gpio") { | ||
109 | |||
110 | pr_debug("%s: Initialising GEF GPIO\n", np->full_name); | ||
111 | |||
112 | /* Allocate chip structure */ | ||
113 | gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); | ||
114 | if (!gef_gpio_chip) { | ||
115 | pr_err("%s: Unable to allocate structure\n", | ||
116 | np->full_name); | ||
117 | continue; | ||
118 | } | ||
119 | |||
120 | /* Setup pointers to chip functions */ | ||
121 | gef_gpio_chip->gc.of_gpio_n_cells = 2; | ||
122 | gef_gpio_chip->gc.ngpio = 19; | ||
123 | gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; | ||
124 | gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; | ||
125 | gef_gpio_chip->gc.get = gef_gpio_get; | ||
126 | gef_gpio_chip->gc.set = gef_gpio_set; | ||
127 | |||
128 | /* This function adds a memory mapped GPIO chip */ | ||
129 | retval = of_mm_gpiochip_add(np, gef_gpio_chip); | ||
130 | if (retval) { | ||
131 | kfree(gef_gpio_chip); | ||
132 | pr_err("%s: Unable to add GPIO\n", np->full_name); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | for_each_compatible_node(np, NULL, "gef,sbc310-gpio") { | ||
137 | |||
138 | pr_debug("%s: Initialising GEF GPIO\n", np->full_name); | ||
139 | |||
140 | /* Allocate chip structure */ | ||
141 | gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); | ||
142 | if (!gef_gpio_chip) { | ||
143 | pr_err("%s: Unable to allocate structure\n", | ||
144 | np->full_name); | ||
145 | continue; | ||
146 | } | ||
147 | |||
148 | /* Setup pointers to chip functions */ | ||
149 | gef_gpio_chip->gc.of_gpio_n_cells = 2; | ||
150 | gef_gpio_chip->gc.ngpio = 6; | ||
151 | gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; | ||
152 | gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; | ||
153 | gef_gpio_chip->gc.get = gef_gpio_get; | ||
154 | gef_gpio_chip->gc.set = gef_gpio_set; | ||
155 | |||
156 | /* This function adds a memory mapped GPIO chip */ | ||
157 | retval = of_mm_gpiochip_add(np, gef_gpio_chip); | ||
158 | if (retval) { | ||
159 | kfree(gef_gpio_chip); | ||
160 | pr_err("%s: Unable to add GPIO\n", np->full_name); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | return 0; | ||
165 | }; | ||
166 | arch_initcall(gef_gpio_init); | ||
167 | |||
168 | MODULE_DESCRIPTION("GE I/O FPGA GPIO driver"); | ||
169 | MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com"); | ||
170 | MODULE_LICENSE("GPL"); | ||
diff --git a/arch/powerpc/platforms/86xx/gef_pic.c b/arch/powerpc/platforms/86xx/gef_pic.c new file mode 100644 index 00000000000..94594e58594 --- /dev/null +++ b/arch/powerpc/platforms/86xx/gef_pic.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * Interrupt handling for GE FPGA based PIC | ||
3 | * | ||
4 | * Author: Martyn Welch <martyn.welch@ge.com> | ||
5 | * | ||
6 | * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/stddef.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | |||
20 | #include <asm/byteorder.h> | ||
21 | #include <asm/io.h> | ||
22 | #include <asm/prom.h> | ||
23 | #include <asm/irq.h> | ||
24 | |||
25 | #include "gef_pic.h" | ||
26 | |||
27 | #define DEBUG | ||
28 | #undef DEBUG | ||
29 | |||
30 | #ifdef DEBUG | ||
31 | #define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0) | ||
32 | #else | ||
33 | #define DBG(fmt...) do { } while (0) | ||
34 | #endif | ||
35 | |||
36 | #define GEF_PIC_NUM_IRQS 32 | ||
37 | |||
38 | /* Interrupt Controller Interface Registers */ | ||
39 | #define GEF_PIC_INTR_STATUS 0x0000 | ||
40 | |||
41 | #define GEF_PIC_INTR_MASK(cpu) (0x0010 + (0x4 * cpu)) | ||
42 | #define GEF_PIC_CPU0_INTR_MASK GEF_PIC_INTR_MASK(0) | ||
43 | #define GEF_PIC_CPU1_INTR_MASK GEF_PIC_INTR_MASK(1) | ||
44 | |||
45 | #define GEF_PIC_MCP_MASK(cpu) (0x0018 + (0x4 * cpu)) | ||
46 | #define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0) | ||
47 | #define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1) | ||
48 | |||
49 | |||
50 | static DEFINE_RAW_SPINLOCK(gef_pic_lock); | ||
51 | |||
52 | static void __iomem *gef_pic_irq_reg_base; | ||
53 | static struct irq_host *gef_pic_irq_host; | ||
54 | static int gef_pic_cascade_irq; | ||
55 | |||
56 | /* | ||
57 | * Interrupt Controller Handling | ||
58 | * | ||
59 | * The interrupt controller handles interrupts for most on board interrupts, | ||
60 | * apart from PCI interrupts. For example on SBC610: | ||
61 | * | ||
62 | * 17:31 RO Reserved | ||
63 | * 16 RO PCI Express Doorbell 3 Status | ||
64 | * 15 RO PCI Express Doorbell 2 Status | ||
65 | * 14 RO PCI Express Doorbell 1 Status | ||
66 | * 13 RO PCI Express Doorbell 0 Status | ||
67 | * 12 RO Real Time Clock Interrupt Status | ||
68 | * 11 RO Temperature Interrupt Status | ||
69 | * 10 RO Temperature Critical Interrupt Status | ||
70 | * 9 RO Ethernet PHY1 Interrupt Status | ||
71 | * 8 RO Ethernet PHY3 Interrupt Status | ||
72 | * 7 RO PEX8548 Interrupt Status | ||
73 | * 6 RO Reserved | ||
74 | * 5 RO Watchdog 0 Interrupt Status | ||
75 | * 4 RO Watchdog 1 Interrupt Status | ||
76 | * 3 RO AXIS Message FIFO A Interrupt Status | ||
77 | * 2 RO AXIS Message FIFO B Interrupt Status | ||
78 | * 1 RO AXIS Message FIFO C Interrupt Status | ||
79 | * 0 RO AXIS Message FIFO D Interrupt Status | ||
80 | * | ||
81 | * Interrupts can be forwarded to one of two output lines. Nothing | ||
82 | * clever is done, so if the masks are incorrectly set, a single input | ||
83 | * interrupt could generate interrupts on both output lines! | ||
84 | * | ||
85 | * The dual lines are there to allow the chained interrupts to be easily | ||
86 | * passed into two different cores. We currently do not use this functionality | ||
87 | * in this driver. | ||
88 | * | ||
89 | * Controller can also be configured to generate Machine checks (MCP), again on | ||
90 | * two lines, to be attached to two different cores. It is suggested that these | ||
91 | * should be masked out. | ||
92 | */ | ||
93 | |||
94 | void gef_pic_cascade(unsigned int irq, struct irq_desc *desc) | ||
95 | { | ||
96 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
97 | unsigned int cascade_irq; | ||
98 | |||
99 | /* | ||
100 | * See if we actually have an interrupt, call generic handling code if | ||
101 | * we do. | ||
102 | */ | ||
103 | cascade_irq = gef_pic_get_irq(); | ||
104 | |||
105 | if (cascade_irq != NO_IRQ) | ||
106 | generic_handle_irq(cascade_irq); | ||
107 | |||
108 | chip->irq_eoi(&desc->irq_data); | ||
109 | } | ||
110 | |||
111 | static void gef_pic_mask(struct irq_data *d) | ||
112 | { | ||
113 | unsigned long flags; | ||
114 | unsigned int hwirq = irqd_to_hwirq(d); | ||
115 | u32 mask; | ||
116 | |||
117 | raw_spin_lock_irqsave(&gef_pic_lock, flags); | ||
118 | mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); | ||
119 | mask &= ~(1 << hwirq); | ||
120 | out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); | ||
121 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); | ||
122 | } | ||
123 | |||
124 | static void gef_pic_mask_ack(struct irq_data *d) | ||
125 | { | ||
126 | /* Don't think we actually have to do anything to ack an interrupt, | ||
127 | * we just need to clear down the devices interrupt and it will go away | ||
128 | */ | ||
129 | gef_pic_mask(d); | ||
130 | } | ||
131 | |||
132 | static void gef_pic_unmask(struct irq_data *d) | ||
133 | { | ||
134 | unsigned long flags; | ||
135 | unsigned int hwirq = irqd_to_hwirq(d); | ||
136 | u32 mask; | ||
137 | |||
138 | raw_spin_lock_irqsave(&gef_pic_lock, flags); | ||
139 | mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); | ||
140 | mask |= (1 << hwirq); | ||
141 | out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); | ||
142 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); | ||
143 | } | ||
144 | |||
145 | static struct irq_chip gef_pic_chip = { | ||
146 | .name = "gefp", | ||
147 | .irq_mask = gef_pic_mask, | ||
148 | .irq_mask_ack = gef_pic_mask_ack, | ||
149 | .irq_unmask = gef_pic_unmask, | ||
150 | }; | ||
151 | |||
152 | |||
153 | /* When an interrupt is being configured, this call allows some flexibilty | ||
154 | * in deciding which irq_chip structure is used | ||
155 | */ | ||
156 | static int gef_pic_host_map(struct irq_host *h, unsigned int virq, | ||
157 | irq_hw_number_t hwirq) | ||
158 | { | ||
159 | /* All interrupts are LEVEL sensitive */ | ||
160 | irq_set_status_flags(virq, IRQ_LEVEL); | ||
161 | irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct, | ||
167 | const u32 *intspec, unsigned int intsize, | ||
168 | irq_hw_number_t *out_hwirq, unsigned int *out_flags) | ||
169 | { | ||
170 | |||
171 | *out_hwirq = intspec[0]; | ||
172 | if (intsize > 1) | ||
173 | *out_flags = intspec[1]; | ||
174 | else | ||
175 | *out_flags = IRQ_TYPE_LEVEL_HIGH; | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static struct irq_host_ops gef_pic_host_ops = { | ||
181 | .map = gef_pic_host_map, | ||
182 | .xlate = gef_pic_host_xlate, | ||
183 | }; | ||
184 | |||
185 | |||
186 | /* | ||
187 | * Initialisation of PIC, this should be called in BSP | ||
188 | */ | ||
189 | void __init gef_pic_init(struct device_node *np) | ||
190 | { | ||
191 | unsigned long flags; | ||
192 | |||
193 | /* Map the devices registers into memory */ | ||
194 | gef_pic_irq_reg_base = of_iomap(np, 0); | ||
195 | |||
196 | raw_spin_lock_irqsave(&gef_pic_lock, flags); | ||
197 | |||
198 | /* Initialise everything as masked. */ | ||
199 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0); | ||
200 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0); | ||
201 | |||
202 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0); | ||
203 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0); | ||
204 | |||
205 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); | ||
206 | |||
207 | /* Map controller */ | ||
208 | gef_pic_cascade_irq = irq_of_parse_and_map(np, 0); | ||
209 | if (gef_pic_cascade_irq == NO_IRQ) { | ||
210 | printk(KERN_ERR "SBC610: failed to map cascade interrupt"); | ||
211 | return; | ||
212 | } | ||
213 | |||
214 | /* Setup an irq_host structure */ | ||
215 | gef_pic_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, | ||
216 | GEF_PIC_NUM_IRQS, | ||
217 | &gef_pic_host_ops, NO_IRQ); | ||
218 | if (gef_pic_irq_host == NULL) | ||
219 | return; | ||
220 | |||
221 | /* Chain with parent controller */ | ||
222 | irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade); | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * This is called when we receive an interrupt with apparently comes from this | ||
227 | * chip - check, returning the highest interrupt generated or return NO_IRQ | ||
228 | */ | ||
229 | unsigned int gef_pic_get_irq(void) | ||
230 | { | ||
231 | u32 cause, mask, active; | ||
232 | unsigned int virq = NO_IRQ; | ||
233 | int hwirq; | ||
234 | |||
235 | cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS); | ||
236 | |||
237 | mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); | ||
238 | |||
239 | active = cause & mask; | ||
240 | |||
241 | if (active) { | ||
242 | for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) { | ||
243 | if (active & (0x1 << hwirq)) | ||
244 | break; | ||
245 | } | ||
246 | virq = irq_linear_revmap(gef_pic_irq_host, | ||
247 | (irq_hw_number_t)hwirq); | ||
248 | } | ||
249 | |||
250 | return virq; | ||
251 | } | ||
252 | |||
diff --git a/arch/powerpc/platforms/86xx/gef_pic.h b/arch/powerpc/platforms/86xx/gef_pic.h new file mode 100644 index 00000000000..6149916da3f --- /dev/null +++ b/arch/powerpc/platforms/86xx/gef_pic.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #ifndef __GEF_PIC_H__ | ||
2 | #define __GEF_PIC_H__ | ||
3 | |||
4 | #include <linux/init.h> | ||
5 | |||
6 | void gef_pic_cascade(unsigned int, struct irq_desc *); | ||
7 | unsigned int gef_pic_get_irq(void); | ||
8 | void gef_pic_init(struct device_node *); | ||
9 | |||
10 | #endif /* __GEF_PIC_H__ */ | ||
11 | |||