git: 0962b9d08676 - stable/14 - newbus: Add a set of bus resource helpers for nexus-like devices

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Wed, 03 Jan 2024 22:02:43 UTC
The branch stable/14 has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=0962b9d0867602eed41361f1982ce8f6b967064b

commit 0962b9d0867602eed41361f1982ce8f6b967064b
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2023-11-24 17:28:00 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2024-01-03 20:47:08 +0000

    newbus: Add a set of bus resource helpers for nexus-like devices
    
    These routines can be used to implement
    bus_alloc/adjust/activate/deactive/release_resource on bus drivers
    which suballocate resources from rman(9) resource managers.
    
    These methods require a new bus_get_rman method in the bus driver to
    return the suitable rman for a given resource type.  The
    activate/deactivate helpers also require the bus to implement the
    bus_map/ummap_resource methods.
    
    Reviewed by:    imp
    Differential Revision:  https://reviews.freebsd.org/D42739
    
    (cherry picked from commit 751615c538446ea0384f8faa9cb2508670c3799a)
---
 sys/kern/bus_if.m   |  24 ++++++++
 sys/kern/subr_bus.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/sys/bus.h       |  17 ++++++
 3 files changed, 198 insertions(+)

diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m
index 7bd08fb713f8..7078683911b8 100644
--- a/sys/kern/bus_if.m
+++ b/sys/kern/bus_if.m
@@ -77,6 +77,12 @@ CODE {
 	{
 		return (0);
 	}
+
+	static struct rman *
+	null_get_rman(device_t bus, int type, u_int flags)
+	{
+		return (NULL);
+	}
 };
 
 /**
@@ -622,6 +628,24 @@ METHOD struct resource_list * get_resource_list {
 	device_t	_child;
 } DEFAULT bus_generic_get_resource_list;
 
+/**
+ * @brief Return a struct rman.
+ *
+ * Used by drivers which use bus_generic_rman_alloc_resource() etc. to
+ * implement their resource handling. It should return the resource
+ * manager used for the given resource type.
+ *
+ * @param _dev		the bus device
+ * @param _type		the resource type
+ * @param _flags	resource flags (@c RF_XXX flags in
+ *			<sys/rman.h>)
+ */
+METHOD struct rman * get_rman {
+	device_t	_dev;
+	int		_type;
+	u_int		_flags;
+} DEFAULT null_get_rman;
+
 /**
  * @brief Is the hardware described by @p _child still attached to the
  * system?
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 571e90b52dfc..4c44c264a5c0 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -4189,6 +4189,163 @@ bus_generic_rl_alloc_resource(device_t dev, device_t child, int type,
 	    start, end, count, flags));
 }
 
+/**
+ * @brief Helper function for implementing BUS_ALLOC_RESOURCE().
+ *
+ * This implementation of BUS_ALLOC_RESOURCE() allocates a
+ * resource from a resource manager.  It uses BUS_GET_RMAN()
+ * to obtain the resource manager.
+ */
+struct resource *
+bus_generic_rman_alloc_resource(device_t dev, device_t child, int type,
+    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+	struct resource *r;
+	struct rman *rm;
+
+	rm = BUS_GET_RMAN(dev, type, flags);
+	if (rm == NULL)
+		return (NULL);
+
+	r = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
+	    child);
+	if (r == NULL)
+		return (NULL);
+	rman_set_rid(r, *rid);
+
+	if (flags & RF_ACTIVE) {
+		if (bus_activate_resource(child, type, *rid, r) != 0) {
+			rman_release_resource(r);
+			return (NULL);
+		}
+	}
+
+	return (r);
+}
+
+/**
+ * @brief Helper function for implementing BUS_ADJUST_RESOURCE().
+ *
+ * This implementation of BUS_ADJUST_RESOURCE() adjusts resources only
+ * if they were allocated from the resource manager returned by
+ * BUS_GET_RMAN().
+ */
+int
+bus_generic_rman_adjust_resource(device_t dev, device_t child, int type,
+    struct resource *r, rman_res_t start, rman_res_t end)
+{
+	struct rman *rm;
+
+	rm = BUS_GET_RMAN(dev, type, rman_get_flags(r));
+	if (rm == NULL)
+		return (ENXIO);
+	if (!rman_is_region_manager(r, rm))
+		return (EINVAL);
+	return (rman_adjust_resource(r, start, end));
+}
+
+/**
+ * @brief Helper function for implementing BUS_RELEASE_RESOURCE().
+ *
+ * This implementation of BUS_RELEASE_RESOURCE() releases resources
+ * allocated by bus_generic_rman_alloc_resource.
+ */
+int
+bus_generic_rman_release_resource(device_t dev, device_t child, int type,
+    int rid, struct resource *r)
+{
+#ifdef INVARIANTS
+	struct rman *rm;
+#endif
+	int error;
+
+#ifdef INVARIANTS
+	rm = BUS_GET_RMAN(dev, type, rman_get_flags(r));
+	KASSERT(rman_is_region_manager(r, rm),
+	    ("%s: rman %p doesn't match for resource %p", __func__, rm, r));
+#endif
+
+	if (rman_get_flags(r) & RF_ACTIVE) {
+		error = bus_deactivate_resource(child, type, rid, r);
+		if (error != 0)
+			return (error);
+	}
+	return (rman_release_resource(r));
+}
+
+/**
+ * @brief Helper function for implementing BUS_ACTIVATE_RESOURCE().
+ *
+ * This implementation of BUS_ACTIVATE_RESOURCE() activates resources
+ * allocated by bus_generic_rman_alloc_resource.
+ */
+int
+bus_generic_rman_activate_resource(device_t dev, device_t child, int type,
+    int rid, struct resource *r)
+{
+	struct resource_map map;
+#ifdef INVARIANTS
+	struct rman *rm;
+#endif
+	int error;
+
+#ifdef INVARIANTS
+	rm = BUS_GET_RMAN(dev, type, rman_get_flags(r));
+	KASSERT(rman_is_region_manager(r, rm),
+	    ("%s: rman %p doesn't match for resource %p", __func__, rm, r));
+#endif
+
+	error = rman_activate_resource(r);
+	if (error != 0)
+		return (error);
+
+	if ((rman_get_flags(r) & RF_UNMAPPED) == 0 &&
+	    (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT)) {
+		error = BUS_MAP_RESOURCE(dev, child, type, r, NULL, &map);
+		if (error != 0) {
+			rman_deactivate_resource(r);
+			return (error);
+		}
+
+		rman_set_mapping(r, &map);
+	}
+	return (0);
+}
+
+/**
+ * @brief Helper function for implementing BUS_DEACTIVATE_RESOURCE().
+ *
+ * This implementation of BUS_DEACTIVATE_RESOURCE() deactivates
+ * resources allocated by bus_generic_rman_alloc_resource.
+ */
+int
+bus_generic_rman_deactivate_resource(device_t dev, device_t child, int type,
+    int rid, struct resource *r)
+{
+	struct resource_map map;
+#ifdef INVARIANTS
+	struct rman *rm;
+#endif
+	int error;
+
+#ifdef INVARIANTS
+	rm = BUS_GET_RMAN(dev, type, rman_get_flags(r));
+	KASSERT(rman_is_region_manager(r, rm),
+	    ("%s: rman %p doesn't match for resource %p", __func__, rm, r));
+#endif
+
+	error = rman_deactivate_resource(r);
+	if (error != 0)
+		return (error);
+
+	if ((rman_get_flags(r) & RF_UNMAPPED) == 0 &&
+	    (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT)) {
+		rman_get_mapping(r, &map);
+		BUS_UNMAP_RESOURCE(dev, child, type, r, &map);
+	}
+	return (0);
+}
+
 /**
  * @brief Helper function for implementing BUS_CHILD_PRESENT().
  *
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index 88ae4000004b..2ec735659452 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -499,6 +499,23 @@ int	bus_generic_rl_set_resource (device_t, device_t, int, int, rman_res_t,
 				     rman_res_t);
 int	bus_generic_rl_release_resource (device_t, device_t, int, int,
 					 struct resource *);
+struct resource *
+	bus_generic_rman_alloc_resource(device_t dev, device_t child, int type,
+					int *rid, rman_res_t start,
+					rman_res_t end, rman_res_t count,
+					u_int flags);
+int	bus_generic_rman_adjust_resource(device_t dev, device_t child, int type,
+					 struct resource *r, rman_res_t start,
+					 rman_res_t end);
+int	bus_generic_rman_release_resource(device_t dev, device_t child,
+					  int type, int rid,
+					  struct resource *r);
+int	bus_generic_rman_activate_resource(device_t dev, device_t child,
+					   int type, int rid,
+					   struct resource *r);
+int	bus_generic_rman_deactivate_resource(device_t dev, device_t child,
+					     int type, int rid,
+					     struct resource *r);
 
 int	bus_generic_shutdown(device_t dev);
 int	bus_generic_suspend(device_t dev);