Home | History | Annotate | Line # | Download | only in samsung
exynos_combiner.c revision 1.12
      1  1.12  jmcneill /*	$NetBSD: exynos_combiner.c,v 1.12 2021/01/15 00:38:23 jmcneill 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.12  jmcneill __KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.12 2021/01/15 00:38:23 jmcneill 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.12  jmcneill 		    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.1     marty static int
    109   1.1     marty exynos_combiner_match(device_t parent, cfdata_t cf, void *aux)
    110   1.1     marty {
    111   1.1     marty 	const char * const compatible[] = { "samsung,exynos4210-combiner",
    112   1.1     marty 					    NULL };
    113   1.1     marty 	struct fdt_attach_args * const faa = aux;
    114   1.1     marty 	return of_match_compatible(faa->faa_phandle, compatible);
    115   1.1     marty }
    116   1.1     marty 
    117   1.1     marty static void
    118   1.1     marty exynos_combiner_attach(device_t parent, device_t self, void *aux)
    119   1.1     marty {
    120   1.5     marty 	struct exynos_combiner_softc * const sc = device_private(self);
    121   1.1     marty 	struct fdt_attach_args * const faa = aux;
    122   1.1     marty 	bus_addr_t addr;
    123   1.1     marty 	bus_size_t size;
    124   1.1     marty 	int error;
    125   1.1     marty 
    126   1.1     marty 	if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
    127   1.1     marty 		aprint_error(": couldn't get registers\n");
    128   1.1     marty 		return;
    129   1.1     marty 	}
    130   1.1     marty 
    131   1.1     marty 	sc->sc_dev = self;
    132   1.1     marty 	sc->sc_phandle = faa->faa_phandle;
    133   1.3     marty 	sc->sc_bst = faa->faa_bst;
    134   1.3     marty 
    135   1.3     marty 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
    136   1.3     marty 	if (error) {
    137  1.11     skrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d",
    138  1.11     skrll 			     addr, error);
    139   1.3     marty 		return;
    140   1.3     marty 	}
    141   1.3     marty 
    142   1.1     marty 	error = fdtbus_register_interrupt_controller(self, faa->faa_phandle,
    143   1.1     marty 	    &exynos_combiner_funcs);
    144   1.1     marty 	if (error) {
    145   1.1     marty 		aprint_error(": couldn't register with fdtbus: %d\n", error);
    146   1.1     marty 		return;
    147   1.1     marty 	}
    148   1.1     marty 
    149   1.1     marty 	aprint_naive("\n");
    150   1.8  jmcneill 	aprint_normal(" @ 0x%08x: interrupt combiner\n", (uint)addr);
    151   1.1     marty 
    152   1.1     marty }
    153   1.1     marty 
    154   1.8  jmcneill static struct exynos_combiner_irq_group *
    155   1.8  jmcneill exynos_combiner_new_group(struct exynos_combiner_softc *sc, int group_no)
    156   1.5     marty {
    157   1.8  jmcneill 	struct exynos_combiner_irq_group *n = kmem_zalloc(sizeof(*n),
    158   1.5     marty 							  KM_SLEEP);
    159   1.8  jmcneill 	n->irq_group_no = group_no;
    160   1.8  jmcneill 	n->irq_group_next = sc->irq_groups;
    161   1.8  jmcneill 	n->irq_sc = sc;
    162   1.8  jmcneill 	sc->irq_groups = n;
    163   1.5     marty 	return n;
    164   1.5     marty }
    165   1.9     skrll 
    166   1.8  jmcneill static struct exynos_combiner_irq_group *
    167   1.8  jmcneill exynos_combiner_get_group(struct exynos_combiner_softc *sc, int group_no)
    168   1.5     marty {
    169   1.8  jmcneill 	for (struct exynos_combiner_irq_group *g = sc->irq_groups;
    170   1.8  jmcneill 	     g; g = g->irq_group_next) {
    171   1.8  jmcneill 		if (g->irq_group_no == group_no)
    172   1.8  jmcneill 			return g;
    173   1.5     marty 	}
    174   1.5     marty 	return NULL;
    175   1.5     marty }
    176   1.5     marty 
    177   1.5     marty static struct exynos_combiner_irq_entry *
    178   1.8  jmcneill exynos_combiner_new_irq(struct exynos_combiner_irq_group *group,
    179   1.7  jmcneill 			int irq, bool mpsafe, int (*func)(void *), void *arg)
    180   1.5     marty {
    181   1.5     marty 	struct exynos_combiner_irq_entry * n = kmem_zalloc(sizeof(*n),
    182   1.5     marty 							   KM_SLEEP);
    183   1.5     marty 	n->irq_no = irq;
    184   1.5     marty 	n->irq_handler = func;
    185   1.8  jmcneill 	n->irq_next = group->irq_entries;
    186   1.5     marty 	n->irq_arg = arg;
    187   1.7  jmcneill 	n->irq_mpsafe = mpsafe;
    188   1.8  jmcneill 	group->irq_entries = n;
    189   1.5     marty 	return n;
    190   1.5     marty }
    191   1.5     marty 
    192   1.5     marty static struct exynos_combiner_irq_entry *
    193   1.8  jmcneill exynos_combiner_get_irq(struct exynos_combiner_irq_group *g, int irq)
    194   1.5     marty {
    195   1.8  jmcneill 	for (struct exynos_combiner_irq_entry *p = g->irq_entries; p;
    196   1.5     marty 	     p = p->irq_next) {
    197   1.5     marty 		if (p->irq_no == irq)
    198   1.5     marty 			return p;
    199   1.5     marty 	}
    200   1.5     marty 	return NULL;
    201   1.5     marty }
    202   1.5     marty 
    203   1.7  jmcneill static int
    204   1.7  jmcneill exynos_combiner_irq(void *cookie)
    205   1.5     marty {
    206   1.8  jmcneill 	struct exynos_combiner_irq_group *groupp = cookie;
    207   1.8  jmcneill 	struct exynos_combiner_softc *sc = groupp->irq_sc;
    208   1.8  jmcneill 	int rv = 0;
    209   1.8  jmcneill 
    210   1.8  jmcneill 	const int group_no = groupp->irq_group_no;
    211   1.8  jmcneill 
    212   1.8  jmcneill 	const uint32_t istr =
    213   1.8  jmcneill 	    bus_space_read_4(sc->sc_bst, sc->sc_bsh, COMBINER_ISTR_REG(group_no));
    214   1.8  jmcneill 	const uint32_t istatus = __SHIFTOUT(istr, COMBINER_GROUP_MASK(group_no));
    215   1.8  jmcneill 
    216   1.5     marty 	for (int irq = 0; irq < 8; irq++) {
    217   1.8  jmcneill 		if (istatus & __BIT(irq)) {
    218   1.5     marty 			struct exynos_combiner_irq_entry *e =
    219   1.8  jmcneill 				exynos_combiner_get_irq(groupp, irq);
    220   1.7  jmcneill 			if (e) {
    221   1.7  jmcneill 				if (!e->irq_mpsafe)
    222   1.7  jmcneill 					KERNEL_LOCK(1, curlwp);
    223   1.8  jmcneill 				rv += e->irq_handler(e->irq_arg);
    224   1.7  jmcneill 				if (!e->irq_mpsafe)
    225   1.7  jmcneill 					KERNEL_UNLOCK_ONE(curlwp);
    226   1.7  jmcneill 			} else
    227   1.8  jmcneill 				printf("%s: Unexpected irq (%d) on group %d\n", __func__,
    228   1.8  jmcneill 				       irq, group_no);
    229   1.5     marty 		}
    230   1.5     marty 	}
    231   1.8  jmcneill 
    232   1.8  jmcneill 	return !!rv;
    233   1.5     marty }
    234   1.5     marty 
    235   1.1     marty static void *
    236   1.6     marty exynos_combiner_establish(device_t dev, u_int *specifier,
    237   1.6     marty 			  int ipl, int flags,
    238  1.12  jmcneill 			  int (*func)(void *), void *arg, const char *xname)
    239   1.1     marty {
    240   1.1     marty 	struct exynos_combiner_softc * const sc = device_private(dev);
    241   1.8  jmcneill 	struct exynos_combiner_irq_group *groupp;
    242   1.5     marty 	struct exynos_combiner_irq_entry *entryp;
    243   1.7  jmcneill 	const bool mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
    244   1.8  jmcneill 	uint32_t iesr;
    245   1.5     marty 
    246   1.8  jmcneill 	const u_int group = be32toh(specifier[0]);
    247   1.8  jmcneill 	const u_int intr = be32toh(specifier[1]);
    248   1.5     marty 
    249   1.8  jmcneill 	groupp = exynos_combiner_get_group(sc, group);
    250   1.8  jmcneill 	if (!groupp) {
    251   1.8  jmcneill 		groupp = exynos_combiner_new_group(sc, group);
    252   1.8  jmcneill 		if (arg == NULL) {
    253  1.12  jmcneill 			groupp->irq_ih = fdtbus_intr_establish(sc->sc_phandle,
    254  1.12  jmcneill 			    group, ipl /* XXX */, flags, func, NULL);
    255   1.8  jmcneill 		} else {
    256  1.12  jmcneill 			groupp->irq_ih = fdtbus_intr_establish(sc->sc_phandle,
    257  1.12  jmcneill 			    group, ipl /* XXX */, FDT_INTR_MPSAFE,
    258  1.12  jmcneill 			    exynos_combiner_irq, groupp);
    259   1.8  jmcneill 		}
    260   1.8  jmcneill 		KASSERT(groupp->irq_ih != NULL);
    261   1.8  jmcneill 		groupp->irq_ipl = ipl;
    262   1.8  jmcneill 	} else if (groupp->irq_ipl != ipl) {
    263   1.8  jmcneill 		aprint_error_dev(dev,
    264   1.8  jmcneill 		    "interrupt combiner cannot share interrupts with different ipl\n");
    265   1.8  jmcneill 		return NULL;
    266   1.5     marty 	}
    267   1.5     marty 
    268   1.8  jmcneill 	if (exynos_combiner_get_irq(groupp, intr) != NULL)
    269   1.5     marty 		return NULL;
    270   1.5     marty 
    271   1.8  jmcneill 	entryp = exynos_combiner_new_irq(groupp, intr, mpsafe, func, arg);
    272   1.8  jmcneill 
    273   1.8  jmcneill 	iesr = bus_space_read_4(sc->sc_bst, sc->sc_bsh, COMBINER_IESR_REG(group));
    274   1.8  jmcneill 	iesr |= __SHIFTIN((1 << intr), COMBINER_GROUP_MASK(group));
    275   1.8  jmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, COMBINER_IESR_REG(group), iesr);
    276   1.8  jmcneill 
    277   1.8  jmcneill 	return entryp;
    278   1.1     marty }
    279   1.1     marty 
    280   1.1     marty static void
    281   1.1     marty exynos_combiner_disestablish(device_t dev, void *ih)
    282   1.1     marty {
    283   1.5     marty 	/* MJF: Find the ih and disable the handler. */
    284   1.7  jmcneill 	panic("exynos_combiner_disestablish not implemented");
    285   1.1     marty }
    286   1.1     marty 
    287   1.1     marty static bool
    288   1.6     marty exynos_combiner_intrstr(device_t dev, u_int *specifier, char *buf,
    289   1.6     marty 			size_t buflen)
    290   1.1     marty {
    291   1.1     marty 
    292   1.8  jmcneill 	/* 1st cell is the combiner group number */
    293   1.8  jmcneill 	/* 2nd cell is the interrupt number within the group */
    294   1.1     marty 
    295   1.8  jmcneill 	const u_int group = be32toh(specifier[0]);
    296   1.8  jmcneill 	const u_int intr = be32toh(specifier[1]);
    297   1.1     marty 
    298   1.8  jmcneill 	snprintf(buf, buflen, "interrupt combiner group %d intr %d", group, intr);
    299   1.1     marty 
    300   1.1     marty 	return true;
    301   1.1     marty }
    302