Home | History | Annotate | Line # | Download | only in fdt
gic_fdt.c revision 1.1
      1 /* $NetBSD: gic_fdt.c,v 1.1 2015/12/13 17:45:37 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      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  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 1.1 2015/12/13 17:45:37 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/device.h>
     35 #include <sys/intr.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 #include <sys/kmem.h>
     39 
     40 #include <arm/cortex/gic_intr.h>
     41 
     42 #include <dev/fdt/fdtvar.h>
     43 
     44 static int	gic_fdt_match(device_t, cfdata_t, void *);
     45 static void	gic_fdt_attach(device_t, device_t, void *);
     46 
     47 static void *	gic_fdt_establish(device_t, int, u_int, int, int,
     48 		    int (*)(void *), void *);
     49 static void	gic_fdt_disestablish(device_t, void *);
     50 static bool	gic_fdt_intrstr(device_t, int, u_int, char *, size_t);
     51 
     52 struct fdtbus_interrupt_controller_func gic_fdt_funcs = {
     53 	.establish = gic_fdt_establish,
     54 	.disestablish = gic_fdt_disestablish,
     55 	.intrstr = gic_fdt_intrstr
     56 };
     57 
     58 struct gic_fdt_softc {
     59 	device_t		sc_dev;
     60 	int			sc_phandle;
     61 };
     62 
     63 CFATTACH_DECL_NEW(gic_fdt, sizeof(struct gic_fdt_softc),
     64 	gic_fdt_match, gic_fdt_attach, NULL, NULL);
     65 
     66 static int
     67 gic_fdt_match(device_t parent, cfdata_t cf, void *aux)
     68 {
     69 	const char * const compatible[] = {
     70 		"arm,gic-400",
     71 		"arm,cortex-a15-gic",
     72 		"arm,cortex-a9-gic",
     73 		"arm,cortex-a7-gic",
     74 		NULL
     75 	};
     76 	struct fdt_attach_args * const faa = aux;
     77 
     78 	return of_compatible(faa->faa_phandle, compatible) >= 0;
     79 }
     80 
     81 static void
     82 gic_fdt_attach(device_t parent, device_t self, void *aux)
     83 {
     84 	struct gic_fdt_softc * const sc = device_private(self);
     85 	struct fdt_attach_args * const faa = aux;
     86 	int error;
     87 
     88 	sc->sc_dev = self;
     89 	sc->sc_phandle = faa->faa_phandle;
     90 
     91 	error = fdtbus_register_interrupt_controller(self, faa->faa_phandle,
     92 	    &gic_fdt_funcs);
     93 	if (error) {
     94 		aprint_error(": couldn't register with fdtbus: %d\n", error);
     95 		return;
     96 	}
     97 
     98 	aprint_naive("\n");
     99 	aprint_normal(": GIC\n");
    100 }
    101 
    102 static void *
    103 gic_fdt_establish(device_t dev, int phandle, u_int index, int ipl, int flags,
    104     int (*func)(void *), void *arg)
    105 {
    106 	struct gic_fdt_softc * const sc = device_private(dev);
    107 	int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
    108 	u_int *interrupts;
    109 	int interrupt_cells, len;
    110 
    111 	len = OF_getprop(sc->sc_phandle, "#interrupt-cells", &interrupt_cells,
    112 	    sizeof(interrupt_cells));
    113 	if (len != sizeof(interrupt_cells) || interrupt_cells <= 0)
    114 		return NULL;
    115 	interrupt_cells = be32toh(interrupt_cells);
    116 
    117 	len = OF_getproplen(phandle, "interrupts");
    118 	if (len <= 0)
    119 		return NULL;
    120 
    121 	const u_int clen = interrupt_cells * 4;
    122 	const u_int nintr = len / interrupt_cells;
    123 
    124 	if (index >= nintr)
    125 		return NULL;
    126 
    127 	interrupts = kmem_alloc(len, KM_SLEEP);
    128 
    129 	if (OF_getprop(phandle, "interrupts", interrupts, len) != len) {
    130 		kmem_free(interrupts, len);
    131 		return NULL;
    132 	}
    133 
    134 	/* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
    135 	/* 2nd cell is the interrupt number */
    136 	/* 3rd cell is flags */
    137 
    138 	const u_int type = be32toh(interrupts[index * clen + 0]);
    139 	const u_int intr = be32toh(interrupts[index * clen + 1]);
    140 	const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
    141 	const u_int trig = be32toh(interrupts[index * clen + 2]) & 0xf;
    142 	const u_int level = (trig & 0x3) ? IST_EDGE : IST_LEVEL;
    143 
    144 	kmem_free(interrupts, len);
    145 
    146 	return intr_establish(irq, ipl, level | iflags, func, arg);
    147 }
    148 
    149 static void
    150 gic_fdt_disestablish(device_t dev, void *ih)
    151 {
    152 	intr_disestablish(ih);
    153 }
    154 
    155 static bool
    156 gic_fdt_intrstr(device_t dev, int phandle, u_int index, char *buf,
    157     size_t buflen)
    158 {
    159 	struct gic_fdt_softc * const sc = device_private(dev);
    160 	u_int *interrupts;
    161 	int interrupt_cells, len;
    162 
    163 	len = OF_getprop(sc->sc_phandle, "#interrupt-cells", &interrupt_cells,
    164 	    sizeof(interrupt_cells));
    165 	if (len != sizeof(interrupt_cells) || interrupt_cells <= 0) {
    166 		return false;
    167 	}
    168 	interrupt_cells = be32toh(interrupt_cells);
    169 
    170 	len = OF_getproplen(phandle, "interrupts");
    171 	if (len <= 0) {
    172 		return false;
    173 	}
    174 
    175 	const u_int clen = interrupt_cells * 4;
    176 	const u_int nintr = len / interrupt_cells;
    177 
    178 	if (index >= nintr) {
    179 		return false;
    180 	}
    181 
    182 	interrupts = kmem_alloc(len, KM_SLEEP);
    183 
    184 	if (OF_getprop(phandle, "interrupts", interrupts, len) != len) {
    185 		kmem_free(interrupts, len);
    186 		return false;
    187 	}
    188 
    189 	/* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
    190 	/* 2nd cell is the interrupt number */
    191 	/* 3rd cell is flags */
    192 
    193 	const u_int type = be32toh(interrupts[index * clen + 0]);
    194 	const u_int intr = be32toh(interrupts[index * clen + 1]);
    195 	const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
    196 
    197 	kmem_free(interrupts, len);
    198 
    199 	snprintf(buf, buflen, "GIC irq %d", irq);
    200 
    201 	return true;
    202 }
    203