Home | History | Annotate | Line # | Download | only in samsung
exynos_combiner.c revision 1.11.8.1
      1  1.11.8.1   thorpej /*	$NetBSD: exynos_combiner.c,v 1.11.8.1 2021/04/03 22:28:18 thorpej Exp $ */
      2       1.1     marty 
      3       1.1     marty /*-
      4       1.1     marty * Copyright (c) 2015 The NetBSD Foundation, Inc.
      5       1.1     marty * All rights reserved.
      6       1.1     marty *
      7       1.1     marty * This code is derived from software contributed to The NetBSD Foundation
      8       1.1     marty * by Marty Fouts
      9       1.1     marty *
     10       1.1     marty * Redistribution and use in source and binary forms, with or without
     11       1.1     marty * modification, are permitted provided that the following conditions
     12       1.1     marty * are met:
     13       1.1     marty * 1. Redistributions of source code must retain the above copyright
     14       1.1     marty *    notice, this list of conditions and the following disclaimer.
     15       1.1     marty * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1     marty *    notice, this list of conditions and the following disclaimer in the
     17       1.1     marty *    documentation and/or other materials provided with the distribution.
     18       1.1     marty *
     19       1.1     marty * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20       1.1     marty * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21       1.1     marty * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22       1.1     marty * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23       1.1     marty * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24       1.1     marty * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25       1.1     marty * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26       1.1     marty * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27       1.1     marty * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28       1.1     marty * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29       1.1     marty * POSSIBILITY OF SUCH DAMAGE.
     30       1.1     marty */
     31       1.1     marty 
     32       1.1     marty #include "opt_exynos.h"
     33       1.1     marty #include "opt_arm_debug.h"
     34      1.10     skrll #include "opt_multiprocessor.h"
     35       1.1     marty #include "gpio.h"
     36       1.1     marty 
     37       1.1     marty #include <sys/cdefs.h>
     38  1.11.8.1   thorpej __KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.11.8.1 2021/04/03 22:28:18 thorpej Exp $");
     39       1.1     marty 
     40       1.1     marty #include <sys/param.h>
     41       1.1     marty #include <sys/bus.h>
     42      1.10     skrll #include <sys/cpu.h>
     43       1.1     marty #include <sys/device.h>
     44       1.1     marty #include <sys/intr.h>
     45       1.1     marty #include <sys/systm.h>
     46       1.1     marty #include <sys/kmem.h>
     47       1.1     marty 
     48       1.1     marty #include <arm/cortex/gic_intr.h>
     49       1.1     marty 
     50       1.1     marty #include <arm/samsung/exynos_reg.h>
     51       1.1     marty #include <arm/samsung/exynos_intr.h>
     52       1.1     marty 
     53       1.1     marty #include <dev/fdt/fdtvar.h>
     54       1.1     marty 
     55       1.8  jmcneill #define	COMBINER_GROUP_BASE(group)	(((group) / 4) * 0x10)
     56       1.8  jmcneill #define	COMBINER_GROUP_MASK(group)	(0xff << (((group) % 4) * 8))
     57       1.8  jmcneill 
     58       1.8  jmcneill #define	COMBINER_IESR_REG(group)	(COMBINER_GROUP_BASE(group) + 0x00)
     59       1.8  jmcneill #define	COMBINER_IECR_REG(group)	(COMBINER_GROUP_BASE(group) + 0x04)
     60       1.8  jmcneill #define	COMBINER_ISTR_REG(group)	(COMBINER_GROUP_BASE(group) + 0x08)
     61       1.8  jmcneill #define	COMBINER_IMSR_REG(group)	(COMBINER_GROUP_BASE(group) + 0x0c)
     62       1.5     marty 
     63       1.5     marty struct exynos_combiner_softc;
     64       1.5     marty 
     65       1.5     marty struct exynos_combiner_irq_entry {
     66       1.5     marty 	int				irq_no;
     67       1.5     marty 	int (*irq_handler)(void *);
     68       1.5     marty 	void *				irq_arg;
     69       1.5     marty 	struct exynos_combiner_irq_entry *irq_next;
     70       1.7  jmcneill 	bool				irq_mpsafe;
     71       1.5     marty };
     72       1.5     marty 
     73       1.8  jmcneill struct exynos_combiner_irq_group {
     74       1.8  jmcneill 	int irq_group_no;
     75       1.5     marty 	struct exynos_combiner_softc	*irq_sc;
     76       1.5     marty 	struct exynos_combiner_irq_entry *irq_entries;
     77       1.8  jmcneill 	struct exynos_combiner_irq_group *irq_group_next;
     78       1.7  jmcneill 	void *irq_ih;
     79       1.8  jmcneill 	int irq_ipl;
     80       1.5     marty };
     81       1.3     marty 
     82       1.1     marty struct exynos_combiner_softc {
     83       1.1     marty 	device_t		sc_dev;
     84       1.3     marty 	bus_space_tag_t		sc_bst;
     85       1.3     marty 	bus_space_handle_t	sc_bsh;
     86       1.1     marty 	int			sc_phandle;
     87       1.8  jmcneill 	struct exynos_combiner_irq_group *irq_groups;
     88       1.1     marty };
     89       1.1     marty 
     90       1.1     marty static int exynos_combiner_match(device_t, cfdata_t, void *);
     91       1.1     marty static void exynos_combiner_attach(device_t, device_t, void *);
     92       1.1     marty 
     93       1.6     marty static void *	exynos_combiner_establish(device_t, u_int *, int, int,
     94  1.11.8.1   thorpej 		    int (*)(void *), void *, const char *);
     95       1.1     marty static void	exynos_combiner_disestablish(device_t, void *);
     96       1.6     marty static bool	exynos_combiner_intrstr(device_t, u_int  *, char *,
     97       1.6     marty 					size_t);
     98       1.1     marty 
     99       1.1     marty struct fdtbus_interrupt_controller_func exynos_combiner_funcs = {
    100       1.1     marty 	.establish = exynos_combiner_establish,
    101       1.1     marty 	.disestablish = exynos_combiner_disestablish,
    102       1.1     marty 	.intrstr = exynos_combiner_intrstr
    103       1.1     marty };
    104       1.1     marty 
    105       1.1     marty CFATTACH_DECL_NEW(exynos_intr, sizeof(struct exynos_combiner_softc),
    106       1.1     marty 	exynos_combiner_match, exynos_combiner_attach, NULL, NULL);
    107       1.1     marty 
    108  1.11.8.1   thorpej static const struct device_compatible_entry compat_data[] = {
    109  1.11.8.1   thorpej 	{ .compat = "samsung,exynos4210-combiner" },
    110  1.11.8.1   thorpej 	DEVICE_COMPAT_EOL
    111  1.11.8.1   thorpej };
    112  1.11.8.1   thorpej 
    113       1.1     marty static int
    114       1.1     marty exynos_combiner_match(device_t parent, cfdata_t cf, void *aux)
    115       1.1     marty {
    116       1.1     marty 	struct fdt_attach_args * const faa = aux;
    117  1.11.8.1   thorpej 
    118  1.11.8.1   thorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
    119       1.1     marty }
    120       1.1     marty 
    121       1.1     marty static void
    122       1.1     marty exynos_combiner_attach(device_t parent, device_t self, void *aux)
    123       1.1     marty {
    124       1.5     marty 	struct exynos_combiner_softc * const sc = device_private(self);
    125       1.1     marty 	struct fdt_attach_args * const faa = aux;
    126       1.1     marty 	bus_addr_t addr;
    127       1.1     marty 	bus_size_t size;
    128       1.1     marty 	int error;
    129       1.1     marty 
    130       1.1     marty 	if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
    131       1.1     marty 		aprint_error(": couldn't get registers\n");
    132       1.1     marty 		return;
    133       1.1     marty 	}
    134       1.1     marty 
    135       1.1     marty 	sc->sc_dev = self;
    136       1.1     marty 	sc->sc_phandle = faa->faa_phandle;
    137       1.3     marty 	sc->sc_bst = faa->faa_bst;
    138       1.3     marty 
    139       1.3     marty 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
    140       1.3     marty 	if (error) {
    141      1.11     skrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d",
    142      1.11     skrll 			     addr, error);
    143       1.3     marty 		return;
    144       1.3     marty 	}
    145       1.3     marty 
    146       1.1     marty 	error = fdtbus_register_interrupt_controller(self, faa->faa_phandle,
    147       1.1     marty 	    &exynos_combiner_funcs);
    148       1.1     marty 	if (error) {
    149       1.1     marty 		aprint_error(": couldn't register with fdtbus: %d\n", error);
    150       1.1     marty 		return;
    151       1.1     marty 	}
    152       1.1     marty 
    153       1.1     marty 	aprint_naive("\n");
    154       1.8  jmcneill 	aprint_normal(" @ 0x%08x: interrupt combiner\n", (uint)addr);
    155       1.1     marty 
    156       1.1     marty }
    157       1.1     marty 
    158       1.8  jmcneill static struct exynos_combiner_irq_group *
    159       1.8  jmcneill exynos_combiner_new_group(struct exynos_combiner_softc *sc, int group_no)
    160       1.5     marty {
    161       1.8  jmcneill 	struct exynos_combiner_irq_group *n = kmem_zalloc(sizeof(*n),
    162       1.5     marty 							  KM_SLEEP);
    163       1.8  jmcneill 	n->irq_group_no = group_no;
    164       1.8  jmcneill 	n->irq_group_next = sc->irq_groups;
    165       1.8  jmcneill 	n->irq_sc = sc;
    166       1.8  jmcneill 	sc->irq_groups = n;
    167       1.5     marty 	return n;
    168       1.5     marty }
    169       1.9     skrll 
    170       1.8  jmcneill static struct exynos_combiner_irq_group *
    171       1.8  jmcneill exynos_combiner_get_group(struct exynos_combiner_softc *sc, int group_no)
    172       1.5     marty {
    173       1.8  jmcneill 	for (struct exynos_combiner_irq_group *g = sc->irq_groups;
    174       1.8  jmcneill 	     g; g = g->irq_group_next) {
    175       1.8  jmcneill 		if (g->irq_group_no == group_no)
    176       1.8  jmcneill 			return g;
    177       1.5     marty 	}
    178       1.5     marty 	return NULL;
    179       1.5     marty }
    180       1.5     marty 
    181       1.5     marty static struct exynos_combiner_irq_entry *
    182       1.8  jmcneill exynos_combiner_new_irq(struct exynos_combiner_irq_group *group,
    183       1.7  jmcneill 			int irq, bool mpsafe, int (*func)(void *), void *arg)
    184       1.5     marty {
    185       1.5     marty 	struct exynos_combiner_irq_entry * n = kmem_zalloc(sizeof(*n),
    186       1.5     marty 							   KM_SLEEP);
    187       1.5     marty 	n->irq_no = irq;
    188       1.5     marty 	n->irq_handler = func;
    189       1.8  jmcneill 	n->irq_next = group->irq_entries;
    190       1.5     marty 	n->irq_arg = arg;
    191       1.7  jmcneill 	n->irq_mpsafe = mpsafe;
    192       1.8  jmcneill 	group->irq_entries = n;
    193       1.5     marty 	return n;
    194       1.5     marty }
    195       1.5     marty 
    196       1.5     marty static struct exynos_combiner_irq_entry *
    197       1.8  jmcneill exynos_combiner_get_irq(struct exynos_combiner_irq_group *g, int irq)
    198       1.5     marty {
    199       1.8  jmcneill 	for (struct exynos_combiner_irq_entry *p = g->irq_entries; p;
    200       1.5     marty 	     p = p->irq_next) {
    201       1.5     marty 		if (p->irq_no == irq)
    202       1.5     marty 			return p;
    203       1.5     marty 	}
    204       1.5     marty 	return NULL;
    205       1.5     marty }
    206       1.5     marty 
    207       1.7  jmcneill static int
    208       1.7  jmcneill exynos_combiner_irq(void *cookie)
    209       1.5     marty {
    210       1.8  jmcneill 	struct exynos_combiner_irq_group *groupp = cookie;
    211       1.8  jmcneill 	struct exynos_combiner_softc *sc = groupp->irq_sc;
    212       1.8  jmcneill 	int rv = 0;
    213       1.8  jmcneill 
    214       1.8  jmcneill 	const int group_no = groupp->irq_group_no;
    215       1.8  jmcneill 
    216       1.8  jmcneill 	const uint32_t istr =
    217       1.8  jmcneill 	    bus_space_read_4(sc->sc_bst, sc->sc_bsh, COMBINER_ISTR_REG(group_no));
    218       1.8  jmcneill 	const uint32_t istatus = __SHIFTOUT(istr, COMBINER_GROUP_MASK(group_no));
    219       1.8  jmcneill 
    220       1.5     marty 	for (int irq = 0; irq < 8; irq++) {
    221       1.8  jmcneill 		if (istatus & __BIT(irq)) {
    222       1.5     marty 			struct exynos_combiner_irq_entry *e =
    223       1.8  jmcneill 				exynos_combiner_get_irq(groupp, irq);
    224       1.7  jmcneill 			if (e) {
    225       1.7  jmcneill 				if (!e->irq_mpsafe)
    226       1.7  jmcneill 					KERNEL_LOCK(1, curlwp);
    227       1.8  jmcneill 				rv += e->irq_handler(e->irq_arg);
    228       1.7  jmcneill 				if (!e->irq_mpsafe)
    229       1.7  jmcneill 					KERNEL_UNLOCK_ONE(curlwp);
    230       1.7  jmcneill 			} else
    231       1.8  jmcneill 				printf("%s: Unexpected irq (%d) on group %d\n", __func__,
    232       1.8  jmcneill 				       irq, group_no);
    233       1.5     marty 		}
    234       1.5     marty 	}
    235       1.8  jmcneill 
    236       1.8  jmcneill 	return !!rv;
    237       1.5     marty }
    238       1.5     marty 
    239       1.1     marty static void *
    240       1.6     marty exynos_combiner_establish(device_t dev, u_int *specifier,
    241       1.6     marty 			  int ipl, int flags,
    242  1.11.8.1   thorpej 			  int (*func)(void *), void *arg, const char *xname)
    243       1.1     marty {
    244       1.1     marty 	struct exynos_combiner_softc * const sc = device_private(dev);
    245       1.8  jmcneill 	struct exynos_combiner_irq_group *groupp;
    246       1.5     marty 	struct exynos_combiner_irq_entry *entryp;
    247       1.7  jmcneill 	const bool mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
    248       1.8  jmcneill 	uint32_t iesr;
    249       1.5     marty 
    250       1.8  jmcneill 	const u_int group = be32toh(specifier[0]);
    251       1.8  jmcneill 	const u_int intr = be32toh(specifier[1]);
    252       1.5     marty 
    253       1.8  jmcneill 	groupp = exynos_combiner_get_group(sc, group);
    254       1.8  jmcneill 	if (!groupp) {
    255       1.8  jmcneill 		groupp = exynos_combiner_new_group(sc, group);
    256       1.8  jmcneill 		if (arg == NULL) {
    257  1.11.8.1   thorpej 			groupp->irq_ih = fdtbus_intr_establish_xname(
    258  1.11.8.1   thorpej 			    sc->sc_phandle, group, ipl /* XXX */, flags, func, NULL,
    259  1.11.8.1   thorpej 			    device_xname(dev));
    260       1.8  jmcneill 		} else {
    261  1.11.8.1   thorpej 			groupp->irq_ih = fdtbus_intr_establish_xname(
    262  1.11.8.1   thorpej 			    sc->sc_phandle,  group, ipl /* XXX */, FDT_INTR_MPSAFE,
    263  1.11.8.1   thorpej 			    exynos_combiner_irq, groupp, device_xname(dev));
    264       1.8  jmcneill 		}
    265       1.8  jmcneill 		KASSERT(groupp->irq_ih != NULL);
    266       1.8  jmcneill 		groupp->irq_ipl = ipl;
    267       1.8  jmcneill 	} else if (groupp->irq_ipl != ipl) {
    268       1.8  jmcneill 		aprint_error_dev(dev,
    269       1.8  jmcneill 		    "interrupt combiner cannot share interrupts with different ipl\n");
    270       1.8  jmcneill 		return NULL;
    271       1.5     marty 	}
    272       1.5     marty 
    273       1.8  jmcneill 	if (exynos_combiner_get_irq(groupp, intr) != NULL)
    274       1.5     marty 		return NULL;
    275       1.5     marty 
    276       1.8  jmcneill 	entryp = exynos_combiner_new_irq(groupp, intr, mpsafe, func, arg);
    277       1.8  jmcneill 
    278       1.8  jmcneill 	iesr = bus_space_read_4(sc->sc_bst, sc->sc_bsh, COMBINER_IESR_REG(group));
    279       1.8  jmcneill 	iesr |= __SHIFTIN((1 << intr), COMBINER_GROUP_MASK(group));
    280       1.8  jmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, COMBINER_IESR_REG(group), iesr);
    281       1.8  jmcneill 
    282       1.8  jmcneill 	return entryp;
    283       1.1     marty }
    284       1.1     marty 
    285       1.1     marty static void
    286       1.1     marty exynos_combiner_disestablish(device_t dev, void *ih)
    287       1.1     marty {
    288       1.5     marty 	/* MJF: Find the ih and disable the handler. */
    289       1.7  jmcneill 	panic("exynos_combiner_disestablish not implemented");
    290       1.1     marty }
    291       1.1     marty 
    292       1.1     marty static bool
    293       1.6     marty exynos_combiner_intrstr(device_t dev, u_int *specifier, char *buf,
    294       1.6     marty 			size_t buflen)
    295       1.1     marty {
    296       1.1     marty 
    297       1.8  jmcneill 	/* 1st cell is the combiner group number */
    298       1.8  jmcneill 	/* 2nd cell is the interrupt number within the group */
    299       1.1     marty 
    300       1.8  jmcneill 	const u_int group = be32toh(specifier[0]);
    301       1.8  jmcneill 	const u_int intr = be32toh(specifier[1]);
    302       1.1     marty 
    303       1.8  jmcneill 	snprintf(buf, buflen, "interrupt combiner group %d intr %d", group, intr);
    304       1.1     marty 
    305       1.1     marty 	return true;
    306       1.1     marty }
    307