mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-28 17:12:22 +00:00
Compare commits
2 Commits
1a3955554a
...
staging-WI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0319701aef | ||
|
|
780ce003b2 |
42
feeds/mediatek/fitblk/Makefile
Normal file
42
feeds/mediatek/fitblk/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
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))
|
||||
63
feeds/mediatek/fitblk/files/fit.sh
Normal file
63
feeds/mediatek/fitblk/files/fit.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
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
|
||||
}
|
||||
24
feeds/mediatek/fitblk/patches/100-header.patch
Normal file
24
feeds/mediatek/fitblk/patches/100-header.patch
Normal file
@@ -0,0 +1,24 @@
|
||||
--- 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 */
|
||||
7
feeds/mediatek/fitblk/src/Makefile
Normal file
7
feeds/mediatek/fitblk/src/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
all: fitblk
|
||||
|
||||
fitblk:
|
||||
$(CC) $(CFLAGS) -o $@ fitblk.c $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f fitblk
|
||||
45
feeds/mediatek/fitblk/src/fitblk.c
Normal file
45
feeds/mediatek/fitblk/src/fitblk.c
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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;
|
||||
}
|
||||
141
feeds/mediatek/fstools/Makefile
Normal file
141
feeds/mediatek/fstools/Makefile
Normal file
@@ -0,0 +1,141 @@
|
||||
#
|
||||
# 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))
|
||||
22
feeds/mediatek/fstools/files/blockd.init
Executable file
22
feeds/mediatek/fstools/files/blockd.init
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/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
|
||||
}
|
||||
2
feeds/mediatek/fstools/files/fstab.default
Normal file
2
feeds/mediatek/fstools/files/fstab.default
Normal file
@@ -0,0 +1,2 @@
|
||||
[ ! -f /etc/config/fstab ] && ( block detect > /etc/config/fstab )
|
||||
exit 0
|
||||
22
feeds/mediatek/fstools/files/fstab.init
Normal file
22
feeds/mediatek/fstools/files/fstab.init
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/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
|
||||
}
|
||||
8
feeds/mediatek/fstools/files/media-change.hotplug
Normal file
8
feeds/mediatek/fstools/files/media-change.hotplug
Normal file
@@ -0,0 +1,8 @@
|
||||
[ -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
feeds/mediatek/fstools/files/mount.hotplug
Normal file
1
feeds/mediatek/fstools/files/mount.hotplug
Normal file
@@ -0,0 +1 @@
|
||||
[ "$ACTION" = "add" -o "$ACTION" = "remove" ] && /sbin/block hotplug
|
||||
113
feeds/mediatek/fstools/files/snapshot
Normal file
113
feeds/mediatek/fstools/files/snapshot
Normal file
@@ -0,0 +1,113 @@
|
||||
#!/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
|
||||
}
|
||||
39
feeds/mediatek/gpio-button-hotplug/Makefile
Normal file
39
feeds/mediatek/gpio-button-hotplug/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# 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
feeds/mediatek/gpio-button-hotplug/src/Makefile
Normal file
1
feeds/mediatek/gpio-button-hotplug/src/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-m += gpio-button-hotplug.o
|
||||
737
feeds/mediatek/gpio-button-hotplug/src/gpio-button-hotplug.c
Normal file
737
feeds/mediatek/gpio-button-hotplug/src/gpio-button-hotplug.c
Normal file
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
* 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);
|
||||
42
feeds/mediatek/linux-firmware/Makefile
Normal file
42
feeds/mediatek/linux-firmware/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# 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))
|
||||
17
feeds/mediatek/linux-firmware/airoha.mk
Normal file
17
feeds/mediatek/linux-firmware/airoha.mk
Normal file
@@ -0,0 +1,17 @@
|
||||
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))
|
||||
29
feeds/mediatek/linux-firmware/amd.mk
Normal file
29
feeds/mediatek/linux-firmware/amd.mk
Normal file
@@ -0,0 +1,29 @@
|
||||
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))
|
||||
214
feeds/mediatek/linux-firmware/broadcom.mk
Normal file
214
feeds/mediatek/linux-firmware/broadcom.mk
Normal file
@@ -0,0 +1,214 @@
|
||||
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))
|
||||
10
feeds/mediatek/linux-firmware/cis.mk
Normal file
10
feeds/mediatek/linux-firmware/cis.mk
Normal file
@@ -0,0 +1,10 @@
|
||||
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))
|
||||
12
feeds/mediatek/linux-firmware/edgeport.mk
Normal file
12
feeds/mediatek/linux-firmware/edgeport.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
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))
|
||||
272
feeds/mediatek/linux-firmware/intel.mk
Normal file
272
feeds/mediatek/linux-firmware/intel.mk
Normal file
@@ -0,0 +1,272 @@
|
||||
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))
|
||||
72
feeds/mediatek/linux-firmware/marvell.mk
Normal file
72
feeds/mediatek/linux-firmware/marvell.mk
Normal file
@@ -0,0 +1,72 @@
|
||||
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))
|
||||
|
||||
108
feeds/mediatek/linux-firmware/mediatek.mk
Normal file
108
feeds/mediatek/linux-firmware/mediatek.mk
Normal file
@@ -0,0 +1,108 @@
|
||||
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))
|
||||
39
feeds/mediatek/linux-firmware/mellanox.mk
Normal file
39
feeds/mediatek/linux-firmware/mellanox.mk
Normal file
@@ -0,0 +1,39 @@
|
||||
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))
|
||||
9
feeds/mediatek/linux-firmware/misc.mk
Normal file
9
feeds/mediatek/linux-firmware/misc.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
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))
|
||||
47
feeds/mediatek/linux-firmware/qca.mk
Normal file
47
feeds/mediatek/linux-firmware/qca.mk
Normal file
@@ -0,0 +1,47 @@
|
||||
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))
|
||||
138
feeds/mediatek/linux-firmware/qca_ath10k.mk
Normal file
138
feeds/mediatek/linux-firmware/qca_ath10k.mk
Normal file
@@ -0,0 +1,138 @@
|
||||
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))
|
||||
28
feeds/mediatek/linux-firmware/qca_ath11k.mk
Normal file
28
feeds/mediatek/linux-firmware/qca_ath11k.mk
Normal file
@@ -0,0 +1,28 @@
|
||||
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))
|
||||
7
feeds/mediatek/linux-firmware/qca_ath12k.mk
Normal file
7
feeds/mediatek/linux-firmware/qca_ath12k.mk
Normal file
@@ -0,0 +1,7 @@
|
||||
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))
|
||||
220
feeds/mediatek/linux-firmware/realtek.mk
Normal file
220
feeds/mediatek/linux-firmware/realtek.mk
Normal file
@@ -0,0 +1,220 @@
|
||||
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))
|
||||
6
feeds/mediatek/linux-firmware/rsi.mk
Normal file
6
feeds/mediatek/linux-firmware/rsi.mk
Normal file
@@ -0,0 +1,6 @@
|
||||
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))
|
||||
39
feeds/mediatek/linux-firmware/ti.mk
Normal file
39
feeds/mediatek/linux-firmware/ti.mk
Normal file
@@ -0,0 +1,39 @@
|
||||
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))
|
||||
@@ -0,0 +1,56 @@
|
||||
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
|
||||
@@ -0,0 +1,58 @@
|
||||
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>
|
||||
@@ -0,0 +1,65 @@
|
||||
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
|
||||
@@ -0,0 +1,51 @@
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
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
|
||||
@@ -0,0 +1,107 @@
|
||||
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
|
||||
@@ -0,0 +1,206 @@
|
||||
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
|
||||
};
|
||||
@@ -0,0 +1,171 @@
|
||||
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()) {
|
||||
@@ -0,0 +1,111 @@
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
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()) {
|
||||
@@ -0,0 +1,36 @@
|
||||
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)
|
||||
@@ -0,0 +1,123 @@
|
||||
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
|
||||
@@ -0,0 +1,50 @@
|
||||
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
|
||||
@@ -0,0 +1,285 @@
|
||||
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);
|
||||
@@ -0,0 +1,205 @@
|
||||
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]) {
|
||||
@@ -0,0 +1,180 @@
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
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);
|
||||
@@ -0,0 +1,244 @@
|
||||
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");
|
||||
@@ -0,0 +1,34 @@
|
||||
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))
|
||||
@@ -0,0 +1,53 @@
|
||||
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)
|
||||
@@ -0,0 +1,94 @@
|
||||
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)
|
||||
@@ -0,0 +1,104 @@
|
||||
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;
|
||||
@@ -0,0 +1,200 @@
|
||||
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;
|
||||
+}
|
||||
@@ -0,0 +1,146 @@
|
||||
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;
|
||||
@@ -0,0 +1,38 @@
|
||||
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 = {
|
||||
@@ -0,0 +1,75 @@
|
||||
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);
|
||||
@@ -0,0 +1,330 @@
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
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,
|
||||
@@ -0,0 +1,164 @@
|
||||
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;
|
||||
@@ -0,0 +1,30 @@
|
||||
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 */
|
||||
@@ -0,0 +1,94 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
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),
|
||||
@@ -0,0 +1,29 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
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,
|
||||
@@ -0,0 +1,183 @@
|
||||
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());
|
||||
@@ -0,0 +1,135 @@
|
||||
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
@@ -0,0 +1,183 @@
|
||||
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
|
||||
*/
|
||||
@@ -0,0 +1,504 @@
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
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,
|
||||
@@ -0,0 +1,129 @@
|
||||
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,
|
||||
}, {
|
||||
@@ -0,0 +1,155 @@
|
||||
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 */
|
||||
@@ -0,0 +1,94 @@
|
||||
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
|
||||
*/
|
||||
@@ -0,0 +1,78 @@
|
||||
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
|
||||
*/
|
||||
@@ -0,0 +1,86 @@
|
||||
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,
|
||||
@@ -0,0 +1,92 @@
|
||||
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,
|
||||
@@ -0,0 +1,78 @@
|
||||
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,
|
||||
@@ -0,0 +1,78 @@
|
||||
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)
|
||||
@@ -0,0 +1,297 @@
|
||||
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;
|
||||
@@ -0,0 +1,114 @@
|
||||
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,
|
||||
@@ -0,0 +1,219 @@
|
||||
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,
|
||||
@@ -0,0 +1,116 @@
|
||||
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,
|
||||
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
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;
|
||||
@@ -0,0 +1,38 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
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[] = {
|
||||
@@ -0,0 +1,62 @@
|
||||
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)
|
||||
@@ -0,0 +1,118 @@
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
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;
|
||||
@@ -0,0 +1,62 @@
|
||||
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,
|
||||
@@ -0,0 +1,408 @@
|
||||
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
@@ -0,0 +1,243 @@
|
||||
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);
|
||||
@@ -0,0 +1,638 @@
|
||||
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);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user