1 /* $NetBSD: s3c2410_extint.c,v 1.8 2007/12/15 00:39:15 perry Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Genetec corporation. All rights reserved. 5 * Written by Hiroyuki Bessho for Genetec corporation. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of Genetec corporation may not be used to endorse 16 * or promote products derived from this software without specific prior 17 * written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY GENETEC CORP. ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORP. 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * device driver to handle cascaded external interrupts of S3C2410. 34 * 35 * EXTINT [8..23] are cascaded to IRQ #5 36 * EXTINT [4..7] are cascaded to IRQ #4 37 * EXTINT [0..3] are not cascaded and connected directly as IRQ #[0..3]. 38 * EXTINT [0..3] are handled by main interrupt handler in s3c2410_intr.c. 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: s3c2410_extint.c,v 1.8 2007/12/15 00:39:15 perry Exp $"); 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/malloc.h> 47 #include <uvm/uvm_extern.h> 48 #include <machine/bus.h> 49 #include <machine/intr.h> 50 #include <arm/cpufunc.h> 51 52 #include <arm/s3c2xx0/s3c2410reg.h> 53 #include <arm/s3c2xx0/s3c2410var.h> 54 55 #include "locators.h" 56 #include "opt_s3c2410.h" /* for S3C2410_EXTINT_MAX */ 57 58 #ifndef S3C2410_EXTINT_MAX 59 #define S3C2410_EXTINT_MAX 23 60 #endif 61 62 #define EXTINT_CASCADE_MIN 4 63 64 #if S3C2410_EXTINT_MAX < EXTINT_CASCADE_MIN 65 #error "Don't enable ssextio if you don't use extint[4..23]." 66 #endif 67 68 #define N_EXTINT (S3C2410_EXTINT_MAX - EXTINT_CASCADE_MIN +1) 69 70 struct ssextio_softc { 71 struct device sc_dev; 72 73 bus_space_tag_t sc_iot; 74 bus_space_handle_t sc_ioh; 75 76 uint32_t sc_pending; 77 uint32_t sc_mask; 78 79 struct extint_handler { 80 int (* func)(void *); 81 void *arg; 82 void *sh; /* softintr handler */ 83 int level; /* IPL */ 84 } sc_handler[N_EXTINT]; 85 86 }; 87 88 static struct ssextio_softc *ssextio_softc = NULL; 89 90 /* cookies */ 91 #define EXTINT_4_7 1 92 #define EXTINT_8_23 2 93 94 /* prototypes */ 95 static int ssextio_match(struct device *, struct cfdata *, void *); 96 static void ssextio_attach(struct device *, struct device *, void *); 97 static int ssextio_search(struct device *, struct cfdata *, 98 const int *, void *); 99 static int ssextio_print(void *, const char *); 100 101 static int ssextio_cascaded_intr(void *); 102 static void ssextio_softintr(void *); 103 104 static inline void 105 update_hw_mask(void) 106 { 107 bus_space_write_4(ssextio_softc->sc_iot, ssextio_softc->sc_ioh, 108 GPIO_EINTMASK, ssextio_softc->sc_mask | ssextio_softc->sc_pending); 109 } 110 111 112 /* attach structures */ 113 CFATTACH_DECL(ssextio, sizeof(struct ssextio_softc), ssextio_match, ssextio_attach, 114 NULL, NULL); 115 116 static int 117 ssextio_print(void *aux, const char *name) 118 { 119 struct s3c2xx0_attach_args *sa = (struct s3c2xx0_attach_args*)aux; 120 121 if (sa->sa_addr != SSEXTIOCF_ADDR_DEFAULT) 122 aprint_normal(" addr 0x%lx", sa->sa_addr); 123 if (sa->sa_intr > 0) 124 aprint_normal(" intr %d", sa->sa_intr); 125 return (UNCONF); 126 } 127 128 int 129 ssextio_match(struct device *parent, struct cfdata *match, void *aux) 130 { 131 #if S3C2410_EXTINT_MAX < 4 132 /* better not configure this driver */ 133 return 0; 134 #else 135 if (ssextio_softc != NULL) 136 return 0; 137 138 return 1; 139 #endif 140 } 141 142 void 143 ssextio_attach(struct device *parent, struct device *self, void *aux) 144 { 145 struct ssextio_softc *sc = (struct ssextio_softc*)self; 146 struct s3c24x0_softc *cpuc = (struct s3c24x0_softc *)parent; 147 148 aprint_normal("\n"); 149 150 ssextio_softc = sc; 151 152 sc->sc_iot = cpuc->sc_sx.sc_iot; 153 sc->sc_ioh = cpuc->sc_sx.sc_gpio_ioh; 154 155 sc->sc_pending = 0; 156 sc->sc_mask = ~0; 157 158 s3c24x0_intr_establish(S3C2410_INT_4_7, IPL_HIGH, IST_NONE, 159 ssextio_cascaded_intr, (void *)EXTINT_4_7); 160 #if S3C2410_EXTINT_MAX >= 8 161 s3c24x0_intr_establish(S3C2410_INT_8_23, IPL_HIGH, IST_NONE, 162 ssextio_cascaded_intr, (void *)EXTINT_8_23); 163 #endif 164 /* 165 * Attach each devices 166 */ 167 config_search_ia(ssextio_search, self, "ssextio", NULL); 168 } 169 170 static int 171 ssextio_search(struct device *parent, struct cfdata *cf, 172 const int *ldesc, void *aux) 173 { 174 struct ssextio_softc *sc = (struct ssextio_softc *)parent; 175 struct s3c24x0_softc *cpuc =(struct s3c24x0_softc *) device_parent(&sc->sc_dev); 176 struct s3c2xx0_attach_args sa; 177 178 sa.sa_sc = sc; 179 sa.sa_iot = sc->sc_iot; 180 sa.sa_addr = cf->cf_loc[SSEXTIOCF_ADDR]; 181 sa.sa_size = cf->cf_loc[SSEXTIOCF_SIZE]; 182 sa.sa_intr = cf->cf_loc[SSEXTIOCF_INTR]; 183 sa.sa_dmat = cpuc->sc_sx.sc_dmat; 184 185 if (config_match(parent, cf, &sa)) 186 config_attach(parent, cf, &sa, ssextio_print); 187 188 return 0; 189 } 190 191 void * 192 s3c2410_extint_establish(int extint, int ipl, int type, 193 int (*func)(void *), void *arg) 194 { 195 int save; 196 int idx; 197 int soft_level; 198 199 if (extint < 0 || N_EXTINT <= extint) 200 panic("Bad interrupt no for extio"); 201 202 if (extint < EXTINT_CASCADE_MIN) { 203 /* 204 * EXINT[0..3] are not cascaded. they are handled by 205 * the main interrupt controller. 206 */ 207 return s3c24x0_intr_establish(extint, ipl, type, func, arg); 208 } 209 210 #ifdef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS 211 soft_level = ipl; 212 #else 213 if (ipl >= IPL_SOFTSERIAL) 214 soft_level = IPL_SOFTSERIAL; 215 else if (ipl >= IPL_SOFTNET) 216 soft_level = IPL_SOFTNET; 217 else 218 soft_level = IPL_SOFT; 219 #endif 220 221 idx = extint - EXTINT_CASCADE_MIN; 222 223 save = disable_interrupts(I32_bit); 224 225 ssextio_softc->sc_handler[idx].func = func; 226 ssextio_softc->sc_handler[idx].arg = arg; 227 ssextio_softc->sc_handler[idx].level = ipl; 228 229 ssextio_softc->sc_handler[idx].sh = softintr_establish(soft_level, 230 ssextio_softintr, &ssextio_softc->sc_handler[idx]); 231 232 s3c2410_setup_extint(extint, type); 233 234 bus_space_write_4(ssextio_softc->sc_iot, ssextio_softc->sc_ioh, 235 GPIO_EINTPEND, 1U << extint); 236 ssextio_softc->sc_mask &= ~(1U << extint); 237 update_hw_mask(); 238 239 restore_interrupts(save); 240 return &ssextio_softc->sc_handler[idx]; 241 } 242 243 244 static int 245 ssextio_cascaded_intr(void *cookie) 246 { 247 uint32_t pending_mask, pending; 248 int int_min; 249 bus_space_tag_t iot = ssextio_softc->sc_iot; 250 bus_space_handle_t ioh = ssextio_softc->sc_ioh; 251 int save, i; 252 253 switch((int)cookie) { 254 case EXTINT_4_7: 255 pending_mask = 0x000000f0; 256 int_min = 4; 257 break; 258 259 case EXTINT_8_23: 260 pending_mask = 0x00ffff00; 261 int_min = 8; 262 break; 263 264 default: 265 panic("Bad cookie for %s", __func__); 266 } 267 268 269 save = disable_interrupts(I32_bit);; 270 pending = pending_mask & bus_space_read_4(iot, ioh, GPIO_EINTPEND); 271 pending &= ~ssextio_softc->sc_mask; 272 ssextio_softc->sc_pending |= pending; 273 /* disable the extint until the handler is called. */ 274 update_hw_mask(); 275 restore_interrupts(save); 276 277 for (i=int_min; pending; ++i) { 278 if (pending & (1<<i)) { 279 assert(ssextio_softc->sc_handler[i-EXTINT_CASCADE_MIN].sh != NULL); 280 281 softintr_schedule( 282 ssextio_softc->sc_handler[i-EXTINT_CASCADE_MIN].sh); 283 pending &= ~ (1<<i); 284 } 285 } 286 287 return 1; 288 } 289 290 static void 291 ssextio_softintr(void *cookie) 292 { 293 struct extint_handler *h = cookie; 294 int extint = EXTINT_CASCADE_MIN + h - ssextio_softc->sc_handler; 295 int s, save; 296 297 save = disable_interrupts(I32_bit); 298 /* clear hardware pending bits */ 299 bus_space_write_4(ssextio_softc->sc_iot, ssextio_softc->sc_ioh, 300 GPIO_EINTPEND, 1<<extint); 301 ssextio_softc->sc_pending &= ~(1<<extint); 302 update_hw_mask(); 303 restore_interrupts(save); 304 305 s = _splraise(h->level); 306 h->func(h->arg); 307 splx(s); 308 } 309