Home | History | Annotate | Line # | Download | only in samsung
      1  1.14     skrll /*	$NetBSD: exynos_combiner.c,v 1.14 2021/03/14 08:16:57 skrll 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.14     skrll __KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.14 2021/03/14 08:16:57 skrll 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.13   thorpej static const struct device_compatible_entry compat_data[] = {
    109  1.13   thorpej 	{ .compat = "samsung,exynos4210-combiner" },
    110  1.13   thorpej 	DEVICE_COMPAT_EOL
    111  1.13   thorpej };
    112  1.13   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.13   thorpej 
    118  1.13   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.12  jmcneill 			  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.14     skrll 			groupp->irq_ih = fdtbus_intr_establish_xname(
    258  1.14     skrll 			    sc->sc_phandle, group, ipl /* XXX */, flags, func, NULL,
    259  1.14     skrll 			    device_xname(dev));
    260   1.8  jmcneill 		} else {
    261  1.14     skrll 			groupp->irq_ih = fdtbus_intr_establish_xname(
    262  1.14     skrll 			    sc->sc_phandle,  group, ipl /* XXX */, FDT_INTR_MPSAFE,
    263  1.14     skrll 			    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