diff --git a/include/image.mk b/include/image.mk index bc64e60766..ec04f0546f 100644 --- a/include/image.mk +++ b/include/image.mk @@ -563,6 +563,7 @@ define Device/Build/image @mkdir -p $$(shell dirname $$@) DEVICE_ID="$(DEVICE_NAME)" \ BIN_DIR="$(BIN_DIR)" \ + SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) \ IMAGE_NAME="$(IMAGE_NAME)" \ IMAGE_TYPE=$(word 1,$(subst ., ,$(2))) \ IMAGE_PREFIX="$(IMAGE_PREFIX)" \ diff --git a/include/kernel-build.mk b/include/kernel-build.mk index 9dfb19ccc7..a698deec3c 100644 --- a/include/kernel-build.mk +++ b/include/kernel-build.mk @@ -136,6 +136,7 @@ define BuildKernel $(LINUX_DIR)/.modules: export PKG_CONFIG_LIBDIR=$$(STAGING_DIR_HOST)/lib/pkgconfig $(LINUX_DIR)/.modules: $(STAMP_CONFIGURED) $(LINUX_DIR)/.config FORCE $(Kernel/CompileModules) + mkdir -p $(PKG_SYMVERS_DIR) touch $$@ $(LINUX_DIR)/.image: export STAGING_PREFIX=$$(STAGING_DIR_HOST) @@ -169,9 +170,7 @@ define BuildKernel ) \ YACC=$(STAGING_DIR_HOST)/bin/bison \ $$@ - $(LINUX_RECONF_DIFF) $(LINUX_DIR)/.config | \ - grep -vE '(CONFIG_CC_(HAS_ASM_GOTO|IS_GCC|IS_CLANG)|GCC_VERSION)=' \ - > $(LINUX_RECONFIG_TARGET) + $(call LINUX_RECONF_DIFF,$(LINUX_DIR)/.config) > $(LINUX_RECONFIG_TARGET) install: $(LINUX_DIR)/.image +$(MAKE) -C image compile install TARGET_BUILD= diff --git a/include/kernel.mk b/include/kernel.mk index 40aa1a5163..1ae9c6be29 100644 --- a/include/kernel.mk +++ b/include/kernel.mk @@ -118,7 +118,8 @@ KERNEL_MAKE_FLAGS = \ CONFIG_SHELL="$(BASH)" \ $(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='') \ $(if $(PKG_BUILD_ID),LDFLAGS_MODULE=--build-id=0x$(PKG_BUILD_ID)) \ - cmd_syscalls= + cmd_syscalls= \ + $(if $(__package_mk),KBUILD_EXTRA_SYMBOLS="$(wildcard $(PKG_SYMVERS_DIR)/*.symvers)") ifeq ($(call qstrip,$(CONFIG_EXTERNAL_KERNEL_TREE))$(call qstrip,$(CONFIG_KERNEL_GIT_CLONE_URI)),) KERNEL_MAKE_FLAGS += \ @@ -140,14 +141,6 @@ PKG_EXTMOD_SUBDIRS ?= . PKG_SYMVERS_DIR = $(KERNEL_BUILD_DIR)/symvers -define populate_module_symvers - @mkdir -p $(PKG_SYMVERS_DIR) - cat /dev/null > $(PKG_SYMVERS_DIR)/$(PKG_NAME).symvers; \ - for subdir in $(PKG_EXTMOD_SUBDIRS); do \ - cat $(PKG_SYMVERS_DIR)/*.symvers 2>/dev/null > $(PKG_BUILD_DIR)/$$$$subdir/Module.symvers; \ - done -endef - define collect_module_symvers for subdir in $(PKG_EXTMOD_SUBDIRS); do \ realdir=$$$$(readlink -f $(PKG_BUILD_DIR)); \ @@ -161,7 +154,6 @@ endef define KernelPackage/hooks ifneq ($(PKG_NAME),kernel) - Hooks/Compile/Pre += populate_module_symvers Hooks/Compile/Post += collect_module_symvers endif define KernelPackage/hooks diff --git a/include/netfilter.mk b/include/netfilter.mk index c1c3b4a9fd..75724968b0 100644 --- a/include/netfilter.mk +++ b/include/netfilter.mk @@ -362,6 +362,7 @@ $(eval $(if $(NF_KMOD),$(call nf_add,NFT_FIB,CONFIG_NFT_FIB_INET, $(P_XT)nft_fib $(eval $(if $(NF_KMOD),$(call nf_add,NFT_FIB,CONFIG_NFT_FIB_IPV4, $(P_V4)nft_fib_ipv4),)) $(eval $(if $(NF_KMOD),$(call nf_add,NFT_FIB,CONFIG_NFT_FIB_IPV6, $(P_V6)nft_fib_ipv6),)) +$(eval $(if $(NF_KMOD),$(call nf_add,NFT_QUEUE,CONFIG_NFT_QUEUE, $(P_XT)nft_queue),)) # userland only IPT_BUILTIN += $(NF_IPT-y) $(NF_IPT-m) diff --git a/include/target.mk b/include/target.mk index b5e17deb2f..378833a1fc 100644 --- a/include/target.mk +++ b/include/target.mk @@ -200,11 +200,11 @@ ifeq ($(CONFIG_TARGET),env) LINUX_RECONFIG_TARGET = $(TOPDIR)/env/kernel-config endif -__linux_confcmd = $(SCRIPT_DIR)/kconfig.pl $(2) $(patsubst %,+,$(wordlist 2,9999,$(1))) $(1) +__linux_confcmd = $(2) $(patsubst %,+,$(wordlist 2,9999,$(1))) $(1) -LINUX_CONF_CMD = $(call __linux_confcmd,$(LINUX_KCONFIG_LIST),) -LINUX_RECONF_CMD = $(call __linux_confcmd,$(LINUX_RECONFIG_LIST),) -LINUX_RECONF_DIFF = $(call __linux_confcmd,$(filter-out $(LINUX_RECONFIG_TARGET),$(LINUX_RECONFIG_LIST)),'>') +LINUX_CONF_CMD = $(SCRIPT_DIR)/kconfig.pl $(call __linux_confcmd,$(LINUX_KCONFIG_LIST)) +LINUX_RECONF_CMD = $(SCRIPT_DIR)/kconfig.pl $(call __linux_confcmd,$(LINUX_RECONFIG_LIST)) +LINUX_RECONF_DIFF = $(SCRIPT_DIR)/kconfig.pl - '>' $(call __linux_confcmd,$(filter-out $(LINUX_RECONFIG_TARGET),$(LINUX_RECONFIG_LIST))) $(1) $(GENERIC_PLATFORM_DIR)/config-filter ifeq ($(DUMP),1) BuildTarget=$(BuildTargets/DumpCurrent) diff --git a/package/boot/uboot-envtools/Makefile b/package/boot/uboot-envtools/Makefile index 473704053c..73cd083d0a 100644 --- a/package/boot/uboot-envtools/Makefile +++ b/package/boot/uboot-envtools/Makefile @@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=uboot-envtools PKG_DISTNAME:=u-boot PKG_VERSION:=2020.04 -PKG_RELEASE:=4 +PKG_RELEASE:=5 PKG_SOURCE:=$(PKG_DISTNAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:= \ diff --git a/package/boot/uboot-envtools/files/ramips b/package/boot/uboot-envtools/files/ramips index 14c12bbf09..3ef345d065 100644 --- a/package/boot/uboot-envtools/files/ramips +++ b/package/boot/uboot-envtools/files/ramips @@ -29,6 +29,8 @@ buffalo,wsr-1166dhp|\ buffalo,wsr-600dhp|\ mediatek,linkit-smart-7688|\ samknows,whitebox-v8|\ +xiaomi,mir3g-v2|\ +xiaomi,mi-router-4a-gigabit|\ xiaomi,miwifi-nano|\ zbtlink,zbt-wg2626) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x1000" "0x10000" diff --git a/package/boot/uboot-sunxi/Makefile b/package/boot/uboot-sunxi/Makefile index 58a5923eab..859a06019b 100644 --- a/package/boot/uboot-sunxi/Makefile +++ b/package/boot/uboot-sunxi/Makefile @@ -226,6 +226,14 @@ define U-Boot/pangolin UENV:=pangolin endef +define U-Boot/libretech_all_h3_cc_h5 + BUILD_SUBTARGET:=cortexa53 + NAME:=Libre Computer ALL-H3-CC H5 + BUILD_DEVICES:=libretech_all-h3-cc-h5 + DEPENDS:=+PACKAGE_u-boot-libretech_all_h3_cc_h5:arm-trusted-firmware-sunxi + UENV:=a64 +endef + define U-Boot/nanopi_neo_plus2 BUILD_SUBTARGET:=cortexa53 NAME:=NanoPi NEO Plus2 (H5) @@ -328,7 +336,8 @@ UBOOT_TARGETS := \ pangolin \ pine64_plus \ sopine_baseboard \ - orangepi_zero_plus + orangepi_zero_plus \ + libretech_all_h3_cc_h5 UBOOT_CONFIGURE_VARS += USE_PRIVATE_LIBGCC=yes diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index 1d051cf84b..2c763dd380 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -1225,3 +1225,41 @@ define KernelPackage/igc/description endef $(eval $(call KernelPackage,igc)) + +define KernelPackage/sfc + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Solarflare SFC9000/SFC9100/EF100-family support + DEPENDS:=@PCI_SUPPORT +kmod-mdio +kmod-lib-crc32c +kmod-ptp +kmod-hwmon-core + KCONFIG:= \ + CONFIG_SFC \ + CONFIG_SFC_MTD \ + CONFIG_SFC_MCDI_MON \ + CONFIG_SFC_MCDI_LOGGING \ + CONFIG_SFC_SRIOV + FILES:=$(LINUX_DIR)/drivers/net/ethernet/sfc/sfc.ko + AUTOLOAD:=$(call AutoProbe,sfc) +endef + +define KernelPackage/sfc/description + Solarflare SFC9000/SFC9100/EF100-family support + Solarflare EF100 support requires at least kernel version 5.9 +endef + +$(eval $(call KernelPackage,sfc)) + +define KernelPackage/sfc-falcon + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Solarflare SFC4000 support + DEPENDS:=@PCI_SUPPORT +kmod-mdio +kmod-lib-crc32c +kmod-i2c-algo-bit + KCONFIG:= \ + CONFIG_SFC_FALCON \ + CONFIG_SFC_FALCON_MTD + FILES:=$(LINUX_DIR)/drivers/net/ethernet/sfc/falcon/sfc-falcon.ko + AUTOLOAD:=$(call AutoProbe,sfc-falcon) +endef + +define KernelPackage/sfc-falcon/description + Solarflare SFC4000 support +endef + +$(eval $(call KernelPackage,sfc-falcon)) diff --git a/package/kernel/linux/modules/netfilter.mk b/package/kernel/linux/modules/netfilter.mk index f8c08cde24..0e3671437c 100644 --- a/package/kernel/linux/modules/netfilter.mk +++ b/package/kernel/linux/modules/netfilter.mk @@ -1252,3 +1252,15 @@ define KernelPackage/nft-fib endef $(eval $(call KernelPackage,nft-fib)) + + +define KernelPackage/nft-queue + SUBMENU:=$(NF_MENU) + TITLE:=Netfilter nf_tables queue support + DEPENDS:=+kmod-nft-core +kmod-nfnetlink-queue + FILES:=$(foreach mod,$(NFT_QUEUE-m),$(LINUX_DIR)/net/$(mod).ko) + AUTOLOAD:=$(call AutoProbe,$(notdir $(NFT_QUEUE-m))) + KCONFIG:=$(KCONFIG_NFT_QUEUE) +endef + +$(eval $(call KernelPackage,nft-queue)) diff --git a/package/kernel/linux/modules/netsupport.mk b/package/kernel/linux/modules/netsupport.mk index 75801114da..0c68b394d1 100644 --- a/package/kernel/linux/modules/netsupport.mk +++ b/package/kernel/linux/modules/netsupport.mk @@ -144,26 +144,6 @@ endef $(eval $(call KernelPackage,nsh)) -define KernelPackage/capi - SUBMENU:=$(NETWORK_SUPPORT_MENU) - TITLE:=CAPI (ISDN) Support - KCONFIG:= \ - CONFIG_ISDN_CAPI \ - CONFIG_ISDN_CAPI_CAPI20 \ - CONFIG_ISDN_CAPIFS \ - CONFIG_ISDN_CAPI_CAPIFS - FILES:= \ - $(LINUX_DIR)/drivers/isdn/capi/kernelcapi.ko \ - $(LINUX_DIR)/drivers/isdn/capi/capi.ko - AUTOLOAD:=$(call AutoLoad,30,kernelcapi capi) -endef - -define KernelPackage/capi/description - Kernel module for basic CAPI (ISDN) support -endef - -$(eval $(call KernelPackage,capi)) - define KernelPackage/misdn SUBMENU:=$(NETWORK_SUPPORT_MENU) TITLE:=mISDN (ISDN) Support diff --git a/package/kernel/mac80211/patches/subsys/073-backports-backport-tasklet_setup-from_tasklet.patch b/package/kernel/mac80211/patches/subsys/073-backports-backport-tasklet_setup-from_tasklet.patch new file mode 100644 index 0000000000..486d10ea58 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/073-backports-backport-tasklet_setup-from_tasklet.patch @@ -0,0 +1,35 @@ +From: Felix Fietkau +Date: Wed, 11 Nov 2020 13:34:26 +0100 +Subject: [PATCH] backports: backport tasklet_setup, from_tasklet + +Backport the new tasklet API + +Signed-off-by: Felix Fietkau +--- + +--- a/backport-include/linux/interrupt.h ++++ b/backport-include/linux/interrupt.h +@@ -31,6 +31,23 @@ static inline void backport_hrtimer_star + hrtimer_start(timer, _time, mode); + } + #define hrtimer_start LINUX_BACKPORT(hrtimer_start) ++ ++#endif ++ ++#if LINUX_VERSION_IS_LESS(5,9,0) ++ ++static inline void ++tasklet_setup(struct tasklet_struct *t, ++ void (*callback)(struct tasklet_struct *)) ++{ ++ void (*cb)(unsigned long data) = (void *)callback; ++ ++ tasklet_init(t, cb, (unsigned long)t); ++} ++ ++#define from_tasklet(var, callback_tasklet, tasklet_fieldname) \ ++ container_of(callback_tasklet, typeof(*var), tasklet_fieldname) ++ + #endif + + #endif /* _BP_LINUX_INTERRUPT_H */ diff --git a/package/kernel/mac80211/patches/subsys/334-mac80211-minstrel-remove-deferred-sampling-code.patch b/package/kernel/mac80211/patches/subsys/334-mac80211-minstrel-remove-deferred-sampling-code.patch new file mode 100644 index 0000000000..e3a877fb94 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/334-mac80211-minstrel-remove-deferred-sampling-code.patch @@ -0,0 +1,96 @@ +From: Felix Fietkau +Date: Wed, 11 Nov 2020 19:17:44 +0100 +Subject: [PATCH] mac80211: minstrel: remove deferred sampling code + +Deferring sampling attempts to the second stage has some bad interactions +with drivers that process the rate table in hardware and use the probe flag +to indicate probing packets (e.g. most mt76 drivers). On affected drivers +it can lead to probing not working at all. + +If the link conditions turn worse, it might not be such a good idea to +do a lot of sampling for lower rates in this case. + +Fix this by simply skipping the sample attempt instead of deferring it, +but keep the checks that would allow it to be sampled if it was skipped +too often, but only if it has less than 95% success probability. + +Also ensure that IEEE80211_TX_CTL_RATE_CTRL_PROBE is set for all probing +packets. + +Cc: stable@vger.kernel.org +Fixes: cccf129f820e ("mac80211: add the 'minstrel' rate control algorithm") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel.c ++++ b/net/mac80211/rc80211_minstrel.c +@@ -287,12 +287,6 @@ minstrel_tx_status(void *priv, struct ie + mi->r[ndx].stats.success += success; + } + +- if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) +- mi->sample_packets++; +- +- if (mi->sample_deferred > 0) +- mi->sample_deferred--; +- + if (time_after(jiffies, mi->last_stats_update + + mp->update_interval / (mp->new_avg ? 2 : 1))) + minstrel_update_stats(mp, mi); +@@ -367,7 +361,7 @@ minstrel_get_rate(void *priv, struct iee + return; + + delta = (mi->total_packets * sampling_ratio / 100) - +- (mi->sample_packets + mi->sample_deferred / 2); ++ mi->sample_packets; + + /* delta < 0: no sampling required */ + prev_sample = mi->prev_sample; +@@ -376,7 +370,6 @@ minstrel_get_rate(void *priv, struct iee + return; + + if (mi->total_packets >= 10000) { +- mi->sample_deferred = 0; + mi->sample_packets = 0; + mi->total_packets = 0; + } else if (delta > mi->n_rates * 2) { +@@ -401,19 +394,8 @@ minstrel_get_rate(void *priv, struct iee + * rate sampling method should be used. + * Respect such rates that are not sampled for 20 interations. + */ +- if (mrr_capable && +- msr->perfect_tx_time > mr->perfect_tx_time && +- msr->stats.sample_skipped < 20) { +- /* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark +- * packets that have the sampling rate deferred to the +- * second MRR stage. Increase the sample counter only +- * if the deferred sample rate was actually used. +- * Use the sample_deferred counter to make sure that +- * the sampling is not done in large bursts */ +- info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; +- rate++; +- mi->sample_deferred++; +- } else { ++ if (msr->perfect_tx_time < mr->perfect_tx_time || ++ msr->stats.sample_skipped >= 20) { + if (!msr->sample_limit) + return; + +@@ -433,6 +415,7 @@ minstrel_get_rate(void *priv, struct iee + + rate->idx = mi->r[ndx].rix; + rate->count = minstrel_get_retry_count(&mi->r[ndx], info); ++ info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; + } + + +--- a/net/mac80211/rc80211_minstrel.h ++++ b/net/mac80211/rc80211_minstrel.h +@@ -126,7 +126,6 @@ struct minstrel_sta_info { + u8 max_prob_rate; + unsigned int total_packets; + unsigned int sample_packets; +- int sample_deferred; + + unsigned int sample_row; + unsigned int sample_column; diff --git a/package/kernel/mac80211/patches/subsys/335-mac80211-minstrel-fix-tx-status-processing-corner-ca.patch b/package/kernel/mac80211/patches/subsys/335-mac80211-minstrel-fix-tx-status-processing-corner-ca.patch new file mode 100644 index 0000000000..b1c339a1e6 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/335-mac80211-minstrel-fix-tx-status-processing-corner-ca.patch @@ -0,0 +1,26 @@ +From: Felix Fietkau +Date: Wed, 11 Nov 2020 19:25:39 +0100 +Subject: [PATCH] mac80211: minstrel: fix tx status processing corner case + +Some drivers fill the status rate list without setting the rate index after +the final rate to -1. minstrel_ht already deals with this, but minstrel +doesn't, which causes it to get stuck at the lowest rate on these drivers. + +Fix this by checking the count as well. + +Cc: stable@vger.kernel.org +Fixes: cccf129f820e ("mac80211: add the 'minstrel' rate control algorithm") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel.c ++++ b/net/mac80211/rc80211_minstrel.c +@@ -274,7 +274,7 @@ minstrel_tx_status(void *priv, struct ie + success = !!(info->flags & IEEE80211_TX_STAT_ACK); + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { +- if (ar[i].idx < 0) ++ if (ar[i].idx < 0 || !ar[i].count) + break; + + ndx = rix_to_ndx(mi, ar[i].idx); diff --git a/package/libs/libnetfilter-log/Makefile b/package/libs/libnetfilter-log/Makefile index 642a462e15..7dc45a01ca 100644 --- a/package/libs/libnetfilter-log/Makefile +++ b/package/libs/libnetfilter-log/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=libnetfilter_log PKG_VERSION:=1.0.1 -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:= \ diff --git a/package/libs/libnetfilter-log/patches/0007-include-Sync-with-current-kernel-headers.patch b/package/libs/libnetfilter-log/patches/0007-include-Sync-with-current-kernel-headers.patch new file mode 100644 index 0000000000..f7f733402a --- /dev/null +++ b/package/libs/libnetfilter-log/patches/0007-include-Sync-with-current-kernel-headers.patch @@ -0,0 +1,108 @@ +From 614d8b6cfb969c6102ef320de22b1eb199efce2a Mon Sep 17 00:00:00 2001 +From: Felix Janda +Date: Sat, 16 May 2015 13:37:53 +0200 +Subject: include: Sync with current kernel headers + +Signed-off-by: Felix Janda +Signed-off-by: Pablo Neira Ayuso +--- + include/libnetfilter_log/linux_nfnetlink_log.h | 51 +++++++++++++------------- + 1 file changed, 25 insertions(+), 26 deletions(-) + +diff --git a/include/libnetfilter_log/linux_nfnetlink_log.h b/include/libnetfilter_log/linux_nfnetlink_log.h +index 4c802c8..9f38277 100644 +--- a/include/libnetfilter_log/linux_nfnetlink_log.h ++++ b/include/libnetfilter_log/linux_nfnetlink_log.h +@@ -20,33 +20,31 @@ enum nfulnl_msg_types { + }; + + struct nfulnl_msg_packet_hdr { +- u_int16_t hw_protocol; /* hw protocol (network order) */ +- u_int8_t hook; /* netfilter hook */ +- u_int8_t _pad; +-} __attribute__ ((packed)); ++ __be16 hw_protocol; /* hw protocol (network order) */ ++ __u8 hook; /* netfilter hook */ ++ __u8 _pad; ++}; + + struct nfulnl_msg_packet_hw { +- u_int16_t hw_addrlen; +- u_int16_t _pad; +- u_int8_t hw_addr[8]; +-} __attribute__ ((packed)); ++ __be16 hw_addrlen; ++ __u16 _pad; ++ __u8 hw_addr[8]; ++}; + + struct nfulnl_msg_packet_timestamp { +- aligned_u64 sec; +- aligned_u64 usec; +-} __attribute__ ((packed)); +- +-#define NFULNL_PREFIXLEN 30 /* just like old log target */ ++ __aligned_be64 sec; ++ __aligned_be64 usec; ++}; + + enum nfulnl_attr_type { + NFULA_UNSPEC, + NFULA_PACKET_HDR, +- NFULA_MARK, /* u_int32_t nfmark */ ++ NFULA_MARK, /* __u32 nfmark */ + NFULA_TIMESTAMP, /* nfulnl_msg_packet_timestamp */ +- NFULA_IFINDEX_INDEV, /* u_int32_t ifindex */ +- NFULA_IFINDEX_OUTDEV, /* u_int32_t ifindex */ +- NFULA_IFINDEX_PHYSINDEV, /* u_int32_t ifindex */ +- NFULA_IFINDEX_PHYSOUTDEV, /* u_int32_t ifindex */ ++ NFULA_IFINDEX_INDEV, /* __u32 ifindex */ ++ NFULA_IFINDEX_OUTDEV, /* __u32 ifindex */ ++ NFULA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ ++ NFULA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFULA_HWADDR, /* nfulnl_msg_packet_hw */ + NFULA_PAYLOAD, /* opaque data payload */ + NFULA_PREFIX, /* string prefix */ +@@ -71,23 +69,23 @@ enum nfulnl_msg_config_cmds { + }; + + struct nfulnl_msg_config_cmd { +- u_int8_t command; /* nfulnl_msg_config_cmds */ ++ __u8 command; /* nfulnl_msg_config_cmds */ + } __attribute__ ((packed)); + + struct nfulnl_msg_config_mode { +- u_int32_t copy_range; +- u_int8_t copy_mode; +- u_int8_t _pad; ++ __be32 copy_range; ++ __u8 copy_mode; ++ __u8 _pad; + } __attribute__ ((packed)); + + enum nfulnl_attr_config { + NFULA_CFG_UNSPEC, + NFULA_CFG_CMD, /* nfulnl_msg_config_cmd */ + NFULA_CFG_MODE, /* nfulnl_msg_config_mode */ +- NFULA_CFG_NLBUFSIZ, /* u_int32_t buffer size */ +- NFULA_CFG_TIMEOUT, /* u_int32_t in 1/100 s */ +- NFULA_CFG_QTHRESH, /* u_int32_t */ +- NFULA_CFG_FLAGS, /* u_int16_t */ ++ NFULA_CFG_NLBUFSIZ, /* __u32 buffer size */ ++ NFULA_CFG_TIMEOUT, /* __u32 in 1/100 s */ ++ NFULA_CFG_QTHRESH, /* __u32 */ ++ NFULA_CFG_FLAGS, /* __u16 */ + __NFULA_CFG_MAX + }; + #define NFULA_CFG_MAX (__NFULA_CFG_MAX -1) +@@ -95,6 +93,7 @@ enum nfulnl_attr_config { + #define NFULNL_COPY_NONE 0x00 + #define NFULNL_COPY_META 0x01 + #define NFULNL_COPY_PACKET 0x02 ++/* 0xff is reserved, don't use it for new copy modes. */ + + #define NFULNL_CFG_F_SEQ 0x0001 + #define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 +-- +2.11.0 + diff --git a/package/libs/libnetfilter-log/patches/0008-include-Sync-with-current-kernel-headers.patch b/package/libs/libnetfilter-log/patches/0008-include-Sync-with-current-kernel-headers.patch new file mode 100644 index 0000000000..60973a276a --- /dev/null +++ b/package/libs/libnetfilter-log/patches/0008-include-Sync-with-current-kernel-headers.patch @@ -0,0 +1,52 @@ +From 721ea5ec049e12afdd7c182f2899ab6d92914e68 Mon Sep 17 00:00:00 2001 +From: Ken-ichirou MATSUZAWA +Date: Fri, 11 Sep 2015 12:12:11 +0900 +Subject: include: Sync with kernel headers + +Signed-off-by: Ken-ichirou MATSUZAWA +Signed-off-by: Pablo Neira Ayuso +--- + include/libnetfilter_log/linux_nfnetlink_log.h | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/include/libnetfilter_log/linux_nfnetlink_log.h b/include/libnetfilter_log/linux_nfnetlink_log.h +index 9f38277..081e7f9 100644 +--- a/include/libnetfilter_log/linux_nfnetlink_log.h ++++ b/include/libnetfilter_log/linux_nfnetlink_log.h +@@ -1,16 +1,12 @@ + #ifndef _NFNETLINK_LOG_H + #define _NFNETLINK_LOG_H + +-#ifndef aligned_u64 +-#define aligned_u64 unsigned long long __attribute__((aligned(8))) +-#endif +- + /* This file describes the netlink messages (i.e. 'protocol packets'), + * and not any kind of function definitions. It is shared between kernel and + * userspace. Don't put kernel specific stuff in here */ + + #include +-#include ++#include + + enum nfulnl_msg_types { + NFULNL_MSG_PACKET, /* packet from kernel to userspace */ +@@ -55,6 +51,8 @@ enum nfulnl_attr_type { + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ ++ NFULA_CT, /* nf_conntrack_netlink.h */ ++ NFULA_CT_INFO, /* enum ip_conntrack_info */ + + __NFULA_MAX + }; +@@ -97,5 +95,6 @@ enum nfulnl_attr_config { + + #define NFULNL_CFG_F_SEQ 0x0001 + #define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 ++#define NFULNL_CFG_F_CONNTRACK 0x0004 + + #endif /* _NFNETLINK_LOG_H */ +-- +2.11.0 + diff --git a/package/libs/libnl-tiny/Makefile b/package/libs/libnl-tiny/Makefile index 3c5f6d79f4..b2eafe259a 100644 --- a/package/libs/libnl-tiny/Makefile +++ b/package/libs/libnl-tiny/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/libnl-tiny.git -PKG_SOURCE_DATE:=2019-10-29 -PKG_SOURCE_VERSION:=0219008cc8767655d7e747497e8e1133a3e8f840 -PKG_MIRROR_HASH:=b84fab21374c6ddbf992acc4ec1c9c6896b32af97603027ca5c866d69d95780f +PKG_SOURCE_DATE:=2020-08-05 +PKG_SOURCE_VERSION:=c291088f631d1694f7ba0444b59677b194348da8 +PKG_MIRROR_HASH:=99bcce12701bb34dadb39689d95c2c5cf1e27719d0ecfd645d3957a8947025ac CMAKE_INSTALL:=1 PKG_LICENSE:=LGPL-2.1 @@ -27,6 +27,7 @@ define Package/libnl-tiny SECTION:=libs CATEGORY:=Libraries TITLE:=netlink socket library + ABI_VERSION:=1 endef define Package/libnl-tiny/description diff --git a/package/network/config/netifd/Makefile b/package/network/config/netifd/Makefile index 2c26517f44..266d0a022d 100644 --- a/package/network/config/netifd/Makefile +++ b/package/network/config/netifd/Makefile @@ -5,9 +5,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git -PKG_SOURCE_DATE:=2020-10-22 -PKG_SOURCE_VERSION:=24ce1eab4910869576406bafd0489daf0d3e6e28 -PKG_MIRROR_HASH:=aa73b4e470e81165baba0262144f410bb05a3ff141cf72ad451914f033868ea1 +PKG_SOURCE_DATE:=2020-11-13 +PKG_SOURCE_VERSION:=4a41135750d97e06d1f6d808a9d24bb4b472aca4 +PKG_MIRROR_HASH:=2f58910f79c73209c20969ffa59ac5ccd4606f736993b207dccef6d42dab7880 PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=GPL-2.0 diff --git a/package/network/services/wireguard/Makefile b/package/network/services/wireguard/Makefile index 7df219f3f7..91bdb0d7a7 100644 --- a/package/network/services/wireguard/Makefile +++ b/package/network/services/wireguard/Makefile @@ -11,12 +11,12 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=wireguard -PKG_VERSION:=1.0.20200908 +PKG_VERSION:=1.0.20201112 PKG_RELEASE:=1 PKG_SOURCE:=wireguard-linux-compat-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=https://git.zx2c4.com/wireguard-linux-compat/snapshot/ -PKG_HASH:=ad33b2d2267a37e0f65c97e65e7d4d926d5aef7d530c251b63fbf919048eead9 +PKG_HASH:=89eae7f0c0bd6c8df3ba2e090984974ff68741a9f26aa0922890f8ca727897e1 PKG_LICENSE:=GPL-2.0 PKG_LICENSE_FILES:=COPYING diff --git a/package/network/utils/layerscape/restool/Makefile b/package/network/utils/layerscape/restool/Makefile index 6a2a8d9e81..935513d4ed 100644 --- a/package/network/utils/layerscape/restool/Makefile +++ b/package/network/utils/layerscape/restool/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=restool PKG_VERSION:=LSDK-20.04 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://source.codeaurora.org/external/qoriq/qoriq-components/restool @@ -22,7 +22,7 @@ define Package/restool SECTION:=net CATEGORY:=Network TITLE:=Layerscape DPAA2 dynamical management tool - DEPENDS:=@TARGET_layerscape + DEPENDS:=@TARGET_layerscape_armv8_64b endef MAKE_FLAGS += \ diff --git a/package/system/ca-certificates/Makefile b/package/system/ca-certificates/Makefile index 239b442ebd..ca3756b34d 100644 --- a/package/system/ca-certificates/Makefile +++ b/package/system/ca-certificates/Makefile @@ -14,7 +14,6 @@ PKG_MAINTAINER:= PKG_SOURCE:=$(PKG_NAME)_$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=http://ftp.debian.org/debian/pool/main/c/ca-certificates PKG_HASH:=43766d5a436519503dfd65ab83488ae33ab4d4ca3d0993797b58c92eb9ed4e63 -PKG_BUILD_DIR:=$(BUILD_DIR)/work PKG_INSTALL:=1 include $(INCLUDE_DIR)/package.mk @@ -35,6 +34,13 @@ define Package/ca-bundle PROVIDES:=ca-certs endef +define Build/Prepare + $(DECOMPRESS_CMD) $(HOST_TAR) -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + $(Build/Patch) +endef + +MAKE_PATH := work + define Build/Install mkdir -p \ $(PKG_INSTALL_DIR)/usr/sbin \ diff --git a/package/system/rpcd/Makefile b/package/system/rpcd/Makefile index 917ad6ff86..b8a974086f 100644 --- a/package/system/rpcd/Makefile +++ b/package/system/rpcd/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rpcd -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/rpcd.git diff --git a/package/system/rpcd/files/50-migrate-rpcd-ubus-sock.sh b/package/system/rpcd/files/50-migrate-rpcd-ubus-sock.sh index 1365aed39a..609b0f9835 100755 --- a/package/system/rpcd/files/50-migrate-rpcd-ubus-sock.sh +++ b/package/system/rpcd/files/50-migrate-rpcd-ubus-sock.sh @@ -1,6 +1,8 @@ #!/bin/sh -[ "$(uci get rpcd.@rpcd[0].socket)" = "/var/run/ubus.sock" ] && { - uci set rpcd.@rpcd[0].socket='/var/run/ubus/ubus.sock' - uci commit rpcd -} +[ "$(uci get rpcd.@rpcd[0].socket)" = "/var/run/ubus.sock" ] || exit 0 + +uci set rpcd.@rpcd[0].socket='/var/run/ubus/ubus.sock' +uci commit rpcd + +exit 0 diff --git a/package/utils/busybox/Makefile b/package/utils/busybox/Makefile index 25db4ff387..71bd888c71 100644 --- a/package/utils/busybox/Makefile +++ b/package/utils/busybox/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=busybox PKG_VERSION:=1.31.1 -PKG_RELEASE:=5 +PKG_RELEASE:=6 PKG_FLAGS:=essential PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 diff --git a/package/utils/busybox/files/cron b/package/utils/busybox/files/cron index ca04a0c170..4efdfa52ca 100755 --- a/package/utils/busybox/files/cron +++ b/package/utils/busybox/files/cron @@ -32,6 +32,7 @@ start_service() { for crontab in /etc/crontabs/*; do procd_set_param file "$crontab" done + procd_set_param respawn procd_close_instance } diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 24793e19d8..9da28baf35 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1,16 +1,23 @@ -#!/usr/bin/env perl +#!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0 +# # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) # (c) 2007,2008, Andy Whitcroft (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft # (c) 2013 Vasilis Tsiligiannis (adapt for OpenWrt tree) -# Licensed under the terms of the GNU GPL License version 2 +# (c) 2010-2018 Joe Perches use strict; use warnings; +use POSIX; +use File::Basename; +use Cwd 'abs_path'; +use Term::ANSIColor qw(:constants); +use Encode qw(decode encode); my $P = $0; -$P =~ s@.*/@@g; +my $D = dirname(abs_path($P)); my $V = '0.32-openwrt'; @@ -23,18 +30,42 @@ my $chk_patch = 1; my $tst_only; my $emacs = 0; my $terse = 0; +my $showfile = 0; my $file = 0; +my $git = 0; +my %git_commits = (); my $check = 0; +my $check_orig = 0; my $summary = 1; my $mailback = 0; my $summary_file = 0; my $show_types = 0; +my $list_types = 0; +my $fix = 0; +my $fix_inplace = 0; my $root; my %debug; +my %camelcase = (); +my %use_type = (); +my @use = (); my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; +my $max_line_length = 100; +my $ignore_perl_version = 0; +my $minimum_perl_version = 5.10.0; +my $min_conf_desc_length = 4; +my $spelling_file = "$D/spelling.txt"; +my $codespell = 0; +my $codespellfile = "/usr/share/codespell/dictionary.txt"; +my $conststructsfile = "$D/const_structs.checkpatch"; +my $typedefsfile = ""; +my $color = "auto"; +my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE +# git output parsing needs US English output, so first set backtick child process LANGUAGE +my $git_command ='export LANGUAGE=en_US.UTF-8; git'; +my $tabsize = 8; sub help { my ($exitcode) = @_; @@ -45,16 +76,34 @@ Version: $V Options: -q, --quiet quiet - --no-tree run without a kernel tree + --no-tree run without a OpenWrt tree --no-signoff do not check for 'Signed-off-by' line --patch treat FILE as patchfile (default) --emacs emacs compile window format --terse one line per report + --showfile emit diffed file position, not input file position + -g, --git treat FILE as a single commit or git revision range + single git commit with: + + ^ + ~n + multiple git commits with: + .. + ... + - + git merges are ignored -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests + --list-types list the possible message types + --types TYPE(,TYPE2...) show only these comma separated message types --ignore TYPE(,TYPE2...) ignore various comma separated message types - --show-types show the message "types" in the output - --root=PATH PATH to the kernel tree root + --show-types show the specific message type in the output + --max-line-length=n set the maximum line length, (default $max_line_length) + if exceeded, warn on patches + requires --strict for use with --file + --min-conf-desc-length=n set the min description length, if shorter, warn + --tab-size=n set the number of spaces for tab (default $tabsize) + --root=PATH PATH to the OpenWrt tree root --no-summary suppress the per-file summary --mailback only produce a report in case of warnings/errors --summary-file include the filename in summary @@ -63,6 +112,22 @@ Options: is all off) --test-only=WORD report only warnings/errors containing WORD literally + --fix EXPERIMENTAL - may create horrible results + If correctable single-line errors exist, create + ".EXPERIMENTAL-checkpatch-fixes" + with potential errors corrected to the preferred + checkpatch style + --fix-inplace EXPERIMENTAL - may create horrible results + Is the same as --fix, but overwrites the input + file. It's your fault if there's no backup or git + --ignore-perl-version override checking of perl version. expect + runtime errors. + --codespell Use the codespell dictionary for spelling/typos + (default:/usr/share/codespell/dictionary.txt) + --codespellfile Use this codespell dictionary + --typedefsfile Read additional types from this file + --color[=WHEN] Use colors 'always', 'never', or only when output + is a terminal ('auto'). Default is 'auto'. -h, --help, --version display this help and exit When FILE is - read standard input. @@ -71,6 +136,38 @@ EOM exit($exitcode); } +sub uniq { + my %seen; + return grep { !$seen{$_}++ } @_; +} + +sub list_types { + my ($exitcode) = @_; + + my $count = 0; + + local $/ = undef; + + open(my $script, '<', abs_path($P)) or + die "$P: Can't read '$P' $!\n"; + + my $text = <$script>; + close($script); + + my @types = (); + # Also catch when type or level is passed through a variable + for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { + push (@types, $_); + } + @types = sort(uniq(@types)); + print("#\tMessage type\n\n"); + foreach my $type (@types) { + print(++$count . "\t" . $type . "\n"); + } + + exit($exitcode); +} + my $conf = which_conf($configuration_file); if (-f $conf) { my @conf_args; @@ -97,6 +194,14 @@ if (-f $conf) { unshift(@ARGV, @conf_args) if @conf_args; } +# Perl's Getopt::Long allows options to take optional arguments after a space. +# Prevent --color by itself from consuming other arguments +foreach (@ARGV) { + if ($_ eq "--color" || $_ eq "-color") { + $_ = "--color=$color"; + } +} + GetOptions( 'q|quiet+' => \$quiet, 'tree!' => \$tree, @@ -104,44 +209,107 @@ GetOptions( 'patch!' => \$chk_patch, 'emacs!' => \$emacs, 'terse!' => \$terse, + 'showfile!' => \$showfile, 'f|file!' => \$file, + 'g|git!' => \$git, 'subjective!' => \$check, 'strict!' => \$check, 'ignore=s' => \@ignore, + 'types=s' => \@use, 'show-types!' => \$show_types, + 'list-types!' => \$list_types, + 'max-line-length=i' => \$max_line_length, + 'min-conf-desc-length=i' => \$min_conf_desc_length, + 'tab-size=i' => \$tabsize, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, - + 'fix!' => \$fix, + 'fix-inplace!' => \$fix_inplace, + 'ignore-perl-version!' => \$ignore_perl_version, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, + 'codespell!' => \$codespell, + 'codespellfile=s' => \$codespellfile, + 'typedefsfile=s' => \$typedefsfile, + 'color=s' => \$color, + 'no-color' => \$color, #keep old behaviors of -nocolor + 'nocolor' => \$color, #keep old behaviors of -nocolor 'h|help' => \$help, 'version' => \$help ) or help(1); help(0) if ($help); +list_types(0) if ($list_types); + +$fix = 1 if ($fix_inplace); +$check_orig = $check; + +die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix)); + my $exit = 0; +my $perl_version_ok = 1; +if ($^V && $^V lt $minimum_perl_version) { + $perl_version_ok = 0; + printf "$P: requires at least perl version %vd\n", $minimum_perl_version; + exit(1) if (!$ignore_perl_version); +} + +#if no filenames are given, push '-' to read patch from stdin if ($#ARGV < 0) { - print "$P: no input files\n"; - exit(1); + push(@ARGV, '-'); } -@ignore = split(/,/, join(',',@ignore)); -foreach my $word (@ignore) { - $word =~ s/\s*\n?$//g; - $word =~ s/^\s*//g; - $word =~ s/\s+/ /g; - $word =~ tr/[a-z]/[A-Z]/; - - next if ($word =~ m/^\s*#/); - next if ($word =~ m/^\s*$/); - - $ignore_type{$word}++; +if ($color =~ /^[01]$/) { + $color = !$color; +} elsif ($color =~ /^always$/i) { + $color = 1; +} elsif ($color =~ /^never$/i) { + $color = 0; +} elsif ($color =~ /^auto$/i) { + $color = (-t STDOUT); +} else { + die "$P: Invalid color mode: $color\n"; } +# skip TAB size 1 to avoid additional checks on $tabsize - 1 +die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2); + +sub hash_save_array_words { + my ($hashRef, $arrayRef) = @_; + + my @array = split(/,/, join(',', @$arrayRef)); + foreach my $word (@array) { + $word =~ s/\s*\n?$//g; + $word =~ s/^\s*//g; + $word =~ s/\s+/ /g; + $word =~ tr/[a-z]/[A-Z]/; + + next if ($word =~ m/^\s*#/); + next if ($word =~ m/^\s*$/); + + $hashRef->{$word}++; + } +} + +sub hash_show_words { + my ($hashRef, $prefix) = @_; + + if (keys %$hashRef) { + print "\nNOTE: $prefix message types:"; + foreach my $word (sort keys %$hashRef) { + print " $word"; + } + print "\n"; + } +} + +hash_save_array_words(\%ignore_type, \@ignore); +hash_save_array_words(\%use_type, \@use); + my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; @@ -192,11 +360,18 @@ our $Sparse = qr{ __force| __iomem| __must_check| - __init_refok| __kprobes| __ref| - __rcu + __refconst| + __refdata| + __rcu| + __private }x; +our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; +our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; +our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; +our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; +our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check @@ -205,7 +380,7 @@ our $Attribute = qr{ __percpu| __nocast| __safe| - __bitwise__| + __bitwise| __packed__| __packed2__| __naked| @@ -214,33 +389,53 @@ our $Attribute = qr{ __noreturn| __used| __cold| + __pure| __noclone| __deprecated| __read_mostly| + __ro_after_init| __kprobes| - __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| + $InitAttribute| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| __weak }x; our $Modifier; -our $Inline = qr{inline|__always_inline|noinline}; +our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; -our $Constant = qr{(?i:(?:[0-9]+|0x[0-9a-f]+)[ul]*)}; -our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; -our $Compare = qr{<=|>=|==|!=|<|>}; +our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; +our $Binary = qr{(?i)0b[01]+$Int_type?}; +our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; +our $Int = qr{[0-9]+$Int_type?}; +our $Octal = qr{0[0-7]+$Int_type?}; +our $String = qr{"[X\t]*"}; +our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; +our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; +our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; +our $Float = qr{$Float_hex|$Float_dec|$Float_int}; +our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; +our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; +our $Compare = qr{<=|>=|==|!=|<|(?}; +our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; +our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; + +our $BasicType; our $NonptrType; +our $NonptrTypeMisordered; +our $NonptrTypeWithAttr; our $Type; +our $TypeMisordered; our $Declare; +our $DeclareMisordered; our $NON_ASCII_UTF8 = qr{ [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte @@ -257,39 +452,85 @@ our $UTF8 = qr{ | $NON_ASCII_UTF8 }x; -our $typeTypedefs = qr{(?x: +our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; +our $typeOtherOSTypedefs = qr{(?x: + u_(?:char|short|int|long) | # bsd + u(?:nchar|short|int|long) # sysv +)}; +our $typeKernelTypedefs = qr{(?x: (?:__)?(?:u|s|be|le)(?:8|16|32|64)| atomic_t )}; +our $typeTypedefs = qr{(?x: + $typeC99Typedefs\b| + $typeOtherOSTypedefs\b| + $typeKernelTypedefs\b +)}; + +our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; our $logFunctions = qr{(?x: - printk(?:_ratelimited|_once|)| - [a-z0-9]+_(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + printk(?:_ratelimited|_once|_deferred_once|_deferred|)| + (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + TP_printk| WARN(?:_RATELIMIT|_ONCE|)| panic| - MODULE_[A-Z_]+ + MODULE_[A-Z_]+| + seq_vprintf|seq_printf|seq_puts +)}; + +our $allocFunctions = qr{(?x: + (?:(?:devm_)? + (?:kv|k|v)[czm]alloc(?:_node|_array)? | + kstrdup(?:_const)? | + kmemdup(?:_nul)?) | + (?:\w+)?alloc_skb(?:_ip_align)? | + # dev_alloc_skb/netdev_alloc_skb, et al + dma_alloc_coherent )}; our $signature_tags = qr{(?xi: Signed-off-by:| + Co-developed-by:| Acked-by:| Tested-by:| Reviewed-by:| Reported-by:| + Suggested-by:| To:| Cc: )}; +our @typeListMisordered = ( + qr{char\s+(?:un)?signed}, + qr{int\s+(?:(?:un)?signed\s+)?short\s}, + qr{int\s+short(?:\s+(?:un)?signed)}, + qr{short\s+int(?:\s+(?:un)?signed)}, + qr{(?:un)?signed\s+int\s+short}, + qr{short\s+(?:un)?signed}, + qr{long\s+int\s+(?:un)?signed}, + qr{int\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed\s+int}, + qr{int\s+(?:un)?signed\s+long}, + qr{int\s+(?:un)?signed}, + qr{int\s+long\s+long\s+(?:un)?signed}, + qr{long\s+long\s+int\s+(?:un)?signed}, + qr{long\s+long\s+(?:un)?signed\s+int}, + qr{long\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed}, +); + our @typeList = ( qr{void}, - qr{(?:unsigned\s+)?char}, - qr{(?:unsigned\s+)?short}, - qr{(?:unsigned\s+)?int}, - qr{(?:unsigned\s+)?long}, - qr{(?:unsigned\s+)?long\s+int}, - qr{(?:unsigned\s+)?long\s+long}, - qr{(?:unsigned\s+)?long\s+long\s+int}, - qr{unsigned}, + qr{(?:(?:un)?signed\s+)?char}, + qr{(?:(?:un)?signed\s+)?short\s+int}, + qr{(?:(?:un)?signed\s+)?short}, + qr{(?:(?:un)?signed\s+)?int}, + qr{(?:(?:un)?signed\s+)?long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long}, + qr{(?:(?:un)?signed\s+)?long}, + qr{(?:un)?signed}, qr{float}, qr{double}, qr{bool}, @@ -299,21 +540,254 @@ our @typeList = ( qr{${Ident}_t}, qr{${Ident}_handler}, qr{${Ident}_handler_fn}, + @typeListMisordered, ); + +our $C90_int_types = qr{(?x: + long\s+long\s+int\s+(?:un)?signed| + long\s+long\s+(?:un)?signed\s+int| + long\s+long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+long\s+int| + (?:(?:un)?signed\s+)?long\s+long| + int\s+long\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long\s+long| + + long\s+int\s+(?:un)?signed| + long\s+(?:un)?signed\s+int| + long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+int| + (?:(?:un)?signed\s+)?long| + int\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long| + + int\s+(?:un)?signed| + (?:(?:un)?signed\s+)?int +)}; + +our @typeListFile = (); +our @typeListWithAttr = ( + @typeList, + qr{struct\s+$InitAttribute\s+$Ident}, + qr{union\s+$InitAttribute\s+$Ident}, +); + our @modifierList = ( qr{fastcall}, ); +our @modifierListFile = (); + +our @mode_permission_funcs = ( + ["module_param", 3], + ["module_param_(?:array|named|string)", 4], + ["module_param_array_named", 5], + ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], + ["proc_create(?:_data|)", 2], + ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2], + ["IIO_DEV_ATTR_[A-Z_]+", 1], + ["SENSOR_(?:DEVICE_|)ATTR_2", 2], + ["SENSOR_TEMPLATE(?:_2|)", 3], + ["__ATTR", 2], +); + +#Create a search pattern for all these functions to speed up a loop below +our $mode_perms_search = ""; +foreach my $entry (@mode_permission_funcs) { + $mode_perms_search .= '|' if ($mode_perms_search ne ""); + $mode_perms_search .= $entry->[0]; +} +$mode_perms_search = "(?:${mode_perms_search})"; + +our %deprecated_apis = ( + "synchronize_rcu_bh" => "synchronize_rcu", + "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited", + "call_rcu_bh" => "call_rcu", + "rcu_barrier_bh" => "rcu_barrier", + "synchronize_sched" => "synchronize_rcu", + "synchronize_sched_expedited" => "synchronize_rcu_expedited", + "call_rcu_sched" => "call_rcu", + "rcu_barrier_sched" => "rcu_barrier", + "get_state_synchronize_sched" => "get_state_synchronize_rcu", + "cond_synchronize_sched" => "cond_synchronize_rcu", +); + +#Create a search pattern for all these strings to speed up a loop below +our $deprecated_apis_search = ""; +foreach my $entry (keys %deprecated_apis) { + $deprecated_apis_search .= '|' if ($deprecated_apis_search ne ""); + $deprecated_apis_search .= $entry; +} +$deprecated_apis_search = "(?:${deprecated_apis_search})"; + +our $mode_perms_world_writable = qr{ + S_IWUGO | + S_IWOTH | + S_IRWXUGO | + S_IALLUGO | + 0[0-7][0-7][2367] +}x; + +our %mode_permission_string_types = ( + "S_IRWXU" => 0700, + "S_IRUSR" => 0400, + "S_IWUSR" => 0200, + "S_IXUSR" => 0100, + "S_IRWXG" => 0070, + "S_IRGRP" => 0040, + "S_IWGRP" => 0020, + "S_IXGRP" => 0010, + "S_IRWXO" => 0007, + "S_IROTH" => 0004, + "S_IWOTH" => 0002, + "S_IXOTH" => 0001, + "S_IRWXUGO" => 0777, + "S_IRUGO" => 0444, + "S_IWUGO" => 0222, + "S_IXUGO" => 0111, +); + +#Create a search pattern for all these strings to speed up a loop below +our $mode_perms_string_search = ""; +foreach my $entry (keys %mode_permission_string_types) { + $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); + $mode_perms_string_search .= $entry; +} +our $single_mode_perms_string_search = "(?:${mode_perms_string_search})"; +our $multi_mode_perms_string_search = qr{ + ${single_mode_perms_string_search} + (?:\s*\|\s*${single_mode_perms_string_search})* +}x; + +sub perms_to_octal { + my ($string) = @_; + + return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/); + + my $val = ""; + my $oval = ""; + my $to = 0; + my $curpos = 0; + my $lastpos = 0; + while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { + $curpos = pos($string); + my $match = $2; + my $omatch = $1; + last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); + $lastpos = $curpos; + $to |= $mode_permission_string_types{$match}; + $val .= '\s*\|\s*' if ($val ne ""); + $val .= $match; + $oval .= $omatch; + } + $oval =~ s/^\s*\|\s*//; + $oval =~ s/\s*\|\s*$//; + return sprintf("%04o", $to); +} our $allowed_asm_includes = qr{(?x: irq| - memory + memory| + time| + reboot )}; # memory.h: ARM has a custom one +# Load common spelling mistakes and build regular expression list. +my $misspellings; +my %spelling_fix; + +if (open(my $spelling, '<', $spelling_file)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my ($suspect, $fix) = split(/\|\|/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); +} else { + warn "No typos will be found - file '$spelling_file': $!\n"; +} + +if ($codespell) { + if (open(my $spelling, '<', $codespellfile)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + next if ($line =~ m/, disabled/i); + + $line =~ s/,.*$//; + + my ($suspect, $fix) = split(/->/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); + } else { + warn "No codespell typos will be found - file '$codespellfile': $!\n"; + } +} + +$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; + +sub read_words { + my ($wordsRef, $file) = @_; + + if (open(my $words, '<', $file)) { + while (<$words>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + if ($line =~ /\s/) { + print("$file: '$line' invalid - ignored\n"); + next; + } + + $$wordsRef .= '|' if ($$wordsRef ne ""); + $$wordsRef .= $line; + } + close($file); + return 1; + } + + return 0; +} + +my $const_structs = ""; +read_words(\$const_structs, $conststructsfile) + or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; + +my $typeOtherTypedefs = ""; +if (length($typedefsfile)) { + read_words(\$typeOtherTypedefs, $typedefsfile) + or warn "No additional types will be considered - file '$typedefsfile': $!\n"; +} +$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne ""); + sub build_types { - my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; - my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; + my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; + my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; + my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; + my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $BasicType = qr{ + (?:$typeTypedefs\b)| + (?:${all}\b) + }x; $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: @@ -323,16 +797,37 @@ sub build_types { ) (?:\s+$Modifier|\s+const)* }x; + $NonptrTypeMisordered = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:${Misordered}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeWithAttr = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${allWithAttr}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; $Type = qr{ $NonptrType - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*|\[\])+|(?:\s*\[\s*\])+)? + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} (?:\s+$Inline|\s+$Modifier)* }x; - $Declare = qr{(?:$Storage\s+)?$Type}; + $TypeMisordered = qr{ + $NonptrTypeMisordered + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; + $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; } build_types(); - our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Using $balanced_parens, $LvalOrFunc, or $FuncArg @@ -340,26 +835,216 @@ our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Any use must be runtime checked with $^V our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; -our $LvalOrFunc = qr{($Lval)\s*($balanced_parens{0,1})\s*}; -our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant)}; +our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; +our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; + +our $declaration_macros = qr{(?x: + (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| + (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| + (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(| + (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\( +)}; sub deparenthesize { my ($string) = @_; return "" if (!defined($string)); - $string =~ s@^\s*\(\s*@@g; - $string =~ s@\s*\)\s*$@@g; + + while ($string =~ /^\s*\(.*\)\s*$/) { + $string =~ s@^\s*\(\s*@@; + $string =~ s@\s*\)\s*$@@; + } + $string =~ s@\s+@ @g; + return $string; } +sub seed_camelcase_file { + my ($file) = @_; + + return if (!(-f $file)); + + local $/; + + open(my $include_file, '<', "$file") + or warn "$P: Can't read '$file' $!\n"; + my $text = <$include_file>; + close($include_file); + + my @lines = split('\n', $text); + + foreach my $line (@lines) { + next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); + if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { + $camelcase{$1} = 1; + } + } +} + +our %maintained_status = (); + +sub is_maintained_obsolete { + my ($filename) = @_; + + return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); + + if (!exists($maintained_status{$filename})) { + $maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; + } + + return $maintained_status{$filename} =~ /obsolete/i; +} + +sub is_SPDX_License_valid { + my ($license) = @_; + + return 1 if (!$tree || which("python") eq "" || !(-e "$root/scripts/spdxcheck.py") || !(-e "$root/.git")); + + my $root_path = abs_path($root); + my $status = `cd "$root_path"; echo "$license" | python scripts/spdxcheck.py -`; + return 0 if ($status ne ""); + return 1; +} + +my $camelcase_seeded = 0; +sub seed_camelcase_includes { + return if ($camelcase_seeded); + + my $files; + my $camelcase_cache = ""; + my @include_files = (); + + $camelcase_seeded = 1; + + if (-e ".git") { + my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; + chomp $git_last_include_commit; + $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } else { + my $last_mod_date = 0; + $files = `find $root/include -name "*.h"`; + @include_files = split('\n', $files); + foreach my $file (@include_files) { + my $date = POSIX::strftime("%Y%m%d%H%M", + localtime((stat $file)[9])); + $last_mod_date = $date if ($last_mod_date < $date); + } + $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; + } + + if ($camelcase_cache ne "" && -f $camelcase_cache) { + open(my $camelcase_file, '<', "$camelcase_cache") + or warn "$P: Can't read '$camelcase_cache' $!\n"; + while (<$camelcase_file>) { + chomp; + $camelcase{$_} = 1; + } + close($camelcase_file); + + return; + } + + if (-e ".git") { + $files = `${git_command} ls-files "include/*.h"`; + @include_files = split('\n', $files); + } + + foreach my $file (@include_files) { + seed_camelcase_file($file); + } + + if ($camelcase_cache ne "") { + unlink glob ".checkpatch-camelcase.*"; + open(my $camelcase_file, '>', "$camelcase_cache") + or warn "$P: Can't write '$camelcase_cache' $!\n"; + foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { + print $camelcase_file ("$_\n"); + } + close($camelcase_file); + } +} + +sub git_commit_info { + my ($commit, $id, $desc) = @_; + + return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); + + my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`; + $output =~ s/^\s*//gm; + my @lines = split("\n", $output); + + return ($id, $desc) if ($#lines < 0); + + if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) { +# Maybe one day convert this block of bash into something that returns +# all matching commit ids, but it's very slow... +# +# echo "checking commits $1..." +# git rev-list --remotes | grep -i "^$1" | +# while read line ; do +# git log --format='%H %s' -1 $line | +# echo "commit $(cut -c 1-12,41-)" +# done + } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { + $id = undef; + } else { + $id = substr($lines[0], 0, 12); + $desc = substr($lines[0], 41); + } + + return ($id, $desc); +} + $chk_signoff = 0 if ($file); my @rawlines = (); my @lines = (); +my @fixed = (); +my @fixed_inserted = (); +my @fixed_deleted = (); +my $fixlinenr = -1; + +# If input is git commits, extract all commits from the commit expressions. +# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. +die "$P: No git repository found\n" if ($git && !-e ".git"); + +if ($git) { + my @commits = (); + foreach my $commit_expr (@ARGV) { + my $git_range; + if ($commit_expr =~ m/^(.*)-(\d+)$/) { + $git_range = "-$2 $1"; + } elsif ($commit_expr =~ m/\.\./) { + $git_range = "$commit_expr"; + } else { + $git_range = "-1 $commit_expr"; + } + my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`; + foreach my $line (split(/\n/, $lines)) { + $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; + next if (!defined($1) || !defined($2)); + my $sha1 = $1; + my $subject = $2; + unshift(@commits, $sha1); + $git_commits{$sha1} = $subject; + } + } + die "$P: no git commits after extraction!\n" if (@commits == 0); + @ARGV = @commits; +} + my $vname; +$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"}; for my $filename (@ARGV) { my $FILE; - if ($file) { + if ($git) { + open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || + die "$P: $filename: git format-patch failed - $!\n"; + } elsif ($file) { open($FILE, '-|', "diff -u /dev/null $filename") || die "$P: $filename: diff failed - $!\n"; } elsif ($filename eq '-') { @@ -370,19 +1055,56 @@ for my $filename (@ARGV) { } if ($filename eq '-') { $vname = 'Your patch'; + } elsif ($git) { + $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")'; } else { $vname = $filename; } while (<$FILE>) { chomp; push(@rawlines, $_); + $vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i); } close($FILE); + + if ($#ARGV > 0 && $quiet == 0) { + print '-' x length($vname) . "\n"; + print "$vname\n"; + print '-' x length($vname) . "\n"; + } + if (!process($filename)) { $exit = 1; } @rawlines = (); @lines = (); + @fixed = (); + @fixed_inserted = (); + @fixed_deleted = (); + $fixlinenr = -1; + @modifierListFile = (); + @typeListFile = (); + build_types(); +} + +if (!$quiet) { + hash_show_words(\%use_type, "Used"); + hash_show_words(\%ignore_type, "Ignored"); + + if (!$perl_version_ok) { + print << "EOM" + +NOTE: perl $^V is not modern enough to detect all possible issues. + An upgrade to at least perl $minimum_perl_version is suggested. +EOM + } + if ($exit) { + print << "EOM" + +NOTE: If any of the errors are false positives, please report + them to the maintainer, see CHECKPATCH in MAINTAINERS. +EOM + } } exit($exit); @@ -402,12 +1124,13 @@ sub top_of_openwrt_tree { } } return 1; - } +} sub parse_email { my ($formatted_email) = @_; my $name = ""; + my $name_comment = ""; my $address = ""; my $comment = ""; @@ -421,9 +1144,9 @@ sub parse_email { } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { $address = $1; $comment = $2 if defined $2; - $formatted_email =~ s/$address.*$//; + $formatted_email =~ s/\Q$address\E.*$//; $name = $formatted_email; - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; # If there's a name left after stripping spaces and # leading quotes, and the address doesn't have both @@ -438,9 +1161,13 @@ sub parse_email { } } - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; - $address =~ s/^\s+|\s+$//g; + $name =~ s/(\s*\([^\)]+\))\s*//; + if (defined($1)) { + $name_comment = trim($1); + } + $address = trim($address); $address =~ s/^\<|\>$//g; if ($name =~ /[^\w \-]/i) { ##has "must quote" chars @@ -448,7 +1175,7 @@ sub parse_email { $name = "\"$name\""; } - return ($name, $address, $comment); + return ($name, $name_comment, $address, $comment); } sub format_email { @@ -456,9 +1183,9 @@ sub format_email { my $formatted_email; - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; - $address =~ s/^\s+|\s+$//g; + $address = trim($address); if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(? 0); + + return !defined $ignore_type{$type}; } sub report { - if (!show_type($_[1]) || - (defined $tst_only && $_[2] !~ /\Q$tst_only\E/)) { + my ($level, $type, $msg) = @_; + + if (!show_type($type) || + (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { return 0; } - my $line; - if ($show_types) { - $line = "$prefix$_[0]:$_[1]: $_[2]\n"; - } else { - $line = "$prefix$_[0]: $_[2]\n"; + my $output = ''; + if ($color) { + if ($level eq 'ERROR') { + $output .= RED; + } elsif ($level eq 'WARNING') { + $output .= YELLOW; + } else { + $output .= GREEN; + } } - $line = (split('\n', $line))[0] . "\n" if ($terse); + $output .= $prefix . $level . ':'; + if ($show_types) { + $output .= BLUE if ($color); + $output .= "$type:"; + } + $output .= RESET if ($color); + $output .= ' ' . $msg . "\n"; - push(our @report, $line); + if ($showfile) { + my @lines = split("\n", $output, -1); + splice(@lines, 1, 1); + $output = join("\n", @lines); + } + $output = (split('\n', $output))[0] . "\n" if ($terse); + + push(our @report, $output); return 1; } + sub report_dump { our @report; } +sub fixup_current_range { + my ($lineRef, $offset, $length) = @_; + + if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { + my $o = $1; + my $l = $2; + my $no = $o + $offset; + my $nl = $l + $length; + $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; + } +} + +sub fix_inserted_deleted_lines { + my ($linesRef, $insertedRef, $deletedRef) = @_; + + my $range_last_linenr = 0; + my $delta_offset = 0; + + my $old_linenr = 0; + my $new_linenr = 0; + + my $next_insert = 0; + my $next_delete = 0; + + my @lines = (); + + my $inserted = @{$insertedRef}[$next_insert++]; + my $deleted = @{$deletedRef}[$next_delete++]; + + foreach my $old_line (@{$linesRef}) { + my $save_line = 1; + my $line = $old_line; #don't modify the array + if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename + $delta_offset = 0; + } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk + $range_last_linenr = $new_linenr; + fixup_current_range(\$line, $delta_offset, 0); + } + + while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { + $deleted = @{$deletedRef}[$next_delete++]; + $save_line = 0; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); + } + + while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { + push(@lines, ${$inserted}{'LINE'}); + $inserted = @{$insertedRef}[$next_insert++]; + $new_linenr++; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); + } + + if ($save_line) { + push(@lines, $line); + $new_linenr++; + } + + $old_linenr++; + } + + return @lines; +} + +sub fix_insert_line { + my ($linenr, $line) = @_; + + my $inserted = { + LINENR => $linenr, + LINE => $line, + }; + push(@fixed_inserted, $inserted); +} + +sub fix_delete_line { + my ($linenr, $line) = @_; + + my $deleted = { + LINENR => $linenr, + LINE => $line, + }; + + push(@fixed_deleted, $deleted); +} + sub ERROR { - if (report("ERROR", $_[0], $_[1])) { + my ($type, $msg) = @_; + + if (report("ERROR", $type, $msg)) { our $clean = 0; our $cnt_error++; + return 1; } + return 0; } sub WARN { - if (report("WARNING", $_[0], $_[1])) { + my ($type, $msg) = @_; + + if (report("WARNING", $type, $msg)) { our $clean = 0; our $cnt_warn++; + return 1; } + return 0; } sub CHK { - if ($check && report("CHECK", $_[0], $_[1])) { + my ($type, $msg) = @_; + + if ($check && report("CHECK", $type, $msg)) { our $clean = 0; our $cnt_chk++; + return 1; } + return 0; } sub check_absolute_file { @@ -1316,6 +2237,53 @@ sub check_absolute_file { } } +sub trim { + my ($string) = @_; + + $string =~ s/^\s+|\s+$//g; + + return $string; +} + +sub ltrim { + my ($string) = @_; + + $string =~ s/^\s+//; + + return $string; +} + +sub rtrim { + my ($string) = @_; + + $string =~ s/\s+$//; + + return $string; +} + +sub string_find_replace { + my ($string, $find, $replace) = @_; + + $string =~ s/$find/$replace/g; + + return $string; +} + +sub tabify { + my ($leading) = @_; + + my $source_indent = $tabsize; + my $max_spaces_before_tab = $source_indent - 1; + my $spaces_to_tab = " " x $source_indent; + + #convert leading spaces to tabs + 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; + #Remove spaces before a tab + 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; + + return "$leading"; +} + sub pos_last_openparen { my ($line) = @_; @@ -1343,7 +2311,20 @@ sub pos_last_openparen { } } - return $last_openparen + 1; + return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; +} + +sub get_raw_comment { + my ($line, $rawline) = @_; + my $comment = ''; + + for my $i (0 .. (length($line) - 1)) { + if (substr($line, $i, 1) eq "$;") { + $comment .= substr($rawline, $i, 1); + } + } + + return $comment; } sub process { @@ -1362,10 +2343,23 @@ sub process { our $clean = 1; my $signoff = 0; + my $author = ''; + my $authorsignoff = 0; my $is_patch = 0; - - my $in_header_lines = 1; + my $is_binding_patch = -1; + my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch + my $has_patch_separator = 0; #Found a --- line + my $has_commit_log = 0; #Encountered lines before patch + my $commit_log_lines = 0; #Number of commit log lines + my $commit_log_possible_stack_dump = 0; + my $commit_log_long_line = 0; + my $commit_log_has_diff = 0; + my $reported_maintainer_file = 0; + my $non_utf8_charset = 0; + + my $last_blank_line = 0; + my $last_coalesced_string_linenr = -1; our @report = (); our $cnt_lines = 0; @@ -1378,6 +2372,7 @@ sub process { my $realline = 0; my $realcnt = 0; my $here = ''; + my $context_function; #undef'd unless there's a known function my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; @@ -1391,14 +2386,21 @@ sub process { my %suppress_export; my $suppress_statement = 0; - # Pre-scan the patch sanitizing the lines. + my %signatures = (); + + my $camelcase_file_seeded = 0; + + my $checklicenseline = 1; + sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; - if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + push(@fixed, $rawline) if ($fix); + + if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { $realcnt=$3+1; @@ -1462,13 +2464,28 @@ sub process { $realcnt = 0; $linenr = 0; + $fixlinenr = -1; foreach my $line (@lines) { $linenr++; + $fixlinenr++; + my $sline = $line; #copy of $line + $sline =~ s/$;/ /g; #with comments as spaces my $rawline = $rawlines[$linenr - 1]; + my $raw_comment = get_raw_comment($line, $rawline); + +# check if it's a mode change, rename or start of a patch + if (!$in_commit_log && + ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ || + ($line =~ /^rename (?:from|to) \S+\s*$/ || + $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) { + $is_patch = 1; + } #extract the line range in the file after the patch is applied - if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + if (!$in_commit_log && + $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { + my $context = $4; $is_patch = 1; $first_line = $linenr + 1; $realline=$1-1; @@ -1484,6 +2501,11 @@ sub process { %suppress_whiletrailers = (); %suppress_export = (); $suppress_statement = 0; + if ($context =~ /\b(\w+)\s*\(/) { + $context_function = $1; + } else { + undef $context_function; + } next; # track the line number as we move through the hunk, note that @@ -1509,21 +2531,19 @@ sub process { my $hunk_line = ($realcnt != 0); -#make up the handle for any error we report on this line - $prefix = "$filename:$realline: " if ($emacs && $file); - $prefix = "$filename:$linenr: " if ($emacs && !$file); - $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); + my $found_file = 0; # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; + $found_file = 1; } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $p1_prefix = $1; @@ -1537,6 +2557,36 @@ sub process { ERROR("MODIFIED_INCLUDE_ASM", "do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); } + $found_file = 1; + } + +#make up the handle for any error we report on this line + if ($showfile) { + $prefix = "$realfile:$realline: " + } elsif ($emacs) { + if ($file) { + $prefix = "$filename:$realline: "; + } else { + $prefix = "$filename:$linenr: "; + } + } + + if ($found_file) { + if (is_maintained_obsolete($realfile)) { + WARN("OBSOLETE", + "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n"); + } + if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { + $check = 1; + } else { + $check = $check_orig; + } + $checklicenseline = 1; + + if ($realfile !~ /^MAINTAINERS/) { + my $last_binding_patch = $is_binding_patch; + } + next; } @@ -1548,44 +2598,110 @@ sub process { $cnt_lines++ if ($realcnt != 0); +# Verify the existence of a commit log if appropriate +# 2 is used because a $signature is counted in $commit_log_lines + if ($in_commit_log) { + if ($line !~ /^\s*$/) { + $commit_log_lines++; #could be a $signature + } + } elsif ($has_commit_log && $commit_log_lines < 2) { + WARN("COMMIT_MESSAGE", + "Missing commit description - Add an appropriate one\n"); + $commit_log_lines = 2; #warn only once + } + +# Check if the commit log has what seems like a diff which can confuse patch + if ($in_commit_log && !$commit_log_has_diff && + (($line =~ m@^\s+diff\b.*a/[\w/]+@ && + $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || + $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || + $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { + ERROR("DIFF_IN_COMMIT_MSG", + "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); + $commit_log_has_diff = 1; + } + # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { + if ($realfile !~ m@scripts/@ && + $realfile !~ /\.(py|pl|awk|sh)$/) { ERROR("EXECUTE_PERMISSIONS", "do not set execute permissions for source files\n" . $permhere); } } +# Check the patch for a From: + if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) { + $author = $1; + $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i); + $author =~ s/"//g; + $author = reformat_email($author); + } + # Check the patch for a signoff: - if ($line =~ /^\s*signed-off-by:/i) { + if ($line =~ /^\s*signed-off-by:\s*(.*)/i) { $signoff++; $in_commit_log = 0; + if ($author ne '') { + if (same_email_addresses($1, $author)) { + $authorsignoff = 1; + } + } + } + +# Check for patch separator + if ($line =~ /^---$/) { + $has_patch_separator = 1; + $in_commit_log = 0; + } + +# Check if MAINTAINERS is being updated. If so, there's probably no need to +# emit the "does MAINTAINERS need updating?" message on file add/move/delete + if ($line =~ /^\s*MAINTAINERS\s*\|/) { + $reported_maintainer_file = 1; } # Check signature styles if (!$in_header_lines && - $line =~ /^(\s*)($signature_tags)(\s*)(.*)/) { + $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { my $space_before = $1; my $sign_off = $2; my $space_after = $3; my $email = $4; my $ucfirst_sign_off = ucfirst(lc($sign_off)); - if (defined $space_before && $space_before ne "") { + if ($sign_off !~ /$signature_tags/) { WARN("BAD_SIGN_OFF", - "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr); + "Non-standard signature: $sign_off\n" . $herecurr); + } + if (defined $space_before && $space_before ne "") { + if (WARN("BAD_SIGN_OFF", + "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } } if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { - WARN("BAD_SIGN_OFF", - "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr); + if (WARN("BAD_SIGN_OFF", + "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } if (!defined $space_after || $space_after ne " ") { - WARN("BAD_SIGN_OFF", - "Use a single space after $ucfirst_sign_off\n" . $herecurr); + if (WARN("BAD_SIGN_OFF", + "Use a single space after $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } } - my ($email_name, $email_address, $comment) = parse_email($email); + my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); my $suggested_email = format_email(($email_name, $email_address)); if ($suggested_email eq "") { ERROR("BAD_SIGN_OFF", @@ -1596,13 +2712,157 @@ sub process { $dequoted =~ s/" $comment" ne $email && - "$suggested_email$comment" ne $email) { + if (!same_email_addresses($email, $suggested_email)) { WARN("BAD_SIGN_OFF", "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); } } + +# Check for duplicate signatures + my $sig_nospace = $line; + $sig_nospace =~ s/\s//g; + $sig_nospace = lc($sig_nospace); + if (defined $signatures{$sig_nospace}) { + WARN("BAD_SIGN_OFF", + "Duplicate signature\n" . $herecurr); + } else { + $signatures{$sig_nospace} = 1; + } + +# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email + if ($sign_off =~ /^co-developed-by:$/i) { + if ($email eq $author) { + WARN("BAD_SIGN_OFF", + "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline); + } + if (!defined $lines[$linenr]) { + WARN("BAD_SIGN_OFF", + "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline); + } elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) { + WARN("BAD_SIGN_OFF", + "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); + } elsif ($1 ne $email) { + WARN("BAD_SIGN_OFF", + "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); + } + } + } + +# Check email subject for common tools that don't need to be mentioned + if ($in_header_lines && + $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { + WARN("EMAIL_SUBJECT", + "A patch subject line should describe the change not the tool that found it\n" . $herecurr); + } + +# Check for Gerrit Change-Ids not in any patch context + if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) { + ERROR("GERRIT_CHANGE_ID", + "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr); + } + +# Check if the commit log is in a possible stack dump + if ($in_commit_log && !$commit_log_possible_stack_dump && + ($line =~ /^\s*(?:WARNING:|BUG:)/ || + $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || + # timestamp + $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) || + $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ || + $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) { + # stack dump address styles + $commit_log_possible_stack_dump = 1; + } + +# Check for line lengths > 75 in commit log, warn once + if ($in_commit_log && !$commit_log_long_line && + length($line) > 75 && + !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || + # file delta changes + $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || + # filename then : + $line =~ /^\s*(?:Fixes:|Link:)/i || + # A Fixes: or Link: line + $commit_log_possible_stack_dump)) { + WARN("COMMIT_LOG_LONG_LINE", + "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); + $commit_log_long_line = 1; + } + +# Reset possible stack dump if a blank line is found + if ($in_commit_log && $commit_log_possible_stack_dump && + $line =~ /^\s*$/) { + $commit_log_possible_stack_dump = 0; + } + +# Check for git id commit length and improperly formed commit descriptions + if ($in_commit_log && !$commit_log_possible_stack_dump && + $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i && + $line !~ /^This reverts commit [0-9a-f]{7,40}/ && + ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || + ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && + $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && + $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { + my $init_char = "c"; + my $orig_commit = ""; + my $short = 1; + my $long = 0; + my $case = 1; + my $space = 1; + my $hasdesc = 0; + my $hasparens = 0; + my $id = '0123456789ab'; + my $orig_desc = "commit description"; + my $description = ""; + + if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { + $init_char = $1; + $orig_commit = lc($2); + } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { + $orig_commit = lc($1); + } + + $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); + $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); + $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); + $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); + if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { + $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; + $orig_desc = $1; + $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; + $orig_desc .= " " . $1; + $hasparens = 1; + } + + ($id, $description) = git_commit_info($orig_commit, + $id, $orig_desc); + + if (defined($id) && + ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) { + ERROR("GIT_COMMIT_ID", + "Please use git commit description style 'commit <12+ chars of sha1> (\"\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); + } + } + +# Check for added, moved or deleted files + if (!$reported_maintainer_file && !$in_commit_log && + ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || + $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || + ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && + (defined($1) || defined($2))))) { + $is_patch = 1; + $reported_maintainer_file = 1; + WARN("FILE_PATH_CHANGES", + "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } # Check for wrappage within a valid hunk of the file @@ -1612,20 +2872,6 @@ sub process { $herecurr) if (!$emitted_corrupt++); } -# Check for absolute kernel paths. - if ($tree) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { @@ -1642,39 +2888,112 @@ sub process { # Check if it's the start of a commit log # (not a header line and we haven't seen the patch filename) if ($in_header_lines && $realfile =~ /^$/ && - $rawline !~ /^(commit\b|from\b|[\w-]+:).+$/i) { + !($rawline =~ /^\s+(?:\S|$)/ || + $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { $in_header_lines = 0; $in_commit_log = 1; + $has_commit_log = 1; } -# Still not yet in a patch, check for any UTF-8 - if ($in_commit_log && $realfile =~ /^$/ && +# Check if there is UTF-8 in a commit log when a mail header has explicitly +# declined it, i.e defined some charset where it is missing. + if ($in_header_lines && + $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && + $1 !~ /utf-8/i) { + $non_utf8_charset = 1; + } + + if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && $rawline =~ /$NON_ASCII_UTF8/) { - CHK("UTF8_BEFORE_PATCH", + WARN("UTF8_BEFORE_PATCH", "8-bit UTF-8 used in possible commit log\n" . $herecurr); } +# Check for absolute kernel paths in commit message + if ($tree && $in_commit_log) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# Check for various typo / spelling mistakes + if (defined($misspellings) && + ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { + while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { + my $typo = $1; + my $typo_fix = $spelling_fix{lc($typo)}; + $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); + $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + if (&{$msg_level}("TYPO_SPELLING", + "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; + } + } + } + +# check for invalid commit id + if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) { + my $id; + my $description; + ($id, $description) = git_commit_info($2, undef, undef); + if (!defined($id)) { + WARN("UNKNOWN_COMMIT_ID", + "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr); + } + } + # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); #trailing whitespace if ($line =~ /^\+.*\015/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - ERROR("DOS_LINE_ENDINGS", - "DOS line endings\n" . $herevet); - + if (ERROR("DOS_LINE_ENDINGS", + "DOS line endings\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/[\s\015]+$//; + } } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - ERROR("TRAILING_WHITESPACE", - "trailing whitespace\n" . $herevet); + if (ERROR("TRAILING_WHITESPACE", + "trailing whitespace\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + $rpt_cleaners = 1; } +# Check for FSF mailing addresses. + if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b675\s+Mass\s+Ave/i || + $rawline =~ /\b59\s+Temple\s+Pl/i || + $rawline =~ /\b51\s+Franklin\s+St/i) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + my $msg_level = \&ERROR; + $msg_level = \&CHK if ($file); + &{$msg_level}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + } + # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && - $line =~ /.\s*config\s+/) { + # 'choice' is usually the last thing on the line (though + # Kconfig supports named choices), so use a word boundary + # (\b) rather than a whitespace character (\s) + $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { my $length = 0; my $cnt = $realcnt; my $ln = $linenr + 1; @@ -1687,10 +3006,15 @@ sub process { $is_end = $lines[$ln - 1] =~ /^\+/; next if ($f =~ /^-/); + last if (!$file && $f =~ /^\@\@/); - if ($lines[$ln - 1] =~ /.\s*(?:bool|tristate)\s*\"/) { + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { $is_start = 1; - } elsif ($lines[$ln - 1] =~ /.\s*(?:---)?help(?:---)?$/) { + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) { + if ($lines[$ln - 1] =~ "---help---") { + WARN("CONFIG_DESCRIPTION", + "prefer 'help' over '---help---' for new help texts\n" . $herecurr); + } $length = -1; } @@ -1698,17 +3022,72 @@ sub process { $f =~ s/#.*//; $f =~ s/^\s+//; next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { + + # This only checks context lines in the patch + # and so hopefully shouldn't trigger false + # positives, even though some of these are + # common words in help texts + if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| + if|endif|menu|endmenu|source)\b/x) { $is_end = 1; last; } $length++; } - WARN("CONFIG_DESCRIPTION", - "please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_start && $is_end && $length < 4); + if ($is_start && $is_end && $length < $min_conf_desc_length) { + WARN("CONFIG_DESCRIPTION", + "please write a paragraph that describes the config symbol fully\n" . $herecurr); + } #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; } +# check MAINTAINERS entries + if ($realfile =~ /^MAINTAINERS$/) { +# check MAINTAINERS entries for the right form + if ($rawline =~ /^\+[A-Z]:/ && + $rawline !~ /^\+[A-Z]:\t\S/) { + if (WARN("MAINTAINERS_STYLE", + "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; + } + } +# check MAINTAINERS entries for the right ordering too + my $preferred_order = 'MRLSWQBCPTFXNK'; + if ($rawline =~ /^\+[A-Z]:/ && + $prevrawline =~ /^[\+ ][A-Z]:/) { + $rawline =~ /^\+([A-Z]):\s*(.*)/; + my $cur = $1; + my $curval = $2; + $prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/; + my $prev = $1; + my $prevval = $2; + my $curindex = index($preferred_order, $cur); + my $previndex = index($preferred_order, $prev); + if ($curindex < 0) { + WARN("MAINTAINERS_STYLE", + "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr); + } else { + if ($previndex >= 0 && $curindex < $previndex) { + WARN("MAINTAINERS_STYLE", + "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev); + } elsif ((($prev eq 'F' && $cur eq 'F') || + ($prev eq 'X' && $cur eq 'X')) && + ($prevval cmp $curval) > 0) { + WARN("MAINTAINERS_STYLE", + "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev); + } + } + } + } + +# discourage the use of boolean for type definition attributes of Kconfig options + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*\bboolean\b/) { + WARN("CONFIG_TYPE_BOOLEAN", + "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); + } + if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { my $flag = $1; @@ -1723,39 +3102,128 @@ sub process { "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); } +# check for using SPDX license tag at beginning of files + if ($realline == $checklicenseline) { + if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { + $checklicenseline = 2; + } elsif ($rawline =~ /^\+/) { + my $comment = ""; + if ($realfile =~ /\.(h|s|S)$/) { + $comment = '/*'; + } elsif ($realfile =~ /\.(c|dts|dtsi)$/) { + $comment = '//'; + } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) { + $comment = '#'; + } elsif ($realfile =~ /\.rst$/) { + $comment = '..'; + } + +# check SPDX comment style for .[chsS] files + if ($realfile =~ /\.[chsS]$/ && + $rawline =~ /SPDX-License-Identifier:/ && + $rawline !~ m@^\+\s*\Q$comment\E\s*@) { + WARN("SPDX_LICENSE_TAG", + "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); + } + + if ($comment !~ /^$/ && + $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { + WARN("SPDX_LICENSE_TAG", + "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); + } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { + my $spdx_license = $1; + if (!is_SPDX_License_valid($spdx_license)) { + WARN("SPDX_LICENSE_TAG", + "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr); + } + if ($realfile =~ m@^Documentation/devicetree/bindings/@ && + not $spdx_license =~ /GPL-2\.0.*BSD-2-Clause/) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + if (&{$msg_level}("SPDX_LICENSE_TAG", + + "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/; + } + } + } + } + } + # check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); -#80 column limit - if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && - $rawline !~ /^.\s*\*\s*\@$Ident\s/ && - !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:|,|\)\s*;)\s*$/ || - $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && - $length > 80) - { - WARN("LONG_LINE", - "line over 80 characters\n" . $herecurr); +# check for using SPDX-License-Identifier on the wrong line number + if ($realline != $checklicenseline && + $rawline =~ /\bSPDX-License-Identifier:/ && + substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) { + WARN("SPDX_LICENSE_TAG", + "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr); } -# Check for user-visible strings broken across lines, which breaks the ability -# to grep for the string. Limited to strings used as parameters (those -# following an open parenthesis), which almost completely eliminates false -# positives, as well as warning only once per parameter rather than once per -# line of the string. Make an exception when the previous string ends in a -# newline (multiple lines in one string constant) or \n\t (common in inline -# assembly to indent the instruction on the following line). - if ($line =~ /^\+\s*"/ && - $prevline =~ /"\s*$/ && - $prevline =~ /\(/ && - $prevrawline !~ /\\n(?:\\t)*"\s*$/) { - WARN("SPLIT_STRING", - "quoted string split across lines\n" . $hereprev); - } +# line length limit (with some exclusions) +# +# There are a few types of lines that may extend beyond $max_line_length: +# logging functions like pr_info that end in a string +# lines with a single string +# #defines that are a single string +# lines with an RFC3986 like URL +# +# There are 3 different line length message types: +# LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length +# LONG_LINE_STRING a string starts before but extends beyond $max_line_length +# LONG_LINE all other lines longer than $max_line_length +# +# if LONG_LINE is ignored, the other 2 types are also ignored +# -# check for spaces before a quoted newline - if ($rawline =~ /^.*\".*\s\\n/) { - WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", - "unnecessary whitespace before a quoted newline\n" . $herecurr); + if ($line =~ /^\+/ && $length > $max_line_length) { + my $msg_type = "LONG_LINE"; + + # Check the allowed long line types first + + # logging functions that end in a string that starts + # before $max_line_length + if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = ""; + + # lines with only strings (w/ possible termination) + # #defines with only strings + } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || + $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { + $msg_type = ""; + + # More special cases + } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || + $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { + $msg_type = ""; + + # URL ($rawline is used in case the URL is in a comment) + } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) { + $msg_type = ""; + + # Otherwise set the alternate message types + + # a comment starts before $max_line_length + } elsif ($line =~ /($;[\s$;]*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_COMMENT" + + # a quoted string starts before $max_line_length + } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_STRING" + } + + if ($msg_type ne "" && + (show_type("LONG_LINE") || show_type($msg_type))) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}($msg_type, + "line length of $length exceeds $max_line_length columns\n" . $herecurr); + } } # check for adding lines without a newline. @@ -1764,38 +3232,39 @@ sub process { "adding a line without newline at end of file\n" . $herecurr); } -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("LO_MACRO", - "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("HI_MACRO", - "use the HI() macro, not (... >> 16)\n" . $herevet); - } - } - # check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl)$/); + next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); # at the beginning of a line any tabs must come first and anything -# more than 8 must use tabs. +# more than $tabsize must use tabs. if ($rawline =~ /^\+\s* \t\s*\S/ || $rawline =~ /^\+\s* \s*/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - ERROR("CODE_INDENT", - "code indent should use tabs where possible\n" . $herevet); $rpt_cleaners = 1; + if (ERROR("CODE_INDENT", + "code indent should use tabs where possible\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } } # check for space before tabs. if ($rawline =~ /^\+/ && $rawline =~ / \t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - WARN("SPACE_BEFORE_TAB", - "please, no space before tabs\n" . $herevet); + if (WARN("SPACE_BEFORE_TAB", + "please, no space before tabs\n" . $herevet) && + $fix) { + while ($fixed[$fixlinenr] =~ + s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {} + while ($fixed[$fixlinenr] =~ + s/(^\+.*) +\t/$1\t/) {} + } + } + +# check for assignments on the start of a line + if ($sline =~ /^\+\s+($Assignment)[^=]/) { + CHK("ASSIGNMENT_CONTINUATIONS", + "Assignment operator '$1' should be on the previous line\n" . $hereprev); } # check for && or || at the start of a line @@ -1804,40 +3273,191 @@ sub process { "Logical continuations should be on the previous line\n" . $hereprev); } +# check indentation starts on a tab stop + if ($perl_version_ok && + $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) { + my $indent = length($1); + if ($indent % $tabsize) { + if (WARN("TABSTOP", + "Statements should start on a tabstop\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e; + } + } + } + # check multi-line statement indentation matches previous line - if ($^V && $^V ge 5.10.0 && - $prevline =~ /^\+(\t*)(if \(|$Ident\().*(\&\&|\|\||,)\s*$/) { + if ($perl_version_ok && + $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; my $rest = $2; my $pos = pos_last_openparen($rest); if ($pos >= 0) { - $line =~ /^\+([ \t]*)/; - my $newindent = $1; + $line =~ /^(\+| )([ \t]*)/; + my $newindent = $2; my $goodtabindent = $oldindent . - "\t" x ($pos / 8) . - " " x ($pos % 8); + "\t" x ($pos / $tabsize) . + " " x ($pos % $tabsize); my $goodspaceindent = $oldindent . " " x $pos; if ($newindent ne $goodtabindent && $newindent ne $goodspaceindent) { - CHK("PARENTHESIS_ALIGNMENT", - "Alignment should match open parenthesis\n" . $hereprev); + + if (CHK("PARENTHESIS_ALIGNMENT", + "Alignment should match open parenthesis\n" . $hereprev) && + $fix && $line =~ /^\+/) { + $fixed[$fixlinenr] =~ + s/^\+[ \t]*/\+$goodtabindent/; + } } } } - if ($line =~ /^\+.*\*[ \t]*\)[ \t]+/) { - CHK("SPACING", - "No space is necessary after a cast\n" . $hereprev); +# check for space after cast like "(int) foo" or "(struct foo) bar" +# avoid checking a few false positives: +# "sizeof(<type>)" or "__alignof__(<type>)" +# function pointer declarations like "(*foo)(int) = bar;" +# structure definitions like "(struct foo) { 0 };" +# multiline macros that define functions +# known attributes or the __attribute__ keyword + if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && + (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { + if (CHK("SPACING", + "No space is necessary after a cast\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/(\(\s*$Type\s*\))[ \t]+/$1/; + } } - if ($rawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $prevrawline =~ /^\+[ \t]*$/) { - CHK("BLOCK_COMMENT_STYLE", - "Don't begin block comments with only a /* line, use /* comment...\n" . $hereprev); +# Block comment styles +# Networking with an initial /* + if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && + $rawline =~ /^\+[ \t]*\*/ && + $realline > 2) { + WARN("NETWORKING_BLOCK_COMMENT_STYLE", + "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); + } + +# Block comments use * on subsequent lines + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $prevrawline =~ /^\+.*?\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline =~ /^\+/ && #line is new + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("BLOCK_COMMENT_STYLE", + "Block comments use * on subsequent lines\n" . $hereprev); + } + +# Block comments use */ on trailing lines + if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ + $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ + $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ + WARN("BLOCK_COMMENT_STYLE", + "Block comments use a trailing */ on a separate line\n" . $herecurr); + } + +# Block comment * alignment + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $line =~ /^\+[ \t]*$;/ && #leading comment + $rawline =~ /^\+[ \t]*\*/ && #leading * + (($prevrawline =~ /^\+.*?\/\*/ && #leading /* + $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ + $prevrawline =~ /^\+[ \t]*\*/)) { #leading * + my $oldindent; + $prevrawline =~ m@^\+([ \t]*/?)\*@; + if (defined($1)) { + $oldindent = expand_tabs($1); + } else { + $prevrawline =~ m@^\+(.*/?)\*@; + $oldindent = expand_tabs($1); + } + $rawline =~ m@^\+([ \t]*)\*@; + my $newindent = $1; + $newindent = expand_tabs($newindent); + if (length($oldindent) ne length($newindent)) { + WARN("BLOCK_COMMENT_STYLE", + "Block comments should align the * on each line\n" . $hereprev); + } + } + +# check for missing blank lines after struct/union declarations +# with exceptions for various attributes and macros + if ($prevline =~ /^[\+ ]};?\s*$/ && + $line =~ /^\+/ && + !($line =~ /^\+\s*$/ || + $line =~ /^\+\s*EXPORT_SYMBOL/ || + $line =~ /^\+\s*MODULE_/i || + $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || + $line =~ /^\+[a-z_]*init/ || + $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || + $line =~ /^\+\s*DECLARE/ || + $line =~ /^\+\s*builtin_[\w_]*driver/ || + $line =~ /^\+\s*__setup/)) { + if (CHK("LINE_SPACING", + "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for multiple consecutive blank lines + if ($prevline =~ /^[\+ ]\s*$/ && + $line =~ /^\+\s*$/ && + $last_blank_line != ($linenr - 1)) { + if (CHK("LINE_SPACING", + "Please don't use multiple blank lines\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + + $last_blank_line = $linenr; + } + +# check for missing blank lines after declarations + if ($sline =~ /^\+\s+\S/ && #Not at char 1 + # actual declarations + ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $prevline =~ /^\+\s+$declaration_macros/) && + # for "else if" which can look like "$Ident $Ident" + !($prevline =~ /^\+\s+$c90_Keywords\b/ || + # other possible extensions of declaration lines + $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || + # not starting a section or a macro "\" extended line + $prevline =~ /(?:\{\s*|\\)$/) && + # looks like a declaration + !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $sline =~ /^\+\s+$declaration_macros/ || + # start of struct or union or enum + $sline =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ || + # start or end of block or continuation of declaration + $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || + # bitfield continuation + $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || + # other possible extensions of declaration lines + $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && + # indentation of previous and current line are the same + (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { + if (WARN("LINE_SPACING", + "Missing a blank line after declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } } # check for spaces at the beginning of a line. @@ -1845,39 +3465,77 @@ sub process { # 1) within comments # 2) indented preprocessor commands # 3) hanging labels - if ($rawline =~ /^\+ / && $line !~ /\+ *(?:$;|#|$Ident:)/) { + if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - WARN("LEADING_SPACE", - "please, no spaces at the start of a line\n" . $herevet); + if (WARN("LEADING_SPACE", + "please, no spaces at the start of a line\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } } # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); +# check for unusual line ending [ or ( + if ($line =~ /^\+.*([\[\(])\s*$/) { + CHK("OPEN_ENDED_LINE", + "Lines should not end with a '$1'\n" . $herecurr); + } + +# check if this appears to be the start function declaration, save the name + if ($sline =~ /^\+\{\s*$/ && + $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { + $context_function = $1; + } + +# check if this appears to be the end of function declaration + if ($sline =~ /^\+\}\s*$/) { + undef $context_function; + } + +# check indentation of any line with a bare else +# (but not if it is a multiple line "if (foo) return bar; else return baz;") +# if the previous line is a break or return and is indented 1 tab more... + if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { + my $tabs = length($1) + 1; + if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || + ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && + defined $lines[$linenr] && + $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { + WARN("UNNECESSARY_ELSE", + "else is not generally useful after a break or return\n" . $hereprev); + } + } + +# check indentation of a line with a break; +# if the previous line is a goto or return and is indented the same # of tabs + if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { + my $tabs = $1; + if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { + WARN("UNNECESSARY_BREAK", + "break is not useful after a goto or return\n" . $hereprev); + } + } + # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS_KEYWORD", "CVS style keyword markers, these will _not_ be updated\n". $herecurr); } -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("CSYNC", - "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("SSYNC", - "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); +# check for old HOTPLUG __dev<foo> section markings + if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { + WARN("HOTPLUG_SECTION", + "Using $1 is unnecessary\n" . $herecurr); } # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); #print "LINE<$line>\n"; - if ($linenr >= $suppress_statement && - $realcnt && $line =~ /.\s*\S/) { + if ($linenr > $suppress_statement && + $realcnt && $sline =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; @@ -1977,7 +3635,7 @@ sub process { # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else - if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); @@ -2004,7 +3662,7 @@ sub process { #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); @@ -2023,7 +3681,7 @@ sub process { } # Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); @@ -2031,15 +3689,22 @@ sub process { substr($s, 0, length($c), ''); - # Make sure we remove the line prefixes as we have - # none on the first line, and are going to readd them - # where necessary. - $s =~ s/\n./\n/gs; + # remove inline comments + $s =~ s/$;/ /g; + $c =~ s/$;/ /g; # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + while ($s =~ /\n\s+\\\n/) { + $cond_lines += $s =~ s/\n\s+\\\n/\n/g; + } + # We want to check the first line inside the block # starting at the end of the conditional, so remove: # 1) any blank line termination @@ -2105,8 +3770,12 @@ sub process { #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; - if ($check && (($sindent % 8) != 0 || - ($sindent <= $indent && $s ne ''))) { + if ($check && $s ne '' && + (($sindent % $tabsize) != 0 || + ($sindent < $indent) || + ($sindent == $indent && + ($s !~ /^\s*(?:\}|\{|else\b)/)) || + ($sindent > $indent + $tabsize))) { WARN("SUSPECT_CODE_INDENT", "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); } @@ -2126,7 +3795,43 @@ sub process { $prev_values = substr($curr_values, -1); #ignore lines not being added - if ($line=~/^[^\+]/) {next;} + next if ($line =~ /^[^\+]/); + +# check for dereferences that span multiple lines + if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && + $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { + $prevline =~ /($Lval\s*(?:\.|->))\s*$/; + my $ref = $1; + $line =~ /^.\s*($Lval)/; + $ref .= $1; + $ref =~ s/\s//g; + WARN("MULTILINE_DEREFERENCE", + "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev); + } + +# check for declarations of signed or unsigned without int + while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { + my $type = $1; + my $var = $2; + $var = "" if (!defined $var); + if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { + my $sign = $1; + my $pointer = $2; + + $pointer = "" if (!defined $pointer); + + if (WARN("UNSPECIFIED_INT", + "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && + $fix) { + my $decl = trim($sign) . " int "; + my $comp_pointer = $pointer; + $comp_pointer =~ s/\s//g; + $decl .= $comp_pointer; + $decl = rtrim($decl) if ($var eq ""); + $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; + } + } + } # TEST: allow direct testing of the type matcher. if ($dbg_type) { @@ -2154,8 +3859,18 @@ sub process { # check for initialisation to aggregates open brace on the next line if ($line =~ /^.\s*{/ && $prevline =~ /(?:^|[^=])=\s*$/) { - ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . $hereprev); + if (ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/\s*=\s*$/ = {/; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $line; + $fixedline =~ s/^(.\s*)\{\s*/$1/; + fix_insert_line($fixlinenr, $fixedline); + } } # @@ -2167,15 +3882,25 @@ sub process { my $path = $1; if ($path =~ m{//}) { ERROR("MALFORMED_INCLUDE", - "malformed #include filename\n" . - $herecurr); + "malformed #include filename\n" . $herecurr); + } + if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { + ERROR("UAPI_INCLUDE", + "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); } } # no C99 // comments if ($line =~ m{//}) { - ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr); + if (ERROR("C99_COMMENTS", + "do not use C99 // comments\n" . $herecurr) && + $fix) { + my $line = $fixed[$fixlinenr]; + if ($line =~ /\/\/(.*)$/) { + my $comment = trim($1); + $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; + } + } } # Remove C99 comments. $line =~ s@//.*@@; @@ -2227,16 +3952,48 @@ sub process { } # check for global initialisers. - if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { - ERROR("GLOBAL_INITIALISERS", - "do not initialise globals to 0 or NULL\n" . - $herecurr); + if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { + if (ERROR("GLOBAL_INITIALISERS", + "do not initialise globals to $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; + } } # check for static initialisers. - if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) { - ERROR("INITIALISED_STATIC", - "do not initialise statics to 0 or NULL\n" . - $herecurr); + if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { + if (ERROR("INITIALISED_STATIC", + "do not initialise statics to $1\n" . + $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; + } + } + +# check for misordered declarations of char/short/int/long with signed/unsigned + while ($sline =~ m{(\b$TypeMisordered\b)}g) { + my $tmp = trim($1); + WARN("MISORDERED_TYPE", + "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); + } + +# check for unnecessary <signed> int declarations of short/long/long long + while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) { + my $type = trim($1); + next if ($type !~ /\bint\b/); + next if ($type !~ /\b(?:short|long\s+long|long)\b/); + my $new_type = $type; + $new_type =~ s/\b\s*int\s*\b/ /; + $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /; + $new_type =~ s/^const\s+//; + $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/); + $new_type = "const $new_type" if ($type =~ /^const\b/); + $new_type =~ s/\s+/ /g; + $new_type = trim($new_type); + if (WARN("UNNECESSARY_INT", + "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/; + } } # check for static const char * arrays. @@ -2244,19 +4001,63 @@ sub process { WARN("STATIC_CONST_CHAR_ARRAY", "static const char * array should probably be static const char * const\n" . $herecurr); - } + } + +# check for initialized const char arrays that should be static const + if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) { + if (WARN("STATIC_CONST_CHAR_ARRAY", + "const array should probably be static const\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/; + } + } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("STATIC_CONST_CHAR_ARRAY", "static char array declaration should probably be static const char\n" . $herecurr); + } + +# check for const <foo> const where <foo> is not a pointer or array type + if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { + my $found = $1; + if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { + WARN("CONST_CONST", + "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); + } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { + WARN("CONST_CONST", + "'const $found const' should probably be 'const $found'\n" . $herecurr); + } + } + +# check for non-global char *foo[] = {"bar", ...} declarations. + if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "char * array declaration might be better as static const\n" . + $herecurr); } -# check for declarations of struct pci_device_id - if ($line =~ /\bstruct\s+pci_device_id\s+\w+\s*\[\s*\]\s*\=\s*\{/) { - WARN("DEFINE_PCI_DEVICE_TABLE", - "Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id\n" . $herecurr); +# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) + if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { + my $array = $1; + if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { + my $array_div = $1; + if (WARN("ARRAY_SIZE", + "Prefer ARRAY_SIZE($array)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; + } + } + } + +# check for function declarations without arguments like "int foo()" + if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) { + if (ERROR("FUNCTION_WITHOUT_ARGS", + "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; + } } # check for new typedefs, only function parameters and sparse annotations @@ -2265,7 +4066,7 @@ sub process { $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && $line !~ /\b$typeTypedefs\b/ && - $line !~ /\b__bitwise(?:__|)\b/) { + $line !~ /\b__bitwise\b/) { WARN("NEW_TYPEDEFS", "do not add new typedefs\n" . $herecurr); } @@ -2274,7 +4075,7 @@ sub process { # (char*[ const]) while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { #print "AA<$1>\n"; - my ($from, $to) = ($2, $2); + my ($ident, $from, $to) = ($1, $2, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; @@ -2284,15 +4085,22 @@ sub process { while ($to =~ s/\*\s+\*/\*\*/) { } - #print "from<$from> to<$to>\n"; +## print "1: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to) { - ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); + if (ERROR("POINTER_LOCATION", + "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && + $fix) { + my $sub_from = $ident; + my $sub_to = $ident; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } } } while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { #print "BB<$1>\n"; - my ($from, $to, $ident) = ($2, $2, $3); + my ($match, $from, $to, $ident) = ($1, $2, $2, $3); # Should start with a space. $to =~ s/^(\S)/ $1/; @@ -2304,20 +4112,30 @@ sub process { # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; - #print "from<$from> to<$to> ident<$ident>\n"; +## print "2: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { - ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); + if (ERROR("POINTER_LOCATION", + "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && + $fix) { + + my $sub_from = $match; + my $sub_to = $match; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } } } -# # no BUG() or BUG_ON() -# if ($line =~ /\b(BUG|BUG_ON)\b/) { -# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; -# print "$herecurr"; -# $clean = 0; -# } +# avoid BUG() or BUG_ON() + if ($line =~ /\b(?:BUG|BUG_ON)\b/) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}("AVOID_BUG", + "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); + } +# avoid LINUX_VERSION_CODE if ($line =~ /\bLINUX_VERSION_CODE\b/) { WARN("LINUX_VERSION_CODE", "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); @@ -2326,52 +4144,171 @@ sub process { # check for uses of printk_ratelimit if ($line =~ /\bprintk_ratelimit\s*\(/) { WARN("PRINTK_RATELIMITED", -"Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); + "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); } -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_ facility level\n" . $herecurr); +# printk should use KERN_* levels + if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { + WARN("PRINTK_WITHOUT_KERN_LEVEL", + "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); + } + + if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + my $level2 = $level; + $level2 = "dbg" if ($level eq "debug"); + WARN("PREFER_PR_LEVEL", + "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); + } + + if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + $level = "dbg" if ($level eq "debug"); + WARN("PREFER_DEV_LEVEL", + "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); + } + +# ENOSYS means "bad syscall nr" and nothing else. This will have a small +# number of false positives, but assembly files are not checked, so at +# least the arch entry code will not trigger this warning. + if ($line =~ /\bENOSYS\b/) { + WARN("ENOSYS", + "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); + } + +# ENOTSUPP is not a standard error code and should be avoided in new patches. +# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP. +# Similarly to ENOSYS warning a small number of false positives is expected. + if (!$file && $line =~ /\bENOTSUPP\b/) { + if (WARN("ENOTSUPP", + "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/; } } # function brace can't be on same line, except for #defines of do while, # or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s\{/) and - !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { - ERROR("OPEN_BRACE", - "open brace '{' following function declarations go on the next line\n" . $herecurr); + if ($perl_version_ok && + $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ && + $sline !~ /\#\s*define\b.*do\s*\{/ && + $sline !~ /}/) { + if (ERROR("OPEN_BRACE", + "open brace '{' following function definitions go on the next line\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + my $fixed_line = $rawline; + $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; + my $line1 = $1; + my $line2 = $2; + fix_insert_line($fixlinenr, ltrim($line1)); + fix_insert_line($fixlinenr, "\+{"); + if ($line2 !~ /^\s*$/) { + fix_insert_line($fixlinenr, "\+\t" . trim($line2)); + } + } } # open braces for enum, union and struct go on the same line. if ($line =~ /^.\s*{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { - ERROR("OPEN_BRACE", - "open brace '{' following $1 go on the same line\n" . $hereprev); + if (ERROR("OPEN_BRACE", + "open brace '{' following $1 go on the same line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = rtrim($prevrawline) . " {"; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/^(.\s*)\{\s*/$1\t/; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + } } # missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - WARN("SPACING", - "missing space after $1 definition\n" . $herecurr); + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { + if (WARN("SPACING", + "missing space after $1 definition\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; + } + } + +# Function pointer declarations +# check spacing between type, funcptr, and args +# canonical declaration is "type (*funcptr)(args...)" + if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { + my $declare = $1; + my $pre_pointer_space = $2; + my $post_pointer_space = $3; + my $funcname = $4; + my $post_funcname_space = $5; + my $pre_args_space = $6; + +# the $Declare variable will capture all spaces after the type +# so check it for a missing trailing missing space but pointer return types +# don't need a space so don't warn for those. + my $post_declare_space = ""; + if ($declare =~ /(\s+)$/) { + $post_declare_space = $1; + $declare = rtrim($declare); + } + if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { + WARN("SPACING", + "missing space after return type\n" . $herecurr); + $post_declare_space = " "; + } + +# unnecessary space "type (*funcptr)(args...)" +# This test is not currently implemented because these declarations are +# equivalent to +# int foo(int bar, ...) +# and this is form shouldn't/doesn't generate a checkpatch warning. +# +# elsif ($declare =~ /\s{2,}$/) { +# WARN("SPACING", +# "Multiple spaces after return type\n" . $herecurr); +# } + +# unnecessary space "type ( *funcptr)(args...)" + if (defined $pre_pointer_space && + $pre_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer open parenthesis\n" . $herecurr); + } + +# unnecessary space "type (* funcptr)(args...)" + if (defined $post_pointer_space && + $post_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr )(args...)" + if (defined $post_funcname_space && + $post_funcname_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr) (args...)" + if (defined $pre_args_space && + $pre_args_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer arguments\n" . $herecurr); + } + + if (show_type("SPACING") && $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; + } } # check for spacing round square brackets; allowed: @@ -2382,9 +4319,13 @@ sub process { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,]\s+$/) { - ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr); + $prefix !~ /[{,:]\s+$/) { + if (ERROR("BRACKET_SPACE", + "space prohibited before open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(\+.*?)\s+\[/$1\[/; + } } } @@ -2401,7 +4342,6 @@ sub process { __attribute__|format|__extension__| asm|__asm__)$/x) { - # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. @@ -2415,25 +4355,53 @@ sub process { } elsif ($ctx =~ /$Type$/) { } else { - WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr); + if (WARN("SPACING", + "space prohibited between function name and open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b$name\s+\(/$name\(/; + } } } + # Check operator spacing. if (!($line=~/\#\s*include/)) { + my $fixed_line = ""; + my $line_fixed = 0; + my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?|: + \?:|\?|: }x; my @elements = split(/($ops|;)/, $opline); + +## print("element count: <" . $#elements . ">\n"); +## foreach my $el (@elements) { +## print("el: <$el>\n"); +## } + + my @fix_elements = (); my $off = 0; + foreach my $el (@elements) { + push(@fix_elements, substr($rawline, $off, length($el))); + $off += length($el); + } + + $off = 0; + my $blank = copy_spacing($opline); + my $last_after = -1; for (my $n = 0; $n < $#elements; $n += 2) { + + my $good = $fix_elements[$n] . $fix_elements[$n + 1]; + +## print("n: <$n> good: <$good>\n"); + $off += length($elements[$n]); # Pick up the preceding and succeeding characters. @@ -2481,7 +4449,7 @@ sub process { # Ignore operators passed as parameters. if ($op_type ne 'V' && - $ca =~ /\s$/ && $cc =~ /^\s*,/) { + $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { # # Ignore comments # } elsif ($op =~ /^$;+$/) { @@ -2490,27 +4458,62 @@ sub process { } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { - ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } # // is a comment } elsif ($op eq '//') { + # : when part of a bitfield + } elsif ($opv eq ':B') { + # skip the bitfield test for now + # No spaces for: # -> - # : when part of a bitfield - } elsif ($op eq '->' || $opv eq ':B') { + } elsif ($op eq '->') { if ($ctx =~ /Wx.|.xW/) { - ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "spaces prohibited around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } - # , must have a space on the right. + # , must not have a space before and must have a space on the right. } elsif ($op eq ',') { + my $rtrim_before = 0; + my $space_after = 0; + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $rtrim_before = 1; + } + } if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $last_after = $n; + $space_after = 1; + } + } + if ($rtrim_before || $space_after) { + if ($rtrim_before) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + } else { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + } + if ($space_after) { + $good .= " "; + } } # '*' as part of a type definition -- reported already. @@ -2524,34 +4527,56 @@ sub process { $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required before that '$op' $at\n" . $hereptr)) { + if ($n != $last_after + 2) { + $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { - ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required one side of that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { - ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } if ($ctx =~ /ExW/) { - ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } - # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or @@ -2559,18 +4584,41 @@ sub process { $op eq '*' or $op eq '/' or $op eq '%') { - if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . - $hereptr); + if ($check) { + if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { + if (CHK("SPACING", + "spaces preferred around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $fix_elements[$n + 2] =~ s/^\s+//; + $line_fixed = 1; + } + } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { + if (CHK("SPACING", + "space preferred before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + if (ERROR("SPACING", + "need consistent spacing around '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./) { - ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } # All the others need spaces both sides. @@ -2586,18 +4634,54 @@ sub process { $ok = 1; } - # Ignore ?: - if (($opv eq ':O' && $ca =~ /\?$/) || - ($op eq '?' && $cc =~ /^:/)) { + # for asm volatile statements + # ignore a colon with another + # colon immediately before or after + if (($op eq ':') && + ($ca =~ /:$/ || $cc =~ /^:/)) { $ok = 1; } + # messages are ERROR, but ?: are CHK if ($ok == 0) { - ERROR("SPACING", - "spaces required around that '$op' $at\n" . $hereptr); + my $msg_level = \&ERROR; + $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); + + if (&{$msg_level}("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } } $off += length($elements[$n + 1]); + +## print("n: <$n> GOOD: <$good>\n"); + + $fixed_line = $fixed_line . $good; + } + + if (($#elements % 2) == 0) { + $fixed_line = $fixed_line . $fix_elements[$#elements]; + } + + if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { + $fixed[$fixlinenr] = $fixed_line; + } + + + } + +# check for whitespace before a non-naked semicolon + if ($line =~ /^\+.*\S\s+;\s*$/) { + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + 1 while $fixed[$fixlinenr] =~ + s/^(\+.*\S)\s+;/$1;/; } } @@ -2609,100 +4693,240 @@ sub process { ## # check for multiple declarations, allowing for a function declaration ## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { ## -## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("MULTIPLE_DECLARATION", +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("MULTIPLE_DECLARATION", ## "declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } +## } +## } #need space before brace following if, while, etc if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /do\{/) { - ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr); + $line =~ /\b(?:else|do)\{/) { + if (ERROR("SPACING", + "space required before the open brace '{'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/; + } } +## # check for blank lines before declarations +## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && +## $prevrawline =~ /^.\s*$/) { +## WARN("SPACING", +## "No blank lines before declarations\n" . $hereprev); +## } +## + # closing brace should have a space following it when it has anything # on the line - if ($line =~ /}(?!(?:,|;|\)))\S/) { - ERROR("SPACING", - "space required after that close brace '}'\n" . $herecurr); + if ($line =~ /}(?!(?:,|;|\)|\}))\S/) { + if (ERROR("SPACING", + "space required after that close brace '}'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/}((?!(?:,|;|\)))\S)/} $1/; + } } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited after that open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\[\s+/\[/; + } } if ($line =~ /\s\]/) { - ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited before that close square bracket ']'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\]/\]/; + } } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { - ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited after that open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\(\s+/\(/; + } } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { - ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited before that close parenthesis ')'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\)/\)/; + } + } + +# check unnecessary parentheses around addressof/dereference single $Lvals +# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar + + while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { + my $var = $1; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around $var\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; + } + } + +# check for unnecessary parentheses around function pointer uses +# ie: (foo->bar)(); should be foo->bar(); +# but not "if (foo->bar) (" to avoid some false positives + if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { + my $var = $2; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around function pointer $var\n" . $herecurr) && + $fix) { + my $var2 = deparenthesize($var); + $var2 =~ s/\s//g; + $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; + } + } + +# check for unnecessary parentheses around comparisons in if uses +# when !drivers/staging or command-line uses --strict + if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) && + $perl_version_ok && defined($stat) && + $stat =~ /(^.\s*if\s*($balanced_parens))/) { + my $if_stat = $1; + my $test = substr($2, 1, -1); + my $herectx; + while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { + my $match = $1; + # avoid parentheses around potential macro args + next if ($match =~ /^\s*\w+\s*$/); + if (!defined($herectx)) { + $herectx = $here . "\n"; + my $cnt = statement_rawlines($if_stat); + for (my $n = 0; $n < $cnt; $n++) { + my $rl = raw_line($linenr, $n); + $herectx .= $rl . "\n"; + last if $rl =~ /^[ \+].*\{/; + } + } + CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around '$match'\n" . $herectx); + } } #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { - WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr); + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.)\s+/$1/; + } } -# Return is not a function. - if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) { +# return is not a function + if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; - my $value = $2; - - # Flatten any parentheses - $value =~ s/\(/ \(/g; - $value =~ s/\)/\) /g; - while ($value =~ s/\[[^\[\]]*\]/1/ || - $value !~ /(?:$Ident|-?$Constant)\s* - $Compare\s* - (?:$Ident|-?$Constant)/x && - $value =~ s/\([^\(\)]*\)/1/) { - } -#print "value<$value>\n"; - if ($value =~ /^\s*(?:$Ident|-?$Constant)\s*$/) { - ERROR("RETURN_PARENTHESES", - "return is not a function, parentheses are not required\n" . $herecurr); - + if ($perl_version_ok && + $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { + my $value = $1; + $value = deparenthesize($value); + if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { + ERROR("RETURN_PARENTHESES", + "return is not a function, parentheses are not required\n" . $herecurr); + } } elsif ($spacing !~ /\s+/) { ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); } } -# Return of what appears to be an errno should normally be -'ve - if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { + +# unnecessary return in a void function +# at end-of-function, with the previous line a single leading tab, then return; +# and the line before that not a goto label target like "out:" + if ($sline =~ /^[ \+]}\s*$/ && + $prevline =~ /^\+\treturn\s*;\s*$/ && + $linenr >= 3 && + $lines[$linenr - 3] =~ /^[ +]/ && + $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { + WARN("RETURN_VOID", + "void function return statements are not generally useful\n" . $hereprev); + } + +# if statements using unnecessary parentheses - ie: if ((foo == bar)) + if ($perl_version_ok && + $line =~ /\bif\s*((?:\(\s*){2,})/) { + my $openparens = $1; + my $count = $openparens =~ tr@\(@\(@; + my $msg = ""; + if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { + my $comp = $4; #Not $1 because of $LvalOrFunc + $msg = " - maybe == should be = ?" if ($comp eq "=="); + WARN("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses$msg\n" . $herecurr); + } + } + +# comparisons with a constant or upper case identifier on the left +# avoid cases like "foo + BAR < baz" +# only fix matches surrounded by parentheses to avoid incorrect +# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" + if ($perl_version_ok && + $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { + my $lead = $1; + my $const = $2; + my $comp = $3; + my $to = $4; + my $newcomp = $comp; + if ($lead !~ /(?:$Operators|\.)\s*$/ && + $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && + WARN("CONSTANT_COMPARISON", + "Comparisons should place the constant on the right side of the test\n" . $herecurr) && + $fix) { + if ($comp eq "<") { + $newcomp = ">"; + } elsif ($comp eq "<=") { + $newcomp = ">="; + } elsif ($comp eq ">") { + $newcomp = "<"; + } elsif ($comp eq ">=") { + $newcomp = "<="; + } + $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; + } + } + +# Return of what appears to be an errno should normally be negative + if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR') { WARN("USE_NEGATIVE_ERRNO", - "return of an errno should typically be -ve (return -$1)\n" . $herecurr); + "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); } } # Need a space before open parenthesis after if, while etc - if ($line=~/\b(if|while|for|switch)\(/) { - ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); + if ($line =~ /\b(if|while|for|switch)\(/) { + if (ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b(if|while|for|switch)\(/$1 \(/; + } } # Check for illegal assignment in if conditional -- and check for trailing @@ -2729,6 +4953,7 @@ sub process { } } if (!defined $suppress_whiletrailers{$linenr} && + defined($stat) && defined($cond) && $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); @@ -2789,7 +5014,7 @@ sub process { # if should not continue a brace if ($line =~ /}\s*if\b/) { ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . + "trailing statements should be on next line (or did you mean 'else if'?)\n" . $herecurr); } # case and default should not have general statements after them @@ -2805,14 +5030,26 @@ sub process { # Check for }<nl>else {, these must be at the same # indent level to be relevant to each other. - if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and - $previndent == $indent) { - ERROR("ELSE_AFTER_BRACE", - "else should follow close brace '}'\n" . $hereprev); + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && + $previndent == $indent) { + if (ERROR("ELSE_AFTER_BRACE", + "else should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/}\s*$//; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $fixedline = $rawline; + $fixedline =~ s/^(.\s*)else/$1} else/; + fix_insert_line($fixlinenr, $fixedline); + } } - if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and - $previndent == $indent) { + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && + $previndent == $indent) { my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); # Find out what is on the end of the line after the @@ -2821,25 +5058,65 @@ sub process { $s =~ s/\n.*//g; if ($s =~ /^\s*;/) { - ERROR("WHILE_AFTER_BRACE", - "while should follow close brace '}'\n" . $hereprev); + if (ERROR("WHILE_AFTER_BRACE", + "while should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + my $trailing = $rawline; + $trailing =~ s/^\+//; + $trailing = trim($trailing); + $fixedline =~ s/}\s*$/} $trailing/; + fix_insert_line($fixlinenr, $fixedline); + } } } -#studly caps, commented out until figure out how to distinguish between use of existing and adding new -# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { -# print "No studly caps, use _\n"; -# print "$herecurr"; -# $clean = 0; -# } +#Specific variable tests + while ($line =~ m{($Constant|$Lval)}g) { + my $var = $1; -#no spaces allowed after \ in define - if ($line=~/\#\s*define.*\\\s$/) { - WARN("WHITESPACE_AFTER_LINE_CONTINUATION", - "Whitepspace after \\ makes next lines useless\n" . $herecurr); +#CamelCase + if ($var !~ /^$Constant$/ && + $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore Page<foo> variants + $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && +#Ignore SI style variants like nS, mV and dB +#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE) + $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ && +#Ignore some three character SI units explicitly, like MiB and KHz + $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { + while ($var =~ m{($Ident)}g) { + my $word = $1; + next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); + if ($check) { + seed_camelcase_includes(); + if (!$file && !$camelcase_file_seeded) { + seed_camelcase_file($realfile); + $camelcase_file_seeded = 1; + } + } + if (!defined $camelcase{$word}) { + $camelcase{$word} = 1; + CHK("CAMELCASE", + "Avoid CamelCase: <$word>\n" . $herecurr); + } + } + } } -#warn if <asm/foo.h> is #included and <linux/foo.h> is available (uses RAW line) +#no spaces allowed after \ in define + if ($line =~ /\#\s*define.*\\\s+$/) { + if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", + "Whitespace after \\ makes next lines useless\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + } + +# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes +# itself <asm/foo.h> (uses RAW line) if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { my $file = "$1.h"; my $checkfile = "include/linux/$file"; @@ -2847,12 +5124,15 @@ sub process { $realfile ne $checkfile && $1 !~ /$allowed_asm_includes/) { - if ($realfile =~ m{^arch/}) { - CHK("ARCH_INCLUDE_LINUX", - "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } else { - WARN("INCLUDE_LINUX", - "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; + if ($asminclude > 0) { + if ($realfile =~ m{^arch/}) { + CHK("ARCH_INCLUDE_LINUX", + "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } else { + WARN("INCLUDE_LINUX", + "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } } } } @@ -2866,13 +5146,29 @@ sub process { my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; + my $has_flow_statement = 0; + my $has_arg_concat = 0; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; - $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//; + $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); + $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); + + $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//; + my $define_args = $1; + my $define_stmt = $dstat; + my @def_args = (); + + if (defined $define_args && $define_args ne "") { + $define_args = substr($define_args, 1, length($define_args) - 2); + $define_args =~ s/\s*//g; + $define_args =~ s/\\\+?//g; + @def_args = split(",", $define_args); + } + $dstat =~ s/$;//g; $dstat =~ s/\\\n.//g; $dstat =~ s/^\s*//s; @@ -2881,68 +5177,169 @@ sub process { # Flatten any parentheses and braces while ($dstat =~ s/\([^\(\)]*\)/1/ || $dstat =~ s/\{[^\{\}]*\}/1/ || - $dstat =~ s/\[[^\[\]]*\]/1/) + $dstat =~ s/.\[[^\[\]]*\]/1/) { } - # Flatten any obvious string concatentation. - while ($dstat =~ s/("X*")\s*$Ident/$1/ || - $dstat =~ s/$Ident\s*("X*")/$1/) + # Flatten any obvious string concatenation. + while ($dstat =~ s/($String)\s*$Ident/$1/ || + $dstat =~ s/$Ident\s*($String)/$1/) { } + # Make asm volatile uses seem like a generic function + $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; + my $exceptions = qr{ $Declare| module_param_named| - MODULE_PARAM_DESC| + MODULE_PARM_DESC| DECLARE_PER_CPU| DEFINE_PER_CPU| __typeof__\(| union| struct| \.$Ident\s*=\s*| - ^\"|\"$ + ^\"|\"$| + ^\[ }x; #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; + + $ctx =~ s/\n*$//; + my $stmt_cnt = statement_rawlines($ctx); + my $herectx = get_stat_here($linenr, $stmt_cnt, $here); + if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Ident|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo - $dstat !~ /^'X'$/ && # character constants + $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz + $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && # .foo = + $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) $dstat !~ /^for\s*$Constant$/ && # for (...) $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() $dstat !~ /^do\s*{/ && # do {... - $dstat !~ /^\(\{/) # ({... + $dstat !~ /^\(\{/ && # ({... + $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) { - $ctx =~ s/\n*$//; - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - if ($dstat =~ /;/) { + if ($dstat =~ /^\s*if\b/) { + ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", + "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); + } elsif ($dstat =~ /;/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); } else { ERROR("COMPLEX_MACRO", - "Macros with complex values should be enclosed in parenthesis\n" . "$herectx"); + "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); } + + } + + # Make $define_stmt single line, comment-free, etc + my @stmt_array = split('\n', $define_stmt); + my $first = 1; + $define_stmt = ""; + foreach my $l (@stmt_array) { + $l =~ s/\\$//; + if ($first) { + $define_stmt = $l; + $first = 0; + } elsif ($l =~ /^[\+ ]/) { + $define_stmt .= substr($l, 1); + } + } + $define_stmt =~ s/$;//g; + $define_stmt =~ s/\s+/ /g; + $define_stmt = trim($define_stmt); + +# check if any macro arguments are reused (ignore '...' and 'type') + foreach my $arg (@def_args) { + next if ($arg =~ /\.\.\./); + next if ($arg =~ /^type$/i); + my $tmp_stmt = $define_stmt; + $tmp_stmt =~ s/\b(sizeof|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; + $tmp_stmt =~ s/\#+\s*$arg\b//g; + $tmp_stmt =~ s/\b$arg\s*\#\#//g; + my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g; + if ($use_cnt > 1) { + CHK("MACRO_ARG_REUSE", + "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); + } +# check if any macro arguments may have other precedence issues + if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && + ((defined($1) && $1 ne ',') || + (defined($2) && $2 ne ','))) { + CHK("MACRO_ARG_PRECEDENCE", + "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx"); + } + } + +# check for macros with flow control, but without ## concatenation +# ## concatenation is commonly a macro that defines a function so ignore those + if ($has_flow_statement && !$has_arg_concat) { + my $cnt = statement_rawlines($ctx); + my $herectx = get_stat_here($linenr, $cnt, $here); + + WARN("MACRO_WITH_FLOW_CONTROL", + "Macros with flow control statements should be avoided\n" . "$herectx"); + } + +# check for line continuations outside of #defines, preprocessor #, and asm + + } else { + if ($prevline !~ /^..*\\$/ && + $line !~ /^\+\s*\#.*\\$/ && # preprocessor + $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm + $line =~ /^\+.*\\$/) { + WARN("LINE_CONTINUATIONS", + "Avoid unnecessary line continuations\n" . $herecurr); } } -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("MISSING_VMLINUX_SYMBOL", - "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); +# do {} while (0) macro tests: +# single-statement macros do not need to be enclosed in do while (0) loop, +# macro should not end with a semicolon + if ($perl_version_ok && + $realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + + $dstat =~ s/\\\n.//g; + $dstat =~ s/$;/ /g; + + if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { + my $stmts = $2; + my $semis = $3; + + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = get_stat_here($linenr, $cnt, $here); + + if (($stmts =~ tr/;/;/) == 1 && + $stmts !~ /^\s*(if|while|for|switch)\b/) { + WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", + "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); + } + if (defined $semis && $semis ne "") { + WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", + "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); + } + } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = get_stat_here($linenr, $cnt, $here); + + WARN("TRAILING_SEMICOLON", + "macros should not use a trailing semicolon\n" . "$herectx"); + } } # check for redundant bracing round if etc @@ -3051,55 +5448,343 @@ sub process { } } if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $herectx = $here . "\n"; my $cnt = statement_rawlines($block); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); WARN("BRACES", "braces {} are not necessary for single statement blocks\n" . $herectx); } } +# check for single line unbalanced braces + if ($sline =~ /^.\s*\}\s*else\s*$/ || + $sline =~ /^.\s*else\s*\{\s*$/) { + CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); + } + +# check for unnecessary blank lines around braces + if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && + $fix && $prevrawline =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + } + } + if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + } + # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("VOLATILE", - "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr); + } + +# Check for user-visible strings broken across lines, which breaks the ability +# to grep for the string. Make exceptions when the previous string ends in a +# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' +# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value + if ($line =~ /^\+\s*$String/ && + $prevline =~ /"\s*$/ && + $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { + if (WARN("SPLIT_STRING", + "quoted string split across lines\n" . $hereprev) && + $fix && + $prevrawline =~ /^\+.*"\s*$/ && + $last_coalesced_string_linenr != $linenr - 1) { + my $extracted_string = get_quoted_string($line, $rawline); + my $comma_close = ""; + if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { + $comma_close = $1; + } + + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/"\s*$//; + $fixedline .= substr($extracted_string, 1) . trim($comma_close); + fix_insert_line($fixlinenr - 1, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; + if ($fixedline !~ /\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $last_coalesced_string_linenr = $linenr; + } + } + +# check for missing a space in a string concatenation + if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { + WARN('MISSING_SPACE', + "break quoted strings at a space character\n" . $hereprev); + } + +# check for an embedded function name in a string when the function is known +# This does not work very well for -f --file checking as it depends on patch +# context providing the function name or a single line form for in-file +# function declarations + if ($line =~ /^\+.*$String/ && + defined($context_function) && + get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && + length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { + WARN("EMBEDDED_FUNCTION_NAME", + "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", + "unnecessary whitespace before a quoted newline\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; + } + + } + +# concatenated string without spaces between elements + if ($line =~ /$String[A-Za-z0-9_]/ || $line =~ /[A-Za-z0-9_]$String/) { + if (CHK("CONCATENATED_STRING", + "Concatenated strings should use spaces between elements\n" . $herecurr) && + $fix) { + while ($line =~ /($String)/g) { + my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); + $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/; + $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/; + } + } + } + +# uncoalesced string fragments + if ($line =~ /$String\s*"/) { + if (WARN("STRING_FRAGMENTS", + "Consecutive strings are generally better as a single string\n" . $herecurr) && + $fix) { + while ($line =~ /($String)(?=\s*")/g) { + my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); + $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e; + } + } + } + +# check for non-standard and hex prefixed decimal printf formats + my $show_L = 1; #don't show the same defect twice + my $show_Z = 1; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + my $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + # check for %L + if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { + WARN("PRINTF_L", + "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); + $show_L = 0; + } + # check for %Z + if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { + WARN("PRINTF_Z", + "%Z$1 is non-standard C, use %z$1\n" . $herecurr); + $show_Z = 0; + } + # check for 0x<decimal> + if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { + ERROR("PRINTF_0XDECIMAL", + "Prefixing 0x with decimal output is defective\n" . $herecurr); + } + } + +# check for line continuations in quoted strings with odd counts of " + if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) { + WARN("LINE_CONTINUATIONS", + "Avoid line continuations in quoted strings\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("REDUNDANT_CODE", - "if this code is redundant consider removing it\n" . - $herecurr); + WARN("IF_0", + "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr); } -# check for needless kfree() checks - if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { - my $expr = $1; - if ($line =~ /\bkfree\(\Q$expr\E\);/) { - WARN("NEEDLESS_KFREE", - "kfree(NULL) is safe this check is probably not required\n" . $hereprev); +# warn about #if 1 + if ($line =~ /^.\s*\#\s*if\s+1\b/) { + WARN("IF_1", + "Consider removing the #if 1 and its #endif\n" . $herecurr); + } + +# check for needless "if (<foo>) fn(<foo>)" uses + if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { + my $tested = quotemeta($1); + my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; + if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { + my $func = $1; + if (WARN('NEEDLESS_IF', + "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && + $fix) { + my $do_fix = 1; + my $leading_tabs = ""; + my $new_leading_tabs = ""; + if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { + $leading_tabs = $1; + } else { + $do_fix = 0; + } + if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { + $new_leading_tabs = $1; + if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { + $do_fix = 0; + } + } else { + $do_fix = 0; + } + if ($do_fix) { + fix_delete_line($fixlinenr - 1, $prevrawline); + $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; + } + } } } -# check for needless usb_free_urb() checks - if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { - my $expr = $1; - if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { - WARN("NEEDLESS_USB_FREE_URB", - "usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); + +# check for unnecessary "Out of Memory" messages + if ($line =~ /^\+.*\b$logFunctions\s*\(/ && + $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && + (defined $1 || defined $3) && + $linenr > 3) { + my $testval = $2; + my $testline = $lines[$linenr - 3]; + + my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); +# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); + + if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ && + $s !~ /\b__GFP_NOWARN\b/ ) { + WARN("OOM_MESSAGE", + "Possible unnecessary 'out of memory' message\n" . $hereprev); + } + } + +# check for logging functions with KERN_<LEVEL> + if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && + $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { + my $level = $1; + if (WARN("UNNECESSARY_KERN_LEVEL", + "Possible unnecessary $level\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s*$level\s*//; + } + } + +# check for logging continuations + if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { + WARN("LOGGING_CONTINUATION", + "Avoid logging continuation uses where feasible\n" . $herecurr); + } + +# check for mask then right shift without a parentheses + if ($perl_version_ok && + $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && + $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so + WARN("MASK_THEN_SHIFT", + "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); + } + +# check for pointer comparisons to NULL + if ($perl_version_ok) { + while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { + my $val = $1; + my $equal = "!"; + $equal = "" if ($4 eq "!="); + if (CHK("COMPARISON_TO_NULL", + "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; + } + } + } + +# check for bad placement of section $InitAttribute (e.g.: __initdata) + if ($line =~ /(\b$InitAttribute\b)/) { + my $attr = $1; + if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { + my $ptr = $1; + my $var = $2; + if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && + ERROR("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr)) || + ($ptr !~ /\b(union|struct)\s+$attr\b/ && + WARN("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr))) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; + } + } + } + +# check for $InitAttributeData (ie: __initdata) with const + if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { + my $attr = $1; + $attr =~ /($InitAttributePrefix)(.*)/; + my $attr_prefix = $1; + my $attr_type = $2; + if (ERROR("INIT_ATTRIBUTE", + "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/$InitAttributeData/${attr_prefix}initconst/; + } + } + +# check for $InitAttributeConst (ie: __initconst) without const + if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { + my $attr = $1; + if (ERROR("INIT_ATTRIBUTE", + "Use of $attr requires a separate use of const\n" . $herecurr) && + $fix) { + my $lead = $fixed[$fixlinenr] =~ + /(^\+\s*(?:static\s+))/; + $lead = rtrim($1); + $lead = "$lead " if ($lead !~ /^\+$/); + $lead = "${lead}const "; + $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; + } + } + +# check for __read_mostly with const non-pointer (should just be const) + if ($line =~ /\b__read_mostly\b/ && + $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { + if (ERROR("CONST_READ_MOSTLY", + "Invalid use of __read_mostly with const type\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; + } + } + +# don't use __constant_<foo> functions outside of include/uapi/ + if ($realfile !~ m@^include/uapi/@ && + $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { + my $constant_func = $1; + my $func = $constant_func; + $func =~ s/^__constant_//; + if (WARN("CONSTANT_CONVERSION", + "$constant_func should be $func\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; } } # prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) { + if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + my $delay = $1; # ignore udelay's < 10, however - if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) { + if (! ($delay < 10) ) { CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); + "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr); + } + if ($delay > 2000) { + WARN("LONG_UDELAY", + "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); } } @@ -3107,10 +5792,22 @@ sub process { if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); + "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr); } } +# check for comparisons of jiffies + if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { + WARN("JIFFIES_COMPARISON", + "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); + } + +# check for comparisons of get_jiffies_64() + if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { + WARN("JIFFIES_COMPARISON", + "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); + } + # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; @@ -3120,8 +5817,13 @@ sub process { # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr); + if (ERROR("SPACING", + "exactly one space required after that #$1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; + } + } # check for spinlock_t definitions without a comment. @@ -3134,22 +5836,84 @@ sub process { } } # check for memory barriers without a comment. - if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + + my $barriers = qr{ + mb| + rmb| + wmb| + read_barrier_depends + }x; + my $barrier_stems = qr{ + mb__before_atomic| + mb__after_atomic| + store_release| + load_acquire| + store_mb| + (?:$barriers) + }x; + my $all_barriers = qr{ + (?:$barriers)| + smp_(?:$barrier_stems)| + virt_(?:$barrier_stems) + }x; + + if ($line =~ /\b(?:$all_barriers)\s*\(/) { if (!ctx_has_comment($first_line, $linenr)) { - CHK("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); + WARN("MEMORY_BARRIER", + "memory barrier without comment\n" . $herecurr); } } + + my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; + + if ($realfile !~ m@^include/asm-generic/@ && + $realfile !~ m@/barrier\.h$@ && + $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && + $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { + WARN("MEMORY_BARRIER", + "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); + } + +# check for waitqueue_active without a comment. + if ($line =~ /\bwaitqueue_active\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("WAITQUEUE_ACTIVE", + "waitqueue_active without comment\n" . $herecurr); + } + } + +# check for data_race without a comment. + if ($line =~ /\bdata_race\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("DATA_RACE", + "data_race without comment\n" . $herecurr); + } + } + +# check for smp_read_barrier_depends and read_barrier_depends + if (!$file && $line =~ /\b(smp_|)read_barrier_depends\s*\(/) { + WARN("READ_BARRIER_DEPENDS", + "$1read_barrier_depends should only be used in READ_ONCE or DEC Alpha code\n" . $herecurr); + } + # check of hardware specific defines if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { CHK("ARCH_DEFINES", "architecture specific defines should be avoided\n" . $herecurr); } -# Check that the storage class is at the beginning of a declaration - if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { +# check that the storage class is not after a type + if ($line =~ /\b($Type)\s+($Storage)\b/) { WARN("STORAGE_CLASS", - "storage class should be at the beginning of the declaration\n" . $herecurr) + "storage class '$2' should be located before type '$1'\n" . $herecurr); + } +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && + $line !~ /^.\s*$Storage/ && + $line =~ /^.\s*(.+?)\$Storage\s/ && + $1 !~ /[\,\)]\s*$/) { + WARN("STORAGE_CLASS", + "storage class should be at the beginning of the declaration\n" . $herecurr); } # check the location of the inline attribute, that it is between @@ -3161,33 +5925,109 @@ sub process { } # Check for __inline__ and __inline, prefer inline - if ($line =~ /\b(__inline__|__inline)\b/) { - WARN("INLINE", - "plain inline is preferred over $1\n" . $herecurr); + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b(__inline__|__inline)\b/) { + if (WARN("INLINE", + "plain inline is preferred over $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; + + } } # Check for __attribute__ packed, prefer __packed - if ($line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { WARN("PREFER_PACKED", "__packed is preferred over __attribute__((packed))\n" . $herecurr); } # Check for __attribute__ aligned, prefer __aligned - if ($line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { WARN("PREFER_ALIGNED", "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); } +# Check for __attribute__ section, prefer __section + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*_*section_*\s*\(\s*("[^"]*")/) { + my $old = substr($rawline, $-[1], $+[1] - $-[1]); + my $new = substr($old, 1, -1); + if (WARN("PREFER_SECTION", + "__section($new) is preferred over __attribute__((section($old)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*\Q$old\E\s*\)\s*\)\s*\)/__section($new)/; + } + } + # Check for __attribute__ format(printf, prefer __printf - if ($line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { - WARN("PREFER_PRINTF", - "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr); + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { + if (WARN("PREFER_PRINTF", + "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; + + } } # Check for __attribute__ format(scanf, prefer __scanf - if ($line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { - WARN("PREFER_SCANF", - "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr); + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { + if (WARN("PREFER_SCANF", + "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; + } + } + +# Check for __attribute__ weak, or __weak declarations (may have link issues) + if ($perl_version_ok && + $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && + ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || + $line =~ /\b__weak\b/)) { + ERROR("WEAK_DECLARATION", + "Using weak declarations can have unintended link defects\n" . $herecurr); + } + +# check for c99 types like uint8_t used outside of uapi/ and tools/ + if ($realfile !~ m@\binclude/uapi/@ && + $realfile !~ m@\btools/@ && + $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { + my $type = $1; + if ($type =~ /\b($typeC99Typedefs)\b/) { + $type = $1; + my $kernel_type = 'u'; + $kernel_type = 's' if ($type =~ /^_*[si]/); + $type =~ /(\d+)/; + $kernel_type .= $1; + if (CHK("PREFER_KERNEL_TYPES", + "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; + } + } + } + +# check for cast of C90 native int or longer types constants + if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { + my $cast = $1; + my $const = $2; + if (WARN("TYPECAST_INT_CONSTANT", + "Unnecessary typecast of c90 int constant\n" . $herecurr) && + $fix) { + my $suffix = ""; + my $newconst = $const; + $newconst =~ s/${Int_type}$//; + $suffix .= 'U' if ($cast =~ /\bunsigned\b/); + if ($cast =~ /\blong\s+long\b/) { + $suffix .= 'LL'; + } elsif ($cast =~ /\blong\b/) { + $suffix .= 'L'; + } + $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; + } } # check for sizeof(&) @@ -3196,16 +6036,88 @@ sub process { "sizeof(& should be avoided\n" . $herecurr); } -# check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { - WARN("LINE_CONTINUATIONS", - "Avoid line continuations in quoted strings\n" . $herecurr); +# check for sizeof without parenthesis + if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { + if (WARN("SIZEOF_PARENTHESIS", + "sizeof $1 should be sizeof($1)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; + } + } + +# check for struct spinlock declarations + if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { + WARN("USE_SPINLOCK_T", + "struct spinlock should be spinlock_t\n" . $herecurr); + } + +# check for seq_printf uses that could be seq_puts + if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { + my $fmt = get_quoted_string($line, $rawline); + $fmt =~ s/%%//g; + if ($fmt !~ /%/) { + if (WARN("PREFER_SEQ_PUTS", + "Prefer seq_puts to seq_printf\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; + } + } + } + +# check for vsprintf extension %p<foo> misuses + if ($perl_version_ok && + defined $stat && + $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && + $1 !~ /^_*volatile_*$/) { + my $stat_real; + + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + for (my $count = $linenr; $count <= $lc; $count++) { + my $specifier; + my $extension; + my $qualifier; + my $bad_specifier = ""; + my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); + $fmt =~ s/%%//g; + + while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) { + $specifier = $1; + $extension = $2; + $qualifier = $3; + if ($extension !~ /[SsBKRraEehMmIiUDdgVCbGNOxtf]/ || + ($extension eq "f" && + defined $qualifier && $qualifier !~ /^w/)) { + $bad_specifier = $specifier; + last; + } + if ($extension eq "x" && !defined($stat_real)) { + if (!defined($stat_real)) { + $stat_real = get_stat_real($linenr, $lc); + } + WARN("VSPRINTF_SPECIFIER_PX", + "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n"); + } + } + if ($bad_specifier ne "") { + my $stat_real = get_stat_real($linenr, $lc); + my $ext_type = "Invalid"; + my $use = ""; + if ($bad_specifier =~ /p[Ff]/) { + $use = " - use %pS instead"; + $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/); + } + + WARN("VSPRINTF_POINTER_EXTENSION", + "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n"); + } + } } # Check for misused memsets - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/s) { + $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { my $ms_addr = $2; my $ms_val = $7; @@ -3220,8 +6132,50 @@ sub process { } } +# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) +# if ($perl_version_ok && +# defined $stat && +# $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { +# if (WARN("PREFER_ETHER_ADDR_COPY", +# "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && +# $fix) { +# $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; +# } +# } + +# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) +# if ($perl_version_ok && +# defined $stat && +# $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { +# WARN("PREFER_ETHER_ADDR_EQUAL", +# "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") +# } + +# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr +# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr +# if ($perl_version_ok && +# defined $stat && +# $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { +# +# my $ms_val = $7; +# +# if ($ms_val =~ /^(?:0x|)0+$/i) { +# if (WARN("PREFER_ETH_ZERO_ADDR", +# "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && +# $fix) { +# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; +# } +# } elsif ($ms_val =~ /^(?:0xff|255)$/i) { +# if (WARN("PREFER_ETH_BROADCAST_ADDR", +# "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && +# $fix) { +# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; +# } +# } +# } + # typecasts on min/max could be min_t/max_t - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (defined $2 || defined $7) { @@ -3244,6 +6198,64 @@ sub process { } } +# check usleep_range arguments + if ($perl_version_ok && + defined $stat && + $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { + my $min = $1; + my $max = $7; + if ($min eq $max) { + WARN("USLEEP_RANGE", + "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); + } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && + $min > $max) { + WARN("USLEEP_RANGE", + "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); + } + } + +# check for naked sscanf + if ($perl_version_ok && + defined $stat && + $line =~ /\bsscanf\b/ && + ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && + $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && + $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = get_stat_real($linenr, $lc); + WARN("NAKED_SSCANF", + "unchecked sscanf return value\n" . "$here\n$stat_real\n"); + } + +# check for simple sscanf that should be kstrto<foo> + if ($perl_version_ok && + defined $stat && + $line =~ /\bsscanf\b/) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = get_stat_real($linenr, $lc); + if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { + my $format = $6; + my $count = $format =~ tr@%@%@; + if ($count == 1 && + $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { + WARN("SSCANF_TO_KSTRTO", + "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); + } + } + } + +# check for new externs in .h files. + if ($realfile =~ /\.h$/ && + $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { + if (CHK("AVOID_EXTERNS", + "extern prototypes should be avoided in .h files\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; + } + } + # check for new externs in .c files. if ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) @@ -3274,22 +6286,206 @@ sub process { "externs should be avoided in .c files\n" . $herecurr); } -# check for pointless casting of kmalloc return - if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { +# check for function declarations that have arguments without identifier names +# while avoiding uninitialized_var(x) + if (defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:($Ident)|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && + (!defined($1) || + (defined($1) && $1 ne "uninitialized_var")) && + $2 ne "void") { + my $args = trim($2); + while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { + my $arg = trim($1); + if ($arg =~ /^$Type$/ && + $arg !~ /enum\s+$Ident$/) { + WARN("FUNCTION_ARGUMENTS", + "function definition argument '$arg' should also have an identifier name\n" . $herecurr); + } + } + } + +# check for function definitions + if ($perl_version_ok && + defined $stat && + $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { + $context_function = $1; + +# check for multiline function definition with misplaced open brace + my $ok = 0; + my $cnt = statement_rawlines($stat); + my $herectx = $here . "\n"; + for (my $n = 0; $n < $cnt; $n++) { + my $rl = raw_line($linenr, $n); + $herectx .= $rl . "\n"; + $ok = 1 if ($rl =~ /^[ \+]\{/); + $ok = 1 if ($rl =~ /\{/ && $n == 0); + last if $rl =~ /^[ \+].*\{/; + } + if (!$ok) { + ERROR("OPEN_BRACE", + "open brace '{' following function definitions go on the next line\n" . $herectx); + } + } + +# check for pointless casting of alloc functions + if ($line =~ /\*\s*\)\s*$allocFunctions\b/) { WARN("UNNECESSARY_CASTS", "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } +# alloc style +# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) + if ($perl_version_ok && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + CHK("ALLOC_SIZEOF_STRUCT", + "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); + } + +# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc + if ($perl_version_ok && + defined $stat && + $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { + my $oldfunc = $3; + my $a1 = $4; + my $a2 = $10; + my $newfunc = "kmalloc_array"; + $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); + my $r1 = $a1; + my $r2 = $a2; + if ($a1 =~ /^sizeof\s*\S/) { + $r1 = $a2; + $r2 = $a1; + } + if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && + !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { + my $cnt = statement_rawlines($stat); + my $herectx = get_stat_here($linenr, $cnt, $here); + + if (WARN("ALLOC_WITH_MULTIPLY", + "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && + $cnt == 1 && + $fix) { + $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; + } + } + } + +# check for krealloc arg reuse + if ($perl_version_ok && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ && + $1 eq $3) { + WARN("KREALLOC_ARG_REUSE", + "Reusing the krealloc arg is almost always a bug\n" . $herecurr); + } + +# check for alloc argument mismatch + if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { + WARN("ALLOC_ARRAY_ARGS", + "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); + } + # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { - WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr); + if (WARN("ONE_SEMICOLON", + "Statements terminations use 1 semicolon\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; + } + } + +# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi + if ($realfile !~ m@^include/uapi/@ && + $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { + my $ull = ""; + $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); + if (CHK("BIT_MACRO", + "Prefer using the BIT$ull macro\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; + } + } + +# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE + if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { + my $config = $1; + if (WARN("PREFER_IS_ENABLED", + "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; + } + } + +# check for case / default statements not preceded by break/fallthrough/switch + if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { + my $has_break = 0; + my $has_statement = 0; + my $count = 0; + my $prevline = $linenr; + while ($prevline > 1 && ($file || $count < 3) && !$has_break) { + $prevline--; + my $rline = $rawlines[$prevline - 1]; + my $fline = $lines[$prevline - 1]; + last if ($fline =~ /^\@\@/); + next if ($fline =~ /^\-/); + next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); + $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); + next if ($fline =~ /^.[\s$;]*$/); + $has_statement = 1; + $count++; + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/); + } + if (!$has_break && $has_statement) { + WARN("MISSING_BREAK", + "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr); + } + } + +# check for /* fallthrough */ like comment, prefer fallthrough; + my @fallthroughs = ( + 'fallthrough', + '@fallthrough@', + 'lint -fallthrough[ \t]*', + 'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)', + '(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?', + 'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', + 'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', + ); + if ($raw_comment ne '') { + foreach my $ft (@fallthroughs) { + if ($raw_comment =~ /$ft/) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}("PREFER_FALLTHROUGH", + "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr); + last; + } + } + } + +# check for switch/default statements without a break; + if ($perl_version_ok && + defined $stat && + $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { + my $cnt = statement_rawlines($stat); + my $herectx = get_stat_here($linenr, $cnt, $here); + + WARN("DEFAULT_NO_BREAK", + "switch default: should use break\n" . $herectx); } # check for gcc specific __FUNCTION__ - if ($line =~ /__FUNCTION__/) { - WARN("USE_FUNC", - "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); + if ($line =~ /\b__FUNCTION__\b/) { + if (WARN("USE_FUNC", + "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; + } + } + +# check for uses of __DATE__, __TIME__, __TIMESTAMP__ + while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { + ERROR("DATE_TIME", + "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); } # check for use of yield() @@ -3298,6 +6494,33 @@ sub process { "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); } +# check for comparisons against true and false + if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { + my $lead = $1; + my $arg = $2; + my $test = $3; + my $otype = $4; + my $trail = $5; + my $op = "!"; + + ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); + + my $type = lc($otype); + if ($type =~ /^(?:true|false)$/) { + if (("$test" eq "==" && "$type" eq "true") || + ("$test" eq "!=" && "$type" eq "false")) { + $op = ""; + } + + CHK("BOOL_COMPARISON", + "Using comparison to $otype is error prone\n" . $herecurr); + +## maybe suggesting a correct construct would better +## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); + + } + } + # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", @@ -3310,55 +6533,32 @@ sub process { "$1 is obsolete, use k$3 instead\n" . $herecurr); } -# check for __initcall(), use device_initcall() explicitly please +# check for __initcall(), use device_initcall() explicitly or more appropriate function please if ($line =~ /^.\s*__initcall\s*\(/) { WARN("USE_DEVICE_INITCALL", - "please use device_initcall() instead of __initcall()\n" . $herecurr); + "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); } -# check for various ops structs, ensure they are const. - my $struct_ops = qr{acpi_dock_ops| - address_space_operations| - backlight_ops| - block_device_operations| - dentry_operations| - dev_pm_ops| - dma_map_ops| - extent_io_ops| - file_lock_operations| - file_operations| - hv_ops| - ide_dma_ops| - intel_dvo_dev_ops| - item_operations| - iwl_ops| - kgdb_arch| - kgdb_io| - kset_uevent_ops| - lock_manager_operations| - microcode_ops| - mtrr_ops| - neigh_ops| - nlmsvc_binding| - pci_raw_ops| - pipe_buf_operations| - platform_hibernation_ops| - platform_suspend_ops| - proto_ops| - rpc_pipe_ops| - seq_operations| - snd_ac97_build_ops| - soc_pcmcia_socket_ops| - stacktrace_ops| - sysfs_ops| - tty_operations| - usb_mon_operations| - wd_ops}x; +# check for spin_is_locked(), suggest lockdep instead + if ($line =~ /\bspin_is_locked\(/) { + WARN("USE_LOCKDEP", + "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr); + } + +# check for deprecated apis + if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) { + my $deprecated_api = $1; + my $new_api = $deprecated_apis{$deprecated_api}; + WARN("DEPRECATED_API", + "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr); + } + +# check for various structs that are normally const (ops, kgdb, device_tree) +# and avoid what seem like struct definitions 'struct foo {' if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($struct_ops)\b/) { + $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . - $herecurr); + "struct $1 should normally be const\n" . $herecurr); } # use of NR_CPUS is usually wrong @@ -3374,16 +6574,23 @@ sub process { "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); } -# check for %L{u,d,i} in strings - my $string; - while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { - $string = substr($rawline, $-[1], $+[1] - $-[1]); - $string =~ s/%%/__/g; - if ($string =~ /(?<!%)%L[udi]/) { - WARN("PRINTF_L", - "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); - last; - } +# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. + if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { + ERROR("DEFINE_ARCH_HAS", + "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); + } + +# likely/unlikely comparisons similar to "(likely(foo) > 0)" + if ($perl_version_ok && + $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { + WARN("LIKELY_MISUSE", + "Using $1 should generally have parentheses around the comparison\n" . $herecurr); + } + +# nested likely/unlikely calls + if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) { + WARN("LIKELY_MISUSE", + "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr); } # whine mightly about in_atomic @@ -3397,6 +6604,12 @@ sub process { } } +# check for mutex_trylock_recursive usage + if ($line =~ /mutex_trylock_recursive/) { + ERROR("LOCKING", + "recursive locking is bad, do not use this ever.\n" . $herecurr); + } + # check for lockdep_set_novalidate_class if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || $line =~ /__lockdep_no_validate__\s*\)/ ) { @@ -3408,11 +6621,143 @@ sub process { } } - if ($line =~ /debugfs_create_file.*S_IWUGO/ || - $line =~ /DEVICE_ATTR.*S_IWUGO/ ) { + if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || + $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { WARN("EXPORTED_WORLD_WRITABLE", "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } + +# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO> +# and whether or not function naming is typical and if +# DEVICE_ATTR permissions uses are unusual too + if ($perl_version_ok && + defined $stat && + $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) { + my $var = $1; + my $perms = $2; + my $show = $3; + my $store = $4; + my $octal_perms = perms_to_octal($perms); + if ($show =~ /^${var}_show$/ && + $store =~ /^${var}_store$/ && + $octal_perms eq "0644") { + if (WARN("DEVICE_ATTR_RW", + "Use DEVICE_ATTR_RW\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/; + } + } elsif ($show =~ /^${var}_show$/ && + $store =~ /^NULL$/ && + $octal_perms eq "0444") { + if (WARN("DEVICE_ATTR_RO", + "Use DEVICE_ATTR_RO\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/; + } + } elsif ($show =~ /^NULL$/ && + $store =~ /^${var}_store$/ && + $octal_perms eq "0200") { + if (WARN("DEVICE_ATTR_WO", + "Use DEVICE_ATTR_WO\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/; + } + } elsif ($octal_perms eq "0644" || + $octal_perms eq "0444" || + $octal_perms eq "0200") { + my $newshow = "$show"; + $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show"); + my $newstore = $store; + $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store"); + my $rename = ""; + if ($show ne $newshow) { + $rename .= " '$show' to '$newshow'"; + } + if ($store ne $newstore) { + $rename .= " '$store' to '$newstore'"; + } + WARN("DEVICE_ATTR_FUNCTIONS", + "Consider renaming function(s)$rename\n" . $herecurr); + } else { + WARN("DEVICE_ATTR_PERMS", + "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr); + } + } + +# Mode permission misuses where it seems decimal should be octal +# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop +# o Ignore module_param*(...) uses with a decimal 0 permission as that has a +# specific definition of not visible in sysfs. +# o Ignore proc_create*(...) uses with a decimal 0 permission as that means +# use the default permissions + if ($perl_version_ok && + defined $stat && + $line =~ /$mode_perms_search/) { + foreach my $entry (@mode_permission_funcs) { + my $func = $entry->[0]; + my $arg_pos = $entry->[1]; + + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = get_stat_real($linenr, $lc); + + my $skip_args = ""; + if ($arg_pos > 1) { + $arg_pos--; + $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; + } + my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]"; + if ($stat =~ /$test/) { + my $val = $1; + $val = $6 if ($skip_args ne ""); + if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") && + (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + ($val =~ /^$Octal$/ && length($val) ne 4))) { + ERROR("NON_OCTAL_PERMISSIONS", + "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); + } + if ($val =~ /^$Octal$/ && (oct($val) & 02)) { + ERROR("EXPORTED_WORLD_WRITABLE", + "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real); + } + } + } + } + +# check for uses of S_<PERMS> that could be octal for readability + while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) { + my $oval = $1; + my $octal = perms_to_octal($oval); + if (WARN("SYMBOLIC_PERMS", + "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/; + } + } + +# validate content of MODULE_LICENSE against list from include/linux/module.h + if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { + my $extracted_string = get_quoted_string($line, $rawline); + my $valid_licenses = qr{ + GPL| + GPL\ v2| + GPL\ and\ additional\ rights| + Dual\ BSD/GPL| + Dual\ MIT/GPL| + Dual\ MPL/GPL| + Proprietary + }x; + if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { + WARN("MODULE_LICENSE", + "unknown module license " . $extracted_string . "\n" . $herecurr); + } + } + +# check for sysctl duplicate constants + if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) { + WARN("DUPLICATED_SYSCTL_CONST", + "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); + } } # If we have no input at all, then there is nothing to report on @@ -3433,13 +6778,18 @@ sub process { exit(0); } - if (!$is_patch) { + if (!$is_patch && $filename !~ /cover-letter\.patch$/) { ERROR("NOT_UNIFIED_DIFF", "Does not appear to be a unified-diff format patch\n"); } - if ($is_patch && $chk_signoff && $signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); + if ($is_patch && $has_commit_log && $chk_signoff) { + if ($signoff == 0) { + ERROR("MISSING_SIGN_OFF", + "Missing Signed-off-by: line(s)\n"); + } elsif (!$authorsignoff) { + WARN("NO_AUTHOR_SIGN_OFF", + "Missing Signed-off-by: line by nominal patch author '$author'\n"); + } } print report_dump(); @@ -3448,44 +6798,75 @@ sub process { print "total: $cnt_error errors, $cnt_warn warnings, " . (($check)? "$cnt_chk checks, " : "") . "$cnt_lines lines checked\n"; - print "\n" if ($quiet == 0); } if ($quiet == 0) { + # If there were any defects found and not already fixing them + if (!$clean and !$fix) { + print << "EOM" - if ($^V lt 5.10.0) { - print("NOTE: perl $^V is not modern enough to detect all possible issues.\n"); - print("An upgrade to at least perl v5.10.0 is suggested.\n\n"); +NOTE: For some of the reported defects, checkpatch may be able to + mechanically convert to the typical style using --fix or --fix-inplace. +EOM } - # If there were whitespace errors which cleanpatch can fix # then suggest that. if ($rpt_cleaners) { - print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n"; - print " scripts/cleanfile\n\n"; $rpt_cleaners = 0; + print << "EOM" + +NOTE: Whitespace errors detected. + You may wish to use scripts/cleanpatch or scripts/cleanfile +EOM } } - if ($quiet == 0 && keys %ignore_type) { - print "NOTE: Ignored message types:"; - foreach my $ignore (sort keys %ignore_type) { - print " $ignore"; - } - print "\n\n"; - } + if ($clean == 0 && $fix && + ("@rawlines" ne "@fixed" || + $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { + my $newfile = $filename; + $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); + my $linecount = 0; + my $f; - if ($clean == 1 && $quiet == 0) { - print "$vname has no obvious style problems and is ready for submission.\n" - } - if ($clean == 0 && $quiet == 0) { - print << "EOM"; -$vname has style problems, please review. + @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); -If any of these errors are false positives, please report -them to the maintainer, see CHECKPATCH in MAINTAINERS. + open($f, '>', $newfile) + or die "$P: Can't open $newfile for write\n"; + foreach my $fixed_line (@fixed) { + $linecount++; + if ($file) { + if ($linecount > 3) { + $fixed_line =~ s/^\+//; + print $f $fixed_line . "\n"; + } + } else { + print $f $fixed_line . "\n"; + } + } + close($f); + + if (!$quiet) { + print << "EOM"; + +Wrote EXPERIMENTAL --fix correction(s) to '$newfile' + +Do _NOT_ trust the results written to this file. +Do _NOT_ submit these changes without inspecting them for correctness. + +This EXPERIMENTAL file is simply a convenience to help rewrite patches. +No warranties, expressed or implied... EOM + } } + if ($quiet == 0) { + print "\n"; + if ($clean == 1) { + print "$vname has no obvious style problems and is ready for submission.\n"; + } else { + print "$vname has style problems, please review.\n"; + } + } return $clean; } diff --git a/scripts/json_add_image_info.py b/scripts/json_add_image_info.py index 768bb10254..ac907c777c 100755 --- a/scripts/json_add_image_info.py +++ b/scripts/json_add_image_info.py @@ -44,6 +44,7 @@ image_info = { "target": "{}/{}".format(getenv("TARGET"), getenv("SUBTARGET")), "version_code": getenv("VERSION_CODE"), "version_number": getenv("VERSION_NUMBER"), + "source_date_epoch": getenv("SOURCE_DATE_EPOCH"), "profiles": { device_id: { "image_prefix": getenv("IMAGE_PREFIX"), diff --git a/scripts/kconfig.pl b/scripts/kconfig.pl index 6a6bbd2747..392f1d5841 100755 --- a/scripts/kconfig.pl +++ b/scripts/kconfig.pl @@ -102,8 +102,15 @@ sub config_sub($$) { my $cfg1 = shift; my $cfg2 = shift; my %config = %{$cfg1}; - - foreach my $config (keys %$cfg2) { + my @keys = map { + my $expr = $_; + $expr =~ /[?.*]/ ? + map { + /^$expr$/ ? $_ : () + } keys %config : $expr; + } keys %$cfg2; + + foreach my $config (@keys) { delete $config{$config}; } return \%config; diff --git a/scripts/spelling.txt b/scripts/spelling.txt new file mode 100644 index 0000000000..c45e9afaab --- /dev/null +++ b/scripts/spelling.txt @@ -0,0 +1,1517 @@ +# Originally from Debian's Lintian tool. Various false positives have been +# removed, and various additions have been made as they've been discovered +# in the kernel source. +# +# License: GPLv2 +# +# The format of each line is: +# mistake||correction +# +abandonning||abandoning +abigious||ambiguous +abitrate||arbitrate +abnornally||abnormally +abnrormal||abnormal +abord||abort +aboslute||absolute +abov||above +abreviated||abbreviated +absense||absence +absolut||absolute +absoulte||absolute +acccess||access +acceess||access +acceleratoin||acceleration +accelleration||acceleration +accesing||accessing +accesnt||accent +accessable||accessible +accesss||access +accidentaly||accidentally +accidentually||accidentally +acclerated||accelerated +accoding||according +accomodate||accommodate +accomodates||accommodates +accordign||according +accoring||according +accout||account +accquire||acquire +accquired||acquired +accross||across +accumalate||accumulate +accumalator||accumulator +acessable||accessible +acess||access +acessing||accessing +achitecture||architecture +acient||ancient +acitions||actions +acitve||active +acknowldegement||acknowledgment +acknowledgement||acknowledgment +ackowledge||acknowledge +ackowledged||acknowledged +acording||according +activete||activate +actived||activated +actualy||actually +acumulating||accumulating +acumulative||accumulative +acumulator||accumulator +acutally||actually +adapater||adapter +addional||additional +additionaly||additionally +additonal||additional +addres||address +adddress||address +addreses||addresses +addresss||address +addrress||address +aditional||additional +aditionally||additionally +aditionaly||additionally +adminstrative||administrative +adress||address +adresses||addresses +adrresses||addresses +advertisment||advertisement +adviced||advised +afecting||affecting +againt||against +agaist||against +aggreataon||aggregation +aggreation||aggregation +albumns||albums +alegorical||allegorical +algined||aligned +algorith||algorithm +algorithmical||algorithmically +algoritm||algorithm +algoritms||algorithms +algorithmn||algorithm +algorrithm||algorithm +algorritm||algorithm +aligment||alignment +alignement||alignment +allign||align +alligned||aligned +alllocate||allocate +alloated||allocated +allocatote||allocate +allocatrd||allocated +allocte||allocate +allpication||application +alocate||allocate +alogirhtms||algorithms +alogrithm||algorithm +alot||a lot +alow||allow +alows||allows +alreay||already +alredy||already +altough||although +alue||value +ambigious||ambiguous +ambigous||ambiguous +amoung||among +amout||amount +amplifer||amplifier +amplifyer||amplifier +an union||a union +an user||a user +an userspace||a userspace +an one||a one +analysator||analyzer +ang||and +anniversery||anniversary +annoucement||announcement +anomolies||anomalies +anomoly||anomaly +anway||anyway +aplication||application +appearence||appearance +applicaion||application +appliction||application +applictions||applications +applys||applies +appplications||applications +appropiate||appropriate +appropriatly||appropriately +approriate||appropriate +approriately||appropriately +apropriate||appropriate +aquainted||acquainted +aquired||acquired +aquisition||acquisition +arbitary||arbitrary +architechture||architecture +arguement||argument +arguements||arguments +aritmetic||arithmetic +arne't||aren't +arraival||arrival +artifical||artificial +artillary||artillery +asign||assign +asser||assert +assertation||assertion +assertting||asserting +assiged||assigned +assigment||assignment +assigments||assignments +assistent||assistant +assocation||association +associcated||associated +assotiated||associated +asssert||assert +assum||assume +assumtpion||assumption +asuming||assuming +asycronous||asynchronous +asynchnous||asynchronous +asynchromous||asynchronous +asymetric||asymmetric +asymmeric||asymmetric +atomatically||automatically +atomicly||atomically +atempt||attempt +attachement||attachment +attatch||attach +attched||attached +attemp||attempt +attemps||attempts +attemping||attempting +attepmpt||attempt +attnetion||attention +attruibutes||attributes +authentification||authentication +authenicated||authenticated +automaticaly||automatically +automaticly||automatically +automatize||automate +automatized||automated +automatizes||automates +autonymous||autonomous +auxillary||auxiliary +auxilliary||auxiliary +avaiable||available +avaible||available +availabe||available +availabled||available +availablity||availability +availaible||available +availale||available +availavility||availability +availble||available +availiable||available +availible||available +avalable||available +avaliable||available +aysnc||async +backgroud||background +backword||backward +backwords||backwards +bahavior||behavior +bakup||backup +baloon||balloon +baloons||balloons +bandwith||bandwidth +banlance||balance +batery||battery +beacuse||because +becasue||because +becomming||becoming +becuase||because +beeing||being +befor||before +begining||beginning +beter||better +betweeen||between +bianries||binaries +bitmast||bitmask +boardcast||broadcast +borad||board +boundry||boundary +brievely||briefly +brigde||bridge +broadcase||broadcast +broadcat||broadcast +bufer||buffer +bufufer||buffer +cacluated||calculated +caculate||calculate +caculation||calculation +cadidate||candidate +cahces||caches +calender||calendar +calescing||coalescing +calle||called +callibration||calibration +callled||called +callser||caller +calucate||calculate +calulate||calculate +cancelation||cancellation +cancle||cancel +capabilites||capabilities +capabilties||capabilities +capabilty||capability +capabitilies||capabilities +capablity||capability +capatibilities||capabilities +capapbilities||capabilities +caputure||capture +carefuly||carefully +cariage||carriage +catagory||category +cehck||check +challange||challenge +challanges||challenges +chache||cache +chanell||channel +changable||changeable +chanined||chained +channle||channel +channnel||channel +charachter||character +charachters||characters +charactor||character +charater||character +charaters||characters +charcter||character +chcek||check +chck||check +checksumed||checksummed +checksuming||checksumming +childern||children +childs||children +chiled||child +chked||checked +chnage||change +chnages||changes +chnnel||channel +choosen||chosen +chouse||chose +circumvernt||circumvent +claread||cleared +clared||cleared +closeing||closing +clustred||clustered +cnfiguration||configuration +coexistance||coexistence +colescing||coalescing +collapsable||collapsible +colorfull||colorful +comand||command +comit||commit +commerical||commercial +comming||coming +comminucation||communication +commited||committed +commiting||committing +committ||commit +commoditiy||commodity +comsume||consume +comsumer||consumer +comsuming||consuming +compability||compatibility +compaibility||compatibility +comparsion||comparison +compatability||compatibility +compatable||compatible +compatibililty||compatibility +compatibiliy||compatibility +compatibilty||compatibility +compatiblity||compatibility +competion||completion +compilant||compliant +compleatly||completely +completition||completion +completly||completely +complient||compliant +componnents||components +compoment||component +comppatible||compatible +compres||compress +compresion||compression +comression||compression +comunication||communication +conbination||combination +conditionaly||conditionally +conditon||condition +condtion||condition +conected||connected +conector||connector +configration||configuration +configuartion||configuration +configuation||configuration +configued||configured +configuratoin||configuration +configuraton||configuration +configuretion||configuration +configutation||configuration +conider||consider +conjuction||conjunction +connecetd||connected +connectinos||connections +connetor||connector +connnection||connection +connnections||connections +consistancy||consistency +consistant||consistent +containes||contains +containts||contains +contaisn||contains +contant||contact +contence||contents +contiguos||contiguous +continious||continuous +continous||continuous +continously||continuously +continueing||continuing +contraints||constraints +contruct||construct +contol||control +contoller||controller +controled||controlled +controler||controller +controll||control +contruction||construction +contry||country +conuntry||country +convertion||conversion +convertor||converter +convienient||convenient +convinient||convenient +corected||corrected +correponding||corresponding +correponds||corresponds +correspoding||corresponding +cotrol||control +cound||could +couter||counter +coutner||counter +cryptocraphic||cryptographic +cunter||counter +curently||currently +cylic||cyclic +dafault||default +deafult||default +deamon||daemon +debouce||debounce +decendant||descendant +decendants||descendants +decompres||decompress +decsribed||described +decription||description +dectected||detected +defailt||default +deferal||deferral +deffered||deferred +defferred||deferred +definate||definite +definately||definitely +defintion||definition +defintions||definitions +defualt||default +defult||default +deintializing||deinitializing +deintialize||deinitialize +deintialized||deinitialized +deivce||device +delared||declared +delare||declare +delares||declares +delaring||declaring +delemiter||delimiter +delievered||delivered +demodualtor||demodulator +demension||dimension +dependancies||dependencies +dependancy||dependency +dependant||dependent +dependend||dependent +depreacted||deprecated +depreacte||deprecate +desactivate||deactivate +desciptor||descriptor +desciptors||descriptors +descripto||descriptor +descripton||description +descrition||description +descritptor||descriptor +desctiptor||descriptor +desriptor||descriptor +desriptors||descriptors +desination||destination +destionation||destination +destoried||destroyed +destory||destroy +destoryed||destroyed +destorys||destroys +destroied||destroyed +detabase||database +deteced||detected +develope||develop +developement||development +developped||developed +developpement||development +developper||developer +developpment||development +deveolpment||development +devided||divided +deviece||device +diable||disable +dicline||decline +dictionnary||dictionary +didnt||didn't +diferent||different +differrence||difference +diffrent||different +differenciate||differentiate +diffrentiate||differentiate +difinition||definition +digial||digital +dimention||dimension +dimesions||dimensions +disgest||digest +dispalying||displaying +diplay||display +directon||direction +direectly||directly +diregard||disregard +disassocation||disassociation +disapear||disappear +disapeared||disappeared +disappared||disappeared +disbale||disable +disbaled||disabled +disble||disable +disbled||disabled +disconnet||disconnect +discontinous||discontinuous +disharge||discharge +disnabled||disabled +dispertion||dispersion +dissapears||disappears +dissconect||disconnect +distiction||distinction +divisable||divisible +divsiors||divisors +docuentation||documentation +documantation||documentation +documentaion||documentation +documment||document +doesnt||doesn't +donwload||download +donwloading||downloading +dorp||drop +dosen||doesn +downlad||download +downlads||downloads +droped||dropped +droput||dropout +druing||during +dynmaic||dynamic +eanable||enable +eanble||enable +easilly||easily +ecspecially||especially +edditable||editable +editting||editing +efective||effective +effectivness||effectiveness +efficently||efficiently +ehther||ether +eigth||eight +elementry||elementary +eletronic||electronic +embeded||embedded +enabledi||enabled +enbale||enable +enble||enable +enchanced||enhanced +encorporating||incorporating +encrupted||encrypted +encrypiton||encryption +encryptio||encryption +endianess||endianness +enhaced||enhanced +enlightnment||enlightenment +enqueing||enqueuing +entires||entries +entites||entities +entrys||entries +enocded||encoded +enterily||entirely +enviroiment||environment +enviroment||environment +environement||environment +environent||environment +eqivalent||equivalent +equiped||equipped +equivelant||equivalent +equivilant||equivalent +eror||error +errorr||error +estbalishment||establishment +etsablishment||establishment +etsbalishment||establishment +excecutable||executable +exceded||exceeded +exceeed||exceed +excellant||excellent +execeeded||exceeded +execeeds||exceeds +exeed||exceed +exeuction||execution +existance||existence +existant||existent +exixt||exist +exlcude||exclude +exlcusive||exclusive +exmaple||example +expecially||especially +experies||expires +explicite||explicit +explicitely||explicitly +explict||explicit +explictely||explicitly +explictly||explicitly +expresion||expression +exprimental||experimental +extened||extended +extensability||extensibility +extention||extension +extenstion||extension +extracter||extractor +faied||failed +faield||failed +falied||failed +faild||failed +failded||failed +failer||failure +faill||fail +failied||failed +faillure||failure +failue||failure +failuer||failure +failng||failing +faireness||fairness +falied||failed +faliure||failure +fallbck||fallback +familar||familiar +fatser||faster +feauture||feature +feautures||features +fetaure||feature +fetaures||features +fileystem||filesystem +fimware||firmware +firmare||firmware +firmaware||firmware +firware||firmware +finanize||finalize +findn||find +finilizes||finalizes +finsih||finish +flusing||flushing +folloing||following +followign||following +followings||following +follwing||following +fonud||found +forseeable||foreseeable +forse||force +fortan||fortran +forwardig||forwarding +frambuffer||framebuffer +framming||framing +framwork||framework +frequncy||frequency +frequancy||frequency +frome||from +fucntion||function +fuction||function +fuctions||functions +fullill||fulfill +funcation||function +funcion||function +functionallity||functionality +functionaly||functionally +functionnality||functionality +functonality||functionality +funtion||function +funtions||functions +furthur||further +futhermore||furthermore +futrue||future +gatable||gateable +gateing||gating +gauage||gauge +gaurenteed||guaranteed +generiously||generously +genereate||generate +genereted||generated +genric||generic +globel||global +grabing||grabbing +grahical||graphical +grahpical||graphical +grapic||graphic +grranted||granted +guage||gauge +guarenteed||guaranteed +guarentee||guarantee +halfs||halves +hander||handler +handfull||handful +hanlde||handle +hanled||handled +happend||happened +harware||hardware +havind||having +heirarchically||hierarchically +helpfull||helpful +hexdecimal||hexadecimal +hybernate||hibernate +hierachy||hierarchy +hierarchie||hierarchy +homogenous||homogeneous +howver||however +hsould||should +hypervior||hypervisor +hypter||hyper +identidier||identifier +iligal||illegal +illigal||illegal +illgal||illegal +iomaped||iomapped +imblance||imbalance +immeadiately||immediately +immedaite||immediate +immedate||immediate +immediatelly||immediately +immediatly||immediately +immidiate||immediate +immutible||immutable +impelentation||implementation +impementated||implemented +implemantation||implementation +implemenation||implementation +implementaiton||implementation +implementated||implemented +implemention||implementation +implementd||implemented +implemetation||implementation +implemntation||implementation +implentation||implementation +implmentation||implementation +implmenting||implementing +incative||inactive +incomming||incoming +incompatabilities||incompatibilities +incompatable||incompatible +incompatble||incompatible +inconsistant||inconsistent +increas||increase +incremeted||incremented +incrment||increment +inculde||include +indendation||indentation +indended||intended +independant||independent +independantly||independently +independed||independent +indiate||indicate +indicat||indicate +inexpect||inexpected +inferface||interface +infomation||information +informatiom||information +informations||information +informtion||information +infromation||information +ingore||ignore +inital||initial +initalized||initialized +initalised||initialized +initalise||initialize +initalize||initialize +initation||initiation +initators||initiators +initialiazation||initialization +initializationg||initialization +initializiation||initialization +initialze||initialize +initialzed||initialized +initialzing||initializing +initilization||initialization +initilize||initialize +initliaze||initialize +initilized||initialized +inofficial||unofficial +inrerface||interface +insititute||institute +instace||instance +instal||install +instanciate||instantiate +instanciated||instantiated +insufficent||insufficient +inteface||interface +integreated||integrated +integrety||integrity +integrey||integrity +intendet||intended +intented||intended +interanl||internal +interchangable||interchangeable +interferring||interfering +interger||integer +intermittant||intermittent +internel||internal +interoprability||interoperability +interuupt||interrupt +interupt||interrupt +interupts||interrupts +interrface||interface +interrrupt||interrupt +interrup||interrupt +interrups||interrupts +interruptted||interrupted +interupted||interrupted +interupt||interrupt +intial||initial +intialisation||initialisation +intialised||initialised +intialise||initialise +intialization||initialization +intialized||initialized +intialize||initialize +intregral||integral +intrerrupt||interrupt +intrrupt||interrupt +intterrupt||interrupt +intuative||intuitive +inavlid||invalid +invaid||invalid +invaild||invalid +invailid||invalid +invald||invalid +invalde||invalid +invalide||invalid +invalidiate||invalidate +invalud||invalid +invididual||individual +invokation||invocation +invokations||invocations +ireelevant||irrelevant +irrelevent||irrelevant +isnt||isn't +isssue||issue +issus||issues +iteraions||iterations +iternations||iterations +itertation||iteration +itslef||itself +jave||java +jeffies||jiffies +jumpimng||jumping +juse||just +jus||just +kown||known +langage||language +langauage||language +langauge||language +langugage||language +lauch||launch +layed||laid +legnth||length +leightweight||lightweight +lengh||length +lenght||length +lenth||length +lesstiff||lesstif +libaries||libraries +libary||library +librairies||libraries +libraris||libraries +licenceing||licencing +limted||limited +logaritmic||logarithmic +loggging||logging +loggin||login +logile||logfile +loobpack||loopback +loosing||losing +losted||lost +maangement||management +machinary||machinery +maibox||mailbox +maintainance||maintenance +maintainence||maintenance +maintan||maintain +makeing||making +mailformed||malformed +malplaced||misplaced +malplace||misplace +managable||manageable +managment||management +mangement||management +manoeuvering||maneuvering +manufaucturing||manufacturing +mappping||mapping +matchs||matches +mathimatical||mathematical +mathimatic||mathematic +mathimatics||mathematics +maximium||maximum +maxium||maximum +mechamism||mechanism +meetign||meeting +memeory||memory +memmber||member +memoery||memory +ment||meant +mergable||mergeable +mesage||message +messags||messages +messgaes||messages +messsage||message +messsages||messages +metdata||metadata +micropone||microphone +microprocesspr||microprocessor +migrateable||migratable +milliseonds||milliseconds +minium||minimum +minimam||minimum +miniumum||minimum +minumum||minimum +misalinged||misaligned +miscelleneous||miscellaneous +misformed||malformed +mispelled||misspelled +mispelt||misspelt +mising||missing +mismactch||mismatch +missign||missing +missmanaged||mismanaged +missmatch||mismatch +misssing||missing +miximum||maximum +mmnemonic||mnemonic +mnay||many +modfiy||modify +modulues||modules +momery||memory +memomry||memory +monitring||monitoring +monochorome||monochrome +monochromo||monochrome +monocrome||monochrome +mopdule||module +mroe||more +mulitplied||multiplied +multidimensionnal||multidimensional +multipe||multiple +multple||multiple +mumber||number +muticast||multicast +mutilcast||multicast +mutiple||multiple +mutli||multi +nams||names +navagating||navigating +nead||need +neccecary||necessary +neccesary||necessary +neccessary||necessary +necesary||necessary +neded||needed +negaive||negative +negoitation||negotiation +negotation||negotiation +nerver||never +nescessary||necessary +nessessary||necessary +noticable||noticeable +notication||notification +notications||notifications +notifcations||notifications +notifed||notified +notity||notify +numebr||number +numner||number +obtaion||obtain +obusing||abusing +occassionally||occasionally +occationally||occasionally +occurance||occurrence +occurances||occurrences +occured||occurred +occurence||occurrence +occure||occurred +occured||occurred +occuring||occurring +offser||offset +offet||offset +offlaod||offload +offloded||offloaded +offseting||offsetting +omited||omitted +omiting||omitting +omitt||omit +ommiting||omitting +ommitted||omitted +onself||oneself +ony||only +operatione||operation +opertaions||operations +optionnal||optional +optmizations||optimizations +orientatied||orientated +orientied||oriented +orignal||original +originial||original +otherise||otherwise +ouput||output +oustanding||outstanding +overaall||overall +overhread||overhead +overlaping||overlapping +overide||override +overrided||overridden +overriden||overridden +overun||overrun +overwritting||overwriting +overwriten||overwritten +pacakge||package +pachage||package +packacge||package +packege||package +packge||package +packtes||packets +pakage||package +paket||packet +pallette||palette +paln||plan +paramameters||parameters +paramaters||parameters +paramater||parameter +parametes||parameters +parametised||parametrised +paramter||parameter +paramters||parameters +parmaters||parameters +particuarly||particularly +particularily||particularly +partion||partition +partions||partitions +partiton||partition +pased||passed +passin||passing +pathes||paths +pattrns||patterns +pecularities||peculiarities +peformance||performance +peforming||performing +peice||piece +pendantic||pedantic +peprocessor||preprocessor +perfoming||performing +perfomring||performing +periperal||peripheral +peripherial||peripheral +permissons||permissions +peroid||period +persistance||persistence +persistant||persistent +phoneticly||phonetically +plalform||platform +platfoem||platform +platfrom||platform +plattform||platform +pleaes||please +ploting||plotting +plugable||pluggable +poinnter||pointer +pointeur||pointer +poiter||pointer +posible||possible +positon||position +possibilites||possibilities +potocol||protocol +powerfull||powerful +pramater||parameter +preamle||preamble +preample||preamble +preapre||prepare +preceeded||preceded +preceeding||preceding +preceed||precede +precendence||precedence +precission||precision +preemptable||preemptible +prefered||preferred +prefferably||preferably +premption||preemption +prepaired||prepared +preperation||preparation +preprare||prepare +pressre||pressure +primative||primitive +princliple||principle +priorty||priority +privilaged||privileged +privilage||privilege +priviledge||privilege +priviledges||privileges +probaly||probably +procceed||proceed +proccesors||processors +procesed||processed +proces||process +procesing||processing +processessing||processing +processess||processes +processpr||processor +processsed||processed +processsing||processing +procteted||protected +prodecure||procedure +progamming||programming +progams||programs +progess||progress +programers||programmers +programm||program +programms||programs +progresss||progress +prohibitted||prohibited +prohibitting||prohibiting +promiscous||promiscuous +promps||prompts +pronnounced||pronounced +prononciation||pronunciation +pronouce||pronounce +pronunce||pronounce +propery||property +propigate||propagate +propigation||propagation +propogate||propagate +prosess||process +protable||portable +protcol||protocol +protecion||protection +protedcted||protected +protocoll||protocol +promixity||proximity +psudo||pseudo +psuedo||pseudo +psychadelic||psychedelic +pwoer||power +queing||queuing +quering||querying +queus||queues +randomally||randomly +raoming||roaming +reasearcher||researcher +reasearchers||researchers +reasearch||research +receieve||receive +recepient||recipient +recevied||received +receving||receiving +recieved||received +recieve||receive +reciever||receiver +recieves||receives +recogniced||recognised +recognizeable||recognizable +recommanded||recommended +recyle||recycle +redircet||redirect +redirectrion||redirection +redundacy||redundancy +reename||rename +refcounf||refcount +refence||reference +refered||referred +referenace||reference +refering||referring +refernces||references +refernnce||reference +refrence||reference +registed||registered +registerd||registered +registeration||registration +registeresd||registered +registerred||registered +registes||registers +registraration||registration +regsiter||register +regster||register +regualar||regular +reguator||regulator +regulamentations||regulations +reigstration||registration +releated||related +relevent||relevant +reloade||reload +remoote||remote +remore||remote +removeable||removable +repectively||respectively +replacable||replaceable +replacments||replacements +replys||replies +reponse||response +representaion||representation +reqeust||request +reqister||register +requestied||requested +requiere||require +requirment||requirement +requred||required +requried||required +requst||request +requsted||requested +reregisteration||reregistration +reseting||resetting +reseved||reserved +reseverd||reserved +resizeable||resizable +resouce||resource +resouces||resources +resoures||resources +responce||response +resrouce||resource +ressizes||resizes +ressource||resource +ressources||resources +restesting||retesting +resumbmitting||resubmitting +retransmited||retransmitted +retreived||retrieved +retreive||retrieve +retreiving||retrieving +retrive||retrieve +retrived||retrieved +retrun||return +retun||return +retuned||returned +reudce||reduce +reuest||request +reuqest||request +reutnred||returned +revsion||revision +rmeoved||removed +rmeove||remove +rmeoves||removes +rountine||routine +routins||routines +rquest||request +runing||running +runned||ran +runnning||running +runtine||runtime +sacrifying||sacrificing +safly||safely +safty||safety +savable||saveable +scaleing||scaling +scaned||scanned +scaning||scanning +scarch||search +schdule||schedule +seach||search +searchs||searches +secquence||sequence +secund||second +segement||segment +semaphone||semaphore +senario||scenario +senarios||scenarios +sentivite||sensitive +separatly||separately +sepcify||specify +seperated||separated +seperately||separately +seperate||separate +seperatly||separately +seperator||separator +sepperate||separate +seqeunce||sequence +seqeuncer||sequencer +seqeuencer||sequencer +sequece||sequence +sequencial||sequential +serivce||service +serveral||several +servive||service +setts||sets +settting||setting +shapshot||snapshot +shotdown||shutdown +shoud||should +shouldnt||shouldn't +shoule||should +shrinked||shrunk +siginificantly||significantly +signabl||signal +significanly||significantly +similary||similarly +similiar||similar +simlar||similar +simliar||similar +simpified||simplified +singaled||signaled +singal||signal +singed||signed +sleeped||slept +sliped||slipped +softwares||software +speach||speech +specfic||specific +specfield||specified +speciefied||specified +specifc||specific +specifed||specified +specificatin||specification +specificaton||specification +specifing||specifying +specifiying||specifying +speficied||specified +speicify||specify +speling||spelling +spinlcok||spinlock +spinock||spinlock +splitted||split +spreaded||spread +spurrious||spurious +sructure||structure +stablilization||stabilization +staically||statically +staion||station +standardss||standards +standartization||standardization +standart||standard +standy||standby +stardard||standard +staticly||statically +statuss||status +stoped||stopped +stoping||stopping +stoppped||stopped +straming||streaming +struc||struct +structres||structures +stuct||struct +strucuture||structure +stucture||structure +sturcture||structure +subdirectoires||subdirectories +suble||subtle +substract||subtract +submition||submission +suceed||succeed +succesfully||successfully +succesful||successful +successed||succeeded +successfull||successful +successfuly||successfully +sucessfully||successfully +sucess||success +superflous||superfluous +superseeded||superseded +suplied||supplied +suported||supported +suport||support +supportet||supported +suppored||supported +supportin||supporting +suppoted||supported +suppported||supported +suppport||support +supress||suppress +surpressed||suppressed +surpresses||suppresses +susbsystem||subsystem +suspeneded||suspended +suspsend||suspend +suspicously||suspiciously +swaping||swapping +switchs||switches +swith||switch +swithable||switchable +swithc||switch +swithced||switched +swithcing||switching +swithed||switched +swithing||switching +swtich||switch +syfs||sysfs +symetric||symmetric +synax||syntax +synchonized||synchronized +synchronuously||synchronously +syncronize||synchronize +syncronized||synchronized +syncronizing||synchronizing +syncronus||synchronous +syste||system +sytem||system +sythesis||synthesis +taht||that +tansmit||transmit +targetted||targeted +targetting||targeting +taskelt||tasklet +teh||the +temorary||temporary +temproarily||temporarily +temperture||temperature +thead||thread +therfore||therefore +thier||their +threds||threads +threee||three +threshhold||threshold +thresold||threshold +throught||through +trackling||tracking +troughput||throughput +thses||these +tiggers||triggers +tiggered||triggered +tipically||typically +timeing||timing +timout||timeout +tmis||this +toogle||toggle +torerable||tolerable +traking||tracking +tramsmitted||transmitted +tramsmit||transmit +tranasction||transaction +tranfer||transfer +transcevier||transceiver +transciever||transceiver +transferd||transferred +transfered||transferred +transfering||transferring +transision||transition +transmittd||transmitted +transormed||transformed +trasfer||transfer +trasmission||transmission +treshold||threshold +trigerred||triggered +trigerring||triggering +trun||turn +tunning||tuning +ture||true +tyep||type +udpate||update +uesd||used +uknown||unknown +usccess||success +usupported||unsupported +uncommited||uncommitted +unconditionaly||unconditionally +undeflow||underflow +underun||underrun +unecessary||unnecessary +unexecpted||unexpected +unexepected||unexpected +unexpcted||unexpected +unexpectd||unexpected +unexpeted||unexpected +unexpexted||unexpected +unfortunatelly||unfortunately +unifiy||unify +uniterrupted||uninterrupted +unintialized||uninitialized +unitialized||uninitialized +unkmown||unknown +unknonw||unknown +unknow||unknown +unkown||unknown +unamed||unnamed +uneeded||unneeded +unneded||unneeded +unneccecary||unnecessary +unneccesary||unnecessary +unneccessary||unnecessary +unnecesary||unnecessary +unneedingly||unnecessarily +unnsupported||unsupported +unmached||unmatched +unregester||unregister +unresgister||unregister +unrgesiter||unregister +unsinged||unsigned +unstabel||unstable +unsolicitied||unsolicited +unsuccessfull||unsuccessful +unsuported||unsupported +untill||until +unuseful||useless +unvalid||invalid +upate||update +upsupported||unsupported +usefule||useful +usefull||useful +usege||usage +usera||users +usualy||usually +usupported||unsupported +utilites||utilities +utillities||utilities +utilties||utilities +utiltity||utility +utitity||utility +utitlty||utility +vaid||valid +vaild||valid +valide||valid +variantions||variations +varible||variable +varient||variant +vaule||value +verbse||verbose +veify||verify +verisons||versions +verison||version +verson||version +vicefersa||vice-versa +virtal||virtual +virtaul||virtual +virtiual||virtual +visiters||visitors +vitual||virtual +vunerable||vulnerable +wakeus||wakeups +wathdog||watchdog +wating||waiting +wiat||wait +wether||whether +whataver||whatever +whcih||which +whenver||whenever +wheter||whether +whe||when +wierd||weird +wiil||will +wirte||write +withing||within +wnat||want +workarould||workaround +writeing||writing +writting||writing +wtih||with +zombe||zombie +zomebie||zombie diff --git a/target/linux/ath79/dts/qca9531_qxwlan_e600g-v2-16m.dts b/target/linux/ath79/dts/qca9531_qxwlan_e600g-v2-16m.dts new file mode 100644 index 0000000000..9b4c0a514d --- /dev/null +++ b/target/linux/ath79/dts/qca9531_qxwlan_e600g-v2-16m.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "qca9531_qxwlan_e600g.dtsi" + +/ { + model = "Qxwlan E600G v2 16M"; + compatible = "qxwlan,e600g-v2-16m", "qca,qca9531"; +}; + +&leds { + wlan { + label = "blue:wlan"; + gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + linux,default-trigger = "phy0tpt"; + }; +}; + +&partitions { + partition@70000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x070000 0xf90000>; + }; +}; + +&usb_phy { + status = "okay"; +}; + +&usb0 { + status = "okay"; +}; diff --git a/target/linux/ath79/dts/qca9531_qxwlan_e600g-v2-8m.dts b/target/linux/ath79/dts/qca9531_qxwlan_e600g-v2-8m.dts new file mode 100644 index 0000000000..7822ec664a --- /dev/null +++ b/target/linux/ath79/dts/qca9531_qxwlan_e600g-v2-8m.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "qca9531_qxwlan_e600g.dtsi" + +/ { + model = "Qxwlan E600G v2 8M"; + compatible = "qxwlan,e600g-v2-8m", "qca,qca9531"; +}; + +&leds { + wlan { + label = "blue:wlan"; + gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + linux,default-trigger = "phy0tpt"; + }; +}; + +&partitions { + partition@70000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x070000 0x790000>; + }; +}; + +&usb_phy { + status = "okay"; +}; + +&usb0 { + status = "okay"; +}; diff --git a/target/linux/ath79/dts/qca9531_qxwlan_e600g.dtsi b/target/linux/ath79/dts/qca9531_qxwlan_e600g.dtsi new file mode 100644 index 0000000000..6ba4ae8fdf --- /dev/null +++ b/target/linux/ath79/dts/qca9531_qxwlan_e600g.dtsi @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "qca953x.dtsi" + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/input/input.h> + +/ { + aliases { + label-mac-device = ð0; + led-boot = &led_system; + led-failsafe = &led_system; + led-running = &led_system; + led-upgrade = &led_system; + }; + + keys: keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = <KEY_RESTART>; + gpios = <&gpio 17 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; + }; + + leds: leds { + compatible = "gpio-leds"; + + led_system: system { + label = "blue:system"; + gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + default-state = "on"; + }; + + wan { + label = "green:wan"; + gpios = <&gpio 15 GPIO_ACTIVE_LOW>; + }; + + lan { + label = "green:lan"; + gpios = <&gpio 16 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&pcie0 { + status = "okay"; +}; + +&spi { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <25000000>; + + partitions: partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x000000 0x040000>; + read-only; + }; + + partition@40000 { + label = "u-boot-env"; + reg = <0x040000 0x010000>; + read-only; + }; + + pridata: partition@50000 { + label = "pri-data"; + reg = <0x050000 0x010000>; + read-only; + }; + + art: partition@60000 { + label = "art"; + reg = <0x060000 0x010000>; + read-only; + }; + }; + }; +}; + +ð0 { + status = "okay"; + + phy-handle = <&swphy4>; + + mtd-mac-address = <&pridata 0x400>; + mtd-mac-address-increment = <1>; +}; + +ð1 { + mtd-mac-address = <&pridata 0x400>; +}; + +&uart { + status = "okay"; +}; + +&wmac { + status = "okay"; + + mtd-cal-data = <&art 0x1000>; +}; diff --git a/target/linux/ath79/dts/qca9531_qxwlan_e600gac-v2-16m.dts b/target/linux/ath79/dts/qca9531_qxwlan_e600gac-v2-16m.dts new file mode 100644 index 0000000000..4be24986df --- /dev/null +++ b/target/linux/ath79/dts/qca9531_qxwlan_e600gac-v2-16m.dts @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "qca9531_qxwlan_e600g.dtsi" + +/ { + model = "Qxwlan E600GAC v2 16M"; + compatible = "qxwlan,e600gac-v2-16m", "qca,qca9531"; +}; + +&keys { + wps { + label = "wps"; + linux,code = <KEY_WPS_BUTTON>; + gpios = <&gpio 1 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; +}; + +&leds { + wlan2g { + label = "orange:wlan2g"; + gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + linux,default-trigger = "phy1tpt"; + }; + + control1 { + label = "green:control"; + gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + }; + + control2 { + label = "red:control"; + gpios = <&gpio 12 GPIO_ACTIVE_LOW>; + }; + + control3 { + label = "blue:control"; + gpios = <&gpio 14 GPIO_ACTIVE_LOW>; + }; +}; + +&partitions { + partition@70000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x070000 0xf90000>; + }; +}; diff --git a/target/linux/ath79/dts/qca9531_qxwlan_e600gac-v2-8m.dts b/target/linux/ath79/dts/qca9531_qxwlan_e600gac-v2-8m.dts new file mode 100644 index 0000000000..826c46b2a1 --- /dev/null +++ b/target/linux/ath79/dts/qca9531_qxwlan_e600gac-v2-8m.dts @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "qca9531_qxwlan_e600g.dtsi" + +/ { + model = "Qxwlan E600GAC v2 8M"; + compatible = "qxwlan,e600gac-v2-8m", "qca,qca9531"; +}; + +&keys { + wps { + label = "wps"; + linux,code = <KEY_WPS_BUTTON>; + gpios = <&gpio 1 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; +}; + +&leds { + wlan2g { + label = "orange:wlan2g"; + gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + linux,default-trigger = "phy1tpt"; + }; + + control1 { + label = "green:control"; + gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + }; + + control2 { + label = "red:control"; + gpios = <&gpio 12 GPIO_ACTIVE_LOW>; + }; + + control3 { + label = "blue:control"; + gpios = <&gpio 14 GPIO_ACTIVE_LOW>; + }; +}; + +&partitions { + partition@70000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x070000 0x790000>; + }; +}; diff --git a/target/linux/ath79/generic/base-files/etc/board.d/01_leds b/target/linux/ath79/generic/base-files/etc/board.d/01_leds index f22d78398b..41d93a6fce 100755 --- a/target/linux/ath79/generic/base-files/etc/board.d/01_leds +++ b/target/linux/ath79/generic/base-files/etc/board.d/01_leds @@ -195,6 +195,10 @@ etactica,eg200) ucidef_set_led_oneshot "modbus" "Modbus" "red:modbus" "100" "33" ;; glinet,gl-mifi|\ +qxwlan,e600g-v2-8m|\ +qxwlan,e600g-v2-16m|\ +qxwlan,e600gac-v2-8m|\ +qxwlan,e600gac-v2-16m|\ qxwlan,e750a-v4-8m|\ qxwlan,e750a-v4-16m) ucidef_set_led_switch "lan" "LAN" "green:lan" "switch0" "0x02" diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/11-ath10k-caldata index 20d04eb39b..e7b4439ead 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/11-ath10k-caldata +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/11-ath10k-caldata @@ -24,6 +24,8 @@ case "$FIRMWARE" in devolo,magic-2-wifi|\ qxwlan,e1700ac-v2-8m|\ qxwlan,e1700ac-v2-16m|\ + qxwlan,e600gac-v2-8m|\ + qxwlan,e600gac-v2-16m|\ ubnt,unifiac-lite|\ ubnt,unifiac-lr|\ ubnt,unifiac-mesh|\ diff --git a/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration b/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration index 0efc2bf9d7..231afd9f57 100644 --- a/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration +++ b/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration @@ -24,6 +24,14 @@ qxwlan,e558-v2-16m|\ qxwlan,e558-v2-8m) migrate_leds ":qss=:sig2" ;; +qxwlan,e600g-v2-16m|\ +qxwlan,e600g-v2-8m) + migrate_leds "blue:wan=blue:wlan" + ;; +qxwlan,e600gac-v2-16m|\ +qxwlan,e600gac-v2-8m) + migrate_leds "orange:wan=orange:wlan2g" "green:system=blue:system" + ;; qxwlan,e750a-v4-16m|\ qxwlan,e750a-v4-8m|\ qxwlan,e750g-v8-16m|\ diff --git a/target/linux/ath79/image/generic.mk b/target/linux/ath79/image/generic.mk index 05d988a38c..6cfb1aa88d 100644 --- a/target/linux/ath79/image/generic.mk +++ b/target/linux/ath79/image/generic.mk @@ -1540,6 +1540,50 @@ define Device/qxwlan_e558-v2-8m endef TARGET_DEVICES += qxwlan_e558-v2-8m +define Device/qxwlan_e600g-v2 + SOC := qca9531 + DEVICE_VENDOR := Qxwlan + DEVICE_MODEL := E600G + DEVICE_PACKAGES := kmod-usb2 + SUPPORTED_DEVICES += e600g-v2 +endef + +define Device/qxwlan_e600g-v2-16m + $(Device/qxwlan_e600g-v2) + DEVICE_VARIANT := v2 (16M) + IMAGE_SIZE := 15936k +endef +TARGET_DEVICES += qxwlan_e600g-v2-16m + +define Device/qxwlan_e600g-v2-8m + $(Device/qxwlan_e600g-v2) + DEVICE_VARIANT := v2 (8M) + IMAGE_SIZE := 7744k +endef +TARGET_DEVICES += qxwlan_e600g-v2-8m + +define Device/qxwlan_e600gac-v2 + SOC := qca9531 + DEVICE_VENDOR := Qxwlan + DEVICE_MODEL := E600GAC + DEVICE_PACKAGES := kmod-ath10k-ct ath10k-firmware-qca9887-ct + SUPPORTED_DEVICES += e600gac-v2 +endef + +define Device/qxwlan_e600gac-v2-16m + $(Device/qxwlan_e600gac-v2) + DEVICE_VARIANT := v2 (16M) + IMAGE_SIZE := 15936k +endef +TARGET_DEVICES += qxwlan_e600gac-v2-16m + +define Device/qxwlan_e600gac-v2-8m + $(Device/qxwlan_e600gac-v2) + DEVICE_VARIANT := v2 (8M) + IMAGE_SIZE := 7744k +endef +TARGET_DEVICES += qxwlan_e600gac-v2-8m + define Device/qxwlan_e750a-v4 SOC := ar9344 DEVICE_VENDOR := Qxwlan diff --git a/target/linux/generic/config-filter b/target/linux/generic/config-filter new file mode 100644 index 0000000000..0d53ed9c3c --- /dev/null +++ b/target/linux/generic/config-filter @@ -0,0 +1,7 @@ +# CONFIG_ARCH_(ENABLE|HAS|HAVE|INLINE|SUPPORTS|USE|WANT)_.* is not set +# CONFIG_AS_.* is not set +# CONFIG_CC_(CAN|HAS|IS|VERSION)_.* is not set +# CONFIG_LD_.* is not set +# CONFIG_GCC_VERSION is not set +# CONFIG_INLINE_.* is not set +# CONFIG_HAVE_(?!(ARCH_TIMER|TCM|SMP)).* is not set diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c index 79e1f73bca..f6353da65b 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c @@ -36,7 +36,7 @@ mtdsplit_parse_squashfs(struct mtd_info *master, if (err) return err; - parent_mtd = mtdpart_get_master(master); + parent_mtd = mtd_get_master(master); part_offset = mtdpart_get_offset(master); part = kzalloc(sizeof(*part), GFP_KERNEL); diff --git a/target/linux/generic/files/drivers/net/phy/ip17xx.c b/target/linux/generic/files/drivers/net/phy/ip17xx.c index 85a9617a5d..88bdc29cb7 100644 --- a/target/linux/generic/files/drivers/net/phy/ip17xx.c +++ b/target/linux/generic/files/drivers/net/phy/ip17xx.c @@ -329,7 +329,7 @@ static int ip_phy_read(struct ip17xx_state *state, int port, int reg) { int val = mdiobus_read(state->mii_bus, port, reg); if (val < 0) - pr_warning("IP17xx: Unable to get MII register %d,%d: error %d\n", port, reg, -val); + pr_warn("IP17xx: Unable to get MII register %d,%d: error %d\n", port, reg, -val); #ifdef DUMP_MII_IO else pr_debug("IP17xx: Read MII(%d,%d) -> %04x\n", port, reg, val); @@ -346,7 +346,7 @@ static int ip_phy_write(struct ip17xx_state *state, int port, int reg, u16 val) #endif err = mdiobus_write(state->mii_bus, port, reg, val); if (err < 0) - pr_warning("IP17xx: Unable to write MII register %d,%d: error %d\n", port, reg, -err); + pr_warn("IP17xx: Unable to write MII register %d,%d: error %d\n", port, reg, -err); return err; } @@ -451,7 +451,7 @@ static int get_model(struct ip17xx_state *state) } } } else { - pr_warning("IP17xx: Found an unknown IC+ switch with model number %02x, revision %X.\n", model_no, rev_no); + pr_warn("IP17xx: Found an unknown IC+ switch with model number %02x, revision %X.\n", model_no, rev_no); return -EPERM; } return 0; diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c index 8861814be4..41dea98b5e 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c @@ -39,7 +39,7 @@ #include "routerboot.h" -#define RB_HARDCONFIG_VER "0.05" +#define RB_HARDCONFIG_VER "0.06" #define RB_HC_PR_PFX "[rb_hardconfig] " /* ID values for hardware settings */ @@ -76,6 +76,17 @@ #define RB_HW_OPT_HAS_TS_FOR_ADC BIT(22) #define RB_HW_OPT_HAS_PLC BIT(29) +/* + * Tag ID values for ERD data. + * Mikrotik used to pack all calibration data under a single tag id 0x1, but + * recently switched to a new scheme where each radio calibration gets a + * separate tag. The new scheme has tag id bit 15 always set and seems to be + * mutually exclusive with the old scheme. + */ +#define RB_WLAN_ERD_ID_SOLO 0x0001 +#define RB_WLAN_ERD_ID_MULTI_8001 0x8001 +#define RB_WLAN_ERD_ID_MULTI_8201 0x8201 + static struct kobject *hc_kobj; static u8 *hc_buf; // ro buffer after init(): no locking required static size_t hc_buflen; @@ -351,10 +362,22 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, loff_t off, size_t count); static struct hc_wlan_attr { + const u16 erd_tag_id; struct bin_attribute battr; u16 pld_ofs; u16 pld_len; -} hc_wlandata_battr = { +} hc_wd_multi_battrs[] = { + { + .erd_tag_id = RB_WLAN_ERD_ID_MULTI_8001, + .battr = __BIN_ATTR(data_0, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), + }, { + .erd_tag_id = RB_WLAN_ERD_ID_MULTI_8201, + .battr = __BIN_ATTR(data_2, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), + } +}; + +static struct hc_wlan_attr hc_wd_solo_battr = { + .erd_tag_id = RB_WLAN_ERD_ID_SOLO, .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), }; @@ -426,19 +449,19 @@ static struct hc_attr { /* * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past * that magic number the payload itself contains a routerboot tag node - * locating the LZO-compressed calibration data at id 0x1. + * locating the LZO-compressed calibration data. So far this scheme is only + * known to use a single tag at id 0x1. */ -static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen, +static int hc_wlan_data_unpack_erd(const u16 tag_id, const u8 *inbuf, size_t inlen, void *outbuf, size_t *outlen) { u16 lzo_ofs, lzo_len; int ret; /* Find embedded tag */ - ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1 - &lzo_ofs, &lzo_len); + ret = routerboot_tag_find(inbuf, inlen, tag_id, &lzo_ofs, &lzo_len); if (ret) { - pr_debug(RB_HC_PR_PFX "ERD data not found\n"); + pr_debug(RB_HC_PR_PFX "no ERD data for id 0x%04x\n", tag_id); goto fail; } @@ -461,10 +484,10 @@ fail: * that magic number is a payload that must be appended to the hc_lzor_prefix, * the resulting blob is LZO-compressed. In the LZO decompression result, * the RB_MAGIC_ERD magic number (aligned) must be located. Following that - * magic, there is a routerboot tag node (id 0x1) locating the RLE-encoded + * magic, there is one or more routerboot tag node(s) locating the RLE-encoded * calibration data payload. */ -static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, +static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t inlen, void *outbuf, size_t *outlen) { u16 rle_ofs, rle_len; @@ -492,10 +515,8 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, if (ret) { if (LZO_E_INPUT_NOT_CONSUMED == ret) { /* - * The tag length appears to always be aligned (probably - * because it is the "root" RB_ID_WLAN_DATA tag), thus - * the LZO payload may be padded, which can trigger a - * spurious error which we ignore here. + * The tag length is always aligned thus the LZO payload may be padded, + * which can trigger a spurious error which we ignore here. */ pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n"); } else { @@ -520,9 +541,9 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, templen -= (u8 *)needle - tempbuf; /* Past magic. Look for tag node */ - ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len); + ret = routerboot_tag_find((u8 *)needle, templen, tag_id, &rle_ofs, &rle_len); if (ret) { - pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n"); + pr_debug(RB_HC_PR_PFX "LZOR: no RLE data for id 0x%04x\n", tag_id); goto fail; } @@ -542,7 +563,7 @@ fail: return ret; } -static int hc_wlan_data_unpack(const size_t tofs, size_t tlen, +static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen, void *outbuf, size_t *outlen) { const u8 *lbuf; @@ -562,23 +583,25 @@ static int hc_wlan_data_unpack(const size_t tofs, size_t tlen, /* Skip magic */ lbuf += sizeof(magic); tlen -= sizeof(magic); - ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen); + ret = hc_wlan_data_unpack_lzor(tag_id, lbuf, tlen, outbuf, outlen); break; case RB_MAGIC_ERD: /* Skip magic */ lbuf += sizeof(magic); tlen -= sizeof(magic); - ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen); + ret = hc_wlan_data_unpack_erd(tag_id, lbuf, tlen, outbuf, outlen); break; default: /* * If the RB_ID_WLAN_DATA payload doesn't start with a * magic number, the payload itself is the raw RLE-encoded - * calibration data. + * calibration data. Only RB_WLAN_ERD_ID_SOLO makes sense here. */ - ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen); - if (ret) - pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret); + if (RB_WLAN_ERD_ID_SOLO == tag_id) { + ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen); + if (ret) + pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret); + } break; } @@ -633,7 +656,7 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, if (!outbuf) return -ENOMEM; - ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen); + ret = hc_wlan_data_unpack(hc_wattr->erd_tag_id, hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen); if (ret) { kfree(outbuf); return ret; @@ -655,14 +678,17 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, int __init rb_hardconfig_init(struct kobject *rb_kobj) { + struct kobject *hc_wlan_kobj; struct mtd_info *mtd; - size_t bytes_read, buflen; + size_t bytes_read, buflen, outlen; const u8 *buf; - int i, ret; + void *outbuf; + int i, j, ret; u32 magic; hc_buf = NULL; hc_kobj = NULL; + hc_wlan_kobj = NULL; // TODO allow override mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG); @@ -713,15 +739,62 @@ int __init rb_hardconfig_init(struct kobject *rb_kobj) /* Account for skipped magic */ hc_attrs[i].pld_ofs += sizeof(magic); - /* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */ + /* + * Special case RB_ID_WLAN_DATA to prep and create the binary attribute. + * We first check if the data is "old style" within a single tag (or no tag at all): + * If it is we publish this single blob as a binary attribute child of hc_kobj to + * preserve backward compatibility. + * If it isn't and instead uses multiple ERD tags, we create a subfolder and + * publish the known ones there. + */ if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) { - hc_wlandata_battr.pld_ofs = hc_attrs[i].pld_ofs; - hc_wlandata_battr.pld_len = hc_attrs[i].pld_len; + outlen = RB_ART_SIZE; + outbuf = kmalloc(outlen, GFP_KERNEL); + if (!outbuf) { + pr_warn(RB_HC_PR_PFX "Out of memory parsing WLAN tag\n"); + continue; + } - ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr); - if (ret) - pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n", - hc_wlandata_battr.battr.attr.name, ret); + /* Test ID_SOLO first, if found: done */ + ret = hc_wlan_data_unpack(RB_WLAN_ERD_ID_SOLO, hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen); + if (!ret) { + hc_wd_solo_battr.pld_ofs = hc_attrs[i].pld_ofs; + hc_wd_solo_battr.pld_len = hc_attrs[i].pld_len; + + ret = sysfs_create_bin_file(hc_kobj, &hc_wd_solo_battr.battr); + if (ret) + pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n", + hc_wd_solo_battr.battr.attr.name, ret); + } + /* Otherwise, create "wlan_data" subtree and publish known data */ + else { + hc_wlan_kobj = kobject_create_and_add("wlan_data", hc_kobj); + if (!hc_wlan_kobj) { + kfree(outbuf); + pr_warn(RB_HC_PR_PFX "Could not create wlan_data sysfs folder\n"); + continue; + } + + for (j = 0; j < ARRAY_SIZE(hc_wd_multi_battrs); j++) { + outlen = RB_ART_SIZE; + ret = hc_wlan_data_unpack(hc_wd_multi_battrs[j].erd_tag_id, + hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen); + if (ret) { + hc_wd_multi_battrs[j].pld_ofs = hc_wd_multi_battrs[j].pld_len = 0; + continue; + } + + hc_wd_multi_battrs[j].pld_ofs = hc_attrs[i].pld_ofs; + hc_wd_multi_battrs[j].pld_len = hc_attrs[i].pld_len; + + ret = sysfs_create_bin_file(hc_wlan_kobj, &hc_wd_multi_battrs[j].battr); + if (ret) + pr_warn(RB_HC_PR_PFX "Could not create wlan_data/%s sysfs entry (%d)\n", + hc_wd_multi_battrs[j].battr.attr.name, ret); + } + } + + kfree(outbuf); } /* All other tags are published via standard attributes */ else { diff --git a/target/linux/generic/pending-5.4/404-mtd-add-more-helper-functions.patch b/target/linux/generic/pending-5.4/404-mtd-add-more-helper-functions.patch index 454a37fc22..059a440f0a 100644 --- a/target/linux/generic/pending-5.4/404-mtd-add-more-helper-functions.patch +++ b/target/linux/generic/pending-5.4/404-mtd-add-more-helper-functions.patch @@ -15,14 +15,14 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org> } EXPORT_SYMBOL_GPL(mtd_is_partition); -+struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd) ++struct mtd_info *mtd_get_master(const struct mtd_info *mtd) +{ + if (!mtd_is_partition(mtd)) + return (struct mtd_info *)mtd; + + return mtd_to_part(mtd)->parent; +} -+EXPORT_SYMBOL_GPL(mtdpart_get_master); ++EXPORT_SYMBOL_GPL(mtd_get_master); + +uint64_t mtdpart_get_offset(const struct mtd_info *mtd) +{ @@ -69,7 +69,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org> int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); -+struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd); ++struct mtd_info *mtd_get_master(const struct mtd_info *mtd); +uint64_t mtdpart_get_offset(const struct mtd_info *mtd); uint64_t mtd_get_device_size(const struct mtd_info *mtd); diff --git a/target/linux/ipq40xx/patches-5.4/0001-v5.7-ARM-qcom-Add-support-for-IPQ40xx.patch b/target/linux/ipq40xx/patches-5.4/0001-v5.7-ARM-qcom-Add-support-for-IPQ40xx.patch new file mode 100644 index 0000000000..8aa71f360f --- /dev/null +++ b/target/linux/ipq40xx/patches-5.4/0001-v5.7-ARM-qcom-Add-support-for-IPQ40xx.patch @@ -0,0 +1,42 @@ +From f125e2d4339dda6937865f975470b29c84714c9b Mon Sep 17 00:00:00 2001 +From: Christian Lamparter <chunkeey@gmail.com> +Date: Mon, 6 Jan 2020 14:57:15 +0100 +Subject: [PATCH] ARM: qcom: Add support for IPQ40xx + +Add support for the Qualcomm IPQ40xx SoC in Kconfig. +Also add its appropriate textofs. + +Signed-off-by: Christian Lamparter <chunkeey@gmail.com> +Signed-off-by: John Crispin <john@phrozen.org> +Tested-by: Robert Marko <robert.marko@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +--- + arch/arm/Makefile | 1 + + arch/arm/mach-qcom/Kconfig | 5 +++++ + 2 files changed, 6 insertions(+) + +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -152,6 +152,7 @@ textofs-$(CONFIG_PM_H1940) := 0x001 + ifeq ($(CONFIG_ARCH_SA1100),y) + textofs-$(CONFIG_SA1111) := 0x00208000 + endif ++textofs-$(CONFIG_ARCH_IPQ40XX) := 0x00208000 + textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 + textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 + textofs-$(CONFIG_ARCH_MESON) := 0x00208000 +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -12,6 +12,11 @@ menuconfig ARCH_QCOM + + if ARCH_QCOM + ++config ARCH_IPQ40XX ++ bool "Enable support for IPQ40XX" ++ select CLKSRC_QCOM ++ select HAVE_ARM_ARCH_TIMER ++ + config ARCH_MSM8X60 + bool "Enable support for MSM8X60" + select CLKSRC_QCOM diff --git a/target/linux/ipq40xx/patches-5.4/088-v5.6-regulator-add-IPQ4019-SDHCI-VQMMC-LDO-driver.patch b/target/linux/ipq40xx/patches-5.4/0002-01-v5.6-regulator-add-IPQ4019-SDHCI-VQMMC-LDO-driver.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/088-v5.6-regulator-add-IPQ4019-SDHCI-VQMMC-LDO-driver.patch rename to target/linux/ipq40xx/patches-5.4/0002-01-v5.6-regulator-add-IPQ4019-SDHCI-VQMMC-LDO-driver.patch diff --git a/target/linux/ipq40xx/patches-5.4/089-v5.5-ARM-dts-qcom-ipq4019-Add-SDHCI-controller-node.patch b/target/linux/ipq40xx/patches-5.4/0002-02-v5.5-ARM-dts-qcom-ipq4019-Add-SDHCI-controller-node.patch similarity index 98% rename from target/linux/ipq40xx/patches-5.4/089-v5.5-ARM-dts-qcom-ipq4019-Add-SDHCI-controller-node.patch rename to target/linux/ipq40xx/patches-5.4/0002-02-v5.5-ARM-dts-qcom-ipq4019-Add-SDHCI-controller-node.patch index b3e0a20b32..25fce8daf0 100644 --- a/target/linux/ipq40xx/patches-5.4/089-v5.5-ARM-dts-qcom-ipq4019-Add-SDHCI-controller-node.patch +++ b/target/linux/ipq40xx/patches-5.4/0002-02-v5.5-ARM-dts-qcom-ipq4019-Add-SDHCI-controller-node.patch @@ -15,7 +15,7 @@ Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -209,6 +209,18 @@ +@@ -206,6 +206,18 @@ interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>; }; diff --git a/target/linux/ipq40xx/patches-5.4/0003-v5.6-ARM-dts-qcom-Add-nodes-for-SMP-boot-in-IPQ40xx.patch b/target/linux/ipq40xx/patches-5.4/0003-v5.6-ARM-dts-qcom-Add-nodes-for-SMP-boot-in-IPQ40xx.patch new file mode 100644 index 0000000000..3a4127febf --- /dev/null +++ b/target/linux/ipq40xx/patches-5.4/0003-v5.6-ARM-dts-qcom-Add-nodes-for-SMP-boot-in-IPQ40xx.patch @@ -0,0 +1,71 @@ +From 5e4548922009870a38bcf1d887317676d4e08f54 Mon Sep 17 00:00:00 2001 +From: Damir Franusic <damir.franusic@sartura.hr> +Date: Thu, 21 Nov 2019 16:29:02 +0100 +Subject: [PATCH] ARM: dts: qcom: Add nodes for SMP boot in IPQ40xx + +Add missing nodes and properties to enable SMP +support on IPQ40xx devices. + +Booting without "saw_l2" node: + +[ 0.001400] CPU: Testing write buffer coherency: ok +[ 0.001856] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000 +[ 0.060163] Setting up static identity map for 0x80300000 - 0x80300060 +[ 0.080140] rcu: Hierarchical SRCU implementation. +[ 0.120258] smp: Bringing up secondary CPUs ... +[ 0.200540] CPU1: failed to boot: -19 +[ 0.280689] CPU2: failed to boot: -19 +[ 0.360874] CPU3: failed to boot: -19 +[ 0.360966] smp: Brought up 1 node, 1 CPU +[ 0.360979] SMP: Total of 1 processors activated (96.00 BogoMIPS). +[ 0.360988] CPU: All CPU(s) started in SVC mode. + +Then, booting with "saw_l2" node present (this patch applied): + +[ 0.001450] CPU: Testing write buffer coherency: ok +[ 0.001904] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000 +[ 0.060161] Setting up static identity map for 0x80300000 - 0x80300060 +[ 0.080137] rcu: Hierarchical SRCU implementation. +[ 0.120252] smp: Bringing up secondary CPUs ... +[ 0.200958] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001 +[ 0.281091] CPU2: thread -1, cpu 2, socket 0, mpidr 80000002 +[ 0.361264] CPU3: thread -1, cpu 3, socket 0, mpidr 80000003 +[ 0.361430] smp: Brought up 1 node, 4 CPUs +[ 0.361460] SMP: Total of 4 processors activated (384.00 BogoMIPS). +[ 0.361469] CPU: All CPU(s) started in SVC mode. + +Signed-off-by: Damir Franusic <damir.franusic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +Cc: Andy Gross <agross@kernel.org> +Cc: Rob Herring <robh+dt@kernel.org> +Cc: linux-arm-msm@vger.kernel.org +Link: https://lore.kernel.org/r/20191121152902.21394-1-damir.franusic@gmail.com +Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> +--- + arch/arm/boot/dts/qcom-ipq4019.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi +@@ -102,6 +102,7 @@ + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; ++ qcom,saw = <&saw_l2>; + }; + }; + +@@ -353,6 +354,12 @@ + regulator; + }; + ++ saw_l2: regulator@b012000 { ++ compatible = "qcom,saw2"; ++ reg = <0xb012000 0x1000>; ++ regulator; ++ }; ++ + blsp1_uart1: serial@78af000 { + compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; + reg = <0x78af000 0x200>; diff --git a/target/linux/ipq40xx/patches-5.4/0003-v5.7-ARM-dts-qcom-add-gpio-ranges-property.patch b/target/linux/ipq40xx/patches-5.4/0003-v5.7-ARM-dts-qcom-add-gpio-ranges-property.patch new file mode 100644 index 0000000000..6922bc8ff3 --- /dev/null +++ b/target/linux/ipq40xx/patches-5.4/0003-v5.7-ARM-dts-qcom-add-gpio-ranges-property.patch @@ -0,0 +1,119 @@ +From 8b99dc0922618062a1589ebd74df6108b4f9ac22 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter <chunkeey@gmail.com> +Date: Wed, 8 Jan 2020 13:54:55 +0100 +Subject: [PATCH] ARM: dts: qcom: add gpio-ranges property + +This patch adds the gpio-ranges property to almost all of +the Qualcomm ARM platforms that utilize the pinctrl-msm +framework. + +The gpio-ranges property is part of the gpiolib subsystem. +As a result, the binding text is available in section +"2.1 gpio- and pin-controller interaction" of +Documentation/devicetree/bindings/gpio/gpio.txt + +For more information please see the patch titled: +"pinctrl: msm: fix gpio-hog related boot issues" from +this series. + +Reported-by: Sven Eckelmann <sven.eckelmann@openmesh.com> +Tested-by: Sven Eckelmann <sven.eckelmann@openmesh.com> [ipq4019] +Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> +Reviewed-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: Christian Lamparter <chunkeey@gmail.com> +Tested-by: Robert Marko <robert.marko@sartura.hr> [ipq4019] +Cc: Luka Perkov <luka.perkov@sartura.hr> +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Link: https://lore.kernel.org/r/20200108125455.308969-1-robert.marko@sartura.hr +Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> +--- + arch/arm/boot/dts/qcom-apq8064.dtsi | 1 + + arch/arm/boot/dts/qcom-apq8084.dtsi | 1 + + arch/arm/boot/dts/qcom-ipq4019.dtsi | 1 + + arch/arm/boot/dts/qcom-ipq8064.dtsi | 1 + + arch/arm/boot/dts/qcom-mdm9615.dtsi | 1 + + arch/arm/boot/dts/qcom-msm8660.dtsi | 1 + + arch/arm/boot/dts/qcom-msm8960.dtsi | 1 + + arch/arm/boot/dts/qcom-msm8974.dtsi | 1 + + 8 files changed, 8 insertions(+) + +--- a/arch/arm/boot/dts/qcom-apq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-apq8064.dtsi +@@ -350,6 +350,7 @@ + reg = <0x800000 0x4000>; + + gpio-controller; ++ gpio-ranges = <&tlmm_pinmux 0 0 90>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; +--- a/arch/arm/boot/dts/qcom-apq8084.dtsi ++++ b/arch/arm/boot/dts/qcom-apq8084.dtsi +@@ -401,6 +401,7 @@ + compatible = "qcom,apq8084-pinctrl"; + reg = <0xfd510000 0x4000>; + gpio-controller; ++ gpio-ranges = <&tlmm 0 0 147>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi +@@ -201,6 +201,7 @@ + compatible = "qcom,ipq4019-pinctrl"; + reg = <0x01000000 0x300000>; + gpio-controller; ++ gpio-ranges = <&tlmm 0 0 100>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -119,6 +119,7 @@ + reg = <0x800000 0x4000>; + + gpio-controller; ++ gpio-ranges = <&qcom_pinmux 0 0 69>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; +--- a/arch/arm/boot/dts/qcom-mdm9615.dtsi ++++ b/arch/arm/boot/dts/qcom-mdm9615.dtsi +@@ -128,6 +128,7 @@ + msmgpio: pinctrl@800000 { + compatible = "qcom,mdm9615-pinctrl"; + gpio-controller; ++ gpio-ranges = <&msmgpio 0 0 88>; + #gpio-cells = <2>; + interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; +--- a/arch/arm/boot/dts/qcom-msm8660.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8660.dtsi +@@ -115,6 +115,7 @@ + reg = <0x800000 0x4000>; + + gpio-controller; ++ gpio-ranges = <&tlmm 0 0 173>; + #gpio-cells = <2>; + interrupts = <0 16 0x4>; + interrupt-controller; +--- a/arch/arm/boot/dts/qcom-msm8960.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8960.dtsi +@@ -107,6 +107,7 @@ + msmgpio: pinctrl@800000 { + compatible = "qcom,msm8960-pinctrl"; + gpio-controller; ++ gpio-ranges = <&msmgpio 0 0 152>; + #gpio-cells = <2>; + interrupts = <0 16 0x4>; + interrupt-controller; +--- a/arch/arm/boot/dts/qcom-msm8974.dtsi ++++ b/arch/arm/boot/dts/qcom-msm8974.dtsi +@@ -707,6 +707,7 @@ + compatible = "qcom,msm8974-pinctrl"; + reg = <0xfd510000 0x4000>; + gpio-controller; ++ gpio-ranges = <&msmgpio 0 0 146>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; diff --git a/target/linux/ipq40xx/patches-5.4/086-ipq40xx-fix-high-resolution-timer.patch b/target/linux/ipq40xx/patches-5.4/0004-v5.8-ARM-dts-qcom-ipq4019-fix-high-resolution-timer.patch similarity index 61% rename from target/linux/ipq40xx/patches-5.4/086-ipq40xx-fix-high-resolution-timer.patch rename to target/linux/ipq40xx/patches-5.4/0004-v5.8-ARM-dts-qcom-ipq4019-fix-high-resolution-timer.patch index 3bfd0d99e0..f82021f4cd 100644 --- a/target/linux/ipq40xx/patches-5.4/086-ipq40xx-fix-high-resolution-timer.patch +++ b/target/linux/ipq40xx/patches-5.4/0004-v5.8-ARM-dts-qcom-ipq4019-fix-high-resolution-timer.patch @@ -1,7 +1,7 @@ -From 09f145f417a5d64d6b8d4476699dfb0eccc6c784 Mon Sep 17 00:00:00 2001 +From 8acc36189dcaf4487d8c6ba7445948f39b1d248a Mon Sep 17 00:00:00 2001 From: Abhishek Sahu <absahu@codeaurora.org> -Date: Tue, 7 May 2019 10:14:05 +0300 -Subject: [PATCH] ipq40xx: fix high resolution timer +Date: Fri, 3 Apr 2020 13:40:40 +0200 +Subject: [PATCH] ARM: dts: qcom: ipq4019: fix high resolution timer Cherry-picked from CAF QSDK repo. Original commit message: @@ -10,9 +10,13 @@ mode and clock source operates in 10ms resolution. The always-on property needs to be given for timer device tree node to make clock source working in 1ns resolution. -Change-Id: I7c00b3c74d97c2a30ac9f05e18b511a0550fd459 Signed-off-by: Abhishek Sahu <absahu@codeaurora.org> Signed-off-by: Pavel Kubelun <be.dissent@gmail.com> +Signed-off-by: Christian Lamparter <chunkeey@gmail.com> +Tested-by: Robert Marko <robert.marko@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Link: https://lore.kernel.org/r/20200403114040.349787-1-robert.marko@sartura.hr +Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- arch/arm/boot/dts/qcom-ipq4019.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/ipq40xx/patches-5.4/0005-v5.8-02-ARM-dts-qcom-ipq4019-add-MDIO-node.patch b/target/linux/ipq40xx/patches-5.4/0005-v5.8-02-ARM-dts-qcom-ipq4019-add-MDIO-node.patch new file mode 100644 index 0000000000..1976686e8f --- /dev/null +++ b/target/linux/ipq40xx/patches-5.4/0005-v5.8-02-ARM-dts-qcom-ipq4019-add-MDIO-node.patch @@ -0,0 +1,57 @@ +From 9c8c0f70ec6fdac2398632c723c48277be09b7c0 Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Thu, 30 Apr 2020 11:07:07 +0200 +Subject: [PATCH] ARM: dts: qcom: ipq4019: add MDIO node + +This patch adds the necessary MDIO interface node +to the Qualcomm IPQ4019 DTSI. + +Built-in QCA8337N switch is managed using it, +and since we have a driver for it lets add it. + +Signed-off-by: Christian Lamparter <chunkeey@gmail.com> +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + arch/arm/boot/dts/qcom-ipq4019.dtsi | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi +@@ -577,5 +577,33 @@ + "legacy"; + status = "disabled"; + }; ++ ++ mdio: mdio@90000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "qcom,ipq4019-mdio"; ++ reg = <0x90000 0x64>; ++ status = "disabled"; ++ ++ ethphy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++ ++ ethphy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ ++ ethphy2: ethernet-phy@2 { ++ reg = <2>; ++ }; ++ ++ ethphy3: ethernet-phy@3 { ++ reg = <3>; ++ }; ++ ++ ethphy4: ethernet-phy@4 { ++ reg = <4>; ++ }; ++ }; + }; + }; diff --git a/target/linux/ipq40xx/patches-5.4/039-crypto-qce-add-CRYPTO_ALG_KERN_DRIVER_ONLY-flag.patch b/target/linux/ipq40xx/patches-5.4/0006-v5.5-crypto-qce-add-CRYPTO_ALG_KERN_DRIVER_ONLY-flag.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/039-crypto-qce-add-CRYPTO_ALG_KERN_DRIVER_ONLY-flag.patch rename to target/linux/ipq40xx/patches-5.4/0006-v5.5-crypto-qce-add-CRYPTO_ALG_KERN_DRIVER_ONLY-flag.patch diff --git a/target/linux/ipq40xx/patches-5.4/040-crypto-qce-switch-to-skcipher-API.patch b/target/linux/ipq40xx/patches-5.4/0007-v5.5-crypto-qce-switch-to-skcipher-API.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/040-crypto-qce-switch-to-skcipher-API.patch rename to target/linux/ipq40xx/patches-5.4/0007-v5.5-crypto-qce-switch-to-skcipher-API.patch diff --git a/target/linux/ipq40xx/patches-5.4/041-crypto-qce-fix-ctr-aes-qce-block-chunk-sizes.patch b/target/linux/ipq40xx/patches-5.4/0008-v5.6-crypto-qce-fix-ctr-aes-qce-block-chunk-sizes.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/041-crypto-qce-fix-ctr-aes-qce-block-chunk-sizes.patch rename to target/linux/ipq40xx/patches-5.4/0008-v5.6-crypto-qce-fix-ctr-aes-qce-block-chunk-sizes.patch diff --git a/target/linux/ipq40xx/patches-5.4/042-crypto-qce-fix-xts-aes-qce-key-sizes.patch b/target/linux/ipq40xx/patches-5.4/0009-v5.6-crypto-qce-fix-xts-aes-qce-key-sizes.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/042-crypto-qce-fix-xts-aes-qce-key-sizes.patch rename to target/linux/ipq40xx/patches-5.4/0009-v5.6-crypto-qce-fix-xts-aes-qce-key-sizes.patch diff --git a/target/linux/ipq40xx/patches-5.4/043-crypto-qce-save-a-sg-table-slot-for-result-buf.patch b/target/linux/ipq40xx/patches-5.4/0010-v5.6-crypto-qce-save-a-sg-table-slot-for-result-buf.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/043-crypto-qce-save-a-sg-table-slot-for-result-buf.patch rename to target/linux/ipq40xx/patches-5.4/0010-v5.6-crypto-qce-save-a-sg-table-slot-for-result-buf.patch diff --git a/target/linux/ipq40xx/patches-5.4/044-crypto-qce-update-the-skcipher-IV.patch b/target/linux/ipq40xx/patches-5.4/0011-v5.6-crypto-qce-update-the-skcipher-IV.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/044-crypto-qce-update-the-skcipher-IV.patch rename to target/linux/ipq40xx/patches-5.4/0011-v5.6-crypto-qce-update-the-skcipher-IV.patch diff --git a/target/linux/ipq40xx/patches-5.4/046-crypto-qce-initialize-fallback-only-for-AES.patch b/target/linux/ipq40xx/patches-5.4/0012-v5.6-crypto-qce-initialize-fallback-only-for-AES.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/046-crypto-qce-initialize-fallback-only-for-AES.patch rename to target/linux/ipq40xx/patches-5.4/0012-v5.6-crypto-qce-initialize-fallback-only-for-AES.patch diff --git a/target/linux/ipq40xx/patches-5.4/051-crypto-qce-allow-building-only-hashes-ciphers.patch b/target/linux/ipq40xx/patches-5.4/0013-v5.6-crypto-qce-allow-building-only-hashes-ciphers.patch similarity index 97% rename from target/linux/ipq40xx/patches-5.4/051-crypto-qce-allow-building-only-hashes-ciphers.patch rename to target/linux/ipq40xx/patches-5.4/0013-v5.6-crypto-qce-allow-building-only-hashes-ciphers.patch index 5f350183d8..bcd5551f54 100644 --- a/target/linux/ipq40xx/patches-5.4/051-crypto-qce-allow-building-only-hashes-ciphers.patch +++ b/target/linux/ipq40xx/patches-5.4/0013-v5.6-crypto-qce-allow-building-only-hashes-ciphers.patch @@ -94,8 +94,8 @@ Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> + +endchoice - config CRYPTO_DEV_QCE_SW_MAX_LEN - int "Default maximum request size to use software for AES" + config CRYPTO_DEV_QCOM_RNG + tristate "Qualcomm Random Number Generator Driver" --- a/drivers/crypto/qce/Makefile +++ b/drivers/crypto/qce/Makefile @@ -2,6 +2,7 @@ @@ -111,7 +111,7 @@ Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> +qcrypto-$(CONFIG_CRYPTO_DEV_QCE_SKCIPHER) += skcipher.o --- a/drivers/crypto/qce/common.c +++ b/drivers/crypto/qce/common.c -@@ -43,52 +43,56 @@ qce_clear_array(struct qce_device *qce, +@@ -45,52 +45,56 @@ qce_clear_array(struct qce_device *qce, qce_write(qce, offset + i * sizeof(u32), 0); } @@ -204,7 +204,7 @@ Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> static u32 qce_auth_cfg(unsigned long flags, u32 key_size) { u32 cfg = 0; -@@ -135,88 +139,6 @@ static u32 qce_auth_cfg(unsigned long fl +@@ -137,88 +141,6 @@ static u32 qce_auth_cfg(unsigned long fl return cfg; } @@ -293,7 +293,7 @@ Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> static int qce_setup_regs_ahash(struct crypto_async_request *async_req, u32 totallen, u32 offset) { -@@ -301,6 +223,87 @@ go_proc: +@@ -303,6 +225,87 @@ go_proc: return 0; } @@ -381,7 +381,7 @@ Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> static int qce_setup_regs_skcipher(struct crypto_async_request *async_req, u32 totallen, u32 offset) -@@ -382,15 +385,20 @@ static int qce_setup_regs_skcipher(struc +@@ -384,15 +387,20 @@ static int qce_setup_regs_skcipher(struc return 0; } diff --git a/target/linux/ipq40xx/patches-5.4/047-crypto-qce-use-cryptlen-when-adding-extra-sgl.patch b/target/linux/ipq40xx/patches-5.4/0014-v5.7-crypto-qce-use-cryptlen-when-adding-extra-sgl.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/047-crypto-qce-use-cryptlen-when-adding-extra-sgl.patch rename to target/linux/ipq40xx/patches-5.4/0014-v5.7-crypto-qce-use-cryptlen-when-adding-extra-sgl.patch diff --git a/target/linux/ipq40xx/patches-5.4/048-crypto-qce-use-AES-fallback-for-small-requests.patch b/target/linux/ipq40xx/patches-5.4/0015-v5.7-crypto-qce-use-AES-fallback-for-small-requests.patch similarity index 96% rename from target/linux/ipq40xx/patches-5.4/048-crypto-qce-use-AES-fallback-for-small-requests.patch rename to target/linux/ipq40xx/patches-5.4/0015-v5.7-crypto-qce-use-AES-fallback-for-small-requests.patch index d9a6c4dff9..55d15f56b5 100644 --- a/target/linux/ipq40xx/patches-5.4/048-crypto-qce-use-AES-fallback-for-small-requests.patch +++ b/target/linux/ipq40xx/patches-5.4/0015-v5.7-crypto-qce-use-AES-fallback-for-small-requests.patch @@ -28,9 +28,9 @@ Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig -@@ -628,6 +628,29 @@ config CRYPTO_DEV_QCE - hardware. To compile this driver as a module, choose M here. The - module will be called qcrypto. +@@ -683,6 +683,29 @@ choice + + endchoice +config CRYPTO_DEV_QCE_SW_MAX_LEN + int "Default maximum request size to use software for AES" diff --git a/target/linux/ipq40xx/patches-5.4/049-crypto-qce-handle-AES-XTS-cases-that-qce-fails.patch b/target/linux/ipq40xx/patches-5.4/0016-v5.7-crypto-qce-handle-AES-XTS-cases-that-qce-fails.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/049-crypto-qce-handle-AES-XTS-cases-that-qce-fails.patch rename to target/linux/ipq40xx/patches-5.4/0016-v5.7-crypto-qce-handle-AES-XTS-cases-that-qce-fails.patch diff --git a/target/linux/ipq40xx/patches-5.4/076-phy-qcom-ipq4019-usb-add-driver-for-QCOM-IPQ4019.patch b/target/linux/ipq40xx/patches-5.4/0017-v5.8-phy-add-driver-for-Qualcomm-IPQ40xx-USB-PHY.patch similarity index 66% rename from target/linux/ipq40xx/patches-5.4/076-phy-qcom-ipq4019-usb-add-driver-for-QCOM-IPQ4019.patch rename to target/linux/ipq40xx/patches-5.4/0017-v5.8-phy-add-driver-for-Qualcomm-IPQ40xx-USB-PHY.patch index 6ded0e9bad..ad09a9f250 100644 --- a/target/linux/ipq40xx/patches-5.4/076-phy-qcom-ipq4019-usb-add-driver-for-QCOM-IPQ4019.patch +++ b/target/linux/ipq40xx/patches-5.4/0017-v5.8-phy-add-driver-for-Qualcomm-IPQ40xx-USB-PHY.patch @@ -1,18 +1,21 @@ -From 633f0e08498aebfdb932bd71319b4cb136709499 Mon Sep 17 00:00:00 2001 -From: John Crispin <john@phrozen.org> -Date: Tue, 24 Jul 2018 14:45:49 +0200 -Subject: [PATCH 2/3] phy: qcom-ipq4019-usb: add driver for QCOM/IPQ4019 +From 3c9d8f6c03a2cda1849ec3c84f82ec030d1f49ef Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Sun, 3 May 2020 22:18:22 +0200 +Subject: [PATCH] phy: add driver for Qualcomm IPQ40xx USB PHY -Add a driver to setup the USB phy on Qualcom Dakota SoCs. -The driver sets up HS and SS phys. In case of HS some magic values need to -be written to magic offsets. These were taken from the SDK driver. +Add a driver to setup the USB PHY-s on Qualcom m IPQ40xx series SoCs. +The driver sets up HS and SS phys. Signed-off-by: John Crispin <john@phrozen.org> +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Link: https://lore.kernel.org/r/20200503201823.531757-1-robert.marko@sartura.hr +Signed-off-by: Vinod Koul <vkoul@kernel.org> --- - drivers/phy/qualcomm/Kconfig | 7 ++ + drivers/phy/qualcomm/Kconfig | 7 + drivers/phy/qualcomm/Makefile | 1 + - drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c | 188 ++++++++++++++++++++++++++++ - 3 files changed, 196 insertions(+) + drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c | 148 ++++++++++++++++++++ + 3 files changed, 156 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c --- a/drivers/phy/qualcomm/Kconfig @@ -22,33 +25,35 @@ Signed-off-by: John Crispin <john@phrozen.org> select GENERIC_PHY +config PHY_QCOM_IPQ4019_USB -+ tristate "Qualcomm IPQ4019 USB PHY module" -+ depends on OF && ARCH_QCOM ++ tristate "Qualcomm IPQ4019 USB PHY driver" ++ depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help -+ Support for the USB PHY on QCOM IPQ4019/Dakota chipsets. ++ Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s. + config PHY_QCOM_IPQ806X_SATA tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" depends on ARCH_QCOM +--- a/drivers/phy/qualcomm/Makefile ++++ b/drivers/phy/qualcomm/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o + obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o ++obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o + obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o + obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o + obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c -@@ -0,0 +1,188 @@ +@@ -0,0 +1,148 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 John Crispin <john@phrozen.org> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. + */ + +#include <linux/delay.h> @@ -58,29 +63,11 @@ Signed-off-by: John Crispin <john@phrozen.org> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_platform.h> ++#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + -+/* -+ * Magic registers copied from the SDK driver code -+ */ -+#define PHY_CTRL0_ADDR 0x000 -+#define PHY_CTRL1_ADDR 0x004 -+#define PHY_CTRL2_ADDR 0x008 -+#define PHY_CTRL3_ADDR 0x00C -+#define PHY_CTRL4_ADDR 0x010 -+#define PHY_MISC_ADDR 0x024 -+#define PHY_IPG_ADDR 0x030 -+ -+#define PHY_CTRL0_VAL 0xA4600015 -+#define PHY_CTRL1_VAL 0x09500000 -+#define PHY_CTRL2_VAL 0x00058180 -+#define PHY_CTRL3_VAL 0x6DB6DCD6 -+#define PHY_CTRL4_VAL 0x836DB6DB -+#define PHY_MISC_VAL 0x3803FB0C -+#define PHY_IPG_VAL 0x47323232 -+ +struct ipq4019_usb_phy { + struct device *dev; + struct phy *phy; @@ -137,15 +124,6 @@ Signed-off-by: John Crispin <john@phrozen.org> + reset_control_deassert(phy->srif_rst); + msleep(10); + -+ writel(PHY_CTRL0_VAL, phy->base + PHY_CTRL0_ADDR); -+ writel(PHY_CTRL1_VAL, phy->base + PHY_CTRL1_ADDR); -+ writel(PHY_CTRL2_VAL, phy->base + PHY_CTRL2_ADDR); -+ writel(PHY_CTRL3_VAL, phy->base + PHY_CTRL3_ADDR); -+ writel(PHY_CTRL4_VAL, phy->base + PHY_CTRL4_ADDR); -+ writel(PHY_MISC_VAL, phy->base + PHY_MISC_ADDR); -+ writel(PHY_IPG_VAL, phy->base + PHY_IPG_ADDR); -+ msleep(10); -+ + reset_control_deassert(phy->por_rst); + + return 0; @@ -169,11 +147,6 @@ Signed-off-by: John Crispin <john@phrozen.org> + struct resource *res; + struct phy_provider *phy_provider; + struct ipq4019_usb_phy *phy; -+ const struct of_device_id *match; -+ -+ match = of_match_device(ipq4019_usb_phy_of_match, &pdev->dev); -+ if (!match) -+ return -ENODEV; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) @@ -198,7 +171,7 @@ Signed-off-by: John Crispin <john@phrozen.org> + if (IS_ERR(phy->srif_rst)) + return PTR_ERR(phy->srif_rst); + -+ phy->phy = devm_phy_create(dev, NULL, match->data); ++ phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev)); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy->phy); @@ -222,13 +195,3 @@ Signed-off-by: John Crispin <john@phrozen.org> +MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver"); +MODULE_AUTHOR("John Crispin <john@phrozen.org>"); +MODULE_LICENSE("GPL v2"); ---- a/drivers/phy/qualcomm/Makefile -+++ b/drivers/phy/qualcomm/Makefile -@@ -1,6 +1,7 @@ - # SPDX-License-Identifier: GPL-2.0 - obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o - obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o -+obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o - obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o - obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o - obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o diff --git a/target/linux/ipq40xx/patches-5.4/090-pinctrl-msm-open-drain.patch b/target/linux/ipq40xx/patches-5.4/0018-v5.9-pinctrl-msm-open-drain.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/090-pinctrl-msm-open-drain.patch rename to target/linux/ipq40xx/patches-5.4/0018-v5.9-pinctrl-msm-open-drain.patch diff --git a/target/linux/ipq40xx/patches-5.4/304-mtd-spi-nor-Add-support-for-mx25r3235f.patch b/target/linux/ipq40xx/patches-5.4/0019-v5.6-mtd-spi-nor-Add-support-for-mx25r3235f.patch similarity index 83% rename from target/linux/ipq40xx/patches-5.4/304-mtd-spi-nor-Add-support-for-mx25r3235f.patch rename to target/linux/ipq40xx/patches-5.4/0019-v5.6-mtd-spi-nor-Add-support-for-mx25r3235f.patch index e0078b7260..97433b030e 100644 --- a/target/linux/ipq40xx/patches-5.4/304-mtd-spi-nor-Add-support-for-mx25r3235f.patch +++ b/target/linux/ipq40xx/patches-5.4/0019-v5.6-mtd-spi-nor-Add-support-for-mx25r3235f.patch @@ -1,6 +1,6 @@ -From 158acdbf0336f601971637f988b57a6a67a0869b Mon Sep 17 00:00:00 2001 +From 707745e8d4e75b638b990d67950ab292b3b8ea2a Mon Sep 17 00:00:00 2001 From: David Bauer <mail@david-bauer.net> -Date: Sun, 15 Dec 2019 13:10:50 +0100 +Date: Mon, 16 Dec 2019 01:36:46 +0100 Subject: [PATCH] mtd: spi-nor: Add support for mx25r3235f Add MTD support for the Macronix MX25R3235F SPI NOR chip from Macronix. @@ -8,7 +8,10 @@ The chip has 4MB of total capacity, divided into a total of 64 sectors, each 64KB sized. The chip also supports 4KB large sectors. Additionally, it supports dual and quad read modes. +Functionality was verified on an HPE/Aruba AP-303 board. + Signed-off-by: David Bauer <mail@david-bauer.net> +Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> --- drivers/mtd/spi-nor/spi-nor.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/linux/ipq40xx/patches-5.4/071-02-ipq40xx-Fix-booting-secondary-cores.patch b/target/linux/ipq40xx/patches-5.4/071-02-ipq40xx-Fix-booting-secondary-cores.patch deleted file mode 100644 index 631a1307dd..0000000000 --- a/target/linux/ipq40xx/patches-5.4/071-02-ipq40xx-Fix-booting-secondary-cores.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 8a4540321e8bcf7a5b485c332a2e78f3501c78ed Mon Sep 17 00:00:00 2001 -From: Robert Marko <robimarko@gmail.com> -Date: Thu, 29 Nov 2018 22:29:36 +0100 -Subject: [PATCH] ipq40xx: Fix booting secondary cores - -Add the second part of old 071-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch -We dont modify the patch itself as its upstream and this change is not. - -Originally added by Mantas Pucka Mantas Pucka <mantas@8devices.com> - -Signed-off-by: Robert Marko <robimarko@gmail.com> ---- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -102,6 +102,7 @@ - L2: l2-cache { - compatible = "cache"; - cache-level = <2>; -+ qcom,saw = <&saw_l2>; - }; - }; - -@@ -341,6 +342,12 @@ - regulator; - }; - -+ saw_l2: regulator@b012000 { -+ compatible = "qcom,saw2"; -+ reg = <0xb012000 0x1000>; -+ regulator; -+ }; -+ - blsp1_uart1: serial@78af000 { - compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; - reg = <0x78af000 0x200>; diff --git a/target/linux/ipq40xx/patches-5.4/074-ARM-qcom-Add-IPQ4019-SoC-support.patch b/target/linux/ipq40xx/patches-5.4/074-ARM-qcom-Add-IPQ4019-SoC-support.patch deleted file mode 100644 index 16ac94fd37..0000000000 --- a/target/linux/ipq40xx/patches-5.4/074-ARM-qcom-Add-IPQ4019-SoC-support.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 89b43d59ec8c9cda588555eb1f2754dd19ef5144 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@gmail.com> -Date: Sun, 22 Jul 2018 12:07:57 +0200 -Subject: [PATCH 8/8] ARM: qcom: Add IPQ4019 SoC support - -Add support for the Qualcomm Atheros IPQ4019 SoC. - -Signed-off-by: Christian Lamparter <chunkeey@gmail.com> -Signed-off-by: John Crispin <john@phrozen.org> ---- - arch/arm/Makefile | 1 + - arch/arm/mach-qcom/Kconfig | 5 +++++ - 2 files changed, 6 insertions(+) - ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -156,6 +156,7 @@ textofs-$(CONFIG_ARCH_MSM8X60) := 0x0020 - textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 - textofs-$(CONFIG_ARCH_MESON) := 0x00208000 - textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 -+textofs-$(CONFIG_ARCH_IPQ40XX) := 0x00208000 - - # Machine directory name. This list is sorted alphanumerically - # by CONFIG_* macro name. ---- a/arch/arm/mach-qcom/Kconfig -+++ b/arch/arm/mach-qcom/Kconfig -@@ -28,4 +28,9 @@ config ARCH_MDM9615 - bool "Enable support for MDM9615" - select CLKSRC_QCOM - -+config ARCH_IPQ40XX -+ bool "Enable support for IPQ40XX" -+ select CLKSRC_QCOM -+ select HAVE_ARM_ARCH_TIMER -+ - endif diff --git a/target/linux/ipq40xx/patches-5.4/075-dt-bindings-phy-qcom-ipq4019-usb-add-binding-documen.patch b/target/linux/ipq40xx/patches-5.4/075-dt-bindings-phy-qcom-ipq4019-usb-add-binding-documen.patch deleted file mode 100644 index e7407bcbab..0000000000 --- a/target/linux/ipq40xx/patches-5.4/075-dt-bindings-phy-qcom-ipq4019-usb-add-binding-documen.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 5f01733dc755dfadfa51b7b3c6c160e632fc6002 Mon Sep 17 00:00:00 2001 -From: John Crispin <john@phrozen.org> -Date: Tue, 24 Jul 2018 15:09:36 +0200 -Subject: [PATCH 1/3] dt-bindings: phy-qcom-ipq4019-usb: add binding document - -This patch adds the binding documentation for the HS/SS USB PHY found -inside Qualcom Dakota SoCs. - -Signed-off-by: John Crispin <john@phrozen.org> ---- - .../bindings/phy/phy-qcom-ipq4019-usb.txt | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - create mode 100644 Documentation/devicetree/bindings/phy/phy-qcom-ipq4019-usb.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/phy/phy-qcom-ipq4019-usb.txt -@@ -0,0 +1,21 @@ -+Qualcom Dakota HS/SS USB PHY -+ -+Required properties: -+ - compatible: "qcom,usb-ss-ipq4019-phy", -+ "qcom,usb-hs-ipq4019-phy" -+ - reg: offset and length of the registers -+ - #phy-cells: should be 0 -+ - resets: the reset controllers as listed below -+ - reset-names: the names of the reset controllers -+ "por_rst" - the POR reset line for SS and HS phys -+ "srif_rst" - the SRIF reset line for HS phys -+Example: -+ -+hsphy@a8000 { -+ compatible = "qcom,usb-hs-ipq4019-phy"; -+ phy-cells = <0>; -+ reg = <0xa8000 0x40>; -+ resets = <&gcc USB2_HSPHY_POR_ARES>, -+ <&gcc USB2_HSPHY_S_ARES>; -+ reset-names = "por_rst", "srif_rst"; -+}; diff --git a/target/linux/ipq40xx/patches-5.4/080-ARM-dts-qcom-add-gpio-ranges-property.patch b/target/linux/ipq40xx/patches-5.4/080-ARM-dts-qcom-add-gpio-ranges-property.patch deleted file mode 100644 index 972bdb7497..0000000000 --- a/target/linux/ipq40xx/patches-5.4/080-ARM-dts-qcom-add-gpio-ranges-property.patch +++ /dev/null @@ -1,70 +0,0 @@ -From patchwork Mon May 21 20:57:38 2018 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v5,3/4] ARM: dts: qcom: add gpio-ranges property -X-Patchwork-Submitter: Christian Lamparter <chunkeey@gmail.com> -X-Patchwork-Id: 917856 -Message-Id: <0ae3376606a89bcdf3fe753a5c967f7103699e09.1526935804.git.chunkeey@gmail.com> -To: linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org -Cc: Bjorn Andersson <bjorn.andersson@linaro.org>, - Linus Walleij <linus.walleij@linaro.org>, - Stephen Boyd <sboyd@kernel.org>, David Brown <david.brown@linaro.org>, - Rob Herring <robh+dt@kernel.org>, Mark Rutland <mark.rutland@arm.com>, - Andy Gross <andy.gross@linaro.org>, - Sven Eckelmann <sven.eckelmann@openmesh.com> -Date: Mon, 21 May 2018 22:57:38 +0200 -From: Christian Lamparter <chunkeey@gmail.com> -List-Id: <linux-gpio.vger.kernel.org> - -This patch adds the gpio-ranges property to almost all of -the Qualcomm ARM platforms that utilize the pinctrl-msm -framework. - -The gpio-ranges property is part of the gpiolib subsystem. -As a result, the binding text is available in section -"2.1 gpio- and pin-controller interaction" of -Documentation/devicetree/bindings/gpio/gpio.txt - -For more information please see the patch titled: -"pinctrl: msm: fix gpio-hog related boot issues" from -this series. - -Reported-by: Sven Eckelmann <sven.eckelmann@openmesh.com> -Tested-by: Sven Eckelmann <sven.eckelmann@openmesh.com> [ipq4019] -Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> -Signed-off-by: Christian Lamparter <chunkeey@gmail.com> -Reviewed-by: Linus Walleij <linus.walleij@linaro.org> ---- -To help with git bisect, the DT update patch has been intentionally -placed after the "pinctrl: msm: fix gpio-hog related boot issues". -Otherwise - if the order was reveresed - and bisect decides to split -between these two patches, the gpiochip_add_pin_ranges() function -will be executed twice with the same parameters for the same pinctrl. ---- - arch/arm/boot/dts/qcom-apq8064.dtsi | 1 + - arch/arm/boot/dts/qcom-apq8084.dtsi | 1 + - arch/arm/boot/dts/qcom-ipq4019.dtsi | 1 + - arch/arm/boot/dts/qcom-ipq8064.dtsi | 1 + - arch/arm/boot/dts/qcom-mdm9615.dtsi | 1 + - arch/arm/boot/dts/qcom-msm8660.dtsi | 1 + - arch/arm/boot/dts/qcom-msm8960.dtsi | 1 + - arch/arm/boot/dts/qcom-msm8974.dtsi | 1 + - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 3 ++- - arch/arm64/boot/dts/qcom/msm8916.dtsi | 1 + - arch/arm64/boot/dts/qcom/msm8992.dtsi | 1 + - arch/arm64/boot/dts/qcom/msm8994.dtsi | 1 + - arch/arm64/boot/dts/qcom/msm8996.dtsi | 1 + - 13 files changed, 14 insertions(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -201,6 +201,7 @@ - compatible = "qcom,ipq4019-pinctrl"; - reg = <0x01000000 0x300000>; - gpio-controller; -+ gpio-ranges = <&tlmm 0 0 100>; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; diff --git a/target/linux/ipq40xx/patches-5.4/100-arm-dts-IPQ4019-add-SDHCI-VQMMC-LDO-node.patch b/target/linux/ipq40xx/patches-5.4/101-arm-dts-IPQ4019-add-SDHCI-VQMMC-LDO-node.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/100-arm-dts-IPQ4019-add-SDHCI-VQMMC-LDO-node.patch rename to target/linux/ipq40xx/patches-5.4/101-arm-dts-IPQ4019-add-SDHCI-VQMMC-LDO-node.patch diff --git a/target/linux/ipq40xx/patches-5.4/077-qcom-ipq4019-add-USB-devicetree-nodes.patch b/target/linux/ipq40xx/patches-5.4/102-ARM-dts-qcom-ipq4019-add-USB-devicetree-nodes.patch similarity index 70% rename from target/linux/ipq40xx/patches-5.4/077-qcom-ipq4019-add-USB-devicetree-nodes.patch rename to target/linux/ipq40xx/patches-5.4/102-ARM-dts-qcom-ipq4019-add-USB-devicetree-nodes.patch index 5efc08bcca..b033a1baee 100644 --- a/target/linux/ipq40xx/patches-5.4/077-qcom-ipq4019-add-USB-devicetree-nodes.patch +++ b/target/linux/ipq40xx/patches-5.4/102-ARM-dts-qcom-ipq4019-add-USB-devicetree-nodes.patch @@ -1,49 +1,23 @@ -From 1fc7d5523e21ed140fed43c4dde011a3b6d9ba08 Mon Sep 17 00:00:00 2001 +From 193856b5fe11c50a0b6ff22457dd674c1a45fec6 Mon Sep 17 00:00:00 2001 From: John Crispin <john@phrozen.org> -Date: Tue, 24 Jul 2018 14:47:55 +0200 -Subject: [PATCH 3/3] qcom: ipq4019: add USB devicetree nodes +Date: Wed, 9 Sep 2020 18:31:03 +0200 +Subject: [PATCH] ARM: dts: qcom: ipq4019: add USB devicetree nodes -This patch makes USB work on the Dakota EVB. +Since we now have driver for the USB PHY, and USB controller is already supported by the DWC3 driver lets add the necessary nodes to DTSI. Signed-off-by: John Crispin <john@phrozen.org> +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Reviewed-by: Vinod Koul <vkoul@kernel.org> --- - arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 20 ++++++++ - arch/arm/boot/dts/qcom-ipq4019.dtsi | 74 +++++++++++++++++++++++++++ - 2 files changed, 94 insertions(+) + arch/arm/boot/dts/qcom-ipq4019.dtsi | 74 +++++++++++++++++++++++++++++ + 1 file changed, 74 insertions(+) ---- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi -@@ -109,5 +109,25 @@ - wifi@a800000 { - status = "ok"; - }; -+ -+ usb3_ss_phy: ssphy@9a000 { -+ status = "okay"; -+ }; -+ -+ usb3_hs_phy: hsphy@a6000 { -+ status = "okay"; -+ }; -+ -+ usb3: usb3@8af8800 { -+ status = "okay"; -+ }; -+ -+ usb2_hs_phy: hsphy@a8000 { -+ status = "okay"; -+ }; -+ -+ usb2: usb2@60f8800 { -+ status = "okay"; -+ }; - }; - }; --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -563,5 +563,79 @@ - "legacy"; - status = "disabled"; +@@ -615,5 +615,79 @@ + reg = <4>; + }; }; + + usb3_ss_phy: ssphy@9a000 { @@ -66,7 +40,7 @@ Signed-off-by: John Crispin <john@phrozen.org> + status = "disabled"; + }; + -+ usb3@8af8800 { ++ usb3: usb3@8af8800 { + compatible = "qcom,dwc3"; + reg = <0x8af8800 0x100>; + #address-cells = <1>; @@ -98,7 +72,7 @@ Signed-off-by: John Crispin <john@phrozen.org> + status = "disabled"; + }; + -+ usb2@60f8800 { ++ usb2: usb2@60f8800 { + compatible = "qcom,dwc3"; + reg = <0x60f8800 0x100>; + #address-cells = <1>; diff --git a/target/linux/ipq40xx/patches-5.4/103-arm-dts-qcom-ipq4019-add-more-labels.patch b/target/linux/ipq40xx/patches-5.4/103-arm-dts-qcom-ipq4019-add-more-labels.patch new file mode 100644 index 0000000000..0e215ee7cf --- /dev/null +++ b/target/linux/ipq40xx/patches-5.4/103-arm-dts-qcom-ipq4019-add-more-labels.patch @@ -0,0 +1,42 @@ +From caa3ee6b094ee18021943504c938919fcac325ec Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Wed, 9 Sep 2020 20:40:33 +0200 +Subject: [PATCH] arm: dts: qcom: ipq4019: add more labels + +Lets add labels to more commonly used nodes for easier modification in board DTS files. + +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +--- + arch/arm/boot/dts/qcom-ipq4019.dtsi | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi +@@ -190,7 +190,7 @@ + reg = <0x1800000 0x60000>; + }; + +- rng@22000 { ++ prng: rng@22000 { + compatible = "qcom,prng"; + reg = <0x22000 0x140>; + clocks = <&gcc GCC_PRNG_AHB_CLK>; +@@ -310,7 +310,7 @@ + status = "disabled"; + }; + +- crypto@8e3a000 { ++ crypto: crypto@8e3a000 { + compatible = "qcom,crypto-v5.1"; + reg = <0x08e3a000 0x6000>; + clocks = <&gcc GCC_CRYPTO_AHB_CLK>, +@@ -396,7 +396,7 @@ + dma-names = "rx", "tx"; + }; + +- watchdog@b017000 { ++ watchdog: watchdog@b017000 { + compatible = "qcom,kpss-wdt", "qcom,kpss-wdt-ipq4019"; + reg = <0xb017000 0x40>; + clocks = <&sleep_clk>; diff --git a/target/linux/ipq40xx/patches-5.4/081-clk-fix-apss-cpu-overclocking.patch b/target/linux/ipq40xx/patches-5.4/104-clk-fix-apss-cpu-overclocking.patch similarity index 100% rename from target/linux/ipq40xx/patches-5.4/081-clk-fix-apss-cpu-overclocking.patch rename to target/linux/ipq40xx/patches-5.4/104-clk-fix-apss-cpu-overclocking.patch diff --git a/target/linux/ipq40xx/patches-5.4/701-dts-ipq4019-add-mdio-node.patch b/target/linux/ipq40xx/patches-5.4/701-dts-ipq4019-add-mdio-node.patch deleted file mode 100644 index b0a69433b1..0000000000 --- a/target/linux/ipq40xx/patches-5.4/701-dts-ipq4019-add-mdio-node.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 09ed737593f71bcca08a537a6c15264a1a6add08 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@gmail.com> -Date: Sun, 20 Nov 2016 01:10:33 +0100 -Subject: [PATCH] dts: ipq4019: add mdio node for ethernet - -This patch adds the mdio device-tree node. -This is where the switch is connected to, so it's needed -for the ethernet interfaces. - -Note: The driver isn't anywhere close to be upstream, -so the info might change. ---- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 28 ++++++++++++++++++++++++++++ - 1 file changed, 28 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -588,6 +588,34 @@ - status = "disabled"; - }; - -+ mdio: mdio@90000 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ compatible = "qcom,ipq4019-mdio"; -+ reg = <0x90000 0x64>; -+ status = "disabled"; -+ -+ ethphy0: ethernet-phy@0 { -+ reg = <0>; -+ }; -+ -+ ethphy1: ethernet-phy@1 { -+ reg = <1>; -+ }; -+ -+ ethphy2: ethernet-phy@2 { -+ reg = <2>; -+ }; -+ -+ ethphy3: ethernet-phy@3 { -+ reg = <3>; -+ }; -+ -+ ethphy4: ethernet-phy@4 { -+ reg = <4>; -+ }; -+ }; -+ - usb3_ss_phy: ssphy@9a000 { - compatible = "qcom,usb-ss-ipq4019-phy"; - #phy-cells = <0>; diff --git a/target/linux/ipq40xx/patches-5.4/705-net-add-qualcomm-ar40xx-phy.patch b/target/linux/ipq40xx/patches-5.4/705-net-add-qualcomm-ar40xx-phy.patch index a90ae0eb89..4cdeab637a 100644 --- a/target/linux/ipq40xx/patches-5.4/705-net-add-qualcomm-ar40xx-phy.patch +++ b/target/linux/ipq40xx/patches-5.4/705-net-add-qualcomm-ar40xx-phy.patch @@ -8,7 +8,7 @@ + tristate "Driver for Qualcomm Atheros IPQ40XX switches" + depends on HAS_IOMEM && OF + select SWCONFIG -+ ---help--- ++ help + This is the driver for Qualcomm Atheros IPQ40XX ESS switches. + endif # PHYLIB diff --git a/target/linux/ipq40xx/patches-5.4/721-dts-ipq4019-add-ethernet-essedma-node.patch b/target/linux/ipq40xx/patches-5.4/721-dts-ipq4019-add-ethernet-essedma-node.patch deleted file mode 100644 index 6424dd5243..0000000000 --- a/target/linux/ipq40xx/patches-5.4/721-dts-ipq4019-add-ethernet-essedma-node.patch +++ /dev/null @@ -1,69 +0,0 @@ -From c611d3780fa101662a822d10acf8feb04ca97409 Mon Sep 17 00:00:00 2001 -From: Christian Lamparter <chunkeey@gmail.com> -Date: Sun, 20 Nov 2016 01:01:10 +0100 -Subject: [PATCH] dts: ipq4019: add ethernet ipqess node - -This patch adds the device-tree node for the ipqess ethernet -interfaces. - -Signed-off-by: Christian Lamparter <chunkeey@gmail.com> ---- - arch/arm/boot/dts/qcom-ipq4019.dtsi | 60 +++++++++++++++++++++++++++++++++++++ - 1 file changed, 60 insertions(+) - ---- a/arch/arm/boot/dts/qcom-ipq4019.dtsi -+++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi -@@ -699,6 +699,53 @@ - }; - }; - -+ gmac: ethernet@c080000 { -+ compatible = "qcom,ipq4019-ess-edma"; -+ reg = <0xc080000 0x8000>; -+ interrupts = <GIC_SPI 65 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 66 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 67 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 68 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 69 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 70 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 71 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 72 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 73 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 74 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 75 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 76 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 77 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 78 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 79 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 80 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 240 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 241 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 242 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 243 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 244 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 245 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 246 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 247 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 248 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 249 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 250 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 251 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 252 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 253 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 254 IRQ_TYPE_EDGE_RISING>, -+ <GIC_SPI 255 IRQ_TYPE_EDGE_RISING>; -+ -+ status = "disabled"; -+ -+ phy-mode = "internal"; -+ fixed-link { -+ speed = <1000>; -+ full-duplex; -+ pause; -+ asym-pause; -+ }; -+ }; -+ - usb3_ss_phy: ssphy@9a000 { - compatible = "qcom,usb-ss-ipq4019-phy"; - #phy-cells = <0>; diff --git a/target/linux/ipq40xx/patches-5.4/900-dts-ipq4019-ap-dk01.1.patch b/target/linux/ipq40xx/patches-5.4/900-dts-ipq4019-ap-dk01.1.patch index 5157079762..5a245eb431 100644 --- a/target/linux/ipq40xx/patches-5.4/900-dts-ipq4019-ap-dk01.1.patch +++ b/target/linux/ipq40xx/patches-5.4/900-dts-ipq4019-ap-dk01.1.patch @@ -56,10 +56,11 @@ }; serial@78af000 { -@@ -110,6 +129,22 @@ +@@ -109,5 +128,41 @@ + wifi@a800000 { status = "ok"; }; - ++ + mdio@90000 { + status = "okay"; + }; @@ -76,9 +77,27 @@ + status = "okay"; + }; + - usb3_ss_phy: ssphy@9a000 { - status = "okay"; - }; ++ usb3_ss_phy: ssphy@9a000 { ++ status = "okay"; ++ }; ++ ++ usb3_hs_phy: hsphy@a6000 { ++ status = "okay"; ++ }; ++ ++ usb3: usb3@8af8800 { ++ status = "okay"; ++ }; ++ ++ usb2_hs_phy: hsphy@a8000 { ++ status = "okay"; ++ }; ++ ++ usb2: usb2@60f8800 { ++ status = "okay"; ++ }; + }; + }; --- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts +++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts @@ -18,5 +18,73 @@ diff --git a/target/linux/ipq40xx/patches-5.4/902-dts-ipq4019-ap-dk04.1.patch b/target/linux/ipq40xx/patches-5.4/902-dts-ipq4019-ap-dk04.1.patch index a34904b6f7..ca32144846 100644 --- a/target/linux/ipq40xx/patches-5.4/902-dts-ipq4019-ap-dk04.1.patch +++ b/target/linux/ipq40xx/patches-5.4/902-dts-ipq4019-ap-dk04.1.patch @@ -102,7 +102,7 @@ }; }; -@@ -89,23 +115,57 @@ +@@ -89,11 +115,11 @@ status = "ok"; cs-gpios = <&tlmm 12 0>; @@ -116,17 +116,17 @@ spi-max-frequency = <24000000>; }; }; +@@ -103,9 +129,48 @@ + perst-gpio = <&tlmm 38 0x1>; + }; -- pci@40000000 { -- status = "ok"; -- perst-gpio = <&tlmm 38 0x1>; + i2c0: i2c@78b7000 { /* BLSP1 QUP2 */ + pinctrl-0 = <&i2c_0_pins>; + pinctrl-names = "default"; + + status = "okay"; - }; - ++ }; ++ qpic-nand@79b0000 { pinctrl-0 = <&nand_pins>; pinctrl-names = "default"; diff --git a/target/linux/ramips/dts/mt7621_xiaomi_mi-router-4a-3g-v2.dtsi b/target/linux/ramips/dts/mt7621_xiaomi_mi-router-4a-3g-v2.dtsi new file mode 100644 index 0000000000..65b299e425 --- /dev/null +++ b/target/linux/ramips/dts/mt7621_xiaomi_mi-router-4a-3g-v2.dtsi @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7621.dtsi" + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/input/input.h> + +/ { + aliases { + led-boot = &led_status_yellow; + led-failsafe = &led_status_yellow; + led-running = &led_status_blue; + led-upgrade = &led_status_yellow; + label-mac-device = &wan; + }; + + chosen { + bootargs = "console=ttyS0,115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led_status_blue: status_blue { + label = "blue:status"; + gpios = <&gpio 8 GPIO_ACTIVE_LOW>; + }; + + led_status_yellow: status_yellow { + label = "yellow:status"; + gpios = <&gpio 10 GPIO_ACTIVE_LOW>; + }; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio 18 GPIO_ACTIVE_LOW>; + linux,code = <KEY_RESTART>; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <80000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "u-boot-env"; + reg = <0x30000 0x10000>; + read-only; + }; + + partition@40000 { + label = "Bdata"; + reg = <0x40000 0x10000>; + read-only; + }; + + factory: partition@50000 { + label = "factory"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "crash"; + reg = <0x60000 0x10000>; + read-only; + }; + + partition@70000 { + label = "cfg_bak"; + reg = <0x70000 0x10000>; + read-only; + }; + + partition@80000 { + label = "overlay"; + reg = <0x80000 0x100000>; + read-only; + }; + + firmware: partition@180000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x180000 0xe80000>; + }; + }; + }; +}; + +&pcie { + status = "okay"; +}; + +&pcie0 { + wifi@0,0 { + compatible = "pci14c3,7662"; + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&factory 0x8000>; + ieee80211-freq-limit = <5000000 6000000>; + }; +}; + +&pcie1 { + wifi@0,0 { + compatible = "pci14c3,7603"; + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&factory 0x0000>; + ieee80211-freq-limit = <2400000 2500000>; + }; +}; + +&gmac0 { + mtd-mac-address = <&factory 0xe000>; +}; + +&switch0 { + ports { + port@2 { + status = "okay"; + label = "lan2"; + }; + + port@3 { + status = "okay"; + label = "lan1"; + }; + + wan: port@4 { + status = "okay"; + label = "wan"; + mtd-mac-address = <&factory 0xe006>; + }; + }; +}; + +&state_default { + gpio { + groups = "jtag", "uart2", "uart3", "wdt"; + function = "gpio"; + }; +}; diff --git a/target/linux/ramips/dts/mt7621_xiaomi_mi-router-4a-gigabit.dts b/target/linux/ramips/dts/mt7621_xiaomi_mi-router-4a-gigabit.dts new file mode 100644 index 0000000000..4387d3b79f --- /dev/null +++ b/target/linux/ramips/dts/mt7621_xiaomi_mi-router-4a-gigabit.dts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7621_xiaomi_mi-router-4a-3g-v2.dtsi" + +/ { + compatible = "xiaomi,mi-router-4a-gigabit", "mediatek,mt7621-soc"; + model = "Xiaomi Mi Router 4A Gigabit Edition"; +}; diff --git a/target/linux/ramips/dts/mt7621_xiaomi_mir3g-v2.dts b/target/linux/ramips/dts/mt7621_xiaomi_mir3g-v2.dts index 3df6931bd4..50c5a7dafb 100644 --- a/target/linux/ramips/dts/mt7621_xiaomi_mir3g-v2.dts +++ b/target/linux/ramips/dts/mt7621_xiaomi_mir3g-v2.dts @@ -1,163 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "mt7621.dtsi" - -#include <dt-bindings/gpio/gpio.h> -#include <dt-bindings/input/input.h> +#include "mt7621_xiaomi_mi-router-4a-3g-v2.dtsi" / { compatible = "xiaomi,mir3g-v2", "mediatek,mt7621-soc"; model = "Xiaomi Mi Router 3G v2"; - - aliases { - led-boot = &led_status_yellow; - led-failsafe = &led_status_yellow; - led-running = &led_status_blue; - led-upgrade = &led_status_yellow; - label-mac-device = &wan; - }; - - chosen { - bootargs = "console=ttyS0,115200n8"; - }; - - leds { - compatible = "gpio-leds"; - - led_status_blue: status_blue { - label = "blue:status"; - gpios = <&gpio 8 GPIO_ACTIVE_LOW>; - }; - - led_status_yellow: status_yellow { - label = "yellow:status"; - gpios = <&gpio 10 GPIO_ACTIVE_LOW>; - }; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&gpio 18 GPIO_ACTIVE_LOW>; - linux,code = <KEY_RESTART>; - }; - }; -}; - -&spi0 { - status = "okay"; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <80000000>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "u-boot"; - reg = <0x0 0x30000>; - read-only; - }; - - partition@30000 { - label = "u-boot-env"; - reg = <0x30000 0x10000>; - read-only; - }; - - partition@40000 { - label = "Bdata"; - reg = <0x40000 0x10000>; - read-only; - }; - - factory: partition@50000 { - label = "factory"; - reg = <0x50000 0x10000>; - read-only; - }; - - partition@60000 { - label = "crash"; - reg = <0x60000 0x10000>; - read-only; - }; - - partition@70000 { - label = "cfg_bak"; - reg = <0x70000 0x10000>; - read-only; - }; - - partition@80000 { - label = "overlay"; - reg = <0x80000 0x100000>; - read-only; - }; - - firmware: partition@180000 { - compatible = "denx,uimage"; - label = "firmware"; - reg = <0x180000 0xe80000>; - }; - }; - }; -}; - -&pcie { - status = "okay"; -}; - -&pcie0 { - wifi@0,0 { - compatible = "pci14c3,7662"; - reg = <0x0000 0 0 0 0>; - mediatek,mtd-eeprom = <&factory 0x8000>; - ieee80211-freq-limit = <5000000 6000000>; - }; -}; - -&pcie1 { - wifi@0,0 { - compatible = "pci14c3,7603"; - reg = <0x0000 0 0 0 0>; - mediatek,mtd-eeprom = <&factory 0x0000>; - ieee80211-freq-limit = <2400000 2500000>; - }; -}; - -&gmac0 { - mtd-mac-address = <&factory 0xe000>; -}; - -&switch0 { - ports { - port@2 { - status = "okay"; - label = "lan2"; - }; - - port@3 { - status = "okay"; - label = "lan1"; - }; - wan: port@4 { - status = "okay"; - label = "wan"; - mtd-mac-address = <&factory 0xe006>; - }; - }; -}; - -&state_default { - gpio { - groups = "jtag", "uart2", "uart3", "wdt"; - function = "gpio"; - }; }; diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index d15af01588..27290db372 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -1225,9 +1225,6 @@ define Device/xiaomi_mir3g-v2 DEVICE_VENDOR := Xiaomi DEVICE_MODEL := Mi Router 3G DEVICE_VARIANT := v2 - DEVICE_ALT0_VENDOR := Xiaomi - DEVICE_ALT0_MODEL := Mi Router 4A - DEVICE_ALT0_VARIANT := Gigabit Edition DEVICE_PACKAGES := kmod-mt7603 kmod-mt76x2 endef TARGET_DEVICES += xiaomi_mir3g-v2 @@ -1251,6 +1248,17 @@ define Device/xiaomi_mir3p endef TARGET_DEVICES += xiaomi_mir3p +define Device/xiaomi_mi-router-4a-gigabit + $(Device/dsa-migration) + $(Device/uimage-lzma-loader) + IMAGE_SIZE := 14848k + DEVICE_VENDOR := Xiaomi + DEVICE_MODEL := Mi Router 4A + DEVICE_VARIANT := Gigabit Edition + DEVICE_PACKAGES := kmod-mt7603 kmod-mt76x2 +endef +TARGET_DEVICES += xiaomi_mi-router-4a-gigabit + define Device/xiaomi_redmi-router-ac2100 $(Device/xiaomi-ac2100) DEVICE_MODEL := Redmi Router AC2100 diff --git a/target/linux/ramips/mt7621/base-files/etc/board.d/02_network b/target/linux/ramips/mt7621/base-files/etc/board.d/02_network index 26063470e8..fccd9c6985 100755 --- a/target/linux/ramips/mt7621/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt7621/base-files/etc/board.d/02_network @@ -17,7 +17,8 @@ ramips_setup_interfaces() lenovo,newifi-d1|\ mikrotik,routerboard-m33g|\ xiaomi,mir3g|\ - xiaomi,mir3g-v2) + xiaomi,mir3g-v2|\ + xiaomi,mi-router-4a-gigabit) ucidef_set_interfaces_lan_wan "lan1 lan2" "wan" ;; edimax,re23s|\ diff --git a/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h index 4050661d33..7759c0cac6 100644 --- a/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h +++ b/target/linux/rtl838x/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl838x.h @@ -429,6 +429,9 @@ struct rtl838x_soc_info { volatile void *icu_base; }; +extern struct rtl838x_soc_info soc_info; +extern struct mutex smi_lock; + void rtl838x_soc_detect(struct rtl838x_soc_info *i); #endif /* _MACH_RTL838X_H_ */ diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c index 0c3a311d1d..40e1269c92 100644 --- a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/irq.c @@ -20,8 +20,6 @@ #include <asm/mipsregs.h> #include <mach-rtl838x.h> -extern struct rtl838x_soc_info soc_info; - #define icu_r32(reg) rtl838x_r32(soc_info.icu_base + reg) #define icu_w32(val, reg) rtl838x_w32(val, soc_info.icu_base + reg) #define icu_w32_mask(clear, set, reg) rtl838x_w32_mask(clear, set, soc_info.icu_base + reg) diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c index c1678b8785..81507e235b 100644 --- a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/prom.c @@ -24,7 +24,6 @@ extern char arcs_cmdline[]; const void *fdt; extern const char __appended_dtb; -//extern int __init rtl838x_serial_init(void); void prom_console_init(void) { @@ -67,8 +66,6 @@ char prom_getchar(void) } #endif -struct rtl838x_soc_info soc_info; - const char *get_system_type(void) { return soc_info.name; diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c index 74a29c505e..9d22f6f9e1 100644 --- a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/serial.c @@ -24,7 +24,6 @@ #include <asm/mach-rtl838x/mach-rtl838x.h> extern char arcs_cmdline[]; -extern struct rtl838x_soc_info soc_info; int __init rtl838x_serial_init(void) { diff --git a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c index f59481f76d..d1ccbcee10 100644 --- a/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c +++ b/target/linux/rtl838x/files-5.4/arch/mips/rtl838x/setup.c @@ -28,7 +28,7 @@ #include "mach-rtl838x.h" extern int rtl838x_serial_init(void); -extern struct rtl838x_soc_info soc_info; +struct rtl838x_soc_info soc_info; struct clk { struct clk_lookup cl; diff --git a/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c b/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c index 9b1cd3d70c..00098715fb 100644 --- a/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c +++ b/target/linux/rtl838x/files-5.4/drivers/gpio/gpio-rtl838x.c @@ -38,8 +38,6 @@ struct rtl838x_gpios { int i2c_sdc; }; -extern struct mutex smi_lock; - u32 rtl838x_rtl8231_read(u8 bus_id, u32 reg) { u32 t = 0; diff --git a/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c b/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c index 20d93ae962..b72f08d2fd 100644 --- a/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c +++ b/target/linux/rtl838x/files-5.4/drivers/mtd/spi-nor/rtl838x-nor.c @@ -15,8 +15,6 @@ #include "rtl838x-spi.h" #include <asm/mach-rtl838x/mach-rtl838x.h> -extern struct rtl838x_soc_info soc_info; - struct rtl838x_nor { struct spi_nor nor; struct device *dev; diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h index 2e420a9db4..13fb804436 100644 --- a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h +++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h @@ -54,20 +54,34 @@ #define MAPLE_SDS4_FIB_REG0r (RTL838X_SDS4_REG28 + 0x880) #define MAPLE_SDS5_FIB_REG0r (RTL838X_SDS4_REG28 + 0x980) +/* Registers of the internal Serdes of the 8390 */ +#define RTL8390_SDS0_1_XSG0 (0xA000) +#define RTL8390_SDS0_1_XSG1 (0xA100) +#define RTL839X_SDS12_13_XSG0 (0xB800) +#define RTL839X_SDS12_13_XSG1 (0xB900) +#define RTL839X_SDS12_13_PWR0 (0xb880) +#define RTL839X_SDS12_13_PWR1 (0xb980) + /* VLAN registers */ -#define RTL838X_VLAN_PROFILE(idx) (0x3A88 + ((idx) << 2)) +#define RTL838X_VLAN_PROFILE (0x3A88) #define RTL838X_VLAN_PORT_EGR_FLTR (0x3A84) -#define RTL838X_VLAN_PORT_PB_VLAN(port) (0x3C00 + ((port) << 2)) +#define RTL838X_VLAN_PORT_PB_VLAN (0x3C00) +#define RTL838X_VLAN_PORT_IGR_FLTR (0x3A7C) #define RTL838X_VLAN_PORT_IGR_FLTR_0 (0x3A7C) -#define RTL838X_VLAN_PORT_IGR_FLTR_1 (0x3A7C + 4) +#define RTL838X_VLAN_PORT_IGR_FLTR_1 (0x3A80) +#define RTL839X_VLAN_PROFILE (0x25C0) +#define RTL839X_VLAN_CTRL (0x26D4) +#define RTL839X_VLAN_PORT_PB_VLAN (0x26D8) +#define RTL839X_VLAN_PORT_IGR_FLTR (0x27B4) +#define RTL839X_VLAN_PORT_EGR_FLTR (0x27C4) /* Table 0/1 access registers */ #define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) -#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) +#define RTL838X_TBL_ACCESS_DATA_0 (0x6918) #define RTL838X_TBL_ACCESS_CTRL_1 (0xA4C8) #define RTL838X_TBL_ACCESS_DATA_1(idx) (0xA4CC + ((idx) << 2)) #define RTL839X_TBL_ACCESS_CTRL_0 (0x1190) -#define RTL839X_TBL_ACCESS_DATA_0(idx) (0x1194 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_DATA_0 (0x1194) #define RTL839X_TBL_ACCESS_CTRL_1 (0x6b80) #define RTL839X_TBL_ACCESS_DATA_1(idx) (0x6b84 + ((idx) << 2)) @@ -75,12 +89,18 @@ #define RTL838X_MAC_LINK_STS (0xa188) #define RTL839X_MAC_LINK_STS (0x0390) #define RTL838X_MAC_LINK_SPD_STS(port) (0xa190 + (((port >> 4) << 2))) +#define RTL839X_MAC_LINK_SPD_STS(port) (0x03a0 + (((port >> 4) << 2))) #define RTL838X_MAC_LINK_DUP_STS (0xa19c) +#define RTL839X_MAC_LINK_DUP_STS (0x03b0) #define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) +#define RTL839X_MAC_TX_PAUSE_STS (0x03b8) #define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) +#define RTL839X_MAC_RX_PAUSE_STS (0x03c0) #define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) #define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) +#define RTL838X_DMA_IF_CTRL (0x9f58) + /* MAC link state bits */ #define FORCE_EN (1 << 0) #define FORCE_LINK_EN (1 << 1) @@ -105,21 +125,31 @@ #define RTL838X_TBL_ACCESS_L2_CTRL (0x6900) #define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) #define RTL838X_TBL_ACCESS_L2_DATA(idx) (0x6908 + ((idx) << 2)) -#define RTL838X_TBL_ACCESS_L2_DATA(idx) (0x6908 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2)) #define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) #define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) +#define RTL838X_L2_PORT_NEW_SALRN(p) (0x328c + (((p >> 4) << 2))) +#define RTL839X_L2_PORT_NEW_SALRN(p) (0x38F0 + (((p >> 4) << 2))) +#define RTL838X_L2_PORT_NEW_SA_FWD(p) (0x3294 + (((p >> 4) << 2))) +#define RTL839X_L2_PORT_NEW_SA_FWD(p) (0x3900 + (((p >> 4) << 2))) +#define RTL838X_L2_PORT_SALRN(p) (0x328c + (((p >> 4) << 2))) +#define RTL839X_L2_PORT_SALRN(p) (0x38F0 + (((p >> 4) << 2))) /* Port Mirroring */ #define RTL838X_MIR_CTRL(grp) (0x5D00 + (((grp) << 2))) #define RTL838X_MIR_DPM_CTRL(grp) (0x5D20 + (((grp) << 2))) #define RTL838X_MIR_SPM_CTRL(grp) (0x5D10 + (((grp) << 2))) +#define RTL839X_MIR_CTRL(grp) (0x2500 + (((grp) << 2))) +#define RTL839X_MIR_DPM_CTRL(grp) (0x2530 + (((grp) << 2))) +#define RTL839X_MIR_SPM_CTRL(grp) (0x2510 + (((grp) << 2))) enum phy_type { PHY_NONE = 0, PHY_RTL838X_SDS = 1, PHY_RTL8218B_INT = 2, PHY_RTL8218B_EXT = 3, - PHY_RTL8214FC = 4 + PHY_RTL8214FC = 4, + PHY_RTL839X_SDS = 5, }; struct rtl838x_port { @@ -133,20 +163,51 @@ struct rtl838x_port { struct rtl838x_vlan_info { u64 untagged_ports; u64 tagged_ports; - u32 vlan_conf; + u8 profile_id; + bool hash_mc; + bool hash_uc; + u8 fid; +}; + +enum l2_entry_type { + L2_INVALID = 0, + L2_UNICAST = 1, + L2_MULTICAST = 2, + IP4_MULTICAST = 3, + IP6_MULTICAST = 4, +}; + +struct rtl838x_l2_entry { + u8 mac[ETH_ALEN]; + u16 vid; + u16 rvid; + u8 port; + bool valid; + enum l2_entry_type type; + bool is_static; + bool is_ip_mc; + bool is_ipv6_mc; + bool block_da; + bool block_sa; + bool suspended; + bool next_hop; + int age; + u16 mc_portmask_index; }; struct rtl838x_switch_priv; struct rtl838x_reg { - void (*mask_port_reg)(u64 clear, u64 set, int reg); - void (*set_port_reg)(u64 set, int reg); - u64 (*get_port_reg)(int reg); + void (*mask_port_reg_be)(u64 clear, u64 set, int reg); + void (*set_port_reg_be)(u64 set, int reg); + u64 (*get_port_reg_be)(int reg); + void (*mask_port_reg_le)(u64 clear, u64 set, int reg); + void (*set_port_reg_le)(u64 set, int reg); + u64 (*get_port_reg_le)(int reg); int stat_port_rst; int stat_rst; int (*stat_port_std_mib)(int p); - void (*mask_port_iso_ctrl)(u64 clear, u64 set, int port); - void (*set_port_iso_ctrl)(u64 set, int port); + int (*port_iso_ctrl)(int p); int l2_ctrl_0; int l2_ctrl_1; int l2_port_aging_out; @@ -160,10 +221,26 @@ struct rtl838x_reg { int imr_port_link_sts_chg; int imr_glb; void (*vlan_tables_read)(u32 vlan, struct rtl838x_vlan_info *info); - void (*vlan_set_tagged)(u32 vlan, u64 portmask, u32 conf); + void (*vlan_set_tagged)(u32 vlan, const struct rtl838x_vlan_info *info); void (*vlan_set_untagged)(u32 vlan, u64 portmask); int (*mac_force_mode_ctrl)(int port); - int rst_glb_ctrl; + int (*mac_port_ctrl)(int port); + int (*l2_port_new_salrn)(int port); + int (*l2_port_new_sa_fwd)(int port); + int (*mir_ctrl)(int group); + int (*mir_dpm)(int group); + int (*mir_spm)(int group); + int mac_link_sts; + int mac_link_dup_sts; + int (*mac_link_spd_sts)(int port); + int mac_rx_pause_sts; + int mac_tx_pause_sts; + u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e); + u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e); + int (*vlan_profile)(int profile); + int (*vlan_port_egr_filter)(int port); + int (*vlan_port_igr_filter)(int port); + int (*vlan_port_pb)(int port); }; struct rtl838x_switch_priv { @@ -181,6 +258,7 @@ struct rtl838x_switch_priv { const struct rtl838x_reg *r; u8 cpu_port; u8 port_mask; + u32 fib_entries; }; extern struct rtl838x_soc_info soc_info; diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c index 7f6c2d6a29..9b9dda1891 100644 --- a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c +++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_sw.c @@ -13,6 +13,7 @@ #include <linux/phylink.h> #include <linux/phy_fixed.h> #include <net/dsa.h> +#include <net/switchdev.h> #include <asm/mach-rtl838x/mach-rtl838x.h> #include "rtl838x.h" @@ -30,34 +31,46 @@ struct rtl838x_mib_desc { const char *name; }; -inline void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) +static inline void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) { sw_w32_mask((u32)clear, (u32)set, reg); } -inline void rtl839x_mask_port_reg(u64 clear, u64 set, int reg) -{ - sw_w32_mask((u32) (clear >> 32), (u32) (set >> 32), reg); - sw_w32_mask((u32) (clear & 0xffffffff), (u32) (set & 0xffffffff), reg + 4); -} - -inline void rtl838x_set_port_reg(u64 set, int reg) +static inline void rtl838x_set_port_reg(u64 set, int reg) { sw_w32(set, reg); } -inline void rtl839x_set_port_reg(u64 set, int reg) +static inline u64 rtl838x_get_port_reg(int reg) +{ + return ((u64) sw_r32(reg)); +} + +static inline void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg); + sw_w32_mask((u32)(clear & 0xffffffff), (u32)(set & 0xffffffff), reg + 4); +} + +static inline void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)clear, (u32)set, reg); + sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg + 4); +} + +static inline void rtl839x_set_port_reg_be(u64 set, int reg) { sw_w32(set >> 32, reg); sw_w32(set & 0xffffffff, reg + 4); } -inline u64 rtl838x_get_port_reg(int reg) +static inline void rtl839x_set_port_reg_le(u64 set, int reg) { - return ((u64) sw_r32(reg)); + sw_w32(set, reg); + sw_w32(set >> 32, reg + 4); } -inline u64 rtl839x_get_port_reg(int reg) +static inline u64 rtl839x_get_port_reg_be(int reg) { u64 v = sw_r32(reg); @@ -66,77 +79,114 @@ inline u64 rtl839x_get_port_reg(int reg) return v; } -inline int rtl838x_stat_port_std_mib(int p) +static inline u64 rtl839x_get_port_reg_le(int reg) +{ + u64 v = sw_r32(reg + 4); + + v <<= 32; + v |= sw_r32(reg); + return v; +} + +static inline int rtl838x_stat_port_std_mib(int p) { return RTL838X_STAT_PORT_STD_MIB + (p << 8); } -inline int rtl839x_stat_port_std_mib(int p) +static inline int rtl839x_stat_port_std_mib(int p) { return RTL839X_STAT_PORT_STD_MIB + (p << 8); } -inline void rtl838x_mask_port_iso_ctrl(u64 clear, u64 set, int port) +static inline int rtl838x_port_iso_ctrl(int p) { - sw_w32_mask(clear, set, RTL838X_PORT_ISO_CTRL(port)); + return RTL838X_PORT_ISO_CTRL(p); } -inline void rtl839x_mask_port_iso_ctrl(u64 clear, u64 set, int port) +static inline int rtl839x_port_iso_ctrl(int p) { - sw_w32_mask(clear >> 32, set >> 32, RTL839X_PORT_ISO_CTRL(port)); - sw_w32_mask(clear & 0xffffffff, set & 0xffffffff, - RTL839X_PORT_ISO_CTRL(port) + 4); + return RTL839X_PORT_ISO_CTRL(p); } -inline void rtl838x_set_port_iso_ctrl(u64 set, int port) -{ - sw_w32(set, RTL838X_PORT_ISO_CTRL(port)); -} - -inline void rtl839x_set_port_iso_ctrl(u64 set, int port) -{ - sw_w32(set >> 32, RTL839X_PORT_ISO_CTRL(port)); - sw_w32(set & 0xffffffff, RTL839X_PORT_ISO_CTRL(port) + 4); -} - -inline void rtl838x_exec_tbl0_cmd(u32 cmd) +static inline void rtl838x_exec_tbl0_cmd(u32 cmd) { sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_0); do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_0) & (1 << 15)); } -inline void rtl839x_exec_tbl0_cmd(u32 cmd) +static inline void rtl839x_exec_tbl0_cmd(u32 cmd) { sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_0); do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_0) & (1 << 16)); } -inline void rtl838x_exec_tbl1_cmd(u32 cmd) +static inline void rtl838x_exec_tbl1_cmd(u32 cmd) { sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_1); do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_1) & (1 << 15)); } -inline void rtl839x_exec_tbl1_cmd(u32 cmd) +static inline void rtl839x_exec_tbl1_cmd(u32 cmd) { sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_1); do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_1) & (1 << 16)); } -inline int rtl838x_tbl_access_data_0(int i) +static inline int rtl838x_tbl_access_data_0(int i) { - return RTL838X_TBL_ACCESS_DATA_0(i); + return RTL838X_TBL_ACCESS_DATA_0 + (i << 2); } -inline int rtl839x_tbl_access_data_0(int i) +static inline int rtl839x_tbl_access_data_0(int i) { - return RTL839X_TBL_ACCESS_DATA_0(i); + return RTL839X_TBL_ACCESS_DATA_0 + (i << 2); +} + +static inline int rtl838x_vlan_profile(int profile) +{ + return RTL838X_VLAN_PROFILE + (profile << 2); +} + +static inline int rtl839x_vlan_profile(int profile) +{ + return RTL839X_VLAN_PROFILE + (profile << 3); +} + +static inline int rtl838x_vlan_port_egr_filter(int port) +{ + return RTL838X_VLAN_PORT_EGR_FLTR; +} + +static inline int rtl839x_vlan_port_egr_filter(int port) +{ + return RTL839X_VLAN_PORT_EGR_FLTR + ((port >> 5) << 2); +} + +static inline int rtl838x_vlan_port_igr_filter(int port) +{ + return RTL838X_VLAN_PORT_IGR_FLTR + ((port >> 4) << 2); +} + +static inline int rtl839x_vlan_port_igr_filter(int port) +{ + return RTL839X_VLAN_PORT_IGR_FLTR + ((port >> 4) << 2); +} + +static inline int rtl838x_vlan_port_pb(int port) +{ + return RTL838X_VLAN_PORT_PB_VLAN + (port << 2); +} + +static inline int rtl839x_vlan_port_pb(int port) +{ + return RTL839X_VLAN_PORT_PB_VLAN + (port << 2); } static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) { u32 cmd; u64 v; + u32 u, w; cmd = 1 << 16 /* Execute cmd */ | 0 << 15 /* Read */ @@ -144,12 +194,18 @@ static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) | (vlan & 0xfff); rtl839x_exec_tbl0_cmd(cmd); - v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); + v = sw_r32(rtl838x_tbl_access_data_0(0)); v <<= 32; - v |= sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); + u = sw_r32(rtl838x_tbl_access_data_0(1)); + v |= u; info->tagged_ports = v >> 11; - info->vlan_conf = (v & 0x7ff) << 2; - info->vlan_conf |= sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)) >> 30; + + w = sw_r32(rtl838x_tbl_access_data_0(2)); + + info->profile_id = w >> 30 | ((u & 1) << 2); + info->hash_mc = !!(u & 2); + info->hash_uc = !!(u & 4); + info->fid = (u >> 3) & 0xff; cmd = 1 << 16 /* Execute cmd */ | 0 << 15 /* Read */ @@ -164,15 +220,20 @@ static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) { - u32 cmd; + u32 cmd, v; cmd = 1 << 15 /* Execute cmd */ | 1 << 14 /* Read */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); rtl838x_exec_tbl0_cmd(cmd); - info->tagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); - info->vlan_conf = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); + info->tagged_ports = sw_r32(rtl838x_tbl_access_data_0(0)); + v = sw_r32(rtl838x_tbl_access_data_0(1)); + info->profile_id = v & 0x7; + info->hash_mc = !!(v & 0x8); + info->hash_uc = !!(v & 0x10); + info->fid = (v >> 5) & 0x3f; + cmd = 1 << 15 /* Execute cmd */ | 1 << 14 /* Read */ @@ -182,29 +243,42 @@ static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) info->untagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0)); } -static void rtl839x_vlan_set_tagged(u32 vlan, u64 portmask, u32 conf) +static void rtl839x_vlan_set_tagged(u32 vlan, const struct rtl838x_vlan_info *info) { u32 cmd = 1 << 16 /* Execute cmd */ | 1 << 15 /* Write */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); - u64 v = portmask << 11; + u32 w; + u64 v = info->tagged_ports << 11; - v |= (conf >> 2) & 0x7ff; - sw_w64(v, RTL838X_TBL_ACCESS_DATA_0(0)); - sw_w32(conf << 30, RTL838X_TBL_ACCESS_DATA_0(2)); + v |= info->profile_id >> 2; + v |= info->hash_mc ? 2 : 0; + v |= info->hash_uc ? 4 : 0; + v |= ((u32)info->fid) << 3; + rtl839x_set_port_reg_be(v, rtl838x_tbl_access_data_0(0)); + + w = info->profile_id; + sw_w32(w << 30, rtl838x_tbl_access_data_0(2)); rtl839x_exec_tbl0_cmd(cmd); } -static void rtl838x_vlan_set_tagged(u32 vlan, u64 portmask, u32 conf) +static void rtl838x_vlan_set_tagged(u32 vlan, const struct rtl838x_vlan_info *info) { u32 cmd = 1 << 15 /* Execute cmd */ | 0 << 14 /* Write */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); + u32 v; - sw_w32(portmask, RTL838X_TBL_ACCESS_DATA_0(0)); - sw_w32(conf, RTL838X_TBL_ACCESS_DATA_0(1)); + sw_w32(info->tagged_ports, rtl838x_tbl_access_data_0(0)); + + v = info->profile_id; + v |= info->hash_mc ? 0x8 : 0; + v |= info->hash_uc ? 0x10 : 0; + v |= ((u32)info->fid) << 5; + + sw_w32(v, rtl838x_tbl_access_data_0(1)); rtl838x_exec_tbl0_cmd(cmd); } @@ -214,7 +288,7 @@ static void rtl839x_vlan_set_untagged(u32 vlan, u64 portmask) | 1 << 15 /* Write */ | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); - sw_w64(portmask << 11, RTL838X_TBL_ACCESS_DATA_1(0)); + rtl839x_set_port_reg_be(portmask << 11, RTL838X_TBL_ACCESS_DATA_1(0)); rtl839x_exec_tbl1_cmd(cmd); } @@ -238,15 +312,272 @@ static inline int rtl839x_mac_force_mode_ctrl(int p) return RTL839X_MAC_FORCE_MODE_CTRL + (p << 2); } +static inline int rtl838x_mac_port_ctrl(int p) +{ + return RTL838X_MAC_PORT_CTRL(p); +} + +static inline int rtl839x_mac_port_ctrl(int p) +{ + return RTL839X_MAC_PORT_CTRL(p); +} + +static inline int rtl838x_l2_port_new_salrn(int p) +{ + return RTL838X_L2_PORT_NEW_SALRN(p); +} + +static inline int rtl839x_l2_port_new_salrn(int p) +{ + return RTL839X_L2_PORT_NEW_SALRN(p); +} + +static inline int rtl838x_l2_port_new_sa_fwd(int p) +{ + return RTL838X_L2_PORT_NEW_SA_FWD(p); +} + +static inline int rtl839x_l2_port_new_sa_fwd(int p) +{ + return RTL839X_L2_PORT_NEW_SA_FWD(p); +} + +static inline int rtl838x_mac_link_spd_sts(int p) +{ + return RTL838X_MAC_LINK_SPD_STS(p); +} + +static inline int rtl839x_mac_link_spd_sts(int p) +{ + return RTL839X_MAC_LINK_SPD_STS(p); +} + +static inline int rtl838x_mir_ctrl(int group) +{ + return RTL838X_MIR_CTRL(group); +} + +static inline int rtl839x_mir_ctrl(int group) +{ + return RTL839X_MIR_CTRL(group); +} + +static inline int rtl838x_mir_dpm(int group) +{ + return RTL838X_MIR_DPM_CTRL(group); +} + +static inline int rtl839x_mir_dpm(int group) +{ + return RTL839X_MIR_DPM_CTRL(group); +} + +static inline int rtl838x_mir_spm(int group) +{ + return RTL838X_MIR_SPM_CTRL(group); +} + +static inline int rtl839x_mir_spm(int group) +{ + return RTL839X_MIR_SPM_CTRL(group); +} + +static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + + /* Search in SRAM, with hash and at position in hash bucket (0-3) */ + u32 idx = (0 << 14) | (hash << 2) | position; + + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Read */ + | 0 << 13 /* Table type 0b00 */ + | (idx & 0x1fff); + + sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); + r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); + + e->mac[0] = (r[1] >> 20); + e->mac[1] = (r[1] >> 12); + e->mac[2] = (r[1] >> 4); + e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + e->mac[4] = (r[2] >> 20); + e->mac[5] = (r[2] >> 12); + e->is_static = !!((r[0] >> 19) & 1); + e->vid = r[0] & 0xfff; + e->rvid = r[2] & 0xfff; + e->port = (r[0] >> 12) & 0x1f; + + e->valid = true; + if (!(r[0] >> 17)) /* Check for invalid entry */ + e->valid = false; + + if (e->valid) + pr_info("Found in Hash: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + + entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); + return entry; +} + +static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + + /* Search in SRAM, with hash and at position in hash bucket (0-3) */ + u32 idx = (0 << 14) | (hash << 2) | position; + + u32 cmd = 1 << 17 /* Execute cmd */ + | 0 << 16 /* Read */ + | 0 << 14 /* Table type 0b00 */ + | (idx & 0x3fff); + + sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); + r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); + + /* Table contains different entry types, we need to identify the right one: + * Check for MC entries, first + */ + e->is_ip_mc = !!(r[2] & (1 << 31)); + e->is_ipv6_mc = !!(r[2] & (1 << 30)); + e->type = L2_INVALID; + if (!e->is_ip_mc) { + e->mac[0] = (r[0] >> 12); + e->mac[1] = (r[0] >> 4); + e->mac[2] = ((r[1] >> 28) | (r[0] << 4)); + e->mac[3] = (r[1] >> 20); + e->mac[4] = (r[1] >> 12); + e->mac[5] = (r[1] >> 4); + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->is_static = !!((r[2] >> 18) & 1); + e->vid = (r[2] >> 4) & 0xfff; + e->rvid = (r[0] >> 20) & 0xfff; + e->port = (r[2] >> 24) & 0x3f; + e->block_da = !!(r[2] & (1 << 19)); + e->block_sa = !!(r[2] & (1 << 20)); + e->suspended = !!(r[2] & (1 << 17)); + e->next_hop = !!(r[2] & (1 << 16)); + if (e->next_hop) + pr_info("Found next hop entry, need to read data\n"); + e->age = (r[2] >> 21) & 3; + e->valid = true; + if (!(r[2] & 0xc0fd0000)) /* Check for valid entry */ + e->valid = false; + else + e->type = L2_UNICAST; + } else { + e->valid = true; + e->type = L2_MULTICAST; + e->mc_portmask_index = (r[2] >> 6) & 0xfff; + } + } + if (e->is_ip_mc) { + e->valid = true; + e->type = IP4_MULTICAST; + } + if (e->is_ipv6_mc) { + e->valid = true; + e->type = IP6_MULTICAST; + } + + entry = (((u64) r[0]) << 44) | ((u64)(r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff); + return entry; +} + +static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Read */ + | 1 << 13 /* Table type 0b01 */ + | (idx & 0x3f); + sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); + r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); + + e->mac[0] = (r[1] >> 20); + e->mac[1] = (r[1] >> 12); + e->mac[2] = (r[1] >> 4); + e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + e->mac[4] = (r[2] >> 20); + e->mac[5] = (r[2] >> 12); + e->is_static = !!((r[0] >> 19) & 1); + e->vid = r[0] & 0xfff; + e->rvid = r[2] & 0xfff; + e->port = (r[0] >> 12) & 0x1f; + + e->valid = true; + if (!(r[0] >> 17)) /* Check for invalid entry */ + e->valid = false; + + if (e->valid) + pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + + entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); + return entry; +} + +static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + + u32 cmd = 1 << 17 /* Execute cmd */ + | 0 << 16 /* Read */ + | 1 << 14 /* Table type 0b01 */ + | (idx & 0x3f); + sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); + r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); + + e->mac[0] = (r[0] >> 12); + e->mac[1] = (r[0] >> 4); + e->mac[2] = ((r[1] >> 28) | (r[0] << 4)); + e->mac[3] = (r[1] >> 20); + e->mac[4] = (r[1] >> 12); + e->mac[5] = (r[1] >> 4); + e->is_static = !!((r[2] >> 18) & 1); + e->vid = (r[2] >> 4) & 0xfff; + e->rvid = (r[0] >> 20) & 0xfff; + e->port = (r[2] >> 24) & 0x3f; + + e->valid = true; + if (!(r[2] & 0x10fd0000)) /* Check for invalid entry */ + e->valid = false; + + if (e->valid) + pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + + entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff); + return entry; +} + static const struct rtl838x_reg rtl838x_reg = { - .mask_port_reg = rtl838x_mask_port_reg, - .set_port_reg = rtl838x_set_port_reg, - .get_port_reg = rtl838x_get_port_reg, + .mask_port_reg_be = rtl838x_mask_port_reg, + .set_port_reg_be = rtl838x_set_port_reg, + .get_port_reg_be = rtl838x_get_port_reg, + .mask_port_reg_le = rtl838x_mask_port_reg, + .set_port_reg_le = rtl838x_set_port_reg, + .get_port_reg_le = rtl838x_get_port_reg, .stat_port_rst = RTL838X_STAT_PORT_RST, .stat_rst = RTL838X_STAT_RST, .stat_port_std_mib = rtl838x_stat_port_std_mib, - .mask_port_iso_ctrl = rtl838x_mask_port_iso_ctrl, - .set_port_iso_ctrl = rtl838x_set_port_iso_ctrl, + .port_iso_ctrl = rtl838x_port_iso_ctrl, .l2_ctrl_0 = RTL838X_L2_CTRL_0, .l2_ctrl_1 = RTL838X_L2_CTRL_1, .l2_port_aging_out = RTL838X_L2_PORT_AGING_OUT, @@ -263,18 +594,36 @@ static const struct rtl838x_reg rtl838x_reg = { .vlan_set_tagged = rtl838x_vlan_set_tagged, .vlan_set_untagged = rtl838x_vlan_set_untagged, .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, - .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, + .mac_port_ctrl = rtl838x_mac_port_ctrl, + .l2_port_new_salrn = rtl838x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl838x_l2_port_new_sa_fwd, + .mir_ctrl = rtl838x_mir_ctrl, + .mir_dpm = rtl838x_mir_dpm, + .mir_spm = rtl838x_mir_spm, + .mac_link_sts = RTL838X_MAC_LINK_STS, + .mac_link_dup_sts = RTL838X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl838x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL838X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL838X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl838x_read_l2_entry_using_hash, + .read_cam = rtl838x_read_cam, + .vlan_profile = rtl838x_vlan_profile, + .vlan_port_egr_filter = rtl838x_vlan_port_egr_filter, + .vlan_port_igr_filter = rtl838x_vlan_port_igr_filter, + .vlan_port_pb = rtl838x_vlan_port_pb, }; static const struct rtl838x_reg rtl839x_reg = { - .mask_port_reg = rtl839x_mask_port_reg, - .set_port_reg = rtl839x_set_port_reg, - .get_port_reg = rtl839x_get_port_reg, + .mask_port_reg_be = rtl839x_mask_port_reg_be, + .set_port_reg_be = rtl839x_set_port_reg_be, + .get_port_reg_be = rtl839x_get_port_reg_be, + .mask_port_reg_le = rtl839x_mask_port_reg_le, + .set_port_reg_le = rtl839x_set_port_reg_le, + .get_port_reg_le = rtl839x_get_port_reg_le, .stat_port_rst = RTL839X_STAT_PORT_RST, .stat_rst = RTL839X_STAT_RST, .stat_port_std_mib = rtl839x_stat_port_std_mib, - .mask_port_iso_ctrl = rtl839x_mask_port_iso_ctrl, - .set_port_iso_ctrl = rtl839x_set_port_iso_ctrl, + .port_iso_ctrl = rtl839x_port_iso_ctrl, .l2_ctrl_0 = RTL839X_L2_CTRL_0, .l2_ctrl_1 = RTL839X_L2_CTRL_1, .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT, @@ -291,7 +640,23 @@ static const struct rtl838x_reg rtl839x_reg = { .vlan_set_tagged = rtl839x_vlan_set_tagged, .vlan_set_untagged = rtl839x_vlan_set_untagged, .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, - .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, + .mac_port_ctrl = rtl839x_mac_port_ctrl, + .l2_port_new_salrn = rtl839x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl839x_l2_port_new_sa_fwd, + .mir_ctrl = rtl839x_mir_ctrl, + .mir_dpm = rtl839x_mir_dpm, + .mir_spm = rtl839x_mir_spm, + .mac_link_sts = RTL839X_MAC_LINK_STS, + .mac_link_dup_sts = RTL839X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl839x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash, + .read_cam = rtl839x_read_cam, + .vlan_profile = rtl839x_vlan_profile, + .vlan_port_egr_filter = rtl839x_vlan_port_egr_filter, + .vlan_port_igr_filter = rtl839x_vlan_port_igr_filter, + .vlan_port_pb = rtl839x_vlan_port_pb, }; static const struct rtl838x_mib_desc rtl838x_mib[] = { @@ -353,7 +718,7 @@ static irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) /* Clear status */ sw_w32(ports, RTL838X_ISR_PORT_LINK_STS_CHG); - pr_info("Link change: status: %x, ports %x\n", status, ports); + pr_info("RTL8380 Link change: status: %x, ports %x\n", status, ports); for (i = 0; i < 28; i++) { if (ports & (1 << i)) { @@ -371,18 +736,18 @@ static irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) { struct dsa_switch *ds = dev_id; u32 status = sw_r32(RTL839X_ISR_GLB_SRC); - u64 ports = sw_r64(RTL839X_ISR_PORT_LINK_STS_CHG); + u64 ports = rtl839x_get_port_reg_le(RTL839X_ISR_PORT_LINK_STS_CHG); u64 link; int i; /* Clear status */ - sw_w64(ports, RTL839X_ISR_PORT_LINK_STS_CHG); - pr_info("Link change: status: %x, ports %llx\n", status, ports); + rtl839x_set_port_reg_le(ports, RTL839X_ISR_PORT_LINK_STS_CHG); + pr_info("RTL8390 Link change: status: %x, ports %llx\n", status, ports); for (i = 0; i < 52; i++) { - if (ports & (1 << i)) { - link = sw_r64(RTL839X_MAC_LINK_STS); - if (link & (1 << i)) + if (ports & (1ULL << i)) { + link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS); + if (link & (1ULL << i)) dsa_port_phylink_mac_change(ds, i, true); else dsa_port_phylink_mac_change(ds, i, false); @@ -391,6 +756,35 @@ static irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) return IRQ_HANDLED; } +struct fdb_update_work { + struct work_struct work; + struct net_device *ndev; + u64 macs[]; +}; + +void rtl838x_fdb_sync(struct work_struct *work) +{ + const struct fdb_update_work *uw = + container_of(work, struct fdb_update_work, work); + struct switchdev_notifier_fdb_info info; + u8 addr[ETH_ALEN]; + int i = 0; + int action; + + while (uw->macs[i]) { + action = (uw->macs[i] & (1ULL << 63)) ? SWITCHDEV_FDB_ADD_TO_BRIDGE + : SWITCHDEV_FDB_DEL_TO_BRIDGE; + u64_to_ether_addr(uw->macs[i] & 0xffffffffffffULL, addr); + info.addr = &addr[0]; + info.vid = 0; + info.offloaded = 1; + pr_debug("FDB entry %d: %llx, action %d\n", i, uw->macs[0], action); + call_switchdev_notifiers(action, uw->ndev, &info.info, NULL); + i++; + } + kfree(work); +} + int rtl8380_sds_power(int mac, int val) { u32 mode = (val == 1) ? 0x4 : 0x9; @@ -408,6 +802,24 @@ int rtl8380_sds_power(int mac, int val) return 0; } +int rtl8390_sds_power(int mac, int val) +{ + u32 offset = (mac == 48) ? 0x0 : 0x100; + u32 mode = val ? 0 : 1; + + pr_info("In %s: mac %d, set %d\n", __func__, mac, val); + + if ((mac != 48) && (mac != 49)) { + pr_err("%s: not an SFP port: %d\n", __func__, mac); + return -1; + } + + // Set bit 1003. 1000 starts at 7c + sw_w32_mask(1 << 11, mode << 11, RTL839X_SDS12_13_PWR0 + offset); + + return 0; +} + static int rtl838x_smi_wait_op(int timeout) { do { @@ -499,7 +911,12 @@ int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val) u32 v; u32 park_page; - if (port > 31 || page > 4095 || reg > 31) + if (port > 31) { + *val = 0xffff; + return 0; + } + + if (page > 4095 || reg > 31) return -ENOTSUPP; mutex_lock(&smi_lock); @@ -607,7 +1024,7 @@ timeout: } /* - * Read to an mmd register of the PHY + * Read an mmd register of the PHY */ int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) { @@ -748,11 +1165,11 @@ static void rtl838x_enable_phy_polling(struct rtl838x_switch_priv *priv) } pr_info("%s: %16llx\n", __func__, v); - priv->r->set_port_reg(v, priv->r->smi_poll_ctrl); + priv->r->set_port_reg_le(v, priv->r->smi_poll_ctrl); /* PHY update complete */ if (priv->family_id == RTL8390_FAMILY_ID) - sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); + sw_w32_mask(0, 1 << 7, RTL839X_SMI_GLB_CTRL); else sw_w32_mask(0, 0x8000, RTL838X_SMI_GLB_CTRL); } @@ -802,27 +1219,30 @@ static void rtl838x_init_stats(struct rtl838x_switch_priv *priv) static int rtl838x_setup(struct dsa_switch *ds) { int i; - u64 port_bitmap = 0; struct rtl838x_switch_priv *priv = ds->priv; + u64 port_bitmap = 1ULL << priv->cpu_port; pr_info("%s called\n", __func__); /* Disable MAC polling the PHY so that we can start configuration */ - priv->r->set_port_reg(0, priv->r->smi_poll_ctrl); + priv->r->set_port_reg_le(0ULL, priv->r->smi_poll_ctrl); for (i = 0; i < ds->num_ports; i++) priv->ports[i].enable = false; priv->ports[priv->cpu_port].enable = true; /* Isolate ports from each other: traffic only CPU <-> port */ + /* Setting bit j in register RTL838X_PORT_ISO_CTRL(i) allows + * traffic from source port i to destination port j + */ for (i = 0; i < priv->cpu_port; i++) { if (priv->ports[i].phy) { - priv->r->set_port_iso_ctrl(1 << priv->cpu_port, i); - priv->r->mask_port_iso_ctrl(0, 1 << i, i); - port_bitmap |= 1 << i; + priv->r->set_port_reg_be(1ULL << priv->cpu_port | 1ULL << i, + priv->r->port_iso_ctrl(i)); + port_bitmap |= 1ULL << i; } } - priv->r->set_port_iso_ctrl(port_bitmap, priv->cpu_port); + priv->r->set_port_reg_be(port_bitmap, priv->r->port_iso_ctrl(priv->cpu_port)); rtl838x_print_matrix(); @@ -886,14 +1306,17 @@ rtl838x_get_tag_protocol(struct dsa_switch *ds, int port) static int rtl838x_get_l2aging(struct rtl838x_switch_priv *priv) { - int t = sw_r32(priv->r->l2_ctrl_1) & 0x7fffff; + int t = sw_r32(priv->r->l2_ctrl_1); - pr_debug("RTL838X_L2_CTRL_1 %x\n", sw_r32(priv->r->l2_ctrl_1)); + t &= priv->family_id == RTL8380_FAMILY_ID ? 0x7fffff : 0x1FFFFF; + + if (priv->family_id == RTL8380_FAMILY_ID) + t = t * 128 / 625; /* Aging time in seconds. 0: L2 aging disabled */ + else + t = (t * 3) / 5; - t = t * 128 / 625; /* Aging time in seconds. 0: L2 aging disabled */ pr_info("L2 AGING time: %d sec\n", t); - pr_info("Dynamic aging for ports: %x\n", - sw_r32(priv->r->l2_port_aging_out)); + pr_info("Dynamic aging for ports: %x\n", sw_r32(priv->r->l2_port_aging_out)); return t; } @@ -904,14 +1327,18 @@ static int rtl838x_get_l2aging(struct rtl838x_switch_priv *priv) static int rtl838x_set_l2aging(struct dsa_switch *ds, u32 t) { struct rtl838x_switch_priv *priv = ds->priv; + int t_max = priv->family_id == RTL8380_FAMILY_ID ? 0x7fffff : 0x1FFFFF; + /* Convert time in mseconds to internal value */ - if (t > 0x10000000) /* Set to maximum */ - t = 0x7fffff; - else - t = ((t * 625) / 1000 + 127) / 128; - + if (t > 0x10000000) { /* Set to maximum */ + t = t_max; + } else { + if (priv->family_id == RTL8380_FAMILY_ID) + t = ((t * 625) / 1000 + 127) / 128; + else + t = (t * 5 + 2) / 3; + } sw_w32(t, priv->r->l2_ctrl_1); - return 0; } @@ -933,7 +1360,7 @@ static void rtl838x_fast_age(struct dsa_switch *ds, int port) * 25: Flush (0) or replace (1) L2 entries * 26: Status of action (1: Start, 0: Done) */ - sw_w32(1 << (26 + s) | 1 << (23 + s) | port << 5, priv->r->l2_tbl_flush_ctrl); + sw_w32(1 << (26 + s) | 1 << (23 + s) | port << (5 + (s / 2)), priv->r->l2_tbl_flush_ctrl); do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << (26 + s))); @@ -968,47 +1395,34 @@ static u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed) return h; } +static u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h1, h2, h; + + if (sw_r32(priv->r->l2_ctrl_0) & 1) { + h1 = (u32) (((seed >> 60) & 0x3f) ^ ((seed >> 54) & 0x3f) + ^ ((seed >> 36) & 0x3f) ^ ((seed >> 30) & 0x3f) + ^ ((seed >> 12) & 0x3f) ^ ((seed >> 6) & 0x3f)); + h2 = (u32) (((seed >> 48) & 0x3f) ^ ((seed >> 42) & 0x3f) + ^ ((seed >> 24) & 0x3f) ^ ((seed >> 18) & 0x3f) + ^ (seed & 0x3f)); + h = (h1 << 6) | h2; + } else { + h = (seed >> 60) + ^ ((((seed >> 48) & 0x3f) << 6) | ((seed >> 54) & 0x3f)) + ^ ((seed >> 36) & 0xfff) ^ ((seed >> 24) & 0xfff) + ^ ((seed >> 12) & 0xfff) ^ (seed & 0xfff); + } + + return h; +} + static u64 rtl838x_hash_key(struct rtl838x_switch_priv *priv, u64 mac, u32 vid) { - return rtl838x_hash(priv, mac << 12 | vid); -} - -static u64 read_l2_entry_using_hash(u32 hash, u32 position, u32 *r) -{ - u64 entry; - /* Search in SRAM, with hash and at position in hash bucket (0-3) */ - u32 idx = (0 << 14) | (hash << 2) | position; - - u32 cmd = 1 << 16 /* Execute cmd */ - | 1 << 15 /* Read */ - | 0 << 13 /* Table type 0b00 */ - | (idx & 0x1fff); - - sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); - r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); - - entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); - return entry; -} - -static u64 rtl838x_read_cam(int idx, u32 *r) -{ - u64 entry; - u32 cmd = 1 << 16 /* Execute cmd */ - | 1 << 15 /* Read */ - | 1 << 13 /* Table type 0b01 */ - | (idx & 0x3f); - sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & (1 << 16)); - r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); - - entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); - return entry; + if (priv->family_id == RTL8380_FAMILY_ID) + return rtl838x_hash(priv, mac << 12 | vid); + else + return rtl839x_hash(priv, mac << 12 | vid); } static void rtl838x_write_cam(int idx, u32 *r) @@ -1042,81 +1456,100 @@ static void rtl838x_write_hash(int idx, u32 *r) static void dump_fdb(struct rtl838x_switch_priv *priv) { - u32 r[3]; + struct rtl838x_l2_entry e; int i; - u8 mac[6]; - u16 vid, rvid; mutex_lock(&priv->reg_mutex); - for (i = 0; i < 8192; i++) { - read_l2_entry_using_hash(i >> 2, i & 0x3, r); - mac[0] = (r[1] >> 20); - mac[1] = (r[1] >> 12); - mac[2] = (r[1] >> 4); - mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); - mac[4] = (r[2] >> 20); - mac[5] = (r[2] >> 12); - vid = r[0] & 0xfff; - rvid = r[2] & 0xfff; + for (i = 0; i < priv->fib_entries; i++) { + priv->r->read_l2_entry_using_hash(i >> 2, i & 0x3, &e); - if (!(r[0] >> 17)) /* Check for invalid entry */ + if (!e.valid) /* Check for invalid entry */ continue; - pr_info("-> port %02d: %pM, vid: %d, rvid: %d\n", - (r[0] >> 12) & priv->port_mask, &mac[0], vid, rvid); + pr_info("-> port %02d: mac %pM, vid: %d, rvid: %d, MC: %d, %d\n", + e.port, &e.mac[0], e.vid, e.rvid, e.is_ip_mc, e.is_ipv6_mc); } mutex_unlock(&priv->reg_mutex); } +static void rtl838x_port_get_stp_state(struct rtl838x_switch_priv *priv, int port) +{ + u32 cmd, msti = 0; + u32 port_state[4]; + int index, bit, i; + int pos = port; + int n = priv->family_id == RTL8380_FAMILY_ID ? 2 : 4; + + /* CPU PORT can only be configured on RTL838x */ + if (port >= priv->cpu_port || port > 51) + return; + + mutex_lock(&priv->reg_mutex); + + /* For the RTL839x, the bits are left-aligned in the 128 bit field */ + if (priv->family_id == RTL8390_FAMILY_ID) + pos += 12; + + index = n - (pos >> 4) - 1; + bit = (pos << 1) % 32; + + if (priv->family_id == RTL8380_FAMILY_ID) { + cmd = 1 << 15 /* Execute cmd */ + | 1 << 14 /* Read */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + } else { + cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Read */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + } + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < n; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); + + mutex_unlock(&priv->reg_mutex); +} + static int rtl838x_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { - u32 r[3]; - u8 mac[6]; - u16 vid, rvid; + struct rtl838x_l2_entry e; struct rtl838x_switch_priv *priv = ds->priv; int i; + u32 fid; + u32 pkey; + u64 mac; mutex_lock(&priv->reg_mutex); - for (i = 0; i < 8192; i++) { - read_l2_entry_using_hash(i >> 2, i & 0x3, r); - mac[0] = (r[1] >> 20); - mac[1] = (r[1] >> 12); - mac[2] = (r[1] >> 4); - mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); - mac[4] = (r[2] >> 20); - mac[5] = (r[2] >> 12); - vid = r[0] & 0xfff; - rvid = r[2] & 0xfff; + for (i = 0; i < priv->fib_entries; i++) { + priv->r->read_l2_entry_using_hash(i >> 2, i & 0x3, &e); - if (!(r[0] >> 17)) /* Check for invalid entry */ + if (!e.valid) continue; - if (port == ((r[0] >> 12) & 0x1f)) { - pr_info("-> mac %pM, vid: %d, rvid: %d\n", &mac[0], vid, rvid); - cb(mac, vid, (r[0] >> 19) & 1, data); + if (e.port == port) { + fid = (i & 0x3ff) | (e.rvid & ~0x3ff); + mac = ether_addr_to_u64(&e.mac[0]); + pkey = rtl838x_hash(priv, mac << 12 | fid); + fid = (pkey & 0x3ff) | (fid & ~0x3ff); + pr_info("-> mac %016llx, fid: %d\n", mac, fid); + cb(e.mac, e.vid, e.is_static, data); } } for (i = 0; i < 64; i++) { - rtl838x_read_cam(i, r); - mac[0] = (r[1] >> 20); - mac[1] = (r[1] >> 12); - mac[2] = (r[1] >> 4); - mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); - mac[4] = (r[2] >> 20); - mac[5] = (r[2] >> 12); - vid = r[0] & 0xfff; + priv->r->read_cam(i, &e); - if (!(r[0] >> 17)) + if (!e.valid) continue; - pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); - if (port == ((r[0] >> 12) & priv->port_mask)) - cb(mac, vid, (r[0] >> 19) & 1, data); + if (e.port == port) + cb(e.mac, e.vid, e.is_static, data); } mutex_unlock(&priv->reg_mutex); @@ -1129,19 +1562,18 @@ static int rtl838x_port_fdb_del(struct dsa_switch *ds, int port, struct rtl838x_switch_priv *priv = ds->priv; u64 mac = ether_addr_to_u64(addr); u32 key = rtl838x_hash_key(priv, mac, vid); - int i; + struct rtl838x_l2_entry e; u32 r[3]; u64 entry; - int idx = -1; - int err = 0; + int idx = -1, err = 0, i; pr_info("In %s, mac %llx, vid: %d, key: %x\n", __func__, mac, vid, key); mutex_lock(&priv->reg_mutex); for (i = 0; i < 4; i++) { - entry = read_l2_entry_using_hash(key, i, r); - if (!(r[0] >> 17)) /* Check for invalid entry */ + entry = priv->r->read_l2_entry_using_hash(key, i, &e); + if (!e.valid) continue; - if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { idx = (key << 2) | i; break; } @@ -1155,8 +1587,8 @@ static int rtl838x_port_fdb_del(struct dsa_switch *ds, int port, /* Check CAM for spillover from hash buckets */ for (i = 0; i < 64; i++) { - entry = rtl838x_read_cam(i, r); - if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + entry = priv->r->read_cam(i, &e); + if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { idx = i; break; } @@ -1178,26 +1610,24 @@ static int rtl838x_port_fdb_add(struct dsa_switch *ds, int port, struct rtl838x_switch_priv *priv = ds->priv; u64 mac = ether_addr_to_u64(addr); u32 key = rtl838x_hash_key(priv, mac, vid); - int i; + struct rtl838x_l2_entry e; u32 r[3]; - int idx = -1; u64 entry; - int err = 0; + int idx = -1, err = 0, i; mutex_lock(&priv->reg_mutex); for (i = 0; i < 4; i++) { - entry = read_l2_entry_using_hash(key, i, r); - if (!(r[0] >> 17)) { /* Check for invalid entry */ + entry = priv->r->read_l2_entry_using_hash(key, i, &e); + if (!e.valid) { idx = (key << 2) | i; break; } - if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { idx = (key << 2) | i; break; } } if (idx >= 0) { - // Found for del: R1 60000 R2 901b0e9 R3 12b0e000, 901b0e912b0e000 r[0] = 3 << 17 | port << 12; // Aging and port r[0] |= vid; r[1] = mac >> 16; @@ -1206,14 +1636,14 @@ static int rtl838x_port_fdb_add(struct dsa_switch *ds, int port, goto out; } - /* Hash bucket full, try CAM */ + /* Hash buckets full, try CAM */ for (i = 0; i < 64; i++) { - entry = rtl838x_read_cam(i, r); - if (!(r[0] >> 17)) { /* Check for invalid entry */ + entry = rtl838x_read_cam(i, &e); + if (!e.valid) { if (idx < 0) /* First empty entry? */ idx = i; break; - } else if ((entry & 0x0fffffffffffffff) == ((mac << 12) | vid)) { + } else if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { pr_debug("Found entry in CAM\n"); idx = i; break; @@ -1239,18 +1669,24 @@ static void rtl838x_port_stp_state_set(struct dsa_switch *ds, int port, u32 cmd, msti = 0; u32 port_state[4]; int index, bit, i; - + int pos = port; struct rtl838x_switch_priv *priv = ds->priv; int n = priv->family_id == RTL8380_FAMILY_ID ? 2 : 4; pr_info("%s: port %d state %2x\n", __func__, port, state); - if (port >= priv->cpu_port) + + /* CPU PORT can only be configured on RTL838x */ + if (port >= priv->cpu_port || port > 51) return; mutex_lock(&priv->reg_mutex); - index = n - (port >> 4) - 1; - bit = (port << 1) % 32; + /* For the RTL839x, the bits are left-aligned in the 128 bit field */ + if (priv->family_id == RTL8390_FAMILY_ID) + pos += 12; + + index = n - (pos >> 4) - 1; + bit = (pos << 1) % 32; if (priv->family_id == RTL8380_FAMILY_ID) { cmd = 1 << 15 /* Execute cmd */ @@ -1332,21 +1768,28 @@ static int rtl838x_port_mirror_add(struct dsa_switch *ds, int port, pr_debug("Using group %d\n", group); mutex_lock(&priv->reg_mutex); - /* Enable mirroring to port across VLANs (bit 11) */ - sw_w32(1 << 11 | (mirror->to_local_port << 4) | 1, RTL838X_MIR_CTRL(group)); - if (ingress && (sw_r32(RTL838X_MIR_SPM_CTRL(group)) & (1 << port))) { + if (priv->family_id == RTL8380_FAMILY_ID) { + /* Enable mirroring to port across VLANs (bit 11) */ + sw_w32(1 << 11 | (mirror->to_local_port << 4) | 1, RTL838X_MIR_CTRL(group)); + } else { + /* Enable mirroring to destination port */ + sw_w32((mirror->to_local_port << 4) | 1, RTL839X_MIR_CTRL(group)); + } + + if (ingress && (priv->r->get_port_reg_be(priv->r->mir_spm(group)) & (1ULL << port))) { mutex_unlock(&priv->reg_mutex); return -EEXIST; } - if ((!ingress) && (sw_r32(RTL838X_MIR_DPM_CTRL(group)) & (1 << port))) { + if ((!ingress) && (priv->r->get_port_reg_be(priv->r->mir_dpm(group)) & (1ULL << port))) { mutex_unlock(&priv->reg_mutex); return -EEXIST; } + if (ingress) - sw_w32_mask(0, 1 << port, RTL838X_MIR_SPM_CTRL(group)); + priv->r->mask_port_reg_be(0, 1ULL << port, priv->r->mir_spm(group)); else - sw_w32_mask(0, 1 << port, RTL838X_MIR_DPM_CTRL(group)); + priv->r->mask_port_reg_be(0, 1ULL << port, priv->r->mir_dpm(group)); priv->mirror_group_ports[group] = mirror->to_local_port; mutex_unlock(&priv->reg_mutex); @@ -1370,21 +1813,20 @@ static void rtl838x_port_mirror_del(struct dsa_switch *ds, int port, mutex_lock(&priv->reg_mutex); if (mirror->ingress) { /* Ingress, clear source port matrix */ - sw_w32_mask(1 << port, 0, RTL838X_MIR_SPM_CTRL(group)); + priv->r->mask_port_reg_be(1ULL << port, 0, priv->r->mir_spm(group)); } else { /* Egress, clear destination port matrix */ - sw_w32_mask(1 << port, 0, RTL838X_MIR_DPM_CTRL(group)); + priv->r->mask_port_reg_be(1ULL << port, 0, priv->r->mir_dpm(group)); } - if (!(sw_r32(RTL838X_MIR_DPM_CTRL(group)) || sw_r32(RTL838X_MIR_DPM_CTRL(group)))) { + if (!(sw_r32(priv->r->mir_spm(group)) || sw_r32(priv->r->mir_dpm(group)))) { priv->mirror_group_ports[group] = -1; - sw_w32(0, RTL838X_MIR_CTRL(group)); + sw_w32(0, priv->r->mir_ctrl(group)); } mutex_unlock(&priv->reg_mutex); } - void rtl838x_vlan_profile_dump(int index) { u32 profile; @@ -1392,12 +1834,28 @@ void rtl838x_vlan_profile_dump(int index) if (index < 0 || index > 7) return; - profile = sw_r32(RTL838X_VLAN_PROFILE(index)); + profile = sw_r32(rtl838x_vlan_profile(index)); - pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x", - index, profile & 1, (profile >> 1) & 0x1ff); - pr_info(" IPv4 Unkn MultiCast Field %x, IPv6 Unkn MultiCast Field: %x", - (profile >> 10) & 0x1ff, (profile >> 19) & 0x1ff); + pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ + IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", + index, profile & 1, (profile >> 1) & 0x1ff, (profile >> 10) & 0x1ff, + (profile >> 19) & 0x1ff); +} + +void rtl839x_vlan_profile_dump(int index) +{ + u32 profile, profile1; + + if (index < 0 || index > 7) + return; + + profile1 = sw_r32(rtl839x_vlan_profile(index) + 4); + profile = sw_r32(rtl839x_vlan_profile(index)); + + pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ + IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", + index, profile & 1, (profile >> 1) & 0xfff, (profile >> 13) & 0xfff, + (profile1) & 0xfff); } static int rtl838x_vlan_filtering(struct dsa_switch *ds, int port, @@ -1410,35 +1868,19 @@ static int rtl838x_vlan_filtering(struct dsa_switch *ds, int port, if (vlan_filtering) { /* Enable ingress and egress filtering */ - if (port != priv->cpu_port) { - if (port < 16) { - sw_w32_mask(0b10 << (port << 1), - 0b01 << (port << 1), - RTL838X_VLAN_PORT_IGR_FLTR_0); - } else { - sw_w32_mask(0b10 << ((port - 16) << 1), - 0b01 << ((port - 16) << 1), - RTL838X_VLAN_PORT_IGR_FLTR_1); - } - } - sw_w32_mask(0, 1 << port, RTL838X_VLAN_PORT_EGR_FLTR); + if (port != priv->cpu_port) + sw_w32_mask(0b10 << ((port % 16) << 1), 0b01 << ((port % 16) << 1), + priv->r->vlan_port_igr_filter(port)); + sw_w32_mask(0, 1 << (port % 32), priv->r->vlan_port_egr_filter(port)); } else { /* Disable ingress and egress filtering */ - if (port != priv->cpu_port) { - if (port < 16) { - sw_w32_mask(0b11 << (port << 1), - 0, - RTL838X_VLAN_PORT_IGR_FLTR_0); - } else { - sw_w32_mask(0b11 << ((port - 16) << 1), - 0, - RTL838X_VLAN_PORT_IGR_FLTR_1); - } - } - sw_w32_mask(1 << port, 0, RTL838X_VLAN_PORT_EGR_FLTR); + if (port != priv->cpu_port) + sw_w32_mask(0b11 << ((port % 16) << 1), 0, + priv->r->vlan_port_igr_filter(port)); + sw_w32_mask(1 << (port % 32), 0, priv->r->vlan_port_egr_filter(port)); } - /* We need to do something to the CPU-Port, too */ + /* Do we need to do something to the CPU-Port, too? */ mutex_unlock(&priv->reg_mutex); return 0; @@ -1454,12 +1896,16 @@ static int rtl838x_vlan_prepare(struct dsa_switch *ds, int port, mutex_lock(&priv->reg_mutex); - priv->r->vlan_tables_read(1, &info); + if (priv->family_id == RTL8380_FAMILY_ID) + rtl839x_vlan_profile_dump(0); + else + rtl839x_vlan_profile_dump(0); - pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, MSTI %x\n", - info.tagged_ports, info.untagged_ports, info.vlan_conf & 7, - (info.vlan_conf & 8) >> 3, (info.vlan_conf & 16) >> 4, - (info.vlan_conf & 0x3e0) >> 5); + priv->r->vlan_tables_read(0, &info); + + pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, FID %x\n", + info.tagged_ports, info.untagged_ports, info.profile_id, + info.hash_mc, info.hash_uc, info.fid); mutex_unlock(&priv->reg_mutex); return 0; @@ -1474,7 +1920,7 @@ static void rtl838x_vlan_add(struct dsa_switch *ds, int port, u64 portmask; pr_info("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__, - port, vlan->vid_begin, vlan->vid_end, vlan->flags); + port, vlan->vid_begin, vlan->vid_end, vlan->flags); if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { dev_err(priv->dev, "VLAN out of range: %d - %d", @@ -1487,7 +1933,7 @@ static void rtl838x_vlan_add(struct dsa_switch *ds, int port, if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { /* Set both inner and outer PVID of the port */ - sw_w32((v << 16) | v, RTL838X_VLAN_PORT_PB_VLAN(port)); + sw_w32((v << 16) | v, priv->r->vlan_port_pb(port)); } } @@ -1503,11 +1949,12 @@ static void rtl838x_vlan_add(struct dsa_switch *ds, int port, for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { /* Get tagged port memberships of this vlan */ priv->r->vlan_tables_read(v, &info); - portmask = info.tagged_ports | (1 << port); - pr_debug("Tagged ports, VLAN %d: %llx\n", v, portmask); - priv->r->vlan_set_tagged(v, portmask, info.vlan_conf); + info.tagged_ports |= (1 << port); + pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + priv->r->vlan_set_tagged(v, &info); } } + mutex_unlock(&priv->reg_mutex); } @@ -1532,21 +1979,21 @@ static int rtl838x_vlan_del(struct dsa_switch *ds, int port, for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { /* Reset both inner and out PVID of the port */ - sw_w32(0, RTL838X_VLAN_PORT_PB_VLAN(port)); + sw_w32(0, priv->r->vlan_port_pb(port)); if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) { /* Get untagged port memberships of this vlan */ priv->r->vlan_tables_read(v, &info); - portmask = info.untagged_ports & (~(1 << port)); + portmask = info.untagged_ports & (~(1ULL << port)); pr_info("Untagged ports, VLAN %d: %llx\n", v, portmask); priv->r->vlan_set_untagged(v, portmask); } /* Get tagged port memberships of this vlan */ priv->r->vlan_tables_read(v, &info); - portmask = info.tagged_ports & (~(1 << port)); - pr_info("Tagged ports, VLAN %d: %llx\n", v, portmask); - priv->r->vlan_set_tagged(v, portmask, info.vlan_conf); + info.tagged_ports &= (~(1ULL << port)); + pr_info("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + priv->r->vlan_set_tagged(v, &info); } mutex_unlock(&priv->reg_mutex); @@ -1554,10 +2001,10 @@ static int rtl838x_vlan_del(struct dsa_switch *ds, int port, } static void rtl838x_port_bridge_leave(struct dsa_switch *ds, int port, - struct net_device *bridge) + struct net_device *bridge) { struct rtl838x_switch_priv *priv = ds->priv; - u64 port_bitmap = 1 << priv->cpu_port; + u64 port_bitmap = 1ULL << priv->cpu_port; int i; pr_info("%s %x: %d", __func__, (u32)priv, port); @@ -1573,27 +2020,26 @@ static void rtl838x_port_bridge_leave(struct dsa_switch *ds, int port, if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) - priv->r->mask_port_iso_ctrl(1 << port, 0, i); - priv->ports[i].pm |= 1 << port; + priv->r->mask_port_reg_be(1ULL << port, 0, + priv->r->port_iso_ctrl(i)); + priv->ports[i].pm |= 1ULL << port; - port_bitmap &= ~(1 << i); + port_bitmap &= ~(1ULL << i); } } /* Add all other ports to this port matrix. */ if (priv->ports[port].enable) - priv->r->mask_port_iso_ctrl(0, port_bitmap, port); + priv->r->mask_port_reg_be(0, port_bitmap, priv->r->port_iso_ctrl(port)); priv->ports[port].pm &= ~port_bitmap; mutex_unlock(&priv->reg_mutex); - - rtl838x_print_matrix(); } static int rtl838x_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *bridge) + struct net_device *bridge) { struct rtl838x_switch_priv *priv = ds->priv; - u64 port_bitmap = 1 << priv->cpu_port; + u64 port_bitmap = 1ULL << priv->cpu_port; int i; pr_info("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap); @@ -1607,17 +2053,20 @@ static int rtl838x_port_bridge_join(struct dsa_switch *ds, int port, if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) - priv->r->mask_port_iso_ctrl(0, 1 << port, i); - priv->ports[i].pm |= 1 << port; + priv->r->mask_port_reg_be(0, 1ULL << port, + priv->r->port_iso_ctrl(i)); + priv->ports[i].pm |= 1ULL << port; - port_bitmap |= 1 << i; + port_bitmap |= 1ULL << i; } } /* Add all other ports to this port matrix. */ if (priv->ports[port].enable) { - priv->r->mask_port_iso_ctrl(0, 1 << port, priv->cpu_port); - priv->r->mask_port_iso_ctrl(0, port_bitmap, port); + priv->r->mask_port_reg_be(0, 1ULL << port, + priv->r->port_iso_ctrl(priv->cpu_port)); + priv->r->mask_port_reg_be(0, port_bitmap, + priv->r->port_iso_ctrl(port)); } priv->ports[port].pm |= port_bitmap; mutex_unlock(&priv->reg_mutex); @@ -1637,13 +2086,10 @@ static int rtl838x_port_enable(struct dsa_switch *ds, int port, return 0; /* add port to switch mask of CPU_PORT */ - priv->r->mask_port_iso_ctrl(0, 1 << port, priv->cpu_port); + priv->r->mask_port_reg_be(0, 1ULL << port, priv->r->port_iso_ctrl(priv->cpu_port)); /* add all other ports in the same bridge to switch mask of port */ - priv->r->mask_port_iso_ctrl(0, priv->ports[port].pm, port); - - /* enable PHY polling */ - sw_w32_mask(0, 1 << port, RTL838X_SMI_POLL_CTRL); + priv->r->mask_port_reg_be(0, priv->ports[port].pm, priv->r->port_iso_ctrl(port)); return 0; } @@ -1653,21 +2099,17 @@ static void rtl838x_port_disable(struct dsa_switch *ds, int port) struct rtl838x_switch_priv *priv = ds->priv; pr_info("%s %x: %d", __func__, (u32)priv, port); - /* you can only disable user ports */ if (!dsa_is_user_port(ds, port)) return; /* remove port from switch mask of CPU_PORT */ - priv->r->mask_port_iso_ctrl(1 << port, 0, priv->cpu_port); + priv->r->mask_port_reg_be(1ULL << port, 0, priv->r->port_iso_ctrl(priv->cpu_port)); /* remove all other ports in the same bridge from switch mask of port */ - priv->r->mask_port_iso_ctrl(priv->ports[port].pm, 0, port); + priv->r->mask_port_reg_be(priv->ports[port].pm, 0LL, priv->r->port_iso_ctrl(port)); priv->ports[port].enable = false; - - /* disable PHY polling */ - sw_w32_mask(1 << port, 0, RTL838X_SMI_POLL_CTRL); } static int rtl838x_get_mac_eee(struct dsa_switch *ds, int port, @@ -1726,11 +2168,12 @@ static int rtl838x_set_mac_eee(struct dsa_switch *ds, int port, } static void rtl838x_phylink_mac_config(struct dsa_switch *ds, int port, - unsigned int mode, - const struct phylink_link_state *state) + unsigned int mode, + const struct phylink_link_state *state) { struct rtl838x_switch_priv *priv = ds->priv; u32 reg; + int speed_bit = priv->family_id == RTL8380_FAMILY_ID ? 4 : 3; pr_info("%s port %d, mode %x\n", __func__, port, mode); @@ -1751,45 +2194,53 @@ static void rtl838x_phylink_mac_config(struct dsa_switch *ds, int port, } reg = sw_r32(priv->r->mac_force_mode_ctrl(port)); - if (mode == MLO_AN_PHY) { - pr_info("PHY autonegotiates\n"); - reg |= 1 << 2; - sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); - return; + /* Auto-Negotiation does not work for MAC in RTL8390 */ + if (priv->family_id == RTL8380_FAMILY_ID) { + if (mode == MLO_AN_PHY) { + pr_info("PHY autonegotiates\n"); + reg |= 1 << 2; + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); + return; + } } if (mode != MLO_AN_FIXED) - pr_info("Not fixed\n"); + pr_info("Fixed state.\n"); - /* Clear id_mode_dis bit, and the existing port mode, let - * RGMII_MODE_EN bet set by mac_link_{up,down} - */ - reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); + if (priv->family_id == RTL8380_FAMILY_ID) { + /* Clear id_mode_dis bit, and the existing port mode, let + * RGMII_MODE_EN bet set by mac_link_{up,down} + */ + reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); - if (state->pause & MLO_PAUSE_TXRX_MASK) { - if (state->pause & MLO_PAUSE_TX) - reg |= TX_PAUSE_EN; - reg |= RX_PAUSE_EN; + if (state->pause & MLO_PAUSE_TXRX_MASK) { + if (state->pause & MLO_PAUSE_TX) + reg |= TX_PAUSE_EN; + reg |= RX_PAUSE_EN; + } } - reg &= ~(3 << 4); + reg &= ~(3 << speed_bit); switch (state->speed) { case SPEED_1000: - reg |= 2 << 4; + reg |= 2 << speed_bit; break; case SPEED_100: - reg |= 1 << 4; + reg |= 1 << speed_bit; break; } - reg &= ~(DUPLEX_FULL | FORCE_LINK_EN); - if (state->link) - reg |= FORCE_LINK_EN; - if (state->duplex == DUPLEX_FULL) - reg |= DUPLX_MODE; + if (priv->family_id == RTL8380_FAMILY_ID) { + reg &= ~(DUPLEX_FULL | FORCE_LINK_EN); + if (state->link) + reg |= FORCE_LINK_EN; + if (state->duplex == DUPLEX_FULL) + reg |= DUPLX_MODE; + } // Disable AN - reg &= ~(1 << 2); + if (priv->family_id == RTL8380_FAMILY_ID) + reg &= ~(1 << 2); sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); } @@ -1797,8 +2248,9 @@ static void rtl838x_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { + struct rtl838x_switch_priv *priv = ds->priv; /* Stop TX/RX to port */ - sw_w32_mask(0x03, 0, RTL838X_MAC_PORT_CTRL(port)); + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port)); } static void rtl838x_phylink_mac_link_up(struct dsa_switch *ds, int port, @@ -1806,14 +2258,16 @@ static void rtl838x_phylink_mac_link_up(struct dsa_switch *ds, int port, phy_interface_t interface, struct phy_device *phydev) { + struct rtl838x_switch_priv *priv = ds->priv; /* Restart TX/RX to port */ - sw_w32_mask(0, 0x03, RTL838X_MAC_PORT_CTRL(port)); + sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port)); } static void rtl838x_phylink_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) { + struct rtl838x_switch_priv *priv = ds->priv; __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; pr_info("In %s port %d", __func__, port); @@ -1833,8 +2287,6 @@ static void rtl838x_phylink_validate(struct dsa_switch *ds, int port, return; } - /* switch chip-id? if (priv->id == 0x8382) */ - /* Allow all the expected bits */ phylink_set(mask, Autoneg); phylink_set_port_modes(mask); @@ -1851,7 +2303,7 @@ static void rtl838x_phylink_validate(struct dsa_switch *ds, int port, } /* On both the 8380 and 8382, ports 24-27 are SFP ports */ - if (port >= 24 && port <= 27) + if (port >= 24 && port <= 27 && priv->family_id == RTL8380_FAMILY_ID) phylink_set(mask, 1000baseX_Full); phylink_set(mask, 10baseT_Half); @@ -1869,19 +2321,19 @@ static int rtl838x_phylink_mac_link_state(struct dsa_switch *ds, int port, struct phylink_link_state *state) { struct rtl838x_switch_priv *priv = ds->priv; - u32 speed; + u64 speed; if (port < 0 || port > priv->cpu_port) return -EINVAL; state->link = 0; - if (sw_r32(RTL838X_MAC_LINK_STS) & (1 << port)) + if (priv->r->get_port_reg_le(priv->r->mac_link_sts) & (1ULL << port)) state->link = 1; state->duplex = 0; - if (sw_r32(RTL838X_MAC_LINK_DUP_STS) & (1 << port)) + if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & (1ULL << port)) state->duplex = 1; - speed = sw_r32(RTL838X_MAC_LINK_SPD_STS(port)); + speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port)); speed >>= (port % 16) << 1; switch (speed & 0x3) { case 0: @@ -1901,9 +2353,9 @@ static int rtl838x_phylink_mac_link_state(struct dsa_switch *ds, int port, } state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); - if (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port)) + if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & (1ULL << port)) state->pause |= MLO_PAUSE_RX; - if (sw_r32(RTL838X_MAC_TX_PAUSE_STS) & (1 << port)) + if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & (1ULL << port)) state->pause |= MLO_PAUSE_TX; return 1; } @@ -1984,10 +2436,17 @@ static int rtl838x_mdio_probe(struct rtl838x_switch_priv *priv) } /* Disable MAC polling the PHY so that we can start configuration */ - sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL); + priv->r->set_port_reg_le(0ULL, priv->r->smi_poll_ctrl); /* Enable PHY control via SoC */ - sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); + if (priv->family_id == RTL8380_FAMILY_ID) { + /* Enable PHY control via SoC */ + sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); + } else { + /* Disable PHY polling via SoC */ + sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); + } + /* Power on fibre ports and reset them if necessary */ if (priv->ports[24].phy == PHY_RTL838X_SDS) { pr_info("Powering on fibre ports & reset\n"); @@ -2030,7 +2489,6 @@ static const struct dsa_switch_ops rtl838x_switch_ops = { .phylink_mac_link_up = rtl838x_phylink_mac_link_up, .set_mac_eee = rtl838x_set_mac_eee, .get_mac_eee = rtl838x_get_mac_eee, - }; static int __init rtl838x_sw_probe(struct platform_device *pdev) @@ -2038,6 +2496,7 @@ static int __init rtl838x_sw_probe(struct platform_device *pdev) int err = 0, i; struct rtl838x_switch_priv *priv; struct device *dev = &pdev->dev; + u64 irq_mask; pr_info("Probing RTL838X switch device\n"); if (!pdev->dev.of_node) { @@ -2065,12 +2524,14 @@ static int __init rtl838x_sw_probe(struct platform_device *pdev) priv->port_mask = 0x1f; priv->r = &rtl838x_reg; priv->ds->num_ports = 30; + priv->fib_entries = 8192; rtl8380_get_version(priv); } else { priv->cpu_port = RTL839X_CPU_PORT; priv->port_mask = 0x3f; priv->r = &rtl839x_reg; priv->ds->num_ports = 53; + priv->fib_entries = 16384; rtl8390_get_version(priv); } pr_info("Chip version %c\n", priv->version); @@ -2091,16 +2552,17 @@ static int __init rtl838x_sw_probe(struct platform_device *pdev) /* Enable link and media change interrupts. Are the SERDES masks needed? */ sw_w32_mask(0, 3, priv->r->isr_glb_src); /* ... for all ports */ - priv->r->set_port_reg(0xffffffffffffffff, priv->r->isr_port_link_sts_chg); - priv->r->set_port_reg(0xffffffffffffffff, priv->r->imr_port_link_sts_chg); + irq_mask = soc_info.family == RTL8380_FAMILY_ID ? 0x0FFFFFFF : 0xFFFFFFFFFFFFFULL; + priv->r->set_port_reg_le(irq_mask, priv->r->isr_port_link_sts_chg); + priv->r->set_port_reg_le(irq_mask, priv->r->imr_port_link_sts_chg); priv->link_state_irq = 20; if (priv->family_id == RTL8380_FAMILY_ID) { err = request_irq(priv->link_state_irq, rtl838x_switch_irq, - IRQF_SHARED, "rtl8838x-link-state", priv->ds); + IRQF_SHARED, "rtl838x-link-state", priv->ds); } else { err = request_irq(priv->link_state_irq, rtl839x_switch_irq, - IRQF_SHARED, "rtl8838x-link-state", priv->ds); + IRQF_SHARED, "rtl838x-link-state", priv->ds); } if (err) { dev_err(dev, "Error setting up switch interrupt.\n"); diff --git a/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c index 9634b6a24f..78c8fdcf68 100644 --- a/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.c @@ -17,12 +17,12 @@ #include <linux/module.h> #include <linux/phylink.h> #include <net/dsa.h> +#include <net/switchdev.h> +#include <asm/cacheflush.h> #include <asm/mach-rtl838x/mach-rtl838x.h> #include "rtl838x_eth.h" -extern struct rtl838x_soc_info soc_info; - /* * Maximum number of RX rings is 8, assigned by switch based on * packet/port priortity (not implemented) @@ -30,10 +30,12 @@ extern struct rtl838x_soc_info soc_info; * RX ringlength needs to be at least 200, otherwise CPU and Switch * may gridlock. */ -#define RXRINGS 2 +#define RXRINGS 8 #define RXRINGLEN 300 #define TXRINGS 2 #define TXRINGLEN 160 +#define NOTIFY_EVENTS 10 +#define NOTIFY_BLOCKS 10 #define TX_EN 0x8 #define RX_EN 0x4 #define TX_DO 0x2 @@ -51,6 +53,15 @@ struct p_hdr { uint16_t cpu_tag[5]; } __packed __aligned(1); +struct n_event { + uint32_t type:2; + uint32_t fidVid:12; + uint64_t mac:48; + uint32_t slp:6; + uint32_t valid:1; + uint32_t reserved:27; +} __packed __aligned(1); + struct ring_b { uint32_t rx_r[RXRINGS][RXRINGLEN]; uint32_t tx_r[TXRINGS][TXRINGLEN]; @@ -62,6 +73,53 @@ struct ring_b { uint8_t tx_space[TXRINGLEN*RING_BUFFER]; }; +struct notify_block { + struct n_event events[NOTIFY_EVENTS]; +}; + +struct notify_b { + struct notify_block blocks[NOTIFY_BLOCKS]; + u32 reserved1[8]; + u32 ring[NOTIFY_BLOCKS]; + u32 reserved2[8]; +}; + +inline void rtl838x_create_tx_header(struct p_hdr *h, int dest_port) +{ + if (dest_port > 0) { + h->cpu_tag[0] = 0x0400; + h->cpu_tag[1] = 0x0200; + h->cpu_tag[2] = 0x0000; + h->cpu_tag[3] = (1 << dest_port) >> 16; + h->cpu_tag[4] = (1 << dest_port) & 0xffff; + } else { + h->cpu_tag[0] = 0; + h->cpu_tag[1] = 0; + h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = 0; + } +} + +inline void rtl839x_create_tx_header(struct p_hdr *h, int dest_port) +{ + if (dest_port > 0) { + h->cpu_tag[0] = 0x0100; + h->cpu_tag[1] = ((1 << (dest_port - 32)) >> 16) | (1 << 21); + h->cpu_tag[2] = (1 << (dest_port - 32)) & 0xffff; + h->cpu_tag[3] = (1 << dest_port) >> 16; + h->cpu_tag[4] = (1 << dest_port) & 0xffff; + } else { + h->cpu_tag[0] = 0; + h->cpu_tag[1] = 0; + h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = 0; + } +} + +extern void rtl838x_fdb_sync(struct work_struct *work); + struct rtl838x_eth_priv { struct net_device *netdev; struct platform_device *pdev; @@ -75,6 +133,8 @@ struct rtl838x_eth_priv { u16 family_id; const struct rtl838x_reg *r; u8 cpu_port; + u8 port_mask; + u32 lastEvent; }; static const struct rtl838x_reg rtl838x_reg = { @@ -119,6 +179,11 @@ static const struct rtl838x_reg rtl839x_reg = { .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, }; +extern int rtl838x_phy_init(struct rtl838x_eth_priv *priv); +extern int rtl838x_read_sds_phy(int phy_addr, int phy_reg); +extern int rtl839x_read_sds_phy(int phy_addr, int phy_reg); +extern int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v); + /* * Discard the RX ring-buffers, called as part of the net-ISR * when the buffer runs over @@ -137,20 +202,64 @@ static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv) if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) break; h = &ring->rx_header[r][ring->c_rx[r]]; - h->buf = (u8 *)CPHYSADDR(ring->rx_space + h->buf = (u8 *)KSEG1ADDR(ring->rx_space + r * ring->c_rx[r] * RING_BUFFER); h->size = RING_BUFFER; h->len = 0; /* make sure the header is visible to the ASIC */ mb(); - ring->rx_r[r][ring->c_rx[r]] = CPHYSADDR(h) | 0x1 - | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1); + ring->rx_r[r][ring->c_rx[r]] = KSEG1ADDR(h) | 0x1 + | (ring->c_rx[r] == (RXRINGLEN - 1) ? WRAP : 0x1); ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN; } while (&ring->rx_r[r][ring->c_rx[r]] != last); } } +struct fdb_update_work { + struct work_struct work; + struct net_device *ndev; + u64 macs[NOTIFY_EVENTS + 1]; +}; + +static void rtl839x_l2_notification_handler(struct rtl838x_eth_priv *priv) +{ + struct notify_b *nb = priv->membase + sizeof(struct ring_b); + u32 e = priv->lastEvent; + struct n_event *event; + int i; + u64 mac; + struct fdb_update_work *w; + + while (!(nb->ring[e] & 1)) { + w = kzalloc(sizeof(*w), GFP_ATOMIC); + if (!w) { + pr_err("Out of memory: %s", __func__); + return; + } + INIT_WORK(&w->work, rtl838x_fdb_sync); + + for (i = 0; i < NOTIFY_EVENTS; i++) { + event = &nb->blocks[e].events[i]; + if (!event->valid) + continue; + mac = event->mac; + if (event->type) + mac |= 1ULL << 63; + w->ndev = priv->netdev; + w->macs[i] = mac; + } + + /* Hand the ring entry back to the switch */ + nb->ring[e] = nb->ring[e] | 1; + e = (e + 1) % NOTIFY_BLOCKS; + + w->macs[i] = 0ULL; + schedule_work(&w->work); + } + priv->lastEvent = e; +} + static irqreturn_t rtl838x_net_irq(int irq, void *dev_id) { struct net_device *dev = dev_id; @@ -174,36 +283,69 @@ static irqreturn_t rtl838x_net_irq(int irq, void *dev_id) /* RX buffer overrun */ if (status & 0x000ff) { + pr_debug("RX buffer overrun: status %x, mask: %x\n", + status, sw_r32(priv->r->dma_if_intr_msk)); sw_w32(0x000000ff, priv->r->dma_if_intr_sts); rtl838x_rb_cleanup(priv); } + if (priv->family_id == RTL8390_FAMILY_ID && status & 0x00100000) { + sw_w32(0x00700000, priv->r->dma_if_intr_sts); + rtl839x_l2_notification_handler(priv); + } + spin_unlock(&priv->lock); return IRQ_HANDLED; } static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) { - u32 int_saved; + u32 int_saved, nbuf; pr_info("RESETTING %x, CPU_PORT %d\n", priv->family_id, priv->cpu_port); /* Stop TX/RX */ - sw_w32(0x0, priv->r->mac_port_ctrl(priv->cpu_port)); + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port)); mdelay(500); - int_saved = sw_r32(priv->r->dma_if_intr_msk); - /* Reset NIC */ + if (priv->family_id == RTL8390_FAMILY_ID) { + /* Preserve L2 notification and NBUF settings */ + int_saved = sw_r32(priv->r->dma_if_intr_msk); + nbuf = sw_r32(RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); + + /* Disable link change interrupt on RTL839x */ + sw_w32(0, RTL839X_IMR_PORT_LINK_STS_CHG); + sw_w32(0, RTL839X_IMR_PORT_LINK_STS_CHG + 4); + + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_sts); + } + + /* Reset NIC and Queue */ sw_w32(0x08, priv->r->rst_glb_ctrl); - do { + if (priv->family_id == RTL8390_FAMILY_ID) + sw_w32(0xffffffff, RTL839X_DMA_IF_RX_RING_CNTR); + do { /* Reset NIC */ udelay(20); } while (sw_r32(priv->r->rst_glb_ctrl) & 0x08); + do { /* Reset Queues */ + udelay(20); + } while (sw_r32(priv->r->rst_glb_ctrl) & 0x04); mdelay(100); - /* Restore notification settings: on RTL838x these bits are null */ - sw_w32_mask(7 << 20, int_saved & (7 << 20), priv->r->dma_if_intr_msk); + /* Re-enable link change interrupt */ + if (priv->family_id == RTL8390_FAMILY_ID) { + sw_w32(0xffffffff, RTL839X_ISR_PORT_LINK_STS_CHG); + sw_w32(0xffffffff, RTL839X_ISR_PORT_LINK_STS_CHG + 4); + sw_w32(0xffffffff, RTL839X_IMR_PORT_LINK_STS_CHG); + sw_w32(0xffffffff, RTL839X_IMR_PORT_LINK_STS_CHG + 4); + + /* Restore notification settings: on RTL838x these bits are null */ + sw_w32_mask(7 << 20, int_saved & (7 << 20), priv->r->dma_if_intr_msk); + sw_w32(nbuf, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); + } /* Restart TX/RX to CPU port */ - sw_w32(0x03, priv->r->mac_port_ctrl(priv->cpu_port)); + sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); if (priv->family_id == RTL8380_FAMILY_ID) { /* Set Speed, duplex, flow control @@ -215,6 +357,11 @@ static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) /* allow CRC errors on CPU-port */ sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port)); } else { + /* CPU port joins Lookup Miss Flooding Portmask */ + sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL); + sw_w32_mask(0, 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0)); + sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL); + /* Force CPU port link up */ sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl(priv->cpu_port)); } @@ -230,17 +377,14 @@ static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv) struct ring_b *ring = priv->membase; for (i = 0; i < RXRINGS; i++) - sw_w32(CPHYSADDR(&ring->rx_r[i]), priv->r->dma_rx_base(i)); + sw_w32(KSEG1ADDR(&ring->rx_r[i]), priv->r->dma_rx_base(i)); for (i = 0; i < TXRINGS; i++) - sw_w32(CPHYSADDR(&ring->tx_r[i]), priv->r->dma_tx_base(i)); + sw_w32(KSEG1ADDR(&ring->tx_r[i]), priv->r->dma_tx_base(i)); } static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) { - u32 v; - - pr_info("%s\n", __func__); /* Disable Head of Line features for all RX rings */ sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); @@ -252,19 +396,18 @@ static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) /* Enable traffic, engine expects empty FCS field */ sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); +} - /* Make sure to flood all traffic to CPU_PORT */ - if (priv->family_id == RTL8390_FAMILY_ID) { - /* CPU port joins Lookup Miss Flooding Portmask */ - /* Table access: CMD: read, table = 2 */ - /* Sets MC_PMSK table port bit for port 52 to 1 */ - sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); - v = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); - sw_w32(v | 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0)); - sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); - } +static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv) +{ + /* Setup CPU-Port: RX Buffer */ + sw_w32(0x0000c808, priv->r->dma_if_ctrl); + + /* Enable Notify, RX done, RX overflow and TX done interrupts */ + sw_w32(0x007fffff, priv->r->dma_if_intr_msk); // Notify IRQ! + + /* Enable traffic */ + sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); } static void rtl838x_setup_ring_buffer(struct ring_b *ring) @@ -276,13 +419,14 @@ static void rtl838x_setup_ring_buffer(struct ring_b *ring) for (i = 0; i < RXRINGS; i++) { for (j = 0; j < RXRINGLEN; j++) { h = &ring->rx_header[i][j]; - h->buf = (u8 *)CPHYSADDR(ring->rx_space + i * j * RING_BUFFER); + h->buf = (u8 *)KSEG1ADDR(ring->rx_space + i * j * RING_BUFFER); h->reserved = 0; h->size = RING_BUFFER; h->offset = 0; h->len = 0; + memset(&h->cpu_tag, 0, sizeof(uint16_t[5])); /* All rings owned by switch, last one wraps */ - ring->rx_r[i][j] = CPHYSADDR(h) | 1 | (j == (RXRINGLEN - 1) ? WRAP : 0); + ring->rx_r[i][j] = KSEG1ADDR(h) | 1 | (j == (RXRINGLEN - 1) ? WRAP : 0); } ring->c_rx[i] = 0; } @@ -290,19 +434,40 @@ static void rtl838x_setup_ring_buffer(struct ring_b *ring) for (i = 0; i < TXRINGS; i++) { for (j = 0; j < TXRINGLEN; j++) { h = &ring->tx_header[i][j]; - h->buf = (u8 *)CPHYSADDR(ring->tx_space + i * j * RING_BUFFER); + h->buf = (u8 *)KSEG1ADDR(ring->tx_space + i * j * RING_BUFFER); h->reserved = 0; h->size = RING_BUFFER; h->offset = 0; h->len = 0; - ring->tx_r[i][j] = CPHYSADDR(&ring->tx_header[i][j]); + memset(&h->cpu_tag, 0, sizeof(uint16_t[5])); + ring->tx_r[i][j] = KSEG1ADDR(&ring->tx_header[i][j]); } /* Last header is wrapping around */ - ring->tx_r[i][j-1] |= 2; + ring->tx_r[i][j-1] |= WRAP; ring->c_tx[i] = 0; } } +static void rtl839x_setup_notify_ring_buffer(struct rtl838x_eth_priv *priv) +{ + int i; + struct notify_b *b = priv->membase + sizeof(struct ring_b); + + for (i = 0; i < NOTIFY_BLOCKS; i++) + b->ring[i] = KSEG1ADDR(&b->blocks[i]) | 1 | (i == (NOTIFY_BLOCKS - 1) ? WRAP : 0); + + sw_w32((u32) b->ring, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); + sw_w32_mask(0x3ff << 2, 100 << 2, RTL839X_L2_NOTIFICATION_CTRL); + + /* Setup notification events */ + sw_w32_mask(0, 1 << 14, RTL839X_L2_CTRL_0); // RTL8390_L2_CTRL_0_FLUSH_NOTIFY_EN + sw_w32_mask(0, 1 << 12, RTL839X_L2_NOTIFICATION_CTRL); // SUSPEND_NOTIFICATION_EN + + /* Enable Notification */ + sw_w32_mask(0, 1 << 0, RTL839X_L2_NOTIFICATION_CTRL); + priv->lastEvent = 0; +} + static int rtl838x_eth_open(struct net_device *ndev) { unsigned long flags; @@ -310,10 +475,18 @@ static int rtl838x_eth_open(struct net_device *ndev) struct ring_b *ring = priv->membase; int err; - pr_info("%s called %x, ring %x\n", __func__, (uint32_t)priv, (uint32_t)ring); + pr_info("%s called: RX rings %d, TX rings %d\n", __func__, RXRINGS, TXRINGS); + spin_lock_irqsave(&priv->lock, flags); rtl838x_hw_reset(priv); rtl838x_setup_ring_buffer(ring); + if (priv->family_id == RTL8390_FAMILY_ID) { + rtl839x_setup_notify_ring_buffer(priv); + /* Make sure the ring structure is visible to the ASIC */ + mb(); + flush_cache_all(); + } + rtl838x_hw_ring_setup(priv); err = request_irq(ndev->irq, rtl838x_net_irq, IRQF_SHARED, ndev->name, ndev); @@ -327,7 +500,17 @@ static int rtl838x_eth_open(struct net_device *ndev) napi_enable(&priv->napi); netif_start_queue(ndev); - rtl838x_hw_en_rxtx(priv); + if (priv->family_id == RTL8380_FAMILY_ID) { + rtl838x_hw_en_rxtx(priv); + /* Trap IGMP traffic to CPU-Port */ + sw_w32(0x3, RTL838X_SPCL_TRAP_IGMP_CTRL); + /* Flush learned FDB entries on link down of a port */ + sw_w32_mask(0, 1 << 7, RTL838X_L2_CTRL_0); + } else { + rtl839x_hw_en_rxtx(priv); + sw_w32(0x3, RTL839X_SPCL_TRAP_IGMP_CTRL); + sw_w32_mask(0, 1 << 7, RTL839X_L2_CTRL_0); + } spin_unlock_irqrestore(&priv->lock, flags); @@ -336,6 +519,8 @@ static int rtl838x_eth_open(struct net_device *ndev) static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) { + u32 force_mac = priv->family_id == RTL8380_FAMILY_ID ? 0x6192D : 0x75; + u32 clear_irq = priv->family_id == RTL8380_FAMILY_ID ? 0x000fffff : 0x007fffff; int i; /* Block all ports */ @@ -358,17 +543,19 @@ static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) } } - /* CPU-Port: Link down BUG: Works only for RTL838x */ - sw_w32(0x6192D, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + /* CPU-Port: Link down */ + sw_w32(force_mac, priv->r->mac_force_mode_ctrl(priv->cpu_port)); mdelay(100); /* Disable traffic */ sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl); - mdelay(200); + mdelay(200); // Test, whether this is needed /* Disable all TX/RX interrupts */ sw_w32(0x00000000, priv->r->dma_if_intr_msk); - sw_w32(0x000fffff, priv->r->dma_if_intr_sts); + sw_w32(clear_irq, priv->r->dma_if_intr_sts); + + /* Disable TX/RX DMA */ sw_w32(0x00000000, priv->r->dma_if_ctrl); mdelay(200); } @@ -387,12 +574,48 @@ static int rtl838x_eth_stop(struct net_device *ndev) napi_disable(&priv->napi); netif_stop_queue(ndev); spin_unlock_irqrestore(&priv->lock, flags); + return 0; } -static void rtl838x_eth_set_multicast_list(struct net_device *dev) +static void rtl839x_eth_set_multicast_list(struct net_device *ndev) { + if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + sw_w32(0x0, RTL839X_RMA_CTRL_0); + sw_w32(0x0, RTL839X_RMA_CTRL_1); + sw_w32(0x0, RTL839X_RMA_CTRL_2); + sw_w32(0x0, RTL839X_RMA_CTRL_3); + } + if (ndev->flags & IFF_ALLMULTI) { + sw_w32(0x7fffffff, RTL839X_RMA_CTRL_0); + sw_w32(0x7fffffff, RTL839X_RMA_CTRL_1); + sw_w32(0x7fffffff, RTL839X_RMA_CTRL_2); + } + if (ndev->flags & IFF_PROMISC) { + sw_w32(0x7fffffff, RTL839X_RMA_CTRL_0); + sw_w32(0x7fffffff, RTL839X_RMA_CTRL_1); + sw_w32(0x7fffffff, RTL839X_RMA_CTRL_2); + sw_w32(0x3ff, RTL839X_RMA_CTRL_3); + } +} +static void rtl838x_eth_set_multicast_list(struct net_device *ndev) +{ + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + if (priv->family_id == RTL8390_FAMILY_ID) + return rtl839x_eth_set_multicast_list(ndev); + + if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + sw_w32(0x0, RTL838X_RMA_CTRL_0); + sw_w32(0x0, RTL838X_RMA_CTRL_1); + } + if (ndev->flags & IFF_ALLMULTI) + sw_w32(0x1fffff, RTL838X_RMA_CTRL_0); + if (ndev->flags & IFF_PROMISC) { + sw_w32(0x1fffff, RTL838X_RMA_CTRL_0); + sw_w32(0x7fff, RTL838X_RMA_CTRL_1); + } } static void rtl838x_eth_tx_timeout(struct net_device *ndev) @@ -448,34 +671,27 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev) /* Set descriptor for tx */ h = &ring->tx_header[0][ring->c_tx[0]]; - h->buf = (u8 *)CPHYSADDR(ring->tx_space); + h->buf = (u8 *)KSEG1ADDR(ring->tx_space); h->size = len; h->len = len; /* Create cpu_tag */ - if (dest_port > 0) { - h->cpu_tag[0] = 0x0400; - h->cpu_tag[1] = 0x0200; - h->cpu_tag[2] = 0x0000; - h->cpu_tag[3] = (1 << dest_port) >> 16; - h->cpu_tag[4] = (1 << dest_port) & 0xffff; - } else { - h->cpu_tag[0] = 0; - h->cpu_tag[1] = 0; - h->cpu_tag[2] = 0; - h->cpu_tag[3] = 0; - h->cpu_tag[4] = 0; - } + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_create_tx_header(h, dest_port); + else + rtl839x_create_tx_header(h, dest_port); /* Copy packet data to tx buffer */ memcpy((void *)KSEG1ADDR(h->buf), skb->data, len); /* Make sure packet data is visible to ASIC */ - mb(); + mb(); /* wmb() probably works, too */ /* Hand over to switch */ ring->tx_r[0][ring->c_tx[0]] = ring->tx_r[0][ring->c_tx[0]] | 0x1; - /* BUG: before tx fetch, need to make sure right data is accessed */ + /* BUG: before tx fetch, need to make sure right data is accessed + * This might not be necessary on newer RTL839x, though. + */ for (i = 0; i < 10; i++) { val = sw_r32(priv->r->dma_if_ctrl); if ((val & 0xc) == 0xc) @@ -534,10 +750,6 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) if (!len) break; - h->buf = (u8 *)CPHYSADDR(ring->rx_space - + r * ring->c_rx[r] * RING_BUFFER); - h->size = RING_BUFFER; - h->len = 0; work_done++; len -= 4; /* strip the CRC */ @@ -549,12 +761,14 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) skb_reserve(skb, NET_IP_ALIGN); if (likely(skb)) { - /* BUG: Prevent ASIC bug */ - sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); - for (i = 0; i < RXRINGS; i++) { - /* Update each ring cnt */ - val = sw_r32(priv->r->dma_if_rx_ring_cntr(i)); - sw_w32(val, priv->r->dma_if_rx_ring_cntr(i)); + /* BUG: Prevent bug on RTL838x SoCs*/ + if (priv->family_id == RTL8380_FAMILY_ID) { + sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); + for (i = 0; i < RXRINGS; i++) { + /* Update each ring cnt */ + val = sw_r32(priv->r->dma_if_rx_ring_cntr(i)); + sw_w32(val, priv->r->dma_if_rx_ring_cntr(i)); + } } skb_data = skb_put(skb, len); @@ -564,7 +778,7 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) /* Overwrite CRC with cpu_tag */ if (dsa) { skb->data[len-4] = 0x80; - skb->data[len-3] = h->cpu_tag[0] & 0x1f; + skb->data[len-3] = h->cpu_tag[0] & priv->port_mask; skb->data[len-2] = 0x10; skb->data[len-1] = 0x00; } @@ -579,8 +793,15 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) dev_warn(&dev->dev, "low on memory - packet dropped\n"); dev->stats.rx_dropped++; } + + h->buf = (u8 *)KSEG1ADDR(ring->rx_space + + r * ring->c_rx[r] * RING_BUFFER); + h->size = RING_BUFFER; + h->len = 0; + memset(&h->cpu_tag, 0, sizeof(uint16_t[5])); + ring->rx_r[r][ring->c_rx[r]] - = CPHYSADDR(h) | 0x1 | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1); + = KSEG1ADDR(h) | 0x1 | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1); ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN; } while (&ring->rx_r[r][ring->c_rx[r]] != last && work_done < budget); @@ -601,7 +822,7 @@ static int rtl838x_poll_rx(struct napi_struct *napi, int budget) if (work_done < budget) { napi_complete_done(napi, work_done); /* Enable RX interrupt */ - sw_w32(0xfffff, priv->r->dma_if_intr_msk); + sw_w32_mask(0, 0xfffff, priv->r->dma_if_intr_msk); } return work_done; } @@ -660,7 +881,7 @@ static void rtl838x_mac_config(struct phylink_config *config, const struct phylink_link_state *state) { /* This is only being called for the master device, - * i.e. the CPU-Port + * i.e. the CPU-Port. We don't need to do anything. */ pr_info("In %s, mode %x\n", __func__, mode); @@ -671,6 +892,10 @@ static void rtl838x_mac_an_restart(struct phylink_config *config) struct net_device *dev = container_of(config->dev, struct net_device, dev); struct rtl838x_eth_priv *priv = netdev_priv(dev); + /* This works only on RTL838x chips */ + if (priv->family_id != RTL8380_FAMILY_ID) + return; + pr_info("In %s\n", __func__); /* Restart by disabling and re-enabling link */ sw_w32(0x6192D, priv->r->mac_force_mode_ctrl(priv->cpu_port)); @@ -715,20 +940,6 @@ static int rtl838x_mac_pcs_get_state(struct phylink_config *config, return 1; } -static void dump_mac_conf(struct rtl838x_eth_priv *priv) -{ - int p; - - for (p = 8; p < 16; p++) { - pr_debug("%d: %x, force %x\n", p, sw_r32(priv->r->mac_port_ctrl(p)), - sw_r32(priv->r->mac_force_mode_ctrl(p)) - ); - } - pr_debug("CPU: %x, force %x\n", sw_r32(priv->r->mac_port_ctrl(priv->cpu_port)), - sw_r32(priv->r->mac_force_mode_ctrl(priv->cpu_port)) - ); -} - static void rtl838x_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) @@ -794,14 +1005,7 @@ static int rtl838x_set_mac_address(struct net_device *dev, void *p) static int rtl8390_init_mac(struct rtl838x_eth_priv *priv) { - pr_info("Configuring RTL8390 MAC\n"); - // from mac_config_init - sw_w32(0x80, RTL839X_MAC_EFUSE_CTRL); - sw_w32(0x4, RTL839X_RST_GLB_CTRL); - sw_w32(0x3c324f40, RTL839X_MAC_GLB_CTRL); - /* Unlimited egress rate */ - sw_w32(0x1297b961, RTL839X_SCHED_LB_TICK_TKN_CTRL); - + // We will need to set-up EEE and the egress-rate limitation return 0; } @@ -812,9 +1016,6 @@ static int rtl8380_init_mac(struct rtl838x_eth_priv *priv) if (priv->family_id == 0x8390) return rtl8390_init_mac(priv); - if (priv->family_id != 0x8380) - return 0; - pr_info("%s\n", __func__); /* fix timer for EEE */ sw_w32(0x5001411, RTL838X_EEE_TX_TIMER_GIGA_CTRL); @@ -850,20 +1051,14 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev, return phylink_ethtool_ksettings_set(priv->phylink, cmd); } - static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { u32 val; - u32 offset = 0; int err; struct rtl838x_eth_priv *priv = bus->priv; - if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) { - if (mii_id == 26) - offset = 0x100; - val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (regnum << 2)) & 0xffff; - return val; - } + if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) + return rtl838x_read_sds_phy(mii_id, regnum); err = rtl838x_read_phy(mii_id, 0, regnum, &val); if (err) return err; @@ -874,6 +1069,10 @@ static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { u32 val; int err; + struct rtl838x_eth_priv *priv = bus->priv; + + if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) + return rtl839x_read_sds_phy(mii_id, regnum); err = rtl839x_read_phy(mii_id, 0, regnum, &val); if (err) @@ -899,6 +1098,11 @@ static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id, static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) { + struct rtl838x_eth_priv *priv = bus->priv; + + if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) + return rtl839x_write_sds_phy(mii_id, regnum, value); + return rtl839x_write_phy(mii_id, 0, regnum, value); } @@ -911,18 +1115,23 @@ static int rtl838x_mdio_reset(struct mii_bus *bus) /* Enable PHY control via SoC */ sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); + // Probably should reset all PHYs here... return 0; } static int rtl839x_mdio_reset(struct mii_bus *bus) { + return 0; + pr_info("%s called\n", __func__); + /* BUG: The following does not work, but should! */ /* Disable MAC polling the PHY so that we can start configuration */ sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL); sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL + 4); /* Disable PHY polling via SoC */ sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); + // Probably should reset all PHYs here... return 0; } @@ -1057,8 +1266,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) /* Allocate buffer memory */ priv->membase = dmam_alloc_coherent(&pdev->dev, - sizeof(struct ring_b), (void *)&dev->mem_start, - GFP_KERNEL); + sizeof(struct ring_b) + sizeof(struct notify_b), + (void *)&dev->mem_start, GFP_KERNEL); if (!priv->membase) { dev_err(&pdev->dev, "cannot allocate DMA buffer\n"); err = -ENOMEM; @@ -1090,9 +1299,11 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) if (priv->family_id == 0x8390) { priv->cpu_port = RTL839X_CPU_PORT; priv->r = &rtl839x_reg; + priv->port_mask = 0x3f; } else { priv->cpu_port = RTL838X_CPU_PORT; priv->r = &rtl838x_reg; + priv->port_mask = 0x1f; } rtl8380_init_mac(priv); @@ -1157,6 +1368,7 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) goto err_free; } priv->phylink = phylink; + return 0; err_free: diff --git a/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h index 53ab7e5eb2..f434b8c94b 100644 --- a/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/rtl838x/files-5.4/drivers/net/ethernet/rtl838x_eth.h @@ -100,6 +100,22 @@ #define TX_PAUSE_EN (1 << 6) #define RX_PAUSE_EN (1 << 7) +/* RTL839X L2 Notification DMA interface */ +#define RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL (0x785C) +#define RTL839X_L2_NOTIFICATION_CTRL (0x7808) +#define RTL838X_L2_CTRL_0 (0x3200) +#define RTL839X_L2_CTRL_0 (0x3800) + +/* TRAPPING to CPU-PORT */ +#define RTL838X_SPCL_TRAP_IGMP_CTRL (0x6984) +#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058) +#define RTL838X_RMA_CTRL_0 (0x4300) +#define RTL838X_RMA_CTRL_1 (0x4304) +#define RTL839X_RMA_CTRL_0 (0x1200) +#define RTL839X_RMA_CTRL_1 (0x1204) +#define RTL839X_RMA_CTRL_2 (0x1208) +#define RTL839X_RMA_CTRL_3 (0x120C) + inline int rtl838x_mac_port_ctrl(int p) { return RTL838X_MAC_PORT_CTRL + (p << 7); diff --git a/target/linux/sunxi/base-files/etc/board.d/01_leds b/target/linux/sunxi/base-files/etc/board.d/01_leds old mode 100644 new mode 100755 index 514b8d1796..32fa00f8a2 --- a/target/linux/sunxi/base-files/etc/board.d/01_leds +++ b/target/linux/sunxi/base-files/etc/board.d/01_leds @@ -1,6 +1,5 @@ #!/bin/sh -. /lib/functions/leds.sh . /lib/functions/uci-defaults.sh board=$(board_name) diff --git a/target/linux/sunxi/image/cortexa53.mk b/target/linux/sunxi/image/cortexa53.mk index 7a70de4dff..536fa2417a 100644 --- a/target/linux/sunxi/image/cortexa53.mk +++ b/target/linux/sunxi/image/cortexa53.mk @@ -38,6 +38,15 @@ define Device/friendlyarm_nanopi-neo2 endef TARGET_DEVICES += friendlyarm_nanopi-neo2 +define Device/libretech_all-h3-cc-h5 + DEVICE_VENDOR := Libre Computer + DEVICE_MODEL := ALL-H3-CC + DEVICE_VARIANT := H5 + $(Device/sun50i-h5) + SUNXI_DTS := $$(SUNXI_DTS_DIR)$$(SOC)-libretech-all-h3-cc +endef +TARGET_DEVICES += libretech_all-h3-cc-h5 + define Device/olimex_a64-olinuxino DEVICE_VENDOR := Olimex DEVICE_MODEL := A64-Olinuxino diff --git a/target/linux/x86/generic/config-5.4 b/target/linux/x86/generic/config-5.4 index ada81ce04e..3b7b32a803 100644 --- a/target/linux/x86/generic/config-5.4 +++ b/target/linux/x86/generic/config-5.4 @@ -126,7 +126,7 @@ CONFIG_EFI_RUNTIME_MAP=y CONFIG_EFI_RUNTIME_WRAPPERS=y CONFIG_EFI_STUB=y # CONFIG_EFI_TEST is not set -CONFIG_EFI_VARS=y +# CONFIG_EFI_VARS is not set # CONFIG_EISA is not set # CONFIG_EL3 is not set CONFIG_FAILOVER=y diff --git a/toolchain/glibc/common.mk b/toolchain/glibc/common.mk index 90a22c31e8..fbaad9d3ac 100644 --- a/toolchain/glibc/common.mk +++ b/toolchain/glibc/common.mk @@ -12,8 +12,8 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) -PKG_SOURCE_VERSION:=70ee5e8b573f76745760dd6b75f705590fc1923a -PKG_MIRROR_HASH:=d816dc7658446c2969d307730b58df5f8a65853b4e57a655895feb685590d63b +PKG_SOURCE_VERSION:=5c36293f067d2af16e4eb9f9465be36f346ea6d0 +PKG_MIRROR_HASH:=f01b467843af97fe29dd67d0dc52e8f2effc968258ae806af37be085239a3e4c PKG_SOURCE_URL:=https://sourceware.org/git/glibc.git PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 7f1754a347..81c62d977a 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME := firmware-utils -PKG_RELEASE := 4 +PKG_RELEASE := 5 include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/kernel.mk diff --git a/tools/firmware-utils/src/nec-enc.c b/tools/firmware-utils/src/nec-enc.c index 3c4e38721e..a2be378586 100644 --- a/tools/firmware-utils/src/nec-enc.c +++ b/tools/firmware-utils/src/nec-enc.c @@ -47,7 +47,7 @@ static unsigned char buf_pattern[4096], buf[4096]; int main(int argc, char **argv) { - int k_off = 0, ptn = 0, c, ret = EXIT_SUCCESS; + int k_off = 0, ptn = 1, c, ret = EXIT_SUCCESS; char *ifn = NULL, *ofn = NULL, *key = NULL; size_t n, k_len; FILE *out, *in; @@ -99,11 +99,11 @@ int main(int argc, char **argv) while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { for (int i = 0; i < n; i++) { - buf_pattern[i] = ptn + 1; + buf_pattern[i] = ptn; ptn++; - if (ptn > 250) - ptn = 0; + if (ptn > PATTERN_LEN) + ptn = 1; } k_off = xor_pattern(buf_pattern, n, key, k_len, k_off);