mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-29 09:32:34 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			staging-li
			...
			staging-WI
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 06f5ab9ad4 | 
| @@ -4,19 +4,19 @@ let fs = require("fs"); | ||||
| let ubus = require('ubus').connect(); | ||||
|  | ||||
| let gps_info = ubus.call('gps', 'info'); | ||||
| let latitude = gps_info.latitude || 0; | ||||
| let longitude = gps_info.longitude || 0; | ||||
| let latitude = gps_info.latitude ?? 0; | ||||
| let longitude = gps_info.longitude ?? 0; | ||||
|  | ||||
| // afc-location.json file content | ||||
| let afc_location = {}; | ||||
| afc_location.location_type = "ellipse"; | ||||
| afc_location.location = longitude + ":" + latitude ; | ||||
| afc_location.height = gps_info.elevation || 0; | ||||
| afc_location.height = gps_info.elevation ?? 0; | ||||
| afc_location.height_type = "AMSL"; | ||||
| afc_location.major_axis = int(gps_info.major_axis) || 1; | ||||
| afc_location.minor_axis = int(gps_info.minor_axis) || 1; | ||||
| afc_location.orientation = gps_info.major_orientation || 0; | ||||
| afc_location.vertical_tolerance = int(gps_info.vdop) || 1; | ||||
| afc_location.major_axis = gps_info.major_axis ?? 0; | ||||
| afc_location.minor_axis = gps_info.minor_axis ?? 0; | ||||
| afc_location.orientation = gps_info.major_orientation ?? 0; | ||||
| afc_location.vertical_tolerance = gps_info.vdop ?? 0; | ||||
|  | ||||
| let afc_location_json = fs.open("/etc/ucentral/afc-location.json", "w"); | ||||
| afc_location_json.write(afc_location); | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| let libubus = require("ubus"); | ||||
| import { open, readfile, writefile } from "fs"; | ||||
| import { open, readfile } from "fs"; | ||||
| import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common"; | ||||
|  | ||||
| let uci = require('uci').cursor(); | ||||
| let ubus = libubus.connect(null, 60); | ||||
|  | ||||
| hostapd.data.config = {}; | ||||
| @@ -894,24 +893,10 @@ return { | ||||
| 		hostapd.ubus.disconnect(); | ||||
| 	}, | ||||
| 	afc_request: function(iface, data) { | ||||
| 		let wireless_config = uci.get_all('wireless'); | ||||
| 		for (let l, afc_server in wireless_config) { | ||||
| 			if (afc_server['.type'] == 'afc-server' && afc_server.url && data) { | ||||
| 				hostapd.printf(`Sending AFC request: ${data}`); | ||||
| 				writefile("/tmp/afc-request.json", data); | ||||
|  | ||||
| 				if (afc_server.access_token) | ||||
| 					system(`curl -s -X POST ${afc_server.url} -H \'accept: \*\/\*\' -H \'Authorization: Bearer ${afc_server.access_token}\' -H \'Content-Type: application/json\' -d \'${data}\' --output /tmp/afc-response.json`); | ||||
| 				else if (afc_server.cert) | ||||
| 					system(`curl -s -X POST ${afc_server.url} -H \'accept: \*\/\*\' --cert \'${afc_server.cert}\' -H \'Content-Type: application/json\' -d \'${data}\' --output /tmp/afc-response.json`); | ||||
|  | ||||
| 				let afc_response = (readfile("/tmp/afc-response.json")); | ||||
| 				if (afc_response) | ||||
| 					return afc_response; | ||||
| 				else | ||||
| 					return; | ||||
| 			} | ||||
| 		} | ||||
| 		let ret = ubus.call("afc", "request", { data }); | ||||
| 		if (type(ret) != "object") | ||||
| 			return; | ||||
| 		return ret.data; | ||||
| 	}, | ||||
| 	bss_add: function(name, obj) { | ||||
| 		bss_event("add", name); | ||||
|   | ||||
| @@ -1,42 +0,0 @@ | ||||
| include $(TOPDIR)/rules.mk | ||||
|  | ||||
| PKG_NAME:=fitblk | ||||
| PKG_RELEASE:=2 | ||||
| PKG_LICENSE:=GPL-2.0-only | ||||
| PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org> | ||||
|  | ||||
| PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) | ||||
|  | ||||
| PKG_FLAGS:=nonshared | ||||
|  | ||||
| include $(INCLUDE_DIR)/package.mk | ||||
|  | ||||
| define Package/fitblk | ||||
|   HIDDEN:=1 | ||||
|   SECTION:=base | ||||
|   CATEGORY:=Base system | ||||
|   TITLE:=fitblk firmware release tool | ||||
| endef | ||||
|  | ||||
| define Package/fitblk/description | ||||
| Release uImage.FIT block devices using ioctl. | ||||
| endef | ||||
|  | ||||
| define Build/Configure | ||||
| endef | ||||
|  | ||||
| define Build/Compile | ||||
| 	$(MAKE) -C $(PKG_BUILD_DIR) \ | ||||
| 		CC="$(TARGET_CC)" \ | ||||
| 		CFLAGS="$(TARGET_CFLAGS) -Wall -Werror" \ | ||||
| 		LDFLAGS="$(TARGET_LDFLAGS)" | ||||
| endef | ||||
|  | ||||
| define Package/fitblk/install | ||||
| 	$(INSTALL_DIR) $(1)/usr/sbin | ||||
| 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/fitblk $(1)/usr/sbin/ | ||||
| 	$(INSTALL_DIR) $(1)/lib/upgrade | ||||
| 	$(INSTALL_DATA) ./files/fit.sh $(1)/lib/upgrade | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,fitblk)) | ||||
| @@ -1,63 +0,0 @@ | ||||
| export_fitblk_bootdev() { | ||||
| 	[ -e /sys/firmware/devicetree/base/chosen/rootdisk ] || return | ||||
|  | ||||
| 	local rootdisk="$(cat /sys/firmware/devicetree/base/chosen/rootdisk)" | ||||
| 	local handle bootdev | ||||
|  | ||||
| 	for handle in /sys/class/mtd/mtd*/of_node/volumes/*/phandle; do | ||||
| 		[ ! -e "$handle" ] && continue | ||||
| 		if [ "$rootdisk" = "$(cat "$handle")" ]; then | ||||
| 			if [ -e "${handle%/phandle}/volname" ]; then | ||||
| 				export CI_KERNPART="$(cat "${handle%/phandle}/volname")" | ||||
| 			elif [ -e "${handle%/phandle}/volid" ]; then | ||||
| 				export CI_KERNVOLID="$(cat "${handle%/phandle}/volid")" | ||||
| 			else | ||||
| 				return | ||||
| 			fi | ||||
| 			export CI_UBIPART="$(cat "${handle%%/of_node*}/name")" | ||||
| 			export CI_METHOD="ubi" | ||||
| 			return | ||||
| 		fi | ||||
| 	done | ||||
|  | ||||
| 	for handle in /sys/class/mtd/mtd*/of_node/phandle; do | ||||
| 		[ ! -e "$handle" ] && continue | ||||
| 		if [ "$rootdisk" = "$(cat $handle)" ]; then | ||||
| 			bootdev="${handle%/of_node/phandle}" | ||||
| 			bootdev="${bootdev#/sys/class/mtd/}" | ||||
| 			export PART_NAME="/dev/$bootdev" | ||||
| 			export CI_METHOD="default" | ||||
| 			return | ||||
| 		fi | ||||
| 	done | ||||
|  | ||||
| 	for handle in /sys/class/block/*/of_node/phandle; do | ||||
| 		[ ! -e "$handle" ] && continue | ||||
| 		if [ "$rootdisk" = "$(cat $handle)" ]; then | ||||
| 			bootdev="${handle%/of_node/phandle}" | ||||
| 			bootdev="${bootdev#/sys/class/block/}" | ||||
| 			export EMMC_KERN_DEV="/dev/$bootdev" | ||||
| 			export CI_METHOD="emmc" | ||||
| 			return | ||||
| 		fi | ||||
| 	done | ||||
| } | ||||
|  | ||||
| fit_do_upgrade() { | ||||
| 	export_fitblk_bootdev | ||||
| 	[ -n "$CI_METHOD" ] || return 1 | ||||
| 	[ -e /dev/fit0 ] && fitblk /dev/fit0 | ||||
| 	[ -e /dev/fitrw ] && fitblk /dev/fitrw | ||||
|  | ||||
| 	case "$CI_METHOD" in | ||||
| 	emmc) | ||||
| 		emmc_do_upgrade "$1" | ||||
| 		;; | ||||
| 	default) | ||||
| 		default_do_upgrade "$1" | ||||
| 		;; | ||||
| 	ubi) | ||||
| 		nand_do_upgrade "$1" | ||||
| 		;; | ||||
| 	esac | ||||
| } | ||||
| @@ -1,24 +0,0 @@ | ||||
| --- a/fitblk.c | ||||
| +++ b/fitblk.c | ||||
| @@ -5,7 +5,7 @@ | ||||
|  #include <string.h> | ||||
|  #include <unistd.h> | ||||
|  #include <sys/ioctl.h> | ||||
| -#include <linux/fitblk.h> | ||||
| +#include "fitblk.h" | ||||
|   | ||||
|  static int fitblk_release(char *device) | ||||
|  { | ||||
| --- /dev/null | ||||
| +++ b/fitblk.h | ||||
| @@ -0,0 +1,10 @@ | ||||
| +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ | ||||
| +#ifndef _LINUX_FITBLK_H | ||||
| +#define _LINUX_FITBLK_H | ||||
| + | ||||
| +/* | ||||
| + * IOCTL commands --- we will commandeer 0x46 ('F') | ||||
| + */ | ||||
| +#define FITBLK_RELEASE             0x4600 | ||||
| + | ||||
| +#endif /* _LINUX_FITBLK_H */ | ||||
| @@ -1,7 +0,0 @@ | ||||
| all: fitblk | ||||
|  | ||||
| fitblk: | ||||
| 	$(CC) $(CFLAGS) -o $@ fitblk.c $(LDFLAGS) | ||||
|  | ||||
| clean: | ||||
| 	rm -f fitblk | ||||
| @@ -1,45 +0,0 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-only | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <linux/fitblk.h> | ||||
|  | ||||
| static int fitblk_release(char *device) | ||||
| { | ||||
| 	int fd, ret; | ||||
|  | ||||
| 	fd = open(device, O_RDONLY); | ||||
| 	if (fd == -1) | ||||
| 		return errno; | ||||
|  | ||||
| 	ret = ioctl(fd, FITBLK_RELEASE, NULL); | ||||
| 	close(fd); | ||||
|  | ||||
| 	if (ret == -1) | ||||
| 		return errno; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argp[]) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	if (argc != 2) { | ||||
| 		fprintf(stderr, "Release uImage.FIT sub-image block device\n"); | ||||
| 		fprintf(stderr, "Syntax: %s /dev/fitXXX\n", argp[0]); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	ret = fitblk_release(argp[1]); | ||||
| 	if (ret) | ||||
| 		fprintf(stderr, "fitblk: error releasing %s: %s\n", argp[1], | ||||
| 			strerror(ret)); | ||||
| 	else | ||||
| 		fprintf(stderr, "fitblk: %s released\n", argp[1]); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
| @@ -1,141 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2014-2015 OpenWrt.org | ||||
| # | ||||
| # This is free software, licensed under the GNU General Public License v2. | ||||
| # See /LICENSE for more information. | ||||
| # | ||||
|  | ||||
| include $(TOPDIR)/rules.mk | ||||
|  | ||||
| PKG_NAME:=fstools | ||||
| PKG_RELEASE:=1 | ||||
|  | ||||
| PKG_SOURCE_PROTO:=git | ||||
| PKG_SOURCE_URL=$(PROJECT_GIT)/project/fstools.git | ||||
| PKG_MIRROR_HASH:=8fd5e2a582114c07066cabf90cf490c749735ee9839c3e13149b71aaab8ba522 | ||||
| PKG_SOURCE_DATE:=2024-07-14 | ||||
| PKG_SOURCE_VERSION:=408c2cc48e6694446c89da7f8121b399063e1067 | ||||
| CMAKE_INSTALL:=1 | ||||
|  | ||||
| PKG_LICENSE:=GPL-2.0 | ||||
| PKG_LICENSE_FILES:= | ||||
|  | ||||
| PKG_BUILD_FLAGS:=no-mips16 | ||||
| PKG_FLAGS:=nonshared | ||||
|  | ||||
| PKG_BUILD_DEPENDS := util-linux | ||||
| PKG_CONFIG_DEPENDS := CONFIG_NAND_SUPPORT CONFIG_FSTOOLS_UBIFS_EXTROOT | ||||
|  | ||||
| PKG_MAINTAINER:=John Crispin <john@phrozen.org> | ||||
|  | ||||
| include $(INCLUDE_DIR)/package.mk | ||||
| include $(INCLUDE_DIR)/cmake.mk | ||||
|  | ||||
| CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_UBIFS_EXTROOT),-DCMAKE_UBIFS_EXTROOT=y) | ||||
| CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_OVL_MOUNT_FULL_ACCESS_TIME),-DCMAKE_OVL_MOUNT_FULL_ACCESS_TIME=y) | ||||
| CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_OVL_MOUNT_COMPRESS_ZLIB),-DCMAKE_OVL_MOUNT_COMPRESS_ZLIB=y) | ||||
|  | ||||
| define Package/fstools | ||||
|   SECTION:=base | ||||
|   CATEGORY:=Base system | ||||
|   DEPENDS:=+ubox +NAND_SUPPORT:ubi-utils | ||||
|   TITLE:=OpenWrt filesystem tools | ||||
|   MENU:=1 | ||||
| endef | ||||
|  | ||||
| define Package/fstools/config | ||||
| 	config FSTOOLS_UBIFS_EXTROOT | ||||
| 		depends on PACKAGE_fstools | ||||
| 		depends on NAND_SUPPORT | ||||
| 		bool "Support extroot functionality with UBIFS" | ||||
| 		default y | ||||
| 		help | ||||
| 			This option makes it possible to use extroot functionality if the root filesystem resides on an UBIFS partition | ||||
|  | ||||
| 	config FSTOOLS_OVL_MOUNT_FULL_ACCESS_TIME | ||||
| 		depends on PACKAGE_fstools | ||||
| 		bool "Full access time accounting" | ||||
| 		default n | ||||
| 		help | ||||
| 			This option enables the full access time accounting (warning: it will increase the flash writes). | ||||
|  | ||||
| 	config FSTOOLS_OVL_MOUNT_COMPRESS_ZLIB | ||||
| 		depends on PACKAGE_fstools | ||||
| 		bool "Compress using zlib" | ||||
| 		default n | ||||
| 		help | ||||
| 			This option enables the compression using zlib on the storage device. | ||||
| endef | ||||
|  | ||||
| define Package/snapshot-tool | ||||
|   SECTION:=base | ||||
|   CATEGORY:=Base system | ||||
|   TITLE:=rootfs snapshoting tool | ||||
|   DEPENDS:=+libubox +fstools | ||||
| endef | ||||
|  | ||||
| define Package/block-mount/conffiles | ||||
| /etc/config/fstab | ||||
| endef | ||||
|  | ||||
| define Package/block-mount | ||||
|   SECTION:=base | ||||
|   CATEGORY:=Base system | ||||
|   TITLE:=Block device mounting and checking | ||||
|   DEPENDS:=+ubox +libubox +libuci +libblobmsg-json +libjson-c | ||||
| endef | ||||
|  | ||||
| define Package/blockd | ||||
|   SECTION:=base | ||||
|   CATEGORY:=Base system | ||||
|   TITLE:=Block device automounting | ||||
|   DEPENDS:=+block-mount +fstools +libubus +kmod-fs-autofs4 +libblobmsg-json +libjson-c | ||||
| endef | ||||
|  | ||||
| define Package/fstools/install | ||||
| 	$(INSTALL_DIR) $(1)/sbin $(1)/lib | ||||
|  | ||||
| 	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{mount_root,jffs2reset} $(1)/sbin/ | ||||
| 	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libfstools.so $(1)/lib/ | ||||
| 	$(LN) jffs2reset $(1)/sbin/jffs2mark | ||||
| endef | ||||
|  | ||||
| define Package/snapshot-tool/install | ||||
| 	$(INSTALL_DIR) $(1)/sbin | ||||
|  | ||||
| 	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/snapshot_tool $(1)/sbin/ | ||||
| 	$(INSTALL_BIN) ./files/snapshot $(1)/sbin/ | ||||
| endef | ||||
|  | ||||
| define Package/block-mount/install | ||||
| 	$(INSTALL_DIR) $(1)/sbin $(1)/lib $(1)/usr/sbin $(1)/etc/hotplug.d/block $(1)/etc/init.d/ $(1)/etc/uci-defaults/ | ||||
|  | ||||
| 	$(INSTALL_BIN) ./files/fstab.init $(1)/etc/init.d/fstab | ||||
| 	$(INSTALL_CONF) ./files/fstab.default $(1)/etc/uci-defaults/10-fstab | ||||
| 	$(INSTALL_CONF) ./files/mount.hotplug $(1)/etc/hotplug.d/block/10-mount | ||||
| 	$(INSTALL_CONF) ./files/media-change.hotplug  $(1)/etc/hotplug.d/block/00-media-change | ||||
|  | ||||
| 	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/block $(1)/sbin/ | ||||
| 	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libblkid-tiny.so $(1)/lib/ | ||||
| 	$(LN) ../../sbin/block $(1)/usr/sbin/swapon | ||||
| 	$(LN) ../../sbin/block $(1)/usr/sbin/swapoff | ||||
|  | ||||
| endef | ||||
|  | ||||
| define Package/blockd/install | ||||
| 	$(INSTALL_DIR) $(1)/sbin $(1)/etc/init.d/ | ||||
| 	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/blockd $(1)/sbin/ | ||||
| 	$(INSTALL_BIN) ./files/blockd.init $(1)/etc/init.d/blockd | ||||
| endef | ||||
|  | ||||
| define Build/InstallDev | ||||
| 	$(INSTALL_DIR) $(1)/usr/include | ||||
| 	$(CP) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/ | ||||
| 	$(INSTALL_DIR) $(1)/usr/lib/ | ||||
| 	$(CP) $(PKG_INSTALL_DIR)/usr/lib/libubi-utils.a $(1)/usr/lib/ | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,fstools)) | ||||
| $(eval $(call BuildPackage,snapshot-tool)) | ||||
| $(eval $(call BuildPackage,block-mount)) | ||||
| $(eval $(call BuildPackage,blockd)) | ||||
| @@ -1,22 +0,0 @@ | ||||
| #!/bin/sh /etc/rc.common | ||||
|  | ||||
| START=80 | ||||
|  | ||||
| USE_PROCD=1 | ||||
| PROG=/sbin/blockd | ||||
|  | ||||
| service_triggers() { | ||||
| 	procd_add_reload_trigger "fstab" | ||||
| } | ||||
|  | ||||
| reload_service() { | ||||
| 	block autofs start | ||||
| } | ||||
|  | ||||
| start_service() { | ||||
| 	procd_open_instance | ||||
| 	procd_set_param command "$PROG" | ||||
| 	procd_set_param watch block | ||||
| 	procd_set_param respawn | ||||
| 	procd_close_instance | ||||
| } | ||||
| @@ -1,2 +0,0 @@ | ||||
| [ ! -f /etc/config/fstab ] && ( block detect > /etc/config/fstab ) | ||||
| exit 0 | ||||
| @@ -1,22 +0,0 @@ | ||||
| #!/bin/sh /etc/rc.common | ||||
| # SPDX-License-Identifier: GPL-2.0-only | ||||
| # | ||||
| # Copyright (C) 2013-2020 OpenWrt.org | ||||
|  | ||||
| START=11 | ||||
|  | ||||
| boot() { | ||||
| 	/sbin/block mount | ||||
| } | ||||
|  | ||||
| start() { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| restart() { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| stop() { | ||||
| 	/sbin/block umount | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| [ -n "$DISK_MEDIA_CHANGE" ] && /sbin/block info | ||||
|  | ||||
| if [ "$ACTION" = "add" -a "$DEVTYPE" = "disk" ]; then | ||||
| 	case "$DEVNAME" in | ||||
| 		mtd*) : ;; | ||||
| 		*) echo 2000 > /sys/block/$DEVNAME/events_poll_msecs ;; | ||||
| 	esac | ||||
| fi | ||||
| @@ -1 +0,0 @@ | ||||
| [ "$ACTION" = "add" -o "$ACTION" = "remove" ] && /sbin/block hotplug | ||||
| @@ -1,113 +0,0 @@ | ||||
| #!/bin/sh | ||||
| # Copyright (C) 2014 OpenWrt.org | ||||
|  | ||||
|  | ||||
| do_snapshot_unpack() { | ||||
| 	echo "- snapshot -" | ||||
| 	mkdir /tmp/snapshot | ||||
| 	cd /tmp/snapshot | ||||
| 	snapshot_tool read | ||||
| 	block=`ls block*.tar.gz 2> /dev/null` | ||||
| 	[ -z "$block" ] || for a in $block; do | ||||
| 		tar xzf $a -C / | ||||
| 		rm -f $a | ||||
| 	done | ||||
| } | ||||
|  | ||||
| do_config_unpack() { | ||||
| 	echo "- config -" | ||||
| 	snapshot_tool config_read | ||||
| 	[ -f /tmp/config.tar.gz ] && { | ||||
| 		tar xzf /tmp/config.tar.gz -C / | ||||
| 		rm -f /tmp/config.tar.gz | ||||
| 	} | ||||
| } | ||||
|  | ||||
| do_snapshot_push() { | ||||
| 	cd /volatile/upper | ||||
| 	tar czf /tmp/snapshot.tar.gz * | ||||
| 	snapshot_tool write | ||||
| 	reboot | ||||
| } | ||||
|  | ||||
| do_config_push() { | ||||
| 	cd /volatile/upper | ||||
| 	tar czf /tmp/config.tar.gz * | ||||
| 	snapshot_tool config_write | ||||
| } | ||||
|  | ||||
| do_snapshot_upgrade() { | ||||
| 	opkg update | ||||
| 	[ $? -eq 0 ] || exit 1 | ||||
|  | ||||
| 	opkg list-upgradable | ||||
| 	[ $? -eq 0 ] || exit 2 | ||||
|  | ||||
| 	UPDATES=`opkg list-upgradable | cut -d" " -f1` | ||||
| 	[ -z "${UPDATES}" ] && exit 0 | ||||
|  | ||||
| 	opkg upgrade ${UPDATES} | ||||
| 	[ $? -eq 0 ] || exit 3 | ||||
|  | ||||
| 	do_snapshot_push | ||||
| 	sleep 5 | ||||
| 	reboot | ||||
| 	sleep 10 | ||||
| } | ||||
|  | ||||
| do_convert_jffs2() { | ||||
| 	snapshot_tool write | ||||
| 	sleep 2 | ||||
| 	reboot -f | ||||
| } | ||||
|  | ||||
| do_convert() { | ||||
| 	. /lib/functions.sh | ||||
| 	. /lib/upgrade/common.sh | ||||
|  | ||||
| 	cd /overlay/upper | ||||
| 	tar czf /tmp/snapshot.tar.gz * | ||||
|  | ||||
| 	install_bin /sbin/upgraded | ||||
| 	ubus call system sysupgrade "{ | ||||
| 		\"prefix\": \"$RAM_ROOT\", | ||||
| 		\"path\": \"\", | ||||
| 		\"command\": \". /sbin/snapshot; do_convert_jffs2\" | ||||
| 	}" | ||||
| } | ||||
|  | ||||
| [ -n "$(cat /proc/mounts|grep /overlay|grep jffs2)" ] && { | ||||
| case $1 in | ||||
| convert) | ||||
| 	do_convert | ||||
| 	;; | ||||
| esac | ||||
| } | ||||
|  | ||||
| [ -d /volatile/upper ] && { | ||||
| case $1 in | ||||
| push) | ||||
| 	do_snapshot_push | ||||
| 	;; | ||||
| config) | ||||
| 	do_config_push | ||||
| 	;; | ||||
| upgrade) | ||||
| 	do_snapshot_upgrade | ||||
| 	;; | ||||
| info) | ||||
| 	snapshot_tool info | ||||
| 	;; | ||||
| esac | ||||
| } | ||||
|  | ||||
| [ "$SNAPSHOT" = "magic" ] && { | ||||
| case $1 in | ||||
| unpack) | ||||
| 	do_snapshot_unpack | ||||
| 	;; | ||||
| config_unpack) | ||||
| 	do_config_unpack | ||||
| 	;; | ||||
| esac | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2008-2012 OpenWrt.org | ||||
| # | ||||
| # This is free software, licensed under the GNU General Public License v2. | ||||
| # See /LICENSE for more information. | ||||
| # | ||||
|  | ||||
| include $(TOPDIR)/rules.mk | ||||
| include $(INCLUDE_DIR)/kernel.mk | ||||
|  | ||||
| PKG_NAME:=gpio-button-hotplug | ||||
| PKG_RELEASE:=5 | ||||
| PKG_LICENSE:=GPL-2.0 | ||||
|  | ||||
| include $(INCLUDE_DIR)/package.mk | ||||
|  | ||||
| define KernelPackage/gpio-button-hotplug | ||||
|   SUBMENU:=GPIO support | ||||
|   TITLE:=Simple GPIO Button Hotplug driver | ||||
|   FILES:=$(PKG_BUILD_DIR)/gpio-button-hotplug.ko | ||||
|   AUTOLOAD:=$(call AutoLoad,30,gpio-button-hotplug,1) | ||||
|   KCONFIG:= | ||||
| endef | ||||
|  | ||||
| define KernelPackage/gpio-button-hotplug/description | ||||
|  This is a replacement for the following in-kernel drivers: | ||||
|  1) gpio_keys (KEYBOARD_GPIO) | ||||
|  2) gpio_keys_polled (KEYBOARD_GPIO_POLLED) | ||||
|  | ||||
|  Instead of generating input events (like in-kernel drivers do) it generates | ||||
|  uevent-s and broadcasts them. This allows disabling input subsystem which is | ||||
|  an overkill for OpenWrt simple needs. | ||||
| endef | ||||
|  | ||||
| define Build/Compile | ||||
| 	$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules | ||||
| endef | ||||
|  | ||||
| $(eval $(call KernelPackage,gpio-button-hotplug)) | ||||
| @@ -1 +0,0 @@ | ||||
| obj-m += gpio-button-hotplug.o | ||||
| @@ -1,737 +0,0 @@ | ||||
| /* | ||||
|  *  GPIO Button Hotplug driver | ||||
|  * | ||||
|  *  Copyright (C) 2012 Felix Fietkau <nbd@nbd.name> | ||||
|  *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> | ||||
|  * | ||||
|  *  Based on the diag.c - GPIO interface driver for Broadcom boards | ||||
|  *    Copyright (C) 2006 Mike Baker <mbm@openwrt.org>, | ||||
|  *    Copyright (C) 2006-2007 Felix Fietkau <nbd@nbd.name> | ||||
|  *    Copyright (C) 2008 Andy Boyett <agb@openwrt.org> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify it | ||||
|  *  under the terms of the GNU General Public License version 2 as published | ||||
|  *  by the Free Software Foundation. | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
| #include <linux/version.h> | ||||
| #include <linux/kmod.h> | ||||
|  | ||||
| #include <linux/workqueue.h> | ||||
| #include <linux/skbuff.h> | ||||
| #include <linux/netlink.h> | ||||
| #include <linux/kobject.h> | ||||
| #include <linux/input.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/of_gpio.h> | ||||
| #include <linux/of_irq.h> | ||||
| #include <linux/gpio_keys.h> | ||||
| #include <linux/gpio/consumer.h> | ||||
|  | ||||
| #define BH_SKB_SIZE	2048 | ||||
|  | ||||
| #define DRV_NAME	"gpio-keys" | ||||
| #define PFX	DRV_NAME ": " | ||||
|  | ||||
| struct bh_event { | ||||
| 	const char		*name; | ||||
| 	unsigned int		type; | ||||
| 	char			*action; | ||||
| 	unsigned long		seen; | ||||
|  | ||||
| 	struct sk_buff		*skb; | ||||
| 	struct work_struct	work; | ||||
| }; | ||||
|  | ||||
| struct bh_map { | ||||
| 	unsigned int	code; | ||||
| 	const char	*name; | ||||
| }; | ||||
|  | ||||
| struct gpio_keys_button_data { | ||||
| 	struct delayed_work work; | ||||
| 	unsigned long seen; | ||||
| 	int map_entry; | ||||
| 	int last_state; | ||||
| 	int count; | ||||
| 	int threshold; | ||||
| 	int can_sleep; | ||||
| 	int irq; | ||||
| 	unsigned int software_debounce; | ||||
| 	struct gpio_desc *gpiod; | ||||
| 	const struct gpio_keys_button *b; | ||||
| }; | ||||
|  | ||||
| extern u64 uevent_next_seqnum(void); | ||||
|  | ||||
| #define BH_MAP(_code, _name)		\ | ||||
| 	{				\ | ||||
| 		.code = (_code),	\ | ||||
| 		.name = (_name),	\ | ||||
| 	} | ||||
|  | ||||
| static struct bh_map button_map[] = { | ||||
| 	BH_MAP(BTN_0,			"BTN_0"), | ||||
| 	BH_MAP(BTN_1,			"BTN_1"), | ||||
| 	BH_MAP(BTN_2,			"BTN_2"), | ||||
| 	BH_MAP(BTN_3,			"BTN_3"), | ||||
| 	BH_MAP(BTN_4,			"BTN_4"), | ||||
| 	BH_MAP(BTN_5,			"BTN_5"), | ||||
| 	BH_MAP(BTN_6,			"BTN_6"), | ||||
| 	BH_MAP(BTN_7,			"BTN_7"), | ||||
| 	BH_MAP(BTN_8,			"BTN_8"), | ||||
| 	BH_MAP(BTN_9,			"BTN_9"), | ||||
| 	BH_MAP(KEY_BRIGHTNESS_ZERO,	"brightness_zero"), | ||||
| 	BH_MAP(KEY_CONFIG,		"config"), | ||||
| 	BH_MAP(KEY_COPY,		"copy"), | ||||
| 	BH_MAP(KEY_EJECTCD,		"eject"), | ||||
| 	BH_MAP(KEY_HELP,		"help"), | ||||
| 	BH_MAP(KEY_LIGHTS_TOGGLE,	"lights_toggle"), | ||||
| 	BH_MAP(KEY_PHONE,		"phone"), | ||||
| 	BH_MAP(KEY_POWER,		"power"), | ||||
| 	BH_MAP(KEY_POWER2,		"reboot"), | ||||
| 	BH_MAP(KEY_RESTART,		"reset"), | ||||
| 	BH_MAP(KEY_RFKILL,		"rfkill"), | ||||
| 	BH_MAP(KEY_VIDEO,		"video"), | ||||
| 	BH_MAP(KEY_VOLUMEDOWN,		"volume_down"), | ||||
| 	BH_MAP(KEY_VOLUMEUP,		"volume_up"), | ||||
| 	BH_MAP(KEY_WIMAX,		"wwan"), | ||||
| 	BH_MAP(KEY_WLAN,		"wlan"), | ||||
| 	BH_MAP(KEY_WPS_BUTTON,		"wps"), | ||||
| 	BH_MAP(KEY_VENDOR,		"vendor"), | ||||
| }; | ||||
|  | ||||
| /* -------------------------------------------------------------------------*/ | ||||
|  | ||||
| static __printf(3, 4) | ||||
| int bh_event_add_var(struct bh_event *event, int argv, const char *format, ...) | ||||
| { | ||||
| 	char buf[128]; | ||||
| 	char *s; | ||||
| 	va_list args; | ||||
| 	int len; | ||||
|  | ||||
| 	if (argv) | ||||
| 		return 0; | ||||
|  | ||||
| 	va_start(args, format); | ||||
| 	len = vsnprintf(buf, sizeof(buf), format, args); | ||||
| 	va_end(args); | ||||
|  | ||||
| 	if (len >= sizeof(buf)) { | ||||
| 		WARN(1, "buffer size too small"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	s = skb_put(event->skb, len + 1); | ||||
| 	strcpy(s, buf); | ||||
|  | ||||
| 	pr_debug(PFX "added variable '%s'\n", s); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int button_hotplug_fill_event(struct bh_event *event) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "HOME=%s", "/"); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "PATH=%s", | ||||
| 					"/sbin:/bin:/usr/sbin:/usr/bin"); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button"); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "ACTION=%s", event->action); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	if (event->type == EV_SW) { | ||||
| 		ret = bh_event_add_var(event, 0, "TYPE=%s", "switch"); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum()); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void button_hotplug_work(struct work_struct *work) | ||||
| { | ||||
| 	struct bh_event *event = container_of(work, struct bh_event, work); | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL); | ||||
| 	if (!event->skb) | ||||
| 		goto out_free_event; | ||||
|  | ||||
| 	ret = bh_event_add_var(event, 0, "%s@", event->action); | ||||
| 	if (ret) | ||||
| 		goto out_free_skb; | ||||
|  | ||||
| 	ret = button_hotplug_fill_event(event); | ||||
| 	if (ret) | ||||
| 		goto out_free_skb; | ||||
|  | ||||
| 	NETLINK_CB(event->skb).dst_group = 1; | ||||
| 	broadcast_uevent(event->skb, 0, 1, GFP_KERNEL); | ||||
|  | ||||
|  out_free_skb: | ||||
| 	if (ret) { | ||||
| 		pr_err(PFX "work error %d\n", ret); | ||||
| 		kfree_skb(event->skb); | ||||
| 	} | ||||
|  out_free_event: | ||||
| 	kfree(event); | ||||
| } | ||||
|  | ||||
| static int button_hotplug_create_event(const char *name, unsigned int type, | ||||
| 		unsigned long seen, int pressed) | ||||
| { | ||||
| 	struct bh_event *event; | ||||
|  | ||||
| 	pr_debug(PFX "create event, name=%s, seen=%lu, pressed=%d\n", | ||||
| 		 name, seen, pressed); | ||||
|  | ||||
| 	event = kzalloc(sizeof(*event), GFP_KERNEL); | ||||
| 	if (!event) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	event->name = name; | ||||
| 	event->type = type; | ||||
| 	event->seen = seen; | ||||
| 	event->action = pressed ? "pressed" : "released"; | ||||
|  | ||||
| 	INIT_WORK(&event->work, (void *)(void *)button_hotplug_work); | ||||
| 	schedule_work(&event->work); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------------------------------------*/ | ||||
|  | ||||
| static int button_get_index(unsigned int code) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(button_map); i++) | ||||
| 		if (button_map[i].code == code) | ||||
| 			return i; | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static int gpio_button_get_value(struct gpio_keys_button_data *bdata) | ||||
| { | ||||
| 	int val; | ||||
|  | ||||
| 	if (bdata->can_sleep) | ||||
| 		val = !!gpiod_get_value_cansleep(bdata->gpiod); | ||||
| 	else | ||||
| 		val = !!gpiod_get_value(bdata->gpiod); | ||||
|  | ||||
| 	return val; | ||||
| } | ||||
|  | ||||
| static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata) | ||||
| { | ||||
| 	unsigned int type = bdata->b->type ?: EV_KEY; | ||||
| 	int state = gpio_button_get_value(bdata); | ||||
| 	unsigned long seen = jiffies; | ||||
|  | ||||
| 	pr_debug(PFX "event type=%u, code=%u, pressed=%d\n", | ||||
| 		 type, bdata->b->code, state); | ||||
|  | ||||
| 	/* is this the initialization state? */ | ||||
| 	if (bdata->last_state == -1) { | ||||
| 		/* | ||||
| 		 * Don't advertise unpressed buttons on initialization. | ||||
| 		 * Just save their state and continue otherwise this | ||||
| 		 * can cause OpenWrt to enter failsafe. | ||||
| 		 */ | ||||
| 		if (type == EV_KEY && state == 0) | ||||
| 			goto set_state; | ||||
| 		/* | ||||
| 		 * But we are very interested in pressed buttons and | ||||
| 		 * initial switch state. These will be reported to | ||||
| 		 * userland. | ||||
| 		 */ | ||||
| 	} else if (bdata->last_state == state) { | ||||
| 		/* reset asserted counter (only relevant for polled keys) */ | ||||
| 		bdata->count = 0; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (bdata->count < bdata->threshold) { | ||||
| 		bdata->count++; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (bdata->seen == 0) | ||||
| 		bdata->seen = seen; | ||||
|  | ||||
| 	button_hotplug_create_event(button_map[bdata->map_entry].name, type, | ||||
| 				    (seen - bdata->seen) / HZ, state); | ||||
| 	bdata->seen = seen; | ||||
|  | ||||
| set_state: | ||||
| 	bdata->last_state = state; | ||||
| 	bdata->count = 0; | ||||
| } | ||||
|  | ||||
| struct gpio_keys_button_dev { | ||||
| 	int polled; | ||||
| 	struct delayed_work work; | ||||
|  | ||||
| 	struct device *dev; | ||||
| 	struct gpio_keys_platform_data *pdata; | ||||
| 	struct gpio_keys_button_data data[]; | ||||
| }; | ||||
|  | ||||
| static void gpio_keys_polled_queue_work(struct gpio_keys_button_dev *bdev) | ||||
| { | ||||
| 	struct gpio_keys_platform_data *pdata = bdev->pdata; | ||||
| 	unsigned long delay = msecs_to_jiffies(pdata->poll_interval); | ||||
|  | ||||
| 	if (delay >= HZ) | ||||
| 		delay = round_jiffies_relative(delay); | ||||
| 	schedule_delayed_work(&bdev->work, delay); | ||||
| } | ||||
|  | ||||
| static void gpio_keys_polled_poll(struct work_struct *work) | ||||
| { | ||||
| 	struct gpio_keys_button_dev *bdev = | ||||
| 		container_of(work, struct gpio_keys_button_dev, work.work); | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < bdev->pdata->nbuttons; i++) { | ||||
| 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | ||||
|  | ||||
| 		if (bdata->gpiod) | ||||
| 			gpio_keys_handle_button(bdata); | ||||
| 	} | ||||
| 	gpio_keys_polled_queue_work(bdev); | ||||
| } | ||||
|  | ||||
| static void gpio_keys_polled_close(struct gpio_keys_button_dev *bdev) | ||||
| { | ||||
| 	struct gpio_keys_platform_data *pdata = bdev->pdata; | ||||
|  | ||||
| 	cancel_delayed_work_sync(&bdev->work); | ||||
|  | ||||
| 	if (pdata->disable) | ||||
| 		pdata->disable(bdev->dev); | ||||
| } | ||||
|  | ||||
| static void gpio_keys_irq_work_func(struct work_struct *work) | ||||
| { | ||||
| 	struct gpio_keys_button_data *bdata = container_of(work, | ||||
| 		struct gpio_keys_button_data, work.work); | ||||
|  | ||||
| 	gpio_keys_handle_button(bdata); | ||||
| } | ||||
|  | ||||
| static irqreturn_t button_handle_irq(int irq, void *_bdata) | ||||
| { | ||||
| 	struct gpio_keys_button_data *bdata = | ||||
| 		(struct gpio_keys_button_data *) _bdata; | ||||
|  | ||||
| 	mod_delayed_work(system_wq, &bdata->work, | ||||
| 			 msecs_to_jiffies(bdata->software_debounce)); | ||||
|  | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
|  | ||||
| #ifdef CONFIG_OF | ||||
| static struct gpio_keys_platform_data * | ||||
| gpio_keys_get_devtree_pdata(struct device *dev) | ||||
| { | ||||
| 	struct device_node *node, *pp; | ||||
| 	struct gpio_keys_platform_data *pdata; | ||||
| 	struct gpio_keys_button *button; | ||||
| 	int nbuttons; | ||||
| 	int i = 0; | ||||
|  | ||||
| 	node = dev->of_node; | ||||
| 	if (!node) | ||||
| 		return NULL; | ||||
|  | ||||
| 	nbuttons = of_get_available_child_count(node); | ||||
| 	if (nbuttons == 0) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
|  | ||||
| 	pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * (sizeof *button), | ||||
| 		GFP_KERNEL); | ||||
| 	if (!pdata) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
|  | ||||
| 	pdata->buttons = (struct gpio_keys_button *)(pdata + 1); | ||||
| 	pdata->nbuttons = nbuttons; | ||||
|  | ||||
| 	pdata->rep = !!of_get_property(node, "autorepeat", NULL); | ||||
| 	of_property_read_u32(node, "poll-interval", &pdata->poll_interval); | ||||
|  | ||||
| 	for_each_available_child_of_node(node, pp) { | ||||
| 		button = (struct gpio_keys_button *)(&pdata->buttons[i++]); | ||||
|  | ||||
| 		if (of_property_read_u32(pp, "linux,code", &button->code)) { | ||||
| 			dev_err(dev, "Button node '%s' without keycode\n", | ||||
| 				pp->full_name); | ||||
| 			of_node_put(pp); | ||||
| 			return ERR_PTR(-EINVAL); | ||||
| 		} | ||||
|  | ||||
| 		button->desc = of_get_property(pp, "label", NULL); | ||||
|  | ||||
| 		if (of_property_read_u32(pp, "linux,input-type", &button->type)) | ||||
| 			button->type = EV_KEY; | ||||
|  | ||||
| 		button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); | ||||
|  | ||||
| 		if (of_property_read_u32(pp, "debounce-interval", | ||||
| 					&button->debounce_interval)) | ||||
| 			button->debounce_interval = 5; | ||||
|  | ||||
| 		button->irq = irq_of_parse_and_map(pp, 0); | ||||
| 		button->gpio = -ENOENT; /* mark this as device-tree */ | ||||
| 	} | ||||
|  | ||||
| 	return pdata; | ||||
| } | ||||
|  | ||||
| static struct of_device_id gpio_keys_of_match[] = { | ||||
| 	{ .compatible = "gpio-keys", }, | ||||
| 	{ }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, gpio_keys_of_match); | ||||
|  | ||||
| static struct of_device_id gpio_keys_polled_of_match[] = { | ||||
| 	{ .compatible = "gpio-keys-polled", }, | ||||
| 	{ }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match); | ||||
|  | ||||
| #else | ||||
|  | ||||
| static inline struct gpio_keys_platform_data * | ||||
| gpio_keys_get_devtree_pdata(struct device *dev) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int gpio_keys_button_probe(struct platform_device *pdev, | ||||
| 		struct gpio_keys_button_dev **_bdev, int polled) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); | ||||
| 	struct gpio_keys_button_dev *bdev; | ||||
| 	struct gpio_keys_button *buttons; | ||||
| 	struct device_node *prev = NULL; | ||||
| 	int error = 0; | ||||
| 	int i; | ||||
|  | ||||
| 	if (!pdata) { | ||||
| 		pdata = gpio_keys_get_devtree_pdata(dev); | ||||
| 		if (IS_ERR(pdata)) | ||||
| 			return PTR_ERR(pdata); | ||||
| 		if (!pdata) { | ||||
| 			dev_err(dev, "missing platform data\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (polled && !pdata->poll_interval) { | ||||
| 		dev_err(dev, "missing poll_interval value\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	buttons = devm_kzalloc(dev, pdata->nbuttons * sizeof(struct gpio_keys_button), | ||||
| 		       GFP_KERNEL); | ||||
| 	if (!buttons) { | ||||
| 		dev_err(dev, "no memory for button data\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	memcpy(buttons, pdata->buttons, pdata->nbuttons * sizeof(struct gpio_keys_button)); | ||||
|  | ||||
| 	bdev = devm_kzalloc(dev, sizeof(struct gpio_keys_button_dev) + | ||||
| 		       pdata->nbuttons * sizeof(struct gpio_keys_button_data), | ||||
| 		       GFP_KERNEL); | ||||
| 	if (!bdev) { | ||||
| 		dev_err(dev, "no memory for private data\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	bdev->polled = polled; | ||||
|  | ||||
| 	for (i = 0; i < pdata->nbuttons; i++) { | ||||
| 		struct gpio_keys_button *button = &buttons[i]; | ||||
| 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | ||||
| 		const char *desc = button->desc ? button->desc : DRV_NAME; | ||||
|  | ||||
| 		if (button->wakeup) { | ||||
| 			dev_err(dev, "does not support wakeup\n"); | ||||
| 			error = -EINVAL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		bdata->map_entry = button_get_index(button->code); | ||||
| 		if (bdata->map_entry < 0) { | ||||
| 			dev_err(dev, "does not support key code:%u\n", | ||||
| 				button->code); | ||||
| 			error = -EINVAL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		if (!(button->type == 0 || button->type == EV_KEY || | ||||
| 		      button->type == EV_SW)) { | ||||
| 			dev_err(dev, "only supports buttons or switches\n"); | ||||
| 			error = -EINVAL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		if (button->irq) { | ||||
| 			dev_err(dev, "skipping button %s (only gpio buttons supported)\n", | ||||
| 				button->desc); | ||||
| 			bdata->b = &pdata->buttons[i]; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (gpio_is_valid(button->gpio)) { | ||||
| 			/* legacy platform data... but is it the lookup table? */ | ||||
| 			bdata->gpiod = devm_gpiod_get_index(dev, desc, i, | ||||
| 							    GPIOD_IN); | ||||
| 			if (IS_ERR(bdata->gpiod)) { | ||||
| 				/* or the legacy (button->gpio is good) way? */ | ||||
| 				error = devm_gpio_request_one(dev, | ||||
| 					button->gpio, GPIOF_IN | ( | ||||
| 					button->active_low ? GPIOF_ACTIVE_LOW : | ||||
| 					0), desc); | ||||
| 				if (error) { | ||||
| 					dev_err_probe(dev, error, | ||||
| 						      "unable to claim gpio %d", | ||||
| 						      button->gpio); | ||||
| 					goto out; | ||||
| 				} | ||||
|  | ||||
| 				bdata->gpiod = gpio_to_desc(button->gpio); | ||||
| 			} | ||||
| 		} else { | ||||
| 			/* Device-tree */ | ||||
| 			struct device_node *child = | ||||
| 				of_get_next_child(dev->of_node, prev); | ||||
|  | ||||
| 			bdata->gpiod = devm_fwnode_gpiod_get(dev, | ||||
| 				of_fwnode_handle(child), NULL, GPIOD_IN, | ||||
| 				desc); | ||||
|  | ||||
| 			prev = child; | ||||
| 		} | ||||
|  | ||||
| 		if (IS_ERR_OR_NULL(bdata->gpiod)) { | ||||
| 			error = IS_ERR(bdata->gpiod) ? PTR_ERR(bdata->gpiod) : | ||||
| 				-EINVAL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		bdata->can_sleep = gpiod_cansleep(bdata->gpiod); | ||||
| 		bdata->last_state = -1; /* Unknown state on boot */ | ||||
|  | ||||
| 		if (bdev->polled) { | ||||
| 			bdata->threshold = DIV_ROUND_UP(button->debounce_interval, | ||||
| 							pdata->poll_interval); | ||||
| 		} else { | ||||
| 			/* bdata->threshold = 0; already initialized */ | ||||
|  | ||||
| 			if (button->debounce_interval) { | ||||
| 				error = gpiod_set_debounce(bdata->gpiod, | ||||
| 					button->debounce_interval * 1000); | ||||
| 				/* | ||||
| 				 * use timer if gpiolib doesn't provide | ||||
| 				 * debounce. | ||||
| 				 */ | ||||
| 				if (error < 0) { | ||||
| 					bdata->software_debounce = | ||||
| 						button->debounce_interval; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		bdata->b = &pdata->buttons[i]; | ||||
| 	} | ||||
|  | ||||
| 	bdev->dev = &pdev->dev; | ||||
| 	bdev->pdata = pdata; | ||||
| 	platform_set_drvdata(pdev, bdev); | ||||
|  | ||||
| 	*_bdev = bdev; | ||||
| 	error = 0; | ||||
|  | ||||
| out: | ||||
| 	of_node_put(prev); | ||||
| 	return error; | ||||
| } | ||||
|  | ||||
| static int gpio_keys_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct gpio_keys_platform_data *pdata; | ||||
| 	struct gpio_keys_button_dev *bdev; | ||||
| 	int ret, i; | ||||
|  | ||||
| 	ret = gpio_keys_button_probe(pdev, &bdev, 0); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	pdata = bdev->pdata; | ||||
| 	for (i = 0; i < pdata->nbuttons; i++) { | ||||
| 		const struct gpio_keys_button *button = &pdata->buttons[i]; | ||||
| 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | ||||
| 		unsigned long irqflags = IRQF_ONESHOT; | ||||
|  | ||||
| 		INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func); | ||||
|  | ||||
| 		if (!button->irq) { | ||||
| 			bdata->irq = gpiod_to_irq(bdata->gpiod); | ||||
| 			if (bdata->irq < 0) { | ||||
| 				dev_err(&pdev->dev, "failed to get irq for gpio:%d\n", | ||||
| 					button->gpio); | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			irqflags |= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; | ||||
| 		} else { | ||||
| 			bdata->irq = button->irq; | ||||
| 		} | ||||
|  | ||||
| 		schedule_delayed_work(&bdata->work, | ||||
| 				      msecs_to_jiffies(bdata->software_debounce)); | ||||
|  | ||||
| 		ret = devm_request_threaded_irq(&pdev->dev, | ||||
| 			bdata->irq, NULL, button_handle_irq, | ||||
| 			irqflags, dev_name(&pdev->dev), bdata); | ||||
| 		if (ret < 0) { | ||||
| 			bdata->irq = 0; | ||||
| 			dev_err(&pdev->dev, "failed to request irq:%d for gpio:%d\n", | ||||
| 				bdata->irq, button->gpio); | ||||
| 			continue; | ||||
| 		} else { | ||||
| 			dev_dbg(&pdev->dev, "gpio:%d has irq:%d\n", | ||||
| 				button->gpio, bdata->irq); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int gpio_keys_polled_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct gpio_keys_platform_data *pdata; | ||||
| 	struct gpio_keys_button_dev *bdev; | ||||
| 	int ret; | ||||
|  | ||||
| 	ret = gpio_keys_button_probe(pdev, &bdev, 1); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll); | ||||
|  | ||||
| 	pdata = bdev->pdata; | ||||
| 	if (pdata->enable) | ||||
| 		pdata->enable(bdev->dev); | ||||
|  | ||||
| 	gpio_keys_polled_queue_work(bdev); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void gpio_keys_irq_close(struct gpio_keys_button_dev *bdev) | ||||
| { | ||||
| 	struct gpio_keys_platform_data *pdata = bdev->pdata; | ||||
| 	size_t i; | ||||
|  | ||||
| 	for (i = 0; i < pdata->nbuttons; i++) { | ||||
| 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | ||||
|  | ||||
| 		disable_irq(bdata->irq); | ||||
| 		cancel_delayed_work_sync(&bdata->work); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int gpio_keys_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct gpio_keys_button_dev *bdev = platform_get_drvdata(pdev); | ||||
|  | ||||
| 	platform_set_drvdata(pdev, NULL); | ||||
|  | ||||
| 	if (bdev->polled) | ||||
| 		gpio_keys_polled_close(bdev); | ||||
| 	else | ||||
| 		gpio_keys_irq_close(bdev); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct platform_driver gpio_keys_driver = { | ||||
| 	.probe	= gpio_keys_probe, | ||||
| 	.remove	= gpio_keys_remove, | ||||
| 	.driver	= { | ||||
| 		.name	= "gpio-keys", | ||||
| 		.of_match_table = of_match_ptr(gpio_keys_of_match), | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static struct platform_driver gpio_keys_polled_driver = { | ||||
| 	.probe	= gpio_keys_polled_probe, | ||||
| 	.remove	= gpio_keys_remove, | ||||
| 	.driver	= { | ||||
| 		.name	= "gpio-keys-polled", | ||||
| 		.of_match_table = of_match_ptr(gpio_keys_polled_of_match), | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static int __init gpio_button_init(void) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	ret = platform_driver_register(&gpio_keys_driver); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	ret = platform_driver_register(&gpio_keys_polled_driver); | ||||
| 	if (ret) | ||||
| 		platform_driver_unregister(&gpio_keys_driver); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void __exit gpio_button_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&gpio_keys_driver); | ||||
| 	platform_driver_unregister(&gpio_keys_polled_driver); | ||||
| } | ||||
|  | ||||
| module_init(gpio_button_init); | ||||
| module_exit(gpio_button_exit); | ||||
|  | ||||
| MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); | ||||
| MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>"); | ||||
| MODULE_DESCRIPTION("Polled GPIO Buttons hotplug driver"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| MODULE_ALIAS("platform:" DRV_NAME); | ||||
| @@ -1,42 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2014 OpenWrt.org | ||||
| # | ||||
| # This is free software, licensed under the GNU General Public License v2. | ||||
| # See /LICENSE for more information. | ||||
| # | ||||
|  | ||||
| include $(TOPDIR)/rules.mk | ||||
|  | ||||
| PKG_NAME:=linux-firmware | ||||
| PKG_VERSION:=20241110 | ||||
| PKG_RELEASE:=2 | ||||
|  | ||||
| PKG_SOURCE_URL:=@KERNEL/linux/kernel/firmware | ||||
| PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz | ||||
| PKG_HASH:=32e6d3eb5c7fcb69fe5d58976c6deafa0d6552719c6e74835064aff049d25bd7 | ||||
|  | ||||
| PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name> | ||||
|  | ||||
| SCAN_DEPS = *.mk | ||||
|  | ||||
| include $(INCLUDE_DIR)/package.mk | ||||
|  | ||||
| RSTRIP:=: | ||||
| STRIP:=: | ||||
|  | ||||
| define Package/firmware-default | ||||
|   SECTION:=firmware | ||||
|   CATEGORY:=Firmware | ||||
|   URL:=http://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git | ||||
|   TITLE:=$(1) | ||||
|   DEPENDS:=$(2) | ||||
|   LICENSE_FILES:=$(3) | ||||
|   LICENSE:=$(4) | ||||
| endef | ||||
|  | ||||
| define Build/Compile | ||||
|  | ||||
| endef | ||||
|  | ||||
| include $(wildcard ./*.mk) | ||||
| #$(eval $(call BuildPackage,linux-firmware)) | ||||
| @@ -1,17 +0,0 @@ | ||||
| Package/airoha-en8811h-firmware = $(call Package/firmware-default,Airoha EN8811H 2.5G Ethernet PHY firmware,,LICENSE.airoha) | ||||
| define Package/airoha-en8811h-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/airoha | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/airoha/EthMD32.dm.bin \ | ||||
| 		$(PKG_BUILD_DIR)/airoha/EthMD32.DSP.bin \ | ||||
| 		$(1)/lib/firmware/airoha | ||||
| ifneq ($(CONFIG_TARGET_mediatek_filogic),) | ||||
| 	$(INSTALL_DIR) $(STAGING_DIR_IMAGE) | ||||
| 	cat \ | ||||
| 		$(PKG_BUILD_DIR)/airoha/EthMD32.dm.bin \ | ||||
| 		$(PKG_BUILD_DIR)/airoha/EthMD32.DSP.bin \ | ||||
| 		> $(STAGING_DIR_IMAGE)/EthMD32.bin | ||||
| endif | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,airoha-en8811h-firmware)) | ||||
| @@ -1,29 +0,0 @@ | ||||
| Package/amd64-microcode = $(call Package/firmware-default,AMD64 CPU microcode,,LICENSE.amd-ucode) | ||||
| define Package/amd64-microcode/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/amd-ucode | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/amd-ucode/*.bin \ | ||||
| 		$(1)/lib/firmware/amd-ucode | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,amd64-microcode)) | ||||
|  | ||||
| Package/amdgpu-firmware = $(call Package/firmware-default,AMDGPU Video Driver firmware,,LICENSE.amdgpura) | ||||
| define Package/amdgpu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/amdgpu | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/amdgpu/*.bin \ | ||||
| 		$(1)/lib/firmware/amdgpu | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,amdgpu-firmware)) | ||||
|  | ||||
| Package/radeon-firmware = $(call Package/firmware-default,Radeon Video Driver firmware,,LICENSE.radeon) | ||||
| define Package/radeon-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/radeon | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/radeon/*.bin \ | ||||
| 		$(1)/lib/firmware/radeon | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,radeon-firmware)) | ||||
| @@ -1,214 +0,0 @@ | ||||
| Package/brcmfmac-firmware-4339-sdio = $(call Package/firmware-default,Broadcom 4339 FullMAC SDIO firmware,,LICENCE.cypressb) | ||||
| define Package/brcmfmac-firmware-4339-sdio/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/cypress | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/cypress/cyfmac4339-sdio.bin \ | ||||
| 		$(1)/lib/firmware/cypress/ | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(LN) \ | ||||
| 		../cypress/cyfmac4339-sdio.bin \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac4339-sdio.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-4339-sdio)) | ||||
|  | ||||
| Package/brcmfmac-firmware-43602a1-pcie = $(call Package/firmware-default,Broadcom 43602a1 FullMAC PCIe firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-firmware-43602a1-pcie/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43602-pcie.ap.bin \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43602-pcie.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-43602a1-pcie)) | ||||
|  | ||||
| Package/brcmfmac-firmware-4366b1-pcie = $(call Package/firmware-default,Broadcom 4366b1 FullMAC PCIe firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-firmware-4366b1-pcie/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac4366b-pcie.bin \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-4366b1-pcie)) | ||||
|  | ||||
| Package/brcmfmac-firmware-4366c0-pcie = $(call Package/firmware-default,Broadcom 4366c0 FullMAC PCIe firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-firmware-4366c0-pcie/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac4366c-pcie.bin \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-4366c0-pcie)) | ||||
|  | ||||
| Package/brcmfmac-firmware-4329-sdio = $(call Package/firmware-default,Broadcom BCM4329 FullMac SDIO firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-firmware-4329-sdio/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac4329-sdio.bin \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac4329-sdio.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-4329-sdio)) | ||||
|  | ||||
| Package/brcmfmac-nvram-43430-sdio = $(call Package/firmware-default,Broadcom BCM43430 SDIO NVRAM,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-nvram-43430-sdio/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.sinovoip,bpi-m2-plus.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.sinovoip,bpi-m2-zero.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.sinovoip,bpi-m2-ultra.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.sinovoip,bpi-m3.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.friendlyarm,nanopi-r1.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.starfive,visionfive-v1.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.beagle,beaglev-starlight-jh7100-a1.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.AP6212.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.beagle,beaglev-starlight-jh7100-r0.txt | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43430-sdio.Hampoo-D2D3_Vi8A1.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43430-sdio.MUR1DX.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.raspberrypi,3-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43430-sdio.raspberrypi,3-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-2-w.txt | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-nvram-43430-sdio)) | ||||
|  | ||||
| Package/brcmfmac-firmware-43430a0-sdio = $(call Package/firmware-default,Broadcom BCM43430a0 FullMac SDIO firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-firmware-43430a0-sdio/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43430a0-sdio.bin \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43430a0-sdio.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-43430a0-sdio)) | ||||
|  | ||||
| Package/brcmfmac-nvram-43455-sdio = $(call Package/firmware-default,Broadcom BCM43455 SDIO NVRAM,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-nvram-43455-sdio/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43455-sdio.acepc-t8.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43455-sdio.raspberrypi,3-model-b-plus.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,3-model-b-plus.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.raspberrypi,3-model-a-plus.txt | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.raspberrypi,4-compute-module.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry\ Pi\ Foundation-Raspberry\ Pi\ 4\ Model\ B.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.raspberrypi,5-model-b.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.raspberrypi,5-compute-module.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry\ Pi\ Foundation-Raspberry\ Pi\ Compute\ Module\ 4.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry\ Pi\ Foundation-Raspberry\ Pi\ 5\ Model\ B.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.raspberrypi,4-model-b.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry\ Pi\ Foundation-Raspberry\ Pi\ Compute\ Module\ 5.txt | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43455-sdio.MINIX-NEO\ Z83-4.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43455-sdio.AW-CM256SM.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.AW-CM256SM.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.beagle,am5729-beagleboneai.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.AW-CM256SM.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.pine64,pinebook-pro.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.AW-CM256SM.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.pine64,pinephone-pro.txt | ||||
| 	$(LN) \ | ||||
| 		brcmfmac43455-sdio.AW-CM256SM.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac43455-sdio.pine64,quartz64-b.txt | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-nvram-43455-sdio)) | ||||
|  | ||||
| Package/brcmfmac-nvram-4356-sdio = $(call Package/firmware-default,Broadcom BCM4356 SDIO NVRAM,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-nvram-4356-sdio/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac4356-sdio.AP6356S.txt \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(LN) \ | ||||
| 		brcmfmac4356-sdio.AP6356S.txt \ | ||||
| 		$(1)/lib/firmware/brcm/brcmfmac4356-sdio.friendlyarm,nanopc-t4.txt | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-nvram-4356-sdio)) | ||||
|  | ||||
| Package/brcmfmac-firmware-usb = $(call Package/firmware-default,Broadcom BCM43xx fullmac USB firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmfmac-firmware-usb/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43236b.bin \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/brcm/brcmfmac43143.bin \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmfmac-firmware-usb)) | ||||
|  | ||||
| Package/brcmsmac-firmware = $(call Package/firmware-default,Broadcom BCM43xx softmac PCIe firmware,,LICENCE.broadcom_bcm43xx) | ||||
| define Package/brcmsmac-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/brcm | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/$(PKG_LINUX_FIRMWARE_SUBDIR)/brcm/bcm43xx-0.fw \ | ||||
| 		$(PKG_BUILD_DIR)/$(PKG_LINUX_FIRMWARE_SUBDIR)/brcm/bcm43xx_hdr-0.fw \ | ||||
| 		$(1)/lib/firmware/brcm/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,brcmsmac-firmware)) | ||||
|  | ||||
| Package/bnx2-firmware = $(call Package/firmware-default,Broadcom BCM5706/5708/5709/5716 firmware) | ||||
| define Package/bnx2-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/bnx2 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/bnx2/* \ | ||||
| 		$(1)/lib/firmware/bnx2/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,bnx2-firmware)) | ||||
|  | ||||
| Package/bnx2x-firmware = $(call Package/firmware-default,=QLogic 5771x/578xx firmware) | ||||
| define Package/bnx2x-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/bnx2x | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/bnx2x/* \ | ||||
| 		$(1)/lib/firmware/bnx2x/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,bnx2x-firmware)) | ||||
| @@ -1,10 +0,0 @@ | ||||
| Package/aircard-pcmcia-firmware = $(call Package/firmware-default,Sierra Wireless Aircard 555/7xx/8x0 firmware) | ||||
| define Package/aircard-pcmcia-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/cis | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/cis/SW_555_SER.cis \ | ||||
| 		$(PKG_BUILD_DIR)/cis/SW_7xx_SER.cis \ | ||||
| 		$(PKG_BUILD_DIR)/cis/SW_8xx_SER.cis \ | ||||
| 		$(1)/lib/firmware/cis | ||||
| endef | ||||
| $(eval $(call BuildPackage,aircard-pcmcia-firmware)) | ||||
| @@ -1,12 +0,0 @@ | ||||
| Package/edgeport-firmware = $(call Package/firmware-default,USB Inside Out Edgeport Serial Driver firmware) | ||||
| define Package/edgeport-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/edgeport | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/edgeport/boot.fw \ | ||||
| 		$(PKG_BUILD_DIR)/edgeport/boot2.fw \ | ||||
| 		$(PKG_BUILD_DIR)/edgeport/down.fw \ | ||||
| 		$(PKG_BUILD_DIR)/edgeport/down2.fw \ | ||||
| 		$(1)/lib/firmware/edgeport | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,edgeport-firmware)) | ||||
| @@ -1,272 +0,0 @@ | ||||
| Package/ibt-firmware = $(call Package/firmware-default,Intel bluetooth firmware) | ||||
| define Package/ibt-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/intel | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/intel/*.bseq \ | ||||
| 		$(PKG_BUILD_DIR)/intel/ibt*.sfi \ | ||||
| 		$(PKG_BUILD_DIR)/intel/ibt*.ddc \ | ||||
| 		$(1)/lib/firmware/intel | ||||
| endef | ||||
| $(eval $(call BuildPackage,ibt-firmware)) | ||||
|  | ||||
| Package/iwl3945-firmware = $(call Package/firmware-default,Intel IWL3945 firmware) | ||||
| define Package/iwl3945-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-3945-2.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwl3945-firmware)) | ||||
|  | ||||
| Package/iwl4965-firmware = $(call Package/firmware-default,Intel IWL4965 firmware) | ||||
| define Package/iwl4965-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-4965-2.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwl4965-firmware)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl100 = $(call Package/firmware-default,Intel Centrino Wireless-N 100 firmware) | ||||
| define Package/iwlwifi-firmware-iwl100/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-100-5.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl100)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl1000 = $(call Package/firmware-default,Intel Centrino Wireless-N 1000 firmware) | ||||
| define Package/iwlwifi-firmware-iwl1000/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-1000-5.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl1000)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl105 = $(call Package/firmware-default,Intel Centrino Wireless-N 105 firmware) | ||||
| define Package/iwlwifi-firmware-iwl105/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-105-6.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl105)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl135 = $(call Package/firmware-default,Intel Centrino Wireless-N 135 firmware) | ||||
| define Package/iwlwifi-firmware-iwl135/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-135-6.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl135)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl2000 = $(call Package/firmware-default,Intel Centrino Wireless-N 2200 firmware) | ||||
| define Package/iwlwifi-firmware-iwl2000/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-2000-6.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl2000)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl2030 = $(call Package/firmware-default,Intel Centrino Wireless-N 2230 firmware) | ||||
| define Package/iwlwifi-firmware-iwl2030/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-2030-6.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl2030)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl3160 = $(call Package/firmware-default,Intel Wireless 3160 firmware) | ||||
| define Package/iwlwifi-firmware-iwl3160/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-3160-17.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl3160)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl3168 = $(call Package/firmware-default,Intel Wireless 3168 firmware) | ||||
| define Package/iwlwifi-firmware-iwl3168/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-3168-29.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl3168)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl5000 = $(call Package/firmware-default,Intel Wireless 5100AGN 5300AGN and 5350AGN firmware) | ||||
| define Package/iwlwifi-firmware-iwl5000/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-5000-5.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl5000)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl5150 = $(call Package/firmware-default,Intel Wireless Wi-Fi 5150AGN firmware) | ||||
| define Package/iwlwifi-firmware-iwl5150/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-5150-2.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl5150)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl6000g2 = $(call Package/firmware-default,Intel Centrino 6300 and 6200 firmware) | ||||
| define Package/iwlwifi-firmware-iwl6000g2/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-6000-4.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl6000g2)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl6000g2a = $(call Package/firmware-default,Intel Centrino 6205 firmware) | ||||
| define Package/iwlwifi-firmware-iwl6000g2a/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-6000g2a-6.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl6000g2a)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl6000g2b = $(call Package/firmware-default,Intel Centrino 6230 1030 130 and 6235 firmware) | ||||
| define Package/iwlwifi-firmware-iwl6000g2b/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-6000g2b-6.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl6000g2b)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl6050 = $(call Package/firmware-default,Intel Centrino 6150 and 6250 firmware) | ||||
| define Package/iwlwifi-firmware-iwl6050/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-6050-5.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl6050)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl7260 = $(call Package/firmware-default,Intel Wireless 7260 firmware) | ||||
| define Package/iwlwifi-firmware-iwl7260/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-7260-17.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl7260)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl7265 = $(call Package/firmware-default,Intel Wireless 7265 firmware) | ||||
| define Package/iwlwifi-firmware-iwl7265/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-7265-17.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl7265)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl7265d = $(call Package/firmware-default,Intel Wireless 7265D and 3165 firmware) | ||||
| define Package/iwlwifi-firmware-iwl7265d/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-7265D-29.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl7265d)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl8260c = $(call Package/firmware-default,Intel Wireless 8260 and 4165 firmware) | ||||
| define Package/iwlwifi-firmware-iwl8260c/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-8000C-36.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl8260c)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl8265 = $(call Package/firmware-default,Intel Wireless 8265 firmware) | ||||
| define Package/iwlwifi-firmware-iwl8265/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-8265-36.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl8265)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl9000 = $(call Package/firmware-default,Intel Wireless 9000 firmware) | ||||
| define Package/iwlwifi-firmware-iwl9000/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-9000-pu-b0-jf-b0-46.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl9000)) | ||||
|  | ||||
| Package/iwlwifi-firmware-iwl9260 = $(call Package/firmware-default,Intel Wireless 9260 firmware) | ||||
| define Package/iwlwifi-firmware-iwl9260/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-9260-th-b0-jf-b0-46.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-iwl9260)) | ||||
|  | ||||
| Package/iwlwifi-firmware-ax101 = $(call Package/firmware-default,Intel AX101 firmware) | ||||
| define Package/iwlwifi-firmware-ax101/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-so-a0-hr-b0-89.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-ax101)) | ||||
|  | ||||
| Package/iwlwifi-firmware-ax200 = $(call Package/firmware-default,Intel AX200 firmware) | ||||
| define Package/iwlwifi-firmware-ax200/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-cc-a0-77.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-ax200)) | ||||
|  | ||||
| Package/iwlwifi-firmware-ax201 = $(call Package/firmware-default,Intel AX201 firmware) | ||||
| define Package/iwlwifi-firmware-ax201/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-QuZ-a0-hr-b0-77.ucode $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-ax201)) | ||||
|  | ||||
| Package/iwlwifi-firmware-ax210 = $(call Package/firmware-default,Intel AX210 firmware) | ||||
| define Package/iwlwifi-firmware-ax210/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-ty-a0-gf-a0-89.ucode $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-ty-a0-gf-a0.pnvm $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-ax210)) | ||||
|  | ||||
| Package/iwlwifi-firmware-ax411 = $(call Package/firmware-default,Intel AX411 firmware) | ||||
| define Package/iwlwifi-firmware-ax411/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-so-a0-gf4-a0-89.ucode $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-so-a0-gf4-a0.pnvm $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-ax411)) | ||||
|  | ||||
| Package/iwlwifi-firmware-be200 = $(call Package/firmware-default,Intel BE200 firmware) | ||||
| define Package/iwlwifi-firmware-be200/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-gl-c0-fm-c0-92.ucode $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/iwlwifi-gl-c0-fm-c0.pnvm $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,iwlwifi-firmware-be200)) | ||||
|  | ||||
| Package/e100-firmware = $(call Package/firmware-default,Intel e100) | ||||
| define Package/e100-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/e100 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/e100/d101m_ucode.bin $(1)/lib/firmware/e100/ | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/e100/d101s_ucode.bin $(1)/lib/firmware/e100/ | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/e100/d102e_ucode.bin $(1)/lib/firmware/e100/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,e100-firmware)) | ||||
|  | ||||
| i915_deps:=+i915-firmware-dmc +i915-firmware-guc +i915-firmware-huc +i915-firmware-gsc | ||||
| Package/i915-firmware = $(call Package/firmware-default,Intel I915 firmware \(meta package\),$(i915_deps),LICENSE.i915) | ||||
| define Package/i915-firmware/install | ||||
| 	true | ||||
| endef | ||||
| $(eval $(call BuildPackage,i915-firmware)) | ||||
|  | ||||
| Package/i915-firmware-dmc = $(call Package/firmware-default,Intel I915 DMC firmware,,LICENSE.i915) | ||||
| define Package/i915-firmware-dmc/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/i915 | ||||
| 	for f in $(PKG_BUILD_DIR)/i915/*_dmc*.bin; do                        \ | ||||
| 	  t=`echo $$$${f##*/} | cut -d_ -f2 | cut -d. -f1`;                  \ | ||||
| 	  if [ "$$$$t" = dmc ]; then $(CP) $$$$f $(1)/lib/firmware/i915/; fi \ | ||||
| 	done | ||||
| endef | ||||
| $(eval $(call BuildPackage,i915-firmware-dmc)) | ||||
|  | ||||
| Package/i915-firmware-guc = $(call Package/firmware-default,Intel I915 GUC firmware,,LICENSE.i915) | ||||
| define Package/i915-firmware-guc/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/i915 | ||||
| 	for f in $(PKG_BUILD_DIR)/i915/*_guc*.bin; do                        \ | ||||
| 	  t=`echo $$$${f##*/} | cut -d_ -f2 | cut -d. -f1`;                  \ | ||||
| 	  if [ "$$$$t" = guc ]; then $(CP) $$$$f $(1)/lib/firmware/i915/; fi \ | ||||
| 	done | ||||
| endef | ||||
| $(eval $(call BuildPackage,i915-firmware-guc)) | ||||
|  | ||||
| Package/i915-firmware-huc = $(call Package/firmware-default,Intel I915 HUC firmware,,LICENSE.i915) | ||||
| define Package/i915-firmware-huc/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/i915 | ||||
| 	for f in $(PKG_BUILD_DIR)/i915/*_huc*.bin; do                        \ | ||||
| 	  t=`echo $$$${f##*/} | cut -d_ -f2 | cut -d. -f1`;                  \ | ||||
| 	  if [ "$$$$t" = huc ]; then $(CP) $$$$f $(1)/lib/firmware/i915/; fi \ | ||||
| 	done | ||||
| endef | ||||
| $(eval $(call BuildPackage,i915-firmware-huc)) | ||||
|  | ||||
| Package/i915-firmware-gsc = $(call Package/firmware-default,Intel I915 GSC firmware,,LICENSE.i915) | ||||
| define Package/i915-firmware-gsc/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/i915 | ||||
| 	for f in $(PKG_BUILD_DIR)/i915/*_gsc*.bin; do                        \ | ||||
| 	  t=`echo $$$${f##*/} | cut -d_ -f2 | cut -d. -f1`;                  \ | ||||
| 	  if [ "$$$$t" = gsc ]; then $(CP) $$$$f $(1)/lib/firmware/i915/; fi \ | ||||
| 	done | ||||
| endef | ||||
| $(eval $(call BuildPackage,i915-firmware-gsc)) | ||||
| @@ -1,72 +0,0 @@ | ||||
| Package/mwl8k-firmware = $(call Package/firmware-default,Marvell 8366/8687 firmware,,LICENCE.Marvell) | ||||
| define Package/mwl8k-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mwl8k | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mwl8k/fmimage_8366_ap-3.fw \ | ||||
| 		$(PKG_BUILD_DIR)/mwl8k/fmimage_8366.fw \ | ||||
| 		$(PKG_BUILD_DIR)/mwl8k/helper_8366.fw \ | ||||
| 		$(PKG_BUILD_DIR)/mwl8k/fmimage_8687.fw \ | ||||
| 		$(PKG_BUILD_DIR)/mwl8k/helper_8687.fw \ | ||||
| 		$(1)/lib/firmware/mwl8k/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,mwl8k-firmware)) | ||||
|  | ||||
| Package/mwifiex-pcie-firmware = $(call Package/firmware-default,Marvell 8897 firmware,,LICENCE.Marvell) | ||||
| define Package/mwifiex-pcie-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mrvl | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mrvl/pcie8897_uapsta.bin \ | ||||
| 		$(1)/lib/firmware/mrvl/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,mwifiex-pcie-firmware)) | ||||
|  | ||||
| Package/mwifiex-sdio-firmware = $(call Package/firmware-default,Marvell 8887/8997 firmware,,LICENCE.Marvell) | ||||
| define Package/mwifiex-sdio-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mrvl | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mrvl/sd8887_uapsta.bin \ | ||||
| 		$(PKG_BUILD_DIR)/mrvl/sdsd8997_combo_v4.bin \ | ||||
| 		$(1)/lib/firmware/mrvl/ | ||||
| 	ln -s ../mrvl/sdsd8997_combo_v4.bin $(1)/lib/firmware/mrvl/sd8997_uapsta.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,mwifiex-sdio-firmware)) | ||||
|  | ||||
| Package/libertas-usb-firmware = $(call Package/firmware-default,Marvell 8388/8682 USB firmware,,LICENCE.Marvell) | ||||
| define Package/libertas-usb-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/libertas | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/usb8388_v9.bin \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/usb8682.bin \ | ||||
| 		$(1)/lib/firmware/libertas/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,libertas-usb-firmware)) | ||||
|  | ||||
| Package/libertas-sdio-firmware = $(call Package/firmware-default,Marvell 8385/8686/8688 SDIO firmware,,LICENCE.Marvell) | ||||
| define Package/libertas-sdio-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/libertas | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/sd8385_helper.bin \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/sd8385.bin \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/sd8686_v9_helper.bin \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/sd8686_v9.bin \ | ||||
| 		$(1)/lib/firmware/libertas | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mrvl | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mrvl/sd8688_helper.bin \ | ||||
| 		$(PKG_BUILD_DIR)/mrvl/sd8688.bin \ | ||||
| 		$(1)/lib/firmware/mrvl | ||||
| 	ln -s ../mrvl/sd8688_helper.bin $(1)/lib/firmware/libertas/sd8688_helper.bin | ||||
| 	ln -s ../mrvl/sd8688.bin $(1)/lib/firmware/libertas/sd8688.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,libertas-sdio-firmware)) | ||||
|  | ||||
| Package/libertas-spi-firmware = $(call Package/firmware-default,Marvell 8686 SPI firmware,,LICENCE.Marvell) | ||||
| define Package/libertas-spi-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/libertas | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/gspi8686_v9_helper.bin \ | ||||
| 		$(PKG_BUILD_DIR)/libertas/gspi8686_v9.bin \ | ||||
| 		$(1)/lib/firmware/libertas | ||||
| endef | ||||
| $(eval $(call BuildPackage,libertas-spi-firmware)) | ||||
|  | ||||
| @@ -1,108 +0,0 @@ | ||||
| Package/mt7601u-firmware = $(call Package/firmware-default,MediaTek MT7601U firmware,,LICENCE.mediatek) | ||||
| define Package/mt7601u-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7601u.bin \ | ||||
| 		$(1)/lib/firmware/mediatek | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7601u-firmware)) | ||||
|  | ||||
| Package/rt2800-pci-firmware = $(call Package/firmware-default,Ralink RT28xx/3xxx PCI/SoC firmware) | ||||
| define Package/rt2800-pci-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/rt2860.bin \ | ||||
| 		$(PKG_BUILD_DIR)/rt3290.bin \ | ||||
| 		$(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,rt2800-pci-firmware)) | ||||
|  | ||||
| Package/rt2800-usb-firmware = $(call Package/firmware-default,Ralink RT28xx/3xxx USB firmware) | ||||
| define Package/rt2800-usb-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rt2870.bin $(1)/lib/firmware/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,rt2800-usb-firmware)) | ||||
|  | ||||
| Package/rt61-pci-firmware = $(call Package/firmware-default,Ralink RT2561 firmware) | ||||
| define Package/rt61-pci-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/rt2561.bin \ | ||||
| 		$(PKG_BUILD_DIR)/rt2561s.bin \ | ||||
| 		$(PKG_BUILD_DIR)/rt2661.bin \ | ||||
| 		$(1)/lib/firmware/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,rt61-pci-firmware)) | ||||
|  | ||||
| Package/rt73-usb-firmware = $(call Package/firmware-default,Ralink RT2573 firmware) | ||||
| define Package/rt73-usb-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rt73.bin $(1)/lib/firmware/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,rt73-usb-firmware)) | ||||
|  | ||||
| Package/mt7622bt-firmware = $(call Package/firmware-default,mt7622bt firmware,,LICENCE.mediatek) | ||||
| define Package/mt7622bt-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7622pr2h.bin \ | ||||
| 		$(1)/lib/firmware/mediatek | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7622bt-firmware)) | ||||
|  | ||||
| Package/mt7921bt-firmware = $(call Package/firmware-default,mt7921bt firmware,,LICENCE.mediatek) | ||||
| define Package/mt7921bt-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin \ | ||||
| 		$(1)/lib/firmware/mediatek | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7921bt-firmware)) | ||||
|  | ||||
| Package/mt7922bt-firmware = $(call Package/firmware-default,mt7922bt firmware,,LICENCE.mediatek) | ||||
| define Package/mt7922bt-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin \ | ||||
| 		$(1)/lib/firmware/mediatek | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7922bt-firmware)) | ||||
|  | ||||
| Package/mt7981-wo-firmware = $(call Package/firmware-default,MT7981 offload firmware,,LICENCE.mediatek) | ||||
| define Package/mt7981-wo-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7981_wo.bin \ | ||||
| 		$(1)/lib/firmware/mediatek | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7981-wo-firmware)) | ||||
|  | ||||
| Package/mt7986-wo-firmware = $(call Package/firmware-default,MT7986 offload firmware,,LICENCE.mediatek) | ||||
| define Package/mt7986-wo-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7986_wo_0.bin \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7986_wo_1.bin \ | ||||
| 		$(1)/lib/firmware/mediatek | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7986-wo-firmware)) | ||||
|  | ||||
| Package/mt7988-2p5g-phy-firmware = $(call Package/firmware-default,MT7988 built-in 2.5G Ethernet PHY firmware,,LICENCE.mediatek) | ||||
| define Package/mt7988-2p5g-phy-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek/mt7988 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7988/i2p5ge-phy-pmb.bin \ | ||||
| 		$(1)/lib/firmware/mediatek/mt7988 | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7988-2p5g-phy-firmware)) | ||||
|  | ||||
| Package/mt7988-wo-firmware = $(call Package/firmware-default,MT7988 offload firmware,,LICENCE.mediatek) | ||||
| define Package/mt7988-wo-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mediatek/mt7988 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7988/mt7988_wo_0.bin \ | ||||
| 		$(PKG_BUILD_DIR)/mediatek/mt7988/mt7988_wo_1.bin \ | ||||
| 		$(1)/lib/firmware/mediatek/mt7988 | ||||
| endef | ||||
| $(eval $(call BuildPackage,mt7988-wo-firmware)) | ||||
| @@ -1,39 +0,0 @@ | ||||
| Package/mlxsw_spectrum-firmware = $(call Package/firmware-default,Mellanox Spectrum firmware) | ||||
| define Package/mlxsw_spectrum-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mellanox | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/mellanox/mlxsw_spectrum-*.mfa2 \ | ||||
| 		$(1)/lib/firmware/mellanox | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,mlxsw_spectrum-firmware)) | ||||
|  | ||||
| Package/mlxsw_spectrum2-firmware = $(call Package/firmware-default,Mellanox Spectrum-2 firmware) | ||||
| define Package/mlxsw_spectrum2-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mellanox | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/mellanox/mlxsw_spectrum2-*.mfa2 \ | ||||
| 		$(1)/lib/firmware/mellanox | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,mlxsw_spectrum2-firmware)) | ||||
|  | ||||
| Package/mlxsw_spectrum3-firmware = $(call Package/firmware-default,Mellanox Spectrum-3 firmware) | ||||
| define Package/mlxsw_spectrum3-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mellanox | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/mellanox/mlxsw_spectrum3-*.mfa2 \ | ||||
| 		$(1)/lib/firmware/mellanox | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,mlxsw_spectrum3-firmware)) | ||||
|  | ||||
| Package/mlxsw_spectrum4-firmware = $(call Package/firmware-default,Mellanox Spectrum-4 firmware) | ||||
| define Package/mlxsw_spectrum4-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/mellanox | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/mellanox/mlxsw_spectrum4-*.mfa2 \ | ||||
| 		$(1)/lib/firmware/mellanox | ||||
| endef | ||||
|  | ||||
| $(eval $(call BuildPackage,mlxsw_spectrum4-firmware)) | ||||
| @@ -1,9 +0,0 @@ | ||||
| Package/eip197-mini-firmware = $(call Package/firmware-default,Inside Secure EIP197 mini firmware) | ||||
| define Package/eip197-mini-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/inside-secure/eip197_minifw | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/inside-secure/eip197_minifw/ifpp.bin \ | ||||
| 		$(PKG_BUILD_DIR)/inside-secure/eip197_minifw/ipue.bin \ | ||||
| 		$(1)/lib/firmware/inside-secure/eip197_minifw | ||||
| endef | ||||
| $(eval $(call BuildPackage,eip197-mini-firmware)) | ||||
| @@ -1,47 +0,0 @@ | ||||
| Package/ar3k-firmware = $(call Package/firmware-default,ath3k firmware,,LICENSE.QualcommAtheros_ar3k) | ||||
| define Package/ar3k-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ar3k | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/ar3k/*.dfu \ | ||||
| 		$(1)/lib/firmware/ar3k | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/qca | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/qca/*.bin \ | ||||
| 		$(1)/lib/firmware/qca | ||||
| endef | ||||
| $(eval $(call BuildPackage,ar3k-firmware)) | ||||
|  | ||||
|  | ||||
| Package/ath6k-firmware = $(call Package/firmware-default,AR600X firmware) | ||||
| define Package/ath6k-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath6k | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/ath6k/* \ | ||||
| 		$(1)/lib/firmware/ath6k | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath6k-firmware)) | ||||
|  | ||||
| Package/ath9k-htc-firmware = $(call Package/firmware-default,AR9271/AR7010 firmware,,LICENCE.open-ath9k-htc-firmware) | ||||
| define Package/ath9k-htc-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath9k_htc | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath9k_htc/htc_9271-1.4.0.fw \ | ||||
| 		$(PKG_BUILD_DIR)/ath9k_htc/htc_7010-1.4.0.fw \ | ||||
| 		$(1)/lib/firmware/ath9k_htc | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath9k-htc-firmware)) | ||||
|  | ||||
| Package/carl9170-firmware = $(call Package/firmware-default,AR9170 firmware,,carl9170fw/GPL) | ||||
| define Package/carl9170-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/carl9170-1.fw $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,carl9170-firmware)) | ||||
|  | ||||
| Package/wil6210-firmware = $(call Package/firmware-default,wil6210 firmware) | ||||
| define Package/wil6210-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/wil6210.fw $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/wil6210.brd $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,wil6210-firmware)) | ||||
| @@ -1,138 +0,0 @@ | ||||
| Package/ath10k-board-qca4019 = $(call Package/firmware-default,ath10k qca4019 board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca4019/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA4019/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA4019/hw1.0/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA4019/hw1.0/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca4019)) | ||||
| Package/ath10k-firmware-qca4019 = $(call Package/firmware-default,ath10k qca4019 firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca4019/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA4019/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA4019/hw1.0/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA4019/hw1.0/firmware-5.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca4019)) | ||||
|  | ||||
| Package/ath10k-board-qca9377 = $(call Package/firmware-default,ath10k qca9377 board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca9377/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9377/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9377/hw1.0/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9377/hw1.0/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca9377)) | ||||
| Package/ath10k-firmware-qca9377 = $(call Package/firmware-default,ath10k qca9377 firmware,+ath10k-board-qca9377,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca9377/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9377/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9377/hw1.0/firmware-6.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9377/hw1.0/firmware-6.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca9377)) | ||||
|  | ||||
| Package/ath10k-board-qca9887 = $(call Package/firmware-default,ath10k qca9887 board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca9887/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9887/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9887/hw1.0/board.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9887/hw1.0/board.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca9887)) | ||||
| Package/ath10k-firmware-qca9887 = $(call Package/firmware-default,ath10k qca9887 firmware,+ath10k-board-qca9887,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca9887/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9887/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9887/hw1.0/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9887/hw1.0/firmware-5.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca9887)) | ||||
|  | ||||
| Package/ath10k-board-qca9888 = $(call Package/firmware-default,ath10k qca9888 board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca9888/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9888/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9888/hw2.0/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9888/hw2.0/board-2.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca9888)) | ||||
| Package/ath10k-firmware-qca9888 = $(call Package/firmware-default,ath10k qca9888 firmware,+ath10k-board-qca9888,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca9888/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9888/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9888/hw2.0/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9888/hw2.0/firmware-5.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca9888)) | ||||
|  | ||||
| Package/ath10k-board-qca988x = $(call Package/firmware-default,ath10k qca988x board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca988x/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA988X/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA988X/hw2.0/board.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA988X/hw2.0/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca988x)) | ||||
| Package/ath10k-firmware-qca988x = $(call Package/firmware-default,ath10k qca988x firmware,+ath10k-board-qca988x,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca988x/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA988X/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA988X/hw2.0/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA988X/hw2.0/firmware-5.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca988x)) | ||||
|  | ||||
| Package/ath10k-firmware-qca6174 = $(call Package/firmware-default,ath10k qca6174 firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca6174/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA6174/hw2.1 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA6174/hw2.1/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA6174/hw2.1/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA6174/hw2.1/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA6174/hw2.1/firmware-5.bin | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA6174/hw3.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA6174/hw3.0/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA6174/hw3.0/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA6174/hw3.0/firmware-6.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA6174/hw3.0/firmware-6.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca6174)) | ||||
|  | ||||
| Package/ath10k-board-qca99x0 = $(call Package/firmware-default,ath10k qca99x0 board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca99x0/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA99X0/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA99X0/hw2.0/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA99X0/hw2.0/board-2.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca99x0)) | ||||
|  | ||||
| Package/ath10k-firmware-qca99x0 = $(call Package/firmware-default,ath10k qca99x0 firmware,+ath10k-board-qca99x0,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca99x0/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA99X0/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA99X0/hw2.0/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA99X0/hw2.0/firmware-5.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca99x0)) | ||||
|  | ||||
| Package/ath10k-board-qca9984 = $(call Package/firmware-default,ath10k qca9984 board firmware,,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-board-qca9984/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9984/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9984/hw1.0/board-2.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9984/hw1.0/board-2.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-board-qca9984)) | ||||
| Package/ath10k-firmware-qca9984 = $(call Package/firmware-default,ath10k qca9984 firmware,+ath10k-board-qca9984,LICENSE.QualcommAtheros_ath10k) | ||||
| define Package/ath10k-firmware-qca9984/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath10k/QCA9984/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath10k/QCA9984/hw1.0/firmware-5.bin \ | ||||
| 		$(1)/lib/firmware/ath10k/QCA9984/hw1.0/firmware-5.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath10k-firmware-qca9984)) | ||||
| @@ -1,28 +0,0 @@ | ||||
| Package/ath11k-firmware-qca6390 = $(call Package/firmware-default,QCA6390 ath11k firmware,,LICENCE.atheros_firmware) | ||||
| define Package/ath11k-firmware-qca6390/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCA6390/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath11k/QCA6390/hw2.0/* $(1)/lib/firmware/ath11k/QCA6390/hw2.0/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath11k-firmware-qca6390)) | ||||
|  | ||||
| Package/ath11k-firmware-wcn6750 = $(call Package/firmware-default,WCN6750 ath11k firmware,,LICENCE.atheros_firmware) | ||||
| define Package/ath11k-firmware-wcn6750/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath11k/WCN6750/hw1.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath11k/WCN6750/hw1.0/board-2.bin $(1)/lib/firmware/ath11k/WCN6750/hw1.0/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath11k/WCN6750/hw1.0/Notice.txt $(1)/lib/firmware/ath11k/WCN6750/hw1.0/ | ||||
| 	$(INSTALL_DATA) \ | ||||
| 	        $(PKG_BUILD_DIR)/ath11k/WCN6750/hw1.0/sc7280/wpss.mbn $(1)/lib/firmware/ath11k/WCN6750/hw1.0/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath11k-firmware-wcn6750)) | ||||
|  | ||||
| Package/ath11k-firmware-wcn6855 = $(call Package/firmware-default,WCN6855 ath11k firmware,,LICENCE.atheros_firmware) | ||||
| define Package/ath11k-firmware-wcn6855/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath11k/WCN6855/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath11k/WCN6855/hw2.0/* $(1)/lib/firmware/ath11k/WCN6855/hw2.0/ | ||||
| 	$(LN) ./hw2.0 $(1)/lib/firmware/ath11k/WCN6855/hw2.1 | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath11k-firmware-wcn6855)) | ||||
| @@ -1,7 +0,0 @@ | ||||
| Package/ath12k-firmware-wcn7850 = $(call Package/firmware-default,WCN7850 ath12k firmware) | ||||
| define Package/ath12k-firmware-wcn7850/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ath12k/WCN7850/hw2.0 | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ath12k/WCN7850/hw2.0/* $(1)/lib/firmware/ath12k/WCN7850/hw2.0/ | ||||
| endef | ||||
| $(eval $(call BuildPackage,ath12k-firmware-wcn7850)) | ||||
| @@ -1,220 +0,0 @@ | ||||
| Package/r8152-firmware = $(call Package/firmware-default,RealTek RTL8152 firmware) | ||||
| define Package/r8152-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtl_nic | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/rtl_nic/rtl8153* \ | ||||
| 		$(PKG_BUILD_DIR)/rtl_nic/rtl8156* \ | ||||
| 		$(1)/lib/firmware/rtl_nic | ||||
| endef | ||||
| $(eval $(call BuildPackage,r8152-firmware)) | ||||
|  | ||||
| Package/r8169-firmware = $(call Package/firmware-default,RealTek RTL8169 firmware) | ||||
| define Package/r8169-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtl_nic | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/rtl_nic/rtl810* \ | ||||
| 		$(PKG_BUILD_DIR)/rtl_nic/rtl812* \ | ||||
| 		$(PKG_BUILD_DIR)/rtl_nic/rtl8168* \ | ||||
| 		$(PKG_BUILD_DIR)/rtl_nic/rtl84* \ | ||||
| 		$(1)/lib/firmware/rtl_nic | ||||
| endef | ||||
| $(eval $(call BuildPackage,r8169-firmware)) | ||||
|  | ||||
| Package/rtl8188eu-firmware = $(call Package/firmware-default,RealTek RTL8188EU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8188eu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/rtlwifi/rtl8188eufw.bin \ | ||||
| 		$(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8188eu-firmware)) | ||||
|  | ||||
| Package/rtl8188fu-firmware = $(call Package/firmware-default,RealTek RTL8188FU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8188fu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(CP) \ | ||||
| 		$(PKG_BUILD_DIR)/rtlwifi/rtl8188fufw.bin \ | ||||
| 		$(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8188fu-firmware)) | ||||
|  | ||||
| Package/rtl8192ce-firmware = $(call Package/firmware-default,RealTek RTL8192CE firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8192ce-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cfw.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cfwU.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cfwU_B.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8192ce-firmware)) | ||||
|  | ||||
| Package/rtl8192cu-firmware = $(call Package/firmware-default,RealTek RTL8192CU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8192cu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cufw.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cufw_A.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cufw_B.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192cufw_TMSC.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8192cu-firmware)) | ||||
|  | ||||
| Package/rtl8192de-firmware = $(call Package/firmware-default,RealTek RTL8192DE firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8192de-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192defw.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8192de-firmware)) | ||||
|  | ||||
| Package/rtl8192du-firmware = $(call Package/firmware-default,RealTek RTL8192DU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8192du-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192dufw.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8192du-firmware)) | ||||
|  | ||||
| Package/rtl8192eu-firmware = $(call Package/firmware-default,RealTek RTL8192EU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8192eu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192eu_nic.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8192eu-firmware)) | ||||
|  | ||||
| Package/rtl8192se-firmware = $(call Package/firmware-default,RealTek RTL8192SE firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8192se-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8192sefw.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8192se-firmware)) | ||||
|  | ||||
| Package/rtl8723au-firmware = $(call Package/firmware-default,RealTek RTL8723AU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8723au-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8723aufw_A.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8723aufw_B.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8723aufw_B_NoBT.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8723au-firmware)) | ||||
|  | ||||
| Package/rtl8723be-firmware = $(call Package/firmware-default,RealTek RTL8723BE firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8723be-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8723befw_36.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8723befw.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8723be-firmware)) | ||||
|  | ||||
| Package/rtl8723bu-firmware = $(call Package/firmware-default,RealTek RTL8723BU firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8723bu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8723bu_nic.bin $(1)/lib/firmware/rtlwifi | ||||
| 	ln -s rtl8723bu_nic.bin $(1)/lib/firmware/rtlwifi/rtl8723bs_nic.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8723bu-firmware)) | ||||
|  | ||||
| Package/rtl8723de-firmware = $(call Package/firmware-default,RealTek RTL8723DE firmware) | ||||
| define Package/rtl8723de-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8723d_fw.bin $(1)/lib/firmware/rtw88 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8723de-firmware)) | ||||
|  | ||||
| Package/rtl8761a-firmware = $(call Package/firmware-default,RealTek RTL8761A firmware) | ||||
| define Package/rtl8761a-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtl_bt | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtl_bt/rtl8761a_fw.bin $(1)/lib/firmware/rtl_bt | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8761a-firmware)) | ||||
|  | ||||
| Package/rtl8761b-firmware = $(call Package/firmware-default,RealTek RTL8761B firmware) | ||||
| define Package/rtl8761b-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtl_bt | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtl_bt/rtl8761b_config.bin $(1)/lib/firmware/rtl_bt | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtl_bt/rtl8761b_fw.bin $(1)/lib/firmware/rtl_bt | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8761b-firmware)) | ||||
|  | ||||
| Package/rtl8761bu-firmware = $(call Package/firmware-default,RealTek RTL8761BU firmware) | ||||
| define Package/rtl8761bu-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtl_bt | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtl_bt/rtl8761bu_config.bin $(1)/lib/firmware/rtl_bt | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtl_bt/rtl8761bu_fw.bin $(1)/lib/firmware/rtl_bt | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8761bu-firmware)) | ||||
|  | ||||
| Package/rtl8812a-firmware = $(call Package/firmware-default,RealTek RTL8812AU firmware) | ||||
| define Package/rtl8812a-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8812a_fw.bin $(1)/lib/firmware/rtw88 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8812a-firmware)) | ||||
|  | ||||
| Package/rtl8821a-firmware = $(call Package/firmware-default,RealTek RTL8821AU firmware) | ||||
| define Package/rtl8821a-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8821a_fw.bin $(1)/lib/firmware/rtw88 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8821a-firmware)) | ||||
|  | ||||
| Package/rtl8821ae-firmware = $(call Package/firmware-default,RealTek RTL8821AE firmware,,LICENCE.rtlwifi_firmware.txt) | ||||
| define Package/rtl8821ae-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8821aefw_29.bin $(1)/lib/firmware/rtlwifi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtlwifi/rtl8821aefw_wowlan.bin $(1)/lib/firmware/rtlwifi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8821ae-firmware)) | ||||
|  | ||||
| Package/rtl8821ce-firmware = $(call Package/firmware-default,RealTek RTL8821CE firmware) | ||||
| define Package/rtl8821ce-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8821c_fw.bin $(1)/lib/firmware/rtw88 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8821ce-firmware)) | ||||
|  | ||||
| Package/rtl8822be-firmware = $(call Package/firmware-default,RealTek RTL8822BE firmware) | ||||
| define Package/rtl8822be-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8822b_fw.bin $(1)/lib/firmware/rtw88 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8822be-firmware)) | ||||
|  | ||||
| Package/rtl8822ce-firmware = $(call Package/firmware-default,RealTek RTL8822CE firmware) | ||||
| define Package/rtl8822ce-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8822c_fw.bin $(1)/lib/firmware/rtw88 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw88/rtw8822c_wow_fw.bin $(1)/lib/firmware/rtw88 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8822ce-firmware)) | ||||
|  | ||||
| Package/rtl8851be-firmware = $(call Package/firmware-default,RealTek RTL8851BE firmware) | ||||
| define Package/rtl8851be-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw89 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw89/rtw8851b_fw.bin $(1)/lib/firmware/rtw89 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8851be-firmware)) | ||||
|  | ||||
| Package/rtl8852ae-firmware = $(call Package/firmware-default,RealTek RTL8852AE firmware) | ||||
| define Package/rtl8852ae-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw89 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw89/rtw8852a_fw.bin $(1)/lib/firmware/rtw89 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8852ae-firmware)) | ||||
|  | ||||
| Package/rtl8852be-firmware = $(call Package/firmware-default,RealTek RTL8852BE firmware) | ||||
| define Package/rtl8852be-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw89 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw89/rtw8852b_fw-1.bin $(1)/lib/firmware/rtw89 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8852be-firmware)) | ||||
|  | ||||
| Package/rtl8852ce-firmware = $(call Package/firmware-default,RealTek RTL8852CE firmware) | ||||
| define Package/rtl8852ce-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw89 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw89/rtw8852c_fw.bin $(1)/lib/firmware/rtw89 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8852ce-firmware)) | ||||
|  | ||||
| Package/rtl8922ae-firmware = $(call Package/firmware-default,RealTek RTL8922AE firmware) | ||||
| define Package/rtl8922ae-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rtw89 | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtw89/rtw8922a_fw.bin $(1)/lib/firmware/rtw89 | ||||
| endef | ||||
| $(eval $(call BuildPackage,rtl8922ae-firmware)) | ||||
| @@ -1,6 +0,0 @@ | ||||
| Package/rs9113-firmware = $(call Package/firmware-default,RedPine Signals rs9113 firmware) | ||||
| define Package/rs9113-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/rsi | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rsi/rs9113_wlan_qspi.rps $(1)/lib/firmware/rsi | ||||
| endef | ||||
| $(eval $(call BuildPackage,rs9113-firmware)) | ||||
| @@ -1,39 +0,0 @@ | ||||
| Package/wl12xx-firmware = $(call Package/firmware-default,TI WL12xx firmware,,LICENCE.ti-connectivity) | ||||
| define Package/wl12xx-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ti-connectivity | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl127x-fw-5-mr.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl127x-fw-5-plt.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl127x-fw-5-sr.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl127x-nvs.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl128x-fw-5-mr.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl128x-fw-5-plt.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl128x-fw-5-sr.bin \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl128x-nvs.bin \ | ||||
| 		$(1)/lib/firmware/ti-connectivity | ||||
| 	ln -s wl127x-nvs.bin $(1)/lib/firmware/ti-connectivity/wl1271-nvs.bin | ||||
| endef | ||||
| $(eval $(call BuildPackage,wl12xx-firmware)) | ||||
|  | ||||
| Package/wl18xx-firmware = $(call Package/firmware-default,TI WL18xx firmware,,LICENCE.ti-connectivity) | ||||
| define Package/wl18xx-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware/ti-connectivity | ||||
| 	$(INSTALL_DATA) \ | ||||
| 		$(PKG_BUILD_DIR)/ti-connectivity/wl18xx-fw-4.bin \ | ||||
| 		$(1)/lib/firmware/ti-connectivity | ||||
| endef | ||||
| $(eval $(call BuildPackage,wl18xx-firmware)) | ||||
|  | ||||
| Package/ti-3410-firmware = $(call Package/firmware-default,TI 3410 firmware) | ||||
| define Package/ti-3410-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/ti_3410.fw $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,ti-3410-firmware)) | ||||
|  | ||||
| Package/ti-5052-firmware = $(call Package/firmware-default,TI 5052 firmware) | ||||
| define Package/ti-5052-firmware/install | ||||
| 	$(INSTALL_DIR) $(1)/lib/firmware | ||||
| 	$(INSTALL_DATA) $(PKG_BUILD_DIR)/ti_5052.fw $(1)/lib/firmware | ||||
| endef | ||||
| $(eval $(call BuildPackage,ti-5052-firmware)) | ||||
| @@ -1,56 +0,0 @@ | ||||
| From 4c8a49854130da0117a0fdb858551824919a2389 Mon Sep 17 00:00:00 2001 | ||||
| From: Ingo Molnar <mingo@kernel.org> | ||||
| Date: Tue, 27 Feb 2024 09:58:15 +0100 | ||||
| Subject: [PATCH] smp: Avoid 'setup_max_cpus' namespace collision/shadowing | ||||
|  | ||||
| bringup_nonboot_cpus() gets passed the 'setup_max_cpus' | ||||
| variable in init/main.c - which is also the name of the parameter, | ||||
| shadowing the name. | ||||
|  | ||||
| To reduce confusion and to allow the 'setup_max_cpus' value | ||||
| to be #defined in the <linux/smp.h> header, use the 'max_cpus' | ||||
| name for the function parameter name. | ||||
|  | ||||
| Signed-off-by: Ingo Molnar <mingo@kernel.org> | ||||
| Cc: Thomas Gleixner <tglx@linutronix.de> | ||||
| Cc: linux-kernel@vger.kernel.org | ||||
| --- | ||||
|  include/linux/cpu.h | 2 +- | ||||
|  kernel/cpu.c        | 6 +++--- | ||||
|  2 files changed, 4 insertions(+), 4 deletions(-) | ||||
|  | ||||
| --- a/include/linux/cpu.h | ||||
| +++ b/include/linux/cpu.h | ||||
| @@ -109,7 +109,7 @@ void notify_cpu_starting(unsigned int cp | ||||
|  extern void cpu_maps_update_begin(void); | ||||
|  extern void cpu_maps_update_done(void); | ||||
|  int bringup_hibernate_cpu(unsigned int sleep_cpu); | ||||
| -void bringup_nonboot_cpus(unsigned int setup_max_cpus); | ||||
| +void bringup_nonboot_cpus(unsigned int max_cpus); | ||||
|   | ||||
|  #else	/* CONFIG_SMP */ | ||||
|  #define cpuhp_tasks_frozen	0 | ||||
| --- a/kernel/cpu.c | ||||
| +++ b/kernel/cpu.c | ||||
| @@ -1905,17 +1905,17 @@ static bool __init cpuhp_bringup_cpus_pa | ||||
|  static inline bool cpuhp_bringup_cpus_parallel(unsigned int ncpus) { return false; } | ||||
|  #endif /* CONFIG_HOTPLUG_PARALLEL */ | ||||
|   | ||||
| -void __init bringup_nonboot_cpus(unsigned int setup_max_cpus) | ||||
| +void __init bringup_nonboot_cpus(unsigned int max_cpus) | ||||
|  { | ||||
| -	if (!setup_max_cpus) | ||||
| +	if (!max_cpus) | ||||
|  		return; | ||||
|   | ||||
|  	/* Try parallel bringup optimization if enabled */ | ||||
| -	if (cpuhp_bringup_cpus_parallel(setup_max_cpus)) | ||||
| +	if (cpuhp_bringup_cpus_parallel(max_cpus)) | ||||
|  		return; | ||||
|   | ||||
|  	/* Full per CPU serialized bringup */ | ||||
| -	cpuhp_bringup_mask(cpu_present_mask, setup_max_cpus, CPUHP_ONLINE); | ||||
| +	cpuhp_bringup_mask(cpu_present_mask, max_cpus, CPUHP_ONLINE); | ||||
|  } | ||||
|   | ||||
|  #ifdef CONFIG_PM_SLEEP_SMP | ||||
| @@ -1,58 +0,0 @@ | ||||
| From 443df175be581618d6ff781dc3af3aa1a9ba789d Mon Sep 17 00:00:00 2001 | ||||
| From: Tony Ambardar <Tony.Ambardar@gmail.com> | ||||
| Date: Fri, 31 May 2024 23:55:55 -0700 | ||||
| Subject: [PATCH 1/2] compiler_types.h: Define __retain for | ||||
|  __attribute__((__retain__)) | ||||
|  | ||||
| Some code includes the __used macro to prevent functions and data from | ||||
| being optimized out. This macro implements __attribute__((__used__)), which | ||||
| operates at the compiler and IR-level, and so still allows a linker to | ||||
| remove objects intended to be kept. | ||||
|  | ||||
| Compilers supporting __attribute__((__retain__)) can address this gap by | ||||
| setting the flag SHF_GNU_RETAIN on the section of a function/variable, | ||||
| indicating to the linker the object should be retained. This attribute is | ||||
| available since gcc 11, clang 13, and binutils 2.36. | ||||
|  | ||||
| Provide a __retain macro implementing __attribute__((__retain__)), whose | ||||
| first user will be the '__bpf_kfunc' tag. | ||||
|  | ||||
| Link: https://lore.kernel.org/bpf/ZlmGoT9KiYLZd91S@krava/T/ | ||||
| Cc: stable@vger.kernel.org # v6.6+ | ||||
| Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com> | ||||
| --- | ||||
|  include/linux/compiler_types.h | 23 +++++++++++++++++++++++ | ||||
|  1 file changed, 23 insertions(+) | ||||
|  | ||||
| --- a/include/linux/compiler_types.h | ||||
| +++ b/include/linux/compiler_types.h | ||||
| @@ -145,6 +145,29 @@ static inline void __chk_io_ptr(const vo | ||||
|  #define __has_builtin(x) (0) | ||||
|  #endif | ||||
|   | ||||
| +/* | ||||
| + * Annotating a function/variable with __retain tells the compiler to place | ||||
| + * the object in its own section and set the flag SHF_GNU_RETAIN. This flag | ||||
| + * instructs the linker to retain the object during garbage-cleanup or LTO | ||||
| + * phases. | ||||
| + * | ||||
| + * Note that the __used macro is also used to prevent functions or data | ||||
| + * being optimized out, but operates at the compiler/IR-level and may still | ||||
| + * allow unintended removal of objects during linking. | ||||
| + * | ||||
| + * Optional: only supported since gcc >= 11, clang >= 13 | ||||
| + * | ||||
| + *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-retain-function-attribute | ||||
| + * clang: https://clang.llvm.org/docs/AttributeReference.html#retain | ||||
| + */ | ||||
| +#if __has_attribute(__retain__) && \ | ||||
| +	(defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || \ | ||||
| +	 defined(CONFIG_LTO_CLANG)) | ||||
| +# define __retain			__attribute__((__retain__)) | ||||
| +#else | ||||
| +# define __retain | ||||
| +#endif | ||||
| + | ||||
|  /* Compiler specific macros. */ | ||||
|  #ifdef __clang__ | ||||
|  #include <linux/compiler-clang.h> | ||||
| @@ -1,65 +0,0 @@ | ||||
| From ac507ed9882fd91a94657d68fe9ceac04b957103 Mon Sep 17 00:00:00 2001 | ||||
| From: Tony Ambardar <Tony.Ambardar@gmail.com> | ||||
| Date: Sat, 1 Jun 2024 00:00:21 -0700 | ||||
| Subject: [PATCH 2/2] bpf: Harden __bpf_kfunc tag against linker kfunc removal | ||||
|  | ||||
| BPF kfuncs are often not directly referenced and may be inadvertently | ||||
| removed by optimization steps during kernel builds, thus the __bpf_kfunc | ||||
| tag mitigates against this removal by including the __used macro. However, | ||||
| this macro alone does not prevent removal during linking, and may still | ||||
| yield build warnings (e.g. on mips64el): | ||||
|  | ||||
|     LD      vmlinux | ||||
|     BTFIDS  vmlinux | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_verify_pkcs7_signature | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_lookup_user_key | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_lookup_system_key | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_key_put | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_iter_task_next | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_iter_css_task_new | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_get_file_xattr | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_ct_insert_entry | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_cgroup_release | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_cgroup_from_id | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_cgroup_acquire | ||||
|   WARN: resolve_btfids: unresolved symbol bpf_arena_free_pages | ||||
|     NM      System.map | ||||
|     SORTTAB vmlinux | ||||
|     OBJCOPY vmlinux.32 | ||||
|  | ||||
| Update the __bpf_kfunc tag to better guard against linker optimization by | ||||
| including the new __retain compiler macro, which fixes the warnings above. | ||||
|  | ||||
| Verify the __retain macro with readelf by checking object flags for 'R': | ||||
|  | ||||
|   $ readelf -Wa kernel/trace/bpf_trace.o | ||||
|   Section Headers: | ||||
|     [Nr]  Name              Type     Address  Off  Size ES Flg Lk Inf Al | ||||
|   ... | ||||
|     [178] .text.bpf_key_put PROGBITS 00000000 6420 0050 00 AXR  0   0  8 | ||||
|   ... | ||||
|   Key to Flags: | ||||
|   ... | ||||
|     R (retain), D (mbind), p (processor specific) | ||||
|  | ||||
| Link: https://lore.kernel.org/bpf/ZlmGoT9KiYLZd91S@krava/T/ | ||||
| Reported-by: kernel test robot <lkp@intel.com> | ||||
| Closes: https://lore.kernel.org/r/202401211357.OCX9yllM-lkp@intel.com/ | ||||
| Fixes: 57e7c169cd6a ("bpf: Add __bpf_kfunc tag for marking kernel functions as kfuncs") | ||||
| Cc: stable@vger.kernel.org # v6.6+ | ||||
| Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com> | ||||
| --- | ||||
|  include/linux/btf.h | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/include/linux/btf.h | ||||
| +++ b/include/linux/btf.h | ||||
| @@ -81,7 +81,7 @@ | ||||
|   * as to avoid issues such as the compiler inlining or eliding either a static | ||||
|   * kfunc, or a global kfunc in an LTO build. | ||||
|   */ | ||||
| -#define __bpf_kfunc __used noinline | ||||
| +#define __bpf_kfunc __used __retain noinline | ||||
|   | ||||
|  /* | ||||
|   * Return the name of the passed struct, if exists, or halt the build if for | ||||
| @@ -1,51 +0,0 @@ | ||||
| From 65033574ade97afccba074d837fd269903a83a9a Mon Sep 17 00:00:00 2001 | ||||
| From: Catalin Marinas <catalin.marinas@arm.com> | ||||
| Date: Thu, 5 Oct 2023 16:40:30 +0100 | ||||
| Subject: [PATCH] arm64: swiotlb: Reduce the default size if no ZONE_DMA | ||||
|  bouncing needed | ||||
|  | ||||
| With CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC enabled, the arm64 kernel still | ||||
| allocates the default SWIOTLB buffer (64MB) even if ZONE_DMA is disabled | ||||
| or all the RAM fits into this zone. However, this potentially wastes a | ||||
| non-negligible amount of memory on platforms with little RAM. | ||||
|  | ||||
| Reduce the SWIOTLB size to 1MB per 1GB of RAM if only needed for | ||||
| kmalloc() buffer bouncing. | ||||
|  | ||||
| Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> | ||||
| Suggested-by: Ross Burton <ross.burton@arm.com> | ||||
| Cc: Ross Burton <ross.burton@arm.com> | ||||
| Cc: Will Deacon <will@kernel.org> | ||||
| Reviewed-by: Robin Murphy <robin.murphy@arm.com> | ||||
| --- | ||||
|  arch/arm64/mm/init.c | 11 ++++++++++- | ||||
|  1 file changed, 10 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/arch/arm64/mm/init.c | ||||
| +++ b/arch/arm64/mm/init.c | ||||
| @@ -16,6 +16,7 @@ | ||||
|  #include <linux/nodemask.h> | ||||
|  #include <linux/initrd.h> | ||||
|  #include <linux/gfp.h> | ||||
| +#include <linux/math.h> | ||||
|  #include <linux/memblock.h> | ||||
|  #include <linux/sort.h> | ||||
|  #include <linux/of.h> | ||||
| @@ -493,8 +494,16 @@ void __init mem_init(void) | ||||
|  { | ||||
|  	bool swiotlb = max_pfn > PFN_DOWN(arm64_dma_phys_limit); | ||||
|   | ||||
| -	if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC)) | ||||
| +	if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb) { | ||||
| +		/* | ||||
| +		 * If no bouncing needed for ZONE_DMA, reduce the swiotlb | ||||
| +		 * buffer for kmalloc() bouncing to 1MB per 1GB of RAM. | ||||
| +		 */ | ||||
| +		unsigned long size = | ||||
| +			DIV_ROUND_UP(memblock_phys_mem_size(), 1024); | ||||
| +		swiotlb_adjust_size(min(swiotlb_size_or_default(), size)); | ||||
|  		swiotlb = true; | ||||
| +	} | ||||
|   | ||||
|  	swiotlb_init(swiotlb, SWIOTLB_VERBOSE); | ||||
|   | ||||
| @@ -1,161 +0,0 @@ | ||||
| From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001 | ||||
| From: Tanzir Hasan <tanzirh@google.com> | ||||
| Date: Tue, 26 Dec 2023 18:00:00 +0000 | ||||
| Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h | ||||
|  | ||||
| This patch creates wordpart.h and includes it in asm/word-at-a-time.h | ||||
| for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h | ||||
| because of REPEAT_BYTE. Moving this to another header and including it | ||||
| where necessary allows us to not include the bloated kernel.h. Making | ||||
| this implicit dependency on REPEAT_BYTE explicit allows for later | ||||
| improvements in the lib/string.c inclusion list. | ||||
|  | ||||
| Suggested-by: Al Viro <viro@zeniv.linux.org.uk> | ||||
| Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com> | ||||
| Signed-off-by: Tanzir Hasan <tanzirh@google.com> | ||||
| Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> | ||||
| Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com | ||||
| Signed-off-by: Kees Cook <keescook@chromium.org> | ||||
| --- | ||||
|  arch/arm/include/asm/word-at-a-time.h     |  3 ++- | ||||
|  arch/arm64/include/asm/word-at-a-time.h   |  3 ++- | ||||
|  arch/powerpc/include/asm/word-at-a-time.h |  4 ++-- | ||||
|  arch/riscv/include/asm/word-at-a-time.h   |  3 ++- | ||||
|  arch/s390/include/asm/word-at-a-time.h    |  3 ++- | ||||
|  arch/sh/include/asm/word-at-a-time.h      |  2 ++ | ||||
|  arch/x86/include/asm/word-at-a-time.h     |  3 ++- | ||||
|  arch/x86/kvm/mmu/mmu.c                    |  1 + | ||||
|  fs/namei.c                                |  2 +- | ||||
|  include/asm-generic/word-at-a-time.h      |  3 ++- | ||||
|  include/linux/kernel.h                    |  8 -------- | ||||
|  include/linux/wordpart.h                  | 13 +++++++++++++ | ||||
|  12 files changed, 31 insertions(+), 17 deletions(-) | ||||
|  create mode 100644 include/linux/wordpart.h | ||||
|  | ||||
| --- a/arch/arm/include/asm/word-at-a-time.h | ||||
| +++ b/arch/arm/include/asm/word-at-a-time.h | ||||
| @@ -8,7 +8,8 @@ | ||||
|   * Little-endian word-at-a-time zero byte handling. | ||||
|   * Heavily based on the x86 algorithm. | ||||
|   */ | ||||
| -#include <linux/kernel.h> | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/wordpart.h> | ||||
|   | ||||
|  struct word_at_a_time { | ||||
|  	const unsigned long one_bits, high_bits; | ||||
| --- a/arch/arm64/include/asm/word-at-a-time.h | ||||
| +++ b/arch/arm64/include/asm/word-at-a-time.h | ||||
| @@ -9,7 +9,8 @@ | ||||
|   | ||||
|  #ifndef __AARCH64EB__ | ||||
|   | ||||
| -#include <linux/kernel.h> | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/wordpart.h> | ||||
|   | ||||
|  struct word_at_a_time { | ||||
|  	const unsigned long one_bits, high_bits; | ||||
| --- a/arch/powerpc/include/asm/word-at-a-time.h | ||||
| +++ b/arch/powerpc/include/asm/word-at-a-time.h | ||||
| @@ -4,8 +4,8 @@ | ||||
|  /* | ||||
|   * Word-at-a-time interfaces for PowerPC. | ||||
|   */ | ||||
| - | ||||
| -#include <linux/kernel.h> | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/wordpart.h> | ||||
|  #include <asm/asm-compat.h> | ||||
|  #include <asm/extable.h> | ||||
|   | ||||
| --- a/arch/sh/include/asm/word-at-a-time.h | ||||
| +++ b/arch/sh/include/asm/word-at-a-time.h | ||||
| @@ -5,6 +5,8 @@ | ||||
|  #ifdef CONFIG_CPU_BIG_ENDIAN | ||||
|  # include <asm-generic/word-at-a-time.h> | ||||
|  #else | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/wordpart.h> | ||||
|  /* | ||||
|   * Little-endian version cribbed from x86. | ||||
|   */ | ||||
| --- a/arch/x86/include/asm/word-at-a-time.h | ||||
| +++ b/arch/x86/include/asm/word-at-a-time.h | ||||
| @@ -2,7 +2,8 @@ | ||||
|  #ifndef _ASM_WORD_AT_A_TIME_H | ||||
|  #define _ASM_WORD_AT_A_TIME_H | ||||
|   | ||||
| -#include <linux/kernel.h> | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/wordpart.h> | ||||
|   | ||||
|  /* | ||||
|   * This is largely generic for little-endian machines, but the | ||||
| --- a/arch/x86/kvm/mmu/mmu.c | ||||
| +++ b/arch/x86/kvm/mmu/mmu.c | ||||
| @@ -47,6 +47,7 @@ | ||||
|  #include <linux/kern_levels.h> | ||||
|  #include <linux/kstrtox.h> | ||||
|  #include <linux/kthread.h> | ||||
| +#include <linux/wordpart.h> | ||||
|   | ||||
|  #include <asm/page.h> | ||||
|  #include <asm/memtype.h> | ||||
| --- a/fs/namei.c | ||||
| +++ b/fs/namei.c | ||||
| @@ -17,8 +17,8 @@ | ||||
|   | ||||
|  #include <linux/init.h> | ||||
|  #include <linux/export.h> | ||||
| -#include <linux/kernel.h> | ||||
|  #include <linux/slab.h> | ||||
| +#include <linux/wordpart.h> | ||||
|  #include <linux/fs.h> | ||||
|  #include <linux/filelock.h> | ||||
|  #include <linux/namei.h> | ||||
| --- a/include/asm-generic/word-at-a-time.h | ||||
| +++ b/include/asm-generic/word-at-a-time.h | ||||
| @@ -2,7 +2,8 @@ | ||||
|  #ifndef _ASM_WORD_AT_A_TIME_H | ||||
|  #define _ASM_WORD_AT_A_TIME_H | ||||
|   | ||||
| -#include <linux/kernel.h> | ||||
| +#include <linux/bitops.h> | ||||
| +#include <linux/wordpart.h> | ||||
|  #include <asm/byteorder.h> | ||||
|   | ||||
|  #ifdef __BIG_ENDIAN | ||||
| --- a/include/linux/kernel.h | ||||
| +++ b/include/linux/kernel.h | ||||
| @@ -38,14 +38,6 @@ | ||||
|   | ||||
|  #define STACK_MAGIC	0xdeadbeef | ||||
|   | ||||
| -/** | ||||
| - * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value | ||||
| - * @x: value to repeat | ||||
| - * | ||||
| - * NOTE: @x is not checked for > 0xff; larger values produce odd results. | ||||
| - */ | ||||
| -#define REPEAT_BYTE(x)	((~0ul / 0xff) * (x)) | ||||
| - | ||||
|  /* generic data direction definitions */ | ||||
|  #define READ			0 | ||||
|  #define WRITE			1 | ||||
| --- /dev/null | ||||
| +++ b/include/linux/wordpart.h | ||||
| @@ -0,0 +1,13 @@ | ||||
| +/* SPDX-License-Identifier: GPL-2.0 */ | ||||
| + | ||||
| +#ifndef _LINUX_WORDPART_H | ||||
| +#define _LINUX_WORDPART_H | ||||
| +/** | ||||
| + * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value | ||||
| + * @x: value to repeat | ||||
| + * | ||||
| + * NOTE: @x is not checked for > 0xff; larger values produce odd results. | ||||
| + */ | ||||
| +#define REPEAT_BYTE(x)	((~0ul / 0xff) * (x)) | ||||
| + | ||||
| +#endif // _LINUX_WORDPART_H | ||||
| @@ -1,107 +0,0 @@ | ||||
| From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001 | ||||
| From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||||
| Date: Wed, 14 Feb 2024 19:26:32 +0200 | ||||
| Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to | ||||
|  wordpart.h | ||||
|  | ||||
| The wordpart.h header is collecting APIs related to the handling | ||||
| parts of the word (usually in byte granularity). The upper_*_bits() | ||||
| and lower_*_bits() are good candidates to be moved to there. | ||||
|  | ||||
| This helps to clean up header dependency hell with regard to kernel.h | ||||
| as the latter gathers completely unrelated stuff together and slows | ||||
| down compilation (especially when it's included into other header). | ||||
|  | ||||
| Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||||
| Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com | ||||
| Reviewed-by: Randy Dunlap <rdunlap@infradead.org> | ||||
| Signed-off-by: Kees Cook <keescook@chromium.org> | ||||
| --- | ||||
|  include/linux/kernel.h   | 30 ++---------------------------- | ||||
|  include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++ | ||||
|  2 files changed, 31 insertions(+), 28 deletions(-) | ||||
|  | ||||
| --- a/include/linux/kernel.h | ||||
| +++ b/include/linux/kernel.h | ||||
| @@ -32,6 +32,8 @@ | ||||
|  #include <linux/sprintf.h> | ||||
|  #include <linux/static_call_types.h> | ||||
|  #include <linux/instruction_pointer.h> | ||||
| +#include <linux/wordpart.h> | ||||
| + | ||||
|  #include <asm/byteorder.h> | ||||
|   | ||||
|  #include <uapi/linux/kernel.h> | ||||
| @@ -57,34 +59,6 @@ | ||||
|  }					\ | ||||
|  ) | ||||
|   | ||||
| -/** | ||||
| - * upper_32_bits - return bits 32-63 of a number | ||||
| - * @n: the number we're accessing | ||||
| - * | ||||
| - * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress | ||||
| - * the "right shift count >= width of type" warning when that quantity is | ||||
| - * 32-bits. | ||||
| - */ | ||||
| -#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) | ||||
| - | ||||
| -/** | ||||
| - * lower_32_bits - return bits 0-31 of a number | ||||
| - * @n: the number we're accessing | ||||
| - */ | ||||
| -#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) | ||||
| - | ||||
| -/** | ||||
| - * upper_16_bits - return bits 16-31 of a number | ||||
| - * @n: the number we're accessing | ||||
| - */ | ||||
| -#define upper_16_bits(n) ((u16)((n) >> 16)) | ||||
| - | ||||
| -/** | ||||
| - * lower_16_bits - return bits 0-15 of a number | ||||
| - * @n: the number we're accessing | ||||
| - */ | ||||
| -#define lower_16_bits(n) ((u16)((n) & 0xffff)) | ||||
| - | ||||
|  struct completion; | ||||
|  struct user; | ||||
|   | ||||
| --- a/include/linux/wordpart.h | ||||
| +++ b/include/linux/wordpart.h | ||||
| @@ -2,6 +2,35 @@ | ||||
|   | ||||
|  #ifndef _LINUX_WORDPART_H | ||||
|  #define _LINUX_WORDPART_H | ||||
| + | ||||
| +/** | ||||
| + * upper_32_bits - return bits 32-63 of a number | ||||
| + * @n: the number we're accessing | ||||
| + * | ||||
| + * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress | ||||
| + * the "right shift count >= width of type" warning when that quantity is | ||||
| + * 32-bits. | ||||
| + */ | ||||
| +#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) | ||||
| + | ||||
| +/** | ||||
| + * lower_32_bits - return bits 0-31 of a number | ||||
| + * @n: the number we're accessing | ||||
| + */ | ||||
| +#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) | ||||
| + | ||||
| +/** | ||||
| + * upper_16_bits - return bits 16-31 of a number | ||||
| + * @n: the number we're accessing | ||||
| + */ | ||||
| +#define upper_16_bits(n) ((u16)((n) >> 16)) | ||||
| + | ||||
| +/** | ||||
| + * lower_16_bits - return bits 0-15 of a number | ||||
| + * @n: the number we're accessing | ||||
| + */ | ||||
| +#define lower_16_bits(n) ((u16)((n) & 0xffff)) | ||||
| + | ||||
|  /** | ||||
|   * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value | ||||
|   * @x: value to repeat | ||||
| @@ -1,206 +0,0 @@ | ||||
| From 8cd2accb71f5eb8e92d775fc1978d3779875c2e5 Mon Sep 17 00:00:00 2001 | ||||
| From: Baoquan He <bhe@redhat.com> | ||||
| Date: Fri, 8 Dec 2023 15:30:34 +0800 | ||||
| Subject: [PATCH] mips, kexec: fix the incorrect ifdeffery and dependency of | ||||
|  CONFIG_KEXEC | ||||
|  | ||||
| The select of KEXEC for CRASH_DUMP in kernel/Kconfig.kexec will be | ||||
| dropped, then compiling errors will be triggered if below config items are | ||||
| set: | ||||
|  | ||||
| === | ||||
| CONFIG_CRASH_CORE=y | ||||
| CONFIG_KEXEC_CORE=y | ||||
| CONFIG_CRASH_DUMP=y | ||||
| === | ||||
|  | ||||
| -------------------------------------------------------------------- | ||||
| mipsel-linux-ld: kernel/kexec_core.o: in function `kimage_free': | ||||
| kernel/kexec_core.c:(.text+0x2200): undefined reference to `machine_kexec_cleanup' | ||||
| mipsel-linux-ld: kernel/kexec_core.o: in function `__crash_kexec': | ||||
| kernel/kexec_core.c:(.text+0x2480): undefined reference to `machine_crash_shutdown' | ||||
| mipsel-linux-ld: kernel/kexec_core.c:(.text+0x2488): undefined reference to `machine_kexec' | ||||
| mipsel-linux-ld: kernel/kexec_core.o: in function `kernel_kexec': | ||||
| kernel/kexec_core.c:(.text+0x29b8): undefined reference to `machine_shutdown' | ||||
| mipsel-linux-ld: kernel/kexec_core.c:(.text+0x29c0): undefined reference to `machine_kexec' | ||||
| -------------------------------------------------------------------- | ||||
|  | ||||
| Here, change the dependency of building kexec_core related object files, | ||||
| and the ifdeffery in mips from CONFIG_KEXEC to CONFIG_KEXEC_CORE. | ||||
|  | ||||
| Link: https://lkml.kernel.org/r/20231208073036.7884-4-bhe@redhat.com | ||||
| Signed-off-by: Baoquan He <bhe@redhat.com> | ||||
| Reported-by: kernel test robot <lkp@intel.com> | ||||
| Closes: https://lore.kernel.org/oe-kbuild-all/202311302042.sn8cDPIX-lkp@intel.com/ | ||||
| Cc: Eric DeVolder <eric_devolder@yahoo.com> | ||||
| Cc: Ignat Korchagin <ignat@cloudflare.com> | ||||
| Cc: Stephen Rothwell <sfr@canb.auug.org.au> | ||||
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> | ||||
| --- | ||||
|  arch/mips/cavium-octeon/smp.c   |  4 ++-- | ||||
|  arch/mips/include/asm/kexec.h   |  2 +- | ||||
|  arch/mips/include/asm/smp-ops.h |  2 +- | ||||
|  arch/mips/include/asm/smp.h     |  2 +- | ||||
|  arch/mips/kernel/Makefile       |  2 +- | ||||
|  arch/mips/kernel/smp-bmips.c    |  4 ++-- | ||||
|  arch/mips/kernel/smp-cps.c      | 10 +++++----- | ||||
|  arch/mips/loongson64/reset.c    |  4 ++-- | ||||
|  arch/mips/loongson64/smp.c      |  2 +- | ||||
|  9 files changed, 16 insertions(+), 16 deletions(-) | ||||
|  | ||||
| --- a/arch/mips/cavium-octeon/smp.c | ||||
| +++ b/arch/mips/cavium-octeon/smp.c | ||||
| @@ -422,7 +422,7 @@ static const struct plat_smp_ops octeon_ | ||||
|  	.cpu_disable		= octeon_cpu_disable, | ||||
|  	.cpu_die		= octeon_cpu_die, | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	.kexec_nonboot_cpu	= kexec_nonboot_cpu_jump, | ||||
|  #endif | ||||
|  }; | ||||
| @@ -502,7 +502,7 @@ static const struct plat_smp_ops octeon_ | ||||
|  	.cpu_disable		= octeon_cpu_disable, | ||||
|  	.cpu_die		= octeon_cpu_die, | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	.kexec_nonboot_cpu	= kexec_nonboot_cpu_jump, | ||||
|  #endif | ||||
|  }; | ||||
| --- a/arch/mips/include/asm/kexec.h | ||||
| +++ b/arch/mips/include/asm/kexec.h | ||||
| @@ -31,7 +31,7 @@ static inline void crash_setup_regs(stru | ||||
|  		prepare_frametrace(newregs); | ||||
|  } | ||||
|   | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  struct kimage; | ||||
|  extern unsigned long kexec_args[4]; | ||||
|  extern int (*_machine_kexec_prepare)(struct kimage *); | ||||
| --- a/arch/mips/include/asm/smp-ops.h | ||||
| +++ b/arch/mips/include/asm/smp-ops.h | ||||
| @@ -35,7 +35,7 @@ struct plat_smp_ops { | ||||
|  	void (*cpu_die)(unsigned int cpu); | ||||
|  	void (*cleanup_dead_cpu)(unsigned cpu); | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	void (*kexec_nonboot_cpu)(void); | ||||
|  #endif | ||||
|  }; | ||||
| --- a/arch/mips/include/asm/smp.h | ||||
| +++ b/arch/mips/include/asm/smp.h | ||||
| @@ -93,7 +93,7 @@ static inline void __cpu_die(unsigned in | ||||
|  extern void __noreturn play_dead(void); | ||||
|  #endif | ||||
|   | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  static inline void kexec_nonboot_cpu(void) | ||||
|  { | ||||
|  	extern const struct plat_smp_ops *mp_ops;	/* private */ | ||||
| --- a/arch/mips/kernel/Makefile | ||||
| +++ b/arch/mips/kernel/Makefile | ||||
| @@ -90,7 +90,7 @@ obj-$(CONFIG_GPIO_TXX9)		+= gpio_txx9.o | ||||
|   | ||||
|  obj-$(CONFIG_RELOCATABLE)	+= relocate.o | ||||
|   | ||||
| -obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o crash.o | ||||
| +obj-$(CONFIG_KEXEC_CORE)	+= machine_kexec.o relocate_kernel.o crash.o | ||||
|  obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o | ||||
|  obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o | ||||
|  obj-$(CONFIG_EARLY_PRINTK_8250)	+= early_printk_8250.o | ||||
| --- a/arch/mips/kernel/smp-bmips.c | ||||
| +++ b/arch/mips/kernel/smp-bmips.c | ||||
| @@ -434,7 +434,7 @@ const struct plat_smp_ops bmips43xx_smp_ | ||||
|  	.cpu_disable		= bmips_cpu_disable, | ||||
|  	.cpu_die		= bmips_cpu_die, | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	.kexec_nonboot_cpu	= kexec_nonboot_cpu_jump, | ||||
|  #endif | ||||
|  }; | ||||
| @@ -451,7 +451,7 @@ const struct plat_smp_ops bmips5000_smp_ | ||||
|  	.cpu_disable		= bmips_cpu_disable, | ||||
|  	.cpu_die		= bmips_cpu_die, | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	.kexec_nonboot_cpu	= kexec_nonboot_cpu_jump, | ||||
|  #endif | ||||
|  }; | ||||
| --- a/arch/mips/kernel/smp-cps.c | ||||
| +++ b/arch/mips/kernel/smp-cps.c | ||||
| @@ -395,7 +395,7 @@ static void cps_smp_finish(void) | ||||
|  	local_irq_enable(); | ||||
|  } | ||||
|   | ||||
| -#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_KEXEC) | ||||
| +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_KEXEC_CORE) | ||||
|   | ||||
|  enum cpu_death { | ||||
|  	CPU_DEATH_HALT, | ||||
| @@ -432,7 +432,7 @@ static void cps_shutdown_this_cpu(enum c | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|   | ||||
|  static void cps_kexec_nonboot_cpu(void) | ||||
|  { | ||||
| @@ -442,9 +442,9 @@ static void cps_kexec_nonboot_cpu(void) | ||||
|  		cps_shutdown_this_cpu(CPU_DEATH_POWER); | ||||
|  } | ||||
|   | ||||
| -#endif /* CONFIG_KEXEC */ | ||||
| +#endif /* CONFIG_KEXEC_CORE */ | ||||
|   | ||||
| -#endif /* CONFIG_HOTPLUG_CPU || CONFIG_KEXEC */ | ||||
| +#endif /* CONFIG_HOTPLUG_CPU || CONFIG_KEXEC_CORE */ | ||||
|   | ||||
|  #ifdef CONFIG_HOTPLUG_CPU | ||||
|   | ||||
| @@ -613,7 +613,7 @@ static const struct plat_smp_ops cps_smp | ||||
|  	.cpu_die		= cps_cpu_die, | ||||
|  	.cleanup_dead_cpu	= cps_cleanup_dead_cpu, | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	.kexec_nonboot_cpu	= cps_kexec_nonboot_cpu, | ||||
|  #endif | ||||
|  }; | ||||
| --- a/arch/mips/loongson64/reset.c | ||||
| +++ b/arch/mips/loongson64/reset.c | ||||
| @@ -39,7 +39,7 @@ static int firmware_poweroff(struct sys_ | ||||
|  	return NOTIFY_DONE; | ||||
|  } | ||||
|   | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|   | ||||
|  /* 0X80000000~0X80200000 is safe */ | ||||
|  #define MAX_ARGS	64 | ||||
| @@ -152,7 +152,7 @@ static int __init mips_reboot_setup(void | ||||
|  				 firmware_poweroff, NULL); | ||||
|  	} | ||||
|   | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	kexec_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL); | ||||
|  	if (WARN_ON(!kexec_argv)) | ||||
|  		return -ENOMEM; | ||||
| --- a/arch/mips/loongson64/smp.c | ||||
| +++ b/arch/mips/loongson64/smp.c | ||||
| @@ -883,7 +883,7 @@ const struct plat_smp_ops loongson3_smp_ | ||||
|  	.cpu_disable = loongson3_cpu_disable, | ||||
|  	.cpu_die = loongson3_cpu_die, | ||||
|  #endif | ||||
| -#ifdef CONFIG_KEXEC | ||||
| +#ifdef CONFIG_KEXEC_CORE | ||||
|  	.kexec_nonboot_cpu = kexec_nonboot_cpu_jump, | ||||
|  #endif | ||||
|  }; | ||||
| @@ -1,171 +0,0 @@ | ||||
| From a5c05453a13ab324ad8719e8a23dfb6af01f3652 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 20 Jun 2024 17:26:42 +0200 | ||||
| Subject: [PATCH 1/4] mips: bmips: rework and cache CBR addr handling | ||||
|  | ||||
| Rework the handling of the CBR address and cache it. This address | ||||
| doesn't change and can be cached instead of reading the register every | ||||
| time. | ||||
|  | ||||
| This is in preparation of permitting to tweak the CBR address in DT with | ||||
| broken SoC or bootloader. | ||||
|  | ||||
| bmips_cbr_addr is defined in setup.c for each arch to keep compatibility | ||||
| with legacy brcm47xx/brcm63xx and generic BMIPS target. | ||||
|  | ||||
| Acked-by: Florian Fainelli <florian.fainelli@broadcom.com> | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> | ||||
| --- | ||||
|  arch/mips/bcm47xx/prom.c      | 3 +++ | ||||
|  arch/mips/bcm47xx/setup.c     | 4 ++++ | ||||
|  arch/mips/bcm63xx/prom.c      | 3 +++ | ||||
|  arch/mips/bcm63xx/setup.c     | 4 ++++ | ||||
|  arch/mips/bmips/dma.c         | 2 +- | ||||
|  arch/mips/bmips/setup.c       | 7 ++++++- | ||||
|  arch/mips/include/asm/bmips.h | 1 + | ||||
|  arch/mips/kernel/smp-bmips.c  | 4 ++-- | ||||
|  8 files changed, 24 insertions(+), 4 deletions(-) | ||||
|  | ||||
| --- a/arch/mips/bcm47xx/prom.c | ||||
| +++ b/arch/mips/bcm47xx/prom.c | ||||
| @@ -32,6 +32,7 @@ | ||||
|  #include <linux/ssb/ssb_driver_chipcommon.h> | ||||
|  #include <linux/ssb/ssb_regs.h> | ||||
|  #include <linux/smp.h> | ||||
| +#include <asm/bmips.h> | ||||
|  #include <asm/bootinfo.h> | ||||
|  #include <bcm47xx.h> | ||||
|  #include <bcm47xx_board.h> | ||||
| @@ -109,6 +110,8 @@ static __init void prom_init_mem(void) | ||||
|   | ||||
|  void __init prom_init(void) | ||||
|  { | ||||
| +	/* Cache CBR addr before CPU/DMA setup */ | ||||
| +	bmips_cbr_addr = BMIPS_GET_CBR(); | ||||
|  	prom_init_mem(); | ||||
|  	setup_8250_early_printk_port(CKSEG1ADDR(BCM47XX_SERIAL_ADDR), 0, 0); | ||||
|  } | ||||
| --- a/arch/mips/bcm47xx/setup.c | ||||
| +++ b/arch/mips/bcm47xx/setup.c | ||||
| @@ -37,6 +37,7 @@ | ||||
|  #include <linux/ssb/ssb.h> | ||||
|  #include <linux/ssb/ssb_embedded.h> | ||||
|  #include <linux/bcma/bcma_soc.h> | ||||
| +#include <asm/bmips.h> | ||||
|  #include <asm/bootinfo.h> | ||||
|  #include <asm/idle.h> | ||||
|  #include <asm/prom.h> | ||||
| @@ -45,6 +46,9 @@ | ||||
|  #include <bcm47xx.h> | ||||
|  #include <bcm47xx_board.h> | ||||
|   | ||||
| +/* CBR addr doesn't change and we can cache it */ | ||||
| +void __iomem *bmips_cbr_addr __read_mostly; | ||||
| + | ||||
|  union bcm47xx_bus bcm47xx_bus; | ||||
|  EXPORT_SYMBOL(bcm47xx_bus); | ||||
|   | ||||
| --- a/arch/mips/bcm63xx/prom.c | ||||
| +++ b/arch/mips/bcm63xx/prom.c | ||||
| @@ -22,6 +22,9 @@ void __init prom_init(void) | ||||
|  { | ||||
|  	u32 reg, mask; | ||||
|   | ||||
| +	/* Cache CBR addr before CPU/DMA setup */ | ||||
| +	bmips_cbr_addr = BMIPS_GET_CBR(); | ||||
| + | ||||
|  	bcm63xx_cpu_init(); | ||||
|   | ||||
|  	/* stop any running watchdog */ | ||||
| --- a/arch/mips/bcm63xx/setup.c | ||||
| +++ b/arch/mips/bcm63xx/setup.c | ||||
| @@ -12,6 +12,7 @@ | ||||
|  #include <linux/memblock.h> | ||||
|  #include <linux/ioport.h> | ||||
|  #include <linux/pm.h> | ||||
| +#include <asm/bmips.h> | ||||
|  #include <asm/bootinfo.h> | ||||
|  #include <asm/time.h> | ||||
|  #include <asm/reboot.h> | ||||
| @@ -22,6 +23,9 @@ | ||||
|  #include <bcm63xx_io.h> | ||||
|  #include <bcm63xx_gpio.h> | ||||
|   | ||||
| +/* CBR addr doesn't change and we can cache it */ | ||||
| +void __iomem *bmips_cbr_addr __read_mostly; | ||||
| + | ||||
|  void bcm63xx_machine_halt(void) | ||||
|  { | ||||
|  	pr_info("System halted\n"); | ||||
| --- a/arch/mips/bmips/dma.c | ||||
| +++ b/arch/mips/bmips/dma.c | ||||
| @@ -9,7 +9,7 @@ bool bmips_rac_flush_disable; | ||||
|   | ||||
|  void arch_sync_dma_for_cpu_all(void) | ||||
|  { | ||||
| -	void __iomem *cbr = BMIPS_GET_CBR(); | ||||
| +	void __iomem *cbr = bmips_cbr_addr; | ||||
|  	u32 cfg; | ||||
|   | ||||
|  	if (boot_cpu_type() != CPU_BMIPS3300 && | ||||
| --- a/arch/mips/bmips/setup.c | ||||
| +++ b/arch/mips/bmips/setup.c | ||||
| @@ -34,6 +34,9 @@ | ||||
|  #define REG_BCM6328_OTP		((void __iomem *)CKSEG1ADDR(0x1000062c)) | ||||
|  #define BCM6328_TP1_DISABLED	BIT(9) | ||||
|   | ||||
| +/* CBR addr doesn't change and we can cache it */ | ||||
| +void __iomem *bmips_cbr_addr __read_mostly; | ||||
| + | ||||
|  extern bool bmips_rac_flush_disable; | ||||
|   | ||||
|  static const unsigned long kbase = VMLINUX_LOAD_ADDRESS & 0xfff00000; | ||||
| @@ -111,7 +114,7 @@ static void bcm6358_quirks(void) | ||||
|  	 * because the bootloader is not initializing it properly. | ||||
|  	 */ | ||||
|  	bmips_rac_flush_disable = !!(read_c0_brcm_cmt_local() & (1 << 31)) || | ||||
| -				  !!BMIPS_GET_CBR(); | ||||
| +				  !!bmips_cbr_addr; | ||||
|  } | ||||
|   | ||||
|  static void bcm6368_quirks(void) | ||||
| @@ -144,6 +147,8 @@ static void __init bmips_init_cfe(void) | ||||
|   | ||||
|  void __init prom_init(void) | ||||
|  { | ||||
| +	/* Cache CBR addr before CPU/DMA setup */ | ||||
| +	bmips_cbr_addr = BMIPS_GET_CBR(); | ||||
|  	bmips_init_cfe(); | ||||
|  	bmips_cpu_setup(); | ||||
|  	register_bmips_smp_ops(); | ||||
| --- a/arch/mips/include/asm/bmips.h | ||||
| +++ b/arch/mips/include/asm/bmips.h | ||||
| @@ -81,6 +81,7 @@ extern char bmips_smp_movevec[]; | ||||
|  extern char bmips_smp_int_vec[]; | ||||
|  extern char bmips_smp_int_vec_end[]; | ||||
|   | ||||
| +extern void __iomem *bmips_cbr_addr; | ||||
|  extern int bmips_smp_enabled; | ||||
|  extern int bmips_cpu_offset; | ||||
|  extern cpumask_t bmips_booted_mask; | ||||
| --- a/arch/mips/kernel/smp-bmips.c | ||||
| +++ b/arch/mips/kernel/smp-bmips.c | ||||
| @@ -518,7 +518,7 @@ static void bmips_set_reset_vec(int cpu, | ||||
|  		info.val = val; | ||||
|  		bmips_set_reset_vec_remote(&info); | ||||
|  	} else { | ||||
| -		void __iomem *cbr = BMIPS_GET_CBR(); | ||||
| +		void __iomem *cbr = bmips_cbr_addr; | ||||
|   | ||||
|  		if (cpu == 0) | ||||
|  			__raw_writel(val, cbr + BMIPS_RELO_VECTOR_CONTROL_0); | ||||
| @@ -591,7 +591,7 @@ asmlinkage void __weak plat_wired_tlb_se | ||||
|   | ||||
|  void bmips_cpu_setup(void) | ||||
|  { | ||||
| -	void __iomem __maybe_unused *cbr = BMIPS_GET_CBR(); | ||||
| +	void __iomem __maybe_unused *cbr = bmips_cbr_addr; | ||||
|  	u32 __maybe_unused cfg; | ||||
|   | ||||
|  	switch (current_cpu_type()) { | ||||
| @@ -1,111 +0,0 @@ | ||||
| From b95b30e50aed225d26e20737873ae2404941901c Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 20 Jun 2024 17:26:44 +0200 | ||||
| Subject: [PATCH 3/4] mips: bmips: setup: make CBR address configurable | ||||
|  | ||||
| Add support to provide CBR address from DT to handle broken | ||||
| SoC/Bootloader that doesn't correctly init it. This permits to use the | ||||
| RAC flush even in these condition. | ||||
|  | ||||
| To provide a CBR address from DT, the property "brcm,bmips-cbr-reg" | ||||
| needs to be set in the "cpus" node. On DT init, this property presence | ||||
| will be checked and will set the bmips_cbr_addr value accordingly. Also | ||||
| bmips_rac_flush_disable will be set to false as RAC flush can be | ||||
| correctly supported. | ||||
|  | ||||
| The CBR address from DT will overwrite the cached one and the | ||||
| one set in the CBR register will be ignored. | ||||
|  | ||||
| Also the DT CBR address is validated on being outside DRAM window. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Acked-by: Florian Fainelli <florian.fainelli@broadcom.com> | ||||
| Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> | ||||
| --- | ||||
|  arch/mips/bcm47xx/setup.c |  6 +++++- | ||||
|  arch/mips/bcm63xx/setup.c |  6 +++++- | ||||
|  arch/mips/bmips/setup.c   | 30 ++++++++++++++++++++++++++++-- | ||||
|  3 files changed, 38 insertions(+), 4 deletions(-) | ||||
|  | ||||
| --- a/arch/mips/bcm47xx/setup.c | ||||
| +++ b/arch/mips/bcm47xx/setup.c | ||||
| @@ -46,7 +46,11 @@ | ||||
|  #include <bcm47xx.h> | ||||
|  #include <bcm47xx_board.h> | ||||
|   | ||||
| -/* CBR addr doesn't change and we can cache it */ | ||||
| +/* | ||||
| + * CBR addr doesn't change and we can cache it. | ||||
| + * For broken SoC/Bootloader CBR addr might also be provided via DT | ||||
| + * with "brcm,bmips-cbr-reg" in the "cpus" node. | ||||
| + */ | ||||
|  void __iomem *bmips_cbr_addr __read_mostly; | ||||
|   | ||||
|  union bcm47xx_bus bcm47xx_bus; | ||||
| --- a/arch/mips/bcm63xx/setup.c | ||||
| +++ b/arch/mips/bcm63xx/setup.c | ||||
| @@ -23,7 +23,11 @@ | ||||
|  #include <bcm63xx_io.h> | ||||
|  #include <bcm63xx_gpio.h> | ||||
|   | ||||
| -/* CBR addr doesn't change and we can cache it */ | ||||
| +/* | ||||
| + * CBR addr doesn't change and we can cache it. | ||||
| + * For broken SoC/Bootloader CBR addr might also be provided via DT | ||||
| + * with "brcm,bmips-cbr-reg" in the "cpus" node. | ||||
| + */ | ||||
|  void __iomem *bmips_cbr_addr __read_mostly; | ||||
|   | ||||
|  void bcm63xx_machine_halt(void) | ||||
| --- a/arch/mips/bmips/setup.c | ||||
| +++ b/arch/mips/bmips/setup.c | ||||
| @@ -34,7 +34,11 @@ | ||||
|  #define REG_BCM6328_OTP		((void __iomem *)CKSEG1ADDR(0x1000062c)) | ||||
|  #define BCM6328_TP1_DISABLED	BIT(9) | ||||
|   | ||||
| -/* CBR addr doesn't change and we can cache it */ | ||||
| +/* | ||||
| + * CBR addr doesn't change and we can cache it. | ||||
| + * For broken SoC/Bootloader CBR addr might also be provided via DT | ||||
| + * with "brcm,bmips-cbr-reg" in the "cpus" node. | ||||
| + */ | ||||
|  void __iomem *bmips_cbr_addr __read_mostly; | ||||
|   | ||||
|  extern bool bmips_rac_flush_disable; | ||||
| @@ -208,13 +212,35 @@ void __init plat_mem_setup(void) | ||||
|  void __init device_tree_init(void) | ||||
|  { | ||||
|  	struct device_node *np; | ||||
| +	u32 addr; | ||||
|   | ||||
|  	unflatten_and_copy_device_tree(); | ||||
|   | ||||
|  	/* Disable SMP boot unless both CPUs are listed in DT and !disabled */ | ||||
|  	np = of_find_node_by_name(NULL, "cpus"); | ||||
| -	if (np && of_get_available_child_count(np) <= 1) | ||||
| +	if (!np) | ||||
| +		return; | ||||
| + | ||||
| +	if (of_get_available_child_count(np) <= 1) | ||||
|  		bmips_smp_enabled = 0; | ||||
| + | ||||
| +	/* Check if DT provide a CBR address */ | ||||
| +	if (of_property_read_u32(np, "brcm,bmips-cbr-reg", &addr)) | ||||
| +		goto exit; | ||||
| + | ||||
| +	/* Make sure CBR address is outside DRAM window */ | ||||
| +	if (addr >= (u32)memblock_start_of_DRAM() && | ||||
| +	    addr < (u32)memblock_end_of_DRAM()) { | ||||
| +		WARN(1, "DT CBR %x inside DRAM window. Ignoring DT CBR.\n", | ||||
| +		     addr); | ||||
| +		goto exit; | ||||
| +	} | ||||
| + | ||||
| +	bmips_cbr_addr = (void __iomem *)addr; | ||||
| +	/* Since CBR is provided by DT, enable RAC flush */ | ||||
| +	bmips_rac_flush_disable = false; | ||||
| + | ||||
| +exit: | ||||
|  	of_node_put(np); | ||||
|  } | ||||
|   | ||||
| @@ -1,57 +0,0 @@ | ||||
| From 04f38d1a4db017f17e82442727b91ce03dd72759 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com> | ||||
| Date: Thu, 20 Jun 2024 17:26:45 +0200 | ||||
| Subject: [PATCH 4/4] mips: bmips: enable RAC on BMIPS4350 | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| The data RAC is left disabled by the bootloader in some SoCs, at least in | ||||
| the core it boots from. | ||||
| Enabling this feature increases the performance up to +30% depending on the | ||||
| task. | ||||
|  | ||||
| Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com> | ||||
| Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> | ||||
| [ rework code and reduce code duplication ] | ||||
| Acked-by: Florian Fainelli <florian.fainelli@broadcom.com> | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> | ||||
| --- | ||||
|  arch/mips/kernel/smp-bmips.c | 18 ++++++++++++++++++ | ||||
|  1 file changed, 18 insertions(+) | ||||
|  | ||||
| --- a/arch/mips/kernel/smp-bmips.c | ||||
| +++ b/arch/mips/kernel/smp-bmips.c | ||||
| @@ -592,6 +592,7 @@ asmlinkage void __weak plat_wired_tlb_se | ||||
|  void bmips_cpu_setup(void) | ||||
|  { | ||||
|  	void __iomem __maybe_unused *cbr = bmips_cbr_addr; | ||||
| +	u32 __maybe_unused rac_addr; | ||||
|  	u32 __maybe_unused cfg; | ||||
|   | ||||
|  	switch (current_cpu_type()) { | ||||
| @@ -620,6 +621,23 @@ void bmips_cpu_setup(void) | ||||
|  		__raw_readl(cbr + BMIPS_RAC_ADDRESS_RANGE); | ||||
|  		break; | ||||
|   | ||||
| +	case CPU_BMIPS4350: | ||||
| +		rac_addr = BMIPS_RAC_CONFIG_1; | ||||
| + | ||||
| +		if (!(read_c0_brcm_cmt_local() & (1 << 31))) | ||||
| +			rac_addr = BMIPS_RAC_CONFIG; | ||||
| + | ||||
| +		/* Enable data RAC */ | ||||
| +		cfg = __raw_readl(cbr + rac_addr); | ||||
| +		__raw_writel(cfg | 0xf, cbr + rac_addr); | ||||
| +		__raw_readl(cbr + rac_addr); | ||||
| + | ||||
| +		/* Flush stale data out of the readahead cache */ | ||||
| +		cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG); | ||||
| +		__raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG); | ||||
| +		__raw_readl(cbr + BMIPS_RAC_CONFIG); | ||||
| +		break; | ||||
| + | ||||
|  	case CPU_BMIPS4380: | ||||
|  		/* CBG workaround for early BMIPS4380 CPUs */ | ||||
|  		switch (read_c0_prid()) { | ||||
| @@ -1,36 +0,0 @@ | ||||
| From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001 | ||||
| From: William Zhang <william.zhang@broadcom.com> | ||||
| Date: Thu, 22 Feb 2024 19:47:58 -0800 | ||||
| Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from | ||||
|  dts | ||||
|  | ||||
| The write protection feature is controlled by the module parameter wp_on | ||||
| with default set to enabled. But not all the board use this feature | ||||
| especially in BCMBCA broadband board. And module parameter is not | ||||
| sufficient as different board can have different option.  Add a device | ||||
| tree property and allow this feature to be configured through the board | ||||
| dts on per board basis. | ||||
|  | ||||
| Signed-off-by: William Zhang <william.zhang@broadcom.com> | ||||
| Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com> | ||||
| Reviewed-by: Kamal Dasu <kamal.dasu@broadcom.com> | ||||
| Reviewed-by: David Regan <dregan@broadcom.com> | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com | ||||
| --- | ||||
|  drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++ | ||||
|  1 file changed, 4 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c | ||||
| +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c | ||||
| @@ -3194,6 +3194,10 @@ int brcmnand_probe(struct platform_devic | ||||
|  	/* Disable XOR addressing */ | ||||
|  	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); | ||||
|   | ||||
| +	/* Check if the board connects the WP pin */ | ||||
| +	if (of_property_read_bool(dn, "brcm,wp-not-connected")) | ||||
| +		wp_on = 0; | ||||
| + | ||||
|  	if (ctrl->features & BRCMNAND_HAS_WP) { | ||||
|  		/* Permanently disable write protection */ | ||||
|  		if (wp_on == 2) | ||||
| @@ -1,123 +0,0 @@ | ||||
| From 25d88bfd35bac3196eafa666e3b05033b46ffa21 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:32:00 +0000 | ||||
| Subject: [PATCH 1/8] dt-bindings: mtd: add basic bindings for UBI | ||||
|  | ||||
| Add basic bindings for UBI devices and volumes. | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Reviewed-by: Rob Herring <robh@kernel.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  .../bindings/mtd/partitions/linux,ubi.yaml    | 65 +++++++++++++++++++ | ||||
|  .../bindings/mtd/partitions/ubi-volume.yaml   | 35 ++++++++++ | ||||
|  2 files changed, 100 insertions(+) | ||||
|  create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml | ||||
|  create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml | ||||
|  | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml | ||||
| @@ -0,0 +1,65 @@ | ||||
| +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||||
| +%YAML 1.2 | ||||
| +--- | ||||
| +$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml# | ||||
| +$schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| + | ||||
| +title: Unsorted Block Images | ||||
| + | ||||
| +description: | | ||||
| +  UBI ("Unsorted Block Images") is a volume management system for raw | ||||
| +  flash devices which manages multiple logical volumes on a single | ||||
| +  physical flash device and spreads the I/O load (i.e wear-leveling) | ||||
| +  across the whole flash chip. | ||||
| + | ||||
| +maintainers: | ||||
| +  - Daniel Golle <daniel@makrotopia.org> | ||||
| + | ||||
| +allOf: | ||||
| +  - $ref: partition.yaml# | ||||
| + | ||||
| +properties: | ||||
| +  compatible: | ||||
| +    const: linux,ubi | ||||
| + | ||||
| +  volumes: | ||||
| +    type: object | ||||
| +    description: UBI Volumes | ||||
| + | ||||
| +    patternProperties: | ||||
| +      "^ubi-volume-.*$": | ||||
| +        $ref: /schemas/mtd/partitions/ubi-volume.yaml# | ||||
| + | ||||
| +    unevaluatedProperties: false | ||||
| + | ||||
| +required: | ||||
| +  - compatible | ||||
| + | ||||
| +unevaluatedProperties: false | ||||
| + | ||||
| +examples: | ||||
| +  - | | ||||
| +    partitions { | ||||
| +        compatible = "fixed-partitions"; | ||||
| +        #address-cells = <1>; | ||||
| +        #size-cells = <1>; | ||||
| + | ||||
| +        partition@0 { | ||||
| +            reg = <0x0 0x100000>; | ||||
| +            label = "bootloader"; | ||||
| +            read-only; | ||||
| +        }; | ||||
| + | ||||
| +        partition@100000 { | ||||
| +            reg = <0x100000 0x1ff00000>; | ||||
| +            label = "ubi"; | ||||
| +            compatible = "linux,ubi"; | ||||
| + | ||||
| +            volumes { | ||||
| +                ubi-volume-caldata { | ||||
| +                    volid = <2>; | ||||
| +                    volname = "rf"; | ||||
| +                }; | ||||
| +            }; | ||||
| +        }; | ||||
| +    }; | ||||
| --- /dev/null | ||||
| +++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml | ||||
| @@ -0,0 +1,35 @@ | ||||
| +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||||
| +%YAML 1.2 | ||||
| +--- | ||||
| +$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml# | ||||
| +$schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| + | ||||
| +title: UBI volume | ||||
| + | ||||
| +description: | | ||||
| +  This binding describes a single UBI volume. Volumes can be matches either | ||||
| +  by their ID or their name, or both. | ||||
| + | ||||
| +maintainers: | ||||
| +  - Daniel Golle <daniel@makrotopia.org> | ||||
| + | ||||
| +properties: | ||||
| +  volid: | ||||
| +    $ref: /schemas/types.yaml#/definitions/uint32 | ||||
| +    description: | ||||
| +      Match UBI volume ID | ||||
| + | ||||
| +  volname: | ||||
| +    $ref: /schemas/types.yaml#/definitions/string | ||||
| +    description: | ||||
| +      Match UBI volume ID | ||||
| + | ||||
| +anyOf: | ||||
| +  - required: | ||||
| +      - volid | ||||
| + | ||||
| +  - required: | ||||
| +      - volname | ||||
| + | ||||
| +# This is a generic file other binding inherit from and extend | ||||
| +additionalProperties: true | ||||
| @@ -1,50 +0,0 @@ | ||||
| From 95b113222b5164ac0887eb5c514ff3970a0136f0 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:32:11 +0000 | ||||
| Subject: [PATCH 2/8] dt-bindings: mtd: ubi-volume: allow UBI volumes to | ||||
|  provide NVMEM | ||||
|  | ||||
| UBI volumes may be used to contain NVMEM bits, typically device MAC | ||||
| addresses or wireless radio calibration data. | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Reviewed-by: Rob Herring <robh@kernel.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  .../devicetree/bindings/mtd/partitions/linux,ubi.yaml  | 10 ++++++++++ | ||||
|  .../devicetree/bindings/mtd/partitions/ubi-volume.yaml |  5 +++++ | ||||
|  2 files changed, 15 insertions(+) | ||||
|  | ||||
| --- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml | ||||
| +++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml | ||||
| @@ -59,6 +59,16 @@ examples: | ||||
|                  ubi-volume-caldata { | ||||
|                      volid = <2>; | ||||
|                      volname = "rf"; | ||||
| + | ||||
| +                    nvmem-layout { | ||||
| +                        compatible = "fixed-layout"; | ||||
| +                        #address-cells = <1>; | ||||
| +                        #size-cells = <1>; | ||||
| + | ||||
| +                        eeprom@0 { | ||||
| +                            reg = <0x0 0x1000>; | ||||
| +                        }; | ||||
| +                    }; | ||||
|                  }; | ||||
|              }; | ||||
|          }; | ||||
| --- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml | ||||
| +++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml | ||||
| @@ -24,6 +24,11 @@ properties: | ||||
|      description: | ||||
|        Match UBI volume ID | ||||
|   | ||||
| +  nvmem-layout: | ||||
| +    $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# | ||||
| +    description: | ||||
| +      This container may reference an NVMEM layout parser. | ||||
| + | ||||
|  anyOf: | ||||
|    - required: | ||||
|        - volid | ||||
| @@ -1,285 +0,0 @@ | ||||
| From 2bba1cdcfcd2907d0696cc0139f1bd078d36ee81 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:32:35 +0000 | ||||
| Subject: [PATCH 3/8] mtd: ubi: block: use notifier to create ubiblock from | ||||
|  parameter | ||||
|  | ||||
| Use UBI_VOLUME_ADDED notification to create ubiblock device specified | ||||
| on kernel cmdline or module parameter. | ||||
| This makes thing more simple and has the advantage that ubiblock devices | ||||
| on volumes which are not present at the time the ubi module is probed | ||||
| will still be created. | ||||
|  | ||||
| Suggested-by: Zhihao Cheng <chengzhihao1@huawei.com> | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  drivers/mtd/ubi/block.c | 136 ++++++++++++++++++++-------------------- | ||||
|  drivers/mtd/ubi/kapi.c  |  54 +++++++++++----- | ||||
|  drivers/mtd/ubi/ubi.h   |   1 + | ||||
|  3 files changed, 106 insertions(+), 85 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/block.c | ||||
| +++ b/drivers/mtd/ubi/block.c | ||||
| @@ -65,10 +65,10 @@ struct ubiblock_pdu { | ||||
|  }; | ||||
|   | ||||
|  /* Numbers of elements set in the @ubiblock_param array */ | ||||
| -static int ubiblock_devs __initdata; | ||||
| +static int ubiblock_devs; | ||||
|   | ||||
|  /* MTD devices specification parameters */ | ||||
| -static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; | ||||
| +static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES]; | ||||
|   | ||||
|  struct ubiblock { | ||||
|  	struct ubi_volume_desc *desc; | ||||
| @@ -532,6 +532,70 @@ static int ubiblock_resize(struct ubi_vo | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static bool | ||||
| +match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id) | ||||
| +{ | ||||
| +	int err, len, cur_ubi_num, cur_vol_id; | ||||
| + | ||||
| +	if (ubi_num == -1) { | ||||
| +		/* No ubi num, name must be a vol device path */ | ||||
| +		err = ubi_get_num_by_path(name, &cur_ubi_num, &cur_vol_id); | ||||
| +		if (err || vi->ubi_num != cur_ubi_num || vi->vol_id != cur_vol_id) | ||||
| +			return false; | ||||
| + | ||||
| +		return true; | ||||
| +	} | ||||
| + | ||||
| +	if (vol_id == -1) { | ||||
| +		/* Got ubi_num, but no vol_id, name must be volume name */ | ||||
| +		if (vi->ubi_num != ubi_num) | ||||
| +			return false; | ||||
| + | ||||
| +		len = strnlen(name, UBI_VOL_NAME_MAX + 1); | ||||
| +		if (len < 1 || vi->name_len != len) | ||||
| +			return false; | ||||
| + | ||||
| +		if (strcmp(name, vi->name)) | ||||
| +			return false; | ||||
| + | ||||
| +		return true; | ||||
| +	} | ||||
| + | ||||
| +	if (vi->ubi_num != ubi_num) | ||||
| +		return false; | ||||
| + | ||||
| +	if (vi->vol_id != vol_id) | ||||
| +		return false; | ||||
| + | ||||
| +	return true; | ||||
| +} | ||||
| + | ||||
| +static void | ||||
| +ubiblock_create_from_param(struct ubi_volume_info *vi) | ||||
| +{ | ||||
| +	int i, ret = 0; | ||||
| +	struct ubiblock_param *p; | ||||
| + | ||||
| +	/* | ||||
| +	 * Iterate over ubiblock cmdline parameters. If a parameter matches the | ||||
| +	 * newly added volume create the ubiblock device for it. | ||||
| +	 */ | ||||
| +	for (i = 0; i < ubiblock_devs; i++) { | ||||
| +		p = &ubiblock_param[i]; | ||||
| + | ||||
| +		if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id)) | ||||
| +			continue; | ||||
| + | ||||
| +		ret = ubiblock_create(vi); | ||||
| +		if (ret) { | ||||
| +			pr_err( | ||||
| +			       "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", | ||||
| +			       vi->name, p->ubi_num, p->vol_id, ret); | ||||
| +		} | ||||
| +		break; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  static int ubiblock_notify(struct notifier_block *nb, | ||||
|  			 unsigned long notification_type, void *ns_ptr) | ||||
|  { | ||||
| @@ -539,10 +603,7 @@ static int ubiblock_notify(struct notifi | ||||
|   | ||||
|  	switch (notification_type) { | ||||
|  	case UBI_VOLUME_ADDED: | ||||
| -		/* | ||||
| -		 * We want to enforce explicit block device creation for | ||||
| -		 * volumes, so when a volume is added we do nothing. | ||||
| -		 */ | ||||
| +		ubiblock_create_from_param(&nt->vi); | ||||
|  		break; | ||||
|  	case UBI_VOLUME_REMOVED: | ||||
|  		ubiblock_remove(&nt->vi); | ||||
| @@ -568,56 +629,6 @@ static struct notifier_block ubiblock_no | ||||
|  	.notifier_call = ubiblock_notify, | ||||
|  }; | ||||
|   | ||||
| -static struct ubi_volume_desc * __init | ||||
| -open_volume_desc(const char *name, int ubi_num, int vol_id) | ||||
| -{ | ||||
| -	if (ubi_num == -1) | ||||
| -		/* No ubi num, name must be a vol device path */ | ||||
| -		return ubi_open_volume_path(name, UBI_READONLY); | ||||
| -	else if (vol_id == -1) | ||||
| -		/* No vol_id, must be vol_name */ | ||||
| -		return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); | ||||
| -	else | ||||
| -		return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); | ||||
| -} | ||||
| - | ||||
| -static void __init ubiblock_create_from_param(void) | ||||
| -{ | ||||
| -	int i, ret = 0; | ||||
| -	struct ubiblock_param *p; | ||||
| -	struct ubi_volume_desc *desc; | ||||
| -	struct ubi_volume_info vi; | ||||
| - | ||||
| -	/* | ||||
| -	 * If there is an error creating one of the ubiblocks, continue on to | ||||
| -	 * create the following ubiblocks. This helps in a circumstance where | ||||
| -	 * the kernel command-line specifies multiple block devices and some | ||||
| -	 * may be broken, but we still want the working ones to come up. | ||||
| -	 */ | ||||
| -	for (i = 0; i < ubiblock_devs; i++) { | ||||
| -		p = &ubiblock_param[i]; | ||||
| - | ||||
| -		desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); | ||||
| -		if (IS_ERR(desc)) { | ||||
| -			pr_err( | ||||
| -			       "UBI: block: can't open volume on ubi%d_%d, err=%ld\n", | ||||
| -			       p->ubi_num, p->vol_id, PTR_ERR(desc)); | ||||
| -			continue; | ||||
| -		} | ||||
| - | ||||
| -		ubi_get_volume_info(desc, &vi); | ||||
| -		ubi_close_volume(desc); | ||||
| - | ||||
| -		ret = ubiblock_create(&vi); | ||||
| -		if (ret) { | ||||
| -			pr_err( | ||||
| -			       "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", | ||||
| -			       vi.name, p->ubi_num, p->vol_id, ret); | ||||
| -			continue; | ||||
| -		} | ||||
| -	} | ||||
| -} | ||||
| - | ||||
|  static void ubiblock_remove_all(void) | ||||
|  { | ||||
|  	struct ubiblock *next; | ||||
| @@ -643,18 +654,7 @@ int __init ubiblock_init(void) | ||||
|  	if (ubiblock_major < 0) | ||||
|  		return ubiblock_major; | ||||
|   | ||||
| -	/* | ||||
| -	 * Attach block devices from 'block=' module param. | ||||
| -	 * Even if one block device in the param list fails to come up, | ||||
| -	 * still allow the module to load and leave any others up. | ||||
| -	 */ | ||||
| -	ubiblock_create_from_param(); | ||||
| - | ||||
| -	/* | ||||
| -	 * Block devices are only created upon user requests, so we ignore | ||||
| -	 * existing volumes. | ||||
| -	 */ | ||||
| -	ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); | ||||
| +	ret = ubi_register_volume_notifier(&ubiblock_notifier, 0); | ||||
|  	if (ret) | ||||
|  		goto err_unreg; | ||||
|  	return 0; | ||||
| --- a/drivers/mtd/ubi/kapi.c | ||||
| +++ b/drivers/mtd/ubi/kapi.c | ||||
| @@ -280,6 +280,41 @@ struct ubi_volume_desc *ubi_open_volume_ | ||||
|  EXPORT_SYMBOL_GPL(ubi_open_volume_nm); | ||||
|   | ||||
|  /** | ||||
| + * ubi_get_num_by_path - get UBI device and volume number from device path | ||||
| + * @pathname: volume character device node path | ||||
| + * @ubi_num: pointer to UBI device number to be set | ||||
| + * @vol_id: pointer to UBI volume ID to be set | ||||
| + * | ||||
| + * Returns 0 on success and sets ubi_num and vol_id, returns error otherwise. | ||||
| + */ | ||||
| +int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id) | ||||
| +{ | ||||
| +	int error; | ||||
| +	struct path path; | ||||
| +	struct kstat stat; | ||||
| + | ||||
| +	error = kern_path(pathname, LOOKUP_FOLLOW, &path); | ||||
| +	if (error) | ||||
| +		return error; | ||||
| + | ||||
| +	error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); | ||||
| +	path_put(&path); | ||||
| +	if (error) | ||||
| +		return error; | ||||
| + | ||||
| +	if (!S_ISCHR(stat.mode)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	*ubi_num = ubi_major2num(MAJOR(stat.rdev)); | ||||
| +	*vol_id = MINOR(stat.rdev) - 1; | ||||
| + | ||||
| +	if (*vol_id < 0 || *ubi_num < 0) | ||||
| +		return -ENODEV; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/** | ||||
|   * ubi_open_volume_path - open UBI volume by its character device node path. | ||||
|   * @pathname: volume character device node path | ||||
|   * @mode: open mode | ||||
| @@ -290,32 +325,17 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm); | ||||
|  struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode) | ||||
|  { | ||||
|  	int error, ubi_num, vol_id; | ||||
| -	struct path path; | ||||
| -	struct kstat stat; | ||||
|   | ||||
|  	dbg_gen("open volume %s, mode %d", pathname, mode); | ||||
|   | ||||
|  	if (!pathname || !*pathname) | ||||
|  		return ERR_PTR(-EINVAL); | ||||
|   | ||||
| -	error = kern_path(pathname, LOOKUP_FOLLOW, &path); | ||||
| -	if (error) | ||||
| -		return ERR_PTR(error); | ||||
| - | ||||
| -	error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); | ||||
| -	path_put(&path); | ||||
| +	error = ubi_get_num_by_path(pathname, &ubi_num, &vol_id); | ||||
|  	if (error) | ||||
|  		return ERR_PTR(error); | ||||
|   | ||||
| -	if (!S_ISCHR(stat.mode)) | ||||
| -		return ERR_PTR(-EINVAL); | ||||
| - | ||||
| -	ubi_num = ubi_major2num(MAJOR(stat.rdev)); | ||||
| -	vol_id = MINOR(stat.rdev) - 1; | ||||
| - | ||||
| -	if (vol_id >= 0 && ubi_num >= 0) | ||||
| -		return ubi_open_volume(ubi_num, vol_id, mode); | ||||
| -	return ERR_PTR(-ENODEV); | ||||
| +	return ubi_open_volume(ubi_num, vol_id, mode); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(ubi_open_volume_path); | ||||
|   | ||||
| --- a/drivers/mtd/ubi/ubi.h | ||||
| +++ b/drivers/mtd/ubi/ubi.h | ||||
| @@ -956,6 +956,7 @@ void ubi_free_internal_volumes(struct ub | ||||
|  void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di); | ||||
|  void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, | ||||
|  			    struct ubi_volume_info *vi); | ||||
| +int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id); | ||||
|  /* scan.c */ | ||||
|  int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, | ||||
|  		      int pnum, const struct ubi_vid_hdr *vid_hdr); | ||||
| @@ -1,205 +0,0 @@ | ||||
| From 6e331888643887ce85657527bc03f97d46235e71 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:33:14 +0000 | ||||
| Subject: [PATCH 4/8] mtd: ubi: attach from device tree | ||||
|  | ||||
| Introduce device tree compatible 'linux,ubi' and attach compatible MTD | ||||
| devices using the MTD add notifier. This is needed for a UBI device to | ||||
| be available early at boot (and not only after late_initcall), so | ||||
| volumes on them can be used eg. as NVMEM providers for other drivers. | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  drivers/mtd/ubi/build.c | 135 ++++++++++++++++++++++++++++------------ | ||||
|  1 file changed, 96 insertions(+), 39 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/build.c | ||||
| +++ b/drivers/mtd/ubi/build.c | ||||
| @@ -27,6 +27,7 @@ | ||||
|  #include <linux/log2.h> | ||||
|  #include <linux/kthread.h> | ||||
|  #include <linux/kernel.h> | ||||
| +#include <linux/of.h> | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/major.h> | ||||
|  #include "ubi.h" | ||||
| @@ -1214,43 +1215,43 @@ static struct mtd_info * __init open_mtd | ||||
|  	return mtd; | ||||
|  } | ||||
|   | ||||
| -static int __init ubi_init(void) | ||||
| +static void ubi_notify_add(struct mtd_info *mtd) | ||||
|  { | ||||
| -	int err, i, k; | ||||
| +	struct device_node *np = mtd_get_of_node(mtd); | ||||
| +	int err; | ||||
|   | ||||
| -	/* Ensure that EC and VID headers have correct size */ | ||||
| -	BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); | ||||
| -	BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); | ||||
| +	if (!of_device_is_compatible(np, "linux,ubi")) | ||||
| +		return; | ||||
|   | ||||
| -	if (mtd_devs > UBI_MAX_DEVICES) { | ||||
| -		pr_err("UBI error: too many MTD devices, maximum is %d\n", | ||||
| -		       UBI_MAX_DEVICES); | ||||
| -		return -EINVAL; | ||||
| -	} | ||||
| +	/* | ||||
| +	 * we are already holding &mtd_table_mutex, but still need | ||||
| +	 * to bump refcount | ||||
| +	 */ | ||||
| +	err = __get_mtd_device(mtd); | ||||
| +	if (err) | ||||
| +		return; | ||||
|   | ||||
| -	/* Create base sysfs directory and sysfs files */ | ||||
| -	err = class_register(&ubi_class); | ||||
| +	/* called while holding mtd_table_mutex */ | ||||
| +	mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING); | ||||
| +	err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false); | ||||
| +	mutex_unlock(&ubi_devices_mutex); | ||||
|  	if (err < 0) | ||||
| -		return err; | ||||
| - | ||||
| -	err = misc_register(&ubi_ctrl_cdev); | ||||
| -	if (err) { | ||||
| -		pr_err("UBI error: cannot register device\n"); | ||||
| -		goto out; | ||||
| -	} | ||||
| +		__put_mtd_device(mtd); | ||||
| +} | ||||
|   | ||||
| -	ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", | ||||
| -					      sizeof(struct ubi_wl_entry), | ||||
| -					      0, 0, NULL); | ||||
| -	if (!ubi_wl_entry_slab) { | ||||
| -		err = -ENOMEM; | ||||
| -		goto out_dev_unreg; | ||||
| -	} | ||||
| +static void ubi_notify_remove(struct mtd_info *mtd) | ||||
| +{ | ||||
| +	/* do nothing for now */ | ||||
| +} | ||||
|   | ||||
| -	err = ubi_debugfs_init(); | ||||
| -	if (err) | ||||
| -		goto out_slab; | ||||
| +static struct mtd_notifier ubi_mtd_notifier = { | ||||
| +	.add = ubi_notify_add, | ||||
| +	.remove = ubi_notify_remove, | ||||
| +}; | ||||
|   | ||||
| +static int __init ubi_init_attach(void) | ||||
| +{ | ||||
| +	int err, i, k; | ||||
|   | ||||
|  	/* Attach MTD devices */ | ||||
|  	for (i = 0; i < mtd_devs; i++) { | ||||
| @@ -1298,25 +1299,79 @@ static int __init ubi_init(void) | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +	return 0; | ||||
| + | ||||
| +out_detach: | ||||
| +	for (k = 0; k < i; k++) | ||||
| +		if (ubi_devices[k]) { | ||||
| +			mutex_lock(&ubi_devices_mutex); | ||||
| +			ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); | ||||
| +			mutex_unlock(&ubi_devices_mutex); | ||||
| +		} | ||||
| +	return err; | ||||
| +} | ||||
| +#ifndef CONFIG_MTD_UBI_MODULE | ||||
| +late_initcall(ubi_init_attach); | ||||
| +#endif | ||||
| + | ||||
| +static int __init ubi_init(void) | ||||
| +{ | ||||
| +	int err; | ||||
| + | ||||
| +	/* Ensure that EC and VID headers have correct size */ | ||||
| +	BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); | ||||
| +	BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); | ||||
| + | ||||
| +	if (mtd_devs > UBI_MAX_DEVICES) { | ||||
| +		pr_err("UBI error: too many MTD devices, maximum is %d\n", | ||||
| +		       UBI_MAX_DEVICES); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	/* Create base sysfs directory and sysfs files */ | ||||
| +	err = class_register(&ubi_class); | ||||
| +	if (err < 0) | ||||
| +		return err; | ||||
| + | ||||
| +	err = misc_register(&ubi_ctrl_cdev); | ||||
| +	if (err) { | ||||
| +		pr_err("UBI error: cannot register device\n"); | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", | ||||
| +					      sizeof(struct ubi_wl_entry), | ||||
| +					      0, 0, NULL); | ||||
| +	if (!ubi_wl_entry_slab) { | ||||
| +		err = -ENOMEM; | ||||
| +		goto out_dev_unreg; | ||||
| +	} | ||||
| + | ||||
| +	err = ubi_debugfs_init(); | ||||
| +	if (err) | ||||
| +		goto out_slab; | ||||
| + | ||||
|  	err = ubiblock_init(); | ||||
|  	if (err) { | ||||
|  		pr_err("UBI error: block: cannot initialize, error %d\n", err); | ||||
|   | ||||
|  		/* See comment above re-ubi_is_module(). */ | ||||
|  		if (ubi_is_module()) | ||||
| -			goto out_detach; | ||||
| +			goto out_slab; | ||||
| +	} | ||||
| + | ||||
| +	register_mtd_user(&ubi_mtd_notifier); | ||||
| + | ||||
| +	if (ubi_is_module()) { | ||||
| +		err = ubi_init_attach(); | ||||
| +		if (err) | ||||
| +			goto out_mtd_notifier; | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
|   | ||||
| -out_detach: | ||||
| -	for (k = 0; k < i; k++) | ||||
| -		if (ubi_devices[k]) { | ||||
| -			mutex_lock(&ubi_devices_mutex); | ||||
| -			ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); | ||||
| -			mutex_unlock(&ubi_devices_mutex); | ||||
| -		} | ||||
| -	ubi_debugfs_exit(); | ||||
| +out_mtd_notifier: | ||||
| +	unregister_mtd_user(&ubi_mtd_notifier); | ||||
|  out_slab: | ||||
|  	kmem_cache_destroy(ubi_wl_entry_slab); | ||||
|  out_dev_unreg: | ||||
| @@ -1326,13 +1381,15 @@ out: | ||||
|  	pr_err("UBI error: cannot initialize UBI, error %d\n", err); | ||||
|  	return err; | ||||
|  } | ||||
| -late_initcall(ubi_init); | ||||
| +device_initcall(ubi_init); | ||||
| + | ||||
|   | ||||
|  static void __exit ubi_exit(void) | ||||
|  { | ||||
|  	int i; | ||||
|   | ||||
|  	ubiblock_exit(); | ||||
| +	unregister_mtd_user(&ubi_mtd_notifier); | ||||
|   | ||||
|  	for (i = 0; i < UBI_MAX_DEVICES; i++) | ||||
|  		if (ubi_devices[i]) { | ||||
| @@ -1,180 +0,0 @@ | ||||
| From 924731fbed3247e3b82b8ab17db587ee28c2e781 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:33:24 +0000 | ||||
| Subject: [PATCH 5/8] mtd: ubi: introduce pre-removal notification for UBI | ||||
|  volumes | ||||
|  | ||||
| Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users | ||||
| that a volume is just about to be removed. | ||||
| This is needed because users (such as the NVMEM subsystem) expect that | ||||
| at the time their removal function is called, the parenting device is | ||||
| still available (for removal of sysfs nodes, for example, in case of | ||||
| NVMEM which otherwise WARNs on volume removal). | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  drivers/mtd/ubi/build.c | 19 ++++++++++++++----- | ||||
|  drivers/mtd/ubi/kapi.c  |  2 +- | ||||
|  drivers/mtd/ubi/ubi.h   |  2 ++ | ||||
|  drivers/mtd/ubi/vmt.c   | 17 +++++++++++++++-- | ||||
|  include/linux/mtd/ubi.h |  2 ++ | ||||
|  5 files changed, 34 insertions(+), 8 deletions(-) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/build.c | ||||
| +++ b/drivers/mtd/ubi/build.c | ||||
| @@ -91,7 +91,7 @@ static struct ubi_device *ubi_devices[UB | ||||
|  /* Serializes UBI devices creations and removals */ | ||||
|  DEFINE_MUTEX(ubi_devices_mutex); | ||||
|   | ||||
| -/* Protects @ubi_devices and @ubi->ref_count */ | ||||
| +/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */ | ||||
|  static DEFINE_SPINLOCK(ubi_devices_lock); | ||||
|   | ||||
|  /* "Show" method for files in '/<sysfs>/class/ubi/' */ | ||||
| @@ -259,6 +259,9 @@ struct ubi_device *ubi_get_device(int ub | ||||
|   | ||||
|  	spin_lock(&ubi_devices_lock); | ||||
|  	ubi = ubi_devices[ubi_num]; | ||||
| +	if (ubi && ubi->is_dead) | ||||
| +		ubi = NULL; | ||||
| + | ||||
|  	if (ubi) { | ||||
|  		ubi_assert(ubi->ref_count >= 0); | ||||
|  		ubi->ref_count += 1; | ||||
| @@ -296,7 +299,7 @@ struct ubi_device *ubi_get_by_major(int | ||||
|  	spin_lock(&ubi_devices_lock); | ||||
|  	for (i = 0; i < UBI_MAX_DEVICES; i++) { | ||||
|  		ubi = ubi_devices[i]; | ||||
| -		if (ubi && MAJOR(ubi->cdev.dev) == major) { | ||||
| +		if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { | ||||
|  			ubi_assert(ubi->ref_count >= 0); | ||||
|  			ubi->ref_count += 1; | ||||
|  			get_device(&ubi->dev); | ||||
| @@ -325,7 +328,7 @@ int ubi_major2num(int major) | ||||
|  	for (i = 0; i < UBI_MAX_DEVICES; i++) { | ||||
|  		struct ubi_device *ubi = ubi_devices[i]; | ||||
|   | ||||
| -		if (ubi && MAJOR(ubi->cdev.dev) == major) { | ||||
| +		if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { | ||||
|  			ubi_num = ubi->ubi_num; | ||||
|  			break; | ||||
|  		} | ||||
| @@ -512,7 +515,7 @@ static void ubi_free_volumes_from(struct | ||||
|  	int i; | ||||
|   | ||||
|  	for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { | ||||
| -		if (!ubi->volumes[i]) | ||||
| +		if (!ubi->volumes[i] || ubi->volumes[i]->is_dead) | ||||
|  			continue; | ||||
|  		ubi_eba_replace_table(ubi->volumes[i], NULL); | ||||
|  		ubi_fastmap_destroy_checkmap(ubi->volumes[i]); | ||||
| @@ -1094,7 +1097,6 @@ int ubi_detach_mtd_dev(int ubi_num, int | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	spin_lock(&ubi_devices_lock); | ||||
| -	put_device(&ubi->dev); | ||||
|  	ubi->ref_count -= 1; | ||||
|  	if (ubi->ref_count) { | ||||
|  		if (!anyway) { | ||||
| @@ -1105,6 +1107,13 @@ int ubi_detach_mtd_dev(int ubi_num, int | ||||
|  		ubi_err(ubi, "%s reference count %d, destroy anyway", | ||||
|  			ubi->ubi_name, ubi->ref_count); | ||||
|  	} | ||||
| +	ubi->is_dead = true; | ||||
| +	spin_unlock(&ubi_devices_lock); | ||||
| + | ||||
| +	ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL); | ||||
| + | ||||
| +	spin_lock(&ubi_devices_lock); | ||||
| +	put_device(&ubi->dev); | ||||
|  	ubi_devices[ubi_num] = NULL; | ||||
|  	spin_unlock(&ubi_devices_lock); | ||||
|   | ||||
| --- a/drivers/mtd/ubi/kapi.c | ||||
| +++ b/drivers/mtd/ubi/kapi.c | ||||
| @@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume( | ||||
|   | ||||
|  	spin_lock(&ubi->volumes_lock); | ||||
|  	vol = ubi->volumes[vol_id]; | ||||
| -	if (!vol) | ||||
| +	if (!vol || vol->is_dead) | ||||
|  		goto out_unlock; | ||||
|   | ||||
|  	err = -EBUSY; | ||||
| --- a/drivers/mtd/ubi/ubi.h | ||||
| +++ b/drivers/mtd/ubi/ubi.h | ||||
| @@ -345,6 +345,7 @@ struct ubi_volume { | ||||
|  	int writers; | ||||
|  	int exclusive; | ||||
|  	int metaonly; | ||||
| +	bool is_dead; | ||||
|   | ||||
|  	int reserved_pebs; | ||||
|  	int vol_type; | ||||
| @@ -564,6 +565,7 @@ struct ubi_device { | ||||
|  	spinlock_t volumes_lock; | ||||
|  	int ref_count; | ||||
|  	int image_seq; | ||||
| +	bool is_dead; | ||||
|   | ||||
|  	int rsvd_pebs; | ||||
|  	int avail_pebs; | ||||
| --- a/drivers/mtd/ubi/vmt.c | ||||
| +++ b/drivers/mtd/ubi/vmt.c | ||||
| @@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct | ||||
|  	struct ubi_device *ubi = vol->ubi; | ||||
|   | ||||
|  	spin_lock(&ubi->volumes_lock); | ||||
| -	if (!ubi->volumes[vol->vol_id]) { | ||||
| +	if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) { | ||||
|  		spin_unlock(&ubi->volumes_lock); | ||||
|  		return -ENODEV; | ||||
|  	} | ||||
| @@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device | ||||
|   | ||||
|  	/* Ensure that the name is unique */ | ||||
|  	for (i = 0; i < ubi->vtbl_slots; i++) | ||||
| -		if (ubi->volumes[i] && | ||||
| +		if (ubi->volumes[i] && !ubi->volumes[i]->is_dead && | ||||
|  		    ubi->volumes[i]->name_len == req->name_len && | ||||
|  		    !strcmp(ubi->volumes[i]->name, req->name)) { | ||||
|  			ubi_err(ubi, "volume \"%s\" exists (ID %d)", | ||||
| @@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_ | ||||
|  		err = -EBUSY; | ||||
|  		goto out_unlock; | ||||
|  	} | ||||
| + | ||||
| +	/* | ||||
| +	 * Mark volume as dead at this point to prevent that anyone | ||||
| +	 * can take a reference to the volume from now on. | ||||
| +	 * This is necessary as we have to release the spinlock before | ||||
| +	 * calling ubi_volume_notify. | ||||
| +	 */ | ||||
| +	vol->is_dead = true; | ||||
| +	spin_unlock(&ubi->volumes_lock); | ||||
| + | ||||
| +	ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN); | ||||
| + | ||||
| +	spin_lock(&ubi->volumes_lock); | ||||
|  	ubi->volumes[vol_id] = NULL; | ||||
|  	spin_unlock(&ubi->volumes_lock); | ||||
|   | ||||
| --- a/include/linux/mtd/ubi.h | ||||
| +++ b/include/linux/mtd/ubi.h | ||||
| @@ -192,6 +192,7 @@ struct ubi_device_info { | ||||
|   *			or a volume was removed) | ||||
|   * @UBI_VOLUME_RESIZED: a volume has been re-sized | ||||
|   * @UBI_VOLUME_RENAMED: a volume has been re-named | ||||
| + * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users | ||||
|   * @UBI_VOLUME_UPDATED: data has been written to a volume | ||||
|   * | ||||
|   * These constants define which type of event has happened when a volume | ||||
| @@ -202,6 +203,7 @@ enum { | ||||
|  	UBI_VOLUME_REMOVED, | ||||
|  	UBI_VOLUME_RESIZED, | ||||
|  	UBI_VOLUME_RENAMED, | ||||
| +	UBI_VOLUME_SHUTDOWN, | ||||
|  	UBI_VOLUME_UPDATED, | ||||
|  }; | ||||
|   | ||||
| @@ -1,66 +0,0 @@ | ||||
| From 1c54542170819e36baa43c17ca55bb3d7da89a53 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:33:38 +0000 | ||||
| Subject: [PATCH 6/8] mtd: ubi: populate ubi volume fwnode | ||||
|  | ||||
| Look for the 'volumes' subnode of an MTD partition attached to a UBI | ||||
| device and attach matching child nodes to UBI volumes. | ||||
| This allows UBI volumes to be referenced in device tree, e.g. for use | ||||
| as NVMEM providers. | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++ | ||||
|  1 file changed, 27 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/vmt.c | ||||
| +++ b/drivers/mtd/ubi/vmt.c | ||||
| @@ -124,6 +124,31 @@ static void vol_release(struct device *d | ||||
|  	kfree(vol); | ||||
|  } | ||||
|   | ||||
| +static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol) | ||||
| +{ | ||||
| +	struct fwnode_handle *fw_vols, *fw_vol; | ||||
| +	const char *volname; | ||||
| +	u32 volid; | ||||
| + | ||||
| +	fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes"); | ||||
| +	if (!fw_vols) | ||||
| +		return NULL; | ||||
| + | ||||
| +	fwnode_for_each_child_node(fw_vols, fw_vol) { | ||||
| +		if (!fwnode_property_read_string(fw_vol, "volname", &volname) && | ||||
| +		    strncmp(volname, vol->name, vol->name_len)) | ||||
| +			continue; | ||||
| + | ||||
| +		if (!fwnode_property_read_u32(fw_vol, "volid", &volid) && | ||||
| +		    vol->vol_id != volid) | ||||
| +			continue; | ||||
| + | ||||
| +		return fw_vol; | ||||
| +	} | ||||
| + | ||||
| +	return NULL; | ||||
| +} | ||||
| + | ||||
|  /** | ||||
|   * ubi_create_volume - create volume. | ||||
|   * @ubi: UBI device description object | ||||
| @@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device | ||||
|  	vol->name_len  = req->name_len; | ||||
|  	memcpy(vol->name, req->name, vol->name_len); | ||||
|  	vol->ubi = ubi; | ||||
| +	device_set_node(&vol->dev, find_volume_fwnode(vol)); | ||||
|   | ||||
|  	/* | ||||
|  	 * Finish all pending erases because there may be some LEBs belonging | ||||
| @@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub | ||||
|  	vol->dev.class = &ubi_class; | ||||
|  	vol->dev.groups = volume_dev_groups; | ||||
|  	dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); | ||||
| +	device_set_node(&vol->dev, find_volume_fwnode(vol)); | ||||
|  	err = device_register(&vol->dev); | ||||
|  	if (err) { | ||||
|  		cdev_del(&vol->cdev); | ||||
| @@ -1,244 +0,0 @@ | ||||
| From 15fc7dc926c91c871f6c0305b2938dbdeb14203b Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Tue, 19 Dec 2023 02:33:48 +0000 | ||||
| Subject: [PATCH 7/8] mtd: ubi: provide NVMEM layer over UBI volumes | ||||
|  | ||||
| In an ideal world we would like UBI to be used where ever possible on a | ||||
| NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it | ||||
| is possible to achieve an (almost-)all-UBI flash layout. Hence the need | ||||
| for a way to also use UBI volumes to store board-level constants, such | ||||
| as MAC addresses and calibration data of wireless interfaces. | ||||
|  | ||||
| Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM | ||||
| providers. Allow UBI devices to have a "volumes" firmware subnode with | ||||
| volumes which may be compatible with "nvmem-cells". | ||||
| Access to UBI volumes via the NVMEM interface at this point is | ||||
| read-only, and it is slow, opening and closing the UBI volume for each | ||||
| access due to limitations of the NVMEM provider API. | ||||
|  | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  drivers/mtd/ubi/Kconfig  |  12 +++ | ||||
|  drivers/mtd/ubi/Makefile |   1 + | ||||
|  drivers/mtd/ubi/nvmem.c  | 188 +++++++++++++++++++++++++++++++++++++++ | ||||
|  3 files changed, 201 insertions(+) | ||||
|  create mode 100644 drivers/mtd/ubi/nvmem.c | ||||
|  | ||||
| --- a/drivers/mtd/ubi/Kconfig | ||||
| +++ b/drivers/mtd/ubi/Kconfig | ||||
| @@ -104,4 +104,16 @@ config MTD_UBI_BLOCK | ||||
|   | ||||
|  	   If in doubt, say "N". | ||||
|   | ||||
| +config MTD_UBI_NVMEM | ||||
| +	tristate "UBI virtual NVMEM" | ||||
| +	default n | ||||
| +	depends on NVMEM | ||||
| +	help | ||||
| +	   This option enabled an additional driver exposing UBI volumes as NVMEM | ||||
| +	   providers, intended for platforms where UBI is part of the firmware | ||||
| +	   specification and used to store also e.g. MAC addresses or board- | ||||
| +	   specific Wi-Fi calibration data. | ||||
| + | ||||
| +	   If in doubt, say "N". | ||||
| + | ||||
|  endif # MTD_UBI | ||||
| --- a/drivers/mtd/ubi/Makefile | ||||
| +++ b/drivers/mtd/ubi/Makefile | ||||
| @@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap | ||||
|  ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o | ||||
|   | ||||
|  obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o | ||||
| +obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/mtd/ubi/nvmem.c | ||||
| @@ -0,0 +1,188 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0-or-later | ||||
| +/* | ||||
| + * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org> | ||||
| + */ | ||||
| + | ||||
| +/* UBI NVMEM provider */ | ||||
| +#include "ubi.h" | ||||
| +#include <linux/nvmem-provider.h> | ||||
| +#include <asm/div64.h> | ||||
| + | ||||
| +/* List of all NVMEM devices */ | ||||
| +static LIST_HEAD(nvmem_devices); | ||||
| +static DEFINE_MUTEX(devices_mutex); | ||||
| + | ||||
| +struct ubi_nvmem { | ||||
| +	struct nvmem_device *nvmem; | ||||
| +	int ubi_num; | ||||
| +	int vol_id; | ||||
| +	int usable_leb_size; | ||||
| +	struct list_head list; | ||||
| +}; | ||||
| + | ||||
| +static int ubi_nvmem_reg_read(void *priv, unsigned int from, | ||||
| +			      void *val, size_t bytes) | ||||
| +{ | ||||
| +	int err = 0, lnum = from, offs, bytes_left = bytes, to_read; | ||||
| +	struct ubi_nvmem *unv = priv; | ||||
| +	struct ubi_volume_desc *desc; | ||||
| + | ||||
| +	desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); | ||||
| +	if (IS_ERR(desc)) | ||||
| +		return PTR_ERR(desc); | ||||
| + | ||||
| +	offs = do_div(lnum, unv->usable_leb_size); | ||||
| +	while (bytes_left) { | ||||
| +		to_read = unv->usable_leb_size - offs; | ||||
| + | ||||
| +		if (to_read > bytes_left) | ||||
| +			to_read = bytes_left; | ||||
| + | ||||
| +		err = ubi_read(desc, lnum, val, offs, to_read); | ||||
| +		if (err) | ||||
| +			break; | ||||
| + | ||||
| +		lnum += 1; | ||||
| +		offs = 0; | ||||
| +		bytes_left -= to_read; | ||||
| +		val += to_read; | ||||
| +	} | ||||
| +	ubi_close_volume(desc); | ||||
| + | ||||
| +	if (err) | ||||
| +		return err; | ||||
| + | ||||
| +	return bytes_left == 0 ? 0 : -EIO; | ||||
| +} | ||||
| + | ||||
| +static int ubi_nvmem_add(struct ubi_volume_info *vi) | ||||
| +{ | ||||
| +	struct device_node *np = dev_of_node(vi->dev); | ||||
| +	struct nvmem_config config = {}; | ||||
| +	struct ubi_nvmem *unv; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (!np) | ||||
| +		return 0; | ||||
| + | ||||
| +	if (!of_get_child_by_name(np, "nvmem-layout")) | ||||
| +		return 0; | ||||
| + | ||||
| +	if (WARN_ON_ONCE(vi->usable_leb_size <= 0) || | ||||
| +	    WARN_ON_ONCE(vi->size <= 0)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL); | ||||
| +	if (!unv) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	config.id = NVMEM_DEVID_NONE; | ||||
| +	config.dev = vi->dev; | ||||
| +	config.name = dev_name(vi->dev); | ||||
| +	config.owner = THIS_MODULE; | ||||
| +	config.priv = unv; | ||||
| +	config.reg_read = ubi_nvmem_reg_read; | ||||
| +	config.size = vi->usable_leb_size * vi->size; | ||||
| +	config.word_size = 1; | ||||
| +	config.stride = 1; | ||||
| +	config.read_only = true; | ||||
| +	config.root_only = true; | ||||
| +	config.ignore_wp = true; | ||||
| +	config.of_node = np; | ||||
| + | ||||
| +	unv->ubi_num = vi->ubi_num; | ||||
| +	unv->vol_id = vi->vol_id; | ||||
| +	unv->usable_leb_size = vi->usable_leb_size; | ||||
| +	unv->nvmem = nvmem_register(&config); | ||||
| +	if (IS_ERR(unv->nvmem)) { | ||||
| +		ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem), | ||||
| +				    "Failed to register NVMEM device\n"); | ||||
| +		kfree(unv); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	mutex_lock(&devices_mutex); | ||||
| +	list_add_tail(&unv->list, &nvmem_devices); | ||||
| +	mutex_unlock(&devices_mutex); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void ubi_nvmem_remove(struct ubi_volume_info *vi) | ||||
| +{ | ||||
| +	struct ubi_nvmem *unv_c, *unv = NULL; | ||||
| + | ||||
| +	mutex_lock(&devices_mutex); | ||||
| +	list_for_each_entry(unv_c, &nvmem_devices, list) | ||||
| +		if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) { | ||||
| +			unv = unv_c; | ||||
| +			break; | ||||
| +		} | ||||
| + | ||||
| +	if (!unv) { | ||||
| +		mutex_unlock(&devices_mutex); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
| +	list_del(&unv->list); | ||||
| +	mutex_unlock(&devices_mutex); | ||||
| +	nvmem_unregister(unv->nvmem); | ||||
| +	kfree(unv); | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + * nvmem_notify - UBI notification handler. | ||||
| + * @nb: registered notifier block | ||||
| + * @l: notification type | ||||
| + * @ns_ptr: pointer to the &struct ubi_notification object | ||||
| + */ | ||||
| +static int nvmem_notify(struct notifier_block *nb, unsigned long l, | ||||
| +			 void *ns_ptr) | ||||
| +{ | ||||
| +	struct ubi_notification *nt = ns_ptr; | ||||
| + | ||||
| +	switch (l) { | ||||
| +	case UBI_VOLUME_RESIZED: | ||||
| +		ubi_nvmem_remove(&nt->vi); | ||||
| +		fallthrough; | ||||
| +	case UBI_VOLUME_ADDED: | ||||
| +		ubi_nvmem_add(&nt->vi); | ||||
| +		break; | ||||
| +	case UBI_VOLUME_SHUTDOWN: | ||||
| +		ubi_nvmem_remove(&nt->vi); | ||||
| +		break; | ||||
| +	default: | ||||
| +		break; | ||||
| +	} | ||||
| +	return NOTIFY_OK; | ||||
| +} | ||||
| + | ||||
| +static struct notifier_block nvmem_notifier = { | ||||
| +	.notifier_call = nvmem_notify, | ||||
| +}; | ||||
| + | ||||
| +static int __init ubi_nvmem_init(void) | ||||
| +{ | ||||
| +	return ubi_register_volume_notifier(&nvmem_notifier, 0); | ||||
| +} | ||||
| + | ||||
| +static void __exit ubi_nvmem_exit(void) | ||||
| +{ | ||||
| +	struct ubi_nvmem *unv, *tmp; | ||||
| + | ||||
| +	mutex_lock(&devices_mutex); | ||||
| +	list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) { | ||||
| +		nvmem_unregister(unv->nvmem); | ||||
| +		list_del(&unv->list); | ||||
| +		kfree(unv); | ||||
| +	} | ||||
| +	mutex_unlock(&devices_mutex); | ||||
| + | ||||
| +	ubi_unregister_volume_notifier(&nvmem_notifier); | ||||
| +} | ||||
| + | ||||
| +module_init(ubi_nvmem_init); | ||||
| +module_exit(ubi_nvmem_exit); | ||||
| +MODULE_DESCRIPTION("NVMEM layer over UBI volumes"); | ||||
| +MODULE_AUTHOR("Daniel Golle"); | ||||
| +MODULE_LICENSE("GPL"); | ||||
| @@ -1,34 +0,0 @@ | ||||
| From 04231c61dcd51db0f12061e49bb761b197109f2f Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Thu, 29 Feb 2024 03:47:24 +0000 | ||||
| Subject: [PATCH 8/8] mtd: ubi: fix NVMEM over UBI volumes on 32-bit systems | ||||
|  | ||||
| A compiler warning related to sizeof(int) != 8 when calling do_div() | ||||
| is triggered when building on 32-bit platforms. | ||||
| Address this by using integer types having a well-defined size. | ||||
|  | ||||
| Fixes: 3ce485803da1 ("mtd: ubi: provide NVMEM layer over UBI volumes") | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com> | ||||
| Tested-by: Randy Dunlap <rdunlap@infradead.org> | ||||
| Signed-off-by: Richard Weinberger <richard@nod.at> | ||||
| --- | ||||
|  drivers/mtd/ubi/nvmem.c | 5 ++++- | ||||
|  1 file changed, 4 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/mtd/ubi/nvmem.c | ||||
| +++ b/drivers/mtd/ubi/nvmem.c | ||||
| @@ -23,9 +23,12 @@ struct ubi_nvmem { | ||||
|  static int ubi_nvmem_reg_read(void *priv, unsigned int from, | ||||
|  			      void *val, size_t bytes) | ||||
|  { | ||||
| -	int err = 0, lnum = from, offs, bytes_left = bytes, to_read; | ||||
| +	size_t to_read, bytes_left = bytes; | ||||
|  	struct ubi_nvmem *unv = priv; | ||||
|  	struct ubi_volume_desc *desc; | ||||
| +	uint32_t offs; | ||||
| +	uint64_t lnum = from; | ||||
| +	int err = 0; | ||||
|   | ||||
|  	desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); | ||||
|  	if (IS_ERR(desc)) | ||||
| @@ -1,53 +0,0 @@ | ||||
| From 03cb793b26834ddca170ba87057c8f883772dd45 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 3 Oct 2024 00:11:41 +0200 | ||||
| Subject: [PATCH 1/5] block: add support for defining read-only partitions | ||||
|  | ||||
| Add support for defining read-only partitions and complete support for | ||||
| it in the cmdline partition parser as the additional "ro" after a | ||||
| partition is scanned but never actually applied. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Christoph Hellwig <hch@lst.de> | ||||
| Link: https://lore.kernel.org/r/20241002221306.4403-2-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jens Axboe <axboe@kernel.dk> | ||||
| --- | ||||
|  block/blk.h                | 1 + | ||||
|  block/partitions/cmdline.c | 3 +++ | ||||
|  block/partitions/core.c    | 3 +++ | ||||
|  3 files changed, 7 insertions(+) | ||||
|  | ||||
| --- a/block/blk.h | ||||
| +++ b/block/blk.h | ||||
| @@ -424,6 +424,7 @@ void blk_free_ext_minor(unsigned int min | ||||
|  #define ADDPART_FLAG_NONE	0 | ||||
|  #define ADDPART_FLAG_RAID	1 | ||||
|  #define ADDPART_FLAG_WHOLEDISK	2 | ||||
| +#define ADDPART_FLAG_READONLY	4 | ||||
|  int bdev_add_partition(struct gendisk *disk, int partno, sector_t start, | ||||
|  		sector_t length); | ||||
|  int bdev_del_partition(struct gendisk *disk, int partno); | ||||
| --- a/block/partitions/cmdline.c | ||||
| +++ b/block/partitions/cmdline.c | ||||
| @@ -237,6 +237,9 @@ static int add_part(int slot, struct cmd | ||||
|  	put_partition(state, slot, subpart->from >> 9, | ||||
|  		      subpart->size >> 9); | ||||
|   | ||||
| +	if (subpart->flags & PF_RDONLY) | ||||
| +		state->parts[slot].flags |= ADDPART_FLAG_READONLY; | ||||
| + | ||||
|  	info = &state->parts[slot].info; | ||||
|   | ||||
|  	strscpy(info->volname, subpart->name, sizeof(info->volname)); | ||||
| --- a/block/partitions/core.c | ||||
| +++ b/block/partitions/core.c | ||||
| @@ -392,6 +392,9 @@ static struct block_device *add_partitio | ||||
|  			goto out_del; | ||||
|  	} | ||||
|   | ||||
| +	if (flags & ADDPART_FLAG_READONLY) | ||||
| +		bdev->bd_read_only = true; | ||||
| + | ||||
|  	/* everything is up and running, commence */ | ||||
|  	err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL); | ||||
|  	if (err) | ||||
| @@ -1,94 +0,0 @@ | ||||
| From e5f587242b6072ffab4f4a084a459a59f3035873 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 3 Oct 2024 00:11:43 +0200 | ||||
| Subject: [PATCH 3/5] block: introduce add_disk_fwnode() | ||||
|  | ||||
| Introduce add_disk_fwnode() as a replacement of device_add_disk() that | ||||
| permits to pass and attach a fwnode to disk dev. | ||||
|  | ||||
| This variant can be useful for eMMC that might have the partition table | ||||
| for the disk defined in DT. A parser can later make use of the attached | ||||
| fwnode to parse the related table and init the hardcoded partition for | ||||
| the disk. | ||||
|  | ||||
| device_add_disk() is converted to a simple wrapper of add_disk_fwnode() | ||||
| with the fwnode entry set as NULL. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Christoph Hellwig <hch@lst.de> | ||||
| Link: https://lore.kernel.org/r/20241002221306.4403-4-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jens Axboe <axboe@kernel.dk> | ||||
| --- | ||||
|  block/genhd.c          | 28 ++++++++++++++++++++++++---- | ||||
|  include/linux/blkdev.h |  3 +++ | ||||
|  2 files changed, 27 insertions(+), 4 deletions(-) | ||||
|  | ||||
| --- a/block/genhd.c | ||||
| +++ b/block/genhd.c | ||||
| @@ -383,16 +383,18 @@ int disk_scan_partitions(struct gendisk | ||||
|  } | ||||
|   | ||||
|  /** | ||||
| - * device_add_disk - add disk information to kernel list | ||||
| + * add_disk_fwnode - add disk information to kernel list with fwnode | ||||
|   * @parent: parent device for the disk | ||||
|   * @disk: per-device partitioning information | ||||
|   * @groups: Additional per-device sysfs groups | ||||
| + * @fwnode: attached disk fwnode | ||||
|   * | ||||
|   * This function registers the partitioning information in @disk | ||||
| - * with the kernel. | ||||
| + * with the kernel. Also attach a fwnode to the disk device. | ||||
|   */ | ||||
| -int __must_check device_add_disk(struct device *parent, struct gendisk *disk, | ||||
| -				 const struct attribute_group **groups) | ||||
| +int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk, | ||||
| +				 const struct attribute_group **groups, | ||||
| +				 struct fwnode_handle *fwnode) | ||||
|   | ||||
|  { | ||||
|  	struct device *ddev = disk_to_dev(disk); | ||||
| @@ -451,6 +453,8 @@ int __must_check device_add_disk(struct | ||||
|  	ddev->parent = parent; | ||||
|  	ddev->groups = groups; | ||||
|  	dev_set_name(ddev, "%s", disk->disk_name); | ||||
| +	if (fwnode) | ||||
| +		device_set_node(ddev, fwnode); | ||||
|  	if (!(disk->flags & GENHD_FL_HIDDEN)) | ||||
|  		ddev->devt = MKDEV(disk->major, disk->first_minor); | ||||
|  	ret = device_add(ddev); | ||||
| @@ -552,6 +556,22 @@ out_exit_elevator: | ||||
|  		elevator_exit(disk->queue); | ||||
|  	return ret; | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(add_disk_fwnode); | ||||
| + | ||||
| +/** | ||||
| + * device_add_disk - add disk information to kernel list | ||||
| + * @parent: parent device for the disk | ||||
| + * @disk: per-device partitioning information | ||||
| + * @groups: Additional per-device sysfs groups | ||||
| + * | ||||
| + * This function registers the partitioning information in @disk | ||||
| + * with the kernel. | ||||
| + */ | ||||
| +int __must_check device_add_disk(struct device *parent, struct gendisk *disk, | ||||
| +				 const struct attribute_group **groups) | ||||
| +{ | ||||
| +	return add_disk_fwnode(parent, disk, groups, NULL); | ||||
| +} | ||||
|  EXPORT_SYMBOL(device_add_disk); | ||||
|   | ||||
|  static void blk_report_disk_dead(struct gendisk *disk, bool surprise) | ||||
| --- a/include/linux/blkdev.h | ||||
| +++ b/include/linux/blkdev.h | ||||
| @@ -741,6 +741,9 @@ static inline unsigned int blk_queue_dep | ||||
|  #define for_each_bio(_bio)		\ | ||||
|  	for (; _bio; _bio = _bio->bi_next) | ||||
|   | ||||
| +int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk, | ||||
| +				 const struct attribute_group **groups, | ||||
| +				 struct fwnode_handle *fwnode); | ||||
|  int __must_check device_add_disk(struct device *parent, struct gendisk *disk, | ||||
|  				 const struct attribute_group **groups); | ||||
|  static inline int __must_check add_disk(struct gendisk *disk) | ||||
| @@ -1,104 +0,0 @@ | ||||
| From 45ff6c340ddfc2dade74d5b7a8962c778ab7042c Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 3 Oct 2024 00:11:44 +0200 | ||||
| Subject: [PATCH 4/5] mmc: block: attach partitions fwnode if found in mmc-card | ||||
|  | ||||
| Attach partitions fwnode if found in mmc-card and register disk with it. | ||||
|  | ||||
| This permits block partition to reference the node and register a | ||||
| partition table defined in DT for the special case for embedded device | ||||
| that doesn't have a partition table flashed but have an hardcoded | ||||
| partition table passed from the system. | ||||
|  | ||||
| JEDEC BOOT partition boot0/boot1 are supported but in DT we refer with | ||||
| the JEDEC name of boot1 and boot2 to better adhere to documentation. | ||||
|  | ||||
| Also JEDEC GP partition gp0/1/2/3 are supported but in DT we refer with | ||||
| the JEDEC name of gp1/2/3/4 to better adhere to documentration. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Linus Walleij <linus.walleij@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20241002221306.4403-5-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jens Axboe <axboe@kernel.dk> | ||||
| --- | ||||
|  drivers/mmc/core/block.c | 55 +++++++++++++++++++++++++++++++++++++++- | ||||
|  1 file changed, 54 insertions(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/mmc/core/block.c | ||||
| +++ b/drivers/mmc/core/block.c | ||||
| @@ -2455,6 +2455,56 @@ static inline int mmc_blk_readonly(struc | ||||
|  	       !(card->csd.cmdclass & CCC_BLOCK_WRITE); | ||||
|  } | ||||
|   | ||||
| +/* | ||||
| + * Search for a declared partitions node for the disk in mmc-card related node. | ||||
| + * | ||||
| + * This is to permit support for partition table defined in DT in special case | ||||
| + * where a partition table is not written in the disk and is expected to be | ||||
| + * passed from the running system. | ||||
| + * | ||||
| + * For the user disk, "partitions" node is searched. | ||||
| + * For the special HW disk, "partitions-" node with the appended name is used | ||||
| + * following this conversion table (to adhere to JEDEC naming) | ||||
| + * - boot0 -> partitions-boot1 | ||||
| + * - boot1 -> partitions-boot2 | ||||
| + * - gp0 -> partitions-gp1 | ||||
| + * - gp1 -> partitions-gp2 | ||||
| + * - gp2 -> partitions-gp3 | ||||
| + * - gp3 -> partitions-gp4 | ||||
| + */ | ||||
| +static struct fwnode_handle *mmc_blk_get_partitions_node(struct device *mmc_dev, | ||||
| +							 const char *subname) | ||||
| +{ | ||||
| +	const char *node_name = "partitions"; | ||||
| + | ||||
| +	if (subname) { | ||||
| +		mmc_dev = mmc_dev->parent; | ||||
| + | ||||
| +		/* | ||||
| +		 * Check if we are allocating a BOOT disk boot0/1 disk. | ||||
| +		 * In DT we use the JEDEC naming boot1/2. | ||||
| +		 */ | ||||
| +		if (!strcmp(subname, "boot0")) | ||||
| +			node_name = "partitions-boot1"; | ||||
| +		if (!strcmp(subname, "boot1")) | ||||
| +			node_name = "partitions-boot2"; | ||||
| +		/* | ||||
| +		 * Check if we are allocating a GP disk gp0/1/2/3 disk. | ||||
| +		 * In DT we use the JEDEC naming gp1/2/3/4. | ||||
| +		 */ | ||||
| +		if (!strcmp(subname, "gp0")) | ||||
| +			node_name = "partitions-gp1"; | ||||
| +		if (!strcmp(subname, "gp1")) | ||||
| +			node_name = "partitions-gp2"; | ||||
| +		if (!strcmp(subname, "gp2")) | ||||
| +			node_name = "partitions-gp3"; | ||||
| +		if (!strcmp(subname, "gp3")) | ||||
| +			node_name = "partitions-gp4"; | ||||
| +	} | ||||
| + | ||||
| +	return device_get_named_child_node(mmc_dev, node_name); | ||||
| +} | ||||
| + | ||||
|  static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, | ||||
|  					      struct device *parent, | ||||
|  					      sector_t size, | ||||
| @@ -2463,6 +2513,7 @@ static struct mmc_blk_data *mmc_blk_allo | ||||
|  					      int area_type, | ||||
|  					      unsigned int part_type) | ||||
|  { | ||||
| +	struct fwnode_handle *disk_fwnode; | ||||
|  	struct mmc_blk_data *md; | ||||
|  	int devidx, ret; | ||||
|  	char cap_str[10]; | ||||
| @@ -2568,7 +2619,9 @@ static struct mmc_blk_data *mmc_blk_allo | ||||
|  	/* used in ->open, must be set before add_disk: */ | ||||
|  	if (area_type == MMC_BLK_DATA_AREA_MAIN) | ||||
|  		dev_set_drvdata(&card->dev, md); | ||||
| -	ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups); | ||||
| +	disk_fwnode = mmc_blk_get_partitions_node(parent, subname); | ||||
| +	ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups, | ||||
| +			      disk_fwnode); | ||||
|  	if (ret) | ||||
|  		goto err_put_disk; | ||||
|  	return md; | ||||
| @@ -1,200 +0,0 @@ | ||||
| From 884555b557e5e6d41c866e2cd8d7b32f50ec974b Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 3 Oct 2024 00:11:45 +0200 | ||||
| Subject: [PATCH 5/5] block: add support for partition table defined in OF | ||||
|  | ||||
| Add support for partition table defined in Device Tree. Similar to how | ||||
| it's done with MTD, add support for defining a fixed partition table in | ||||
| device tree. | ||||
|  | ||||
| A common scenario for this is fixed block (eMMC) embedded devices that | ||||
| have no MBR or GPT partition table to save storage space. Bootloader | ||||
| access the block device with absolute address of data. | ||||
|  | ||||
| This is to complete the functionality with an equivalent implementation | ||||
| with providing partition table with bootargs, for case where the booargs | ||||
| can't be modified and tweaking the Device Tree is the only solution to | ||||
| have an usabe partition table. | ||||
|  | ||||
| The implementation follow the fixed-partitions parser used on MTD | ||||
| devices where a "partitions" node is expected to be declared with | ||||
| "fixed-partitions" compatible in the OF node of the disk device | ||||
| (mmc-card for eMMC for example) and each child node declare a label | ||||
| and a reg with offset and size. If label is not declared, the node name | ||||
| is used as fallback. Eventually is also possible to declare the read-only | ||||
| property to flag the partition as read-only. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Christoph Hellwig <hch@lst.de> | ||||
| Link: https://lore.kernel.org/r/20241002221306.4403-6-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jens Axboe <axboe@kernel.dk> | ||||
| --- | ||||
|  block/partitions/Kconfig  |   9 ++++ | ||||
|  block/partitions/Makefile |   1 + | ||||
|  block/partitions/check.h  |   1 + | ||||
|  block/partitions/core.c   |   3 ++ | ||||
|  block/partitions/of.c     | 110 ++++++++++++++++++++++++++++++++++++++ | ||||
|  5 files changed, 124 insertions(+) | ||||
|  create mode 100644 block/partitions/of.c | ||||
|  | ||||
| --- a/block/partitions/Kconfig | ||||
| +++ b/block/partitions/Kconfig | ||||
| @@ -270,4 +270,13 @@ config CMDLINE_PARTITION | ||||
|  	  Say Y here if you want to read the partition table from bootargs. | ||||
|  	  The format for the command line is just like mtdparts. | ||||
|   | ||||
| +config OF_PARTITION | ||||
| +	bool "Device Tree partition support" if PARTITION_ADVANCED | ||||
| +	depends on OF | ||||
| +	help | ||||
| +	  Say Y here if you want to enable support for partition table | ||||
| +	  defined in Device Tree. (mainly for eMMC) | ||||
| +	  The format for the device tree node is just like MTD fixed-partition | ||||
| +	  schema. | ||||
| + | ||||
|  endmenu | ||||
| --- a/block/partitions/Makefile | ||||
| +++ b/block/partitions/Makefile | ||||
| @@ -12,6 +12,7 @@ obj-$(CONFIG_CMDLINE_PARTITION) += cmdli | ||||
|  obj-$(CONFIG_MAC_PARTITION) += mac.o | ||||
|  obj-$(CONFIG_LDM_PARTITION) += ldm.o | ||||
|  obj-$(CONFIG_MSDOS_PARTITION) += msdos.o | ||||
| +obj-$(CONFIG_OF_PARTITION) += of.o | ||||
|  obj-$(CONFIG_OSF_PARTITION) += osf.o | ||||
|  obj-$(CONFIG_SGI_PARTITION) += sgi.o | ||||
|  obj-$(CONFIG_SUN_PARTITION) += sun.o | ||||
| --- a/block/partitions/check.h | ||||
| +++ b/block/partitions/check.h | ||||
| @@ -62,6 +62,7 @@ int karma_partition(struct parsed_partit | ||||
|  int ldm_partition(struct parsed_partitions *state); | ||||
|  int mac_partition(struct parsed_partitions *state); | ||||
|  int msdos_partition(struct parsed_partitions *state); | ||||
| +int of_partition(struct parsed_partitions *state); | ||||
|  int osf_partition(struct parsed_partitions *state); | ||||
|  int sgi_partition(struct parsed_partitions *state); | ||||
|  int sun_partition(struct parsed_partitions *state); | ||||
| --- a/block/partitions/core.c | ||||
| +++ b/block/partitions/core.c | ||||
| @@ -43,6 +43,9 @@ static int (*const check_part[])(struct | ||||
|  #ifdef CONFIG_CMDLINE_PARTITION | ||||
|  	cmdline_partition, | ||||
|  #endif | ||||
| +#ifdef CONFIG_OF_PARTITION | ||||
| +	of_partition,		/* cmdline have priority to OF */ | ||||
| +#endif | ||||
|  #ifdef CONFIG_EFI_PARTITION | ||||
|  	efi_partition,		/* this must come before msdos */ | ||||
|  #endif | ||||
| --- /dev/null | ||||
| +++ b/block/partitions/of.c | ||||
| @@ -0,0 +1,110 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0 | ||||
| + | ||||
| +#include <linux/blkdev.h> | ||||
| +#include <linux/major.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/string.h> | ||||
| +#include "check.h" | ||||
| + | ||||
| +static int validate_of_partition(struct device_node *np, int slot) | ||||
| +{ | ||||
| +	u64 offset, size; | ||||
| +	int len; | ||||
| + | ||||
| +	const __be32 *reg = of_get_property(np, "reg", &len); | ||||
| +	int a_cells = of_n_addr_cells(np); | ||||
| +	int s_cells = of_n_size_cells(np); | ||||
| + | ||||
| +	/* Make sure reg len match the expected addr and size cells */ | ||||
| +	if (len / sizeof(*reg) != a_cells + s_cells) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	/* Validate offset conversion from bytes to sectors */ | ||||
| +	offset = of_read_number(reg, a_cells); | ||||
| +	if (offset % SECTOR_SIZE) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	/* Validate size conversion from bytes to sectors */ | ||||
| +	size = of_read_number(reg + a_cells, s_cells); | ||||
| +	if (!size || size % SECTOR_SIZE) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static void add_of_partition(struct parsed_partitions *state, int slot, | ||||
| +			     struct device_node *np) | ||||
| +{ | ||||
| +	struct partition_meta_info *info; | ||||
| +	char tmp[sizeof(info->volname) + 4]; | ||||
| +	const char *partname; | ||||
| +	int len; | ||||
| + | ||||
| +	const __be32 *reg = of_get_property(np, "reg", &len); | ||||
| +	int a_cells = of_n_addr_cells(np); | ||||
| +	int s_cells = of_n_size_cells(np); | ||||
| + | ||||
| +	/* Convert bytes to sector size */ | ||||
| +	u64 offset = of_read_number(reg, a_cells) / SECTOR_SIZE; | ||||
| +	u64 size = of_read_number(reg + a_cells, s_cells) / SECTOR_SIZE; | ||||
| + | ||||
| +	put_partition(state, slot, offset, size); | ||||
| + | ||||
| +	if (of_property_read_bool(np, "read-only")) | ||||
| +		state->parts[slot].flags |= ADDPART_FLAG_READONLY; | ||||
| + | ||||
| +	/* | ||||
| +	 * Follow MTD label logic, search for label property, | ||||
| +	 * fallback to node name if not found. | ||||
| +	 */ | ||||
| +	info = &state->parts[slot].info; | ||||
| +	partname = of_get_property(np, "label", &len); | ||||
| +	if (!partname) | ||||
| +		partname = of_get_property(np, "name", &len); | ||||
| +	strscpy(info->volname, partname, sizeof(info->volname)); | ||||
| + | ||||
| +	snprintf(tmp, sizeof(tmp), "(%s)", info->volname); | ||||
| +	strlcat(state->pp_buf, tmp, PAGE_SIZE); | ||||
| +} | ||||
| + | ||||
| +int of_partition(struct parsed_partitions *state) | ||||
| +{ | ||||
| +	struct device *ddev = disk_to_dev(state->disk); | ||||
| +	struct device_node *np; | ||||
| +	int slot; | ||||
| + | ||||
| +	struct device_node *partitions_np = of_node_get(ddev->of_node); | ||||
| + | ||||
| +	if (!partitions_np || | ||||
| +	    !of_device_is_compatible(partitions_np, "fixed-partitions")) | ||||
| +		return 0; | ||||
| + | ||||
| +	slot = 1; | ||||
| +	/* Validate parition offset and size */ | ||||
| +	for_each_child_of_node(partitions_np, np) { | ||||
| +		if (validate_of_partition(np, slot)) { | ||||
| +			of_node_put(np); | ||||
| +			of_node_put(partitions_np); | ||||
| + | ||||
| +			return -1; | ||||
| +		} | ||||
| + | ||||
| +		slot++; | ||||
| +	} | ||||
| + | ||||
| +	slot = 1; | ||||
| +	for_each_child_of_node(partitions_np, np) { | ||||
| +		if (slot >= state->limit) { | ||||
| +			of_node_put(np); | ||||
| +			break; | ||||
| +		} | ||||
| + | ||||
| +		add_of_partition(state, slot, np); | ||||
| + | ||||
| +		slot++; | ||||
| +	} | ||||
| + | ||||
| +	strlcat(state->pp_buf, "\n", PAGE_SIZE); | ||||
| + | ||||
| +	return 1; | ||||
| +} | ||||
| @@ -1,146 +0,0 @@ | ||||
| From 49c8e854869d673df8452f24dfa8989cd0f615a8 Mon Sep 17 00:00:00 2001 | ||||
| From: Martin Kurbanov <mmkurbanov@salutedevices.com> | ||||
| Date: Mon, 2 Oct 2023 17:04:58 +0300 | ||||
| Subject: [PATCH] mtd: spinand: add support for FORESEE F35SQA002G | ||||
|  | ||||
| Add support for FORESEE F35SQA002G SPI NAND. | ||||
| Datasheet: | ||||
|   https://www.longsys.com/uploads/LM-00006FORESEEF35SQA002GDatasheet_1650183701.pdf | ||||
|  | ||||
| Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com> | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Link: https://lore.kernel.org/linux-mtd/20231002140458.147605-1-mmkurbanov@salutedevices.com | ||||
| --- | ||||
|  drivers/mtd/nand/spi/Makefile  |  2 +- | ||||
|  drivers/mtd/nand/spi/core.c    |  1 + | ||||
|  drivers/mtd/nand/spi/foresee.c | 95 ++++++++++++++++++++++++++++++++++ | ||||
|  include/linux/mtd/spinand.h    |  1 + | ||||
|  4 files changed, 98 insertions(+), 1 deletion(-) | ||||
|  create mode 100644 drivers/mtd/nand/spi/foresee.c | ||||
|  | ||||
| --- a/drivers/mtd/nand/spi/Makefile | ||||
| +++ b/drivers/mtd/nand/spi/Makefile | ||||
| @@ -1,4 +1,4 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
| -spinand-objs := core.o alliancememory.o ato.o esmt.o gigadevice.o macronix.o | ||||
| +spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o | ||||
|  spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o | ||||
|  obj-$(CONFIG_MTD_SPI_NAND) += spinand.o | ||||
| --- a/drivers/mtd/nand/spi/core.c | ||||
| +++ b/drivers/mtd/nand/spi/core.c | ||||
| @@ -940,6 +940,7 @@ static const struct spinand_manufacturer | ||||
|  	&alliancememory_spinand_manufacturer, | ||||
|  	&ato_spinand_manufacturer, | ||||
|  	&esmt_c8_spinand_manufacturer, | ||||
| +	&foresee_spinand_manufacturer, | ||||
|  	&gigadevice_spinand_manufacturer, | ||||
|  	¯onix_spinand_manufacturer, | ||||
|  	µn_spinand_manufacturer, | ||||
| --- /dev/null | ||||
| +++ b/drivers/mtd/nand/spi/foresee.c | ||||
| @@ -0,0 +1,95 @@ | ||||
| +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) | ||||
| +/* | ||||
| + * Copyright (c) 2023, SberDevices. All Rights Reserved. | ||||
| + * | ||||
| + * Author: Martin Kurbanov <mmkurbanov@salutedevices.com> | ||||
| + */ | ||||
| + | ||||
| +#include <linux/device.h> | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/mtd/spinand.h> | ||||
| + | ||||
| +#define SPINAND_MFR_FORESEE		0xCD | ||||
| + | ||||
| +static SPINAND_OP_VARIANTS(read_cache_variants, | ||||
| +		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), | ||||
| +		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), | ||||
| +		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), | ||||
| +		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); | ||||
| + | ||||
| +static SPINAND_OP_VARIANTS(write_cache_variants, | ||||
| +		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), | ||||
| +		SPINAND_PROG_LOAD(true, 0, NULL, 0)); | ||||
| + | ||||
| +static SPINAND_OP_VARIANTS(update_cache_variants, | ||||
| +		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), | ||||
| +		SPINAND_PROG_LOAD(false, 0, NULL, 0)); | ||||
| + | ||||
| +static int f35sqa002g_ooblayout_ecc(struct mtd_info *mtd, int section, | ||||
| +				    struct mtd_oob_region *region) | ||||
| +{ | ||||
| +	return -ERANGE; | ||||
| +} | ||||
| + | ||||
| +static int f35sqa002g_ooblayout_free(struct mtd_info *mtd, int section, | ||||
| +				     struct mtd_oob_region *region) | ||||
| +{ | ||||
| +	if (section) | ||||
| +		return -ERANGE; | ||||
| + | ||||
| +	/* Reserve 2 bytes for the BBM. */ | ||||
| +	region->offset = 2; | ||||
| +	region->length = 62; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct mtd_ooblayout_ops f35sqa002g_ooblayout = { | ||||
| +	.ecc = f35sqa002g_ooblayout_ecc, | ||||
| +	.free = f35sqa002g_ooblayout_free, | ||||
| +}; | ||||
| + | ||||
| +static int f35sqa002g_ecc_get_status(struct spinand_device *spinand, u8 status) | ||||
| +{ | ||||
| +	struct nand_device *nand = spinand_to_nand(spinand); | ||||
| + | ||||
| +	switch (status & STATUS_ECC_MASK) { | ||||
| +	case STATUS_ECC_NO_BITFLIPS: | ||||
| +		return 0; | ||||
| + | ||||
| +	case STATUS_ECC_HAS_BITFLIPS: | ||||
| +		return nanddev_get_ecc_conf(nand)->strength; | ||||
| + | ||||
| +	default: | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	/* More than 1-bit error was detected in one or more sectors and | ||||
| +	 * cannot be corrected. | ||||
| +	 */ | ||||
| +	return -EBADMSG; | ||||
| +} | ||||
| + | ||||
| +static const struct spinand_info foresee_spinand_table[] = { | ||||
| +	SPINAND_INFO("F35SQA002G", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72, 0x72), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(1, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&f35sqa002g_ooblayout, | ||||
| +				     f35sqa002g_ecc_get_status)), | ||||
| +}; | ||||
| + | ||||
| +static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = { | ||||
| +}; | ||||
| + | ||||
| +const struct spinand_manufacturer foresee_spinand_manufacturer = { | ||||
| +	.id = SPINAND_MFR_FORESEE, | ||||
| +	.name = "FORESEE", | ||||
| +	.chips = foresee_spinand_table, | ||||
| +	.nchips = ARRAY_SIZE(foresee_spinand_table), | ||||
| +	.ops = &foresee_spinand_manuf_ops, | ||||
| +}; | ||||
| --- a/include/linux/mtd/spinand.h | ||||
| +++ b/include/linux/mtd/spinand.h | ||||
| @@ -263,6 +263,7 @@ struct spinand_manufacturer { | ||||
|  extern const struct spinand_manufacturer alliancememory_spinand_manufacturer; | ||||
|  extern const struct spinand_manufacturer ato_spinand_manufacturer; | ||||
|  extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; | ||||
| +extern const struct spinand_manufacturer foresee_spinand_manufacturer; | ||||
|  extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; | ||||
|  extern const struct spinand_manufacturer macronix_spinand_manufacturer; | ||||
|  extern const struct spinand_manufacturer micron_spinand_manufacturer; | ||||
| @@ -1,38 +0,0 @@ | ||||
| From ae461cde5c559675fc4c0ba351c7c31ace705f56 Mon Sep 17 00:00:00 2001 | ||||
| From: Bohdan Chubuk <chbgdn@gmail.com> | ||||
| Date: Sun, 10 Nov 2024 22:50:47 +0200 | ||||
| Subject: [PATCH] mtd: spinand: add support for FORESEE F35SQA001G | ||||
|  | ||||
| Add support for FORESEE F35SQA001G SPI NAND. | ||||
|  | ||||
| Similar to F35SQA002G, but differs in capacity. | ||||
| Datasheet: | ||||
|   -  https://cdn.ozdisan.com/ETicaret_Dosya/704795_871495.pdf | ||||
|  | ||||
| Tested on Xiaomi AX3000T flashed with OpenWRT. | ||||
|  | ||||
| Signed-off-by: Bohdan Chubuk <chbgdn@gmail.com> | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| --- | ||||
|  drivers/mtd/nand/spi/foresee.c | 10 ++++++++++ | ||||
|  1 file changed, 10 insertions(+) | ||||
|  | ||||
| --- a/drivers/mtd/nand/spi/foresee.c | ||||
| +++ b/drivers/mtd/nand/spi/foresee.c | ||||
| @@ -81,6 +81,16 @@ static const struct spinand_info foresee | ||||
|  		     SPINAND_HAS_QE_BIT, | ||||
|  		     SPINAND_ECCINFO(&f35sqa002g_ooblayout, | ||||
|  				     f35sqa002g_ecc_get_status)), | ||||
| +	SPINAND_INFO("F35SQA001G", | ||||
| +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x71, 0x71), | ||||
| +		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), | ||||
| +		     NAND_ECCREQ(1, 512), | ||||
| +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants, | ||||
| +					      &write_cache_variants, | ||||
| +					      &update_cache_variants), | ||||
| +		     SPINAND_HAS_QE_BIT, | ||||
| +		     SPINAND_ECCINFO(&f35sqa002g_ooblayout, | ||||
| +				     f35sqa002g_ecc_get_status)), | ||||
|  }; | ||||
|   | ||||
|  static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = { | ||||
| @@ -1,75 +0,0 @@ | ||||
| From 56364c910691f6d10ba88c964c9041b9ab777bd6 Mon Sep 17 00:00:00 2001 | ||||
| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Date: Mon, 25 Mar 2024 08:40:28 +0100 | ||||
| Subject: [PATCH 1/4] net: Remove conditional threaded-NAPI wakeup based on | ||||
|  task state. | ||||
|  | ||||
| A NAPI thread is scheduled by first setting NAPI_STATE_SCHED bit. If | ||||
| successful (the bit was not yet set) then the NAPI_STATE_SCHED_THREADED | ||||
| is set but only if thread's state is not TASK_INTERRUPTIBLE (is | ||||
| TASK_RUNNING) followed by task wakeup. | ||||
|  | ||||
| If the task is idle (TASK_INTERRUPTIBLE) then the | ||||
| NAPI_STATE_SCHED_THREADED bit is not set. The thread is no relying on | ||||
| the bit but always leaving the wait-loop after returning from schedule() | ||||
| because there must have been a wakeup. | ||||
|  | ||||
| The smpboot-threads implementation for per-CPU threads requires an | ||||
| explicit condition and does not support "if we get out of schedule() | ||||
| then there must be something to do". | ||||
|  | ||||
| Removing this optimisation simplifies the following integration. | ||||
|  | ||||
| Set NAPI_STATE_SCHED_THREADED unconditionally on wakeup and rely on it | ||||
| in the wait path by removing the `woken' condition. | ||||
|  | ||||
| Acked-by: Jakub Kicinski <kuba@kernel.org> | ||||
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  net/core/dev.c | 14 ++------------ | ||||
|  1 file changed, 2 insertions(+), 12 deletions(-) | ||||
|  | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -4514,13 +4514,7 @@ static inline void ____napi_schedule(str | ||||
|  		 */ | ||||
|  		thread = READ_ONCE(napi->thread); | ||||
|  		if (thread) { | ||||
| -			/* Avoid doing set_bit() if the thread is in | ||||
| -			 * INTERRUPTIBLE state, cause napi_thread_wait() | ||||
| -			 * makes sure to proceed with napi polling | ||||
| -			 * if the thread is explicitly woken from here. | ||||
| -			 */ | ||||
| -			if (READ_ONCE(thread->__state) != TASK_INTERRUPTIBLE) | ||||
| -				set_bit(NAPI_STATE_SCHED_THREADED, &napi->state); | ||||
| +			set_bit(NAPI_STATE_SCHED_THREADED, &napi->state); | ||||
|  			wake_up_process(thread); | ||||
|  			return; | ||||
|  		} | ||||
| @@ -6676,8 +6670,6 @@ static int napi_poll(struct napi_struct | ||||
|   | ||||
|  static int napi_thread_wait(struct napi_struct *napi) | ||||
|  { | ||||
| -	bool woken = false; | ||||
| - | ||||
|  	set_current_state(TASK_INTERRUPTIBLE); | ||||
|   | ||||
|  	while (!kthread_should_stop()) { | ||||
| @@ -6686,15 +6678,13 @@ static int napi_thread_wait(struct napi_ | ||||
|  		 * Testing SCHED bit is not enough because SCHED bit might be | ||||
|  		 * set by some other busy poll thread or by napi_disable(). | ||||
|  		 */ | ||||
| -		if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state) || woken) { | ||||
| +		if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state)) { | ||||
|  			WARN_ON(!list_empty(&napi->poll_list)); | ||||
|  			__set_current_state(TASK_RUNNING); | ||||
|  			return 0; | ||||
|  		} | ||||
|   | ||||
|  		schedule(); | ||||
| -		/* woken being true indicates this thread owns this napi. */ | ||||
| -		woken = true; | ||||
|  		set_current_state(TASK_INTERRUPTIBLE); | ||||
|  	} | ||||
|  	__set_current_state(TASK_RUNNING); | ||||
| @@ -1,330 +0,0 @@ | ||||
| From dad6b97702639fba27a2bd3e986982ad6f0db3a7 Mon Sep 17 00:00:00 2001 | ||||
| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Date: Mon, 25 Mar 2024 08:40:29 +0100 | ||||
| Subject: [PATCH 2/4] net: Allow to use SMP threads for backlog NAPI. | ||||
|  | ||||
| Backlog NAPI is a per-CPU NAPI struct only (with no device behind it) | ||||
| used by drivers which don't do NAPI them self, RPS and parts of the | ||||
| stack which need to avoid recursive deadlocks while processing a packet. | ||||
|  | ||||
| The non-NAPI driver use the CPU local backlog NAPI. If RPS is enabled | ||||
| then a flow for the skb is computed and based on the flow the skb can be | ||||
| enqueued on a remote CPU. Scheduling/ raising the softirq (for backlog's | ||||
| NAPI) on the remote CPU isn't trivial because the softirq is only | ||||
| scheduled on the local CPU and performed after the hardirq is done. | ||||
| In order to schedule a softirq on the remote CPU, an IPI is sent to the | ||||
| remote CPU which schedules the backlog-NAPI on the then local CPU. | ||||
|  | ||||
| On PREEMPT_RT interrupts are force-threaded. The soft interrupts are | ||||
| raised within the interrupt thread and processed after the interrupt | ||||
| handler completed still within the context of the interrupt thread. The | ||||
| softirq is handled in the context where it originated. | ||||
|  | ||||
| With force-threaded interrupts enabled, ksoftirqd is woken up if a | ||||
| softirq is raised from hardirq context. This is the case if it is raised | ||||
| from an IPI. Additionally there is a warning on PREEMPT_RT if the | ||||
| softirq is raised from the idle thread. | ||||
| This was done for two reasons: | ||||
| - With threaded interrupts the processing should happen in thread | ||||
|   context (where it originated) and ksoftirqd is the only thread for | ||||
|   this context if raised from hardirq. Using the currently running task | ||||
|   instead would "punish" a random task. | ||||
| - Once ksoftirqd is active it consumes all further softirqs until it | ||||
|   stops running. This changed recently and is no longer the case. | ||||
|  | ||||
| Instead of keeping the backlog NAPI in ksoftirqd (in force-threaded/ | ||||
| PREEMPT_RT setups) I am proposing NAPI-threads for backlog. | ||||
| The "proper" setup with threaded-NAPI is not doable because the threads | ||||
| are not pinned to an individual CPU and can be modified by the user. | ||||
| Additionally a dummy network device would have to be assigned. Also | ||||
| CPU-hotplug has to be considered if additional CPUs show up. | ||||
| All this can be probably done/ solved but the smpboot-threads already | ||||
| provide this infrastructure. | ||||
|  | ||||
| Sending UDP packets over loopback expects that the packet is processed | ||||
| within the call. Delaying it by handing it over to the thread hurts | ||||
| performance. It is not beneficial to the outcome if the context switch | ||||
| happens immediately after enqueue or after a while to process a few | ||||
| packets in a batch. | ||||
| There is no need to always use the thread if the backlog NAPI is | ||||
| requested on the local CPU. This restores the loopback throuput. The | ||||
| performance drops mostly to the same value after enabling RPS on the | ||||
| loopback comparing the IPI and the tread result. | ||||
|  | ||||
| Create NAPI-threads for backlog if request during boot. The thread runs | ||||
| the inner loop from napi_threaded_poll(), the wait part is different. It | ||||
| checks for NAPI_STATE_SCHED (the backlog NAPI can not be disabled). | ||||
|  | ||||
| The NAPI threads for backlog are optional, it has to be enabled via the boot | ||||
| argument "thread_backlog_napi". It is mandatory for PREEMPT_RT to avoid the | ||||
| wakeup of ksoftirqd from the IPI. | ||||
|  | ||||
| Acked-by: Jakub Kicinski <kuba@kernel.org> | ||||
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  net/core/dev.c | 148 +++++++++++++++++++++++++++++++++++++------------ | ||||
|  1 file changed, 113 insertions(+), 35 deletions(-) | ||||
|  | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -78,6 +78,7 @@ | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/sched.h> | ||||
|  #include <linux/sched/mm.h> | ||||
| +#include <linux/smpboot.h> | ||||
|  #include <linux/mutex.h> | ||||
|  #include <linux/rwsem.h> | ||||
|  #include <linux/string.h> | ||||
| @@ -217,6 +218,31 @@ static inline struct hlist_head *dev_ind | ||||
|  	return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)]; | ||||
|  } | ||||
|   | ||||
| +#ifndef CONFIG_PREEMPT_RT | ||||
| + | ||||
| +static DEFINE_STATIC_KEY_FALSE(use_backlog_threads_key); | ||||
| + | ||||
| +static int __init setup_backlog_napi_threads(char *arg) | ||||
| +{ | ||||
| +	static_branch_enable(&use_backlog_threads_key); | ||||
| +	return 0; | ||||
| +} | ||||
| +early_param("thread_backlog_napi", setup_backlog_napi_threads); | ||||
| + | ||||
| +static bool use_backlog_threads(void) | ||||
| +{ | ||||
| +	return static_branch_unlikely(&use_backlog_threads_key); | ||||
| +} | ||||
| + | ||||
| +#else | ||||
| + | ||||
| +static bool use_backlog_threads(void) | ||||
| +{ | ||||
| +	return true; | ||||
| +} | ||||
| + | ||||
| +#endif | ||||
| + | ||||
|  static inline void rps_lock_irqsave(struct softnet_data *sd, | ||||
|  				    unsigned long *flags) | ||||
|  { | ||||
| @@ -4482,6 +4508,7 @@ EXPORT_SYMBOL(__dev_direct_xmit); | ||||
|  /************************************************************************* | ||||
|   *			Receiver routines | ||||
|   *************************************************************************/ | ||||
| +static DEFINE_PER_CPU(struct task_struct *, backlog_napi); | ||||
|   | ||||
|  int netdev_max_backlog __read_mostly = 1000; | ||||
|  EXPORT_SYMBOL(netdev_max_backlog); | ||||
| @@ -4514,12 +4541,16 @@ static inline void ____napi_schedule(str | ||||
|  		 */ | ||||
|  		thread = READ_ONCE(napi->thread); | ||||
|  		if (thread) { | ||||
| +			if (use_backlog_threads() && thread == raw_cpu_read(backlog_napi)) | ||||
| +				goto use_local_napi; | ||||
| + | ||||
|  			set_bit(NAPI_STATE_SCHED_THREADED, &napi->state); | ||||
|  			wake_up_process(thread); | ||||
|  			return; | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +use_local_napi: | ||||
|  	list_add_tail(&napi->poll_list, &sd->poll_list); | ||||
|  	WRITE_ONCE(napi->list_owner, smp_processor_id()); | ||||
|  	/* If not called from net_rx_action() | ||||
| @@ -4765,6 +4796,11 @@ static void napi_schedule_rps(struct sof | ||||
|   | ||||
|  #ifdef CONFIG_RPS | ||||
|  	if (sd != mysd) { | ||||
| +		if (use_backlog_threads()) { | ||||
| +			__napi_schedule_irqoff(&sd->backlog); | ||||
| +			return; | ||||
| +		} | ||||
| + | ||||
|  		sd->rps_ipi_next = mysd->rps_ipi_list; | ||||
|  		mysd->rps_ipi_list = sd; | ||||
|   | ||||
| @@ -5988,7 +6024,7 @@ static void net_rps_action_and_irq_enabl | ||||
|  #ifdef CONFIG_RPS | ||||
|  	struct softnet_data *remsd = sd->rps_ipi_list; | ||||
|   | ||||
| -	if (remsd) { | ||||
| +	if (!use_backlog_threads() && remsd) { | ||||
|  		sd->rps_ipi_list = NULL; | ||||
|   | ||||
|  		local_irq_enable(); | ||||
| @@ -6003,7 +6039,7 @@ static void net_rps_action_and_irq_enabl | ||||
|  static bool sd_has_rps_ipi_waiting(struct softnet_data *sd) | ||||
|  { | ||||
|  #ifdef CONFIG_RPS | ||||
| -	return sd->rps_ipi_list != NULL; | ||||
| +	return !use_backlog_threads() && sd->rps_ipi_list; | ||||
|  #else | ||||
|  	return false; | ||||
|  #endif | ||||
| @@ -6047,7 +6083,7 @@ static int process_backlog(struct napi_s | ||||
|  			 * We can use a plain write instead of clear_bit(), | ||||
|  			 * and we dont need an smp_mb() memory barrier. | ||||
|  			 */ | ||||
| -			napi->state = 0; | ||||
| +			napi->state &= NAPIF_STATE_THREADED; | ||||
|  			again = false; | ||||
|  		} else { | ||||
|  			skb_queue_splice_tail_init(&sd->input_pkt_queue, | ||||
| @@ -6713,43 +6749,48 @@ static void skb_defer_free_flush(struct | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static int napi_threaded_poll(void *data) | ||||
| +static void napi_threaded_poll_loop(struct napi_struct *napi) | ||||
|  { | ||||
| -	struct napi_struct *napi = data; | ||||
|  	struct softnet_data *sd; | ||||
| -	void *have; | ||||
| +	unsigned long last_qs = jiffies; | ||||
|   | ||||
| -	while (!napi_thread_wait(napi)) { | ||||
| -		unsigned long last_qs = jiffies; | ||||
| +	for (;;) { | ||||
| +		bool repoll = false; | ||||
| +		void *have; | ||||
|   | ||||
| -		for (;;) { | ||||
| -			bool repoll = false; | ||||
| +		local_bh_disable(); | ||||
| +		sd = this_cpu_ptr(&softnet_data); | ||||
| +		sd->in_napi_threaded_poll = true; | ||||
|   | ||||
| -			local_bh_disable(); | ||||
| -			sd = this_cpu_ptr(&softnet_data); | ||||
| -			sd->in_napi_threaded_poll = true; | ||||
| - | ||||
| -			have = netpoll_poll_lock(napi); | ||||
| -			__napi_poll(napi, &repoll); | ||||
| -			netpoll_poll_unlock(have); | ||||
| - | ||||
| -			sd->in_napi_threaded_poll = false; | ||||
| -			barrier(); | ||||
| - | ||||
| -			if (sd_has_rps_ipi_waiting(sd)) { | ||||
| -				local_irq_disable(); | ||||
| -				net_rps_action_and_irq_enable(sd); | ||||
| -			} | ||||
| -			skb_defer_free_flush(sd); | ||||
| -			local_bh_enable(); | ||||
| +		have = netpoll_poll_lock(napi); | ||||
| +		__napi_poll(napi, &repoll); | ||||
| +		netpoll_poll_unlock(have); | ||||
| + | ||||
| +		sd->in_napi_threaded_poll = false; | ||||
| +		barrier(); | ||||
| + | ||||
| +		if (sd_has_rps_ipi_waiting(sd)) { | ||||
| +			local_irq_disable(); | ||||
| +			net_rps_action_and_irq_enable(sd); | ||||
| +		} | ||||
| +		skb_defer_free_flush(sd); | ||||
| +		local_bh_enable(); | ||||
|   | ||||
| -			if (!repoll) | ||||
| -				break; | ||||
| +		if (!repoll) | ||||
| +			break; | ||||
|   | ||||
| -			rcu_softirq_qs_periodic(last_qs); | ||||
| -			cond_resched(); | ||||
| -		} | ||||
| +		rcu_softirq_qs_periodic(last_qs); | ||||
| +		cond_resched(); | ||||
|  	} | ||||
| +} | ||||
| + | ||||
| +static int napi_threaded_poll(void *data) | ||||
| +{ | ||||
| +	struct napi_struct *napi = data; | ||||
| + | ||||
| +	while (!napi_thread_wait(napi)) | ||||
| +		napi_threaded_poll_loop(napi); | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -11334,7 +11375,7 @@ static int dev_cpu_dead(unsigned int old | ||||
|   | ||||
|  		list_del_init(&napi->poll_list); | ||||
|  		if (napi->poll == process_backlog) | ||||
| -			napi->state = 0; | ||||
| +			napi->state &= NAPIF_STATE_THREADED; | ||||
|  		else | ||||
|  			____napi_schedule(sd, napi); | ||||
|  	} | ||||
| @@ -11342,12 +11383,14 @@ static int dev_cpu_dead(unsigned int old | ||||
|  	raise_softirq_irqoff(NET_TX_SOFTIRQ); | ||||
|  	local_irq_enable(); | ||||
|   | ||||
| +	if (!use_backlog_threads()) { | ||||
|  #ifdef CONFIG_RPS | ||||
| -	remsd = oldsd->rps_ipi_list; | ||||
| -	oldsd->rps_ipi_list = NULL; | ||||
| +		remsd = oldsd->rps_ipi_list; | ||||
| +		oldsd->rps_ipi_list = NULL; | ||||
|  #endif | ||||
| -	/* send out pending IPI's on offline CPU */ | ||||
| -	net_rps_send_ipi(remsd); | ||||
| +		/* send out pending IPI's on offline CPU */ | ||||
| +		net_rps_send_ipi(remsd); | ||||
| +	} | ||||
|   | ||||
|  	/* Process offline CPU's input_pkt_queue */ | ||||
|  	while ((skb = __skb_dequeue(&oldsd->process_queue))) { | ||||
| @@ -11610,6 +11653,38 @@ static struct pernet_operations __net_in | ||||
|   * | ||||
|   */ | ||||
|   | ||||
| +static int backlog_napi_should_run(unsigned int cpu) | ||||
| +{ | ||||
| +	struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu); | ||||
| +	struct napi_struct *napi = &sd->backlog; | ||||
| + | ||||
| +	return test_bit(NAPI_STATE_SCHED_THREADED, &napi->state); | ||||
| +} | ||||
| + | ||||
| +static void run_backlog_napi(unsigned int cpu) | ||||
| +{ | ||||
| +	struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu); | ||||
| + | ||||
| +	napi_threaded_poll_loop(&sd->backlog); | ||||
| +} | ||||
| + | ||||
| +static void backlog_napi_setup(unsigned int cpu) | ||||
| +{ | ||||
| +	struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu); | ||||
| +	struct napi_struct *napi = &sd->backlog; | ||||
| + | ||||
| +	napi->thread = this_cpu_read(backlog_napi); | ||||
| +	set_bit(NAPI_STATE_THREADED, &napi->state); | ||||
| +} | ||||
| + | ||||
| +static struct smp_hotplug_thread backlog_threads = { | ||||
| +	.store			= &backlog_napi, | ||||
| +	.thread_should_run	= backlog_napi_should_run, | ||||
| +	.thread_fn		= run_backlog_napi, | ||||
| +	.thread_comm		= "backlog_napi/%u", | ||||
| +	.setup			= backlog_napi_setup, | ||||
| +}; | ||||
| + | ||||
|  /* | ||||
|   *       This is called single threaded during boot, so no need | ||||
|   *       to take the rtnl semaphore. | ||||
| @@ -11660,7 +11735,10 @@ static int __init net_dev_init(void) | ||||
|  		init_gro_hash(&sd->backlog); | ||||
|  		sd->backlog.poll = process_backlog; | ||||
|  		sd->backlog.weight = weight_p; | ||||
| +		INIT_LIST_HEAD(&sd->backlog.poll_list); | ||||
|  	} | ||||
| +	if (use_backlog_threads()) | ||||
| +		smpboot_register_percpu_thread(&backlog_threads); | ||||
|   | ||||
|  	dev_boot_phase = 0; | ||||
|   | ||||
| @@ -1,121 +0,0 @@ | ||||
| From 80d2eefcb4c84aa9018b2a997ab3a4c567bc821a Mon Sep 17 00:00:00 2001 | ||||
| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Date: Mon, 25 Mar 2024 08:40:30 +0100 | ||||
| Subject: [PATCH 3/4] net: Use backlog-NAPI to clean up the defer_list. | ||||
|  | ||||
| The defer_list is a per-CPU list which is used to free skbs outside of | ||||
| the socket lock and on the CPU on which they have been allocated. | ||||
| The list is processed during NAPI callbacks so ideally the list is | ||||
| cleaned up. | ||||
| Should the amount of skbs on the list exceed a certain water mark then | ||||
| the softirq is triggered remotely on the target CPU by invoking a remote | ||||
| function call. The raise of the softirqs via a remote function call | ||||
| leads to waking the ksoftirqd on PREEMPT_RT which is undesired. | ||||
| The backlog-NAPI threads already provide the infrastructure which can be | ||||
| utilized to perform the cleanup of the defer_list. | ||||
|  | ||||
| The NAPI state is updated with the input_pkt_queue.lock acquired. It | ||||
| order not to break the state, it is needed to also wake the backlog-NAPI | ||||
| thread with the lock held. This requires to acquire the use the lock in | ||||
| rps_lock_irq*() if the backlog-NAPI threads are used even with RPS | ||||
| disabled. | ||||
|  | ||||
| Move the logic of remotely starting softirqs to clean up the defer_list | ||||
| into kick_defer_list_purge(). Make sure a lock is held in | ||||
| rps_lock_irq*() if backlog-NAPI threads are used. Schedule backlog-NAPI | ||||
| for defer_list cleanup if backlog-NAPI is available. | ||||
|  | ||||
| Acked-by: Jakub Kicinski <kuba@kernel.org> | ||||
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  include/linux/netdevice.h |  1 + | ||||
|  net/core/dev.c            | 25 +++++++++++++++++++++---- | ||||
|  net/core/skbuff.c         |  4 ++-- | ||||
|  3 files changed, 24 insertions(+), 6 deletions(-) | ||||
|  | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -3308,6 +3308,7 @@ static inline void dev_xmit_recursion_de | ||||
|  	__this_cpu_dec(softnet_data.xmit.recursion); | ||||
|  } | ||||
|   | ||||
| +void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu); | ||||
|  void __netif_schedule(struct Qdisc *q); | ||||
|  void netif_schedule_queue(struct netdev_queue *txq); | ||||
|   | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -246,7 +246,7 @@ static bool use_backlog_threads(void) | ||||
|  static inline void rps_lock_irqsave(struct softnet_data *sd, | ||||
|  				    unsigned long *flags) | ||||
|  { | ||||
| -	if (IS_ENABLED(CONFIG_RPS)) | ||||
| +	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags); | ||||
|  	else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) | ||||
|  		local_irq_save(*flags); | ||||
| @@ -254,7 +254,7 @@ static inline void rps_lock_irqsave(stru | ||||
|   | ||||
|  static inline void rps_lock_irq_disable(struct softnet_data *sd) | ||||
|  { | ||||
| -	if (IS_ENABLED(CONFIG_RPS)) | ||||
| +	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_lock_irq(&sd->input_pkt_queue.lock); | ||||
|  	else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) | ||||
|  		local_irq_disable(); | ||||
| @@ -263,7 +263,7 @@ static inline void rps_lock_irq_disable( | ||||
|  static inline void rps_unlock_irq_restore(struct softnet_data *sd, | ||||
|  					  unsigned long *flags) | ||||
|  { | ||||
| -	if (IS_ENABLED(CONFIG_RPS)) | ||||
| +	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags); | ||||
|  	else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) | ||||
|  		local_irq_restore(*flags); | ||||
| @@ -271,7 +271,7 @@ static inline void rps_unlock_irq_restor | ||||
|   | ||||
|  static inline void rps_unlock_irq_enable(struct softnet_data *sd) | ||||
|  { | ||||
| -	if (IS_ENABLED(CONFIG_RPS)) | ||||
| +	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_unlock_irq(&sd->input_pkt_queue.lock); | ||||
|  	else if (!IS_ENABLED(CONFIG_PREEMPT_RT)) | ||||
|  		local_irq_enable(); | ||||
| @@ -4815,6 +4815,23 @@ static void napi_schedule_rps(struct sof | ||||
|  	__napi_schedule_irqoff(&mysd->backlog); | ||||
|  } | ||||
|   | ||||
| +void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu) | ||||
| +{ | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	if (use_backlog_threads()) { | ||||
| +		rps_lock_irqsave(sd, &flags); | ||||
| + | ||||
| +		if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) | ||||
| +			__napi_schedule_irqoff(&sd->backlog); | ||||
| + | ||||
| +		rps_unlock_irq_restore(sd, &flags); | ||||
| + | ||||
| +	} else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) { | ||||
| +		smp_call_function_single_async(cpu, &sd->defer_csd); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
|  #ifdef CONFIG_NET_FLOW_LIMIT | ||||
|  int netdev_flow_limit_table_len __read_mostly = (1 << 12); | ||||
|  #endif | ||||
| --- a/net/core/skbuff.c | ||||
| +++ b/net/core/skbuff.c | ||||
| @@ -6863,8 +6863,8 @@ nodefer:	__kfree_skb(skb); | ||||
|  	/* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU | ||||
|  	 * if we are unlucky enough (this seems very unlikely). | ||||
|  	 */ | ||||
| -	if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) | ||||
| -		smp_call_function_single_async(cpu, &sd->defer_csd); | ||||
| +	if (unlikely(kick)) | ||||
| +		kick_defer_list_purge(sd, cpu); | ||||
|  } | ||||
|   | ||||
|  static void skb_splice_csum_page(struct sk_buff *skb, struct page *page, | ||||
| @@ -1,164 +0,0 @@ | ||||
| From 765b11f8f4e20b7433e4ba4a3e9106a0d59501ed Mon Sep 17 00:00:00 2001 | ||||
| From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Date: Mon, 25 Mar 2024 08:40:31 +0100 | ||||
| Subject: [PATCH 4/4] net: Rename rps_lock to backlog_lock. | ||||
|  | ||||
| The rps_lock.*() functions use the inner lock of a sk_buff_head for | ||||
| locking. This lock is used if RPS is enabled, otherwise the list is | ||||
| accessed lockless and disabling interrupts is enough for the | ||||
| synchronisation because it is only accessed CPU local. Not only the list | ||||
| is protected but also the NAPI state protected. | ||||
| With the addition of backlog threads, the lock is also needed because of | ||||
| the cross CPU access even without RPS. The clean up of the defer_list | ||||
| list is also done via backlog threads (if enabled). | ||||
|  | ||||
| It has been suggested to rename the locking function since it is no | ||||
| longer just RPS. | ||||
|  | ||||
| Rename the rps_lock*() functions to backlog_lock*(). | ||||
|  | ||||
| Suggested-by: Jakub Kicinski <kuba@kernel.org> | ||||
| Acked-by: Jakub Kicinski <kuba@kernel.org> | ||||
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  net/core/dev.c | 34 +++++++++++++++++----------------- | ||||
|  1 file changed, 17 insertions(+), 17 deletions(-) | ||||
|  | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -243,8 +243,8 @@ static bool use_backlog_threads(void) | ||||
|   | ||||
|  #endif | ||||
|   | ||||
| -static inline void rps_lock_irqsave(struct softnet_data *sd, | ||||
| -				    unsigned long *flags) | ||||
| +static inline void backlog_lock_irq_save(struct softnet_data *sd, | ||||
| +					 unsigned long *flags) | ||||
|  { | ||||
|  	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags); | ||||
| @@ -252,7 +252,7 @@ static inline void rps_lock_irqsave(stru | ||||
|  		local_irq_save(*flags); | ||||
|  } | ||||
|   | ||||
| -static inline void rps_lock_irq_disable(struct softnet_data *sd) | ||||
| +static inline void backlog_lock_irq_disable(struct softnet_data *sd) | ||||
|  { | ||||
|  	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_lock_irq(&sd->input_pkt_queue.lock); | ||||
| @@ -260,8 +260,8 @@ static inline void rps_lock_irq_disable( | ||||
|  		local_irq_disable(); | ||||
|  } | ||||
|   | ||||
| -static inline void rps_unlock_irq_restore(struct softnet_data *sd, | ||||
| -					  unsigned long *flags) | ||||
| +static inline void backlog_unlock_irq_restore(struct softnet_data *sd, | ||||
| +					      unsigned long *flags) | ||||
|  { | ||||
|  	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags); | ||||
| @@ -269,7 +269,7 @@ static inline void rps_unlock_irq_restor | ||||
|  		local_irq_restore(*flags); | ||||
|  } | ||||
|   | ||||
| -static inline void rps_unlock_irq_enable(struct softnet_data *sd) | ||||
| +static inline void backlog_unlock_irq_enable(struct softnet_data *sd) | ||||
|  { | ||||
|  	if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads()) | ||||
|  		spin_unlock_irq(&sd->input_pkt_queue.lock); | ||||
| @@ -4820,12 +4820,12 @@ void kick_defer_list_purge(struct softne | ||||
|  	unsigned long flags; | ||||
|   | ||||
|  	if (use_backlog_threads()) { | ||||
| -		rps_lock_irqsave(sd, &flags); | ||||
| +		backlog_lock_irq_save(sd, &flags); | ||||
|   | ||||
|  		if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) | ||||
|  			__napi_schedule_irqoff(&sd->backlog); | ||||
|   | ||||
| -		rps_unlock_irq_restore(sd, &flags); | ||||
| +		backlog_unlock_irq_restore(sd, &flags); | ||||
|   | ||||
|  	} else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) { | ||||
|  		smp_call_function_single_async(cpu, &sd->defer_csd); | ||||
| @@ -4887,7 +4887,7 @@ static int enqueue_to_backlog(struct sk_ | ||||
|  	reason = SKB_DROP_REASON_NOT_SPECIFIED; | ||||
|  	sd = &per_cpu(softnet_data, cpu); | ||||
|   | ||||
| -	rps_lock_irqsave(sd, &flags); | ||||
| +	backlog_lock_irq_save(sd, &flags); | ||||
|  	if (!netif_running(skb->dev)) | ||||
|  		goto drop; | ||||
|  	qlen = skb_queue_len(&sd->input_pkt_queue); | ||||
| @@ -4896,7 +4896,7 @@ static int enqueue_to_backlog(struct sk_ | ||||
|  enqueue: | ||||
|  			__skb_queue_tail(&sd->input_pkt_queue, skb); | ||||
|  			input_queue_tail_incr_save(sd, qtail); | ||||
| -			rps_unlock_irq_restore(sd, &flags); | ||||
| +			backlog_unlock_irq_restore(sd, &flags); | ||||
|  			return NET_RX_SUCCESS; | ||||
|  		} | ||||
|   | ||||
| @@ -4911,7 +4911,7 @@ enqueue: | ||||
|   | ||||
|  drop: | ||||
|  	sd->dropped++; | ||||
| -	rps_unlock_irq_restore(sd, &flags); | ||||
| +	backlog_unlock_irq_restore(sd, &flags); | ||||
|   | ||||
|  	dev_core_stats_rx_dropped_inc(skb->dev); | ||||
|  	kfree_skb_reason(skb, reason); | ||||
| @@ -5942,7 +5942,7 @@ static void flush_backlog(struct work_st | ||||
|  	local_bh_disable(); | ||||
|  	sd = this_cpu_ptr(&softnet_data); | ||||
|   | ||||
| -	rps_lock_irq_disable(sd); | ||||
| +	backlog_lock_irq_disable(sd); | ||||
|  	skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) { | ||||
|  		if (skb->dev->reg_state == NETREG_UNREGISTERING) { | ||||
|  			__skb_unlink(skb, &sd->input_pkt_queue); | ||||
| @@ -5950,7 +5950,7 @@ static void flush_backlog(struct work_st | ||||
|  			input_queue_head_incr(sd); | ||||
|  		} | ||||
|  	} | ||||
| -	rps_unlock_irq_enable(sd); | ||||
| +	backlog_unlock_irq_enable(sd); | ||||
|   | ||||
|  	skb_queue_walk_safe(&sd->process_queue, skb, tmp) { | ||||
|  		if (skb->dev->reg_state == NETREG_UNREGISTERING) { | ||||
| @@ -5968,14 +5968,14 @@ static bool flush_required(int cpu) | ||||
|  	struct softnet_data *sd = &per_cpu(softnet_data, cpu); | ||||
|  	bool do_flush; | ||||
|   | ||||
| -	rps_lock_irq_disable(sd); | ||||
| +	backlog_lock_irq_disable(sd); | ||||
|   | ||||
|  	/* as insertion into process_queue happens with the rps lock held, | ||||
|  	 * process_queue access may race only with dequeue | ||||
|  	 */ | ||||
|  	do_flush = !skb_queue_empty(&sd->input_pkt_queue) || | ||||
|  		   !skb_queue_empty_lockless(&sd->process_queue); | ||||
| -	rps_unlock_irq_enable(sd); | ||||
| +	backlog_unlock_irq_enable(sd); | ||||
|   | ||||
|  	return do_flush; | ||||
|  #endif | ||||
| @@ -6090,7 +6090,7 @@ static int process_backlog(struct napi_s | ||||
|   | ||||
|  		} | ||||
|   | ||||
| -		rps_lock_irq_disable(sd); | ||||
| +		backlog_lock_irq_disable(sd); | ||||
|  		if (skb_queue_empty(&sd->input_pkt_queue)) { | ||||
|  			/* | ||||
|  			 * Inline a custom version of __napi_complete(). | ||||
| @@ -6106,7 +6106,7 @@ static int process_backlog(struct napi_s | ||||
|  			skb_queue_splice_tail_init(&sd->input_pkt_queue, | ||||
|  						   &sd->process_queue); | ||||
|  		} | ||||
| -		rps_unlock_irq_enable(sd); | ||||
| +		backlog_unlock_irq_enable(sd); | ||||
|  	} | ||||
|   | ||||
|  	return work; | ||||
| @@ -1,30 +0,0 @@ | ||||
| From 6c06c88fa838fcc1b7e5380facd086f57fd9d1c4 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> | ||||
| Date: Sun, 4 Feb 2024 15:16:46 +0100 | ||||
| Subject: [PATCH] net: mdio: add 2.5g and 5g related PMA speed constants | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| Add constants indicating 2.5g and 5g ability in the MMD PMA speed | ||||
| register. | ||||
|  | ||||
| Signed-off-by: Marek Behún <kabel@kernel.org> | ||||
| Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> | ||||
| Link: https://lore.kernel.org/r/98e15038-d96c-442f-93e4-410100d27866@gmail.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  include/uapi/linux/mdio.h | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
|  | ||||
| --- a/include/uapi/linux/mdio.h | ||||
| +++ b/include/uapi/linux/mdio.h | ||||
| @@ -138,6 +138,8 @@ | ||||
|  #define MDIO_PMA_SPEED_1000		0x0010	/* 1000M capable */ | ||||
|  #define MDIO_PMA_SPEED_100		0x0020	/* 100M capable */ | ||||
|  #define MDIO_PMA_SPEED_10		0x0040	/* 10M capable */ | ||||
| +#define MDIO_PMA_SPEED_2_5G		0x2000	/* 2.5G capable */ | ||||
| +#define MDIO_PMA_SPEED_5G		0x4000	/* 5G capable */ | ||||
|  #define MDIO_PCS_SPEED_10P2B		0x0002	/* 10PASS-TS/2BASE-TL capable */ | ||||
|  #define MDIO_PCS_SPEED_2_5G		0x0040	/* 2.5G capable */ | ||||
|  #define MDIO_PCS_SPEED_5G		0x0080	/* 5G capable */ | ||||
| @@ -1,94 +0,0 @@ | ||||
| From: Jakub Sitnicki <jakub@cloudflare.com> | ||||
| Date: Wed, 26 Jun 2024 19:51:26 +0200 | ||||
| Subject: [PATCH] udp: Allow GSO transmit from devices with no checksum offload | ||||
|  | ||||
| Today sending a UDP GSO packet from a TUN device results in an EIO error: | ||||
|  | ||||
|   import fcntl, os, struct | ||||
|   from socket import * | ||||
|  | ||||
|   TUNSETIFF = 0x400454CA | ||||
|   IFF_TUN = 0x0001 | ||||
|   IFF_NO_PI = 0x1000 | ||||
|   UDP_SEGMENT = 103 | ||||
|  | ||||
|   tun_fd = os.open("/dev/net/tun", os.O_RDWR) | ||||
|   ifr = struct.pack("16sH", b"tun0", IFF_TUN | IFF_NO_PI) | ||||
|   fcntl.ioctl(tun_fd, TUNSETIFF, ifr) | ||||
|  | ||||
|   os.system("ip addr add 192.0.2.1/24 dev tun0") | ||||
|   os.system("ip link set dev tun0 up") | ||||
|  | ||||
|   s = socket(AF_INET, SOCK_DGRAM) | ||||
|   s.setsockopt(SOL_UDP, UDP_SEGMENT, 1200) | ||||
|   s.sendto(b"x" * 3000, ("192.0.2.2", 9)) # EIO | ||||
|  | ||||
| This is due to a check in the udp stack if the egress device offers | ||||
| checksum offload. While TUN/TAP devices, by default, don't advertise this | ||||
| capability because it requires support from the TUN/TAP reader. | ||||
|  | ||||
| However, the GSO stack has a software fallback for checksum calculation, | ||||
| which we can use. This way we don't force UDP_SEGMENT users to handle the | ||||
| EIO error and implement a segmentation fallback. | ||||
|  | ||||
| Lift the restriction so that UDP_SEGMENT can be used with any egress | ||||
| device. We also need to adjust the UDP GSO code to match the GSO stack | ||||
| expectation about ip_summed field, as set in commit 8d63bee643f1 ("net: | ||||
| avoid skb_warn_bad_offload false positives on UFO"). Otherwise we will hit | ||||
| the bad offload check. | ||||
|  | ||||
| Users should, however, expect a potential performance impact when | ||||
| batch-sending packets with UDP_SEGMENT without checksum offload on the | ||||
| egress device. In such case the packet payload is read twice: first during | ||||
| the sendmsg syscall when copying data from user memory, and then in the GSO | ||||
| stack for checksum computation. This double memory read can be less | ||||
| efficient than a regular sendmsg where the checksum is calculated during | ||||
| the initial data copy from user memory. | ||||
|  | ||||
| Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com> | ||||
| Reviewed-by: Willem de Bruijn <willemb@google.com> | ||||
| Link: https://patch.msgid.link/20240626-linux-udpgso-v2-1-422dfcbd6b48@cloudflare.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  | ||||
| --- a/net/ipv4/udp.c | ||||
| +++ b/net/ipv4/udp.c | ||||
| @@ -942,8 +942,7 @@ static int udp_send_skb(struct sk_buff * | ||||
|  			kfree_skb(skb); | ||||
|  			return -EINVAL; | ||||
|  		} | ||||
| -		if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || | ||||
| -		    dst_xfrm(skb_dst(skb))) { | ||||
| +		if (is_udplite || dst_xfrm(skb_dst(skb))) { | ||||
|  			kfree_skb(skb); | ||||
|  			return -EIO; | ||||
|  		} | ||||
| --- a/net/ipv4/udp_offload.c | ||||
| +++ b/net/ipv4/udp_offload.c | ||||
| @@ -384,6 +384,14 @@ struct sk_buff *__udp_gso_segment(struct | ||||
|  	else | ||||
|  		uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0; | ||||
|   | ||||
| +	/* On the TX path, CHECKSUM_NONE and CHECKSUM_UNNECESSARY have the same | ||||
| +	 * meaning. However, check for bad offloads in the GSO stack expects the | ||||
| +	 * latter, if the checksum was calculated in software. To vouch for the | ||||
| +	 * segment skbs we actually need to set it on the gso_skb. | ||||
| +	 */ | ||||
| +	if (gso_skb->ip_summed == CHECKSUM_NONE) | ||||
| +		gso_skb->ip_summed = CHECKSUM_UNNECESSARY; | ||||
| + | ||||
|  	/* update refcount for the packet */ | ||||
|  	if (copy_dtor) { | ||||
|  		int delta = sum_truesize - gso_skb->truesize; | ||||
| --- a/net/ipv6/udp.c | ||||
| +++ b/net/ipv6/udp.c | ||||
| @@ -1258,8 +1258,7 @@ static int udp_v6_send_skb(struct sk_buf | ||||
|  			kfree_skb(skb); | ||||
|  			return -EINVAL; | ||||
|  		} | ||||
| -		if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || | ||||
| -		    dst_xfrm(skb_dst(skb))) { | ||||
| +		if (is_udplite || dst_xfrm(skb_dst(skb))) { | ||||
|  			kfree_skb(skb); | ||||
|  			return -EIO; | ||||
|  		} | ||||
| @@ -1,69 +0,0 @@ | ||||
| From: Jakub Sitnicki <jakub@cloudflare.com> | ||||
| Date: Thu, 8 Aug 2024 11:56:21 +0200 | ||||
| Subject: [PATCH] net: Make USO depend on CSUM offload | ||||
|  | ||||
| UDP segmentation offload inherently depends on checksum offload. It should | ||||
| not be possible to disable checksum offload while leaving USO enabled. | ||||
| Enforce this dependency in code. | ||||
|  | ||||
| There is a single tx-udp-segmentation feature flag to indicate support for | ||||
| both IPv4/6, hence the devices wishing to support USO must offer checksum | ||||
| offload for both IP versions. | ||||
|  | ||||
| Fixes: 10154dbded6d ("udp: Allow GSO transmit from devices with no checksum offload") | ||||
| Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com> | ||||
| Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com> | ||||
| Reviewed-by: Willem de Bruijn <willemb@google.com> | ||||
| Link: https://patch.msgid.link/20240808-udp-gso-egress-from-tunnel-v4-1-f5c5b4149ab9@cloudflare.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -9796,6 +9796,15 @@ static void netdev_sync_lower_features(s | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +static bool netdev_has_ip_or_hw_csum(netdev_features_t features) | ||||
| +{ | ||||
| +	netdev_features_t ip_csum_mask = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; | ||||
| +	bool ip_csum = (features & ip_csum_mask) == ip_csum_mask; | ||||
| +	bool hw_csum = features & NETIF_F_HW_CSUM; | ||||
| + | ||||
| +	return ip_csum || hw_csum; | ||||
| +} | ||||
| + | ||||
|  static netdev_features_t netdev_fix_features(struct net_device *dev, | ||||
|  	netdev_features_t features) | ||||
|  { | ||||
| @@ -9877,15 +9886,9 @@ static netdev_features_t netdev_fix_feat | ||||
|  		features &= ~NETIF_F_LRO; | ||||
|  	} | ||||
|   | ||||
| -	if (features & NETIF_F_HW_TLS_TX) { | ||||
| -		bool ip_csum = (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) == | ||||
| -			(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); | ||||
| -		bool hw_csum = features & NETIF_F_HW_CSUM; | ||||
| - | ||||
| -		if (!ip_csum && !hw_csum) { | ||||
| -			netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n"); | ||||
| -			features &= ~NETIF_F_HW_TLS_TX; | ||||
| -		} | ||||
| +	if ((features & NETIF_F_HW_TLS_TX) && !netdev_has_ip_or_hw_csum(features)) { | ||||
| +		netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n"); | ||||
| +		features &= ~NETIF_F_HW_TLS_TX; | ||||
|  	} | ||||
|   | ||||
|  	if ((features & NETIF_F_HW_TLS_RX) && !(features & NETIF_F_RXCSUM)) { | ||||
| @@ -9893,6 +9896,11 @@ static netdev_features_t netdev_fix_feat | ||||
|  		features &= ~NETIF_F_HW_TLS_RX; | ||||
|  	} | ||||
|   | ||||
| +	if ((features & NETIF_F_GSO_UDP_L4) && !netdev_has_ip_or_hw_csum(features)) { | ||||
| +		netdev_dbg(dev, "Dropping USO feature since no CSUM feature.\n"); | ||||
| +		features &= ~NETIF_F_GSO_UDP_L4; | ||||
| +	} | ||||
| + | ||||
|  	return features; | ||||
|  } | ||||
|   | ||||
| @@ -1,86 +0,0 @@ | ||||
| From: Jakub Sitnicki <jakub@cloudflare.com> | ||||
| Date: Thu, 8 Aug 2024 11:56:22 +0200 | ||||
| Subject: [PATCH] udp: Fall back to software USO if IPv6 extension headers are | ||||
|  present | ||||
|  | ||||
| In commit 10154dbded6d ("udp: Allow GSO transmit from devices with no | ||||
| checksum offload") we have intentionally allowed UDP GSO packets marked | ||||
| CHECKSUM_NONE to pass to the GSO stack, so that they can be segmented and | ||||
| checksummed by a software fallback when the egress device lacks these | ||||
| features. | ||||
|  | ||||
| What was not taken into consideration is that a CHECKSUM_NONE skb can be | ||||
| handed over to the GSO stack also when the egress device advertises the | ||||
| tx-udp-segmentation / NETIF_F_GSO_UDP_L4 feature. | ||||
|  | ||||
| This will happen when there are IPv6 extension headers present, which we | ||||
| check for in __ip6_append_data(). Syzbot has discovered this scenario, | ||||
| producing a warning as below: | ||||
|  | ||||
|   ip6tnl0: caps=(0x00000006401d7869, 0x00000006401d7869) | ||||
|   WARNING: CPU: 0 PID: 5112 at net/core/dev.c:3293 skb_warn_bad_offload+0x166/0x1a0 net/core/dev.c:3291 | ||||
|   Modules linked in: | ||||
|   CPU: 0 PID: 5112 Comm: syz-executor391 Not tainted 6.10.0-rc7-syzkaller-01603-g80ab5445da62 #0 | ||||
|   Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 06/07/2024 | ||||
|   RIP: 0010:skb_warn_bad_offload+0x166/0x1a0 net/core/dev.c:3291 | ||||
|   [...] | ||||
|   Call Trace: | ||||
|    <TASK> | ||||
|    __skb_gso_segment+0x3be/0x4c0 net/core/gso.c:127 | ||||
|    skb_gso_segment include/net/gso.h:83 [inline] | ||||
|    validate_xmit_skb+0x585/0x1120 net/core/dev.c:3661 | ||||
|    __dev_queue_xmit+0x17a4/0x3e90 net/core/dev.c:4415 | ||||
|    neigh_output include/net/neighbour.h:542 [inline] | ||||
|    ip6_finish_output2+0xffa/0x1680 net/ipv6/ip6_output.c:137 | ||||
|    ip6_finish_output+0x41e/0x810 net/ipv6/ip6_output.c:222 | ||||
|    ip6_send_skb+0x112/0x230 net/ipv6/ip6_output.c:1958 | ||||
|    udp_v6_send_skb+0xbf5/0x1870 net/ipv6/udp.c:1292 | ||||
|    udpv6_sendmsg+0x23b3/0x3270 net/ipv6/udp.c:1588 | ||||
|    sock_sendmsg_nosec net/socket.c:730 [inline] | ||||
|    __sock_sendmsg+0xef/0x270 net/socket.c:745 | ||||
|    ____sys_sendmsg+0x525/0x7d0 net/socket.c:2585 | ||||
|    ___sys_sendmsg net/socket.c:2639 [inline] | ||||
|    __sys_sendmmsg+0x3b2/0x740 net/socket.c:2725 | ||||
|    __do_sys_sendmmsg net/socket.c:2754 [inline] | ||||
|    __se_sys_sendmmsg net/socket.c:2751 [inline] | ||||
|    __x64_sys_sendmmsg+0xa0/0xb0 net/socket.c:2751 | ||||
|    do_syscall_x64 arch/x86/entry/common.c:52 [inline] | ||||
|    do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83 | ||||
|    entry_SYSCALL_64_after_hwframe+0x77/0x7f | ||||
|    [...] | ||||
|    </TASK> | ||||
|  | ||||
| We are hitting the bad offload warning because when an egress device is | ||||
| capable of handling segmentation offload requested by | ||||
| skb_shinfo(skb)->gso_type, the chain of gso_segment callbacks won't produce | ||||
| any segment skbs and return NULL. See the skb_gso_ok() branch in | ||||
| {__udp,tcp,sctp}_gso_segment helpers. | ||||
|  | ||||
| To fix it, force a fallback to software USO when processing a packet with | ||||
| IPv6 extension headers, since we don't know if these can checksummed by | ||||
| all devices which offer USO. | ||||
|  | ||||
| Fixes: 10154dbded6d ("udp: Allow GSO transmit from devices with no checksum offload") | ||||
| Reported-by: syzbot+e15b7e15b8a751a91d9a@syzkaller.appspotmail.com | ||||
| Closes: https://lore.kernel.org/all/000000000000e1609a061d5330ce@google.com/ | ||||
| Reviewed-by: Willem de Bruijn <willemb@google.com> | ||||
| Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com> | ||||
| Link: https://patch.msgid.link/20240808-udp-gso-egress-from-tunnel-v4-2-f5c5b4149ab9@cloudflare.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  | ||||
| --- a/net/ipv4/udp_offload.c | ||||
| +++ b/net/ipv4/udp_offload.c | ||||
| @@ -283,6 +283,12 @@ struct sk_buff *__udp_gso_segment(struct | ||||
|  		     !(skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST))) | ||||
|  		return ERR_PTR(-EINVAL); | ||||
|   | ||||
| +	/* We don't know if egress device can segment and checksum the packet | ||||
| +	 * when IPv6 extension headers are present. Fall back to software GSO. | ||||
| +	 */ | ||||
| +	if (gso_skb->ip_summed != CHECKSUM_PARTIAL) | ||||
| +		features &= ~(NETIF_F_GSO_UDP_L4 | NETIF_F_CSUM_MASK); | ||||
| + | ||||
|  	if (skb_gso_ok(gso_skb, features | NETIF_F_GSO_ROBUST)) { | ||||
|  		/* Packet is from an untrusted source, reset gso_segs. */ | ||||
|  		skb_shinfo(gso_skb)->gso_segs = DIV_ROUND_UP(gso_skb->len - sizeof(*uh), | ||||
| @@ -1,29 +0,0 @@ | ||||
| From: Breno Leitao <leitao@debian.org> | ||||
| Date: Wed, 28 Feb 2024 03:31:21 -0800 | ||||
| Subject: [PATCH] net: get stats64 if device if driver is configured | ||||
|  | ||||
| If the network driver is relying in the net core to do stats allocation, | ||||
| then we want to dev_get_tstats64() instead of netdev_stats_to_stats64(), | ||||
| since there are per-cpu stats that needs to be taken in consideration. | ||||
|  | ||||
| This will also simplify the drivers in regard to statistics. Once the | ||||
| driver sets NETDEV_PCPU_STAT_TSTATS, it doesn't not need to allocate the | ||||
| stacks, neither it needs to set `.ndo_get_stats64 = dev_get_tstats64` | ||||
| for the generic stats collection function anymore. | ||||
|  | ||||
| Signed-off-by: Breno Leitao <leitao@debian.org> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -10703,6 +10703,8 @@ struct rtnl_link_stats64 *dev_get_stats( | ||||
|  		ops->ndo_get_stats64(dev, storage); | ||||
|  	} else if (ops->ndo_get_stats) { | ||||
|  		netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev)); | ||||
| +	} else if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_TSTATS) { | ||||
| +		dev_get_tstats64(dev, storage); | ||||
|  	} else { | ||||
|  		netdev_stats_to_stats64(storage, &dev->stats); | ||||
|  	} | ||||
| @@ -1,139 +0,0 @@ | ||||
| From: Yunsheng Lin <linyunsheng@huawei.com> | ||||
| Date: Fri, 13 Oct 2023 14:48:21 +0800 | ||||
| Subject: [PATCH] page_pool: fragment API support for 32-bit arch with 64-bit | ||||
|  DMA | ||||
|  | ||||
| Currently page_pool_alloc_frag() is not supported in 32-bit | ||||
| arch with 64-bit DMA because of the overlap issue between | ||||
| pp_frag_count and dma_addr_upper in 'struct page' for those | ||||
| arches, which seems to be quite common, see [1], which means | ||||
| driver may need to handle it when using fragment API. | ||||
|  | ||||
| It is assumed that the combination of the above arch with an | ||||
| address space >16TB does not exist, as all those arches have | ||||
| 64b equivalent, it seems logical to use the 64b version for a | ||||
| system with a large address space. It is also assumed that dma | ||||
| address is page aligned when we are dma mapping a page aligned | ||||
| buffer, see [2]. | ||||
|  | ||||
| That means we're storing 12 bits of 0 at the lower end for a | ||||
| dma address, we can reuse those bits for the above arches to | ||||
| support 32b+12b, which is 16TB of memory. | ||||
|  | ||||
| If we make a wrong assumption, a warning is emitted so that | ||||
| user can report to us. | ||||
|  | ||||
| 1. https://lore.kernel.org/all/20211117075652.58299-1-linyunsheng@huawei.com/ | ||||
| 2. https://lore.kernel.org/all/20230818145145.4b357c89@kernel.org/ | ||||
|  | ||||
| Tested-by: Alexander Lobakin <aleksander.lobakin@intel.com> | ||||
| Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com> | ||||
| CC: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| CC: Alexander Duyck <alexander.duyck@gmail.com> | ||||
| CC: Liang Chen <liangchen.linux@gmail.com> | ||||
| CC: Guillaume Tucker <guillaume.tucker@collabora.com> | ||||
| CC: Matthew Wilcox <willy@infradead.org> | ||||
| CC: Linux-MM <linux-mm@kvack.org> | ||||
| Link: https://lore.kernel.org/r/20231013064827.61135-2-linyunsheng@huawei.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  | ||||
| --- a/include/linux/mm_types.h | ||||
| +++ b/include/linux/mm_types.h | ||||
| @@ -125,18 +125,7 @@ struct page { | ||||
|  			struct page_pool *pp; | ||||
|  			unsigned long _pp_mapping_pad; | ||||
|  			unsigned long dma_addr; | ||||
| -			union { | ||||
| -				/** | ||||
| -				 * dma_addr_upper: might require a 64-bit | ||||
| -				 * value on 32-bit architectures. | ||||
| -				 */ | ||||
| -				unsigned long dma_addr_upper; | ||||
| -				/** | ||||
| -				 * For frag page support, not supported in | ||||
| -				 * 32-bit architectures with 64-bit DMA. | ||||
| -				 */ | ||||
| -				atomic_long_t pp_frag_count; | ||||
| -			}; | ||||
| +			atomic_long_t pp_frag_count; | ||||
|  		}; | ||||
|  		struct {	/* Tail pages of compound page */ | ||||
|  			unsigned long compound_head;	/* Bit zero is set */ | ||||
| --- a/include/net/page_pool/helpers.h | ||||
| +++ b/include/net/page_pool/helpers.h | ||||
| @@ -197,7 +197,7 @@ static inline void page_pool_recycle_dir | ||||
|  	page_pool_put_full_page(pool, page, true); | ||||
|  } | ||||
|   | ||||
| -#define PAGE_POOL_DMA_USE_PP_FRAG_COUNT	\ | ||||
| +#define PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA	\ | ||||
|  		(sizeof(dma_addr_t) > sizeof(unsigned long)) | ||||
|   | ||||
|  /** | ||||
| @@ -211,17 +211,25 @@ static inline dma_addr_t page_pool_get_d | ||||
|  { | ||||
|  	dma_addr_t ret = page->dma_addr; | ||||
|   | ||||
| -	if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT) | ||||
| -		ret |= (dma_addr_t)page->dma_addr_upper << 16 << 16; | ||||
| +	if (PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA) | ||||
| +		ret <<= PAGE_SHIFT; | ||||
|   | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static inline void page_pool_set_dma_addr(struct page *page, dma_addr_t addr) | ||||
| +static inline bool page_pool_set_dma_addr(struct page *page, dma_addr_t addr) | ||||
|  { | ||||
| +	if (PAGE_POOL_32BIT_ARCH_WITH_64BIT_DMA) { | ||||
| +		page->dma_addr = addr >> PAGE_SHIFT; | ||||
| + | ||||
| +		/* We assume page alignment to shave off bottom bits, | ||||
| +		 * if this "compression" doesn't work we need to drop. | ||||
| +		 */ | ||||
| +		return addr != (dma_addr_t)page->dma_addr << PAGE_SHIFT; | ||||
| +	} | ||||
| + | ||||
|  	page->dma_addr = addr; | ||||
| -	if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT) | ||||
| -		page->dma_addr_upper = upper_32_bits(addr); | ||||
| +	return false; | ||||
|  } | ||||
|   | ||||
|  static inline bool page_pool_put(struct page_pool *pool) | ||||
| --- a/net/core/page_pool.c | ||||
| +++ b/net/core/page_pool.c | ||||
| @@ -211,10 +211,6 @@ static int page_pool_init(struct page_po | ||||
|  		 */ | ||||
|  	} | ||||
|   | ||||
| -	if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT && | ||||
| -	    pool->p.flags & PP_FLAG_PAGE_FRAG) | ||||
| -		return -EINVAL; | ||||
| - | ||||
|  #ifdef CONFIG_PAGE_POOL_STATS | ||||
|  	pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats); | ||||
|  	if (!pool->recycle_stats) | ||||
| @@ -363,12 +359,20 @@ static bool page_pool_dma_map(struct pag | ||||
|  	if (dma_mapping_error(pool->p.dev, dma)) | ||||
|  		return false; | ||||
|   | ||||
| -	page_pool_set_dma_addr(page, dma); | ||||
| +	if (page_pool_set_dma_addr(page, dma)) | ||||
| +		goto unmap_failed; | ||||
|   | ||||
|  	if (pool->p.flags & PP_FLAG_DMA_SYNC_DEV) | ||||
|  		page_pool_dma_sync_for_device(pool, page, pool->p.max_len); | ||||
|   | ||||
|  	return true; | ||||
| + | ||||
| +unmap_failed: | ||||
| +	WARN_ON_ONCE("unexpected DMA address, please report to netdev@"); | ||||
| +	dma_unmap_page_attrs(pool->p.dev, dma, | ||||
| +			     PAGE_SIZE << pool->p.order, pool->p.dma_dir, | ||||
| +			     DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); | ||||
| +	return false; | ||||
|  } | ||||
|   | ||||
|  static void page_pool_set_pp_info(struct page_pool *pool, | ||||
| @@ -1,183 +0,0 @@ | ||||
| From: Yunsheng Lin <linyunsheng@huawei.com> | ||||
| Date: Fri, 20 Oct 2023 17:59:48 +0800 | ||||
| Subject: [PATCH] page_pool: unify frag_count handling in | ||||
|  page_pool_is_last_frag() | ||||
|  | ||||
| Currently when page_pool_create() is called with | ||||
| PP_FLAG_PAGE_FRAG flag, page_pool_alloc_pages() is only | ||||
| allowed to be called under the below constraints: | ||||
| 1. page_pool_fragment_page() need to be called to setup | ||||
|    page->pp_frag_count immediately. | ||||
| 2. page_pool_defrag_page() often need to be called to drain | ||||
|    the page->pp_frag_count when there is no more user will | ||||
|    be holding on to that page. | ||||
|  | ||||
| Those constraints exist in order to support a page to be | ||||
| split into multi fragments. | ||||
|  | ||||
| And those constraints have some overhead because of the | ||||
| cache line dirtying/bouncing and atomic update. | ||||
|  | ||||
| Those constraints are unavoidable for case when we need a | ||||
| page to be split into more than one fragment, but there is | ||||
| also case that we want to avoid the above constraints and | ||||
| their overhead when a page can't be split as it can only | ||||
| hold a fragment as requested by user, depending on different | ||||
| use cases: | ||||
| use case 1: allocate page without page splitting. | ||||
| use case 2: allocate page with page splitting. | ||||
| use case 3: allocate page with or without page splitting | ||||
|             depending on the fragment size. | ||||
|  | ||||
| Currently page pool only provide page_pool_alloc_pages() and | ||||
| page_pool_alloc_frag() API to enable the 1 & 2 separately, | ||||
| so we can not use a combination of 1 & 2 to enable 3, it is | ||||
| not possible yet because of the per page_pool flag | ||||
| PP_FLAG_PAGE_FRAG. | ||||
|  | ||||
| So in order to allow allocating unsplit page without the | ||||
| overhead of split page while still allow allocating split | ||||
| page we need to remove the per page_pool flag in | ||||
| page_pool_is_last_frag(), as best as I can think of, it seems | ||||
| there are two methods as below: | ||||
| 1. Add per page flag/bit to indicate a page is split or | ||||
|    not, which means we might need to update that flag/bit | ||||
|    everytime the page is recycled, dirtying the cache line | ||||
|    of 'struct page' for use case 1. | ||||
| 2. Unify the page->pp_frag_count handling for both split and | ||||
|    unsplit page by assuming all pages in the page pool is split | ||||
|    into a big fragment initially. | ||||
|  | ||||
| As page pool already supports use case 1 without dirtying the | ||||
| cache line of 'struct page' whenever a page is recyclable, we | ||||
| need to support the above use case 3 with minimal overhead, | ||||
| especially not adding any noticeable overhead for use case 1, | ||||
| and we are already doing an optimization by not updating | ||||
| pp_frag_count in page_pool_defrag_page() for the last fragment | ||||
| user, this patch chooses to unify the pp_frag_count handling | ||||
| to support the above use case 3. | ||||
|  | ||||
| There is no noticeable performance degradation and some | ||||
| justification for unifying the frag_count handling with this | ||||
| patch applied using a micro-benchmark testing in [1]. | ||||
|  | ||||
| 1. https://lore.kernel.org/all/bf2591f8-7b3c-4480-bb2c-31dc9da1d6ac@huawei.com/ | ||||
|  | ||||
| Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com> | ||||
| CC: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| CC: Alexander Duyck <alexander.duyck@gmail.com> | ||||
| CC: Liang Chen <liangchen.linux@gmail.com> | ||||
| CC: Alexander Lobakin <aleksander.lobakin@intel.com> | ||||
| Link: https://lore.kernel.org/r/20231020095952.11055-2-linyunsheng@huawei.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  | ||||
| --- a/include/net/page_pool/helpers.h | ||||
| +++ b/include/net/page_pool/helpers.h | ||||
| @@ -115,28 +115,49 @@ static inline long page_pool_defrag_page | ||||
|  	long ret; | ||||
|   | ||||
|  	/* If nr == pp_frag_count then we have cleared all remaining | ||||
| -	 * references to the page. No need to actually overwrite it, instead | ||||
| -	 * we can leave this to be overwritten by the calling function. | ||||
| +	 * references to the page: | ||||
| +	 * 1. 'n == 1': no need to actually overwrite it. | ||||
| +	 * 2. 'n != 1': overwrite it with one, which is the rare case | ||||
| +	 *              for pp_frag_count draining. | ||||
|  	 * | ||||
| -	 * The main advantage to doing this is that an atomic_read is | ||||
| -	 * generally a much cheaper operation than an atomic update, | ||||
| -	 * especially when dealing with a page that may be partitioned | ||||
| -	 * into only 2 or 3 pieces. | ||||
| +	 * The main advantage to doing this is that not only we avoid a atomic | ||||
| +	 * update, as an atomic_read is generally a much cheaper operation than | ||||
| +	 * an atomic update, especially when dealing with a page that may be | ||||
| +	 * partitioned into only 2 or 3 pieces; but also unify the pp_frag_count | ||||
| +	 * handling by ensuring all pages have partitioned into only 1 piece | ||||
| +	 * initially, and only overwrite it when the page is partitioned into | ||||
| +	 * more than one piece. | ||||
|  	 */ | ||||
| -	if (atomic_long_read(&page->pp_frag_count) == nr) | ||||
| +	if (atomic_long_read(&page->pp_frag_count) == nr) { | ||||
| +		/* As we have ensured nr is always one for constant case using | ||||
| +		 * the BUILD_BUG_ON(), only need to handle the non-constant case | ||||
| +		 * here for pp_frag_count draining, which is a rare case. | ||||
| +		 */ | ||||
| +		BUILD_BUG_ON(__builtin_constant_p(nr) && nr != 1); | ||||
| +		if (!__builtin_constant_p(nr)) | ||||
| +			atomic_long_set(&page->pp_frag_count, 1); | ||||
| + | ||||
|  		return 0; | ||||
| +	} | ||||
|   | ||||
|  	ret = atomic_long_sub_return(nr, &page->pp_frag_count); | ||||
|  	WARN_ON(ret < 0); | ||||
| + | ||||
| +	/* We are the last user here too, reset pp_frag_count back to 1 to | ||||
| +	 * ensure all pages have been partitioned into 1 piece initially, | ||||
| +	 * this should be the rare case when the last two fragment users call | ||||
| +	 * page_pool_defrag_page() currently. | ||||
| +	 */ | ||||
| +	if (unlikely(!ret)) | ||||
| +		atomic_long_set(&page->pp_frag_count, 1); | ||||
| + | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static inline bool page_pool_is_last_frag(struct page_pool *pool, | ||||
| -					  struct page *page) | ||||
| +static inline bool page_pool_is_last_frag(struct page *page) | ||||
|  { | ||||
| -	/* If fragments aren't enabled or count is 0 we were the last user */ | ||||
| -	return !(pool->p.flags & PP_FLAG_PAGE_FRAG) || | ||||
| -	       (page_pool_defrag_page(page, 1) == 0); | ||||
| +	/* If page_pool_defrag_page() returns 0, we were the last user */ | ||||
| +	return page_pool_defrag_page(page, 1) == 0; | ||||
|  } | ||||
|   | ||||
|  /** | ||||
| @@ -161,7 +182,7 @@ static inline void page_pool_put_page(st | ||||
|  	 * allow registering MEM_TYPE_PAGE_POOL, but shield linker. | ||||
|  	 */ | ||||
|  #ifdef CONFIG_PAGE_POOL | ||||
| -	if (!page_pool_is_last_frag(pool, page)) | ||||
| +	if (!page_pool_is_last_frag(page)) | ||||
|  		return; | ||||
|   | ||||
|  	page_pool_put_defragged_page(pool, page, dma_sync_size, allow_direct); | ||||
| --- a/net/core/page_pool.c | ||||
| +++ b/net/core/page_pool.c | ||||
| @@ -380,6 +380,14 @@ static void page_pool_set_pp_info(struct | ||||
|  { | ||||
|  	page->pp = pool; | ||||
|  	page->pp_magic |= PP_SIGNATURE; | ||||
| + | ||||
| +	/* Ensuring all pages have been split into one fragment initially: | ||||
| +	 * page_pool_set_pp_info() is only called once for every page when it | ||||
| +	 * is allocated from the page allocator and page_pool_fragment_page() | ||||
| +	 * is dirtying the same cache line as the page->pp_magic above, so | ||||
| +	 * the overhead is negligible. | ||||
| +	 */ | ||||
| +	page_pool_fragment_page(page, 1); | ||||
|  	if (pool->p.init_callback) | ||||
|  		pool->p.init_callback(page, pool->p.init_arg); | ||||
|  } | ||||
| @@ -676,7 +684,7 @@ void page_pool_put_page_bulk(struct page | ||||
|  		struct page *page = virt_to_head_page(data[i]); | ||||
|   | ||||
|  		/* It is not the last user for the page frag case */ | ||||
| -		if (!page_pool_is_last_frag(pool, page)) | ||||
| +		if (!page_pool_is_last_frag(page)) | ||||
|  			continue; | ||||
|   | ||||
|  		page = __page_pool_put_page(pool, page, -1, false); | ||||
| @@ -752,8 +760,7 @@ struct page *page_pool_alloc_frag(struct | ||||
|  	unsigned int max_size = PAGE_SIZE << pool->p.order; | ||||
|  	struct page *page = pool->frag_page; | ||||
|   | ||||
| -	if (WARN_ON(!(pool->p.flags & PP_FLAG_PAGE_FRAG) || | ||||
| -		    size > max_size)) | ||||
| +	if (WARN_ON(size > max_size)) | ||||
|  		return NULL; | ||||
|   | ||||
|  	size = ALIGN(size, dma_get_cache_alignment()); | ||||
| @@ -1,135 +0,0 @@ | ||||
| From c661050f93d3fd37a33c06041bb18a89688de7d2 Mon Sep 17 00:00:00 2001 | ||||
| From: Breno Leitao <leitao@debian.org> | ||||
| Date: Mon, 22 Apr 2024 05:38:56 -0700 | ||||
| Subject: [PATCH] net: create a dummy net_device allocator | ||||
|  | ||||
| It is impossible to use init_dummy_netdev together with alloc_netdev() | ||||
| as the 'setup' argument. | ||||
|  | ||||
| This is because alloc_netdev() initializes some fields in the net_device | ||||
| structure, and later init_dummy_netdev() memzero them all. This causes | ||||
| some problems as reported here: | ||||
|  | ||||
| 	https://lore.kernel.org/all/20240322082336.49f110cc@kernel.org/ | ||||
|  | ||||
| Split the init_dummy_netdev() function in two. Create a new function called | ||||
| init_dummy_netdev_core() that does not memzero the net_device structure. | ||||
| Then have init_dummy_netdev() memzero-ing and calling | ||||
| init_dummy_netdev_core(), keeping the old behaviour. | ||||
|  | ||||
| init_dummy_netdev_core() is the new function that could be called as an | ||||
| argument for alloc_netdev(). | ||||
|  | ||||
| Also, create a helper to allocate and initialize dummy net devices, | ||||
| leveraging init_dummy_netdev_core() as the setup argument. This function | ||||
| basically simplify the allocation of dummy devices, by allocating and | ||||
| initializing it. Freeing the device continue to be done through | ||||
| free_netdev() | ||||
|  | ||||
| Suggested-by: Jakub Kicinski <kuba@kernel.org> | ||||
| Signed-off-by: Breno Leitao <leitao@debian.org> | ||||
| Reviewed-by: Ido Schimmel <idosch@nvidia.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  include/linux/netdevice.h |  3 +++ | ||||
|  net/core/dev.c            | 56 ++++++++++++++++++++++++++------------- | ||||
|  2 files changed, 41 insertions(+), 18 deletions(-) | ||||
|  | ||||
| --- a/include/linux/netdevice.h | ||||
| +++ b/include/linux/netdevice.h | ||||
| @@ -4569,6 +4569,9 @@ static inline void netif_addr_unlock_bh( | ||||
|   | ||||
|  void ether_setup(struct net_device *dev); | ||||
|   | ||||
| +/* Allocate dummy net_device */ | ||||
| +struct net_device *alloc_netdev_dummy(int sizeof_priv); | ||||
| + | ||||
|  /* Support for loadable net-drivers */ | ||||
|  struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, | ||||
|  				    unsigned char name_assign_type, | ||||
| --- a/net/core/dev.c | ||||
| +++ b/net/core/dev.c | ||||
| @@ -10390,25 +10390,12 @@ err_free_name: | ||||
|  } | ||||
|  EXPORT_SYMBOL(register_netdevice); | ||||
|   | ||||
| -/** | ||||
| - *	init_dummy_netdev	- init a dummy network device for NAPI | ||||
| - *	@dev: device to init | ||||
| - * | ||||
| - *	This takes a network device structure and initialize the minimum | ||||
| - *	amount of fields so it can be used to schedule NAPI polls without | ||||
| - *	registering a full blown interface. This is to be used by drivers | ||||
| - *	that need to tie several hardware interfaces to a single NAPI | ||||
| - *	poll scheduler due to HW limitations. | ||||
| +/* Initialize the core of a dummy net device. | ||||
| + * This is useful if you are calling this function after alloc_netdev(), | ||||
| + * since it does not memset the net_device fields. | ||||
|   */ | ||||
| -int init_dummy_netdev(struct net_device *dev) | ||||
| +static void init_dummy_netdev_core(struct net_device *dev) | ||||
|  { | ||||
| -	/* Clear everything. Note we don't initialize spinlocks | ||||
| -	 * are they aren't supposed to be taken by any of the | ||||
| -	 * NAPI code and this dummy netdev is supposed to be | ||||
| -	 * only ever used for NAPI polls | ||||
| -	 */ | ||||
| -	memset(dev, 0, sizeof(struct net_device)); | ||||
| - | ||||
|  	/* make sure we BUG if trying to hit standard | ||||
|  	 * register/unregister code path | ||||
|  	 */ | ||||
| @@ -10428,12 +10415,32 @@ int init_dummy_netdev(struct net_device | ||||
|  	 * because users of this 'device' dont need to change | ||||
|  	 * its refcount. | ||||
|  	 */ | ||||
| +} | ||||
| + | ||||
| +/** | ||||
| + *	init_dummy_netdev	- init a dummy network device for NAPI | ||||
| + *	@dev: device to init | ||||
| + * | ||||
| + *	This takes a network device structure and initializes the minimum | ||||
| + *	amount of fields so it can be used to schedule NAPI polls without | ||||
| + *	registering a full blown interface. This is to be used by drivers | ||||
| + *	that need to tie several hardware interfaces to a single NAPI | ||||
| + *	poll scheduler due to HW limitations. | ||||
| + */ | ||||
| +int init_dummy_netdev(struct net_device *dev) | ||||
| +{ | ||||
| +	/* Clear everything. Note we don't initialize spinlocks | ||||
| +	 * as they aren't supposed to be taken by any of the | ||||
| +	 * NAPI code and this dummy netdev is supposed to be | ||||
| +	 * only ever used for NAPI polls | ||||
| +	 */ | ||||
| +	memset(dev, 0, sizeof(struct net_device)); | ||||
| +	init_dummy_netdev_core(dev); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(init_dummy_netdev); | ||||
|   | ||||
| - | ||||
|  /** | ||||
|   *	register_netdev	- register a network device | ||||
|   *	@dev: device to register | ||||
| @@ -11027,6 +11034,19 @@ void free_netdev(struct net_device *dev) | ||||
|  EXPORT_SYMBOL(free_netdev); | ||||
|   | ||||
|  /** | ||||
| + * alloc_netdev_dummy - Allocate and initialize a dummy net device. | ||||
| + * @sizeof_priv: size of private data to allocate space for | ||||
| + * | ||||
| + * Return: the allocated net_device on success, NULL otherwise | ||||
| + */ | ||||
| +struct net_device *alloc_netdev_dummy(int sizeof_priv) | ||||
| +{ | ||||
| +	return alloc_netdev(sizeof_priv, "dummy#", NET_NAME_UNKNOWN, | ||||
| +			    init_dummy_netdev_core); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(alloc_netdev_dummy); | ||||
| + | ||||
| +/** | ||||
|   *	synchronize_net -  Synchronize with packet receive processing | ||||
|   * | ||||
|   *	Wait for packets currently being received to be done. | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,183 +0,0 @@ | ||||
| From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Tue, 14 Nov 2023 15:08:42 +0100 | ||||
| Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header | ||||
|  | ||||
| Move MMD_VEND define to header to clean things up and in preparation for | ||||
| firmware loading support that require some define placed in | ||||
| aquantia_main. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/aquantia/aquantia.h       | 69 +++++++++++++++++++++++ | ||||
|  drivers/net/phy/aquantia/aquantia_hwmon.c | 14 ----- | ||||
|  drivers/net/phy/aquantia/aquantia_main.c  | 55 ------------------ | ||||
|  3 files changed, 69 insertions(+), 69 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/aquantia/aquantia.h | ||||
| +++ b/drivers/net/phy/aquantia/aquantia.h | ||||
| @@ -9,6 +9,75 @@ | ||||
|  #include <linux/device.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +/* Vendor specific 1, MDIO_MMD_VEND1 */ | ||||
| +#define VEND1_GLOBAL_FW_ID			0x0020 | ||||
| +#define VEND1_GLOBAL_FW_ID_MAJOR		GENMASK(15, 8) | ||||
| +#define VEND1_GLOBAL_FW_ID_MINOR		GENMASK(7, 0) | ||||
| + | ||||
| +/* The following registers all have similar layouts; first the registers... */ | ||||
| +#define VEND1_GLOBAL_CFG_10M			0x0310 | ||||
| +#define VEND1_GLOBAL_CFG_100M			0x031b | ||||
| +#define VEND1_GLOBAL_CFG_1G			0x031c | ||||
| +#define VEND1_GLOBAL_CFG_2_5G			0x031d | ||||
| +#define VEND1_GLOBAL_CFG_5G			0x031e | ||||
| +#define VEND1_GLOBAL_CFG_10G			0x031f | ||||
| +/* ...and now the fields */ | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT		GENMASK(8, 7) | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE	0 | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX		1 | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2 | ||||
| + | ||||
| +/* Vendor specific 1, MDIO_MMD_VEND2 */ | ||||
| +#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL	0xc421 | ||||
| +#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL	0xc422 | ||||
| +#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN	0xc423 | ||||
| +#define VEND1_THERMAL_PROV_LOW_TEMP_WARN	0xc424 | ||||
| +#define VEND1_THERMAL_STAT1			0xc820 | ||||
| +#define VEND1_THERMAL_STAT2			0xc821 | ||||
| +#define VEND1_THERMAL_STAT2_VALID		BIT(0) | ||||
| +#define VEND1_GENERAL_STAT1			0xc830 | ||||
| +#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL	BIT(14) | ||||
| +#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL	BIT(13) | ||||
| +#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN	BIT(12) | ||||
| +#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN	BIT(11) | ||||
| + | ||||
| +#define VEND1_GLOBAL_GEN_STAT2			0xc831 | ||||
| +#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG	BIT(15) | ||||
| + | ||||
| +#define VEND1_GLOBAL_RSVD_STAT1			0xc885 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID	GENMASK(7, 4) | ||||
| +#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID		GENMASK(3, 0) | ||||
| + | ||||
| +#define VEND1_GLOBAL_RSVD_STAT9			0xc88d | ||||
| +#define VEND1_GLOBAL_RSVD_STAT9_MODE		GENMASK(7, 0) | ||||
| +#define VEND1_GLOBAL_RSVD_STAT9_1000BT2		0x23 | ||||
| + | ||||
| +#define VEND1_GLOBAL_INT_STD_STATUS		0xfc00 | ||||
| +#define VEND1_GLOBAL_INT_VEND_STATUS		0xfc01 | ||||
| + | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK		0xff00 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PMA1		BIT(15) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PMA2		BIT(14) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PCS1		BIT(13) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PCS2		BIT(12) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PCS3		BIT(11) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1	BIT(10) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2	BIT(9) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_AN1		BIT(8) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_AN2		BIT(7) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_GBE		BIT(6) | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_ALL		BIT(0) | ||||
| + | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK		0xff01 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_PMA		BIT(15) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_PCS		BIT(14) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS	BIT(13) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_AN		BIT(12) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GBE		BIT(11) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1	BIT(2) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2	BIT(1) | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3	BIT(0) | ||||
| + | ||||
|  #if IS_REACHABLE(CONFIG_HWMON) | ||||
|  int aqr_hwmon_probe(struct phy_device *phydev); | ||||
|  #else | ||||
| --- a/drivers/net/phy/aquantia/aquantia_hwmon.c | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_hwmon.c | ||||
| @@ -13,20 +13,6 @@ | ||||
|   | ||||
|  #include "aquantia.h" | ||||
|   | ||||
| -/* Vendor specific 1, MDIO_MMD_VEND2 */ | ||||
| -#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL	0xc421 | ||||
| -#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL	0xc422 | ||||
| -#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN	0xc423 | ||||
| -#define VEND1_THERMAL_PROV_LOW_TEMP_WARN	0xc424 | ||||
| -#define VEND1_THERMAL_STAT1			0xc820 | ||||
| -#define VEND1_THERMAL_STAT2			0xc821 | ||||
| -#define VEND1_THERMAL_STAT2_VALID		BIT(0) | ||||
| -#define VEND1_GENERAL_STAT1			0xc830 | ||||
| -#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL	BIT(14) | ||||
| -#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL	BIT(13) | ||||
| -#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN	BIT(12) | ||||
| -#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN	BIT(11) | ||||
| - | ||||
|  #if IS_REACHABLE(CONFIG_HWMON) | ||||
|   | ||||
|  static umode_t aqr_hwmon_is_visible(const void *data, | ||||
| --- a/drivers/net/phy/aquantia/aquantia_main.c | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_main.c | ||||
| @@ -91,61 +91,6 @@ | ||||
|  #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR	0xd31a | ||||
|  #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES		0xd31b | ||||
|   | ||||
| -/* Vendor specific 1, MDIO_MMD_VEND1 */ | ||||
| -#define VEND1_GLOBAL_FW_ID			0x0020 | ||||
| -#define VEND1_GLOBAL_FW_ID_MAJOR		GENMASK(15, 8) | ||||
| -#define VEND1_GLOBAL_FW_ID_MINOR		GENMASK(7, 0) | ||||
| - | ||||
| -#define VEND1_GLOBAL_GEN_STAT2			0xc831 | ||||
| -#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG	BIT(15) | ||||
| - | ||||
| -/* The following registers all have similar layouts; first the registers... */ | ||||
| -#define VEND1_GLOBAL_CFG_10M			0x0310 | ||||
| -#define VEND1_GLOBAL_CFG_100M			0x031b | ||||
| -#define VEND1_GLOBAL_CFG_1G			0x031c | ||||
| -#define VEND1_GLOBAL_CFG_2_5G			0x031d | ||||
| -#define VEND1_GLOBAL_CFG_5G			0x031e | ||||
| -#define VEND1_GLOBAL_CFG_10G			0x031f | ||||
| -/* ...and now the fields */ | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT		GENMASK(8, 7) | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE	0 | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX		1 | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2 | ||||
| - | ||||
| -#define VEND1_GLOBAL_RSVD_STAT1			0xc885 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID	GENMASK(7, 4) | ||||
| -#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID		GENMASK(3, 0) | ||||
| - | ||||
| -#define VEND1_GLOBAL_RSVD_STAT9			0xc88d | ||||
| -#define VEND1_GLOBAL_RSVD_STAT9_MODE		GENMASK(7, 0) | ||||
| -#define VEND1_GLOBAL_RSVD_STAT9_1000BT2		0x23 | ||||
| - | ||||
| -#define VEND1_GLOBAL_INT_STD_STATUS		0xfc00 | ||||
| -#define VEND1_GLOBAL_INT_VEND_STATUS		0xfc01 | ||||
| - | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK		0xff00 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PMA1		BIT(15) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PMA2		BIT(14) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PCS1		BIT(13) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PCS2		BIT(12) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PCS3		BIT(11) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1	BIT(10) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2	BIT(9) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_AN1		BIT(8) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_AN2		BIT(7) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_GBE		BIT(6) | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_ALL		BIT(0) | ||||
| - | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK		0xff01 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_PMA		BIT(15) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_PCS		BIT(14) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS	BIT(13) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_AN		BIT(12) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GBE		BIT(11) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1	BIT(2) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2	BIT(1) | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3	BIT(0) | ||||
| - | ||||
|  /* Sleep and timeout for checking if the Processor-Intensive | ||||
|   * MDIO operation is finished | ||||
|   */ | ||||
| @@ -1,504 +0,0 @@ | ||||
| From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001 | ||||
| From: Robert Marko <robimarko@gmail.com> | ||||
| Date: Tue, 14 Nov 2023 15:08:43 +0100 | ||||
| Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support | ||||
|  | ||||
| Aquantia PHY-s require firmware to be loaded before they start operating. | ||||
| It can be automatically loaded in case when there is a SPI-NOR connected | ||||
| to Aquantia PHY-s or can be loaded from the host via MDIO. | ||||
|  | ||||
| This patch adds support for loading the firmware via MDIO as in most cases | ||||
| there is no SPI-NOR being used to save on cost. | ||||
| Firmware loading code itself is ported from mainline U-boot with cleanups. | ||||
|  | ||||
| The firmware has mixed values both in big and little endian. | ||||
| PHY core itself is big-endian but it expects values to be in little-endian. | ||||
| The firmware is little-endian but CRC-16 value for it is stored at the end | ||||
| of firmware in big-endian. | ||||
|  | ||||
| It seems the PHY does the conversion internally from firmware that is | ||||
| little-endian to the PHY that is big-endian on using the mailbox | ||||
| but mailbox returns a big-endian CRC-16 to verify the written data | ||||
| integrity. | ||||
|  | ||||
| Co-developed-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: Robert Marko <robimarko@gmail.com> | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/aquantia/Kconfig             |   1 + | ||||
|  drivers/net/phy/aquantia/Makefile            |   2 +- | ||||
|  drivers/net/phy/aquantia/aquantia.h          |  32 ++ | ||||
|  drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++ | ||||
|  drivers/net/phy/aquantia/aquantia_main.c     |   6 + | ||||
|  5 files changed, 410 insertions(+), 1 deletion(-) | ||||
|  create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c | ||||
|  | ||||
| --- a/drivers/net/phy/aquantia/Kconfig | ||||
| +++ b/drivers/net/phy/aquantia/Kconfig | ||||
| @@ -1,5 +1,6 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0-only | ||||
|  config AQUANTIA_PHY | ||||
|  	tristate "Aquantia PHYs" | ||||
| +	select CRC_CCITT | ||||
|  	help | ||||
|  	  Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 | ||||
| --- a/drivers/net/phy/aquantia/Makefile | ||||
| +++ b/drivers/net/phy/aquantia/Makefile | ||||
| @@ -1,5 +1,5 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
| -aquantia-objs			+= aquantia_main.o | ||||
| +aquantia-objs			+= aquantia_main.o aquantia_firmware.o | ||||
|  ifdef CONFIG_HWMON | ||||
|  aquantia-objs			+= aquantia_hwmon.o | ||||
|  endif | ||||
| --- a/drivers/net/phy/aquantia/aquantia.h | ||||
| +++ b/drivers/net/phy/aquantia/aquantia.h | ||||
| @@ -10,10 +10,35 @@ | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
|  /* Vendor specific 1, MDIO_MMD_VEND1 */ | ||||
| +#define VEND1_GLOBAL_SC				0x0 | ||||
| +#define VEND1_GLOBAL_SC_SOFT_RESET		BIT(15) | ||||
| +#define VEND1_GLOBAL_SC_LOW_POWER		BIT(11) | ||||
| + | ||||
|  #define VEND1_GLOBAL_FW_ID			0x0020 | ||||
|  #define VEND1_GLOBAL_FW_ID_MAJOR		GENMASK(15, 8) | ||||
|  #define VEND1_GLOBAL_FW_ID_MINOR		GENMASK(7, 0) | ||||
|   | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1			0x0200 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE		BIT(15) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE		BIT(14) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET	BIT(12) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY		BIT(8) | ||||
| + | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE2			0x0201 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE3			0x0202 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK	GENMASK(15, 0) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16)) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE4			0x0203 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK	GENMASK(15, 2) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x)) | ||||
| + | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE5			0x0204 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK	GENMASK(15, 0) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16)) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE6			0x0205 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK	GENMASK(15, 0) | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x)) | ||||
| + | ||||
|  /* The following registers all have similar layouts; first the registers... */ | ||||
|  #define VEND1_GLOBAL_CFG_10M			0x0310 | ||||
|  #define VEND1_GLOBAL_CFG_100M			0x031b | ||||
| @@ -28,6 +53,11 @@ | ||||
|  #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2 | ||||
|   | ||||
|  /* Vendor specific 1, MDIO_MMD_VEND2 */ | ||||
| +#define VEND1_GLOBAL_CONTROL2			0xc001 | ||||
| +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST	BIT(15) | ||||
| +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD	BIT(6) | ||||
| +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL	BIT(0) | ||||
| + | ||||
|  #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL	0xc421 | ||||
|  #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL	0xc422 | ||||
|  #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN	0xc423 | ||||
| @@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p | ||||
|  #else | ||||
|  static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } | ||||
|  #endif | ||||
| + | ||||
| +int aqr_firmware_load(struct phy_device *phydev); | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_firmware.c | ||||
| @@ -0,0 +1,370 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0 | ||||
| + | ||||
| +#include <linux/bitfield.h> | ||||
| +#include <linux/of.h> | ||||
| +#include <linux/firmware.h> | ||||
| +#include <linux/crc-ccitt.h> | ||||
| +#include <linux/nvmem-consumer.h> | ||||
| + | ||||
| +#include <asm/unaligned.h> | ||||
| + | ||||
| +#include "aquantia.h" | ||||
| + | ||||
| +#define UP_RESET_SLEEP		100 | ||||
| + | ||||
| +/* addresses of memory segments in the phy */ | ||||
| +#define DRAM_BASE_ADDR		0x3FFE0000 | ||||
| +#define IRAM_BASE_ADDR		0x40000000 | ||||
| + | ||||
| +/* firmware image format constants */ | ||||
| +#define VERSION_STRING_SIZE		0x40 | ||||
| +#define VERSION_STRING_OFFSET		0x0200 | ||||
| +/* primary offset is written at an offset from the start of the fw blob */ | ||||
| +#define PRIMARY_OFFSET_OFFSET		0x8 | ||||
| +/* primary offset needs to be then added to a base offset */ | ||||
| +#define PRIMARY_OFFSET_SHIFT		12 | ||||
| +#define PRIMARY_OFFSET(x)		((x) << PRIMARY_OFFSET_SHIFT) | ||||
| +#define HEADER_OFFSET			0x300 | ||||
| + | ||||
| +struct aqr_fw_header { | ||||
| +	u32 padding; | ||||
| +	u8 iram_offset[3]; | ||||
| +	u8 iram_size[3]; | ||||
| +	u8 dram_offset[3]; | ||||
| +	u8 dram_size[3]; | ||||
| +} __packed; | ||||
| + | ||||
| +enum aqr_fw_src { | ||||
| +	AQR_FW_SRC_NVMEM = 0, | ||||
| +	AQR_FW_SRC_FS, | ||||
| +}; | ||||
| + | ||||
| +static const char * const aqr_fw_src_string[] = { | ||||
| +	[AQR_FW_SRC_NVMEM] = "NVMEM", | ||||
| +	[AQR_FW_SRC_FS] = "FS", | ||||
| +}; | ||||
| + | ||||
| +/* AQR firmware doesn't have fixed offsets for iram and dram section | ||||
| + * but instead provide an header with the offset to use on reading | ||||
| + * and parsing the firmware. | ||||
| + * | ||||
| + * AQR firmware can't be trusted and each offset is validated to be | ||||
| + * not negative and be in the size of the firmware itself. | ||||
| + */ | ||||
| +static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size) | ||||
| +{ | ||||
| +	return offset + get_size <= size; | ||||
| +} | ||||
| + | ||||
| +static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value) | ||||
| +{ | ||||
| +	if (!aqr_fw_validate_get(size, offset, sizeof(u16))) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	*value = get_unaligned_be16(data + offset); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value) | ||||
| +{ | ||||
| +	if (!aqr_fw_validate_get(size, offset, sizeof(u16))) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	*value = get_unaligned_le16(data + offset); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value) | ||||
| +{ | ||||
| +	if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	*value = get_unaligned_le24(data + offset); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* load data into the phy's memory */ | ||||
| +static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr, | ||||
| +			      const u8 *data, size_t len) | ||||
| +{ | ||||
| +	u16 crc = 0, up_crc; | ||||
| +	size_t pos; | ||||
| + | ||||
| +	/* PHY expect addr in LE */ | ||||
| +	addr = (__force u32)cpu_to_le32(addr); | ||||
| + | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE1, | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE3, | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr)); | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE4, | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr)); | ||||
| + | ||||
| +	/* We assume and enforce the size to be word aligned. | ||||
| +	 * If a firmware that is not word aligned is found, please report upstream. | ||||
| +	 */ | ||||
| +	for (pos = 0; pos < len; pos += sizeof(u32)) { | ||||
| +		u32 word; | ||||
| + | ||||
| +		/* FW data is always stored in little-endian */ | ||||
| +		word = get_unaligned((const u32 *)(data + pos)); | ||||
| + | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6, | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word)); | ||||
| + | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1, | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); | ||||
| + | ||||
| +		/* calculate CRC as we load data to the mailbox. | ||||
| +		 * We convert word to big-endian as PHY is BE and mailbox will | ||||
| +		 * return a BE CRC. | ||||
| +		 */ | ||||
| +		word = (__force u32)cpu_to_be32(word); | ||||
| +		crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word)); | ||||
| +	} | ||||
| + | ||||
| +	up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); | ||||
| +	if (crc != up_crc) { | ||||
| +		phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", | ||||
| +			   crc, up_crc); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size, | ||||
| +		       enum aqr_fw_src fw_src) | ||||
| +{ | ||||
| +	u16 calculated_crc, read_crc, read_primary_offset; | ||||
| +	u32 iram_offset = 0, iram_size = 0; | ||||
| +	u32 dram_offset = 0, dram_size = 0; | ||||
| +	char version[VERSION_STRING_SIZE]; | ||||
| +	u32 primary_offset = 0; | ||||
| +	int ret; | ||||
| + | ||||
| +	/* extract saved CRC at the end of the fw | ||||
| +	 * CRC is saved in big-endian as PHY is BE | ||||
| +	 */ | ||||
| +	ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "bad firmware CRC in firmware\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| +	calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16)); | ||||
| +	if (read_crc != calculated_crc) { | ||||
| +		phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n", | ||||
| +			   read_crc, calculated_crc); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	/* Get the primary offset to extract DRAM and IRAM sections. */ | ||||
| +	ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "bad primary offset in firmware\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| +	primary_offset = PRIMARY_OFFSET(read_primary_offset); | ||||
| + | ||||
| +	/* Find the DRAM and IRAM sections within the firmware file. | ||||
| +	 * Make sure the fw_header is correctly in the firmware. | ||||
| +	 */ | ||||
| +	if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET, | ||||
| +				 sizeof(struct aqr_fw_header))) { | ||||
| +		phydev_err(phydev, "bad fw_header in firmware\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	/* offset are in LE and values needs to be converted to cpu endian */ | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + | ||||
| +			      offsetof(struct aqr_fw_header, iram_offset), | ||||
| +			      size, &iram_offset); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "bad iram offset in firmware\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + | ||||
| +			      offsetof(struct aqr_fw_header, iram_size), | ||||
| +			      size, &iram_size); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "invalid iram size in firmware\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + | ||||
| +			      offsetof(struct aqr_fw_header, dram_offset), | ||||
| +			      size, &dram_offset); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "bad dram offset in firmware\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + | ||||
| +			      offsetof(struct aqr_fw_header, dram_size), | ||||
| +			      size, &dram_size); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "invalid dram size in firmware\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* Increment the offset with the primary offset. | ||||
| +	 * Validate iram/dram offset and size. | ||||
| +	 */ | ||||
| +	iram_offset += primary_offset; | ||||
| +	if (iram_size % sizeof(u32)) { | ||||
| +		phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| +	if (!aqr_fw_validate_get(size, iram_offset, iram_size)) { | ||||
| +		phydev_err(phydev, "invalid iram offset for iram size\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	dram_offset += primary_offset; | ||||
| +	if (dram_size % sizeof(u32)) { | ||||
| +		phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| +	if (!aqr_fw_validate_get(size, dram_offset, dram_size)) { | ||||
| +		phydev_err(phydev, "invalid iram offset for iram size\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n", | ||||
| +		   primary_offset, iram_offset, iram_size, dram_offset, dram_size); | ||||
| + | ||||
| +	if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET, | ||||
| +				 VERSION_STRING_SIZE)) { | ||||
| +		phydev_err(phydev, "invalid version in firmware\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| +	strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET, | ||||
| +		VERSION_STRING_SIZE); | ||||
| +	if (version[0] == '\0') { | ||||
| +		phydev_err(phydev, "invalid version in firmware\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| +	phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version, | ||||
| +		    aqr_fw_src_string[fw_src]); | ||||
| + | ||||
| +	/* stall the microcprocessor */ | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); | ||||
| + | ||||
| +	phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n", | ||||
| +		   DRAM_BASE_ADDR, dram_offset, dram_size); | ||||
| +	ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset, | ||||
| +				 dram_size); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n", | ||||
| +		   IRAM_BASE_ADDR, iram_offset, iram_size); | ||||
| +	ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset, | ||||
| +				 iram_size); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* make sure soft reset and low power mode are clear */ | ||||
| +	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC, | ||||
| +			   VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER); | ||||
| + | ||||
| +	/* Release the microprocessor. UP_RESET must be held for 100 usec. */ | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD | | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST); | ||||
| +	usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2); | ||||
| + | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int aqr_firmware_load_nvmem(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct nvmem_cell *cell; | ||||
| +	size_t size; | ||||
| +	u8 *buf; | ||||
| +	int ret; | ||||
| + | ||||
| +	cell = nvmem_cell_get(&phydev->mdio.dev, "firmware"); | ||||
| +	if (IS_ERR(cell)) | ||||
| +		return PTR_ERR(cell); | ||||
| + | ||||
| +	buf = nvmem_cell_read(cell, &size); | ||||
| +	if (IS_ERR(buf)) { | ||||
| +		ret = PTR_ERR(buf); | ||||
| +		goto exit; | ||||
| +	} | ||||
| + | ||||
| +	ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM); | ||||
| +	if (ret) | ||||
| +		phydev_err(phydev, "firmware loading failed: %d\n", ret); | ||||
| + | ||||
| +	kfree(buf); | ||||
| +exit: | ||||
| +	nvmem_cell_put(cell); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static int aqr_firmware_load_fs(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct device *dev = &phydev->mdio.dev; | ||||
| +	const struct firmware *fw; | ||||
| +	const char *fw_name; | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = of_property_read_string(dev->of_node, "firmware-name", | ||||
| +				      &fw_name); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = request_firmware(&fw, fw_name, dev); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "failed to find FW file %s (%d)\n", | ||||
| +			   fw_name, ret); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS); | ||||
| +	if (ret) | ||||
| +		phydev_err(phydev, "firmware loading failed: %d\n", ret); | ||||
| + | ||||
| +	release_firmware(fw); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +int aqr_firmware_load(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	/* Check if the firmware is not already loaded by pooling | ||||
| +	 * the current version returned by the PHY. If 0 is returned, | ||||
| +	 * no firmware is loaded. | ||||
| +	 */ | ||||
| +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); | ||||
| +	if (ret > 0) | ||||
| +		goto exit; | ||||
| + | ||||
| +	ret = aqr_firmware_load_nvmem(phydev); | ||||
| +	if (!ret) | ||||
| +		goto exit; | ||||
| + | ||||
| +	ret = aqr_firmware_load_fs(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +exit: | ||||
| +	return 0; | ||||
| +} | ||||
| --- a/drivers/net/phy/aquantia/aquantia_main.c | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_main.c | ||||
| @@ -658,11 +658,17 @@ static int aqr107_resume(struct phy_devi | ||||
|   | ||||
|  static int aqr107_probe(struct phy_device *phydev) | ||||
|  { | ||||
| +	int ret; | ||||
| + | ||||
|  	phydev->priv = devm_kzalloc(&phydev->mdio.dev, | ||||
|  				    sizeof(struct aqr107_priv), GFP_KERNEL); | ||||
|  	if (!phydev->priv) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| +	ret = aqr_firmware_load(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
|  	return aqr_hwmon_probe(phydev); | ||||
|  } | ||||
|   | ||||
| @@ -1,69 +0,0 @@ | ||||
| From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:49 +0100 | ||||
| Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031 | ||||
|  probe | ||||
|  | ||||
| Move the WOL disable call to specific at8031 probe to make at803x_probe | ||||
| more generic and drop extra check for PHY ID. | ||||
|  | ||||
| Keep the same previous behaviour by first calling at803x_probe and then | ||||
| disabling WOL. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 27 +++++++++++++++++---------- | ||||
|  1 file changed, 17 insertions(+), 10 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic | ||||
|  			priv->is_fiber = true; | ||||
|  			break; | ||||
|  		} | ||||
| - | ||||
| -		/* Disable WoL in 1588 register which is enabled | ||||
| -		 * by default | ||||
| -		 */ | ||||
| -		ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, | ||||
| -				     AT803X_PHY_MMD3_WOL_CTRL, | ||||
| -				     AT803X_WOL_EN, 0); | ||||
| -		if (ret) | ||||
| -			return ret; | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
| @@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int at8031_probe(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = at803x_probe(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* Disable WoL in 1588 register which is enabled | ||||
| +	 * by default | ||||
| +	 */ | ||||
| +	return phy_modify_mmd(phydev, MDIO_MMD_PCS, | ||||
| +			      AT803X_PHY_MMD3_WOL_CTRL, | ||||
| +			      AT803X_WOL_EN, 0); | ||||
| +} | ||||
| + | ||||
|  static int qca83xx_config_init(struct phy_device *phydev) | ||||
|  { | ||||
|  	u8 switch_revision; | ||||
| @@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), | ||||
|  	.name			= "Qualcomm Atheros AR8031/AR8033", | ||||
|  	.flags			= PHY_POLL_CABLE_TEST, | ||||
| -	.probe			= at803x_probe, | ||||
| +	.probe			= at8031_probe, | ||||
|  	.config_init		= at803x_config_init, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
| @@ -1,129 +0,0 @@ | ||||
| From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:50 +0100 | ||||
| Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx | ||||
|  specific name | ||||
|  | ||||
| The function and the struct related to hw_stats were specific to qca83xx | ||||
| PHY but were called following the convention in the driver of calling | ||||
| everything with at803x prefix. | ||||
|  | ||||
| To better organize the code, rename these function a more specific name | ||||
| to better describe that they are specific to 83xx PHY family. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 44 ++++++++++++++++++++-------------------- | ||||
|  1 file changed, 22 insertions(+), 22 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -295,7 +295,7 @@ struct at803x_hw_stat { | ||||
|  	enum stat_access_type access_type; | ||||
|  }; | ||||
|   | ||||
| -static struct at803x_hw_stat at803x_hw_stats[] = { | ||||
| +static struct at803x_hw_stat qca83xx_hw_stats[] = { | ||||
|  	{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, | ||||
|  	{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, | ||||
|  	{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, | ||||
| @@ -311,7 +311,7 @@ struct at803x_priv { | ||||
|  	bool is_1000basex; | ||||
|  	struct regulator_dev *vddio_rdev; | ||||
|  	struct regulator_dev *vddh_rdev; | ||||
| -	u64 stats[ARRAY_SIZE(at803x_hw_stats)]; | ||||
| +	u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; | ||||
|  }; | ||||
|   | ||||
|  struct at803x_context { | ||||
| @@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de | ||||
|  		wol->wolopts |= WAKE_MAGIC; | ||||
|  } | ||||
|   | ||||
| -static int at803x_get_sset_count(struct phy_device *phydev) | ||||
| +static int qca83xx_get_sset_count(struct phy_device *phydev) | ||||
|  { | ||||
| -	return ARRAY_SIZE(at803x_hw_stats); | ||||
| +	return ARRAY_SIZE(qca83xx_hw_stats); | ||||
|  } | ||||
|   | ||||
| -static void at803x_get_strings(struct phy_device *phydev, u8 *data) | ||||
| +static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) | ||||
|  { | ||||
|  	int i; | ||||
|   | ||||
| -	for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) { | ||||
| +	for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { | ||||
|  		strscpy(data + i * ETH_GSTRING_LEN, | ||||
| -			at803x_hw_stats[i].string, ETH_GSTRING_LEN); | ||||
| +			qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static u64 at803x_get_stat(struct phy_device *phydev, int i) | ||||
| +static u64 qca83xx_get_stat(struct phy_device *phydev, int i) | ||||
|  { | ||||
| -	struct at803x_hw_stat stat = at803x_hw_stats[i]; | ||||
| +	struct at803x_hw_stat stat = qca83xx_hw_stats[i]; | ||||
|  	struct at803x_priv *priv = phydev->priv; | ||||
|  	int val; | ||||
|  	u64 ret; | ||||
| @@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static void at803x_get_stats(struct phy_device *phydev, | ||||
| -			     struct ethtool_stats *stats, u64 *data) | ||||
| +static void qca83xx_get_stats(struct phy_device *phydev, | ||||
| +			      struct ethtool_stats *stats, u64 *data) | ||||
|  { | ||||
|  	int i; | ||||
|   | ||||
| -	for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) | ||||
| -		data[i] = at803x_get_stat(phydev, i); | ||||
| +	for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) | ||||
| +		data[i] = qca83xx_get_stat(phydev, i); | ||||
|  } | ||||
|   | ||||
|  static int at803x_suspend(struct phy_device *phydev) | ||||
| @@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[] | ||||
|  	.flags			= PHY_IS_INTERNAL, | ||||
|  	.config_init		= qca83xx_config_init, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
| -	.get_sset_count		= at803x_get_sset_count, | ||||
| -	.get_strings		= at803x_get_strings, | ||||
| -	.get_stats		= at803x_get_stats, | ||||
| +	.get_sset_count		= qca83xx_get_sset_count, | ||||
| +	.get_strings		= qca83xx_get_strings, | ||||
| +	.get_stats		= qca83xx_get_stats, | ||||
|  	.suspend		= qca83xx_suspend, | ||||
|  	.resume			= qca83xx_resume, | ||||
|  }, { | ||||
| @@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[] | ||||
|  	.flags			= PHY_IS_INTERNAL, | ||||
|  	.config_init		= qca83xx_config_init, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
| -	.get_sset_count		= at803x_get_sset_count, | ||||
| -	.get_strings		= at803x_get_strings, | ||||
| -	.get_stats		= at803x_get_stats, | ||||
| +	.get_sset_count		= qca83xx_get_sset_count, | ||||
| +	.get_strings		= qca83xx_get_strings, | ||||
| +	.get_stats		= qca83xx_get_stats, | ||||
|  	.suspend		= qca83xx_suspend, | ||||
|  	.resume			= qca83xx_resume, | ||||
|  }, { | ||||
| @@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[] | ||||
|  	.flags			= PHY_IS_INTERNAL, | ||||
|  	.config_init		= qca83xx_config_init, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
| -	.get_sset_count		= at803x_get_sset_count, | ||||
| -	.get_strings		= at803x_get_strings, | ||||
| -	.get_stats		= at803x_get_stats, | ||||
| +	.get_sset_count		= qca83xx_get_sset_count, | ||||
| +	.get_strings		= qca83xx_get_strings, | ||||
| +	.get_stats		= qca83xx_get_stats, | ||||
|  	.suspend		= qca83xx_suspend, | ||||
|  	.resume			= qca83xx_resume, | ||||
|  }, { | ||||
| @@ -1,155 +0,0 @@ | ||||
| From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:51 +0100 | ||||
| Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in | ||||
|  dedicated functions | ||||
|  | ||||
| Rework qca83xx specific check to dedicated function to tidy things up | ||||
| and drop useless phy_id check. | ||||
|  | ||||
| Also drop an useless link_change_notify for QCA8337 as it did nothing an | ||||
| returned early. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------ | ||||
|  1 file changed, 37 insertions(+), 31 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph | ||||
|  		break; | ||||
|  	} | ||||
|   | ||||
| +	/* Following original QCA sourcecode set port to prefer master */ | ||||
| +	phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca8327_config_init(struct phy_device *phydev) | ||||
| +{ | ||||
|  	/* QCA8327 require DAC amplitude adjustment for 100m set to +6%. | ||||
|  	 * Disable on init and enable only with 100m speed following | ||||
|  	 * qca original source code. | ||||
|  	 */ | ||||
| -	if (phydev->drv->phy_id == QCA8327_A_PHY_ID || | ||||
| -	    phydev->drv->phy_id == QCA8327_B_PHY_ID) | ||||
| -		at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| -				      QCA8327_DEBUG_MANU_CTRL_EN, 0); | ||||
| +	at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| +			      QCA8327_DEBUG_MANU_CTRL_EN, 0); | ||||
|   | ||||
| -	/* Following original QCA sourcecode set port to prefer master */ | ||||
| -	phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); | ||||
| - | ||||
| -	return 0; | ||||
| +	return qca83xx_config_init(phydev); | ||||
|  } | ||||
|   | ||||
|  static void qca83xx_link_change_notify(struct phy_device *phydev) | ||||
|  { | ||||
| -	/* QCA8337 doesn't require DAC Amplitude adjustement */ | ||||
| -	if (phydev->drv->phy_id == QCA8337_PHY_ID) | ||||
| -		return; | ||||
| - | ||||
|  	/* Set DAC Amplitude adjustment to +6% for 100m on link running */ | ||||
|  	if (phydev->state == PHY_RUNNING) { | ||||
|  		if (phydev->speed == SPEED_100) | ||||
| @@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev | ||||
|   | ||||
|  static int qca83xx_suspend(struct phy_device *phydev) | ||||
|  { | ||||
| -	u16 mask = 0; | ||||
| - | ||||
| -	/* Only QCA8337 support actual suspend. | ||||
| -	 * QCA8327 cause port unreliability when phy suspend | ||||
| -	 * is set. | ||||
| -	 */ | ||||
| -	if (phydev->drv->phy_id == QCA8337_PHY_ID) { | ||||
| -		genphy_suspend(phydev); | ||||
| -	} else { | ||||
| -		mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); | ||||
| -		phy_modify(phydev, MII_BMCR, mask, 0); | ||||
| -	} | ||||
| - | ||||
|  	at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, | ||||
|  			      AT803X_DEBUG_GATE_CLK_IN1000, 0); | ||||
|   | ||||
| @@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int qca8337_suspend(struct phy_device *phydev) | ||||
| +{ | ||||
| +	/* Only QCA8337 support actual suspend. */ | ||||
| +	genphy_suspend(phydev); | ||||
| + | ||||
| +	return qca83xx_suspend(phydev); | ||||
| +} | ||||
| + | ||||
| +static int qca8327_suspend(struct phy_device *phydev) | ||||
| +{ | ||||
| +	u16 mask = 0; | ||||
| + | ||||
| +	/* QCA8327 cause port unreliability when phy suspend | ||||
| +	 * is set. | ||||
| +	 */ | ||||
| +	mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); | ||||
| +	phy_modify(phydev, MII_BMCR, mask, 0); | ||||
| + | ||||
| +	return qca83xx_suspend(phydev); | ||||
| +} | ||||
| + | ||||
|  static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) | ||||
|  { | ||||
|  	int ret; | ||||
| @@ -2170,7 +2177,6 @@ static struct phy_driver at803x_driver[] | ||||
|  	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
|  	.name			= "Qualcomm Atheros 8337 internal PHY", | ||||
|  	/* PHY_GBIT_FEATURES */ | ||||
| -	.link_change_notify	= qca83xx_link_change_notify, | ||||
|  	.probe			= at803x_probe, | ||||
|  	.flags			= PHY_IS_INTERNAL, | ||||
|  	.config_init		= qca83xx_config_init, | ||||
| @@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.get_sset_count		= qca83xx_get_sset_count, | ||||
|  	.get_strings		= qca83xx_get_strings, | ||||
|  	.get_stats		= qca83xx_get_stats, | ||||
| -	.suspend		= qca83xx_suspend, | ||||
| +	.suspend		= qca8337_suspend, | ||||
|  	.resume			= qca83xx_resume, | ||||
|  }, { | ||||
|  	/* QCA8327-A from switch QCA8327-AL1A */ | ||||
| @@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[] | ||||
|  	.link_change_notify	= qca83xx_link_change_notify, | ||||
|  	.probe			= at803x_probe, | ||||
|  	.flags			= PHY_IS_INTERNAL, | ||||
| -	.config_init		= qca83xx_config_init, | ||||
| +	.config_init		= qca8327_config_init, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
|  	.get_sset_count		= qca83xx_get_sset_count, | ||||
|  	.get_strings		= qca83xx_get_strings, | ||||
|  	.get_stats		= qca83xx_get_stats, | ||||
| -	.suspend		= qca83xx_suspend, | ||||
| +	.suspend		= qca8327_suspend, | ||||
|  	.resume			= qca83xx_resume, | ||||
|  }, { | ||||
|  	/* QCA8327-B from switch QCA8327-BL1A */ | ||||
| @@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[] | ||||
|  	.link_change_notify	= qca83xx_link_change_notify, | ||||
|  	.probe			= at803x_probe, | ||||
|  	.flags			= PHY_IS_INTERNAL, | ||||
| -	.config_init		= qca83xx_config_init, | ||||
| +	.config_init		= qca8327_config_init, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
|  	.get_sset_count		= qca83xx_get_sset_count, | ||||
|  	.get_strings		= qca83xx_get_strings, | ||||
|  	.get_stats		= qca83xx_get_stats, | ||||
| -	.suspend		= qca83xx_suspend, | ||||
| +	.suspend		= qca8327_suspend, | ||||
|  	.resume			= qca83xx_resume, | ||||
|  }, { | ||||
|  	/* Qualcomm QCA8081 */ | ||||
| @@ -1,94 +0,0 @@ | ||||
| From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:52 +0100 | ||||
| Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to | ||||
|  specific probe | ||||
|  | ||||
| Move specific DT options for at8031 to specific probe to tidy things up | ||||
| and make at803x_parse_dt more generic. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------ | ||||
|  1 file changed, 31 insertions(+), 24 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| -	/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping | ||||
| -	 * options. | ||||
| -	 */ | ||||
| -	if (phydev->drv->phy_id == ATH8031_PHY_ID) { | ||||
| -		if (of_property_read_bool(node, "qca,keep-pll-enabled")) | ||||
| -			priv->flags |= AT803X_KEEP_PLL_ENABLED; | ||||
| - | ||||
| -		ret = at8031_register_regulators(phydev); | ||||
| -		if (ret < 0) | ||||
| -			return ret; | ||||
| - | ||||
| -		ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, | ||||
| -							 "vddio"); | ||||
| -		if (ret) { | ||||
| -			phydev_err(phydev, "failed to get VDDIO regulator\n"); | ||||
| -			return ret; | ||||
| -		} | ||||
| - | ||||
| -		/* Only AR8031/8033 support 1000Base-X for SFP modules */ | ||||
| -		ret = phy_sfp_probe(phydev, &at803x_sfp_ops); | ||||
| -		if (ret < 0) | ||||
| -			return ret; | ||||
| -	} | ||||
| - | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int at8031_parse_dt(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct device_node *node = phydev->mdio.dev.of_node; | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (of_property_read_bool(node, "qca,keep-pll-enabled")) | ||||
| +		priv->flags |= AT803X_KEEP_PLL_ENABLED; | ||||
| + | ||||
| +	ret = at8031_register_regulators(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, | ||||
| +						 "vddio"); | ||||
| +	if (ret) { | ||||
| +		phydev_err(phydev, "failed to get VDDIO regulator\n"); | ||||
| +		return ret; | ||||
| +	} | ||||
| + | ||||
| +	/* Only AR8031/8033 support 1000Base-X for SFP modules */ | ||||
| +	return phy_sfp_probe(phydev, &at803x_sfp_ops); | ||||
| +} | ||||
| + | ||||
|  static int at8031_probe(struct phy_device *phydev) | ||||
|  { | ||||
|  	int ret; | ||||
| @@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| +	/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping | ||||
| +	 * options. | ||||
| +	 */ | ||||
| +	ret = at8031_parse_dt(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
|  	/* Disable WoL in 1588 register which is enabled | ||||
|  	 * by default | ||||
|  	 */ | ||||
| @@ -1,78 +0,0 @@ | ||||
| From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:53 +0100 | ||||
| Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check | ||||
|  to dedicated probe | ||||
|  | ||||
| Move specific at8031 probe mode check to dedicated probe to make | ||||
| at803x_probe more generic and keep code tidy. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 39 +++++++++++++++++++-------------------- | ||||
|  1 file changed, 19 insertions(+), 20 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	if (phydev->drv->phy_id == ATH8031_PHY_ID) { | ||||
| -		int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); | ||||
| -		int mode_cfg; | ||||
| - | ||||
| -		if (ccr < 0) | ||||
| -			return ccr; | ||||
| -		mode_cfg = ccr & AT803X_MODE_CFG_MASK; | ||||
| - | ||||
| -		switch (mode_cfg) { | ||||
| -		case AT803X_MODE_CFG_BX1000_RGMII_50OHM: | ||||
| -		case AT803X_MODE_CFG_BX1000_RGMII_75OHM: | ||||
| -			priv->is_1000basex = true; | ||||
| -			fallthrough; | ||||
| -		case AT803X_MODE_CFG_FX100_RGMII_50OHM: | ||||
| -		case AT803X_MODE_CFG_FX100_RGMII_75OHM: | ||||
| -			priv->is_fiber = true; | ||||
| -			break; | ||||
| -		} | ||||
| -	} | ||||
| - | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de | ||||
|   | ||||
|  static int at8031_probe(struct phy_device *phydev) | ||||
|  { | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	int mode_cfg; | ||||
| +	int ccr; | ||||
|  	int ret; | ||||
|   | ||||
|  	ret = at803x_probe(phydev); | ||||
| @@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| +	ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); | ||||
| +	if (ccr < 0) | ||||
| +		return ccr; | ||||
| +	mode_cfg = ccr & AT803X_MODE_CFG_MASK; | ||||
| + | ||||
| +	switch (mode_cfg) { | ||||
| +	case AT803X_MODE_CFG_BX1000_RGMII_50OHM: | ||||
| +	case AT803X_MODE_CFG_BX1000_RGMII_75OHM: | ||||
| +		priv->is_1000basex = true; | ||||
| +		fallthrough; | ||||
| +	case AT803X_MODE_CFG_FX100_RGMII_50OHM: | ||||
| +	case AT803X_MODE_CFG_FX100_RGMII_75OHM: | ||||
| +		priv->is_fiber = true; | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
|  	/* Disable WoL in 1588 register which is enabled | ||||
|  	 * by default | ||||
|  	 */ | ||||
| @@ -1,86 +0,0 @@ | ||||
| From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:54 +0100 | ||||
| Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to | ||||
|  dedicated function | ||||
|  | ||||
| Move specific at8031 config_init to dedicated function to make | ||||
| at803x_config_init more generic and tidy things up. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------ | ||||
|  1 file changed, 25 insertions(+), 20 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi | ||||
|   | ||||
|  static int at803x_config_init(struct phy_device *phydev) | ||||
|  { | ||||
| -	struct at803x_priv *priv = phydev->priv; | ||||
|  	int ret; | ||||
|   | ||||
| -	if (phydev->drv->phy_id == ATH8031_PHY_ID) { | ||||
| -		/* Some bootloaders leave the fiber page selected. | ||||
| -		 * Switch to the appropriate page (fiber or copper), as otherwise we | ||||
| -		 * read the PHY capabilities from the wrong page. | ||||
| -		 */ | ||||
| -		phy_lock_mdio_bus(phydev); | ||||
| -		ret = at803x_write_page(phydev, | ||||
| -					priv->is_fiber ? AT803X_PAGE_FIBER : | ||||
| -							 AT803X_PAGE_COPPER); | ||||
| -		phy_unlock_mdio_bus(phydev); | ||||
| -		if (ret) | ||||
| -			return ret; | ||||
| - | ||||
| -		ret = at8031_pll_config(phydev); | ||||
| -		if (ret < 0) | ||||
| -			return ret; | ||||
| -	} | ||||
| - | ||||
|  	/* The RX and TX delay default is: | ||||
|  	 *   after HW reset: RX delay enabled and TX delay disabled | ||||
|  	 *   after SW reset: RX delay enabled, while TX delay retains the | ||||
| @@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic | ||||
|  			      AT803X_WOL_EN, 0); | ||||
|  } | ||||
|   | ||||
| +static int at8031_config_init(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	int ret; | ||||
| + | ||||
| +	/* Some bootloaders leave the fiber page selected. | ||||
| +	 * Switch to the appropriate page (fiber or copper), as otherwise we | ||||
| +	 * read the PHY capabilities from the wrong page. | ||||
| +	 */ | ||||
| +	phy_lock_mdio_bus(phydev); | ||||
| +	ret = at803x_write_page(phydev, | ||||
| +				priv->is_fiber ? AT803X_PAGE_FIBER : | ||||
| +						 AT803X_PAGE_COPPER); | ||||
| +	phy_unlock_mdio_bus(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = at8031_pll_config(phydev); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	return at803x_config_init(phydev); | ||||
| +} | ||||
| + | ||||
|  static int qca83xx_config_init(struct phy_device *phydev) | ||||
|  { | ||||
|  	u8 switch_revision; | ||||
| @@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.name			= "Qualcomm Atheros AR8031/AR8033", | ||||
|  	.flags			= PHY_POLL_CABLE_TEST, | ||||
|  	.probe			= at8031_probe, | ||||
| -	.config_init		= at803x_config_init, | ||||
| +	.config_init		= at8031_config_init, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
|  	.set_wol		= at803x_set_wol, | ||||
| @@ -1,92 +0,0 @@ | ||||
| From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:55 +0100 | ||||
| Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to | ||||
|  dedicated function | ||||
|  | ||||
| Move specific at8031 WOL enable/disable to dedicated function to make | ||||
| at803x_set_wol more generic. | ||||
|  | ||||
| This is needed in preparation for PHY driver split as qca8081 share the | ||||
| same function to toggle WOL settings. | ||||
|  | ||||
| In this new implementation WOL module in at8031 is enabled after the | ||||
| generic interrupt is setup. This should not cause any problem as the | ||||
| WOL_INT has a separate implementation and only relay on MAC bits. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++---------------- | ||||
|  1 file changed, 25 insertions(+), 17 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev | ||||
|  			phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], | ||||
|  				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); | ||||
|   | ||||
| -		/* Enable WOL function for 1588 */ | ||||
| -		if (phydev->drv->phy_id == ATH8031_PHY_ID) { | ||||
| -			ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, | ||||
| -					     AT803X_PHY_MMD3_WOL_CTRL, | ||||
| -					     0, AT803X_WOL_EN); | ||||
| -			if (ret) | ||||
| -				return ret; | ||||
| -		} | ||||
|  		/* Enable WOL interrupt */ | ||||
|  		ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); | ||||
|  		if (ret) | ||||
|  			return ret; | ||||
|  	} else { | ||||
| -		/* Disable WoL function for 1588 */ | ||||
| -		if (phydev->drv->phy_id == ATH8031_PHY_ID) { | ||||
| -			ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, | ||||
| -					     AT803X_PHY_MMD3_WOL_CTRL, | ||||
| -					     AT803X_WOL_EN, 0); | ||||
| -			if (ret) | ||||
| -				return ret; | ||||
| -		} | ||||
|  		/* Disable WOL interrupt */ | ||||
|  		ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); | ||||
|  		if (ret) | ||||
| @@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy | ||||
|  	return at803x_config_init(phydev); | ||||
|  } | ||||
|   | ||||
| +static int at8031_set_wol(struct phy_device *phydev, | ||||
| +			  struct ethtool_wolinfo *wol) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	/* First setup MAC address and enable WOL interrupt */ | ||||
| +	ret = at803x_set_wol(phydev, wol); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	if (wol->wolopts & WAKE_MAGIC) | ||||
| +		/* Enable WOL function for 1588 */ | ||||
| +		ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, | ||||
| +				     AT803X_PHY_MMD3_WOL_CTRL, | ||||
| +				     0, AT803X_WOL_EN); | ||||
| +	else | ||||
| +		/* Disable WoL function for 1588 */ | ||||
| +		ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, | ||||
| +				     AT803X_PHY_MMD3_WOL_CTRL, | ||||
| +				     AT803X_WOL_EN, 0); | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  static int qca83xx_config_init(struct phy_device *phydev) | ||||
|  { | ||||
|  	u8 switch_revision; | ||||
| @@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.config_init		= at8031_config_init, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
| -	.set_wol		= at803x_set_wol, | ||||
| +	.set_wol		= at8031_set_wol, | ||||
|  	.get_wol		= at803x_get_wol, | ||||
|  	.suspend		= at803x_suspend, | ||||
|  	.resume			= at803x_resume, | ||||
| @@ -1,78 +0,0 @@ | ||||
| From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:56 +0100 | ||||
| Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to | ||||
|  dedicated function | ||||
|  | ||||
| Move specific at8031 config_intr bits to dedicated function to make | ||||
| at803x_config_initr more generic. | ||||
|  | ||||
| This is needed in preparation for PHY driver split as qca8081 share the | ||||
| same function to setup interrupts. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------ | ||||
|  1 file changed, 24 insertions(+), 6 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p | ||||
|   | ||||
|  static int at803x_config_intr(struct phy_device *phydev) | ||||
|  { | ||||
| -	struct at803x_priv *priv = phydev->priv; | ||||
|  	int err; | ||||
|  	int value; | ||||
|   | ||||
| @@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy | ||||
|  		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; | ||||
|  		value |= AT803X_INTR_ENABLE_LINK_FAIL; | ||||
|  		value |= AT803X_INTR_ENABLE_LINK_SUCCESS; | ||||
| -		if (priv->is_fiber) { | ||||
| -			value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; | ||||
| -			value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; | ||||
| -		} | ||||
|   | ||||
|  		err = phy_write(phydev, AT803X_INTR_ENABLE, value); | ||||
|  	} else { | ||||
| @@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static int at8031_config_intr(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	int err, value = 0; | ||||
| + | ||||
| +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED && | ||||
| +	    priv->is_fiber) { | ||||
| +		/* Clear any pending interrupts */ | ||||
| +		err = at803x_ack_interrupt(phydev); | ||||
| +		if (err) | ||||
| +			return err; | ||||
| + | ||||
| +		value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; | ||||
| +		value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; | ||||
| + | ||||
| +		err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); | ||||
| +		if (err) | ||||
| +			return err; | ||||
| +	} | ||||
| + | ||||
| +	return at803x_config_intr(phydev); | ||||
| +} | ||||
| + | ||||
|  static int qca83xx_config_init(struct phy_device *phydev) | ||||
|  { | ||||
|  	u8 switch_revision; | ||||
| @@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.write_page		= at803x_write_page, | ||||
|  	.get_features		= at803x_get_features, | ||||
|  	.read_status		= at803x_read_status, | ||||
| -	.config_intr		= at803x_config_intr, | ||||
| +	.config_intr		= at8031_config_intr, | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.get_tunable		= at803x_get_tunable, | ||||
|  	.set_tunable		= at803x_set_tunable, | ||||
| @@ -1,78 +0,0 @@ | ||||
| From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:57 +0100 | ||||
| Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name | ||||
|  more specific | ||||
|  | ||||
| Rename at8031 related DT function name to a more specific name | ||||
| referencing they are only related to at8031 and not to the generic | ||||
| at803x PHY family. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 16 ++++++++-------- | ||||
|  1 file changed, 8 insertions(+), 8 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi | ||||
|  	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); | ||||
|  } | ||||
|   | ||||
| -static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, | ||||
| +static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, | ||||
|  					    unsigned int selector) | ||||
|  { | ||||
|  	struct phy_device *phydev = rdev_get_drvdata(rdev); | ||||
| @@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_ | ||||
|  					     AT803X_DEBUG_RGMII_1V8, 0); | ||||
|  } | ||||
|   | ||||
| -static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) | ||||
| +static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) | ||||
|  { | ||||
|  	struct phy_device *phydev = rdev_get_drvdata(rdev); | ||||
|  	int val; | ||||
| @@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_ | ||||
|   | ||||
|  static const struct regulator_ops vddio_regulator_ops = { | ||||
|  	.list_voltage = regulator_list_voltage_table, | ||||
| -	.set_voltage_sel = at803x_rgmii_reg_set_voltage_sel, | ||||
| -	.get_voltage_sel = at803x_rgmii_reg_get_voltage_sel, | ||||
| +	.set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, | ||||
| +	.get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, | ||||
|  }; | ||||
|   | ||||
|  static const unsigned int vddio_voltage_table[] = { | ||||
| @@ -666,7 +666,7 @@ static int at8031_register_regulators(st | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) | ||||
| +static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) | ||||
|  { | ||||
|  	struct phy_device *phydev = upstream; | ||||
|  	__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); | ||||
| @@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static const struct sfp_upstream_ops at803x_sfp_ops = { | ||||
| +static const struct sfp_upstream_ops at8031_sfp_ops = { | ||||
|  	.attach = phy_sfp_attach, | ||||
|  	.detach = phy_sfp_detach, | ||||
| -	.module_insert = at803x_sfp_insert, | ||||
| +	.module_insert = at8031_sfp_insert, | ||||
|  }; | ||||
|   | ||||
|  static int at803x_parse_dt(struct phy_device *phydev) | ||||
| @@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de | ||||
|  	} | ||||
|   | ||||
|  	/* Only AR8031/8033 support 1000Base-X for SFP modules */ | ||||
| -	return phy_sfp_probe(phydev, &at803x_sfp_ops); | ||||
| +	return phy_sfp_probe(phydev, &at8031_sfp_ops); | ||||
|  } | ||||
|   | ||||
|  static int at8031_probe(struct phy_device *phydev) | ||||
| @@ -1,297 +0,0 @@ | ||||
| From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:58 +0100 | ||||
| Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated | ||||
|  section | ||||
|  | ||||
| Move at8031 functions in dedicated section with dedicated at8031 | ||||
| parse_dt and probe. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 266 +++++++++++++++++++-------------------- | ||||
|  1 file changed, 133 insertions(+), 133 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi | ||||
|  	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); | ||||
|  } | ||||
|   | ||||
| -static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, | ||||
| -					    unsigned int selector) | ||||
| -{ | ||||
| -	struct phy_device *phydev = rdev_get_drvdata(rdev); | ||||
| - | ||||
| -	if (selector) | ||||
| -		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, | ||||
| -					     0, AT803X_DEBUG_RGMII_1V8); | ||||
| -	else | ||||
| -		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, | ||||
| -					     AT803X_DEBUG_RGMII_1V8, 0); | ||||
| -} | ||||
| - | ||||
| -static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) | ||||
| -{ | ||||
| -	struct phy_device *phydev = rdev_get_drvdata(rdev); | ||||
| -	int val; | ||||
| - | ||||
| -	val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); | ||||
| -	if (val < 0) | ||||
| -		return val; | ||||
| - | ||||
| -	return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; | ||||
| -} | ||||
| - | ||||
| -static const struct regulator_ops vddio_regulator_ops = { | ||||
| -	.list_voltage = regulator_list_voltage_table, | ||||
| -	.set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, | ||||
| -	.get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, | ||||
| -}; | ||||
| - | ||||
| -static const unsigned int vddio_voltage_table[] = { | ||||
| -	1500000, | ||||
| -	1800000, | ||||
| -}; | ||||
| - | ||||
| -static const struct regulator_desc vddio_desc = { | ||||
| -	.name = "vddio", | ||||
| -	.of_match = of_match_ptr("vddio-regulator"), | ||||
| -	.n_voltages = ARRAY_SIZE(vddio_voltage_table), | ||||
| -	.volt_table = vddio_voltage_table, | ||||
| -	.ops = &vddio_regulator_ops, | ||||
| -	.type = REGULATOR_VOLTAGE, | ||||
| -	.owner = THIS_MODULE, | ||||
| -}; | ||||
| - | ||||
| -static const struct regulator_ops vddh_regulator_ops = { | ||||
| -}; | ||||
| - | ||||
| -static const struct regulator_desc vddh_desc = { | ||||
| -	.name = "vddh", | ||||
| -	.of_match = of_match_ptr("vddh-regulator"), | ||||
| -	.n_voltages = 1, | ||||
| -	.fixed_uV = 2500000, | ||||
| -	.ops = &vddh_regulator_ops, | ||||
| -	.type = REGULATOR_VOLTAGE, | ||||
| -	.owner = THIS_MODULE, | ||||
| -}; | ||||
| - | ||||
| -static int at8031_register_regulators(struct phy_device *phydev) | ||||
| -{ | ||||
| -	struct at803x_priv *priv = phydev->priv; | ||||
| -	struct device *dev = &phydev->mdio.dev; | ||||
| -	struct regulator_config config = { }; | ||||
| - | ||||
| -	config.dev = dev; | ||||
| -	config.driver_data = phydev; | ||||
| - | ||||
| -	priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); | ||||
| -	if (IS_ERR(priv->vddio_rdev)) { | ||||
| -		phydev_err(phydev, "failed to register VDDIO regulator\n"); | ||||
| -		return PTR_ERR(priv->vddio_rdev); | ||||
| -	} | ||||
| - | ||||
| -	priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); | ||||
| -	if (IS_ERR(priv->vddh_rdev)) { | ||||
| -		phydev_err(phydev, "failed to register VDDH regulator\n"); | ||||
| -		return PTR_ERR(priv->vddh_rdev); | ||||
| -	} | ||||
| - | ||||
| -	return 0; | ||||
| -} | ||||
| - | ||||
| -static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) | ||||
| -{ | ||||
| -	struct phy_device *phydev = upstream; | ||||
| -	__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); | ||||
| -	__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); | ||||
| -	DECLARE_PHY_INTERFACE_MASK(interfaces); | ||||
| -	phy_interface_t iface; | ||||
| - | ||||
| -	linkmode_zero(phy_support); | ||||
| -	phylink_set(phy_support, 1000baseX_Full); | ||||
| -	phylink_set(phy_support, 1000baseT_Full); | ||||
| -	phylink_set(phy_support, Autoneg); | ||||
| -	phylink_set(phy_support, Pause); | ||||
| -	phylink_set(phy_support, Asym_Pause); | ||||
| - | ||||
| -	linkmode_zero(sfp_support); | ||||
| -	sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); | ||||
| -	/* Some modules support 10G modes as well as others we support. | ||||
| -	 * Mask out non-supported modes so the correct interface is picked. | ||||
| -	 */ | ||||
| -	linkmode_and(sfp_support, phy_support, sfp_support); | ||||
| - | ||||
| -	if (linkmode_empty(sfp_support)) { | ||||
| -		dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); | ||||
| -		return -EINVAL; | ||||
| -	} | ||||
| - | ||||
| -	iface = sfp_select_interface(phydev->sfp_bus, sfp_support); | ||||
| - | ||||
| -	/* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes | ||||
| -	 * interface for use with SFP modules. | ||||
| -	 * However, some copper modules detected as having a preferred SGMII | ||||
| -	 * interface do default to and function in 1000Base-X mode, so just | ||||
| -	 * print a warning and allow such modules, as they may have some chance | ||||
| -	 * of working. | ||||
| -	 */ | ||||
| -	if (iface == PHY_INTERFACE_MODE_SGMII) | ||||
| -		dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); | ||||
| -	else if (iface != PHY_INTERFACE_MODE_1000BASEX) | ||||
| -		return -EINVAL; | ||||
| - | ||||
| -	return 0; | ||||
| -} | ||||
| - | ||||
| -static const struct sfp_upstream_ops at8031_sfp_ops = { | ||||
| -	.attach = phy_sfp_attach, | ||||
| -	.detach = phy_sfp_detach, | ||||
| -	.module_insert = at8031_sfp_insert, | ||||
| -}; | ||||
| - | ||||
|  static int at803x_parse_dt(struct phy_device *phydev) | ||||
|  { | ||||
|  	struct device_node *node = phydev->mdio.dev.of_node; | ||||
| @@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, | ||||
| +					    unsigned int selector) | ||||
| +{ | ||||
| +	struct phy_device *phydev = rdev_get_drvdata(rdev); | ||||
| + | ||||
| +	if (selector) | ||||
| +		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, | ||||
| +					     0, AT803X_DEBUG_RGMII_1V8); | ||||
| +	else | ||||
| +		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, | ||||
| +					     AT803X_DEBUG_RGMII_1V8, 0); | ||||
| +} | ||||
| + | ||||
| +static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) | ||||
| +{ | ||||
| +	struct phy_device *phydev = rdev_get_drvdata(rdev); | ||||
| +	int val; | ||||
| + | ||||
| +	val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); | ||||
| +	if (val < 0) | ||||
| +		return val; | ||||
| + | ||||
| +	return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; | ||||
| +} | ||||
| + | ||||
| +static const struct regulator_ops vddio_regulator_ops = { | ||||
| +	.list_voltage = regulator_list_voltage_table, | ||||
| +	.set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, | ||||
| +	.get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, | ||||
| +}; | ||||
| + | ||||
| +static const unsigned int vddio_voltage_table[] = { | ||||
| +	1500000, | ||||
| +	1800000, | ||||
| +}; | ||||
| + | ||||
| +static const struct regulator_desc vddio_desc = { | ||||
| +	.name = "vddio", | ||||
| +	.of_match = of_match_ptr("vddio-regulator"), | ||||
| +	.n_voltages = ARRAY_SIZE(vddio_voltage_table), | ||||
| +	.volt_table = vddio_voltage_table, | ||||
| +	.ops = &vddio_regulator_ops, | ||||
| +	.type = REGULATOR_VOLTAGE, | ||||
| +	.owner = THIS_MODULE, | ||||
| +}; | ||||
| + | ||||
| +static const struct regulator_ops vddh_regulator_ops = { | ||||
| +}; | ||||
| + | ||||
| +static const struct regulator_desc vddh_desc = { | ||||
| +	.name = "vddh", | ||||
| +	.of_match = of_match_ptr("vddh-regulator"), | ||||
| +	.n_voltages = 1, | ||||
| +	.fixed_uV = 2500000, | ||||
| +	.ops = &vddh_regulator_ops, | ||||
| +	.type = REGULATOR_VOLTAGE, | ||||
| +	.owner = THIS_MODULE, | ||||
| +}; | ||||
| + | ||||
| +static int at8031_register_regulators(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	struct device *dev = &phydev->mdio.dev; | ||||
| +	struct regulator_config config = { }; | ||||
| + | ||||
| +	config.dev = dev; | ||||
| +	config.driver_data = phydev; | ||||
| + | ||||
| +	priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); | ||||
| +	if (IS_ERR(priv->vddio_rdev)) { | ||||
| +		phydev_err(phydev, "failed to register VDDIO regulator\n"); | ||||
| +		return PTR_ERR(priv->vddio_rdev); | ||||
| +	} | ||||
| + | ||||
| +	priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); | ||||
| +	if (IS_ERR(priv->vddh_rdev)) { | ||||
| +		phydev_err(phydev, "failed to register VDDH regulator\n"); | ||||
| +		return PTR_ERR(priv->vddh_rdev); | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) | ||||
| +{ | ||||
| +	struct phy_device *phydev = upstream; | ||||
| +	__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); | ||||
| +	__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); | ||||
| +	DECLARE_PHY_INTERFACE_MASK(interfaces); | ||||
| +	phy_interface_t iface; | ||||
| + | ||||
| +	linkmode_zero(phy_support); | ||||
| +	phylink_set(phy_support, 1000baseX_Full); | ||||
| +	phylink_set(phy_support, 1000baseT_Full); | ||||
| +	phylink_set(phy_support, Autoneg); | ||||
| +	phylink_set(phy_support, Pause); | ||||
| +	phylink_set(phy_support, Asym_Pause); | ||||
| + | ||||
| +	linkmode_zero(sfp_support); | ||||
| +	sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); | ||||
| +	/* Some modules support 10G modes as well as others we support. | ||||
| +	 * Mask out non-supported modes so the correct interface is picked. | ||||
| +	 */ | ||||
| +	linkmode_and(sfp_support, phy_support, sfp_support); | ||||
| + | ||||
| +	if (linkmode_empty(sfp_support)) { | ||||
| +		dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	iface = sfp_select_interface(phydev->sfp_bus, sfp_support); | ||||
| + | ||||
| +	/* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes | ||||
| +	 * interface for use with SFP modules. | ||||
| +	 * However, some copper modules detected as having a preferred SGMII | ||||
| +	 * interface do default to and function in 1000Base-X mode, so just | ||||
| +	 * print a warning and allow such modules, as they may have some chance | ||||
| +	 * of working. | ||||
| +	 */ | ||||
| +	if (iface == PHY_INTERFACE_MODE_SGMII) | ||||
| +		dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); | ||||
| +	else if (iface != PHY_INTERFACE_MODE_1000BASEX) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct sfp_upstream_ops at8031_sfp_ops = { | ||||
| +	.attach = phy_sfp_attach, | ||||
| +	.detach = phy_sfp_detach, | ||||
| +	.module_insert = at8031_sfp_insert, | ||||
| +}; | ||||
| + | ||||
|  static int at8031_parse_dt(struct phy_device *phydev) | ||||
|  { | ||||
|  	struct device_node *node = phydev->mdio.dev.of_node; | ||||
| @@ -1,114 +0,0 @@ | ||||
| From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:51:59 +0100 | ||||
| Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to | ||||
|  dedicated probe | ||||
|  | ||||
| Move at8035 specific DT parse for clock out frequency to dedicated probe | ||||
| to make at803x probe function more generic. | ||||
|  | ||||
| This is to tidy code and no behaviour change are intended. | ||||
|  | ||||
| Detection logic is changed, we check if the clk 25m mask is set and if | ||||
| it's not zero, we assume the qca,clk-out-frequency property is set. | ||||
|  | ||||
| The property is checked in the generic at803x_parse_dt called by | ||||
| at803x_probe. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++------------- | ||||
|  1 file changed, 41 insertions(+), 19 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de | ||||
|   | ||||
|  		priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); | ||||
|  		priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; | ||||
| - | ||||
| -		/* Fixup for the AR8030/AR8035. This chip has another mask and | ||||
| -		 * doesn't support the DSP reference. Eg. the lowest bit of the | ||||
| -		 * mask. The upper two bits select the same frequencies. Mask | ||||
| -		 * the lowest bit here. | ||||
| -		 * | ||||
| -		 * Warning: | ||||
| -		 *   There was no datasheet for the AR8030 available so this is | ||||
| -		 *   just a guess. But the AR8035 is listed as pin compatible | ||||
| -		 *   to the AR8030 so there might be a good chance it works on | ||||
| -		 *   the AR8030 too. | ||||
| -		 */ | ||||
| -		if (phydev->drv->phy_id == ATH8030_PHY_ID || | ||||
| -		    phydev->drv->phy_id == ATH8035_PHY_ID) { | ||||
| -			priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; | ||||
| -			priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; | ||||
| -		} | ||||
|  	} | ||||
|   | ||||
|  	ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); | ||||
| @@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy | ||||
|  	return at803x_config_intr(phydev); | ||||
|  } | ||||
|   | ||||
| +static int at8035_parse_dt(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| + | ||||
| +	/* Mask is set by the generic at803x_parse_dt | ||||
| +	 * if property is set. Assume property is set | ||||
| +	 * with the mask not zero. | ||||
| +	 */ | ||||
| +	if (priv->clk_25m_mask) { | ||||
| +		/* Fixup for the AR8030/AR8035. This chip has another mask and | ||||
| +		 * doesn't support the DSP reference. Eg. the lowest bit of the | ||||
| +		 * mask. The upper two bits select the same frequencies. Mask | ||||
| +		 * the lowest bit here. | ||||
| +		 * | ||||
| +		 * Warning: | ||||
| +		 *   There was no datasheet for the AR8030 available so this is | ||||
| +		 *   just a guess. But the AR8035 is listed as pin compatible | ||||
| +		 *   to the AR8030 so there might be a good chance it works on | ||||
| +		 *   the AR8030 too. | ||||
| +		 */ | ||||
| +		priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; | ||||
| +		priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* AR8030 and AR8035 shared the same special mask for clk_25m */ | ||||
| +static int at8035_probe(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = at803x_probe(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return at8035_parse_dt(phydev); | ||||
| +} | ||||
| + | ||||
|  static int qca83xx_config_init(struct phy_device *phydev) | ||||
|  { | ||||
|  	u8 switch_revision; | ||||
| @@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), | ||||
|  	.name			= "Qualcomm Atheros AR8035", | ||||
|  	.flags			= PHY_POLL_CABLE_TEST, | ||||
| -	.probe			= at803x_probe, | ||||
| +	.probe			= at8035_probe, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
|  	.config_init		= at803x_config_init, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
| @@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.phy_id			= ATH8030_PHY_ID, | ||||
|  	.name			= "Qualcomm Atheros AR8030", | ||||
|  	.phy_id_mask		= AT8030_PHY_ID_MASK, | ||||
| -	.probe			= at803x_probe, | ||||
| +	.probe			= at8035_probe, | ||||
|  	.config_init		= at803x_config_init, | ||||
|  	.link_change_notify	= at803x_link_change_notify, | ||||
|  	.set_wol		= at803x_set_wol, | ||||
| @@ -1,219 +0,0 @@ | ||||
| From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Fri, 8 Dec 2023 15:52:00 +0100 | ||||
| Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable | ||||
|  test functions | ||||
|  | ||||
| Drop specific PHY ID check for cable test functions for at803x. This is | ||||
| done to make functions more generic. While at it better describe what | ||||
| the functions does by using more symbolic function names. | ||||
|  | ||||
| PHYs that requires to set additional reg are moved to specific function | ||||
| calling the more generic one. | ||||
|  | ||||
| cdt_start and cdt_wait_for_completion are changed to take an additional | ||||
| arg to pass specific values specific to the PHY. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 95 +++++++++++++++++++++------------------- | ||||
|  1 file changed, 50 insertions(+), 45 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s | ||||
|  	return (dt * 824) / 10; | ||||
|  } | ||||
|   | ||||
| -static int at803x_cdt_start(struct phy_device *phydev, int pair) | ||||
| +static int at803x_cdt_start(struct phy_device *phydev, | ||||
| +			    u32 cdt_start) | ||||
|  { | ||||
| -	u16 cdt; | ||||
| - | ||||
| -	/* qca8081 takes the different bit 15 to enable CDT test */ | ||||
| -	if (phydev->drv->phy_id == QCA8081_PHY_ID) | ||||
| -		cdt = QCA808X_CDT_ENABLE_TEST | | ||||
| -			QCA808X_CDT_LENGTH_UNIT | | ||||
| -			QCA808X_CDT_INTER_CHECK_DIS; | ||||
| -	else | ||||
| -		cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | | ||||
| -			AT803X_CDT_ENABLE_TEST; | ||||
| - | ||||
| -	return phy_write(phydev, AT803X_CDT, cdt); | ||||
| +	return phy_write(phydev, AT803X_CDT, cdt_start); | ||||
|  } | ||||
|   | ||||
| -static int at803x_cdt_wait_for_completion(struct phy_device *phydev) | ||||
| +static int at803x_cdt_wait_for_completion(struct phy_device *phydev, | ||||
| +					  u32 cdt_en) | ||||
|  { | ||||
|  	int val, ret; | ||||
| -	u16 cdt_en; | ||||
| - | ||||
| -	if (phydev->drv->phy_id == QCA8081_PHY_ID) | ||||
| -		cdt_en = QCA808X_CDT_ENABLE_TEST; | ||||
| -	else | ||||
| -		cdt_en = AT803X_CDT_ENABLE_TEST; | ||||
|   | ||||
|  	/* One test run takes about 25ms */ | ||||
|  	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, | ||||
| @@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st | ||||
|  	}; | ||||
|  	int ret, val; | ||||
|   | ||||
| -	ret = at803x_cdt_start(phydev, pair); | ||||
| +	val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | | ||||
| +	      AT803X_CDT_ENABLE_TEST; | ||||
| +	ret = at803x_cdt_start(phydev, val); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	ret = at803x_cdt_wait_for_completion(phydev); | ||||
| +	ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st | ||||
|  } | ||||
|   | ||||
|  static int at803x_cable_test_get_status(struct phy_device *phydev, | ||||
| -					bool *finished) | ||||
| +					bool *finished, unsigned long pair_mask) | ||||
|  { | ||||
| -	unsigned long pair_mask; | ||||
|  	int retries = 20; | ||||
|  	int pair, ret; | ||||
|   | ||||
| -	if (phydev->phy_id == ATH9331_PHY_ID || | ||||
| -	    phydev->phy_id == ATH8032_PHY_ID || | ||||
| -	    phydev->phy_id == QCA9561_PHY_ID) | ||||
| -		pair_mask = 0x3; | ||||
| -	else | ||||
| -		pair_mask = 0xf; | ||||
| - | ||||
|  	*finished = false; | ||||
|   | ||||
|  	/* According to the datasheet the CDT can be performed when | ||||
| @@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status( | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static int at803x_cable_test_start(struct phy_device *phydev) | ||||
| +static void at803x_cable_test_autoneg(struct phy_device *phydev) | ||||
|  { | ||||
|  	/* Enable auto-negotiation, but advertise no capabilities, no link | ||||
|  	 * will be established. A restart of the auto-negotiation is not | ||||
| @@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc | ||||
|  	 */ | ||||
|  	phy_write(phydev, MII_BMCR, BMCR_ANENABLE); | ||||
|  	phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); | ||||
| -	if (phydev->phy_id != ATH9331_PHY_ID && | ||||
| -	    phydev->phy_id != ATH8032_PHY_ID && | ||||
| -	    phydev->phy_id != QCA9561_PHY_ID) | ||||
| -		phy_write(phydev, MII_CTRL1000, 0); | ||||
| +} | ||||
|   | ||||
| +static int at803x_cable_test_start(struct phy_device *phydev) | ||||
| +{ | ||||
| +	at803x_cable_test_autoneg(phydev); | ||||
|  	/* we do all the (time consuming) work later */ | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy | ||||
|  	return at803x_config_intr(phydev); | ||||
|  } | ||||
|   | ||||
| +/* AR8031 and AR8035 share the same cable test get status reg */ | ||||
| +static int at8031_cable_test_get_status(struct phy_device *phydev, | ||||
| +					bool *finished) | ||||
| +{ | ||||
| +	return at803x_cable_test_get_status(phydev, finished, 0xf); | ||||
| +} | ||||
| + | ||||
| +/* AR8031 and AR8035 share the same cable test start logic */ | ||||
| +static int at8031_cable_test_start(struct phy_device *phydev) | ||||
| +{ | ||||
| +	at803x_cable_test_autoneg(phydev); | ||||
| +	phy_write(phydev, MII_CTRL1000, 0); | ||||
| +	/* we do all the (time consuming) work later */ | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ | ||||
| +static int at8032_cable_test_get_status(struct phy_device *phydev, | ||||
| +					bool *finished) | ||||
| +{ | ||||
| +	return at803x_cable_test_get_status(phydev, finished, 0x3); | ||||
| +} | ||||
| + | ||||
|  static int at8035_parse_dt(struct phy_device *phydev) | ||||
|  { | ||||
|  	struct at803x_priv *priv = phydev->priv; | ||||
| @@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status | ||||
|   | ||||
|  	*finished = false; | ||||
|   | ||||
| -	ret = at803x_cdt_start(phydev, 0); | ||||
| +	val = QCA808X_CDT_ENABLE_TEST | | ||||
| +	      QCA808X_CDT_LENGTH_UNIT | | ||||
| +	      QCA808X_CDT_INTER_CHECK_DIS; | ||||
| +	ret = at803x_cdt_start(phydev, val); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	ret = at803x_cdt_wait_for_completion(phydev); | ||||
| +	ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[] | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.get_tunable		= at803x_get_tunable, | ||||
|  	.set_tunable		= at803x_set_tunable, | ||||
| -	.cable_test_start	= at803x_cable_test_start, | ||||
| -	.cable_test_get_status	= at803x_cable_test_get_status, | ||||
| +	.cable_test_start	= at8031_cable_test_start, | ||||
| +	.cable_test_get_status	= at8031_cable_test_get_status, | ||||
|  }, { | ||||
|  	/* Qualcomm Atheros AR8030 */ | ||||
|  	.phy_id			= ATH8030_PHY_ID, | ||||
| @@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[] | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.get_tunable		= at803x_get_tunable, | ||||
|  	.set_tunable		= at803x_set_tunable, | ||||
| -	.cable_test_start	= at803x_cable_test_start, | ||||
| -	.cable_test_get_status	= at803x_cable_test_get_status, | ||||
| +	.cable_test_start	= at8031_cable_test_start, | ||||
| +	.cable_test_get_status	= at8031_cable_test_get_status, | ||||
|  }, { | ||||
|  	/* Qualcomm Atheros AR8032 */ | ||||
|  	PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), | ||||
| @@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.config_intr		= at803x_config_intr, | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.cable_test_start	= at803x_cable_test_start, | ||||
| -	.cable_test_get_status	= at803x_cable_test_get_status, | ||||
| +	.cable_test_get_status	= at8032_cable_test_get_status, | ||||
|  }, { | ||||
|  	/* ATHEROS AR9331 */ | ||||
|  	PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), | ||||
| @@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.config_intr		= at803x_config_intr, | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.cable_test_start	= at803x_cable_test_start, | ||||
| -	.cable_test_get_status	= at803x_cable_test_get_status, | ||||
| +	.cable_test_get_status	= at8032_cable_test_get_status, | ||||
|  	.read_status		= at803x_read_status, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
| @@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.config_intr		= at803x_config_intr, | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.cable_test_start	= at803x_cable_test_start, | ||||
| -	.cable_test_get_status	= at803x_cable_test_get_status, | ||||
| +	.cable_test_get_status	= at8032_cable_test_get_status, | ||||
|  	.read_status		= at803x_read_status, | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
| @@ -1,116 +0,0 @@ | ||||
| From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 14 Dec 2023 01:44:31 +0100 | ||||
| Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to | ||||
|  dedicated function | ||||
|  | ||||
| Move specific qca808x config_aneg to dedicated function to permit easier | ||||
| split of qca808x portion from at803x driver. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++---------------- | ||||
|  1 file changed, 40 insertions(+), 26 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy | ||||
|  			  FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); | ||||
|  } | ||||
|   | ||||
| -static int at803x_config_aneg(struct phy_device *phydev) | ||||
| +static int at803x_prepare_config_aneg(struct phy_device *phydev) | ||||
|  { | ||||
| -	struct at803x_priv *priv = phydev->priv; | ||||
|  	int ret; | ||||
|   | ||||
|  	ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); | ||||
| @@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy | ||||
|  			return ret; | ||||
|  	} | ||||
|   | ||||
| -	if (priv->is_1000basex) | ||||
| -		return genphy_c37_config_aneg(phydev); | ||||
| - | ||||
| -	/* Do not restart auto-negotiation by setting ret to 0 defautly, | ||||
| -	 * when calling __genphy_config_aneg later. | ||||
| -	 */ | ||||
| -	ret = 0; | ||||
| - | ||||
| -	if (phydev->drv->phy_id == QCA8081_PHY_ID) { | ||||
| -		int phy_ctrl = 0; | ||||
| +	return 0; | ||||
| +} | ||||
|   | ||||
| -		/* The reg MII_BMCR also needs to be configured for force mode, the | ||||
| -		 * genphy_config_aneg is also needed. | ||||
| -		 */ | ||||
| -		if (phydev->autoneg == AUTONEG_DISABLE) | ||||
| -			genphy_c45_pma_setup_forced(phydev); | ||||
| +static int at803x_config_aneg(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	int ret; | ||||
|   | ||||
| -		if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) | ||||
| -			phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; | ||||
| +	ret = at803x_prepare_config_aneg(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
|   | ||||
| -		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, | ||||
| -				MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); | ||||
| -		if (ret < 0) | ||||
| -			return ret; | ||||
| -	} | ||||
| +	if (priv->is_1000basex) | ||||
| +		return genphy_c37_config_aneg(phydev); | ||||
|   | ||||
| -	return __genphy_config_aneg(phydev, ret); | ||||
| +	return genphy_config_aneg(phydev); | ||||
|  } | ||||
|   | ||||
|  static int at803x_get_downshift(struct phy_device *phydev, u8 *d) | ||||
| @@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int qca808x_config_aneg(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int phy_ctrl = 0; | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = at803x_prepare_config_aneg(phydev); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* The reg MII_BMCR also needs to be configured for force mode, the | ||||
| +	 * genphy_config_aneg is also needed. | ||||
| +	 */ | ||||
| +	if (phydev->autoneg == AUTONEG_DISABLE) | ||||
| +		genphy_c45_pma_setup_forced(phydev); | ||||
| + | ||||
| +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) | ||||
| +		phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; | ||||
| + | ||||
| +	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, | ||||
| +				     MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	return __genphy_config_aneg(phydev, ret); | ||||
| +} | ||||
| + | ||||
|  static void qca808x_link_change_notify(struct phy_device *phydev) | ||||
|  { | ||||
|  	/* Assert interface sgmii fifo on link down, deassert it on link up, | ||||
| @@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.set_wol		= at803x_set_wol, | ||||
|  	.get_wol		= at803x_get_wol, | ||||
|  	.get_features		= qca808x_get_features, | ||||
| -	.config_aneg		= at803x_config_aneg, | ||||
| +	.config_aneg		= qca808x_config_aneg, | ||||
|  	.suspend		= genphy_suspend, | ||||
|  	.resume			= genphy_resume, | ||||
|  	.read_status		= qca808x_read_status, | ||||
| @@ -1,97 +0,0 @@ | ||||
| From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 14 Dec 2023 01:44:32 +0100 | ||||
| Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more | ||||
|  generic | ||||
|  | ||||
| Rework read specific status function to be more generic. The function | ||||
| apply different speed mask based on the PHY ID. Make it more generic by | ||||
| adding an additional arg to pass the specific speed (ss) mask and use | ||||
| the provided mask to parse the speed value. | ||||
|  | ||||
| This is needed to permit an easier deatch of qca808x code from the | ||||
| at803x driver. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 26 ++++++++++++++++++-------- | ||||
|  1 file changed, 18 insertions(+), 8 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_ | ||||
|  	{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, | ||||
|  }; | ||||
|   | ||||
| +struct at803x_ss_mask { | ||||
| +	u16 speed_mask; | ||||
| +	u8 speed_shift; | ||||
| +}; | ||||
| + | ||||
|  struct at803x_priv { | ||||
|  	int flags; | ||||
|  	u16 clk_25m_reg; | ||||
| @@ -921,7 +926,8 @@ static void at803x_link_change_notify(st | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static int at803x_read_specific_status(struct phy_device *phydev) | ||||
| +static int at803x_read_specific_status(struct phy_device *phydev, | ||||
| +				       struct at803x_ss_mask ss_mask) | ||||
|  { | ||||
|  	int ss; | ||||
|   | ||||
| @@ -940,11 +946,8 @@ static int at803x_read_specific_status(s | ||||
|  		if (sfc < 0) | ||||
|  			return sfc; | ||||
|   | ||||
| -		/* qca8081 takes the different bits for speed value from at803x */ | ||||
| -		if (phydev->drv->phy_id == QCA8081_PHY_ID) | ||||
| -			speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss); | ||||
| -		else | ||||
| -			speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss); | ||||
| +		speed = ss & ss_mask.speed_mask; | ||||
| +		speed >>= ss_mask.speed_shift; | ||||
|   | ||||
|  		switch (speed) { | ||||
|  		case AT803X_SS_SPEED_10: | ||||
| @@ -989,6 +992,7 @@ static int at803x_read_specific_status(s | ||||
|  static int at803x_read_status(struct phy_device *phydev) | ||||
|  { | ||||
|  	struct at803x_priv *priv = phydev->priv; | ||||
| +	struct at803x_ss_mask ss_mask = { 0 }; | ||||
|  	int err, old_link = phydev->link; | ||||
|   | ||||
|  	if (priv->is_1000basex) | ||||
| @@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy | ||||
|  	if (err < 0) | ||||
|  		return err; | ||||
|   | ||||
| -	err = at803x_read_specific_status(phydev); | ||||
| +	ss_mask.speed_mask = AT803X_SS_SPEED_MASK; | ||||
| +	ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); | ||||
| +	err = at803x_read_specific_status(phydev, ss_mask); | ||||
|  	if (err < 0) | ||||
|  		return err; | ||||
|   | ||||
| @@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph | ||||
|   | ||||
|  static int qca808x_read_status(struct phy_device *phydev) | ||||
|  { | ||||
| +	struct at803x_ss_mask ss_mask = { 0 }; | ||||
|  	int ret; | ||||
|   | ||||
|  	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); | ||||
| @@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	ret = at803x_read_specific_status(phydev); | ||||
| +	/* qca8081 takes the different bits for speed value from at803x */ | ||||
| +	ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; | ||||
| +	ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); | ||||
| +	ret = at803x_read_specific_status(phydev, ss_mask); | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Mon, 18 Dec 2023 00:27:39 +0100 | ||||
| Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast | ||||
|  | ||||
| Remove extra space after cast as reported by checkpatch to keep code | ||||
| clean. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev | ||||
|  		if (!ndev) | ||||
|  			return -ENODEV; | ||||
|   | ||||
| -		mac = (const u8 *) ndev->dev_addr; | ||||
| +		mac = (const u8 *)ndev->dev_addr; | ||||
|   | ||||
|  		if (!is_valid_ether_addr(mac)) | ||||
|  			return -EINVAL; | ||||
| @@ -1,38 +0,0 @@ | ||||
| From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Mon, 18 Dec 2023 00:25:08 +0100 | ||||
| Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range | ||||
|  | ||||
| Replace msleep(1) with usleep_range as suggested by timers-howto guide. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 6 +++--- | ||||
|  1 file changed, 3 insertions(+), 3 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -916,9 +916,9 @@ static void at803x_link_change_notify(st | ||||
|  		at803x_context_save(phydev, &context); | ||||
|   | ||||
|  		phy_device_reset(phydev, 1); | ||||
| -		msleep(1); | ||||
| +		usleep_range(1000, 2000); | ||||
|  		phy_device_reset(phydev, 0); | ||||
| -		msleep(1); | ||||
| +		usleep_range(1000, 2000); | ||||
|   | ||||
|  		at803x_context_restore(phydev, &context); | ||||
|   | ||||
| @@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	msleep(1); | ||||
| +	usleep_range(1000, 2000); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1,152 +0,0 @@ | ||||
| From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Tue, 19 Dec 2023 21:21:24 +0100 | ||||
| Subject: [PATCH] net: phy: at803x: better align function varibles to open | ||||
|  parenthesis | ||||
|  | ||||
| Better align function variables to open parenthesis as suggested by | ||||
| checkpatch script for qca808x function to make code cleaner. | ||||
|  | ||||
| For cable_test_get_status function some additional rework was needed to | ||||
| handle too long functions. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------ | ||||
|  1 file changed, 37 insertions(+), 30 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf | ||||
|  		return ret; | ||||
|   | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, | ||||
| -			QCA808X_TOP_OPTION1_DATA); | ||||
| +		      QCA808X_TOP_OPTION1_DATA); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, | ||||
| -			QCA808X_MSE_THRESHOLD_20DB_VALUE); | ||||
| +		      QCA808X_MSE_THRESHOLD_20DB_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, | ||||
| -			QCA808X_MSE_THRESHOLD_17DB_VALUE); | ||||
| +		      QCA808X_MSE_THRESHOLD_17DB_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, | ||||
| -			QCA808X_MSE_THRESHOLD_27DB_VALUE); | ||||
| +		      QCA808X_MSE_THRESHOLD_27DB_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, | ||||
| -			QCA808X_MSE_THRESHOLD_28DB_VALUE); | ||||
| +		      QCA808X_MSE_THRESHOLD_28DB_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, | ||||
| -			QCA808X_MMD3_DEBUG_1_VALUE); | ||||
| +		      QCA808X_MMD3_DEBUG_1_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, | ||||
| -			QCA808X_MMD3_DEBUG_4_VALUE); | ||||
| +		      QCA808X_MMD3_DEBUG_4_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, | ||||
| -			QCA808X_MMD3_DEBUG_5_VALUE); | ||||
| +		      QCA808X_MMD3_DEBUG_5_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, | ||||
| -			QCA808X_MMD3_DEBUG_3_VALUE); | ||||
| +		      QCA808X_MMD3_DEBUG_3_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, | ||||
| -			QCA808X_MMD3_DEBUG_6_VALUE); | ||||
| +		      QCA808X_MMD3_DEBUG_6_VALUE); | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, | ||||
| -			QCA808X_MMD3_DEBUG_2_VALUE); | ||||
| +		      QCA808X_MMD3_DEBUG_2_VALUE); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph | ||||
|   | ||||
|  	/* Active adc&vga on 802.3az for the link 1000M and 100M */ | ||||
|  	ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, | ||||
| -			QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); | ||||
| +			     QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
|  	/* Adjust the threshold on 802.3az for the link 1000M */ | ||||
|  	ret = phy_write_mmd(phydev, MDIO_MMD_PCS, | ||||
| -			QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL); | ||||
| +			    QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, | ||||
| +			    QCA808X_MMD3_AZ_TRAINING_VAL); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph | ||||
|   | ||||
|  	/* Configure adc threshold as 100mv for the link 10M */ | ||||
|  	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, | ||||
| -			QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV); | ||||
| +				     QCA808X_ADC_THRESHOLD_MASK, | ||||
| +				     QCA808X_ADC_THRESHOLD_100MV); | ||||
|  } | ||||
|   | ||||
|  static int qca808x_read_status(struct phy_device *phydev) | ||||
| @@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph | ||||
|  		return ret; | ||||
|   | ||||
|  	linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, | ||||
| -			ret & MDIO_AN_10GBT_STAT_LP2_5G); | ||||
| +			 ret & MDIO_AN_10GBT_STAT_LP2_5G); | ||||
|   | ||||
|  	ret = genphy_read_status(phydev); | ||||
|  	if (ret) | ||||
| @@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph | ||||
|  		 */ | ||||
|  		if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { | ||||
|  			if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || | ||||
| -					qca808x_is_prefer_master(phydev)) { | ||||
| +			    qca808x_is_prefer_master(phydev)) { | ||||
|  				qca808x_phy_ms_seed_enable(phydev, false); | ||||
|  			} else { | ||||
|  				qca808x_phy_ms_seed_enable(phydev, true); | ||||
| @@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status | ||||
|  	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, | ||||
|  				qca808x_cable_test_result_trans(pair_d)); | ||||
|   | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_a)) | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, | ||||
| -				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A)); | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_b)) | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, | ||||
| -				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B)); | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_c)) | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, | ||||
| -				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C)); | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_d)) | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, | ||||
| -				qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D)); | ||||
| +	if (qca808x_cdt_fault_length_valid(pair_a)) { | ||||
| +		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); | ||||
| +		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); | ||||
| +	} | ||||
| +	if (qca808x_cdt_fault_length_valid(pair_b)) { | ||||
| +		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); | ||||
| +		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); | ||||
| +	} | ||||
| +	if (qca808x_cdt_fault_length_valid(pair_c)) { | ||||
| +		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); | ||||
| +		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); | ||||
| +	} | ||||
| +	if (qca808x_cdt_fault_length_valid(pair_d)) { | ||||
| +		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); | ||||
| +		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); | ||||
| +	} | ||||
|   | ||||
|  	*finished = true; | ||||
|   | ||||
| @@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s | ||||
|  	 * the interface device address is always phy address added by 1. | ||||
|  	 */ | ||||
|  	mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, | ||||
| -			MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, | ||||
| -			QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); | ||||
| +				   MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, | ||||
| +				   QCA8081_PHY_FIFO_RSTN, | ||||
| +				   phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); | ||||
|  } | ||||
|   | ||||
|  static struct phy_driver at803x_driver[] = { | ||||
| @@ -1,62 +0,0 @@ | ||||
| From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 4 Jan 2024 22:30:38 +0100 | ||||
| Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function | ||||
|  | ||||
| Generalize cable test fault length function since they all base on the | ||||
| same magic values (already reverse engineered to understand the meaning | ||||
| of it) to have consistenct values on every PHY. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 13 ++++++------- | ||||
|  1 file changed, 6 insertions(+), 7 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali | ||||
|  	return false; | ||||
|  } | ||||
|   | ||||
| -static int at803x_cdt_fault_length(u16 status) | ||||
| +static int at803x_cdt_fault_length(int dt) | ||||
|  { | ||||
| -	int dt; | ||||
| - | ||||
|  	/* According to the datasheet the distance to the fault is | ||||
|  	 * DELTA_TIME * 0.824 meters. | ||||
|  	 * | ||||
| @@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s | ||||
|  	 * With a VF of 0.69 we get the factor 0.824 mentioned in the | ||||
|  	 * datasheet. | ||||
|  	 */ | ||||
| -	dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status); | ||||
| - | ||||
|  	return (dt * 824) / 10; | ||||
|  } | ||||
|   | ||||
| @@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st | ||||
|  	ethnl_cable_test_result(phydev, ethtool_pair[pair], | ||||
|  				at803x_cable_test_result_trans(val)); | ||||
|   | ||||
| -	if (at803x_cdt_fault_length_valid(val)) | ||||
| +	if (at803x_cdt_fault_length_valid(val)) { | ||||
| +		val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); | ||||
|  		ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], | ||||
|  					      at803x_cdt_fault_length(val)); | ||||
| +	} | ||||
|   | ||||
|  	return 1; | ||||
|  } | ||||
| @@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru | ||||
|  	if (val < 0) | ||||
|  		return val; | ||||
|   | ||||
| -	return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10; | ||||
| +	val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val); | ||||
| +	return at803x_cdt_fault_length(val); | ||||
|  } | ||||
|   | ||||
|  static int qca808x_cable_test_start(struct phy_device *phydev) | ||||
| @@ -1,118 +0,0 @@ | ||||
| From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 4 Jan 2024 22:30:39 +0100 | ||||
| Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status | ||||
|  function | ||||
|  | ||||
| Refactor qca808x cable test get status function to remove code | ||||
| duplication and clean things up. | ||||
|  | ||||
| The same logic is applied to each pair hence it can be generalized and | ||||
| moved to a common function. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++---------------- | ||||
|  1 file changed, 49 insertions(+), 31 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, | ||||
| +					      u16 status) | ||||
| +{ | ||||
| +	u16 pair_code; | ||||
| +	int length; | ||||
| + | ||||
| +	switch (pair) { | ||||
| +	case ETHTOOL_A_CABLE_PAIR_A: | ||||
| +		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); | ||||
| +		break; | ||||
| +	case ETHTOOL_A_CABLE_PAIR_B: | ||||
| +		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); | ||||
| +		break; | ||||
| +	case ETHTOOL_A_CABLE_PAIR_C: | ||||
| +		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); | ||||
| +		break; | ||||
| +	case ETHTOOL_A_CABLE_PAIR_D: | ||||
| +		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); | ||||
| +		break; | ||||
| +	default: | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	ethnl_cable_test_result(phydev, pair, | ||||
| +				qca808x_cable_test_result_trans(pair_code)); | ||||
| + | ||||
| +	if (qca808x_cdt_fault_length_valid(pair_code)) { | ||||
| +		length = qca808x_cdt_fault_length(phydev, pair); | ||||
| +		ethnl_cable_test_fault_length(phydev, pair, length); | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) | ||||
|  { | ||||
|  	int ret, val; | ||||
| -	int pair_a, pair_b, pair_c, pair_d; | ||||
|   | ||||
|  	*finished = false; | ||||
|   | ||||
| @@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status | ||||
|  	if (val < 0) | ||||
|  		return val; | ||||
|   | ||||
| -	pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val); | ||||
| -	pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val); | ||||
| -	pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val); | ||||
| -	pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val); | ||||
| - | ||||
| -	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, | ||||
| -				qca808x_cable_test_result_trans(pair_a)); | ||||
| -	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, | ||||
| -				qca808x_cable_test_result_trans(pair_b)); | ||||
| -	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, | ||||
| -				qca808x_cable_test_result_trans(pair_c)); | ||||
| -	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, | ||||
| -				qca808x_cable_test_result_trans(pair_d)); | ||||
| - | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_a)) { | ||||
| -		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); | ||||
| -	} | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_b)) { | ||||
| -		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); | ||||
| -	} | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_c)) { | ||||
| -		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); | ||||
| -	} | ||||
| -	if (qca808x_cdt_fault_length_valid(pair_d)) { | ||||
| -		val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); | ||||
| -		ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); | ||||
| -	} | ||||
| +	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
|   | ||||
|  	*finished = true; | ||||
|   | ||||
| @@ -1,182 +0,0 @@ | ||||
| From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 4 Jan 2024 22:30:40 +0100 | ||||
| Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test | ||||
|  for qca808x | ||||
|  | ||||
| QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair | ||||
| Short. | ||||
|  | ||||
| Add all the define to make enable and support these additional tests. | ||||
|  | ||||
| Cross Short test was previously disabled by default, this is now changed | ||||
| and enabled by default. In this mode, the mask changed a bit and length | ||||
| is shifted based on the fault condition. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++-------- | ||||
|  1 file changed, 69 insertions(+), 17 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -254,6 +254,7 @@ | ||||
|   | ||||
|  #define QCA808X_CDT_ENABLE_TEST			BIT(15) | ||||
|  #define QCA808X_CDT_INTER_CHECK_DIS		BIT(13) | ||||
| +#define QCA808X_CDT_STATUS			BIT(11) | ||||
|  #define QCA808X_CDT_LENGTH_UNIT			BIT(10) | ||||
|   | ||||
|  #define QCA808X_MMD3_CDT_STATUS			0x8064 | ||||
| @@ -261,16 +262,44 @@ | ||||
|  #define QCA808X_MMD3_CDT_DIAG_PAIR_B		0x8066 | ||||
|  #define QCA808X_MMD3_CDT_DIAG_PAIR_C		0x8067 | ||||
|  #define QCA808X_MMD3_CDT_DIAG_PAIR_D		0x8068 | ||||
| -#define QCA808X_CDT_DIAG_LENGTH			GENMASK(7, 0) | ||||
| +#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT	GENMASK(15, 8) | ||||
| +#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT	GENMASK(7, 0) | ||||
|   | ||||
|  #define QCA808X_CDT_CODE_PAIR_A			GENMASK(15, 12) | ||||
|  #define QCA808X_CDT_CODE_PAIR_B			GENMASK(11, 8) | ||||
|  #define QCA808X_CDT_CODE_PAIR_C			GENMASK(7, 4) | ||||
|  #define QCA808X_CDT_CODE_PAIR_D			GENMASK(3, 0) | ||||
| -#define QCA808X_CDT_STATUS_STAT_FAIL		0 | ||||
| -#define QCA808X_CDT_STATUS_STAT_NORMAL		1 | ||||
| -#define QCA808X_CDT_STATUS_STAT_OPEN		2 | ||||
| -#define QCA808X_CDT_STATUS_STAT_SHORT		3 | ||||
| + | ||||
| +#define QCA808X_CDT_STATUS_STAT_TYPE		GENMASK(1, 0) | ||||
| +#define QCA808X_CDT_STATUS_STAT_FAIL		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) | ||||
| +#define QCA808X_CDT_STATUS_STAT_NORMAL		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) | ||||
| +#define QCA808X_CDT_STATUS_STAT_SAME_OPEN	FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) | ||||
| +#define QCA808X_CDT_STATUS_STAT_SAME_SHORT	FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) | ||||
| + | ||||
| +#define QCA808X_CDT_STATUS_STAT_MDI		GENMASK(3, 2) | ||||
| +#define QCA808X_CDT_STATUS_STAT_MDI1		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) | ||||
| +#define QCA808X_CDT_STATUS_STAT_MDI2		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) | ||||
| +#define QCA808X_CDT_STATUS_STAT_MDI3		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) | ||||
| + | ||||
| +/* NORMAL are MDI with type set to 0 */ | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI1 | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ | ||||
| +									 QCA808X_CDT_STATUS_STAT_MDI1) | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ | ||||
| +									 QCA808X_CDT_STATUS_STAT_MDI1) | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI2 | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ | ||||
| +									 QCA808X_CDT_STATUS_STAT_MDI2) | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ | ||||
| +									 QCA808X_CDT_STATUS_STAT_MDI2) | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI3 | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ | ||||
| +									 QCA808X_CDT_STATUS_STAT_MDI3) | ||||
| +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ | ||||
| +									 QCA808X_CDT_STATUS_STAT_MDI3) | ||||
| + | ||||
| +/* Added for reference of existence but should be handled by wait_for_completion already */ | ||||
| +#define QCA808X_CDT_STATUS_STAT_BUSY		(BIT(1) | BIT(3)) | ||||
|   | ||||
|  /* QCA808X 1G chip type */ | ||||
|  #define QCA808X_PHY_MMD7_CHIP_TYPE		0x901d | ||||
| @@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy | ||||
|  static bool qca808x_cdt_fault_length_valid(int cdt_code) | ||||
|  { | ||||
|  	switch (cdt_code) { | ||||
| -	case QCA808X_CDT_STATUS_STAT_SHORT: | ||||
| -	case QCA808X_CDT_STATUS_STAT_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_SAME_SHORT: | ||||
| +	case QCA808X_CDT_STATUS_STAT_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: | ||||
|  		return true; | ||||
|  	default: | ||||
|  		return false; | ||||
| @@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra | ||||
|  	switch (cdt_code) { | ||||
|  	case QCA808X_CDT_STATUS_STAT_NORMAL: | ||||
|  		return ETHTOOL_A_CABLE_RESULT_CODE_OK; | ||||
| -	case QCA808X_CDT_STATUS_STAT_SHORT: | ||||
| +	case QCA808X_CDT_STATUS_STAT_SAME_SHORT: | ||||
|  		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; | ||||
| -	case QCA808X_CDT_STATUS_STAT_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_SAME_OPEN: | ||||
|  		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: | ||||
| +	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: | ||||
| +		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; | ||||
|  	case QCA808X_CDT_STATUS_STAT_FAIL: | ||||
|  	default: | ||||
|  		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair) | ||||
| +static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, | ||||
| +				    int result) | ||||
|  { | ||||
|  	int val; | ||||
|  	u32 cdt_length_reg = 0; | ||||
| @@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru | ||||
|  	if (val < 0) | ||||
|  		return val; | ||||
|   | ||||
| -	val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val); | ||||
| +	if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) | ||||
| +		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); | ||||
| +	else | ||||
| +		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); | ||||
| + | ||||
|  	return at803x_cdt_fault_length(val); | ||||
|  } | ||||
|   | ||||
| @@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru | ||||
|  static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, | ||||
|  					      u16 status) | ||||
|  { | ||||
| +	int length, result; | ||||
|  	u16 pair_code; | ||||
| -	int length; | ||||
|   | ||||
|  	switch (pair) { | ||||
|  	case ETHTOOL_A_CABLE_PAIR_A: | ||||
| @@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	ethnl_cable_test_result(phydev, pair, | ||||
| -				qca808x_cable_test_result_trans(pair_code)); | ||||
| +	result = qca808x_cable_test_result_trans(pair_code); | ||||
| +	ethnl_cable_test_result(phydev, pair, result); | ||||
|   | ||||
|  	if (qca808x_cdt_fault_length_valid(pair_code)) { | ||||
| -		length = qca808x_cdt_fault_length(phydev, pair); | ||||
| +		length = qca808x_cdt_fault_length(phydev, pair, result); | ||||
|  		ethnl_cable_test_fault_length(phydev, pair, length); | ||||
|  	} | ||||
|   | ||||
| @@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status | ||||
|  	*finished = false; | ||||
|   | ||||
|  	val = QCA808X_CDT_ENABLE_TEST | | ||||
| -	      QCA808X_CDT_LENGTH_UNIT | | ||||
| -	      QCA808X_CDT_INTER_CHECK_DIS; | ||||
| +	      QCA808X_CDT_LENGTH_UNIT; | ||||
|  	ret = at803x_cdt_start(phydev, val); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
| @@ -1,62 +0,0 @@ | ||||
| From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 4 Jan 2024 22:30:41 +0100 | ||||
| Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic | ||||
|  | ||||
| Make read_status more generic in preparation on moving it to shared | ||||
| library as other PHY Family Driver will have the exact same | ||||
| implementation. | ||||
|  | ||||
| The only specific part was a check for AR8031/33 if 1000basex was used. | ||||
| The check is moved to a dedicated function specific for those PHYs. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 17 ++++++++++++----- | ||||
|  1 file changed, 12 insertions(+), 5 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s | ||||
|   | ||||
|  static int at803x_read_status(struct phy_device *phydev) | ||||
|  { | ||||
| -	struct at803x_priv *priv = phydev->priv; | ||||
|  	struct at803x_ss_mask ss_mask = { 0 }; | ||||
|  	int err, old_link = phydev->link; | ||||
|   | ||||
| -	if (priv->is_1000basex) | ||||
| -		return genphy_c37_read_status(phydev); | ||||
| - | ||||
|  	/* Update the link, but return if there was an error */ | ||||
|  	err = genphy_update_link(phydev); | ||||
|  	if (err) | ||||
| @@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy | ||||
|  	return at803x_config_intr(phydev); | ||||
|  } | ||||
|   | ||||
| +/* AR8031 and AR8033 share the same read status logic */ | ||||
| +static int at8031_read_status(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| + | ||||
| +	if (priv->is_1000basex) | ||||
| +		return genphy_c37_read_status(phydev); | ||||
| + | ||||
| +	return at803x_read_status(phydev); | ||||
| +} | ||||
| + | ||||
|  /* AR8031 and AR8035 share the same cable test get status reg */ | ||||
|  static int at8031_cable_test_get_status(struct phy_device *phydev, | ||||
|  					bool *finished) | ||||
| @@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[] | ||||
|  	.read_page		= at803x_read_page, | ||||
|  	.write_page		= at803x_write_page, | ||||
|  	.get_features		= at803x_get_features, | ||||
| -	.read_status		= at803x_read_status, | ||||
| +	.read_status		= at8031_read_status, | ||||
|  	.config_intr		= at8031_config_intr, | ||||
|  	.handle_interrupt	= at803x_handle_interrupt, | ||||
|  	.get_tunable		= at803x_get_tunable, | ||||
| @@ -1,408 +0,0 @@ | ||||
| From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Thu, 25 Jan 2024 21:37:01 +0100 | ||||
| Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x | ||||
|  | ||||
| Add LED support for QCA8081 PHY. | ||||
|  | ||||
| Documentation for this LEDs PHY is very scarce even with NDA access | ||||
| to Documentation for OEMs. Only the blink pattern are documented and are | ||||
| very confusing most of the time. No documentation is present about | ||||
| forcing the LED on/off or to always blink. | ||||
|  | ||||
| Those settings were reversed by poking the regs and trying to find the | ||||
| correct bits to trigger these modes. Some bits mode are not clear and | ||||
| maybe the documentation option are not 100% correct. For the sake of LED | ||||
| support the reversed option are enough to add support for current LED | ||||
| APIs. | ||||
|  | ||||
| Supported HW control modes are: | ||||
| - tx | ||||
| - rx | ||||
| - link_10 | ||||
| - link_100 | ||||
| - link_1000 | ||||
| - link_2500 | ||||
| - half_duplex | ||||
| - full_duplex | ||||
|  | ||||
| Also add support for LED polarity set to set LED polarity to active | ||||
| high or low. QSDK sets this value to high by default but PHY reset value | ||||
| doesn't have this enabled by default. | ||||
|  | ||||
| QSDK also sets 2 additional bits but their usage is not clear, info about | ||||
| this is added in the header. It was verified that for correct function | ||||
| of the LED if active high is needed, only BIT 6 is needed. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 327 insertions(+) | ||||
|  | ||||
| --- a/drivers/net/phy/at803x.c | ||||
| +++ b/drivers/net/phy/at803x.c | ||||
| @@ -301,6 +301,87 @@ | ||||
|  /* Added for reference of existence but should be handled by wait_for_completion already */ | ||||
|  #define QCA808X_CDT_STATUS_STAT_BUSY		(BIT(1) | BIT(3)) | ||||
|   | ||||
| +#define QCA808X_MMD7_LED_GLOBAL			0x8073 | ||||
| +#define QCA808X_LED_BLINK_1			GENMASK(11, 6) | ||||
| +#define QCA808X_LED_BLINK_2			GENMASK(5, 0) | ||||
| +/* Values are the same for both BLINK_1 and BLINK_2 */ | ||||
| +#define QCA808X_LED_BLINK_FREQ_MASK		GENMASK(5, 3) | ||||
| +#define QCA808X_LED_BLINK_FREQ_2HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) | ||||
| +#define QCA808X_LED_BLINK_FREQ_4HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) | ||||
| +#define QCA808X_LED_BLINK_FREQ_8HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) | ||||
| +#define QCA808X_LED_BLINK_FREQ_16HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) | ||||
| +#define QCA808X_LED_BLINK_FREQ_32HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) | ||||
| +#define QCA808X_LED_BLINK_FREQ_64HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) | ||||
| +#define QCA808X_LED_BLINK_FREQ_128HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) | ||||
| +#define QCA808X_LED_BLINK_FREQ_256HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) | ||||
| +#define QCA808X_LED_BLINK_DUTY_MASK		GENMASK(2, 0) | ||||
| +#define QCA808X_LED_BLINK_DUTY_50_50		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) | ||||
| +#define QCA808X_LED_BLINK_DUTY_75_25		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) | ||||
| +#define QCA808X_LED_BLINK_DUTY_25_75		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) | ||||
| +#define QCA808X_LED_BLINK_DUTY_33_67		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) | ||||
| +#define QCA808X_LED_BLINK_DUTY_67_33		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) | ||||
| +#define QCA808X_LED_BLINK_DUTY_17_83		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) | ||||
| +#define QCA808X_LED_BLINK_DUTY_83_17		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) | ||||
| +#define QCA808X_LED_BLINK_DUTY_8_92		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) | ||||
| + | ||||
| +#define QCA808X_MMD7_LED2_CTRL			0x8074 | ||||
| +#define QCA808X_MMD7_LED2_FORCE_CTRL		0x8075 | ||||
| +#define QCA808X_MMD7_LED1_CTRL			0x8076 | ||||
| +#define QCA808X_MMD7_LED1_FORCE_CTRL		0x8077 | ||||
| +#define QCA808X_MMD7_LED0_CTRL			0x8078 | ||||
| +#define QCA808X_MMD7_LED_CTRL(x)		(0x8078 - ((x) * 2)) | ||||
| + | ||||
| +/* LED hw control pattern is the same for every LED */ | ||||
| +#define QCA808X_LED_PATTERN_MASK		GENMASK(15, 0) | ||||
| +#define QCA808X_LED_SPEED2500_ON		BIT(15) | ||||
| +#define QCA808X_LED_SPEED2500_BLINK		BIT(14) | ||||
| +/* Follow blink trigger even if duplex or speed condition doesn't match */ | ||||
| +#define QCA808X_LED_BLINK_CHECK_BYPASS		BIT(13) | ||||
| +#define QCA808X_LED_FULL_DUPLEX_ON		BIT(12) | ||||
| +#define QCA808X_LED_HALF_DUPLEX_ON		BIT(11) | ||||
| +#define QCA808X_LED_TX_BLINK			BIT(10) | ||||
| +#define QCA808X_LED_RX_BLINK			BIT(9) | ||||
| +#define QCA808X_LED_TX_ON_10MS			BIT(8) | ||||
| +#define QCA808X_LED_RX_ON_10MS			BIT(7) | ||||
| +#define QCA808X_LED_SPEED1000_ON		BIT(6) | ||||
| +#define QCA808X_LED_SPEED100_ON			BIT(5) | ||||
| +#define QCA808X_LED_SPEED10_ON			BIT(4) | ||||
| +#define QCA808X_LED_COLLISION_BLINK		BIT(3) | ||||
| +#define QCA808X_LED_SPEED1000_BLINK		BIT(2) | ||||
| +#define QCA808X_LED_SPEED100_BLINK		BIT(1) | ||||
| +#define QCA808X_LED_SPEED10_BLINK		BIT(0) | ||||
| + | ||||
| +#define QCA808X_MMD7_LED0_FORCE_CTRL		0x8079 | ||||
| +#define QCA808X_MMD7_LED_FORCE_CTRL(x)		(0x8079 - ((x) * 2)) | ||||
| + | ||||
| +/* LED force ctrl is the same for every LED | ||||
| + * No documentation exist for this, not even internal one | ||||
| + * with NDA as QCOM gives only info about configuring | ||||
| + * hw control pattern rules and doesn't indicate any way | ||||
| + * to force the LED to specific mode. | ||||
| + * These define comes from reverse and testing and maybe | ||||
| + * lack of some info or some info are not entirely correct. | ||||
| + * For the basic LED control and hw control these finding | ||||
| + * are enough to support LED control in all the required APIs. | ||||
| + * | ||||
| + * On doing some comparison with implementation with qca807x, | ||||
| + * it was found that it's 1:1 equal to it and confirms all the | ||||
| + * reverse done. It was also found further specification with the | ||||
| + * force mode and the blink modes. | ||||
| + */ | ||||
| +#define QCA808X_LED_FORCE_EN			BIT(15) | ||||
| +#define QCA808X_LED_FORCE_MODE_MASK		GENMASK(14, 13) | ||||
| +#define QCA808X_LED_FORCE_BLINK_1		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) | ||||
| +#define QCA808X_LED_FORCE_BLINK_2		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) | ||||
| +#define QCA808X_LED_FORCE_ON			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) | ||||
| +#define QCA808X_LED_FORCE_OFF			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) | ||||
| + | ||||
| +#define QCA808X_MMD7_LED_POLARITY_CTRL		0x901a | ||||
| +/* QSDK sets by default 0x46 to this reg that sets BIT 6 for | ||||
| + * LED to active high. It's not clear what BIT 3 and BIT 4 does. | ||||
| + */ | ||||
| +#define QCA808X_LED_ACTIVE_HIGH			BIT(6) | ||||
| + | ||||
|  /* QCA808X 1G chip type */ | ||||
|  #define QCA808X_PHY_MMD7_CHIP_TYPE		0x901d | ||||
|  #define QCA808X_PHY_CHIP_TYPE_1G		BIT(0) | ||||
| @@ -346,6 +427,7 @@ struct at803x_priv { | ||||
|  	struct regulator_dev *vddio_rdev; | ||||
|  	struct regulator_dev *vddh_rdev; | ||||
|  	u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; | ||||
| +	int led_polarity_mode; | ||||
|  }; | ||||
|   | ||||
|  struct at803x_context { | ||||
| @@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic | ||||
|  	if (!priv) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| +	/* Init LED polarity mode to -1 */ | ||||
| +	priv->led_polarity_mode = -1; | ||||
| + | ||||
|  	phydev->priv = priv; | ||||
|   | ||||
|  	ret = at803x_parse_dt(phydev); | ||||
| @@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s | ||||
|  				   phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); | ||||
|  } | ||||
|   | ||||
| +static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, | ||||
| +				    u16 *offload_trigger) | ||||
| +{ | ||||
| +	/* Parsing specific to netdev trigger */ | ||||
| +	if (test_bit(TRIGGER_NETDEV_TX, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_TX_BLINK; | ||||
| +	if (test_bit(TRIGGER_NETDEV_RX, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_RX_BLINK; | ||||
| +	if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_SPEED10_ON; | ||||
| +	if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_SPEED100_ON; | ||||
| +	if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_SPEED1000_ON; | ||||
| +	if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_SPEED2500_ON; | ||||
| +	if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; | ||||
| +	if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) | ||||
| +		*offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; | ||||
| + | ||||
| +	if (rules && !*offload_trigger) | ||||
| +		return -EOPNOTSUPP; | ||||
| + | ||||
| +	/* Enable BLINK_CHECK_BYPASS by default to make the LED | ||||
| +	 * blink even with duplex or speed mode not enabled. | ||||
| +	 */ | ||||
| +	*offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) | ||||
| +{ | ||||
| +	u16 reg; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_FORCE_CTRL(index); | ||||
| + | ||||
| +	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, | ||||
| +				  QCA808X_LED_FORCE_EN); | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, | ||||
| +				       unsigned long rules) | ||||
| +{ | ||||
| +	u16 offload_trigger = 0; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, | ||||
| +				      unsigned long rules) | ||||
| +{ | ||||
| +	u16 reg, offload_trigger = 0; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_CTRL(index); | ||||
| + | ||||
| +	ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ret = qca808x_led_hw_control_enable(phydev, index); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, | ||||
| +			      QCA808X_LED_PATTERN_MASK, | ||||
| +			      offload_trigger); | ||||
| +} | ||||
| + | ||||
| +static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) | ||||
| +{ | ||||
| +	u16 reg; | ||||
| +	int val; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return false; | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_FORCE_CTRL(index); | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); | ||||
| + | ||||
| +	return !(val & QCA808X_LED_FORCE_EN); | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, | ||||
| +				      unsigned long *rules) | ||||
| +{ | ||||
| +	u16 reg; | ||||
| +	int val; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	/* Check if we have hw control enabled */ | ||||
| +	if (qca808x_led_hw_control_status(phydev, index)) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_CTRL(index); | ||||
| + | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); | ||||
| +	if (val & QCA808X_LED_TX_BLINK) | ||||
| +		set_bit(TRIGGER_NETDEV_TX, rules); | ||||
| +	if (val & QCA808X_LED_RX_BLINK) | ||||
| +		set_bit(TRIGGER_NETDEV_RX, rules); | ||||
| +	if (val & QCA808X_LED_SPEED10_ON) | ||||
| +		set_bit(TRIGGER_NETDEV_LINK_10, rules); | ||||
| +	if (val & QCA808X_LED_SPEED100_ON) | ||||
| +		set_bit(TRIGGER_NETDEV_LINK_100, rules); | ||||
| +	if (val & QCA808X_LED_SPEED1000_ON) | ||||
| +		set_bit(TRIGGER_NETDEV_LINK_1000, rules); | ||||
| +	if (val & QCA808X_LED_SPEED2500_ON) | ||||
| +		set_bit(TRIGGER_NETDEV_LINK_2500, rules); | ||||
| +	if (val & QCA808X_LED_HALF_DUPLEX_ON) | ||||
| +		set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); | ||||
| +	if (val & QCA808X_LED_FULL_DUPLEX_ON) | ||||
| +		set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) | ||||
| +{ | ||||
| +	u16 reg; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_CTRL(index); | ||||
| + | ||||
| +	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, | ||||
| +				  QCA808X_LED_PATTERN_MASK); | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_brightness_set(struct phy_device *phydev, | ||||
| +				      u8 index, enum led_brightness value) | ||||
| +{ | ||||
| +	u16 reg; | ||||
| +	int ret; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	if (!value) { | ||||
| +		ret = qca808x_led_hw_control_reset(phydev, index); | ||||
| +		if (ret) | ||||
| +			return ret; | ||||
| +	} | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_FORCE_CTRL(index); | ||||
| + | ||||
| +	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, | ||||
| +			      QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, | ||||
| +			      QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : | ||||
| +							     QCA808X_LED_FORCE_OFF); | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, | ||||
| +				 unsigned long *delay_on, | ||||
| +				 unsigned long *delay_off) | ||||
| +{ | ||||
| +	int ret; | ||||
| +	u16 reg; | ||||
| + | ||||
| +	if (index > 2) | ||||
| +		return -EINVAL; | ||||
| + | ||||
| +	reg = QCA808X_MMD7_LED_FORCE_CTRL(index); | ||||
| + | ||||
| +	/* Set blink to 50% off, 50% on at 4Hz by default */ | ||||
| +	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, | ||||
| +			     QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, | ||||
| +			     QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* We use BLINK_1 for normal blinking */ | ||||
| +	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, | ||||
| +			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, | ||||
| +			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* We set blink to 4Hz, aka 250ms */ | ||||
| +	*delay_on = 250 / 2; | ||||
| +	*delay_off = 250 / 2; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca808x_led_polarity_set(struct phy_device *phydev, int index, | ||||
| +				    unsigned long modes) | ||||
| +{ | ||||
| +	struct at803x_priv *priv = phydev->priv; | ||||
| +	bool active_low = false; | ||||
| +	u32 mode; | ||||
| + | ||||
| +	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { | ||||
| +		switch (mode) { | ||||
| +		case PHY_LED_ACTIVE_LOW: | ||||
| +			active_low = true; | ||||
| +			break; | ||||
| +		default: | ||||
| +			return -EINVAL; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	/* PHY polarity is global and can't be set per LED. | ||||
| +	 * To detect this, check if last requested polarity mode | ||||
| +	 * match the new one. | ||||
| +	 */ | ||||
| +	if (priv->led_polarity_mode >= 0 && | ||||
| +	    priv->led_polarity_mode != active_low) { | ||||
| +		phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	/* Save the last PHY polarity mode */ | ||||
| +	priv->led_polarity_mode = active_low; | ||||
| + | ||||
| +	return phy_modify_mmd(phydev, MDIO_MMD_AN, | ||||
| +			      QCA808X_MMD7_LED_POLARITY_CTRL, | ||||
| +			      QCA808X_LED_ACTIVE_HIGH, | ||||
| +			      active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); | ||||
| +} | ||||
| + | ||||
|  static struct phy_driver at803x_driver[] = { | ||||
|  { | ||||
|  	/* Qualcomm Atheros AR8035 */ | ||||
| @@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[] | ||||
|  	.cable_test_start	= qca808x_cable_test_start, | ||||
|  	.cable_test_get_status	= qca808x_cable_test_get_status, | ||||
|  	.link_change_notify	= qca808x_link_change_notify, | ||||
| +	.led_brightness_set	= qca808x_led_brightness_set, | ||||
| +	.led_blink_set		= qca808x_led_blink_set, | ||||
| +	.led_hw_is_supported	= qca808x_led_hw_is_supported, | ||||
| +	.led_hw_control_set	= qca808x_led_hw_control_set, | ||||
| +	.led_hw_control_get	= qca808x_led_hw_control_get, | ||||
| +	.led_polarity_set	= qca808x_led_polarity_set, | ||||
|  }, }; | ||||
|   | ||||
|  module_phy_driver(at803x_driver); | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,243 +0,0 @@ | ||||
| From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Mon, 29 Jan 2024 15:15:20 +0100 | ||||
| Subject: [PATCH 2/5] net: phy: qcom: create and move functions to shared | ||||
|  library | ||||
|  | ||||
| Create and move functions to shared library in preparation for qca83xx | ||||
| PHY Family to be detached from at803x driver. | ||||
|  | ||||
| Only the shared defines are moved to the shared qcom.h header. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  drivers/net/phy/qcom/Kconfig        |  4 ++ | ||||
|  drivers/net/phy/qcom/Makefile       |  1 + | ||||
|  drivers/net/phy/qcom/at803x.c       | 69 +---------------------------- | ||||
|  drivers/net/phy/qcom/qcom-phy-lib.c | 53 ++++++++++++++++++++++ | ||||
|  drivers/net/phy/qcom/qcom.h         | 34 ++++++++++++++ | ||||
|  5 files changed, 94 insertions(+), 67 deletions(-) | ||||
|  create mode 100644 drivers/net/phy/qcom/qcom-phy-lib.c | ||||
|  create mode 100644 drivers/net/phy/qcom/qcom.h | ||||
|  | ||||
| --- a/drivers/net/phy/qcom/Kconfig | ||||
| +++ b/drivers/net/phy/qcom/Kconfig | ||||
| @@ -1,6 +1,10 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0-only | ||||
| +config QCOM_NET_PHYLIB | ||||
| +	tristate | ||||
| + | ||||
|  config AT803X_PHY | ||||
|  	tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" | ||||
| +	select QCOM_NET_PHYLIB | ||||
|  	depends on REGULATOR | ||||
|  	help | ||||
|  	  Currently supports the AR8030, AR8031, AR8033, AR8035 and internal | ||||
| --- a/drivers/net/phy/qcom/Makefile | ||||
| +++ b/drivers/net/phy/qcom/Makefile | ||||
| @@ -1,2 +1,3 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
| +obj-$(CONFIG_QCOM_NET_PHYLIB)	+= qcom-phy-lib.o | ||||
|  obj-$(CONFIG_AT803X_PHY)	+= at803x.o | ||||
| --- a/drivers/net/phy/qcom/at803x.c | ||||
| +++ b/drivers/net/phy/qcom/at803x.c | ||||
| @@ -22,6 +22,8 @@ | ||||
|  #include <linux/sfp.h> | ||||
|  #include <dt-bindings/net/qca-ar803x.h> | ||||
|   | ||||
| +#include "qcom.h" | ||||
| + | ||||
|  #define AT803X_SPECIFIC_FUNCTION_CONTROL	0x10 | ||||
|  #define AT803X_SFC_ASSERT_CRS			BIT(11) | ||||
|  #define AT803X_SFC_FORCE_LINK			BIT(10) | ||||
| @@ -84,9 +86,6 @@ | ||||
|  #define AT803X_REG_CHIP_CONFIG			0x1f | ||||
|  #define AT803X_BT_BX_REG_SEL			0x8000 | ||||
|   | ||||
| -#define AT803X_DEBUG_ADDR			0x1D | ||||
| -#define AT803X_DEBUG_DATA			0x1E | ||||
| - | ||||
|  #define AT803X_MODE_CFG_MASK			0x0F | ||||
|  #define AT803X_MODE_CFG_BASET_RGMII		0x00 | ||||
|  #define AT803X_MODE_CFG_BASET_SGMII		0x01 | ||||
| @@ -103,19 +102,6 @@ | ||||
|  #define AT803X_PSSR				0x11	/*PHY-Specific Status Register*/ | ||||
|  #define AT803X_PSSR_MR_AN_COMPLETE		0x0200 | ||||
|   | ||||
| -#define AT803X_DEBUG_ANALOG_TEST_CTRL		0x00 | ||||
| -#define QCA8327_DEBUG_MANU_CTRL_EN		BIT(2) | ||||
| -#define QCA8337_DEBUG_MANU_CTRL_EN		GENMASK(3, 2) | ||||
| -#define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15) | ||||
| - | ||||
| -#define AT803X_DEBUG_SYSTEM_CTRL_MODE		0x05 | ||||
| -#define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8) | ||||
| - | ||||
| -#define AT803X_DEBUG_REG_HIB_CTRL		0x0b | ||||
| -#define   AT803X_DEBUG_HIB_CTRL_SEL_RST_80U	BIT(10) | ||||
| -#define   AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE	BIT(13) | ||||
| -#define   AT803X_DEBUG_HIB_CTRL_PS_HIB_EN	BIT(15) | ||||
| - | ||||
|  #define AT803X_DEBUG_REG_3C			0x3C | ||||
|   | ||||
|  #define AT803X_DEBUG_REG_GREEN			0x3D | ||||
| @@ -393,18 +379,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 | ||||
|  MODULE_AUTHOR("Matus Ujhelyi"); | ||||
|  MODULE_LICENSE("GPL"); | ||||
|   | ||||
| -enum stat_access_type { | ||||
| -	PHY, | ||||
| -	MMD | ||||
| -}; | ||||
| - | ||||
| -struct at803x_hw_stat { | ||||
| -	const char *string; | ||||
| -	u8 reg; | ||||
| -	u32 mask; | ||||
| -	enum stat_access_type access_type; | ||||
| -}; | ||||
| - | ||||
|  static struct at803x_hw_stat qca83xx_hw_stats[] = { | ||||
|  	{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, | ||||
|  	{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, | ||||
| @@ -439,45 +413,6 @@ struct at803x_context { | ||||
|  	u16 led_control; | ||||
|  }; | ||||
|   | ||||
| -static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) | ||||
| -{ | ||||
| -	int ret; | ||||
| - | ||||
| -	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); | ||||
| -	if (ret < 0) | ||||
| -		return ret; | ||||
| - | ||||
| -	return phy_write(phydev, AT803X_DEBUG_DATA, data); | ||||
| -} | ||||
| - | ||||
| -static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) | ||||
| -{ | ||||
| -	int ret; | ||||
| - | ||||
| -	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); | ||||
| -	if (ret < 0) | ||||
| -		return ret; | ||||
| - | ||||
| -	return phy_read(phydev, AT803X_DEBUG_DATA); | ||||
| -} | ||||
| - | ||||
| -static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, | ||||
| -				 u16 clear, u16 set) | ||||
| -{ | ||||
| -	u16 val; | ||||
| -	int ret; | ||||
| - | ||||
| -	ret = at803x_debug_reg_read(phydev, reg); | ||||
| -	if (ret < 0) | ||||
| -		return ret; | ||||
| - | ||||
| -	val = ret & 0xffff; | ||||
| -	val &= ~clear; | ||||
| -	val |= set; | ||||
| - | ||||
| -	return phy_write(phydev, AT803X_DEBUG_DATA, val); | ||||
| -} | ||||
| - | ||||
|  static int at803x_write_page(struct phy_device *phydev, int page) | ||||
|  { | ||||
|  	int mask; | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/phy/qcom/qcom-phy-lib.c | ||||
| @@ -0,0 +1,53 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0 | ||||
| + | ||||
| +#include <linux/phy.h> | ||||
| +#include <linux/module.h> | ||||
| + | ||||
| +#include "qcom.h" | ||||
| + | ||||
| +MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); | ||||
| +MODULE_AUTHOR("Matus Ujhelyi"); | ||||
| +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); | ||||
| +MODULE_LICENSE("GPL"); | ||||
| + | ||||
| +int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	return phy_read(phydev, AT803X_DEBUG_DATA); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(at803x_debug_reg_read); | ||||
| + | ||||
| +int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, | ||||
| +			  u16 clear, u16 set) | ||||
| +{ | ||||
| +	u16 val; | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = at803x_debug_reg_read(phydev, reg); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	val = ret & 0xffff; | ||||
| +	val &= ~clear; | ||||
| +	val |= set; | ||||
| + | ||||
| +	return phy_write(phydev, AT803X_DEBUG_DATA, val); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(at803x_debug_reg_mask); | ||||
| + | ||||
| +int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) | ||||
| +{ | ||||
| +	int ret; | ||||
| + | ||||
| +	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); | ||||
| +	if (ret < 0) | ||||
| +		return ret; | ||||
| + | ||||
| +	return phy_write(phydev, AT803X_DEBUG_DATA, data); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(at803x_debug_reg_write); | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/phy/qcom/qcom.h | ||||
| @@ -0,0 +1,34 @@ | ||||
| +/* SPDX-License-Identifier: GPL-2.0 */ | ||||
| + | ||||
| +#define AT803X_DEBUG_ADDR			0x1D | ||||
| +#define AT803X_DEBUG_DATA			0x1E | ||||
| + | ||||
| +#define AT803X_DEBUG_ANALOG_TEST_CTRL		0x00 | ||||
| +#define QCA8327_DEBUG_MANU_CTRL_EN		BIT(2) | ||||
| +#define QCA8337_DEBUG_MANU_CTRL_EN		GENMASK(3, 2) | ||||
| +#define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15) | ||||
| + | ||||
| +#define AT803X_DEBUG_SYSTEM_CTRL_MODE		0x05 | ||||
| +#define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8) | ||||
| + | ||||
| +#define AT803X_DEBUG_REG_HIB_CTRL		0x0b | ||||
| +#define   AT803X_DEBUG_HIB_CTRL_SEL_RST_80U	BIT(10) | ||||
| +#define   AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE	BIT(13) | ||||
| +#define   AT803X_DEBUG_HIB_CTRL_PS_HIB_EN	BIT(15) | ||||
| + | ||||
| +enum stat_access_type { | ||||
| +	PHY, | ||||
| +	MMD | ||||
| +}; | ||||
| + | ||||
| +struct at803x_hw_stat { | ||||
| +	const char *string; | ||||
| +	u8 reg; | ||||
| +	u32 mask; | ||||
| +	enum stat_access_type access_type; | ||||
| +}; | ||||
| + | ||||
| +int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); | ||||
| +int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, | ||||
| +			  u16 clear, u16 set); | ||||
| +int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); | ||||
| @@ -1,638 +0,0 @@ | ||||
| From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Mon, 29 Jan 2024 15:15:21 +0100 | ||||
| Subject: [PATCH 3/5] net: phy: qcom: deatch qca83xx PHY driver from at803x | ||||
|  | ||||
| Deatch qca83xx PHY driver from at803x. | ||||
|  | ||||
| The QCA83xx PHYs implement specific function and doesn't use generic | ||||
| at803x so it can be detached from the driver and moved to a dedicated | ||||
| one. | ||||
|  | ||||
| Probe function and priv struct is reimplemented to allocate and use | ||||
| only the qca83xx specific data. Unused data from at803x PHY driver | ||||
| are dropped from at803x priv struct. | ||||
|  | ||||
| This is to make slimmer PHY drivers instead of including lots of bloat | ||||
| that would never be used in specific SoC. | ||||
|  | ||||
| A new Kconfig flag QCA83XX_PHY is introduced to compile the new | ||||
| introduced PHY driver. | ||||
|  | ||||
| As the Kconfig name starts with Qualcomm the same order is kept. | ||||
|  | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| --- | ||||
|  drivers/net/phy/qcom/Kconfig   |  11 +- | ||||
|  drivers/net/phy/qcom/Makefile  |   1 + | ||||
|  drivers/net/phy/qcom/at803x.c  | 235 ---------------------------- | ||||
|  drivers/net/phy/qcom/qca83xx.c | 275 +++++++++++++++++++++++++++++++++ | ||||
|  4 files changed, 284 insertions(+), 238 deletions(-) | ||||
|  create mode 100644 drivers/net/phy/qcom/qca83xx.c | ||||
|  | ||||
| --- a/drivers/net/phy/qcom/Kconfig | ||||
| +++ b/drivers/net/phy/qcom/Kconfig | ||||
| @@ -3,9 +3,14 @@ config QCOM_NET_PHYLIB | ||||
|  	tristate | ||||
|   | ||||
|  config AT803X_PHY | ||||
| -	tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" | ||||
| +	tristate "Qualcomm Atheros AR803X PHYs" | ||||
|  	select QCOM_NET_PHYLIB | ||||
|  	depends on REGULATOR | ||||
|  	help | ||||
| -	  Currently supports the AR8030, AR8031, AR8033, AR8035 and internal | ||||
| -	  QCA8337(Internal qca8k PHY) model | ||||
| +	  Currently supports the AR8030, AR8031, AR8033, AR8035 model | ||||
| + | ||||
| +config QCA83XX_PHY | ||||
| +	tristate "Qualcomm Atheros QCA833x PHYs" | ||||
| +	select QCOM_NET_PHYLIB | ||||
| +	help | ||||
| +	  Currently supports the internal QCA8337(Internal qca8k PHY) model | ||||
| --- a/drivers/net/phy/qcom/Makefile | ||||
| +++ b/drivers/net/phy/qcom/Makefile | ||||
| @@ -1,3 +1,4 @@ | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
|  obj-$(CONFIG_QCOM_NET_PHYLIB)	+= qcom-phy-lib.o | ||||
|  obj-$(CONFIG_AT803X_PHY)	+= at803x.o | ||||
| +obj-$(CONFIG_QCA83XX_PHY)	+= qca83xx.o | ||||
| --- a/drivers/net/phy/qcom/at803x.c | ||||
| +++ b/drivers/net/phy/qcom/at803x.c | ||||
| @@ -102,17 +102,10 @@ | ||||
|  #define AT803X_PSSR				0x11	/*PHY-Specific Status Register*/ | ||||
|  #define AT803X_PSSR_MR_AN_COMPLETE		0x0200 | ||||
|   | ||||
| -#define AT803X_DEBUG_REG_3C			0x3C | ||||
| - | ||||
| -#define AT803X_DEBUG_REG_GREEN			0x3D | ||||
| -#define   AT803X_DEBUG_GATE_CLK_IN1000		BIT(6) | ||||
| - | ||||
|  #define AT803X_DEBUG_REG_1F			0x1F | ||||
|  #define AT803X_DEBUG_PLL_ON			BIT(2) | ||||
|  #define AT803X_DEBUG_RGMII_1V8			BIT(3) | ||||
|   | ||||
| -#define MDIO_AZ_DEBUG				0x800D | ||||
| - | ||||
|  /* AT803x supports either the XTAL input pad, an internal PLL or the | ||||
|   * DSP as clock reference for the clock output pad. The XTAL reference | ||||
|   * is only used for 25 MHz output, all other frequencies need the PLL. | ||||
| @@ -163,13 +156,7 @@ | ||||
|   | ||||
|  #define QCA8081_PHY_ID				0x004dd101 | ||||
|   | ||||
| -#define QCA8327_A_PHY_ID			0x004dd033 | ||||
| -#define QCA8327_B_PHY_ID			0x004dd034 | ||||
| -#define QCA8337_PHY_ID				0x004dd036 | ||||
|  #define QCA9561_PHY_ID				0x004dd042 | ||||
| -#define QCA8K_PHY_ID_MASK			0xffffffff | ||||
| - | ||||
| -#define QCA8K_DEVFLAGS_REVISION_MASK		GENMASK(2, 0) | ||||
|   | ||||
|  #define AT803X_PAGE_FIBER			0 | ||||
|  #define AT803X_PAGE_COPPER			1 | ||||
| @@ -379,12 +366,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 | ||||
|  MODULE_AUTHOR("Matus Ujhelyi"); | ||||
|  MODULE_LICENSE("GPL"); | ||||
|   | ||||
| -static struct at803x_hw_stat qca83xx_hw_stats[] = { | ||||
| -	{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, | ||||
| -	{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, | ||||
| -	{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, | ||||
| -}; | ||||
| - | ||||
|  struct at803x_ss_mask { | ||||
|  	u16 speed_mask; | ||||
|  	u8 speed_shift; | ||||
| @@ -400,7 +381,6 @@ struct at803x_priv { | ||||
|  	bool is_1000basex; | ||||
|  	struct regulator_dev *vddio_rdev; | ||||
|  	struct regulator_dev *vddh_rdev; | ||||
| -	u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; | ||||
|  	int led_polarity_mode; | ||||
|  }; | ||||
|   | ||||
| @@ -564,53 +544,6 @@ static void at803x_get_wol(struct phy_de | ||||
|  		wol->wolopts |= WAKE_MAGIC; | ||||
|  } | ||||
|   | ||||
| -static int qca83xx_get_sset_count(struct phy_device *phydev) | ||||
| -{ | ||||
| -	return ARRAY_SIZE(qca83xx_hw_stats); | ||||
| -} | ||||
| - | ||||
| -static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) | ||||
| -{ | ||||
| -	int i; | ||||
| - | ||||
| -	for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { | ||||
| -		strscpy(data + i * ETH_GSTRING_LEN, | ||||
| -			qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); | ||||
| -	} | ||||
| -} | ||||
| - | ||||
| -static u64 qca83xx_get_stat(struct phy_device *phydev, int i) | ||||
| -{ | ||||
| -	struct at803x_hw_stat stat = qca83xx_hw_stats[i]; | ||||
| -	struct at803x_priv *priv = phydev->priv; | ||||
| -	int val; | ||||
| -	u64 ret; | ||||
| - | ||||
| -	if (stat.access_type == MMD) | ||||
| -		val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); | ||||
| -	else | ||||
| -		val = phy_read(phydev, stat.reg); | ||||
| - | ||||
| -	if (val < 0) { | ||||
| -		ret = U64_MAX; | ||||
| -	} else { | ||||
| -		val = val & stat.mask; | ||||
| -		priv->stats[i] += val; | ||||
| -		ret = priv->stats[i]; | ||||
| -	} | ||||
| - | ||||
| -	return ret; | ||||
| -} | ||||
| - | ||||
| -static void qca83xx_get_stats(struct phy_device *phydev, | ||||
| -			      struct ethtool_stats *stats, u64 *data) | ||||
| -{ | ||||
| -	int i; | ||||
| - | ||||
| -	for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) | ||||
| -		data[i] = qca83xx_get_stat(phydev, i); | ||||
| -} | ||||
| - | ||||
|  static int at803x_suspend(struct phy_device *phydev) | ||||
|  { | ||||
|  	int value; | ||||
| @@ -1707,124 +1640,6 @@ static int at8035_probe(struct phy_devic | ||||
|  	return at8035_parse_dt(phydev); | ||||
|  } | ||||
|   | ||||
| -static int qca83xx_config_init(struct phy_device *phydev) | ||||
| -{ | ||||
| -	u8 switch_revision; | ||||
| - | ||||
| -	switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; | ||||
| - | ||||
| -	switch (switch_revision) { | ||||
| -	case 1: | ||||
| -		/* For 100M waveform */ | ||||
| -		at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); | ||||
| -		/* Turn on Gigabit clock */ | ||||
| -		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); | ||||
| -		break; | ||||
| - | ||||
| -	case 2: | ||||
| -		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); | ||||
| -		fallthrough; | ||||
| -	case 4: | ||||
| -		phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); | ||||
| -		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); | ||||
| -		at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); | ||||
| -		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); | ||||
| -		break; | ||||
| -	} | ||||
| - | ||||
| -	/* Following original QCA sourcecode set port to prefer master */ | ||||
| -	phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); | ||||
| - | ||||
| -	return 0; | ||||
| -} | ||||
| - | ||||
| -static int qca8327_config_init(struct phy_device *phydev) | ||||
| -{ | ||||
| -	/* QCA8327 require DAC amplitude adjustment for 100m set to +6%. | ||||
| -	 * Disable on init and enable only with 100m speed following | ||||
| -	 * qca original source code. | ||||
| -	 */ | ||||
| -	at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| -			      QCA8327_DEBUG_MANU_CTRL_EN, 0); | ||||
| - | ||||
| -	return qca83xx_config_init(phydev); | ||||
| -} | ||||
| - | ||||
| -static void qca83xx_link_change_notify(struct phy_device *phydev) | ||||
| -{ | ||||
| -	/* Set DAC Amplitude adjustment to +6% for 100m on link running */ | ||||
| -	if (phydev->state == PHY_RUNNING) { | ||||
| -		if (phydev->speed == SPEED_100) | ||||
| -			at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| -					      QCA8327_DEBUG_MANU_CTRL_EN, | ||||
| -					      QCA8327_DEBUG_MANU_CTRL_EN); | ||||
| -	} else { | ||||
| -		/* Reset DAC Amplitude adjustment */ | ||||
| -		at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| -				      QCA8327_DEBUG_MANU_CTRL_EN, 0); | ||||
| -	} | ||||
| -} | ||||
| - | ||||
| -static int qca83xx_resume(struct phy_device *phydev) | ||||
| -{ | ||||
| -	int ret, val; | ||||
| - | ||||
| -	/* Skip reset if not suspended */ | ||||
| -	if (!phydev->suspended) | ||||
| -		return 0; | ||||
| - | ||||
| -	/* Reinit the port, reset values set by suspend */ | ||||
| -	qca83xx_config_init(phydev); | ||||
| - | ||||
| -	/* Reset the port on port resume */ | ||||
| -	phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); | ||||
| - | ||||
| -	/* On resume from suspend the switch execute a reset and | ||||
| -	 * restart auto-negotiation. Wait for reset to complete. | ||||
| -	 */ | ||||
| -	ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), | ||||
| -				    50000, 600000, true); | ||||
| -	if (ret) | ||||
| -		return ret; | ||||
| - | ||||
| -	usleep_range(1000, 2000); | ||||
| - | ||||
| -	return 0; | ||||
| -} | ||||
| - | ||||
| -static int qca83xx_suspend(struct phy_device *phydev) | ||||
| -{ | ||||
| -	at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, | ||||
| -			      AT803X_DEBUG_GATE_CLK_IN1000, 0); | ||||
| - | ||||
| -	at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, | ||||
| -			      AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | | ||||
| -			      AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); | ||||
| - | ||||
| -	return 0; | ||||
| -} | ||||
| - | ||||
| -static int qca8337_suspend(struct phy_device *phydev) | ||||
| -{ | ||||
| -	/* Only QCA8337 support actual suspend. */ | ||||
| -	genphy_suspend(phydev); | ||||
| - | ||||
| -	return qca83xx_suspend(phydev); | ||||
| -} | ||||
| - | ||||
| -static int qca8327_suspend(struct phy_device *phydev) | ||||
| -{ | ||||
| -	u16 mask = 0; | ||||
| - | ||||
| -	/* QCA8327 cause port unreliability when phy suspend | ||||
| -	 * is set. | ||||
| -	 */ | ||||
| -	mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); | ||||
| -	phy_modify(phydev, MII_BMCR, mask, 0); | ||||
| - | ||||
| -	return qca83xx_suspend(phydev); | ||||
| -} | ||||
| - | ||||
|  static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) | ||||
|  { | ||||
|  	int ret; | ||||
| @@ -2599,53 +2414,6 @@ static struct phy_driver at803x_driver[] | ||||
|  	.soft_reset		= genphy_soft_reset, | ||||
|  	.config_aneg		= at803x_config_aneg, | ||||
|  }, { | ||||
| -	/* QCA8337 */ | ||||
| -	.phy_id			= QCA8337_PHY_ID, | ||||
| -	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
| -	.name			= "Qualcomm Atheros 8337 internal PHY", | ||||
| -	/* PHY_GBIT_FEATURES */ | ||||
| -	.probe			= at803x_probe, | ||||
| -	.flags			= PHY_IS_INTERNAL, | ||||
| -	.config_init		= qca83xx_config_init, | ||||
| -	.soft_reset		= genphy_soft_reset, | ||||
| -	.get_sset_count		= qca83xx_get_sset_count, | ||||
| -	.get_strings		= qca83xx_get_strings, | ||||
| -	.get_stats		= qca83xx_get_stats, | ||||
| -	.suspend		= qca8337_suspend, | ||||
| -	.resume			= qca83xx_resume, | ||||
| -}, { | ||||
| -	/* QCA8327-A from switch QCA8327-AL1A */ | ||||
| -	.phy_id			= QCA8327_A_PHY_ID, | ||||
| -	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
| -	.name			= "Qualcomm Atheros 8327-A internal PHY", | ||||
| -	/* PHY_GBIT_FEATURES */ | ||||
| -	.link_change_notify	= qca83xx_link_change_notify, | ||||
| -	.probe			= at803x_probe, | ||||
| -	.flags			= PHY_IS_INTERNAL, | ||||
| -	.config_init		= qca8327_config_init, | ||||
| -	.soft_reset		= genphy_soft_reset, | ||||
| -	.get_sset_count		= qca83xx_get_sset_count, | ||||
| -	.get_strings		= qca83xx_get_strings, | ||||
| -	.get_stats		= qca83xx_get_stats, | ||||
| -	.suspend		= qca8327_suspend, | ||||
| -	.resume			= qca83xx_resume, | ||||
| -}, { | ||||
| -	/* QCA8327-B from switch QCA8327-BL1A */ | ||||
| -	.phy_id			= QCA8327_B_PHY_ID, | ||||
| -	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
| -	.name			= "Qualcomm Atheros 8327-B internal PHY", | ||||
| -	/* PHY_GBIT_FEATURES */ | ||||
| -	.link_change_notify	= qca83xx_link_change_notify, | ||||
| -	.probe			= at803x_probe, | ||||
| -	.flags			= PHY_IS_INTERNAL, | ||||
| -	.config_init		= qca8327_config_init, | ||||
| -	.soft_reset		= genphy_soft_reset, | ||||
| -	.get_sset_count		= qca83xx_get_sset_count, | ||||
| -	.get_strings		= qca83xx_get_strings, | ||||
| -	.get_stats		= qca83xx_get_stats, | ||||
| -	.suspend		= qca8327_suspend, | ||||
| -	.resume			= qca83xx_resume, | ||||
| -}, { | ||||
|  	/* Qualcomm QCA8081 */ | ||||
|  	PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), | ||||
|  	.name			= "Qualcomm QCA8081", | ||||
| @@ -2683,9 +2451,6 @@ static struct mdio_device_id __maybe_unu | ||||
|  	{ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, | ||||
|  	{ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, | ||||
|  	{ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, | ||||
| -	{ PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, | ||||
| -	{ PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, | ||||
| -	{ PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, | ||||
|  	{ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, | ||||
|  	{ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, | ||||
|  	{ } | ||||
| --- /dev/null | ||||
| +++ b/drivers/net/phy/qcom/qca83xx.c | ||||
| @@ -0,0 +1,275 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0+ | ||||
| + | ||||
| +#include <linux/phy.h> | ||||
| +#include <linux/module.h> | ||||
| + | ||||
| +#include "qcom.h" | ||||
| + | ||||
| +#define AT803X_DEBUG_REG_3C			0x3C | ||||
| + | ||||
| +#define AT803X_DEBUG_REG_GREEN			0x3D | ||||
| +#define   AT803X_DEBUG_GATE_CLK_IN1000		BIT(6) | ||||
| + | ||||
| +#define MDIO_AZ_DEBUG				0x800D | ||||
| + | ||||
| +#define QCA8327_A_PHY_ID			0x004dd033 | ||||
| +#define QCA8327_B_PHY_ID			0x004dd034 | ||||
| +#define QCA8337_PHY_ID				0x004dd036 | ||||
| +#define QCA8K_PHY_ID_MASK			0xffffffff | ||||
| + | ||||
| +#define QCA8K_DEVFLAGS_REVISION_MASK		GENMASK(2, 0) | ||||
| + | ||||
| +static struct at803x_hw_stat qca83xx_hw_stats[] = { | ||||
| +	{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, | ||||
| +	{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, | ||||
| +	{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, | ||||
| +}; | ||||
| + | ||||
| +struct qca83xx_priv { | ||||
| +	u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; | ||||
| +}; | ||||
| + | ||||
| +MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver"); | ||||
| +MODULE_AUTHOR("Matus Ujhelyi"); | ||||
| +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); | ||||
| +MODULE_LICENSE("GPL"); | ||||
| + | ||||
| +static int qca83xx_get_sset_count(struct phy_device *phydev) | ||||
| +{ | ||||
| +	return ARRAY_SIZE(qca83xx_hw_stats); | ||||
| +} | ||||
| + | ||||
| +static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { | ||||
| +		strscpy(data + i * ETH_GSTRING_LEN, | ||||
| +			qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static u64 qca83xx_get_stat(struct phy_device *phydev, int i) | ||||
| +{ | ||||
| +	struct at803x_hw_stat stat = qca83xx_hw_stats[i]; | ||||
| +	struct qca83xx_priv *priv = phydev->priv; | ||||
| +	int val; | ||||
| +	u64 ret; | ||||
| + | ||||
| +	if (stat.access_type == MMD) | ||||
| +		val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); | ||||
| +	else | ||||
| +		val = phy_read(phydev, stat.reg); | ||||
| + | ||||
| +	if (val < 0) { | ||||
| +		ret = U64_MAX; | ||||
| +	} else { | ||||
| +		val = val & stat.mask; | ||||
| +		priv->stats[i] += val; | ||||
| +		ret = priv->stats[i]; | ||||
| +	} | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
| +static void qca83xx_get_stats(struct phy_device *phydev, | ||||
| +			      struct ethtool_stats *stats, u64 *data) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) | ||||
| +		data[i] = qca83xx_get_stat(phydev, i); | ||||
| +} | ||||
| + | ||||
| +static int qca83xx_probe(struct phy_device *phydev) | ||||
| +{ | ||||
| +	struct device *dev = &phydev->mdio.dev; | ||||
| +	struct qca83xx_priv *priv; | ||||
| + | ||||
| +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||||
| +	if (!priv) | ||||
| +		return -ENOMEM; | ||||
| + | ||||
| +	phydev->priv = priv; | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca83xx_config_init(struct phy_device *phydev) | ||||
| +{ | ||||
| +	u8 switch_revision; | ||||
| + | ||||
| +	switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; | ||||
| + | ||||
| +	switch (switch_revision) { | ||||
| +	case 1: | ||||
| +		/* For 100M waveform */ | ||||
| +		at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); | ||||
| +		/* Turn on Gigabit clock */ | ||||
| +		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); | ||||
| +		break; | ||||
| + | ||||
| +	case 2: | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); | ||||
| +		fallthrough; | ||||
| +	case 4: | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); | ||||
| +		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); | ||||
| +		at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); | ||||
| +		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); | ||||
| +		break; | ||||
| +	} | ||||
| + | ||||
| +	/* Following original QCA sourcecode set port to prefer master */ | ||||
| +	phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca8327_config_init(struct phy_device *phydev) | ||||
| +{ | ||||
| +	/* QCA8327 require DAC amplitude adjustment for 100m set to +6%. | ||||
| +	 * Disable on init and enable only with 100m speed following | ||||
| +	 * qca original source code. | ||||
| +	 */ | ||||
| +	at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| +			      QCA8327_DEBUG_MANU_CTRL_EN, 0); | ||||
| + | ||||
| +	return qca83xx_config_init(phydev); | ||||
| +} | ||||
| + | ||||
| +static void qca83xx_link_change_notify(struct phy_device *phydev) | ||||
| +{ | ||||
| +	/* Set DAC Amplitude adjustment to +6% for 100m on link running */ | ||||
| +	if (phydev->state == PHY_RUNNING) { | ||||
| +		if (phydev->speed == SPEED_100) | ||||
| +			at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| +					      QCA8327_DEBUG_MANU_CTRL_EN, | ||||
| +					      QCA8327_DEBUG_MANU_CTRL_EN); | ||||
| +	} else { | ||||
| +		/* Reset DAC Amplitude adjustment */ | ||||
| +		at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, | ||||
| +				      QCA8327_DEBUG_MANU_CTRL_EN, 0); | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +static int qca83xx_resume(struct phy_device *phydev) | ||||
| +{ | ||||
| +	int ret, val; | ||||
| + | ||||
| +	/* Skip reset if not suspended */ | ||||
| +	if (!phydev->suspended) | ||||
| +		return 0; | ||||
| + | ||||
| +	/* Reinit the port, reset values set by suspend */ | ||||
| +	qca83xx_config_init(phydev); | ||||
| + | ||||
| +	/* Reset the port on port resume */ | ||||
| +	phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); | ||||
| + | ||||
| +	/* On resume from suspend the switch execute a reset and | ||||
| +	 * restart auto-negotiation. Wait for reset to complete. | ||||
| +	 */ | ||||
| +	ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), | ||||
| +				    50000, 600000, true); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	usleep_range(1000, 2000); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca83xx_suspend(struct phy_device *phydev) | ||||
| +{ | ||||
| +	at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, | ||||
| +			      AT803X_DEBUG_GATE_CLK_IN1000, 0); | ||||
| + | ||||
| +	at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, | ||||
| +			      AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | | ||||
| +			      AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int qca8337_suspend(struct phy_device *phydev) | ||||
| +{ | ||||
| +	/* Only QCA8337 support actual suspend. */ | ||||
| +	genphy_suspend(phydev); | ||||
| + | ||||
| +	return qca83xx_suspend(phydev); | ||||
| +} | ||||
| + | ||||
| +static int qca8327_suspend(struct phy_device *phydev) | ||||
| +{ | ||||
| +	u16 mask = 0; | ||||
| + | ||||
| +	/* QCA8327 cause port unreliability when phy suspend | ||||
| +	 * is set. | ||||
| +	 */ | ||||
| +	mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); | ||||
| +	phy_modify(phydev, MII_BMCR, mask, 0); | ||||
| + | ||||
| +	return qca83xx_suspend(phydev); | ||||
| +} | ||||
| + | ||||
| +static struct phy_driver qca83xx_driver[] = { | ||||
| +{ | ||||
| +	/* QCA8337 */ | ||||
| +	.phy_id			= QCA8337_PHY_ID, | ||||
| +	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
| +	.name			= "Qualcomm Atheros 8337 internal PHY", | ||||
| +	/* PHY_GBIT_FEATURES */ | ||||
| +	.probe			= qca83xx_probe, | ||||
| +	.flags			= PHY_IS_INTERNAL, | ||||
| +	.config_init		= qca83xx_config_init, | ||||
| +	.soft_reset		= genphy_soft_reset, | ||||
| +	.get_sset_count		= qca83xx_get_sset_count, | ||||
| +	.get_strings		= qca83xx_get_strings, | ||||
| +	.get_stats		= qca83xx_get_stats, | ||||
| +	.suspend		= qca8337_suspend, | ||||
| +	.resume			= qca83xx_resume, | ||||
| +}, { | ||||
| +	/* QCA8327-A from switch QCA8327-AL1A */ | ||||
| +	.phy_id			= QCA8327_A_PHY_ID, | ||||
| +	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
| +	.name			= "Qualcomm Atheros 8327-A internal PHY", | ||||
| +	/* PHY_GBIT_FEATURES */ | ||||
| +	.link_change_notify	= qca83xx_link_change_notify, | ||||
| +	.probe			= qca83xx_probe, | ||||
| +	.flags			= PHY_IS_INTERNAL, | ||||
| +	.config_init		= qca8327_config_init, | ||||
| +	.soft_reset		= genphy_soft_reset, | ||||
| +	.get_sset_count		= qca83xx_get_sset_count, | ||||
| +	.get_strings		= qca83xx_get_strings, | ||||
| +	.get_stats		= qca83xx_get_stats, | ||||
| +	.suspend		= qca8327_suspend, | ||||
| +	.resume			= qca83xx_resume, | ||||
| +}, { | ||||
| +	/* QCA8327-B from switch QCA8327-BL1A */ | ||||
| +	.phy_id			= QCA8327_B_PHY_ID, | ||||
| +	.phy_id_mask		= QCA8K_PHY_ID_MASK, | ||||
| +	.name			= "Qualcomm Atheros 8327-B internal PHY", | ||||
| +	/* PHY_GBIT_FEATURES */ | ||||
| +	.link_change_notify	= qca83xx_link_change_notify, | ||||
| +	.probe			= qca83xx_probe, | ||||
| +	.flags			= PHY_IS_INTERNAL, | ||||
| +	.config_init		= qca8327_config_init, | ||||
| +	.soft_reset		= genphy_soft_reset, | ||||
| +	.get_sset_count		= qca83xx_get_sset_count, | ||||
| +	.get_strings		= qca83xx_get_strings, | ||||
| +	.get_stats		= qca83xx_get_stats, | ||||
| +	.suspend		= qca8327_suspend, | ||||
| +	.resume			= qca83xx_resume, | ||||
| +}, }; | ||||
| + | ||||
| +module_phy_driver(qca83xx_driver); | ||||
| + | ||||
| +static struct mdio_device_id __maybe_unused qca83xx_tbl[] = { | ||||
| +	{ PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, | ||||
| +	{ PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, | ||||
| +	{ PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, | ||||
| +	{ } | ||||
| +}; | ||||
| + | ||||
| +MODULE_DEVICE_TABLE(mdio, qca83xx_tbl); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user