diff --git a/drivers/Makefile b/drivers/Makefile
index bdf1c66141c9..a6d3296f0c23 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -159,7 +159,7 @@ obj-$(CONFIG_SOUNDWIRE)		+= soundwire/
 
 # Virtualization drivers
 obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
-obj-$(subst m,y,$(CONFIG_HYPERV))	+= hv/
+obj-$(call always_built_in,$(CONFIG_HYPERV))	+= hv/
 
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
 obj-$(CONFIG_EXTCON)		+= extcon/
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index d76df5c8c2a9..06777af02a78 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -13,4 +13,4 @@ hv_vmbus-$(CONFIG_HYPERV_TESTING)	+= hv_debugfs.o
 hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
 
 # Code that must be built-in
-obj-$(subst m,y,$(CONFIG_HYPERV)) += hv_common.o
+obj-$(call always_built_in,$(CONFIG_HYPERV)) += hv_common.o
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 3ea0126a9a72..1f4f9ce02490 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -4,4 +4,4 @@
 #
 
 obj-$(CONFIG_MMC)		+= core/
-obj-$(subst m,y,$(CONFIG_MMC))	+= host/
+obj-$(call always_built_in,$(CONFIG_MMC))	+= host/
diff --git a/drivers/net/wireless/silabs/wfx/Makefile b/drivers/net/wireless/silabs/wfx/Makefile
index c8b356f71c99..9fe47f5a050a 100644
--- a/drivers/net/wireless/silabs/wfx/Makefile
+++ b/drivers/net/wireless/silabs/wfx/Makefile
@@ -20,6 +20,6 @@ wfx-y := \
 	debug.o
 wfx-$(CONFIG_SPI) += bus_spi.o
 # When CONFIG_MMC == m, append to 'wfx-y' (and not to 'wfx-m')
-wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o
+wfx-$(call always_built_in,$(CONFIG_MMC)) += bus_sdio.o
 
 obj-$(CONFIG_WFX) += wfx.o
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index ce32270082f5..d21086e99528 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -34,7 +34,7 @@ obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
 
 obj-$(CONFIG_PCI) += sclp_pci.o
 
-obj-$(subst m,y,$(CONFIG_ZCRYPT)) += sclp_ap.o
+obj-$(call always_built_in,$(CONFIG_ZCRYPT)) += sclp_ap.o
 
 obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
 obj-$(CONFIG_VMCP) += vmcp.o
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index 22d2db690cd3..e3594486d8f3 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -4,7 +4,7 @@
 #
 
 ap-objs := ap_bus.o ap_card.o ap_queue.o
-obj-$(subst m,y,$(CONFIG_ZCRYPT)) += ap.o
+obj-$(call always_built_in,$(CONFIG_ZCRYPT)) += ap.o
 # zcrypt_api.o and zcrypt_msgtype*.o depend on ap.o
 zcrypt-objs := zcrypt_api.o zcrypt_card.o zcrypt_queue.o
 zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o
diff --git a/net/8021q/Makefile b/net/8021q/Makefile
index e05d4d7aab35..50b48af58322 100644
--- a/net/8021q/Makefile
+++ b/net/8021q/Makefile
@@ -2,7 +2,7 @@
 #
 # Makefile for the Linux VLAN layer.
 #
-obj-$(subst m,y,$(CONFIG_VLAN_8021Q))	+= vlan_core.o
+obj-$(call always_built_in,$(CONFIG_VLAN_8021Q))	+= vlan_core.o
 obj-$(CONFIG_VLAN_8021Q)		+= 8021q.o
 
 8021q-y					:= vlan.o vlan_dev.o vlan_netlink.o
diff --git a/net/Makefile b/net/Makefile
index 6a62e5b27378..94165eb38b65 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -42,7 +42,7 @@ obj-$(CONFIG_PHONET)		+= phonet/
 ifneq ($(CONFIG_VLAN_8021Q),)
 obj-y				+= 8021q/
 endif
-obj-$(CONFIG_IP_DCCP)		+= dccp/
+obj-$(call always_built_in,$(CONFIG_IP_DCCP)) += dccp/
 obj-$(CONFIG_IP_SCTP)		+= sctp/
 obj-$(CONFIG_RDS)		+= rds/
 obj-$(CONFIG_WIRELESS)		+= wireless/
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 24bd1c0a9a5a..4f3109e170c8 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -12,10 +12,10 @@ bridge-y	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
 
 bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
 
-bridge-$(subst m,y,$(CONFIG_BRIDGE_NETFILTER)) += br_nf_core.o
+bridge-$(call always_built_in,$(CONFIG_BRIDGE_NETFILTER)) += br_nf_core.o
 
 br_netfilter-y := br_netfilter_hooks.o
-br_netfilter-$(subst m,y,$(CONFIG_IPV6)) += br_netfilter_ipv6.o
+br_netfilter-$(call always_built_in,$(CONFIG_IPV6)) += br_netfilter_ipv6.o
 obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
 
 bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o br_multicast_eht.o
diff --git a/net/dccp/Makefile b/net/dccp/Makefile
index 5b4ff37bc806..607f6d3dd795 100644
--- a/net/dccp/Makefile
+++ b/net/dccp/Makefile
@@ -17,8 +17,8 @@ dccp-$(CONFIG_IP_DCCP_TFRC_LIB) += ccids/lib/tfrc.o		\
 dccp_ipv4-y := ipv4.o
 
 # build dccp_ipv6 as module whenever either IPv6 or DCCP is a module
-obj-$(subst y,$(CONFIG_IP_DCCP),$(CONFIG_IPV6)) += dccp_ipv6.o
-dccp_ipv6-y := ipv6.o
+obj-$(call module_if_any_modular,$(CONFIG_IP_DCCP)$(CONFIG_IPV6)) += dccp_ipv6.o
+dccp_ipv6-$(call module_if_any_modular,$(CONFIG_IP_DCCP)$(CONFIG_IPV6)) := ipv6.o
 
 obj-$(CONFIG_INET_DCCP_DIAG) += dccp_diag.o
 
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 3036a45e8a1e..b05b0e4668f5 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -47,7 +47,7 @@ obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o ip6_icmp.o
 obj-$(CONFIG_INET) += output_core.o protocol.o \
 			ip6_offload.o tcpv6_offload.o exthdrs_offload.o
 
-obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o
+obj-$(call always_built_in,$(CONFIG_IPV6)) += inet6_hashtables.o
 
 ifneq ($(CONFIG_IPV6),)
 obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o
diff --git a/net/l2tp/Makefile b/net/l2tp/Makefile
index cf8f27071d3f..ff94be92a36e 100644
--- a/net/l2tp/Makefile
+++ b/net/l2tp/Makefile
@@ -8,11 +8,11 @@ obj-$(CONFIG_L2TP) += l2tp_core.o
 CFLAGS_l2tp_core.o += -I$(src)
 
 # Build l2tp as modules if L2TP is M
-obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o
-obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o
-obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o
-obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_ETH)) += l2tp_eth.o
-obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_DEBUGFS)) += l2tp_debugfs.o
+obj-$(subst Y,$(CONFIG_L2TP),$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP))) += l2tp_ppp.o
+obj-$(subst Y,$(CONFIG_L2TP),$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP))) += l2tp_ip.o
+obj-$(subst Y,$(CONFIG_L2TP),$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3))) += l2tp_netlink.o
+obj-$(subst Y,$(CONFIG_L2TP),$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_ETH))) += l2tp_eth.o
+obj-$(subst Y,$(CONFIG_L2TP),$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_DEBUGFS))) += l2tp_debugfs.o
 ifneq ($(CONFIG_IPV6),)
-obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip6.o
+obj-$(subst Y,$(CONFIG_L2TP),$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP))) += l2tp_ip6.o
 endif
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 0f060d100880..a6f65c956b1b 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -6,7 +6,7 @@ nf_conntrack-y	:= nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_exp
 		   nf_conntrack_proto_icmp.o \
 		   nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack_seqadj.o
 
-nf_conntrack-$(subst m,y,$(CONFIG_IPV6)) += nf_conntrack_proto_icmpv6.o
+nf_conntrack-$(call always_built_in,$(CONFIG_IPV6)) += nf_conntrack_proto_icmpv6.o
 nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o
 nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
 nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile
index 5a46381a64e7..8052bd8e7af8 100644
--- a/net/netlabel/Makefile
+++ b/net/netlabel/Makefile
@@ -13,4 +13,4 @@ obj-y	+= netlabel_mgmt.o
 # protocol modules
 obj-y	+= netlabel_unlabeled.o
 obj-y	+= netlabel_cipso_v4.o
-obj-$(subst m,y,$(CONFIG_IPV6)) += netlabel_calipso.o
+obj-$(call always_built_in,$(CONFIG_IPV6)) += netlabel_calipso.o
diff --git a/net/sctp/Makefile b/net/sctp/Makefile
index e845e4588535..683a345f3626 100644
--- a/net/sctp/Makefile
+++ b/net/sctp/Makefile
@@ -21,4 +21,4 @@ sctp-$(CONFIG_SCTP_DBG_OBJCNT) += objcnt.o
 sctp-$(CONFIG_PROC_FS) += proc.o
 sctp-$(CONFIG_SYSCTL) += sysctl.o
 
-sctp-$(subst m,y,$(CONFIG_IPV6))	+= ipv6.o
+sctp-$(call always_built_in,$(CONFIG_IPV6))	+= ipv6.o
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 8042c067312e..701bbb499427 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -188,6 +188,21 @@ cmd_and_fixdep =                                                             \
 # and if so will execute $(rule_foo).
 if_changed_rule = $(if $(if-changed-cond),$(rule_$(1)),@:)
 
+# Usage. $(call always-built-in,CONFIG_VAR)
+# Expands to y if CONFIG_VAR is m, suitable for always-built-in pieces
+# of things that otherwise may be modular.
+always_built_in = $(subst M,Y,$(subst m,y,$(1)))
+
+# Usage, obj-$(call module_if_any_modular,$(CONCATENATED)$(CONFIG)$(VARS))
+# Expands to m if any of the concatenated config vars are m, otherwise
+# y if any of them are y, otherwise n
+module_if_any_modular = $(strip \
+    $(if $(findstring m,$(subst M,m,$(1))), \
+        $(if $(findstring M,$(1)),M,m), \
+        $(if $(findstring y,$(subst Y,y,$(1))), \
+            $(if $(findstring Y,$(1)),Y,y), \
+            $(if $(findstring N,$(1)),N,n))))
+
 ###
 # why - tell why a target got built
 #       enabled by make V=2
