Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/fdt Add fdt_intr_mask() / fdt_intr_unmask() function...



details:   https://anonhg.NetBSD.org/src/rev/1de775837474
branches:  trunk
changeset: 1007379:1de775837474
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Sun Feb 16 20:28:18 2020 +0000

description:
Add fdt_intr_mask() / fdt_intr_unmask() functions, for masking and
unmasking invididual interrupt sources (similar to acpi_intr_mask()
and acpi_intr_unmask()).

diffstat:

 sys/dev/fdt/fdt_intr.c |  143 +++++++++++++++++++++++++++++++++++++++++++-----
 sys/dev/fdt/fdt_subr.c |    9 ++-
 sys/dev/fdt/fdtvar.h   |    6 +-
 3 files changed, 139 insertions(+), 19 deletions(-)

diffs (274 lines):

diff -r 2e419d21926a -r 1de775837474 sys/dev/fdt/fdt_intr.c
--- a/sys/dev/fdt/fdt_intr.c    Sun Feb 16 17:45:11 2020 +0000
+++ b/sys/dev/fdt/fdt_intr.c    Sun Feb 16 20:28:18 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: fdt_intr.c,v 1.24 2019/12/31 20:47:05 jmcneill Exp $ */
+/* $NetBSD: fdt_intr.c,v 1.25 2020/02/16 20:28:18 thorpej Exp $ */
 
 /*-
  * Copyright (c) 2015-2018 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -27,15 +27,19 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fdt_intr.c,v 1.24 2019/12/31 20:47:05 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fdt_intr.c,v 1.25 2020/02/16 20:28:18 thorpej Exp $");
 
 #include <sys/param.h>
+#include <sys/atomic.h>
 #include <sys/bus.h>
 #include <sys/kmem.h>
 #include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
 
 #include <libfdt.h>
 #include <dev/fdt/fdtvar.h>
+#include <dev/fdt/fdt_private.h>
 
 struct fdtbus_interrupt_controller {
        device_t ic_dev;
@@ -53,14 +57,26 @@
        void *c_ih;
 
        LIST_ENTRY(fdtbus_interrupt_cookie) c_next;
+       uint32_t c_refcnt;
 };
 
 static LIST_HEAD(, fdtbus_interrupt_cookie) fdtbus_interrupt_cookies =
     LIST_HEAD_INITIALIZER(fdtbus_interrupt_cookies);
+static kmutex_t fdtbus_interrupt_cookie_mutex;
+static kcondvar_t fdtbus_interrupt_cookie_wait;
+static bool fdtbus_interrupt_cookies_wanted;
 
 static const u_int *   get_specifier_by_index(int, int, int *);
 static const u_int *   get_specifier_from_map(int, const u_int *, int *);
 
+void
+fdtbus_intr_init(void)
+{
+
+       mutex_init(&fdtbus_interrupt_cookie_mutex, MUTEX_SPIN, IPL_HIGH);
+       cv_init(&fdtbus_interrupt_cookie_wait, "fdtintr");
+}
+
 /*
  * Find the interrupt controller for a given node. This function will either
  * return the phandle of the interrupt controller for this node, or the phandle
@@ -130,6 +146,37 @@
        return 0;
 }
 
+static struct fdtbus_interrupt_cookie *
+fdtbus_get_interrupt_cookie(void *cookie)
+{
+       struct fdtbus_interrupt_cookie *c;
+
+       mutex_enter(&fdtbus_interrupt_cookie_mutex);
+       LIST_FOREACH(c, &fdtbus_interrupt_cookies, c_next) {
+               if (c->c_ih == cookie) {
+                       c->c_refcnt++;
+                       KASSERT(c->c_refcnt > 0);
+                       break;
+               }
+       }
+       mutex_exit(&fdtbus_interrupt_cookie_mutex);
+       return c;
+}
+
+static void
+fdtbus_put_interrupt_cookie(struct fdtbus_interrupt_cookie *c)
+{
+
+       mutex_enter(&fdtbus_interrupt_cookie_mutex);
+       KASSERT(c->c_refcnt > 0);
+       c->c_refcnt--;
+       if (fdtbus_interrupt_cookies_wanted) {
+               fdtbus_interrupt_cookies_wanted = false;
+               cv_signal(&fdtbus_interrupt_cookie_wait);
+       }
+       mutex_exit(&fdtbus_interrupt_cookie_mutex);
+}
+
 void *
 fdtbus_intr_establish(int phandle, u_int index, int ipl, int flags,
     int (*func)(void *), void *arg)
@@ -173,13 +220,29 @@
                return NULL;
        }
 
+       c = kmem_zalloc(sizeof(*c), KM_SLEEP);
+       c->c_ic = ic;
+       mutex_enter(&fdtbus_interrupt_cookie_mutex);
+       LIST_INSERT_HEAD(&fdtbus_interrupt_cookies, c, c_next);
+       mutex_exit(&fdtbus_interrupt_cookie_mutex);
+
+       /*
+        * XXX This leaves a small window where the handler is registered
+        * (and thus could be called) before the cookie on the list has a
+        * valid lookup key (and thus can be found).  This will cause a
+        * panic in fdt_intr_mask() if that is called from the handler before
+        * this situation is resolved.  For now we just cross our fingers
+        * and hope that the device won't actually interrupt until we return.
+        */
        ih = ic->ic_funcs->establish(ic->ic_dev, __UNCONST(specifier),
            ipl, flags, func, arg);
        if (ih != NULL) {
-               c = kmem_alloc(sizeof(*c), KM_SLEEP);
-               c->c_ic = ic;
-               c->c_ih = ih;
-               LIST_INSERT_HEAD(&fdtbus_interrupt_cookies, c, c_next);
+               atomic_store_release(&c->c_ih, ih);
+       } else {
+               mutex_enter(&fdtbus_interrupt_cookie_mutex);
+               LIST_REMOVE(c, c_next);
+               mutex_exit(&fdtbus_interrupt_cookie_mutex);
+               kmem_free(c, sizeof(*c));
        }
 
        return ih;
@@ -188,22 +251,70 @@
 void
 fdtbus_intr_disestablish(int phandle, void *cookie)
 {
-       struct fdtbus_interrupt_controller *ic = NULL;
+       struct fdtbus_interrupt_cookie *c;
+
+       if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) {
+               panic("%s: interrupt handle not valid", __func__);
+       }
+
+       const struct fdtbus_interrupt_controller *ic = c->c_ic;
+       ic->ic_funcs->disestablish(ic->ic_dev, cookie);
+
+       /*
+        * Wait for any dangling references other than ours to
+        * drain away.
+        */
+       mutex_enter(&fdtbus_interrupt_cookie_mutex);
+       while (c->c_refcnt != 1) {
+               KASSERT(c->c_refcnt > 0);
+               fdtbus_interrupt_cookies_wanted = true;
+               cv_wait(&fdtbus_interrupt_cookie_wait,
+                       &fdtbus_interrupt_cookie_mutex);
+       }
+       LIST_REMOVE(c, c_next);
+       mutex_exit(&fdtbus_interrupt_cookie_mutex);
+
+       kmem_free(c, sizeof(*c));
+}
+
+void
+fdtbus_intr_mask(int phandle, void *cookie)
+{
        struct fdtbus_interrupt_cookie *c;
 
-       LIST_FOREACH(c, &fdtbus_interrupt_cookies, c_next) {
-               if (c->c_ih == cookie) {
-                       ic = c->c_ic;
-                       LIST_REMOVE(c, c_next);
-                       kmem_free(c, sizeof(*c));
-                       break;
-               }
+       if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) {
+               panic("%s: interrupt handle not valid", __func__);
+       }
+
+       struct fdtbus_interrupt_controller * const ic = c->c_ic;
+
+       if (ic->ic_funcs->mask == NULL) {
+               panic("%s: no 'mask' method for %s", __func__,
+                   device_xname(ic->ic_dev));
        }
 
-       if (ic == NULL)
+       ic->ic_funcs->mask(ic->ic_dev, cookie);
+       fdtbus_put_interrupt_cookie(c);
+}
+
+void
+fdtbus_intr_unmask(int phandle, void *cookie)
+{
+       struct fdtbus_interrupt_cookie *c;
+
+       if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) {
                panic("%s: interrupt handle not valid", __func__);
+       }
 
-       return ic->ic_funcs->disestablish(ic->ic_dev, cookie);
+       struct fdtbus_interrupt_controller * const ic = c->c_ic;
+
+       if (ic->ic_funcs->unmask == NULL) {
+               panic("%s: no 'unmask' method for %s", __func__,
+                   device_xname(ic->ic_dev));
+       }
+
+       ic->ic_funcs->unmask(ic->ic_dev, cookie);
+       fdtbus_put_interrupt_cookie(c);
 }
 
 bool
diff -r 2e419d21926a -r 1de775837474 sys/dev/fdt/fdt_subr.c
--- a/sys/dev/fdt/fdt_subr.c    Sun Feb 16 17:45:11 2020 +0000
+++ b/sys/dev/fdt/fdt_subr.c    Sun Feb 16 20:28:18 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: fdt_subr.c,v 1.32 2020/02/16 14:56:52 thorpej Exp $ */
+/* $NetBSD: fdt_subr.c,v 1.33 2020/02/16 20:28:18 thorpej Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.32 2020/02/16 14:56:52 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.33 2020/02/16 20:28:18 thorpej Exp $");
 
 #include "opt_fdt.h"
 
@@ -36,6 +36,7 @@
 
 #include <libfdt.h>
 #include <dev/fdt/fdtvar.h>
+#include <dev/fdt/fdt_private.h>
 
 static const void *fdt_data;
 
@@ -50,6 +51,10 @@
                return false;
        }
        fdt_data = data;
+
+       /* Now that we have a FDT blob, initialize other bits that need it. */
+       fdtbus_intr_init();
+
        return true;
 }
 
diff -r 2e419d21926a -r 1de775837474 sys/dev/fdt/fdtvar.h
--- a/sys/dev/fdt/fdtvar.h      Sun Feb 16 17:45:11 2020 +0000
+++ b/sys/dev/fdt/fdtvar.h      Sun Feb 16 20:28:18 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: fdtvar.h,v 1.58 2020/02/16 14:56:52 thorpej Exp $ */
+/* $NetBSD: fdtvar.h,v 1.59 2020/02/16 20:28:18 thorpej Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -76,6 +76,8 @@
                             int (*)(void *), void *);
        void    (*disestablish)(device_t, void *);
        bool    (*intrstr)(device_t, u_int *, char *, size_t);
+       void    (*mask)(device_t, void *);
+       void    (*unmask)(device_t, void *);
 };
 
 struct fdtbus_i2c_controller_func {
@@ -316,6 +318,8 @@
                    int (*func)(void *), void *arg);
 void *         fdtbus_intr_establish_raw(int, const u_int *, int, int,
                    int (*func)(void *), void *arg);
+void           fdtbus_intr_mask(int, void *);
+void           fdtbus_intr_unmask(int, void *);
 void           fdtbus_intr_disestablish(int, void *);
 bool           fdtbus_intr_str(int, u_int, char *, size_t);
 bool           fdtbus_intr_str_raw(int, const u_int *, char *, size_t);



Home | Main Index | Thread Index | Old Index