imx31_gpio.c revision 1.1.2.1 1 /*-
2 * Copyright (c) 2007 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Matt Thomas
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the NetBSD
19 * Foundation, Inc. and its contributors.
20 * 4. Neither the name of The NetBSD Foundation nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: imx31_gpio.c,v 1.1.2.1 2007/08/29 05:24:23 matt Exp $");
38
39 #define _INTR_PRIVATE
40
41 #include <sys/param.h>
42 #include <sys/evcnt.h>
43
44 #include <uvm/uvm_extern.h>
45
46 #include <machine/intr.h>
47
48 #include <arm/cpu.h>
49 #include <arm/armreg.h>
50 #include <arm/cpufunc.h>
51
52 #include <machine/atomic.h>
53 #include <machine/bus.h>
54
55 #include <arm/imx/imx31reg.h>
56
57 static void gpio_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
58 static void gpio_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
59 static int gpio_pic_find_pending_irqs(struct pic_softc *);
60 static void gpio_pic_establish_irq(struct pic_softc *, int, int, int);
61
62 const struct pic_ops gpio_pic_ops = {
63 .pic_block_irqs = gpio_pic_block_irqs,
64 .pic_unblock_irqs = gpio_pic_unblock_irqs,
65 .pic_find_pending_irqs = gpio_find_pending_irqs,
66 .pic_establish_irq = establish_irq,
67 };
68
69 struct gpio_pic_softc {
70 struct pic_softc gpic_pic;
71 bus_space_tag_t gpic_memt;
72 bus_space_handle_t gpic_memh;
73 uint32_t gpio_enable_mask;
74 uint32_t gpio_edge_mask;
75 uint32_t gpio_level_mask;
76 };
77
78 void
79 gpio_pic_unblock_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask)
80 {
81 struct gpic_softc * const gpic = pic
82 KASSERT(irq_base == 0);
83 gpic->gpic_enable_mask |= irq_mask;
84 /*
85 * If this a level source, ack it now. If it's still asserted
86 * it'll come back.
87 */
88 if (irq_mask & gpic->gpic_level_mask)
89 GPIO_WRITE(gpic, GPIO_ISR, irq_mask);
90 GPIO_WRITE(gpic, GPIO_IMR, gpic->gpic_enable_mask);
91 }
92
93 void
94 gpio_pic_block_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask)
95 {
96 KASSERT(irq_base == 0);
97 gpic->gpic_enable_mask &= ~irq_mask;
98 GPIO_WRITE(gpic, GPIO_IMR, gpic->gpic_enable_mask);
99 }
100
101 int
102 gpio_pic_find_pending_irqs(struct pic_softc *pic)
103 {
104 struct gpio_pic_softc * const gpic = pic;
105 uint32_t v;
106 uint32_t pending;
107
108 v = GPIO_READ(gpic, GPIO_ISR);
109 pending = (v & gpic->gpic_enabled);
110 if (pending == 0)
111 return 0;
112
113 /*
114 * Disable the pending interrupts.
115 */
116 gpic->gpic_enable_mask &= ~pending;
117 GPIO_WRITE(gpic, GPIO_IMR, gpic->gpic_enable_mask);
118
119 /*
120 * If any of the sources are edge triggered, ack them now so
121 * we won't lose them.
122 */
123 if (v & gpic->gpic_edge_mask)
124 GPIO_WRITE(gpic, GPIO_ISR, v & gpic->gpic_edge_mask);
125
126 /*
127 * Now find all the pending bits and mark them as pending.
128 */
129 do {
130 KASSERT(pending != 0)
131 irq = 31 - __builtin_clz(pending);
132 pending &= ~(1 << irq);
133 pic_mark_pending(gpic->gpic_pic, irq)
134 } while (pending != 0);
135
136 return 1;
137 }
138
139 #define GPIO_TYPEMAP \
140 ((GPIO_TYPE_LEVEL_LOW << (2*IST_LEVEL_LOW)) | \
141 (GPIO_TYPE_LEVEL_HIGH << (2*IST_LEVEL_HIGH)) | \
142 (GPIO_TYPE_EDGE_RISING << (2*IST_EDGE_RISING)) | \
143 (GPIO_TYPE_EDGE_FAILING << (2*IST_EDGE_FAILING)))
144
145 void
146 gpio_pic_establish_irq(struct pic_softc *pic, int irq, int ipl, int type)
147 {
148 struct gpio_pic_softc * const gpic = pic;
149 KASSERT(irq < 32);
150 uint32_t irq_mask = BIT(irq);
151
152 /*
153 * Make sure the irq isn't enabled and not asserting.
154 */
155 gpic->gpic_enable_mask &= ~irq_mask;
156 GPIO_WRITE(gpic, GPIO_IMR, gpic->gpic_enable_mask);
157 GPIO_WRITE(gpic, GPIO_ISR, irq_mask);
158
159 /*
160 * Convert the type to a gpio type and figure out which bits in what
161 * register we have to tweak.
162 */
163 gtype = (GPIO_TYPEMASK >> (2 * type)) & 3;
164 icr_shift = (irq & 0x0f) << 1;
165 icr_reg = GPIO_ICR + ((irq & 0x10) >> 2);
166
167 /*
168 * Set the interrupt type.
169 */
170 v = GPIO_READ(gpic, icr_reg);
171 v &= ~(3 << icr_shift);
172 v |= gtype << icr_shift;
173 GPIO_WRITE(gpic, icr_reg, v);
174
175 /*
176 * Mark it as input.
177 */
178 v = GPIO_READ(gpic, GPIO_DIR);
179 v &= ~irq_mask;
180 GPIO_WRITE(gpic, GPIO_DIR, v);
181
182 /*
183 * Now record the type of interrupt.
184 */
185 if (gtype == GPIO_TYPE_EDGE_RISING || gtype == GPIO_TYPE_EDGE_FAILING) {
186 gpic->gpic_edge_mask |= irq_mask;
187 gpic->gpic_level_mask &= ~irq_mask;
188 } else {
189 gpic->gpic_edge_mask &= ~irq_mask;
190 gpic->gpic_level_mask |= irq_mask;
191 }
192 }
193
194 static int gpio_match(struct device *, struct cfdata *, void *);
195 static int gpio_attach(struct device *, struct device *, void *);
196
197 int
198 gpio_match(struct device *parent, struct cfdata *cfdata, void *aux)
199 {
200 struct mainbus_attach_args *mba = aux;
201 bus_space_handle_t memh;
202 int error;
203
204 if (mba->mba_addr != GPIO1_BASE
205 && mba->mba_addr != GPIO2_BASE
206 && mba->mba_addr != GPIO3_BASE)
207 return 0;
208
209 if (mba->mba_size == MAINBUSCF_SIZE_DEFAULT)
210 return 0;
211
212 error = bus_space_map(mba->mba_memt, mba->mba_addr, mba->mba_size,
213 0, &memh);
214 if (error)
215 return 0;
216
217 bus_space_unmap(mba->mba_memt, memh);
218 return 1;
219 }
220
221 int
222 gpio_attach(struct device *parent, struct device *self, void *aux)
223 {
224 struct mainbus_attach_args * const mba = aux;
225 struct gpio_softc * const gsc = (void *) self;
226 struct gpio_pic * const gpic = &gsc->gsc_gpic;
227 int error;
228
229 gsc->gsc_memt = mba->mba_memt;
230 error = bus_space_map(mba->mba_memt, mba->mba_addr, mba->mba_size,
231 0, &gsc->gsc_memh);
232
233 if (error) {
234 aprint_error(": failed to map register %#x@%#x: %d\n",
235 mba->mba_size, mba->mba_addr, error);
236 return;
237 }
238
239 if (mba->mba_irqbase != MAINBUSCF_IRQBASE_DEFAULT) {
240 gpic->gpic_pic.pic_ops = &gpio_pic_ops;
241 strlcpy(gpic->gpic_pic.pic_name, self->dv_xname,
242 sizeof(gpic->gpic_pic.pic_name));
243 gpic->gpic_pic.pic_maxsources = 32;
244 pic_add(&gpic->gpic_pic, mba->mba_irqbase);
245 }
246
247 }
248