linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] DT address cleanups and refactoring
@ 2021-05-27 19:45 Rob Herring
  2021-05-27 19:45 ` [PATCH 1/4] PCI: Add empty stub for pci_register_io_range() Rob Herring
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Rob Herring @ 2021-05-27 19:45 UTC (permalink / raw)
  To: devicetree, Frank Rowand
  Cc: linux-kernel, Bjorn Helgaas, linux-pci, Randy Dunlap

This series merges implementations of som PCI and 'regular' DT address 
functions and it simplifies the of_address.h ifdefs a bit.

Rob

Rob Herring (4):
  PCI: Add empty stub for pci_register_io_range()
  of: Merge of_get_address() and of_get_pci_address() implementations
  of: address: Use IS_ENABLED() for !CONFIG_PCI
  of: Merge of_address_to_resource() and of_pci_address_to_resource()
    implementations

 drivers/of/address.c       | 114 +++++++++++++------------------------
 include/linux/of_address.h |  54 +++++++++---------
 include/linux/pci.h        |   4 ++
 3 files changed, 70 insertions(+), 102 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] PCI: Add empty stub for pci_register_io_range()
  2021-05-27 19:45 [PATCH 0/4] DT address cleanups and refactoring Rob Herring
@ 2021-05-27 19:45 ` Rob Herring
  2021-05-27 21:55   ` Bjorn Helgaas
  2021-05-27 19:45 ` [PATCH 2/4] of: Merge of_get_address() and of_get_pci_address() implementations Rob Herring
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Rob Herring @ 2021-05-27 19:45 UTC (permalink / raw)
  To: devicetree, Frank Rowand
  Cc: linux-kernel, Bjorn Helgaas, linux-pci, Randy Dunlap

Add an empty stub for pci_register_io_range() when !CONFIG_PCI. It's needed
to convert of_pci_range_to_resource() to use IS_ENABLED(CONFIG_PCI).

Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: linux-pci@vger.kernel.org
Signed-off-by: Rob Herring <robh@kernel.org>
---
 include/linux/pci.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/linux/pci.h b/include/linux/pci.h
index c20211e59a57..29da7598f8d0 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1772,6 +1772,10 @@ static inline int pci_request_regions(struct pci_dev *dev, const char *res_name)
 { return -EIO; }
 static inline void pci_release_regions(struct pci_dev *dev) { }
 
+static inline int pci_register_io_range(struct fwnode_handle *fwnode,
+					phys_addr_t addr, resource_size_t size)
+{ return -EINVAL; }
+
 static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
 
 static inline struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] of: Merge of_get_address() and of_get_pci_address() implementations
  2021-05-27 19:45 [PATCH 0/4] DT address cleanups and refactoring Rob Herring
  2021-05-27 19:45 ` [PATCH 1/4] PCI: Add empty stub for pci_register_io_range() Rob Herring
@ 2021-05-27 19:45 ` Rob Herring
  2021-05-27 19:45 ` [PATCH 3/4] of: address: Use IS_ENABLED() for !CONFIG_PCI Rob Herring
  2021-05-27 19:45 ` [PATCH 4/4] of: Merge of_address_to_resource() and of_pci_address_to_resource() implementations Rob Herring
  3 siblings, 0 replies; 6+ messages in thread
From: Rob Herring @ 2021-05-27 19:45 UTC (permalink / raw)
  To: devicetree, Frank Rowand
  Cc: linux-kernel, Bjorn Helgaas, linux-pci, Randy Dunlap

of_get_address() and of_get_pci_address() are the same implementation
except of_get_pci_address() takes the PCI BAR number rather than an
index. Modify the of_get_address() implementation to work on either
index or BAR and provide wrapper functions for the existing functions.

Cc: Frank Rowand <frowand.list@gmail.com>
Signed-off-by: Rob Herring <robh@kernel.org>
---
 drivers/of/address.c       | 62 ++++++++------------------------------
 include/linux/of_address.h | 27 ++++++++++-------
 2 files changed, 29 insertions(+), 60 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index aca94c348bd4..aa766437995c 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -199,50 +199,6 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
 	return of_bus_default_translate(addr + 1, offset, na - 1);
 }
 
-const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
-			unsigned int *flags)
-{
-	const __be32 *prop;
-	unsigned int psize;
-	struct device_node *parent;
-	struct of_bus *bus;
-	int onesize, i, na, ns;
-
-	/* Get parent & match bus type */
-	parent = of_get_parent(dev);
-	if (parent == NULL)
-		return NULL;
-	bus = of_match_bus(parent);
-	if (strcmp(bus->name, "pci")) {
-		of_node_put(parent);
-		return NULL;
-	}
-	bus->count_cells(dev, &na, &ns);
-	of_node_put(parent);
-	if (!OF_CHECK_ADDR_COUNT(na))
-		return NULL;
-
-	/* Get "reg" or "assigned-addresses" property */
-	prop = of_get_property(dev, bus->addresses, &psize);
-	if (prop == NULL)
-		return NULL;
-	psize /= 4;
-
-	onesize = na + ns;
-	for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) {
-		u32 val = be32_to_cpu(prop[0]);
-		if ((val & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) {
-			if (size)
-				*size = of_read_number(prop + na, ns);
-			if (flags)
-				*flags = bus->get_flags(prop);
-			return prop;
-		}
-	}
-	return NULL;
-}
-EXPORT_SYMBOL(of_get_pci_address);
-
 int of_pci_address_to_resource(struct device_node *dev, int bar,
 			       struct resource *r)
 {
@@ -675,8 +631,8 @@ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
 }
 EXPORT_SYMBOL(of_translate_dma_address);
 
-const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
-		    unsigned int *flags)
+const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no,
+			       u64 *size, unsigned int *flags)
 {
 	const __be32 *prop;
 	unsigned int psize;
@@ -689,6 +645,10 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 	if (parent == NULL)
 		return NULL;
 	bus = of_match_bus(parent);
+	if (strcmp(bus->name, "pci") && (bar_no >= 0)) {
+		of_node_put(parent);
+		return NULL;
+	}
 	bus->count_cells(dev, &na, &ns);
 	of_node_put(parent);
 	if (!OF_CHECK_ADDR_COUNT(na))
@@ -701,17 +661,21 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 	psize /= 4;
 
 	onesize = na + ns;
-	for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
-		if (i == index) {
+	for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) {
+		u32 val = be32_to_cpu(prop[0]);
+		/* PCI bus matches on BAR number instead of index */
+		if (((bar_no >= 0) && ((val & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0))) ||
+		    ((index >= 0) && (i == index))) {
 			if (size)
 				*size = of_read_number(prop + na, ns);
 			if (flags)
 				*flags = bus->get_flags(prop);
 			return prop;
 		}
+	}
 	return NULL;
 }
-EXPORT_SYMBOL(of_get_address);
+EXPORT_SYMBOL(__of_get_address);
 
 static int parser_init(struct of_pci_range_parser *parser,
 			struct device_node *node, const char *name)
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 88bc943405cd..b72807faf037 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -51,8 +51,8 @@ void __iomem *of_io_request_and_map(struct device_node *device,
  * the address space flags too. The PCI version uses a BAR number
  * instead of an absolute index
  */
-extern const __be32 *of_get_address(struct device_node *dev, int index,
-			   u64 *size, unsigned int *flags);
+extern const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no,
+				      u64 *size, unsigned int *flags);
 
 extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
 			struct device_node *node);
@@ -75,8 +75,8 @@ static inline u64 of_translate_address(struct device_node *np,
 	return OF_BAD_ADDR;
 }
 
-static inline const __be32 *of_get_address(struct device_node *dev, int index,
-					u64 *size, unsigned int *flags)
+static inline const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no,
+					     u64 *size, unsigned int *flags)
 {
 	return NULL;
 }
@@ -125,8 +125,6 @@ static inline void __iomem *of_iomap(struct device_node *device, int index)
 #define of_range_parser_init of_pci_range_parser_init
 
 #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI)
-extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no,
-			       u64 *size, unsigned int *flags);
 extern int of_pci_address_to_resource(struct device_node *dev, int bar,
 				      struct resource *r);
 extern int of_pci_range_to_resource(struct of_pci_range *range,
@@ -139,11 +137,6 @@ static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
 	return -ENOSYS;
 }
 
-static inline const __be32 *of_get_pci_address(struct device_node *dev,
-		int bar_no, u64 *size, unsigned int *flags)
-{
-	return NULL;
-}
 static inline int of_pci_range_to_resource(struct of_pci_range *range,
 					   struct device_node *np,
 					   struct resource *res)
@@ -152,4 +145,16 @@ static inline int of_pci_range_to_resource(struct of_pci_range *range,
 }
 #endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */
 
+static inline const __be32 *of_get_address(struct device_node *dev, int index,
+					   u64 *size, unsigned int *flags)
+{
+	return __of_get_address(dev, index, -1, size, flags);
+}
+
+static inline const __be32 *of_get_pci_address(struct device_node *dev, int bar_no,
+					       u64 *size, unsigned int *flags)
+{
+	return __of_get_address(dev, -1, bar_no, size, flags);
+}
+
 #endif /* __OF_ADDRESS_H */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] of: address: Use IS_ENABLED() for !CONFIG_PCI
  2021-05-27 19:45 [PATCH 0/4] DT address cleanups and refactoring Rob Herring
  2021-05-27 19:45 ` [PATCH 1/4] PCI: Add empty stub for pci_register_io_range() Rob Herring
  2021-05-27 19:45 ` [PATCH 2/4] of: Merge of_get_address() and of_get_pci_address() implementations Rob Herring
@ 2021-05-27 19:45 ` Rob Herring
  2021-05-27 19:45 ` [PATCH 4/4] of: Merge of_address_to_resource() and of_pci_address_to_resource() implementations Rob Herring
  3 siblings, 0 replies; 6+ messages in thread
From: Rob Herring @ 2021-05-27 19:45 UTC (permalink / raw)
  To: devicetree, Frank Rowand
  Cc: linux-kernel, Bjorn Helgaas, linux-pci, Randy Dunlap

Convert address.c to use IS_ENABLED() instead of ifdefs for the
public PCI functions. This simplifies the ifdefs in of_address.h.

Cc: Frank Rowand <frowand.list@gmail.com>
Signed-off-by: Rob Herring <robh@kernel.org>
---
 drivers/of/address.c       |  8 +++++++-
 include/linux/of_address.h | 39 ++++++++++++++++++--------------------
 2 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index aa766437995c..e643f999743a 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -198,6 +198,7 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
 {
 	return of_bus_default_translate(addr + 1, offset, na - 1);
 }
+#endif /* CONFIG_PCI */
 
 int of_pci_address_to_resource(struct device_node *dev, int bar,
 			       struct resource *r)
@@ -206,6 +207,9 @@ int of_pci_address_to_resource(struct device_node *dev, int bar,
 	u64		size;
 	unsigned int	flags;
 
+	if (!IS_ENABLED(CONFIG_PCI))
+		return -ENOSYS;
+
 	addrp = of_get_pci_address(dev, bar, &size, &flags);
 	if (addrp == NULL)
 		return -EINVAL;
@@ -236,6 +240,9 @@ int of_pci_range_to_resource(struct of_pci_range *range,
 	res->parent = res->child = res->sibling = NULL;
 	res->name = np->full_name;
 
+	if (!IS_ENABLED(CONFIG_PCI))
+		return -ENOSYS;
+
 	if (res->flags & IORESOURCE_IO) {
 		unsigned long port;
 		err = pci_register_io_range(&np->fwnode, range->cpu_addr,
@@ -266,7 +273,6 @@ int of_pci_range_to_resource(struct of_pci_range *range,
 	return err;
 }
 EXPORT_SYMBOL(of_pci_range_to_resource);
-#endif /* CONFIG_PCI */
 
 /*
  * ISA bus specific translator
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index b72807faf037..45598dbec269 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -61,6 +61,11 @@ extern int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser,
 extern struct of_pci_range *of_pci_range_parser_one(
 					struct of_pci_range_parser *parser,
 					struct of_pci_range *range);
+extern int of_pci_address_to_resource(struct device_node *dev, int bar,
+				      struct resource *r);
+extern int of_pci_range_to_resource(struct of_pci_range *range,
+				    struct device_node *np,
+				    struct resource *res);
 extern bool of_dma_is_coherent(struct device_node *np);
 #else /* CONFIG_OF_ADDRESS */
 static inline void __iomem *of_io_request_and_map(struct device_node *device,
@@ -100,6 +105,19 @@ static inline struct of_pci_range *of_pci_range_parser_one(
 	return NULL;
 }
 
+static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
+				             struct resource *r)
+{
+	return -ENOSYS;
+}
+
+static inline int of_pci_range_to_resource(struct of_pci_range *range,
+					   struct device_node *np,
+					   struct resource *res)
+{
+	return -ENOSYS;
+}
+
 static inline bool of_dma_is_coherent(struct device_node *np)
 {
 	return false;
@@ -124,27 +142,6 @@ static inline void __iomem *of_iomap(struct device_node *device, int index)
 #endif
 #define of_range_parser_init of_pci_range_parser_init
 
-#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI)
-extern int of_pci_address_to_resource(struct device_node *dev, int bar,
-				      struct resource *r);
-extern int of_pci_range_to_resource(struct of_pci_range *range,
-				    struct device_node *np,
-				    struct resource *res);
-#else /* CONFIG_OF_ADDRESS && CONFIG_PCI */
-static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
-				             struct resource *r)
-{
-	return -ENOSYS;
-}
-
-static inline int of_pci_range_to_resource(struct of_pci_range *range,
-					   struct device_node *np,
-					   struct resource *res)
-{
-	return -ENOSYS;
-}
-#endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */
-
 static inline const __be32 *of_get_address(struct device_node *dev, int index,
 					   u64 *size, unsigned int *flags)
 {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] of: Merge of_address_to_resource() and of_pci_address_to_resource() implementations
  2021-05-27 19:45 [PATCH 0/4] DT address cleanups and refactoring Rob Herring
                   ` (2 preceding siblings ...)
  2021-05-27 19:45 ` [PATCH 3/4] of: address: Use IS_ENABLED() for !CONFIG_PCI Rob Herring
@ 2021-05-27 19:45 ` Rob Herring
  3 siblings, 0 replies; 6+ messages in thread
From: Rob Herring @ 2021-05-27 19:45 UTC (permalink / raw)
  To: devicetree, Frank Rowand
  Cc: linux-kernel, Bjorn Helgaas, linux-pci, Randy Dunlap

of_address_to_resource() and of_pci_address_to_resource() are almost the
same except the former takes an index and the latter takes a BAR number.
Now that __of_get_address() can take either one, refactor the functions
to use a common implementation.

Cc: Frank Rowand <frowand.list@gmail.com>
Signed-off-by: Rob Herring <robh@kernel.org>
---
 drivers/of/address.c | 44 ++++++++++++++++++--------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)

diff --git a/drivers/of/address.c b/drivers/of/address.c
index e643f999743a..3b2acca7e363 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -23,9 +23,8 @@
 #define OF_CHECK_COUNTS(na, ns)	(OF_CHECK_ADDR_COUNT(na) && (ns) > 0)
 
 static struct of_bus *of_match_bus(struct device_node *np);
-static int __of_address_to_resource(struct device_node *dev,
-		const __be32 *addrp, u64 size, unsigned int flags,
-		const char *name, struct resource *r);
+static int __of_address_to_resource(struct device_node *dev, int index,
+		int bar_no, struct resource *r);
 static bool of_mmio_is_nonposted(struct device_node *np);
 
 /* Debug utility */
@@ -203,17 +202,11 @@ static int of_bus_pci_translate(__be32 *addr, u64 offset, int na)
 int of_pci_address_to_resource(struct device_node *dev, int bar,
 			       struct resource *r)
 {
-	const __be32	*addrp;
-	u64		size;
-	unsigned int	flags;
 
 	if (!IS_ENABLED(CONFIG_PCI))
 		return -ENOSYS;
 
-	addrp = of_get_pci_address(dev, bar, &size, &flags);
-	if (addrp == NULL)
-		return -EINVAL;
-	return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
+	return __of_address_to_resource(dev, -1, bar, r);
 }
 EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
 
@@ -804,11 +797,22 @@ static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr,
 	return port;
 }
 
-static int __of_address_to_resource(struct device_node *dev,
-		const __be32 *addrp, u64 size, unsigned int flags,
-		const char *name, struct resource *r)
+static int __of_address_to_resource(struct device_node *dev, int index, int bar_no,
+		struct resource *r)
 {
 	u64 taddr;
+	const __be32	*addrp;
+	u64		size;
+	unsigned int	flags;
+	const char	*name = NULL;
+
+	addrp = __of_get_address(dev, index, bar_no, &size, &flags);
+	if (addrp == NULL)
+		return -EINVAL;
+
+	/* Get optional "reg-names" property to add a name to a resource */
+	if (index >= 0)
+		of_property_read_string_index(dev, "reg-names",	index, &name);
 
 	if (flags & IORESOURCE_MEM)
 		taddr = of_translate_address(dev, addrp);
@@ -846,19 +850,7 @@ static int __of_address_to_resource(struct device_node *dev,
 int of_address_to_resource(struct device_node *dev, int index,
 			   struct resource *r)
 {
-	const __be32	*addrp;
-	u64		size;
-	unsigned int	flags;
-	const char	*name = NULL;
-
-	addrp = of_get_address(dev, index, &size, &flags);
-	if (addrp == NULL)
-		return -EINVAL;
-
-	/* Get optional "reg-names" property to add a name to a resource */
-	of_property_read_string_index(dev, "reg-names",	index, &name);
-
-	return __of_address_to_resource(dev, addrp, size, flags, name, r);
+	return __of_address_to_resource(dev, index, -1, r);
 }
 EXPORT_SYMBOL_GPL(of_address_to_resource);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/4] PCI: Add empty stub for pci_register_io_range()
  2021-05-27 19:45 ` [PATCH 1/4] PCI: Add empty stub for pci_register_io_range() Rob Herring
@ 2021-05-27 21:55   ` Bjorn Helgaas
  0 siblings, 0 replies; 6+ messages in thread
From: Bjorn Helgaas @ 2021-05-27 21:55 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree, Frank Rowand, linux-kernel, Bjorn Helgaas, linux-pci,
	Randy Dunlap

On Thu, May 27, 2021 at 02:45:44PM -0500, Rob Herring wrote:
> Add an empty stub for pci_register_io_range() when !CONFIG_PCI. It's needed
> to convert of_pci_range_to_resource() to use IS_ENABLED(CONFIG_PCI).
> 
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: linux-pci@vger.kernel.org
> Signed-off-by: Rob Herring <robh@kernel.org>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

I assume you'll merge these through your tree.

> ---
>  include/linux/pci.h | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index c20211e59a57..29da7598f8d0 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -1772,6 +1772,10 @@ static inline int pci_request_regions(struct pci_dev *dev, const char *res_name)
>  { return -EIO; }
>  static inline void pci_release_regions(struct pci_dev *dev) { }
>  
> +static inline int pci_register_io_range(struct fwnode_handle *fwnode,
> +					phys_addr_t addr, resource_size_t size)
> +{ return -EINVAL; }
> +
>  static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
>  
>  static inline struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
> -- 
> 2.27.0
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-05-27 21:55 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-27 19:45 [PATCH 0/4] DT address cleanups and refactoring Rob Herring
2021-05-27 19:45 ` [PATCH 1/4] PCI: Add empty stub for pci_register_io_range() Rob Herring
2021-05-27 21:55   ` Bjorn Helgaas
2021-05-27 19:45 ` [PATCH 2/4] of: Merge of_get_address() and of_get_pci_address() implementations Rob Herring
2021-05-27 19:45 ` [PATCH 3/4] of: address: Use IS_ENABLED() for !CONFIG_PCI Rob Herring
2021-05-27 19:45 ` [PATCH 4/4] of: Merge of_address_to_resource() and of_pci_address_to_resource() implementations Rob Herring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).