S: Maintained
F: drivers/i2c/busses/i2c-amd-mp2*
+AMD PMC DRIVER
+M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/amd-pmc.*
+
AMD POWERPLAY
M: Evan Quan <evan.quan@amd.com>
L: amd-gfx@lists.freedesktop.org
S: Supported
T: git git://people.freedesktop.org/~agd5f/linux
-F: drivers/gpu/drm/amd/powerplay/
+F: drivers/gpu/drm/amd/pm/powerplay/
AMD SEATTLE DEVICE TREE SUPPORT
M: Brijesh Singh <brijeshkumar.singh@amd.com>
L: linux-iio@vger.kernel.org
S: Supported
W: http://ez.analog.com/community/linux-device-drivers
-F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt
+F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
F: drivers/iio/adc/ad7768-1.c
ANALOG DEVICES INC AD7780 DRIVER
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/adv7604*
+F: Documentation/devicetree/bindings/media/i2c/adv7604.yaml
ANALOG DEVICES INC ADV7842 DRIVER
M: Hans Verkuil <hverkuil-cisco@xs4all.nl>
F: Documentation/devicetree/bindings/rtc/google,goldfish-rtc.txt
F: drivers/rtc/rtc-goldfish.c
-ANDROID ION DRIVER
-M: Laura Abbott <labbott@redhat.com>
-M: Sumit Semwal <sumit.semwal@linaro.org>
-L: devel@driverdev.osuosl.org
-L: dri-devel@lists.freedesktop.org
-L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers)
-S: Supported
-F: drivers/staging/android/ion
-F: drivers/staging/android/uapi/ion.h
-
AOA (Apple Onboard Audio) ALSA DRIVER
M: Johannes Berg <johannes@sipsolutions.net>
L: linuxppc-dev@lists.ozlabs.org
L: netdev@vger.kernel.org
S: Supported
W: https://www.marvell.com/
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: Documentation/networking/device_drivers/ethernet/aquantia/atlantic.rst
F: drivers/net/ethernet/aquantia/atlantic/
F: drivers/iommu/arm/
F: drivers/iommu/io-pgtable-arm*
+ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS)
+M: Arnd Bergmann <arnd@arndb.de>
+M: Olof Johansson <olof@lixom.net>
+M: soc@kernel.org
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git
+F: arch/arm/boot/dts/Makefile
+F: arch/arm64/boot/dts/Makefile
+
ARM SUB-ARCHITECTURES
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git
F: arch/arm/mach-*/
F: arch/arm/plat-*/
F: Documentation/devicetree/bindings/arm/actions.yaml
F: Documentation/devicetree/bindings/clock/actions,owl-cmu.txt
F: Documentation/devicetree/bindings/dma/owl-dma.yaml
-F: Documentation/devicetree/bindings/i2c/i2c-owl.txt
+F: Documentation/devicetree/bindings/i2c/i2c-owl.yaml
F: Documentation/devicetree/bindings/interrupt-controller/actions,owl-sirq.yaml
F: Documentation/devicetree/bindings/mmc/owl-mmc.yaml
F: Documentation/devicetree/bindings/pinctrl/actions,*
ARM/Allwinner sunXi SoC support
M: Maxime Ripard <mripard@kernel.org>
M: Chen-Yu Tsai <wens@csie.org>
+R: Jernej Skrabec <jernej.skrabec@siol.net>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git
ARM/CORESIGHT FRAMEWORK AND DRIVERS
M: Mathieu Poirier <mathieu.poirier@linaro.org>
-R: Suzuki K Poulose <suzuki.poulose@arm.com>
+M: Suzuki K Poulose <suzuki.poulose@arm.com>
R: Mike Leach <mike.leach@linaro.org>
+R: Leo Yan <leo.yan@linaro.org>
L: coresight@lists.linaro.org (moderated for non-subscribers)
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git
F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
F: Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt
F: Documentation/devicetree/bindings/arm/coresight-cti.yaml
F: drivers/gpio/gpio-moxtet.c
F: include/linux/moxtet.h
-ARM/EBSA110 MACHINE SUPPORT
-M: Russell King <linux@armlinux.org.uk>
-L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S: Maintained
-W: http://www.armlinux.org.uk/
-F: arch/arm/mach-ebsa110/
-F: drivers/net/ethernet/amd/am79c961a.*
-
ARM/ENERGY MICRO (SILICON LABS) EFM32 SUPPORT
M: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
R: Pengutronix Kernel Team <kernel@pengutronix.de>
ARM/LPC32XX SOC SUPPORT
M: Vladimir Zapolskiy <vz@mleia.com>
-M: Sylvain Lemieux <slemieux.tyco@gmail.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
T: git git://github.com/vzapolskiy/linux-lpc32xx.git
S: Maintained
ARM/Marvell Dove/MV78xx0/Orion SOC support
-M: Jason Cooper <jason@lakedaemon.net>
M: Andrew Lunn <andrew@lunn.ch>
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
M: Gregory Clement <gregory.clement@bootlin.com>
F: drivers/soc/dove/
ARM/Marvell Kirkwood and Armada 370, 375, 38x, 39x, XP, 3700, 7K/8K, CN9130 SOC support
-M: Jason Cooper <jason@lakedaemon.net>
M: Andrew Lunn <andrew@lunn.ch>
M: Gregory Clement <gregory.clement@bootlin.com>
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
F: arch/arm64/boot/dts/microchip/
N: sparx5
+Microchip Timer Counter Block (TCB) Capture Driver
+M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/counter/microchip-tcb-capture.c
+
ARM/MIOA701 MACHINE SUPPORT
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
F: sound/soc/rockchip/
N: rockchip
-ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
-M: Kukjin Kim <kgene@kernel.org>
+ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-samsung-soc@vger.kernel.org
N: s3c64xx
N: s5pv210
-ARM/SAMSUNG MOBILE MACHINE SUPPORT
-M: Kyungmin Park <kyungmin.park@samsung.com>
-L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S: Maintained
-F: arch/arm/mach-s5pv210/
-
ARM/SAMSUNG S5P SERIES 2D GRAPHICS ACCELERATION (G2D) SUPPORT
-M: Kyungmin Park <kyungmin.park@samsung.com>
-M: Kamil Debski <kamil@wypas.org>
M: Andrzej Hajda <a.hajda@samsung.com>
L: linux-arm-kernel@lists.infradead.org
L: linux-media@vger.kernel.org
F: drivers/media/platform/s5p-jpeg/
ARM/SAMSUNG S5P SERIES Multi Format Codec (MFC) SUPPORT
-M: Kyungmin Park <kyungmin.park@samsung.com>
-M: Kamil Debski <kamil@wypas.org>
-M: Jeongtae Park <jtp.park@samsung.com>
M: Andrzej Hajda <a.hajda@samsung.com>
L: linux-arm-kernel@lists.infradead.org
L: linux-media@vger.kernel.org
ARM/SOCFPGA EDAC SUPPORT
M: Dinh Nguyen <dinguyen@kernel.org>
S: Maintained
-F: drivers/edac/altera_edac.
+F: drivers/edac/altera_edac.[ch]
ARM/SPREADTRUM SoC SUPPORT
M: Orson Zhai <orsonzhai@gmail.com>
N: visconti
ARM/UNIPHIER ARCHITECTURE
-M: Masahiro Yamada <yamada.masahiro@socionext.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-uniphier.git
+S: Orphan
F: Documentation/devicetree/bindings/arm/socionext/uniphier.yaml
F: Documentation/devicetree/bindings/gpio/socionext,uniphier-gpio.yaml
F: Documentation/devicetree/bindings/pinctrl/socionext,uniphier-pinctrl.yaml
M: Nick Dyer <nick@shmanahar.org>
S: Maintained
T: git git://github.com/ndyer/linux.git
-F: Documentation/devicetree/bindings/input/atmel,maxtouch.txt
+F: Documentation/devicetree/bindings/input/atmel,maxtouch.yaml
F: drivers/input/touchscreen/atmel_mxt_ts.c
ATMEL WIRELESS DRIVER
S: Maintained
F: arch/*/include/asm/atomic*.h
F: include/*/atomic*.h
+F: include/linux/refcount.h
+F: Documentation/atomic_*.txt
F: scripts/atomic/
ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
B: https://www.open-mesh.org/projects/batman-adv/issues
C: irc://chat.freenode.net/batman
T: git https://git.open-mesh.org/linux-merge.git
-F: Documentation/ABI/obsolete/sysfs-class-net-batman-adv
-F: Documentation/ABI/obsolete/sysfs-class-net-mesh
F: Documentation/networking/batman-adv.rst
F: include/uapi/linux/batadv_packet.h
F: include/uapi/linux/batman_adv.h
BLUETOOTH DRIVERS
M: Marcel Holtmann <marcel@holtmann.org>
M: Johan Hedberg <johan.hedberg@gmail.com>
+M: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
L: linux-bluetooth@vger.kernel.org
-S: Maintained
+S: Supported
W: http://www.bluez.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
BLUETOOTH SUBSYSTEM
M: Marcel Holtmann <marcel@holtmann.org>
M: Johan Hedberg <johan.hedberg@gmail.com>
+M: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
L: linux-bluetooth@vger.kernel.org
-S: Maintained
+S: Supported
W: http://www.bluez.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
BPF (Safe dynamic programs and tools)
M: Alexei Starovoitov <ast@kernel.org>
M: Daniel Borkmann <daniel@iogearbox.net>
+M: Andrii Nakryiko <andrii@kernel.org>
R: Martin KaFai Lau <kafai@fb.com>
R: Song Liu <songliubraving@fb.com>
R: Yonghong Song <yhs@fb.com>
-R: Andrii Nakryiko <andrii@kernel.org>
R: John Fastabend <john.fastabend@gmail.com>
-R: KP Singh <kpsingh@chromium.org>
+R: KP Singh <kpsingh@kernel.org>
L: netdev@vger.kernel.org
L: bpf@vger.kernel.org
S: Supported
F: arch/x86/net/
X: arch/x86/net/bpf_jit_comp32.c
+BPF LSM (Security Audit and Enforcement using BPF)
+M: KP Singh <kpsingh@kernel.org>
+R: Florent Revest <revest@chromium.org>
+R: Brendan Jackman <jackmanb@chromium.org>
+L: bpf@vger.kernel.org
+S: Maintained
+F: Documentation/bpf/bpf_lsm.rst
+F: include/linux/bpf_lsm.h
+F: kernel/bpf/bpf_lsm.c
+F: security/bpf/
+
BROADCOM B44 10/100 ETHERNET DRIVER
M: Michael Chan <michael.chan@broadcom.com>
L: netdev@vger.kernel.org
M: Arend van Spriel <arend.vanspriel@broadcom.com>
M: Franky Lin <franky.lin@broadcom.com>
M: Hante Meuleman <hante.meuleman@broadcom.com>
-M: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
-M: Wright Feng <wright.feng@cypress.com>
+M: Chi-hsien Lin <chi-hsien.lin@infineon.com>
+M: Wright Feng <wright.feng@infineon.com>
+M: Chung-hsien Hsu <chung-hsien.hsu@infineon.com>
L: linux-wireless@vger.kernel.org
L: brcm80211-dev-list.pdl@broadcom.com
-L: brcm80211-dev-list@cypress.com
+L: SHA-cyfmac-dev-list@infineon.com
S: Supported
F: drivers/net/wireless/broadcom/brcm80211/
F: Documentation/devicetree/bindings/usb/brcm,bcm7445-ehci.yaml
F: drivers/usb/host/ehci-brcm.*
+BROADCOM BRCMSTB USB PIN MAP DRIVER
+M: Al Cooper <alcooperx@gmail.com>
+L: linux-usb@vger.kernel.org
+L: bcm-kernel-feedback-list@broadcom.com
+S: Maintained
+F: Documentation/devicetree/bindings/usb/brcm,usb-pinmap.yaml
+F: drivers/usb/misc/brcmstb-usb-pinmap.c
+
BROADCOM BRCMSTB USB2 and USB3 PHY DRIVER
M: Al Cooper <alcooperx@gmail.com>
L: linux-kernel@vger.kernel.org
M: Peter Chen <peter.chen@nxp.com>
M: Pawel Laszczak <pawell@cadence.com>
M: Roger Quadros <rogerq@ti.com>
+R: Aswath Govindraju <a-govindraju@ti.com>
L: linux-usb@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
-F: Documentation/devicetree/bindings/usb/cdns-usb3.txt
+F: Documentation/devicetree/bindings/usb/cdns,usb3.yaml
F: drivers/usb/cdns3/
CADET FM/AM RADIO RECEIVER DRIVER
F: drivers/media/radio/radio-cadet*
CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER
-M: Jonathan Corbet <corbet@lwn.net>
L: linux-media@vger.kernel.org
-S: Maintained
+S: Orphan
T: git git://linuxtv.org/media_tree.git
F: Documentation/admin-guide/media/cafe_ccic*
F: drivers/media/platform/marvell-ccic/
C: irc://chat.freenode.net/clangbuiltlinux
F: Documentation/kbuild/llvm.rst
F: scripts/clang-tools/
+F: scripts/lld-version.sh
K: \b(?i:clang|llvm)\b
CLEANCACHE API
M: Philipp Zabel <p.zabel@pengutronix.de>
L: linux-media@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/media/coda.txt
+F: Documentation/devicetree/bindings/media/coda.yaml
F: drivers/media/platform/coda/
CODE OF CONDUCT
S: Maintained
F: drivers/hwmon/corsair-cpro.c
+CORSAIR-PSU HARDWARE MONITOR DRIVER
+M: Wilken Gottwalt <wilken.gottwalt@posteo.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/corsair-psu.rst
+F: drivers/hwmon/corsair-psu.c
+
COSA/SRP SYNC SERIAL DRIVER
M: Jan "Yenya" Kasprzak <kas@fi.muni.cz>
S: Maintained
F: drivers/media/dvb-frontends/cxd2820r*
CXGB3 ETHERNET DRIVER (CXGB3)
-M: Vishal Kulkarni <vishal@chelsio.com>
+M: Raju Rangoju <rajur@chelsio.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.chelsio.com
F: drivers/net/ethernet/chelsio/inline_crypto/
CXGB4 ETHERNET DRIVER (CXGB4)
-M: Vishal Kulkarni <vishal@chelsio.com>
+M: Raju Rangoju <rajur@chelsio.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.chelsio.com
F: include/uapi/rdma/cxgb4-abi.h
CXGB4VF ETHERNET DRIVER (CXGB4VF)
-M: Vishal Kulkarni <vishal@gmail.com>
+M: Raju Rangoju <rajur@chelsio.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.chelsio.com
S: Maintained
F: drivers/platform/x86/dell-wmi-descriptor.c
+DELL WMI SYSMAN DRIVER
+M: Divya Bharathi <divya.bharathi@dell.com>
+M: Mario Limonciello <mario.limonciello@dell.com>
+M: Prasanth Ksr <prasanth.ksr@dell.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-firmware-attributes
+F: drivers/platform/x86/dell-wmi-sysman/
+
DELL WMI NOTIFICATIONS DRIVER
M: Matthew Garrett <mjg59@srcf.ucam.org>
M: Pali Rohár <pali@kernel.org>
F: drivers/media/platform/sti/delta
DENALI NAND DRIVER
-M: Masahiro Yamada <yamada.masahiro@socionext.com>
L: linux-mtd@lists.infradead.org
-S: Supported
+S: Orphan
F: drivers/mtd/nand/raw/denali*
DESIGNWARE EDMA CORE IP DRIVER
S: Supported
W: http://www.dialog-semiconductor.com/products
F: Documentation/devicetree/bindings/input/da90??-onkey.txt
+F: Documentation/devicetree/bindings/input/dlg,da72??.txt
F: Documentation/devicetree/bindings/mfd/da90*.txt
+F: Documentation/devicetree/bindings/regulator/dlg,da9*.yaml
F: Documentation/devicetree/bindings/regulator/da92*.txt
F: Documentation/devicetree/bindings/regulator/slg51000.txt
F: Documentation/devicetree/bindings/sound/da[79]*.txt
F: drivers/gpio/gpio-da90??.c
F: drivers/hwmon/da90??-hwmon.c
F: drivers/iio/adc/da91??-*.c
+F: drivers/input/misc/da72??.[ch]
F: drivers/input/misc/da90??_onkey.c
F: drivers/input/touchscreen/da9052_tsi.c
F: drivers/leds/leds-da90??.c
F: drivers/thermal/da90??-thermal.c
F: drivers/video/backlight/da90??_bl.c
F: drivers/watchdog/da90??_wdt.c
+F: include/dt-bindings/regulator/dlg,da9*-regulator.h
F: include/linux/mfd/da903x.h
F: include/linux/mfd/da9052/
F: include/linux/mfd/da9055/
F: Documentation/devicetree/bindings/display/panel/novatek,nt35510.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt35510.c
+DRM DRIVER FOR NOVATEK NT36672A PANELS
+M: Sumit Semwal <sumit.semwal@linaro.org>
+S: Maintained
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml
+F: drivers/gpu/drm/panel/panel-novatek-nt36672a.c
+
DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS
M: Ben Skeggs <bskeggs@redhat.com>
L: dri-devel@lists.freedesktop.org
F: Documentation/devicetree/bindings/display/mediatek/
F: drivers/gpu/drm/mediatek/
F: drivers/phy/mediatek/phy-mtk-hdmi*
+F: drivers/phy/mediatek/phy-mtk-mipi*
DRM DRIVERS FOR NVIDIA TEGRA
M: Thierry Reding <thierry.reding@gmail.com>
DRM DRIVERS FOR VC4
M: Eric Anholt <eric@anholt.net>
+M: Maxime Ripard <mripard@kernel.org>
S: Supported
T: git git://github.com/anholt/linux
T: git git://anongit.freedesktop.org/drm/drm-misc
S: Maintained
F: drivers/edac/ie31200_edac.c
+EDAC-IGEN6
+M: Tony Luck <tony.luck@intel.com>
+R: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
+L: linux-edac@vger.kernel.org
+S: Maintained
+F: drivers/edac/igen6_edac.c
+
EDAC-MPC85XX
M: Johannes Thumshirn <morbidrsa@gmail.com>
L: linux-edac@vger.kernel.org
M: Tony Luck <tony.luck@intel.com>
L: linux-edac@vger.kernel.org
S: Maintained
-F: drivers/edac/skx_*.c
+F: drivers/edac/skx_*.[ch]
EDAC-TI
M: Tero Kristo <t-kristo@ti.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git
F: Documentation/filesystems/ext4/
F: fs/ext4/
+F: include/trace/events/ext4.h
Extended Verification Module (EVM)
M: Mimi Zohar <zohar@linux.ibm.com>
W: http://floatingpoint.sourceforge.net/emulator/index.html
F: arch/x86/math-emu/
-FRAME RELAY DLCI/FRAD (Sangoma drivers too)
-L: netdev@vger.kernel.org
-S: Orphan
-F: drivers/net/wan/dlci.c
-F: drivers/net/wan/sdla.c
-
FRAMEBUFFER LAYER
-M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
L: dri-devel@lists.freedesktop.org
L: linux-fbdev@vger.kernel.org
-S: Maintained
+S: Orphan
Q: http://patchwork.kernel.org/project/linux-fbdev/list/
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/fb/
GCC PLUGINS
M: Kees Cook <keescook@chromium.org>
-R: Emese Revfy <re.emese@gmail.com>
L: linux-hardening@vger.kernel.org
S: Maintained
F: Documentation/kbuild/gcc-plugins.rst
F: drivers/base/arch_topology.c
F: include/linux/arch_topology.h
+GENERIC ENTRY CODE
+M: Thomas Gleixner <tglx@linutronix.de>
+M: Peter Zijlstra <peterz@infradead.org>
+M: Andy Lutomirski <luto@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/entry
+F: include/linux/entry-common.h
+F: include/linux/entry-kvm.h
+F: kernel/entry/
+
GENERIC GPIO I2C DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
S: Supported
F: drivers/irqchip/irq-renesas-h8*.c
HABANALABS PCI DRIVER
-M: Oded Gabbay <oded.gabbay@gmail.com>
+M: Oded Gabbay <ogabbay@kernel.org>
S: Supported
-T: git https://github.com/HabanaAI/linux.git
+T: git https://git.kernel.org/pub/scm/linux/kernel/git/ogabbay/linux.git
F: Documentation/ABI/testing/debugfs-driver-habanalabs
F: Documentation/ABI/testing/sysfs-driver-habanalabs
F: drivers/misc/habanalabs/
F: include/uapi/linux/if_hippi.h
F: net/802/hippi.c
+HIRSCHMANN HELLCREEK ETHERNET SWITCH DRIVER
+M: Kurt Kanzenbach <kurt@linutronix.de>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml
+F: drivers/net/dsa/hirschmann/*
+F: include/linux/platform_data/hirschmann-hellcreek.h
+F: net/dsa/tag_hellcreek.c
+
HISILICON DMA DRIVER
M: Zhou Wang <wangzhou1@hisilicon.com>
L: dmaengine@vger.kernel.org
M: john.garry@huawei.com
S: Maintained
W: http://www.hisilicon.com
-F: Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
+F: Documentation/devicetree/bindings/arm/hisilicon/low-pin-count.yaml
F: drivers/bus/hisi_lpc.c
HISILICON NETWORK SUBSYSTEM 3 DRIVER (HNS3)
HISILICON TRUE RANDOM NUMBER GENERATOR V2 SUPPORT
M: Zaibo Xu <xuzaibo@huawei.com>
S: Maintained
-F: drivers/char/hw_random/hisi-trng-v2.c
+F: drivers/crypto/hisilicon/trng/trng.c
HISILICON V3XX SPI NOR FLASH Controller Driver
M: John Garry <john.garry@huawei.com>
M: Guenter Roeck <linux@roeck-us.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/hwmon/ina2xx.txt
+F: Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
F: Documentation/hwmon/ina209.rst
F: drivers/hwmon/ina209.c
F: samples/bpf/ibumad_kern.c
F: samples/bpf/ibumad_user.c
-INGENIC JZ4780 DMA Driver
-M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com>
-S: Maintained
-F: drivers/dma/dma-jz4780.c
-
INGENIC JZ4780 NAND DRIVER
M: Harvey Hunt <harveyhuntnexus@gmail.com>
L: linux-mtd@lists.infradead.org
+L: linux-mips@vger.kernel.org
S: Maintained
F: drivers/mtd/nand/raw/ingenic/
INGENIC JZ47xx SoCs
M: Paul Cercueil <paul@crapouillou.net>
+L: linux-mips@vger.kernel.org
S: Maintained
F: arch/mips/boot/dts/ingenic/
F: arch/mips/generic/board-ingenic.c
W: http://www.intel.com/support/feedback.htm
W: http://e1000.sourceforge.net/
Q: http://patchwork.ozlabs.org/project/intel-wired-lan/list/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git
F: Documentation/networking/device_drivers/ethernet/intel/
F: drivers/net/ethernet/intel/
F: drivers/net/ethernet/intel/*/
S: Maintained
F: drivers/char/hw_random/ixp4xx-rng.c
+INTEL KEEM BAY DRM DRIVER
+M: Anitha Chrisanthus <anitha.chrisanthus@intel.com>
+M: Edmund Dea <edmund.j.dea@intel.com>
+S: Maintained
+F: Documentation/devicetree/bindings/display/intel,kmb_display.yaml
+F: drivers/gpu/drm/kmb/
+
+INTEL KEEM BAY OCS AES/SM4 CRYPTO DRIVER
+M: Daniele Alessandrelli <daniele.alessandrelli@intel.com>
+S: Maintained
+F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-aes.yaml
+F: drivers/crypto/keembay/Kconfig
+F: drivers/crypto/keembay/Makefile
+F: drivers/crypto/keembay/keembay-ocs-aes-core.c
+F: drivers/crypto/keembay/ocs-aes.c
+F: drivers/crypto/keembay/ocs-aes.h
+
INTEL MANAGEMENT ENGINE (mei)
M: Tomas Winkler <tomas.winkler@intel.com>
L: linux-kernel@vger.kernel.org
W: https://01.org/linux-acpi
F: drivers/platform/x86/intel_menlow.c
-INTEL MIC DRIVERS (mic)
-M: Sudeep Dutt <sudeep.dutt@intel.com>
-M: Ashutosh Dixit <ashutosh.dixit@intel.com>
-S: Supported
-W: https://github.com/sudeepdutt/mic
-W: http://software.intel.com/en-us/mic-developer
-F: Documentation/misc-devices/mic/
-F: drivers/dma/mic_x100_dma.c
-F: drivers/dma/mic_x100_dma.h
-F: drivers/misc/mic/
-F: include/linux/mic_bus.h
-F: include/linux/scif.h
-F: include/uapi/linux/mic_common.h
-F: include/uapi/linux/mic_ioctl.h
-F: include/uapi/linux/scif_ioctl.h
-
INTEL P-Unit IPC DRIVER
M: Zha Qipeng <qipeng.zha@intel.com>
L: platform-driver-x86@vger.kernel.org
F: include/linux/mfd/intel_msic.h
F: include/linux/mfd/intel_soc_pmic*
+INTEL PMT DRIVER
+M: "David E. Box" <david.e.box@linux.intel.com>
+S: Maintained
+F: drivers/mfd/intel_pmt.c
+F: drivers/platform/x86/intel_pmt_*
+
INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
M: Stanislav Yakovlev <stas.yakovlev@gmail.com>
L: linux-wireless@vger.kernel.org
F: drivers/net/wireless/intel/iwlegacy/
INTEL WIRELESS WIFI LINK (iwlwifi)
-M: Johannes Berg <johannes.berg@intel.com>
-M: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
M: Luca Coelho <luciano.coelho@intel.com>
-M: Intel Linux Wireless <linuxwifi@intel.com>
L: linux-wireless@vger.kernel.org
S: Supported
W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
F: drivers/net/wireless/intel/iwlwifi/
-INTEL WIRELESS WIMAX CONNECTION 2400
-M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
-M: linux-wimax@intel.com
-L: wimax@linuxwimax.org (subscribers-only)
-S: Supported
-W: http://linuxwimax.org
-F: Documentation/admin-guide/wimax/i2400m.rst
-F: drivers/net/wimax/i2400m/
-F: include/uapi/linux/wimax/i2400m.h
-
INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER
M: Jithu Joseph <jithu.joseph@intel.com>
R: Maurice Ma <maurice.ma@intel.com>
F: arch/x86/kernel/tboot.c
F: include/linux/tboot.h
+INTEL SGX
+M: Jarkko Sakkinen <jarkko@kernel.org>
+L: linux-sgx@vger.kernel.org
+S: Supported
+Q: https://patchwork.kernel.org/project/intel-sgx/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git
+F: Documentation/x86/sgx.rst
+F: arch/x86/entry/vdso/vsgx.S
+F: arch/x86/include/uapi/asm/sgx.h
+F: arch/x86/kernel/cpu/sgx/*
+F: tools/testing/selftests/sgx/*
+K: \bSGX_
+
INTERCONNECT API
M: Georgi Djakov <georgi.djakov@linaro.org>
L: linux-pm@vger.kernel.org
M: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
L: linux-iio@vger.kernel.org
S: Maintained
-W https://invensense.tdk.com/
+W: https://invensense.tdk.com/
F: Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
F: drivers/iio/imu/inv_icm42600/
IOMMU DRIVERS
M: Joerg Roedel <joro@8bytes.org>
+M: Will Deacon <will@kernel.org>
L: iommu@lists.linux-foundation.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
IRQCHIP DRIVERS
M: Thomas Gleixner <tglx@linutronix.de>
-M: Jason Cooper <jason@lakedaemon.net>
M: Marc Zyngier <maz@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: include/kvm/arm_*
KERNEL VIRTUAL MACHINE FOR MIPS (KVM/mips)
-M: Huacai Chen <chenhc@lemote.com>
+M: Huacai Chen <chenhuacai@kernel.org>
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
L: linux-mips@vger.kernel.org
L: kvm@vger.kernel.org
F: arch/s390/include/asm/gmap.h
F: arch/s390/include/asm/kvm*
F: arch/s390/include/uapi/asm/kvm*
+F: arch/s390/kernel/uv.c
F: arch/s390/kvm/
F: arch/s390/mm/gmap.c
F: tools/testing/selftests/kvm/*/s390x/
F: arch/mips/lantiq
F: drivers/soc/lantiq
-LAPB module
-L: linux-x25@vger.kernel.org
-S: Orphan
-F: Documentation/networking/lapb-module.rst
-F: include/*/lapb.h
-F: net/lapb/
-
LASI 53c700 driver for PARISC
M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
L: linux-scsi@vger.kernel.org
S: Maintained
W: http://www.mac.linux-m68k.org/
F: arch/m68k/mac/
+F: drivers/macintosh/adb-iop.c
+F: drivers/macintosh/via-macii.c
M68K ON HP9000/300
M: Philip Blundell <philb@gnu.org>
L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/crypto/marvell/
+F: include/linux/soc/marvell/octeontx2/
MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
M: Mirko Lindner <mlindner@marvell.com>
S: Maintained
F: drivers/net/ethernet/marvell/mvneta.*
+MARVELL MVPP2 ETHERNET DRIVER
+M: Marcin Wojtas <mw@semihalf.com>
+M: Russell King <linux@armlinux.org.uk>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/marvell-pp2.txt
+F: drivers/net/ethernet/marvell/mvpp2/
+
MARVELL MWIFIEX WIRELESS DRIVER
M: Amitkumar Karwar <amitkarwar@gmail.com>
M: Ganapathi Bhat <ganapathi.bhat@nxp.com>
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/marvell/octeontx2/nic/
+F: include/linux/soc/marvell/octeontx2/
MARVELL OCTEONTX2 RVU ADMIN FUNCTION DRIVER
M: Sunil Goutham <sgoutham@marvell.com>
F: Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst
F: drivers/net/ethernet/marvell/octeontx2/af/
+MARVELL PRESTERA ETHERNET SWITCH DRIVER
+M: Vadym Kochan <vkochan@marvell.com>
+M: Taras Chornyi <tchornyi@marvell.com>
+S: Supported
+W: https://github.com/Marvell-switching/switchdev-prestera
+F: drivers/net/ethernet/marvell/prestera/
+
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
M: Nicolas Pitre <nico@fluxnic.net>
S: Odd Fixes
S: Maintained
T: git git://linuxtv.org/media_tree.git
F: Documentation/admin-guide/media/imx7.rst
-F: Documentation/devicetree/bindings/media/imx7-csi.txt
-F: Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt
+F: Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml
+F: Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml
F: drivers/staging/media/imx/imx7-media-csi.c
F: drivers/staging/media/imx/imx7-mipi-csis.c
F: Documentation/devicetree/bindings/i2c/i2c-mt7621.txt
F: drivers/i2c/busses/i2c-mt7621.c
+MEDIATEK MT7621 PHY PCI DRIVER
+M: Sergio Paracuellos <sergio.paracuellos@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/phy/mediatek,mt7621-pci-phy.yaml
+F: drivers/phy/ralink/phy-mt7621-pci.c
+
MEDIATEK NAND CONTROLLER DRIVER
L: linux-mtd@lists.infradead.org
S: Orphan
F: drivers/input/touchscreen/melfas_mip4.c
MELLANOX BLUEFIELD I2C DRIVER
-M: Khalil Blaiech <kblaiech@mellanox.com>
+M: Khalil Blaiech <kblaiech@nvidia.com>
L: linux-i2c@vger.kernel.org
S: Supported
+F: Documentation/devicetree/bindings/i2c/mellanox,i2c-mlxbf.yaml
F: drivers/i2c/busses/i2c-mlxbf.c
MELLANOX ETHERNET DRIVER (mlx4_en)
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx4/en_*
MELLANOX ETHERNET DRIVER (mlx5e)
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx5/core/en_*
MELLANOX ETHERNET INNOVA DRIVERS
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx5/core/accel/*
F: drivers/net/ethernet/mellanox/mlx5/core/en_accel/*
F: drivers/net/ethernet/mellanox/mlx5/core/fpga/*
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlxsw/
F: tools/testing/selftests/drivers/net/mlxsw/
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlxfw/
MELLANOX HARDWARE PLATFORM SUPPORT
L: linux-rdma@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: drivers/net/ethernet/mellanox/mlx4/
F: include/linux/mlx4/
L: linux-rdma@vger.kernel.org
S: Supported
W: http://www.mellanox.com
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
F: Documentation/networking/device_drivers/ethernet/mellanox/
F: drivers/net/ethernet/mellanox/mlx5/core/
F: include/linux/mlx5/
L: linux-tegra@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Maintained
-F: drivers/devfreq/tegra20-devfreq.c
F: drivers/devfreq/tegra30-devfreq.c
MEMORY MANAGEMENT
F: drivers/media/cec/platform/meson/ao-cec-g12a.c
F: drivers/media/cec/platform/meson/ao-cec.c
+MESON GE2D DRIVER FOR AMLOGIC SOCS
+M: Neil Armstrong <narmstrong@baylibre.com>
+L: linux-media@vger.kernel.org
+L: linux-amlogic@lists.infradead.org
+S: Supported
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml
+F: drivers/media/meson/ge2d/
+
MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS
M: Liang Yang <liang.yang@amlogic.com>
L: linux-mtd@lists.infradead.org
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
L: netdev@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/net/dsa/ksz.txt
+F: Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
F: drivers/net/dsa/microchip/*
F: include/linux/platform_data/microchip-ksz.h
F: net/dsa/tag_ksz.c
F: include/linux/cciss*.h
F: include/uapi/linux/cciss*.h
+MICROSOFT SURFACE GPE LID SUPPORT DRIVER
+M: Maximilian Luz <luzmaximilian@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/surface/surface_gpe.c
+
+MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT
+M: Hans de Goede <hdegoede@redhat.com>
+M: Mark Gross <mgross@linux.intel.com>
+M: Maximilian Luz <luzmaximilian@gmail.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
+F: drivers/platform/surface/
+
MICROSOFT SURFACE PRO 3 BUTTON DRIVER
M: Chen Yu <yu.c.chen@intel.com>
L: platform-driver-x86@vger.kernel.org
S: Supported
-F: drivers/platform/x86/surfacepro3_button.c
+F: drivers/platform/surface/surfacepro3_button.c
MICROTEK X6 SCANNER
M: Oliver Neukum <oliver@neukum.org>
S: Maintained
F: drivers/usb/image/microtek.*
+MIPI CCS, SMIA AND SMIA++ IMAGE SENSOR DRIVER
+M: Sakari Ailus <sakari.ailus@linux.intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml
+F: Documentation/driver-api/media/drivers/ccs/
+F: drivers/media/i2c/ccs-pll.c
+F: drivers/media/i2c/ccs-pll.h
+F: drivers/media/i2c/ccs/
+F: include/uapi/linux/smiapp.h
+
MIPS
M: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
L: linux-mips@vger.kernel.org
F: drivers/*/*loongson2*
MIPS/LOONGSON64 ARCHITECTURE
-M: Huacai Chen <chenhc@lemote.com>
+M: Huacai Chen <chenhuacai@kernel.org>
M: Jiaxun Yang <jiaxun.yang@flygoat.com>
L: linux-mips@vger.kernel.org
S: Maintained
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
-F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt
+F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml
F: drivers/media/i2c/mt9v111.c
MULTIFUNCTION DEVICES (MFD)
L: netdev@vger.kernel.org
S: Maintained
W: http://www.linuxfoundation.org/en/Net
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
F: Documentation/devicetree/bindings/net/
L: netdev@vger.kernel.org
S: Maintained
W: http://www.linuxfoundation.org/en/Net
-Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: https://patchwork.kernel.org/project/netdevbpf/list/
B: mailto:netdev@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
S: Maintained
W: https://github.com/multipath-tcp/mptcp_net-next/wiki
B: https://github.com/multipath-tcp/mptcp_net-next/issues
+F: Documentation/networking/mptcp-sysctl.rst
F: include/net/mptcp.h
F: include/uapi/linux/mptcp.h
F: net/mptcp/
M: Rui Miguel Silva <rmfrfs@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/iio/gyroscope/nxp,fxas21002c.txt
+F: Documentation/devicetree/bindings/iio/gyroscope/nxp,fxas21002c.yaml
F: drivers/iio/gyro/fxas21002c.h
F: drivers/iio/gyro/fxas21002c_core.c
F: drivers/iio/gyro/fxas21002c_i2c.c
F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml
F: drivers/gpu/drm/imx/dcss/
+NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
+M: Jagan Teki <jagan@amarulasolutions.com>
+S: Maintained
+F: Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml
+F: drivers/regulator/pf8x00-regulator.c
+
NXP PTN5150A CC LOGIC AND EXTCON DRIVER
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/char/pcmcia/cm4040_cs.*
+OMNIVISION OV02A10 SENSOR DRIVER
+M: Dongchun Zhu <dongchun.zhu@mediatek.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml
+F: drivers/media/i2c/ov02a10.c
+
OMNIVISION OV13858 SENSOR DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
-F: Documentation/devicetree/bindings/media/i2c/ov2680.txt
+F: Documentation/devicetree/bindings/media/i2c/ov2680.yaml
F: drivers/media/i2c/ov2680.c
OMNIVISION OV2685 SENSOR DRIVER
F: drivers/media/i2c/ov5695.c
OMNIVISION OV7670 SENSOR DRIVER
-M: Jonathan Corbet <corbet@lwn.net>
L: linux-media@vger.kernel.org
-S: Maintained
+S: Orphan
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/ov7670.txt
F: drivers/media/i2c/ov7670.c
L: linux-media@vger.kernel.org
S: Odd fixes
T: git git://linuxtv.org/media_tree.git
-F: Documentation/devicetree/bindings/media/i2c/ov772x.txt
+F: Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml
F: drivers/media/i2c/ov772x.c
F: include/media/i2c/ov772x.h
F: Documentation/devicetree/bindings/media/i2c/ov9650.txt
F: drivers/media/i2c/ov9650.c
+OMNIVISION OV9734 SENSOR DRIVER
+M: Tianshu Qiu <tian.shu.qiu@intel.com>
+R: Bingbu Cao <bingbu.cao@intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: drivers/media/i2c/ov9734.c
+
ONENAND FLASH DRIVER
M: Kyungmin Park <kyungmin.park@samsung.com>
L: linux-mtd@lists.infradead.org
M: Ilias Apalodimas <ilias.apalodimas@linaro.org>
L: netdev@vger.kernel.org
S: Supported
+F: Documentation/networking/page_pool.rst
F: include/net/page_pool.h
+F: include/trace/events/page_pool.h
F: net/core/page_pool.c
PANASONIC LAPTOP ACPI EXTRAS DRIVER
-M: Harald Welte <laforge@gnumonks.org>
+M: Kenneth Chan <kenneth.t.chan@gmail.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/panasonic-laptop.c
PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
M: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
-M: Jason Cooper <jason@lakedaemon.net>
L: linux-pci@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
L: linux-pci@vger.kernel.org
S: Supported
+F: Documentation/PCI/endpoint/*
+F: Documentation/misc-devices/pci-endpoint-test.rst
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/pci-endpoint.git
F: drivers/misc/pci_endpoint_test.c
F: drivers/pci/endpoint/
S: Maintained
F: drivers/dma/plx_dma.c
+PM6764TR DRIVER
+M: Charles Hsu <hsu.yungteng@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/pm6764tr.rst
+F: drivers/hwmon/pmbus/pm6764tr.c
+
PM-GRAPH UTILITY
M: "Todd E Brandt" <todd.e.brandt@linux.intel.com>
L: linux-pm@vger.kernel.org
F: include/trace/events/pwc.h
PWM FAN DRIVER
-M: Kamil Debski <kamil@wypas.org>
M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
L: linux-hwmon@vger.kernel.org
S: Supported
F: drivers/net/wireless/ath/ath9k/
QUALCOMM CAMERA SUBSYSTEM DRIVER
+M: Robert Foss <robert.foss@linaro.org>
M: Todor Tomov <todor.too@gmail.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/mailbox/qcom-ipcc.c
F: include/dt-bindings/mailbox/qcom-ipcc.h
+QUALCOMM IPQ4019 USB PHY DRIVER
+M: Robert Marko <robert.marko@sartura.hr>
+M: Luka Perkov <luka.perkov@sartura.hr>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/phy/qcom-usb-ipq4019-phy.yaml
+F: drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c
+
+QUALCOMM IPQ4019 VQMMC REGULATOR DRIVER
+M: Robert Marko <robert.marko@sartura.hr>
+M: Luka Perkov <luka.perkov@sartura.hr>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/regulator/vqmmc-ipq4019-regulator.yaml
+F: drivers/regulator/vqmmc-ipq4019-regulator.c
+
QUALCOMM RMNET DRIVER
M: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
M: Sean Tranchetti <stranche@codeaurora.org>
F: drivers/net/wireless/realtek/rtlwifi/
REALTEK WIRELESS DRIVER (rtw88)
-M: Yan-Hsuan Chuang <yhchuang@realtek.com>
+M: Yan-Hsuan Chuang <tony0620emma@gmail.com>
L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/net/wireless/realtek/rtw88/
R: Sergei Shtylyov <sergei.shtylyov@gmail.com>
L: netdev@vger.kernel.org
L: linux-renesas-soc@vger.kernel.org
-F: Documentation/devicetree/bindings/net/renesas,*.txt
F: Documentation/devicetree/bindings/net/renesas,*.yaml
F: drivers/net/ethernet/renesas/
F: include/linux/sh_eth.h
S: Maintained
T: git git://git.pengutronix.de/git/pza/linux
F: Documentation/devicetree/bindings/reset/
+F: Documentation/driver-api/reset.rst
F: drivers/reset/
F: include/dt-bindings/reset/
F: include/linux/reset-controller.h
M: Helen Koike <helen.koike@collabora.com>
M: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
L: linux-media@vger.kernel.org
+L: linux-rockchip@lists.infradead.org
S: Maintained
F: Documentation/admin-guide/media/rkisp1.rst
+F: Documentation/devicetree/bindings/media/rockchip-isp1.yaml
F: Documentation/userspace-api/media/v4l/pixfmt-meta-rkisp1.rst
-F: drivers/staging/media/rkisp1/
+F: drivers/media/platform/rockchip/rkisp1
+F: include/uapi/linux/rkisp1-config.h
ROCKCHIP RASTER 2D GRAPHIC ACCELERATION UNIT DRIVER
M: Jacob Chen <jacob-chen@iotwrt.com>
S390 IUCV NETWORK LAYER
M: Julian Wiedmann <jwi@linux.ibm.com>
M: Karsten Graul <kgraul@linux.ibm.com>
-M: Ursula Braun <ubraun@linux.ibm.com>
L: linux-s390@vger.kernel.org
S: Supported
W: http://www.ibm.com/developerworks/linux/linux390/
S390 NETWORK DRIVERS
M: Julian Wiedmann <jwi@linux.ibm.com>
M: Karsten Graul <kgraul@linux.ibm.com>
-M: Ursula Braun <ubraun@linux.ibm.com>
L: linux-s390@vger.kernel.org
S: Supported
W: http://www.ibm.com/developerworks/linux/linux390/
SAMSUNG AUDIO (ASoC) DRIVERS
M: Krzysztof Kozlowski <krzk@kernel.org>
-M: Sangbeom Kim <sbkim73@samsung.com>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Supported
S: Maintained
F: drivers/video/fbdev/s3c-fb.c
+SAMSUNG INTERCONNECT DRIVERS
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+M: Artur Świgoń <a.swigon@samsung.com>
+L: linux-pm@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+F: drivers/interconnect/samsung/
+
SAMSUNG LAPTOP DRIVER
M: Corentin Chary <corentin.chary@gmail.com>
L: platform-driver-x86@vger.kernel.org
F: drivers/platform/x86/samsung-laptop.c
SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS
-M: Sangbeom Kim <sbkim73@samsung.com>
M: Krzysztof Kozlowski <krzk@kernel.org>
M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
L: linux-kernel@vger.kernel.org
F: drivers/nfc/s3fwrn5
SAMSUNG S5C73M3 CAMERA DRIVER
-M: Kyungmin Park <kyungmin.park@samsung.com>
M: Andrzej Hajda <a.hajda@samsung.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/i2c/s5c73m3/*
SAMSUNG S5K5BAF CAMERA DRIVER
-M: Kyungmin Park <kyungmin.park@samsung.com>
M: Andrzej Hajda <a.hajda@samsung.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/crypto/s5p-sss.c
SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS
-M: Kyungmin Park <kyungmin.park@samsung.com>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
L: linux-media@vger.kernel.org
S: Supported
F: include/linux/platform_data/clk-s3c2410.h
SAMSUNG SPI DRIVERS
-M: Kukjin Kim <kgene@kernel.org>
M: Krzysztof Kozlowski <krzk@kernel.org>
M: Andi Shyti <andi@etezian.org>
L: linux-spi@vger.kernel.org
F: drivers/thermal/samsung/
SAMSUNG USB2 PHY DRIVER
-M: Kamil Debski <kamil@wypas.org>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
L: linux-kernel@vger.kernel.org
S: Supported
F: drivers/scsi/st.*
F: drivers/scsi/st_*.h
+ SCSI TARGET CORE USER DRIVER
+ M: Bodo Stroesser <bostroesser@gmail.com>
+ L: linux-scsi@vger.kernel.org
+ L: target-devel@vger.kernel.org
+ S: Supported
+ F: Documentation/target/tcmu-design.rst
+ F: drivers/target/target_core_user.c
+ F: include/uapi/linux/target_core_user.h
+
SCSI TARGET SUBSYSTEM
M: "Martin K. Petersen" <martin.petersen@oracle.com>
L: linux-scsi@vger.kernel.org
F: include/linux/slimbus.h
SFC NETWORK DRIVER
-M: Solarflare linux maintainers <linux-net-drivers@solarflare.com>
-M: Edward Cree <ecree@solarflare.com>
-M: Martin Habets <mhabets@solarflare.com>
+M: Edward Cree <ecree.xilinx@gmail.com>
+M: Martin Habets <habetsm.xilinx@gmail.com>
L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/sfc/
K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate)
SGI GRU DRIVER
-M: Dimitri Sivanich <sivanich@sgi.com>
+M: Dimitri Sivanich <dimitri.sivanich@hpe.com>
S: Maintained
F: drivers/misc/sgi-gru/
SGI XP/XPC/XPNET DRIVER
-M: Cliff Whickman <cpw@sgi.com>
M: Robin Holt <robinmholt@gmail.com>
+M: Steve Wahl <steve.wahl@hpe.com>
+R: Mike Travis <mike.travis@hpe.com>
S: Maintained
F: drivers/misc/sgi-xp/
SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
-M: Ursula Braun <ubraun@linux.ibm.com>
M: Karsten Graul <kgraul@linux.ibm.com>
L: linux-s390@vger.kernel.org
S: Supported
F: drivers/firmware/smccc/
F: include/linux/arm-smccc.h
-SMIA AND SMIA++ IMAGE SENSOR DRIVER
-M: Sakari Ailus <sakari.ailus@linux.intel.com>
-L: linux-media@vger.kernel.org
-S: Maintained
-F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt
-F: drivers/media/i2c/smiapp-pll.c
-F: drivers/media/i2c/smiapp-pll.h
-F: drivers/media/i2c/smiapp/
-F: include/uapi/linux/smiapp.h
-
SMM665 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-hwmon@vger.kernel.org
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
-F: Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
+F: Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml
F: drivers/media/i2c/imx214.c
SONY IMX219 SENSOR DRIVER
F: drivers/net/ethernet/toshiba/spider_net*
SPMI SUBSYSTEM
-R: Stephen Boyd <sboyd@kernel.org>
-L: linux-arm-msm@vger.kernel.org
+M: Stephen Boyd <sboyd@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/sboyd/spmi.git
F: Documentation/devicetree/bindings/spmi/
F: drivers/spmi/
F: include/dt-bindings/spmi/spmi.h
T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
F: drivers/platform/x86/thinkpad_acpi.c
+THUNDERBOLT DMA TRAFFIC TEST DRIVER
+M: Isaac Hazan <isaac.hazan@intel.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/thunderbolt/dma_test.c
+
THUNDERBOLT DRIVER
M: Andreas Noever <andreas.noever@gmail.com>
M: Michael Jamet <michael.jamet@intel.com>
M: Binghui Wang <wangbinghui@hisilicon.com>
L: linux-usb@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/phy/phy-hi3660-usb3.txt
+F: Documentation/devicetree/bindings/phy/hisilicon,hi3660-usb3.yaml
F: drivers/phy/hisilicon/phy-hi3660-usb3.c
USB ISP116X DRIVER
S: Supported
F: drivers/usb/class/usblp.c
+USB RAW GADGET DRIVER
+R: Andrey Konovalov <andreyknvl@gmail.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/usb/raw-gadget.rst
+F: drivers/usb/gadget/legacy/raw_gadget.c
+F: include/uapi/linux/usb/raw_gadget.h
+
USB QMI WWAN NETWORK DRIVER
M: Bjørn Mork <bjorn@mork.no>
L: netdev@vger.kernel.org
F: lib/test_uuid.c
F: lib/uuid.c
+UV SYSFS DRIVER
+M: Justin Ernst <justin.ernst@hpe.com>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/uv_sysfs.c
+
UVESAFB DRIVER
M: Michal Januszewski <spock@gentoo.org>
L: linux-fbdev@vger.kernel.org
W: https://wireless.wiki.kernel.org/en/users/Drivers/wil6210
F: drivers/net/wireless/ath/wil6210/
-WIMAX STACK
-M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
-M: linux-wimax@intel.com
-L: wimax@linuxwimax.org (subscribers-only)
-S: Supported
-W: http://linuxwimax.org
-F: Documentation/admin-guide/wimax/wimax.rst
-F: include/linux/wimax/debug.h
-F: include/net/wimax.h
-F: include/uapi/linux/wimax.h
-F: net/wimax/
-
WINBOND CIR DRIVER
M: David Härdeman <david@hardeman.nu>
S: Maintained
S: Maintained
N: axp[128]
-X.25 NETWORK LAYER
-M: Andrew Hendry <andrew.hendry@gmail.com>
+X.25 STACK
+M: Martin Schiller <ms@dev.tdt.de>
L: linux-x25@vger.kernel.org
-S: Odd Fixes
+S: Maintained
+F: Documentation/networking/lapb-module.rst
F: Documentation/networking/x25*
+F: drivers/net/wan/hdlc_x25.c
+F: drivers/net/wan/lapbether.c
+F: include/*/lapb.h
F: include/net/x25*
+F: include/uapi/linux/x25.h
+F: net/lapb/
F: net/x25/
X86 ARCHITECTURE (32-BIT AND 64-BIT)
X86 PLATFORM UV HPE SUPERDOME FLEX
M: Steve Wahl <steve.wahl@hpe.com>
+R: Mike Travis <mike.travis@hpe.com>
R: Dimitri Sivanich <dimitri.sivanich@hpe.com>
R: Russ Anderson <russ.anderson@hpe.com>
S: Supported
L: bpf@vger.kernel.org
S: Supported
F: include/net/xdp.h
+F: include/net/xdp_priv.h
F: include/trace/events/xdp.h
F: kernel/bpf/cpumap.c
F: kernel/bpf/devmap.c
F: net/core/xdp.c
-N: xdp
-K: xdp
+F: samples/bpf/xdp*
+F: tools/testing/selftests/bpf/*xdp*
+F: tools/testing/selftests/bpf/*/*xdp*
+F: drivers/net/ethernet/*/*/*/*/*xdp*
+F: drivers/net/ethernet/*/*/*xdp*
+K: (?:\b|_)xdp(?:\b|_)
XDP SOCKETS (AF_XDP)
M: Björn Töpel <bjorn.topel@intel.com>
L: netdev@vger.kernel.org
L: bpf@vger.kernel.org
S: Maintained
+F: Documentation/networking/af_xdp.rst
F: include/net/xdp_sock*
F: include/net/xsk_buff_pool.h
F: include/uapi/linux/if_xdp.h
+F: include/uapi/linux/xdp_diag.h
+F: include/net/netns/xdp.h
F: net/xdp/
F: samples/bpf/xdpsock*
F: tools/lib/bpf/xsk*
F: Documentation/filesystems/zonefs.rst
F: fs/zonefs/
+ZPOOL COMPRESSED PAGE STORAGE API
+M: Dan Streetman <ddstreet@ieee.org>
+L: linux-mm@kvack.org
+S: Maintained
+F: include/linux/zpool.h
+F: mm/zpool.c
+
ZR36067 VIDEO FOR LINUX DRIVER
M: Corentin Labbe <clabbe@baylibre.com>
L: mjpeg-users@lists.sourceforge.net
F: Documentation/driver-api/media/drivers/zoran.rst
F: drivers/staging/media/zoran/
-ZPOOL COMPRESSED PAGE STORAGE API
-M: Dan Streetman <ddstreet@ieee.org>
-L: linux-mm@kvack.org
-S: Maintained
-F: include/linux/zpool.h
-F: mm/zpool.c
-
ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER
M: Minchan Kim <minchan@kernel.org>
M: Nitin Gupta <ngupta@vflare.org>
}
struct mq_inflight {
- struct hd_struct *part;
+ struct block_device *part;
unsigned int inflight[2];
};
{
struct mq_inflight *mi = priv;
- if (rq->part == mi->part && blk_mq_rq_state(rq) == MQ_RQ_IN_FLIGHT)
+ if ((!mi->part->bd_partno || rq->part == mi->part) &&
+ blk_mq_rq_state(rq) == MQ_RQ_IN_FLIGHT)
mi->inflight[rq_data_dir(rq)]++;
return true;
}
-unsigned int blk_mq_in_flight(struct request_queue *q, struct hd_struct *part)
+unsigned int blk_mq_in_flight(struct request_queue *q,
+ struct block_device *part)
{
struct mq_inflight mi = { .part = part };
return mi.inflight[0] + mi.inflight[1];
}
-void blk_mq_in_flight_rw(struct request_queue *q, struct hd_struct *part,
- unsigned int inflight[2])
+void blk_mq_in_flight_rw(struct request_queue *q, struct block_device *part,
+ unsigned int inflight[2])
{
struct mq_inflight mi = { .part = part };
return false;
if (blk_mq_complete_need_ipi(rq)) {
- rq->csd.func = __blk_mq_complete_request_remote;
- rq->csd.info = rq;
- rq->csd.flags = 0;
+ INIT_CSD(&rq->csd, __blk_mq_complete_request_remote, rq);
smp_call_function_single_async(rq->mq_ctx->cpu, &rq->csd);
} else {
if (rq->q->nr_hw_queues > 1)
{
struct request_queue *q = rq->q;
- trace_block_rq_issue(q, rq);
+ trace_block_rq_issue(rq);
if (test_bit(QUEUE_FLAG_STATS, &q->queue_flags)) {
rq->io_start_time_ns = ktime_get_ns();
blk_mq_put_driver_tag(rq);
- trace_block_rq_requeue(q, rq);
+ trace_block_rq_requeue(rq);
rq_qos_requeue(q, rq);
if (blk_mq_request_started(rq)) {
break;
default:
errors++;
- blk_mq_end_request(rq, BLK_STS_IOERR);
+ blk_mq_end_request(rq, ret);
}
} while (!list_empty(list));
out:
* __blk_mq_delay_run_hw_queue - Run (or schedule to run) a hardware queue.
* @hctx: Pointer to the hardware queue to run.
* @async: If we want to run the queue asynchronously.
- * @msecs: Microseconds of delay to wait before running the queue.
+ * @msecs: Milliseconds of delay to wait before running the queue.
*
* If !@async, try to run the queue now. Else, run the queue asynchronously and
* with a delay of @msecs.
/**
* blk_mq_delay_run_hw_queue - Run a hardware queue asynchronously.
* @hctx: Pointer to the hardware queue to run.
- * @msecs: Microseconds of delay to wait before running the queue.
+ * @msecs: Milliseconds of delay to wait before running the queue.
*
* Run a hardware queue asynchronously with a delay of @msecs.
*/
/**
* blk_mq_delay_run_hw_queues - Run all hardware queues asynchronously.
* @q: Pointer to the request queue to run.
- * @msecs: Microseconds of delay to wait before running the queues.
+ * @msecs: Milliseconds of delay to wait before running the queues.
*/
void blk_mq_delay_run_hw_queues(struct request_queue *q, unsigned long msecs)
{
lockdep_assert_held(&ctx->lock);
- trace_block_rq_insert(hctx->queue, rq);
+ trace_block_rq_insert(rq);
if (at_head)
list_add(&rq->queuelist, &ctx->rq_lists[type]);
*/
list_for_each_entry(rq, list, queuelist) {
BUG_ON(rq->mq_ctx != ctx);
- trace_block_rq_insert(hctx->queue, rq);
+ trace_block_rq_insert(rq);
}
spin_lock(&ctx->lock);
unsigned int nr_segs;
blk_qc_t cookie;
blk_status_t ret;
+ bool hipri;
blk_queue_bounce(q, &bio);
__blk_queue_split(&bio, &nr_segs);
rq_qos_throttle(q, bio);
+ hipri = bio->bi_opf & REQ_HIPRI;
+
data.cmd_flags = bio->bi_opf;
rq = __blk_mq_alloc_request(&data);
if (unlikely(!rq)) {
goto queue_exit;
}
- trace_block_getrq(q, bio, bio->bi_opf);
+ trace_block_getrq(bio);
rq_qos_track(q, rq, bio);
blk_mq_sched_insert_request(rq, false, true, true);
}
+ if (!hipri)
+ return BLK_QC_T_NONE;
return cookie;
queue_exit:
blk_queue_exit(q);
return 0;
}
+static int blk_mq_alloc_tag_set_tags(struct blk_mq_tag_set *set,
+ int new_nr_hw_queues)
+{
+ return blk_mq_realloc_tag_set_tags(set, 0, new_nr_hw_queues);
+}
+
/*
* Alloc a tag set to be associated with one or more request queues.
* May fail with EINVAL for various error conditions. May adjust the
if (set->nr_maps == 1 && set->nr_hw_queues > nr_cpu_ids)
set->nr_hw_queues = nr_cpu_ids;
- if (blk_mq_realloc_tag_set_tags(set, 0, set->nr_hw_queues) < 0)
+ if (blk_mq_alloc_tag_set_tags(set, set->nr_hw_queues) < 0)
return -ENOMEM;
ret = -ENOMEM;
* the state. Like for the other success return cases, the
* caller is responsible for checking if the IO completed. If
* the IO isn't complete, we'll get called again and will go
- * straight to the busy poll loop.
+ * straight to the busy poll loop. If specified not to spin,
+ * we also should not sleep.
*/
- if (blk_mq_poll_hybrid(q, hctx, cookie))
+ if (spin && blk_mq_poll_hybrid(q, hctx, cookie))
return 1;
hctx->poll_considered++;
/**
* srpt_unregister_mad_agent - unregister MAD callback functions
* @sdev: SRPT HCA pointer.
+ * @port_cnt: number of ports with registered MAD
*
* Note: It is safe to call this function more than once for the same device.
*/
-static void srpt_unregister_mad_agent(struct srpt_device *sdev)
+static void srpt_unregister_mad_agent(struct srpt_device *sdev, int port_cnt)
{
struct ib_port_modify port_modify = {
.clr_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP,
struct srpt_port *sport;
int i;
- for (i = 1; i <= sdev->device->phys_port_cnt; i++) {
+ for (i = 1; i <= port_cnt; i++) {
sport = &sdev->port[i - 1];
WARN_ON(sport->port != i);
if (sport->mad_agent) {
se_sess = ch->sess;
BUG_ON(!se_sess);
- target_sess_cmd_list_set_waiting(se_sess);
+ target_stop_session(se_sess);
target_wait_for_sess_cmds(se_sess);
target_remove_session(se_sess);
if (ret) {
pr_err("MAD registration failed for %s-%d.\n",
dev_name(&sdev->device->dev), i);
- goto err_event;
+ i--;
+ goto err_port;
}
}
pr_debug("added %s.\n", dev_name(&device->dev));
return 0;
-err_event:
+err_port:
+ srpt_unregister_mad_agent(sdev, i);
ib_unregister_event_handler(&sdev->event_handler);
err_cm:
if (sdev->cm_id)
struct srpt_device *sdev = client_data;
int i;
- srpt_unregister_mad_agent(sdev);
+ srpt_unregister_mad_agent(sdev, sdev->device->phys_port_cnt);
ib_unregister_event_handler(&sdev->event_handler);
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/delay.h> /* for mdelay */
- #include <linux/interrupt.h> /* needed for in_interrupt() proto */
+ #include <linux/interrupt.h>
#include <linux/reboot.h> /* notifier code */
#include <linux/workqueue.h>
MPT_SCSI_HOST *hd;
int sz1;
- if((hd = shost_priv(host)) == NULL)
- return;
+ if (host == NULL)
+ hd = NULL;
+ else
+ hd = shost_priv(host);
mptscsih_shutdown(pdev);
"Free'd ScsiLookup (%d) memory\n",
ioc->name, sz1));
- kfree(hd->info_kbuf);
+ if (hd)
+ kfree(hd->info_kbuf);
/* NULL the Scsi_Host pointer
*/
ioc->sh = NULL;
- scsi_host_put(host);
-
+ if (host)
+ scsi_host_put(host);
mpt_detach(pdev);
}
#define ZFCP_STATUS_ADAPTER_SIOSL_ISSUED 0x00000004
#define ZFCP_STATUS_ADAPTER_XCONFIG_OK 0x00000008
#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT 0x00000010
-#define ZFCP_STATUS_ADAPTER_SUSPENDED 0x00000040
#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100
#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200
#define ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED 0x00000400
struct zfcp_fc_events events;
unsigned long next_port_scan;
struct zfcp_diag_adapter *diagnostics;
+ struct work_struct version_change_lost_work;
};
struct zfcp_port {
extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32,
u32);
- extern void zfcp_sg_free_table(struct scatterlist *, int);
- extern int zfcp_sg_setup_table(struct scatterlist *, int);
extern void zfcp_adapter_release(struct kref *);
extern void zfcp_adapter_unregister(struct zfcp_adapter *);
u32 fc_security_new);
extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **);
-extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *);
extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32);
extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *);
}
}
+ static void
+ zfcp_fsf_status_read_version_change(struct zfcp_adapter *adapter,
+ struct fsf_status_read_buffer *sr_buf)
+ {
+ if (sr_buf->status_subtype == FSF_STATUS_READ_SUB_LIC_CHANGE) {
+ u32 version = sr_buf->payload.version_change.current_version;
+
+ WRITE_ONCE(adapter->fsf_lic_version, version);
+ snprintf(fc_host_firmware_version(adapter->scsi_host),
+ FC_VERSION_STRING_SIZE, "%#08x", version);
+ }
+ }
+
static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
{
struct zfcp_adapter *adapter = req->adapter;
case FSF_STATUS_READ_NOTIFICATION_LOST:
if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS)
zfcp_fc_conditional_port_scan(adapter);
+ if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_VERSION_CHANGE)
+ queue_work(adapter->work_queue,
+ &adapter->version_change_lost_work);
break;
case FSF_STATUS_READ_FEATURE_UPDATE_ALERT:
adapter->adapter_features = sr_buf->payload.word[0];
break;
+ case FSF_STATUS_READ_VERSION_CHANGE:
+ zfcp_fsf_status_read_version_change(adapter, sr_buf);
+ break;
}
mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data);
}
}
- blk_add_driver_data(scsi->request->q, scsi->request, &blktrc,
- sizeof(blktrc));
+ blk_add_driver_data(scsi->request, &blktrc, sizeof(blktrc));
}
/**
beiscsi_active_session_disp, NULL);
DEVICE_ATTR(beiscsi_free_session_count, S_IRUGO,
beiscsi_free_session_disp, NULL);
- struct device_attribute *beiscsi_attrs[] = {
+ static struct device_attribute *beiscsi_attrs[] = {
&dev_attr_beiscsi_log_enable,
&dev_attr_beiscsi_drvr_ver,
&dev_attr_beiscsi_adapter_family,
goto create_eq_error;
}
- mem->dma = paddr;
mem->va = eq_vaddress;
ret = be_fill_queue(eq, phba->params.num_eq_entries,
sizeof(struct be_eq_entry), eq_vaddress);
goto create_eq_error;
}
+ mem->dma = paddr;
ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
BEISCSI_EQ_DELAY_DEF);
if (ret) {
goto create_cq_error;
}
- mem->dma = paddr;
ret = be_fill_queue(cq, phba->params.num_cq_entries,
sizeof(struct sol_cqe), cq_vaddress);
if (ret) {
goto create_cq_error;
}
+ mem->dma = paddr;
ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false,
false, 0);
if (ret) {
#include "bnx2fc.h"
+#include <linux/ethtool.h>
+
static struct list_head adapter_list;
static struct list_head if_list;
static u32 adapter_count;
{
struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr);
- if (interface->enabled == true) {
+ if (interface->enabled) {
if (!ctlr->lp) {
pr_err(PFX "__bnx2fc_disable: lport not found\n");
return -ENODEV;
struct cnic_fc_npiv_tbl *npiv_tbl;
struct fc_lport *lport;
- if (interface->enabled == false) {
+ if (!interface->enabled) {
if (!ctlr->lp) {
pr_err(PFX "__bnx2fc_enable: lport not found\n");
return -ENODEV;
case FCOE_CTLR_UNUSED:
default:
return -ENOTSUPP;
- };
+ }
}
enum bnx2fc_create_link_state {
static int alua_check_sense(struct scsi_device *sdev,
struct scsi_sense_hdr *sense_hdr)
{
+ struct alua_dh_data *h = sdev->handler_data;
+ struct alua_port_group *pg;
+
switch (sense_hdr->sense_key) {
case NOT_READY:
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
/*
* LUN Not Accessible - ALUA state transition
*/
+ rcu_read_lock();
+ pg = rcu_dereference(h->pg);
+ if (pg)
+ pg->state = SCSI_ACCESS_STATE_TRANSITIONING;
+ rcu_read_unlock();
alua_check(sdev, false);
return NEEDS_RETRY;
}
rcu_read_lock();
list_for_each_entry_rcu(h,
&tmp_pg->dh_list, node) {
- /* h->sdev should always be valid */
- BUG_ON(!h->sdev);
+ if (!h->sdev)
+ continue;
h->sdev->access_state = desc[0];
}
rcu_read_unlock();
pg->expiry = 0;
rcu_read_lock();
list_for_each_entry_rcu(h, &pg->dh_list, node) {
- BUG_ON(!h->sdev);
+ if (!h->sdev)
+ continue;
h->sdev->access_state =
(pg->state & SCSI_ACCESS_STATE_MASK);
if (pg->pref)
case SCSI_ACCESS_STATE_LBA:
return BLK_STS_OK;
case SCSI_ACCESS_STATE_TRANSITIONING:
- return BLK_STS_RESOURCE;
+ return BLK_STS_AGAIN;
default:
req->rq_flags |= RQF_QUIET;
return BLK_STS_IOERR;
spin_lock(&h->pg_lock);
pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock));
rcu_assign_pointer(h->pg, NULL);
- h->sdev = NULL;
spin_unlock(&h->pg_lock);
if (pg) {
spin_lock_irq(&pg->lock);
kref_put(&pg->kref, release_port_group);
}
sdev->handler_data = NULL;
+ synchronize_rcu();
kfree(h);
}
}
}
- if (scmd) {
+ if (scmd && hisi_hba->shost->nr_hw_queues) {
unsigned int dq_index;
u32 blk_tag;
blk_tag = blk_mq_unique_tag(scmd->request);
dq_index = blk_mq_unique_tag_to_hwq(blk_tag);
*dq_pointer = dq = &hisi_hba->dq[dq_index];
+ } else if (hisi_hba->shost->nr_hw_queues) {
+ struct Scsi_Host *shost = hisi_hba->shost;
+ struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+ int queue = qmap->mq_map[raw_smp_processor_id()];
+
+ *dq_pointer = dq = &hisi_hba->dq[queue];
} else {
*dq_pointer = dq = sas_dev->dq;
}
dev = hisi_hba->dev;
if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) {
- /*
- * For IOs from upper layer, it may already disable preempt
- * in the IO path, if disable preempt again in down(),
- * function schedule() will report schedule_bug(), so check
- * preemptible() before goto down().
- */
- if (!preemptible())
+ if (!gfpflags_allow_blocking(gfp_flags))
return -EINVAL;
down(&hisi_hba->sem);
err_out_register_ha:
scsi_remove_host(shost);
err_out_ha:
- hisi_sas_debugfs_exit(hisi_hba);
hisi_sas_free(hisi_hba);
scsi_host_put(shost);
return rc;
}
EXPORT_SYMBOL_GPL(hisi_sas_probe);
- struct dentry *hisi_sas_debugfs_dir;
-
- static void hisi_sas_debugfs_snapshot_cq_reg(struct hisi_hba *hisi_hba)
- {
- int queue_entry_size = hisi_hba->hw->complete_hdr_size;
- int dump_index = hisi_hba->debugfs_dump_index;
- int i;
-
- for (i = 0; i < hisi_hba->queue_count; i++)
- memcpy(hisi_hba->debugfs_cq[dump_index][i].complete_hdr,
- hisi_hba->complete_hdr[i],
- HISI_SAS_QUEUE_SLOTS * queue_entry_size);
- }
-
- static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba)
- {
- int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr);
- int dump_index = hisi_hba->debugfs_dump_index;
- int i;
-
- for (i = 0; i < hisi_hba->queue_count; i++) {
- struct hisi_sas_cmd_hdr *debugfs_cmd_hdr, *cmd_hdr;
- int j;
-
- debugfs_cmd_hdr = hisi_hba->debugfs_dq[dump_index][i].hdr;
- cmd_hdr = hisi_hba->cmd_hdr[i];
-
- for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++)
- memcpy(&debugfs_cmd_hdr[j], &cmd_hdr[j],
- queue_entry_size);
- }
- }
-
- static void hisi_sas_debugfs_snapshot_port_reg(struct hisi_hba *hisi_hba)
- {
- int dump_index = hisi_hba->debugfs_dump_index;
- const struct hisi_sas_debugfs_reg *port =
- hisi_hba->hw->debugfs_reg_port;
- int i, phy_cnt;
- u32 offset;
- u32 *databuf;
-
- for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) {
- databuf = hisi_hba->debugfs_port_reg[dump_index][phy_cnt].data;
- for (i = 0; i < port->count; i++, databuf++) {
- offset = port->base_off + 4 * i;
- *databuf = port->read_port_reg(hisi_hba, phy_cnt,
- offset);
- }
- }
- }
-
- static void hisi_sas_debugfs_snapshot_global_reg(struct hisi_hba *hisi_hba)
- {
- int dump_index = hisi_hba->debugfs_dump_index;
- u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_GLOBAL].data;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const struct hisi_sas_debugfs_reg *global =
- hw->debugfs_reg_array[DEBUGFS_GLOBAL];
- int i;
-
- for (i = 0; i < global->count; i++, databuf++)
- *databuf = global->read_global_reg(hisi_hba, 4 * i);
- }
-
- static void hisi_sas_debugfs_snapshot_axi_reg(struct hisi_hba *hisi_hba)
- {
- int dump_index = hisi_hba->debugfs_dump_index;
- u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_AXI].data;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const struct hisi_sas_debugfs_reg *axi =
- hw->debugfs_reg_array[DEBUGFS_AXI];
- int i;
-
- for (i = 0; i < axi->count; i++, databuf++)
- *databuf = axi->read_global_reg(hisi_hba,
- 4 * i + axi->base_off);
- }
-
- static void hisi_sas_debugfs_snapshot_ras_reg(struct hisi_hba *hisi_hba)
- {
- int dump_index = hisi_hba->debugfs_dump_index;
- u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_RAS].data;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const struct hisi_sas_debugfs_reg *ras =
- hw->debugfs_reg_array[DEBUGFS_RAS];
- int i;
-
- for (i = 0; i < ras->count; i++, databuf++)
- *databuf = ras->read_global_reg(hisi_hba,
- 4 * i + ras->base_off);
- }
-
- static void hisi_sas_debugfs_snapshot_itct_reg(struct hisi_hba *hisi_hba)
- {
- int dump_index = hisi_hba->debugfs_dump_index;
- void *cachebuf = hisi_hba->debugfs_itct_cache[dump_index].cache;
- void *databuf = hisi_hba->debugfs_itct[dump_index].itct;
- struct hisi_sas_itct *itct;
- int i;
-
- hisi_hba->hw->read_iost_itct_cache(hisi_hba, HISI_SAS_ITCT_CACHE,
- cachebuf);
-
- itct = hisi_hba->itct;
-
- for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
- memcpy(databuf, itct, sizeof(struct hisi_sas_itct));
- databuf += sizeof(struct hisi_sas_itct);
- }
- }
-
- static void hisi_sas_debugfs_snapshot_iost_reg(struct hisi_hba *hisi_hba)
- {
- int dump_index = hisi_hba->debugfs_dump_index;
- int max_command_entries = HISI_SAS_MAX_COMMANDS;
- void *cachebuf = hisi_hba->debugfs_iost_cache[dump_index].cache;
- void *databuf = hisi_hba->debugfs_iost[dump_index].iost;
- struct hisi_sas_iost *iost;
- int i;
-
- hisi_hba->hw->read_iost_itct_cache(hisi_hba, HISI_SAS_IOST_CACHE,
- cachebuf);
-
- iost = hisi_hba->iost;
-
- for (i = 0; i < max_command_entries; i++, iost++) {
- memcpy(databuf, iost, sizeof(struct hisi_sas_iost));
- databuf += sizeof(struct hisi_sas_iost);
- }
- }
-
- static const char *
- hisi_sas_debugfs_to_reg_name(int off, int base_off,
- const struct hisi_sas_debugfs_reg_lu *lu)
- {
- for (; lu->name; lu++) {
- if (off == lu->off - base_off)
- return lu->name;
- }
-
- return NULL;
- }
-
- static void hisi_sas_debugfs_print_reg(u32 *regs_val, const void *ptr,
- struct seq_file *s)
- {
- const struct hisi_sas_debugfs_reg *reg = ptr;
- int i;
-
- for (i = 0; i < reg->count; i++) {
- int off = i * 4;
- const char *name;
-
- name = hisi_sas_debugfs_to_reg_name(off, reg->base_off,
- reg->lu);
-
- if (name)
- seq_printf(s, "0x%08x 0x%08x %s\n", off,
- regs_val[i], name);
- else
- seq_printf(s, "0x%08x 0x%08x\n", off,
- regs_val[i]);
- }
- }
-
- static int hisi_sas_debugfs_global_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_regs *global = s->private;
- struct hisi_hba *hisi_hba = global->hisi_hba;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const void *reg_global = hw->debugfs_reg_array[DEBUGFS_GLOBAL];
-
- hisi_sas_debugfs_print_reg(global->data,
- reg_global, s);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_global_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_global_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_global_fops = {
- .open = hisi_sas_debugfs_global_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_axi_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_regs *axi = s->private;
- struct hisi_hba *hisi_hba = axi->hisi_hba;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const void *reg_axi = hw->debugfs_reg_array[DEBUGFS_AXI];
-
- hisi_sas_debugfs_print_reg(axi->data,
- reg_axi, s);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_axi_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_axi_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_axi_fops = {
- .open = hisi_sas_debugfs_axi_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_ras_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_regs *ras = s->private;
- struct hisi_hba *hisi_hba = ras->hisi_hba;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const void *reg_ras = hw->debugfs_reg_array[DEBUGFS_RAS];
-
- hisi_sas_debugfs_print_reg(ras->data,
- reg_ras, s);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_ras_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_ras_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_ras_fops = {
- .open = hisi_sas_debugfs_ras_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_port_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_port *port = s->private;
- struct hisi_sas_phy *phy = port->phy;
- struct hisi_hba *hisi_hba = phy->hisi_hba;
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- const struct hisi_sas_debugfs_reg *reg_port = hw->debugfs_reg_port;
-
- hisi_sas_debugfs_print_reg(port->data, reg_port, s);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_port_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_port_show, inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_port_fops = {
- .open = hisi_sas_debugfs_port_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static void hisi_sas_show_row_64(struct seq_file *s, int index,
- int sz, __le64 *ptr)
- {
- int i;
-
- /* completion header size not fixed per HW version */
- seq_printf(s, "index %04d:\n\t", index);
- for (i = 1; i <= sz / 8; i++, ptr++) {
- seq_printf(s, " 0x%016llx", le64_to_cpu(*ptr));
- if (!(i % 2))
- seq_puts(s, "\n\t");
- }
-
- seq_puts(s, "\n");
- }
-
- static void hisi_sas_show_row_32(struct seq_file *s, int index,
- int sz, __le32 *ptr)
- {
- int i;
-
- /* completion header size not fixed per HW version */
- seq_printf(s, "index %04d:\n\t", index);
- for (i = 1; i <= sz / 4; i++, ptr++) {
- seq_printf(s, " 0x%08x", le32_to_cpu(*ptr));
- if (!(i % 4))
- seq_puts(s, "\n\t");
- }
- seq_puts(s, "\n");
- }
-
- static void hisi_sas_cq_show_slot(struct seq_file *s, int slot,
- struct hisi_sas_debugfs_cq *debugfs_cq)
- {
- struct hisi_sas_cq *cq = debugfs_cq->cq;
- struct hisi_hba *hisi_hba = cq->hisi_hba;
- __le32 *complete_hdr = debugfs_cq->complete_hdr +
- (hisi_hba->hw->complete_hdr_size * slot);
-
- hisi_sas_show_row_32(s, slot,
- hisi_hba->hw->complete_hdr_size,
- complete_hdr);
- }
-
- static int hisi_sas_debugfs_cq_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_cq *debugfs_cq = s->private;
- int slot;
-
- for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) {
- hisi_sas_cq_show_slot(s, slot, debugfs_cq);
- }
- return 0;
- }
-
- static int hisi_sas_debugfs_cq_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_cq_show, inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_cq_fops = {
- .open = hisi_sas_debugfs_cq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static void hisi_sas_dq_show_slot(struct seq_file *s, int slot, void *dq_ptr)
- {
- struct hisi_sas_debugfs_dq *debugfs_dq = dq_ptr;
- void *cmd_queue = debugfs_dq->hdr;
- __le32 *cmd_hdr = cmd_queue +
- sizeof(struct hisi_sas_cmd_hdr) * slot;
-
- hisi_sas_show_row_32(s, slot, sizeof(struct hisi_sas_cmd_hdr), cmd_hdr);
- }
-
- static int hisi_sas_debugfs_dq_show(struct seq_file *s, void *p)
- {
- int slot;
-
- for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++) {
- hisi_sas_dq_show_slot(s, slot, s->private);
- }
- return 0;
- }
-
- static int hisi_sas_debugfs_dq_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_dq_show, inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_dq_fops = {
- .open = hisi_sas_debugfs_dq_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_iost_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_iost *debugfs_iost = s->private;
- struct hisi_sas_iost *iost = debugfs_iost->iost;
- int i, max_command_entries = HISI_SAS_MAX_COMMANDS;
-
- for (i = 0; i < max_command_entries; i++, iost++) {
- __le64 *data = &iost->qw0;
-
- hisi_sas_show_row_64(s, i, sizeof(*iost), data);
- }
-
- return 0;
- }
-
- static int hisi_sas_debugfs_iost_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_iost_show, inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_iost_fops = {
- .open = hisi_sas_debugfs_iost_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_iost_cache_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_debugfs_iost_cache *debugfs_iost_cache = s->private;
- struct hisi_sas_iost_itct_cache *iost_cache = debugfs_iost_cache->cache;
- u32 cache_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ * 4;
- int i, tab_idx;
- __le64 *iost;
-
- for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, iost_cache++) {
- /*
- * Data struct of IOST cache:
- * Data[1]: BIT0~15: Table index
- * Bit16: Valid mask
- * Data[2]~[9]: IOST table
- */
- tab_idx = (iost_cache->data[1] & 0xffff);
- iost = (__le64 *)iost_cache;
-
- hisi_sas_show_row_64(s, tab_idx, cache_size, iost);
- }
-
- return 0;
- }
-
- static int hisi_sas_debugfs_iost_cache_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_iost_cache_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_iost_cache_fops = {
- .open = hisi_sas_debugfs_iost_cache_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_itct_show(struct seq_file *s, void *p)
- {
- int i;
- struct hisi_sas_debugfs_itct *debugfs_itct = s->private;
- struct hisi_sas_itct *itct = debugfs_itct->itct;
-
- for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
- __le64 *data = &itct->qw0;
-
- hisi_sas_show_row_64(s, i, sizeof(*itct), data);
- }
-
- return 0;
- }
-
- static int hisi_sas_debugfs_itct_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_itct_show, inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_itct_fops = {
- .open = hisi_sas_debugfs_itct_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static int hisi_sas_debugfs_itct_cache_show(struct seq_file *s, void *p)
+ int hisi_sas_remove(struct platform_device *pdev)
{
- struct hisi_sas_debugfs_itct_cache *debugfs_itct_cache = s->private;
- struct hisi_sas_iost_itct_cache *itct_cache = debugfs_itct_cache->cache;
- u32 cache_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ * 4;
- int i, tab_idx;
- __le64 *itct;
+ struct sas_ha_struct *sha = platform_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+ struct Scsi_Host *shost = sha->core.shost;
- for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, itct_cache++) {
- /*
- * Data struct of ITCT cache:
- * Data[1]: BIT0~15: Table index
- * Bit16: Valid mask
- * Data[2]~[9]: ITCT table
- */
- tab_idx = itct_cache->data[1] & 0xffff;
- itct = (__le64 *)itct_cache;
+ if (timer_pending(&hisi_hba->timer))
+ del_timer(&hisi_hba->timer);
- hisi_sas_show_row_64(s, tab_idx, cache_size, itct);
- }
+ sas_unregister_ha(sha);
+ sas_remove_host(sha->core.shost);
+ hisi_sas_free(hisi_hba);
+ scsi_host_put(shost);
return 0;
}
+ EXPORT_SYMBOL_GPL(hisi_sas_remove);
- static int hisi_sas_debugfs_itct_cache_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_itct_cache_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_itct_cache_fops = {
- .open = hisi_sas_debugfs_itct_cache_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba)
- {
- u64 *debugfs_timestamp;
- int dump_index = hisi_hba->debugfs_dump_index;
- struct dentry *dump_dentry;
- struct dentry *dentry;
- char name[256];
- int p;
- int c;
- int d;
-
- snprintf(name, 256, "%d", dump_index);
-
- dump_dentry = debugfs_create_dir(name, hisi_hba->debugfs_dump_dentry);
-
- debugfs_timestamp = &hisi_hba->debugfs_timestamp[dump_index];
-
- debugfs_create_u64("timestamp", 0400, dump_dentry,
- debugfs_timestamp);
-
- debugfs_create_file("global", 0400, dump_dentry,
- &hisi_hba->debugfs_regs[dump_index][DEBUGFS_GLOBAL],
- &hisi_sas_debugfs_global_fops);
-
- /* Create port dir and files */
- dentry = debugfs_create_dir("port", dump_dentry);
- for (p = 0; p < hisi_hba->n_phy; p++) {
- snprintf(name, 256, "%d", p);
-
- debugfs_create_file(name, 0400, dentry,
- &hisi_hba->debugfs_port_reg[dump_index][p],
- &hisi_sas_debugfs_port_fops);
- }
-
- /* Create CQ dir and files */
- dentry = debugfs_create_dir("cq", dump_dentry);
- for (c = 0; c < hisi_hba->queue_count; c++) {
- snprintf(name, 256, "%d", c);
-
- debugfs_create_file(name, 0400, dentry,
- &hisi_hba->debugfs_cq[dump_index][c],
- &hisi_sas_debugfs_cq_fops);
- }
-
- /* Create DQ dir and files */
- dentry = debugfs_create_dir("dq", dump_dentry);
- for (d = 0; d < hisi_hba->queue_count; d++) {
- snprintf(name, 256, "%d", d);
-
- debugfs_create_file(name, 0400, dentry,
- &hisi_hba->debugfs_dq[dump_index][d],
- &hisi_sas_debugfs_dq_fops);
- }
-
- debugfs_create_file("iost", 0400, dump_dentry,
- &hisi_hba->debugfs_iost[dump_index],
- &hisi_sas_debugfs_iost_fops);
-
- debugfs_create_file("iost_cache", 0400, dump_dentry,
- &hisi_hba->debugfs_iost_cache[dump_index],
- &hisi_sas_debugfs_iost_cache_fops);
-
- debugfs_create_file("itct", 0400, dump_dentry,
- &hisi_hba->debugfs_itct[dump_index],
- &hisi_sas_debugfs_itct_fops);
-
- debugfs_create_file("itct_cache", 0400, dump_dentry,
- &hisi_hba->debugfs_itct_cache[dump_index],
- &hisi_sas_debugfs_itct_cache_fops);
-
- debugfs_create_file("axi", 0400, dump_dentry,
- &hisi_hba->debugfs_regs[dump_index][DEBUGFS_AXI],
- &hisi_sas_debugfs_axi_fops);
-
- debugfs_create_file("ras", 0400, dump_dentry,
- &hisi_hba->debugfs_regs[dump_index][DEBUGFS_RAS],
- &hisi_sas_debugfs_ras_fops);
-
- return;
- }
-
- static void hisi_sas_debugfs_snapshot_regs(struct hisi_hba *hisi_hba)
- {
- hisi_hba->hw->snapshot_prepare(hisi_hba);
-
- hisi_sas_debugfs_snapshot_global_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_port_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_axi_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_ras_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_cq_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_dq_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_itct_reg(hisi_hba);
- hisi_sas_debugfs_snapshot_iost_reg(hisi_hba);
-
- hisi_sas_debugfs_create_files(hisi_hba);
-
- hisi_hba->hw->snapshot_restore(hisi_hba);
- }
+ bool hisi_sas_debugfs_enable;
+ EXPORT_SYMBOL_GPL(hisi_sas_debugfs_enable);
+ module_param_named(debugfs_enable, hisi_sas_debugfs_enable, bool, 0444);
+ MODULE_PARM_DESC(hisi_sas_debugfs_enable, "Enable driver debugfs (default disabled)");
- static ssize_t hisi_sas_debugfs_trigger_dump_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct hisi_hba *hisi_hba = file->f_inode->i_private;
- char buf[8];
+ u32 hisi_sas_debugfs_dump_count = 1;
+ EXPORT_SYMBOL_GPL(hisi_sas_debugfs_dump_count);
+ module_param_named(debugfs_dump_count, hisi_sas_debugfs_dump_count, uint, 0444);
+ MODULE_PARM_DESC(hisi_sas_debugfs_dump_count, "Number of debugfs dumps to allow");
- if (hisi_hba->debugfs_dump_index >= hisi_sas_debugfs_dump_count)
- return -EFAULT;
-
- if (count > 8)
- return -EFAULT;
-
- if (copy_from_user(buf, user_buf, count))
- return -EFAULT;
-
- if (buf[0] != '1')
- return -EFAULT;
-
- queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
-
- return count;
- }
-
- static const struct file_operations hisi_sas_debugfs_trigger_dump_fops = {
- .write = &hisi_sas_debugfs_trigger_dump_write,
- .owner = THIS_MODULE,
- };
-
- enum {
- HISI_SAS_BIST_LOOPBACK_MODE_DIGITAL = 0,
- HISI_SAS_BIST_LOOPBACK_MODE_SERDES,
- HISI_SAS_BIST_LOOPBACK_MODE_REMOTE,
- };
-
- static const struct {
- int value;
- char *name;
- } hisi_sas_debugfs_loop_linkrate[] = {
- { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" },
- { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" },
- { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" },
- { SAS_LINK_RATE_12_0_GBPS, "12.0 Gbit" },
- };
-
- static int hisi_sas_debugfs_bist_linkrate_show(struct seq_file *s, void *p)
- {
- struct hisi_hba *hisi_hba = s->private;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hisi_sas_debugfs_loop_linkrate); i++) {
- int match = (hisi_hba->debugfs_bist_linkrate ==
- hisi_sas_debugfs_loop_linkrate[i].value);
-
- seq_printf(s, "%s%s%s ", match ? "[" : "",
- hisi_sas_debugfs_loop_linkrate[i].name,
- match ? "]" : "");
- }
- seq_puts(s, "\n");
-
- return 0;
- }
-
- static ssize_t hisi_sas_debugfs_bist_linkrate_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct seq_file *m = filp->private_data;
- struct hisi_hba *hisi_hba = m->private;
- char kbuf[16] = {}, *pkbuf;
- bool found = false;
- int i;
-
- if (hisi_hba->debugfs_bist_enable)
- return -EPERM;
-
- if (count >= sizeof(kbuf))
- return -EOVERFLOW;
-
- if (copy_from_user(kbuf, buf, count))
- return -EINVAL;
-
- pkbuf = strstrip(kbuf);
-
- for (i = 0; i < ARRAY_SIZE(hisi_sas_debugfs_loop_linkrate); i++) {
- if (!strncmp(hisi_sas_debugfs_loop_linkrate[i].name,
- pkbuf, 16)) {
- hisi_hba->debugfs_bist_linkrate =
- hisi_sas_debugfs_loop_linkrate[i].value;
- found = true;
- break;
- }
- }
-
- if (!found)
- return -EINVAL;
-
- return count;
- }
-
- static int hisi_sas_debugfs_bist_linkrate_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_bist_linkrate_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_bist_linkrate_ops = {
- .open = hisi_sas_debugfs_bist_linkrate_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_bist_linkrate_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static const struct {
- int value;
- char *name;
- } hisi_sas_debugfs_loop_code_mode[] = {
- { HISI_SAS_BIST_CODE_MODE_PRBS7, "PRBS7" },
- { HISI_SAS_BIST_CODE_MODE_PRBS23, "PRBS23" },
- { HISI_SAS_BIST_CODE_MODE_PRBS31, "PRBS31" },
- { HISI_SAS_BIST_CODE_MODE_JTPAT, "JTPAT" },
- { HISI_SAS_BIST_CODE_MODE_CJTPAT, "CJTPAT" },
- { HISI_SAS_BIST_CODE_MODE_SCRAMBED_0, "SCRAMBED_0" },
- { HISI_SAS_BIST_CODE_MODE_TRAIN, "TRAIN" },
- { HISI_SAS_BIST_CODE_MODE_TRAIN_DONE, "TRAIN_DONE" },
- { HISI_SAS_BIST_CODE_MODE_HFTP, "HFTP" },
- { HISI_SAS_BIST_CODE_MODE_MFTP, "MFTP" },
- { HISI_SAS_BIST_CODE_MODE_LFTP, "LFTP" },
- { HISI_SAS_BIST_CODE_MODE_FIXED_DATA, "FIXED_DATA" },
- };
-
- static int hisi_sas_debugfs_bist_code_mode_show(struct seq_file *s, void *p)
- {
- struct hisi_hba *hisi_hba = s->private;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hisi_sas_debugfs_loop_code_mode); i++) {
- int match = (hisi_hba->debugfs_bist_code_mode ==
- hisi_sas_debugfs_loop_code_mode[i].value);
-
- seq_printf(s, "%s%s%s ", match ? "[" : "",
- hisi_sas_debugfs_loop_code_mode[i].name,
- match ? "]" : "");
- }
- seq_puts(s, "\n");
-
- return 0;
- }
-
- static ssize_t hisi_sas_debugfs_bist_code_mode_write(struct file *filp,
- const char __user *buf,
- size_t count,
- loff_t *ppos)
- {
- struct seq_file *m = filp->private_data;
- struct hisi_hba *hisi_hba = m->private;
- char kbuf[16] = {}, *pkbuf;
- bool found = false;
- int i;
-
- if (hisi_hba->debugfs_bist_enable)
- return -EPERM;
-
- if (count >= sizeof(kbuf))
- return -EINVAL;
-
- if (copy_from_user(kbuf, buf, count))
- return -EOVERFLOW;
-
- pkbuf = strstrip(kbuf);
-
- for (i = 0; i < ARRAY_SIZE(hisi_sas_debugfs_loop_code_mode); i++) {
- if (!strncmp(hisi_sas_debugfs_loop_code_mode[i].name,
- pkbuf, 16)) {
- hisi_hba->debugfs_bist_code_mode =
- hisi_sas_debugfs_loop_code_mode[i].value;
- found = true;
- break;
- }
- }
-
- if (!found)
- return -EINVAL;
-
- return count;
- }
-
- static int hisi_sas_debugfs_bist_code_mode_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_bist_code_mode_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_bist_code_mode_ops = {
- .open = hisi_sas_debugfs_bist_code_mode_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_bist_code_mode_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static ssize_t hisi_sas_debugfs_bist_phy_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct seq_file *m = filp->private_data;
- struct hisi_hba *hisi_hba = m->private;
- unsigned int phy_no;
- int val;
-
- if (hisi_hba->debugfs_bist_enable)
- return -EPERM;
-
- val = kstrtouint_from_user(buf, count, 0, &phy_no);
- if (val)
- return val;
-
- if (phy_no >= hisi_hba->n_phy)
- return -EINVAL;
-
- hisi_hba->debugfs_bist_phy_no = phy_no;
-
- return count;
- }
-
- static int hisi_sas_debugfs_bist_phy_show(struct seq_file *s, void *p)
- {
- struct hisi_hba *hisi_hba = s->private;
-
- seq_printf(s, "%d\n", hisi_hba->debugfs_bist_phy_no);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_bist_phy_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_bist_phy_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_bist_phy_ops = {
- .open = hisi_sas_debugfs_bist_phy_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_bist_phy_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static const struct {
- int value;
- char *name;
- } hisi_sas_debugfs_loop_modes[] = {
- { HISI_SAS_BIST_LOOPBACK_MODE_DIGITAL, "digital" },
- { HISI_SAS_BIST_LOOPBACK_MODE_SERDES, "serdes" },
- { HISI_SAS_BIST_LOOPBACK_MODE_REMOTE, "remote" },
- };
-
- static int hisi_sas_debugfs_bist_mode_show(struct seq_file *s, void *p)
- {
- struct hisi_hba *hisi_hba = s->private;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hisi_sas_debugfs_loop_modes); i++) {
- int match = (hisi_hba->debugfs_bist_mode ==
- hisi_sas_debugfs_loop_modes[i].value);
-
- seq_printf(s, "%s%s%s ", match ? "[" : "",
- hisi_sas_debugfs_loop_modes[i].name,
- match ? "]" : "");
- }
- seq_puts(s, "\n");
-
- return 0;
- }
-
- static ssize_t hisi_sas_debugfs_bist_mode_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct seq_file *m = filp->private_data;
- struct hisi_hba *hisi_hba = m->private;
- char kbuf[16] = {}, *pkbuf;
- bool found = false;
- int i;
-
- if (hisi_hba->debugfs_bist_enable)
- return -EPERM;
-
- if (count >= sizeof(kbuf))
- return -EINVAL;
-
- if (copy_from_user(kbuf, buf, count))
- return -EOVERFLOW;
-
- pkbuf = strstrip(kbuf);
-
- for (i = 0; i < ARRAY_SIZE(hisi_sas_debugfs_loop_modes); i++) {
- if (!strncmp(hisi_sas_debugfs_loop_modes[i].name, pkbuf, 16)) {
- hisi_hba->debugfs_bist_mode =
- hisi_sas_debugfs_loop_modes[i].value;
- found = true;
- break;
- }
- }
-
- if (!found)
- return -EINVAL;
-
- return count;
- }
-
- static int hisi_sas_debugfs_bist_mode_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_bist_mode_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_bist_mode_ops = {
- .open = hisi_sas_debugfs_bist_mode_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_bist_mode_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static ssize_t hisi_sas_debugfs_bist_enable_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct seq_file *m = filp->private_data;
- struct hisi_hba *hisi_hba = m->private;
- unsigned int enable;
- int val;
-
- val = kstrtouint_from_user(buf, count, 0, &enable);
- if (val)
- return val;
-
- if (enable > 1)
- return -EINVAL;
-
- if (enable == hisi_hba->debugfs_bist_enable)
- return count;
-
- if (!hisi_hba->hw->set_bist)
- return -EPERM;
-
- val = hisi_hba->hw->set_bist(hisi_hba, enable);
- if (val < 0)
- return val;
-
- hisi_hba->debugfs_bist_enable = enable;
-
- return count;
- }
-
- static int hisi_sas_debugfs_bist_enable_show(struct seq_file *s, void *p)
- {
- struct hisi_hba *hisi_hba = s->private;
-
- seq_printf(s, "%d\n", hisi_hba->debugfs_bist_enable);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_bist_enable_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_bist_enable_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_bist_enable_ops = {
- .open = hisi_sas_debugfs_bist_enable_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_bist_enable_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static const struct {
- char *name;
- } hisi_sas_debugfs_ffe_name[FFE_CFG_MAX] = {
- { "SAS_1_5_GBPS" },
- { "SAS_3_0_GBPS" },
- { "SAS_6_0_GBPS" },
- { "SAS_12_0_GBPS" },
- { "FFE_RESV" },
- { "SATA_1_5_GBPS" },
- { "SATA_3_0_GBPS" },
- { "SATA_6_0_GBPS" },
- };
-
- static ssize_t hisi_sas_debugfs_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct seq_file *m = filp->private_data;
- u32 *val = m->private;
- int res;
-
- res = kstrtouint_from_user(buf, count, 0, val);
- if (res)
- return res;
-
- return count;
- }
-
- static int hisi_sas_debugfs_show(struct seq_file *s, void *p)
- {
- u32 *val = s->private;
-
- seq_printf(s, "0x%x\n", *val);
-
- return 0;
- }
-
- static int hisi_sas_debugfs_open(struct inode *inode, struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_ops = {
- .open = hisi_sas_debugfs_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- static ssize_t hisi_sas_debugfs_phy_down_cnt_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct seq_file *s = filp->private_data;
- struct hisi_sas_phy *phy = s->private;
- unsigned int set_val;
- int res;
-
- res = kstrtouint_from_user(buf, count, 0, &set_val);
- if (res)
- return res;
-
- if (set_val > 0)
- return -EINVAL;
-
- atomic_set(&phy->down_cnt, 0);
-
- return count;
- }
-
- static int hisi_sas_debugfs_phy_down_cnt_show(struct seq_file *s, void *p)
- {
- struct hisi_sas_phy *phy = s->private;
-
- seq_printf(s, "%d\n", atomic_read(&phy->down_cnt));
-
- return 0;
- }
-
- static int hisi_sas_debugfs_phy_down_cnt_open(struct inode *inode,
- struct file *filp)
- {
- return single_open(filp, hisi_sas_debugfs_phy_down_cnt_show,
- inode->i_private);
- }
-
- static const struct file_operations hisi_sas_debugfs_phy_down_cnt_ops = {
- .open = hisi_sas_debugfs_phy_down_cnt_open,
- .read = seq_read,
- .write = hisi_sas_debugfs_phy_down_cnt_write,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
- };
-
- void hisi_sas_debugfs_work_handler(struct work_struct *work)
- {
- struct hisi_hba *hisi_hba =
- container_of(work, struct hisi_hba, debugfs_work);
- int debugfs_dump_index = hisi_hba->debugfs_dump_index;
- struct device *dev = hisi_hba->dev;
- u64 timestamp = local_clock();
-
- if (debugfs_dump_index >= hisi_sas_debugfs_dump_count) {
- dev_warn(dev, "dump count exceeded!\n");
- return;
- }
-
- do_div(timestamp, NSEC_PER_MSEC);
- hisi_hba->debugfs_timestamp[debugfs_dump_index] = timestamp;
-
- hisi_sas_debugfs_snapshot_regs(hisi_hba);
- hisi_hba->debugfs_dump_index++;
- }
- EXPORT_SYMBOL_GPL(hisi_sas_debugfs_work_handler);
-
- static void hisi_sas_debugfs_release(struct hisi_hba *hisi_hba, int dump_index)
- {
- struct device *dev = hisi_hba->dev;
- int i;
-
- devm_kfree(dev, hisi_hba->debugfs_iost_cache[dump_index].cache);
- devm_kfree(dev, hisi_hba->debugfs_itct_cache[dump_index].cache);
- devm_kfree(dev, hisi_hba->debugfs_iost[dump_index].iost);
- devm_kfree(dev, hisi_hba->debugfs_itct[dump_index].itct);
-
- for (i = 0; i < hisi_hba->queue_count; i++)
- devm_kfree(dev, hisi_hba->debugfs_dq[dump_index][i].hdr);
-
- for (i = 0; i < hisi_hba->queue_count; i++)
- devm_kfree(dev,
- hisi_hba->debugfs_cq[dump_index][i].complete_hdr);
-
- for (i = 0; i < DEBUGFS_REGS_NUM; i++)
- devm_kfree(dev, hisi_hba->debugfs_regs[dump_index][i].data);
-
- for (i = 0; i < hisi_hba->n_phy; i++)
- devm_kfree(dev, hisi_hba->debugfs_port_reg[dump_index][i].data);
- }
-
- static int hisi_sas_debugfs_alloc(struct hisi_hba *hisi_hba, int dump_index)
- {
- const struct hisi_sas_hw *hw = hisi_hba->hw;
- struct device *dev = hisi_hba->dev;
- int p, c, d, r, i;
- size_t sz;
-
- for (r = 0; r < DEBUGFS_REGS_NUM; r++) {
- struct hisi_sas_debugfs_regs *regs =
- &hisi_hba->debugfs_regs[dump_index][r];
-
- sz = hw->debugfs_reg_array[r]->count * 4;
- regs->data = devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!regs->data)
- goto fail;
- regs->hisi_hba = hisi_hba;
- }
-
- sz = hw->debugfs_reg_port->count * 4;
- for (p = 0; p < hisi_hba->n_phy; p++) {
- struct hisi_sas_debugfs_port *port =
- &hisi_hba->debugfs_port_reg[dump_index][p];
-
- port->data = devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!port->data)
- goto fail;
- port->phy = &hisi_hba->phy[p];
- }
-
- sz = hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
- for (c = 0; c < hisi_hba->queue_count; c++) {
- struct hisi_sas_debugfs_cq *cq =
- &hisi_hba->debugfs_cq[dump_index][c];
-
- cq->complete_hdr = devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!cq->complete_hdr)
- goto fail;
- cq->cq = &hisi_hba->cq[c];
- }
-
- sz = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
- for (d = 0; d < hisi_hba->queue_count; d++) {
- struct hisi_sas_debugfs_dq *dq =
- &hisi_hba->debugfs_dq[dump_index][d];
-
- dq->hdr = devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!dq->hdr)
- goto fail;
- dq->dq = &hisi_hba->dq[d];
- }
-
- sz = HISI_SAS_MAX_COMMANDS * sizeof(struct hisi_sas_iost);
-
- hisi_hba->debugfs_iost[dump_index].iost =
- devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!hisi_hba->debugfs_iost[dump_index].iost)
- goto fail;
-
- sz = HISI_SAS_IOST_ITCT_CACHE_NUM *
- sizeof(struct hisi_sas_iost_itct_cache);
-
- hisi_hba->debugfs_iost_cache[dump_index].cache =
- devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!hisi_hba->debugfs_iost_cache[dump_index].cache)
- goto fail;
-
- sz = HISI_SAS_IOST_ITCT_CACHE_NUM *
- sizeof(struct hisi_sas_iost_itct_cache);
-
- hisi_hba->debugfs_itct_cache[dump_index].cache =
- devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!hisi_hba->debugfs_itct_cache[dump_index].cache)
- goto fail;
-
- /* New memory allocation must be locate before itct */
- sz = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
-
- hisi_hba->debugfs_itct[dump_index].itct =
- devm_kmalloc(dev, sz, GFP_KERNEL);
- if (!hisi_hba->debugfs_itct[dump_index].itct)
- goto fail;
-
- return 0;
- fail:
- for (i = 0; i < hisi_sas_debugfs_dump_count; i++)
- hisi_sas_debugfs_release(hisi_hba, i);
- return -ENOMEM;
- }
-
- static void hisi_sas_debugfs_phy_down_cnt_init(struct hisi_hba *hisi_hba)
- {
- struct dentry *dir = debugfs_create_dir("phy_down_cnt",
- hisi_hba->debugfs_dir);
- char name[16];
- int phy_no;
-
- for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
- snprintf(name, 16, "%d", phy_no);
- debugfs_create_file(name, 0600, dir,
- &hisi_hba->phy[phy_no],
- &hisi_sas_debugfs_phy_down_cnt_ops);
- }
- }
-
- static void hisi_sas_debugfs_bist_init(struct hisi_hba *hisi_hba)
- {
- struct dentry *ports_dentry;
- int phy_no;
-
- hisi_hba->debugfs_bist_dentry =
- debugfs_create_dir("bist", hisi_hba->debugfs_dir);
- debugfs_create_file("link_rate", 0600,
- hisi_hba->debugfs_bist_dentry, hisi_hba,
- &hisi_sas_debugfs_bist_linkrate_ops);
-
- debugfs_create_file("code_mode", 0600,
- hisi_hba->debugfs_bist_dentry, hisi_hba,
- &hisi_sas_debugfs_bist_code_mode_ops);
-
- debugfs_create_file("fixed_code", 0600,
- hisi_hba->debugfs_bist_dentry,
- &hisi_hba->debugfs_bist_fixed_code[0],
- &hisi_sas_debugfs_ops);
-
- debugfs_create_file("fixed_code_1", 0600,
- hisi_hba->debugfs_bist_dentry,
- &hisi_hba->debugfs_bist_fixed_code[1],
- &hisi_sas_debugfs_ops);
-
- debugfs_create_file("phy_id", 0600, hisi_hba->debugfs_bist_dentry,
- hisi_hba, &hisi_sas_debugfs_bist_phy_ops);
-
- debugfs_create_u32("cnt", 0600, hisi_hba->debugfs_bist_dentry,
- &hisi_hba->debugfs_bist_cnt);
-
- debugfs_create_file("loopback_mode", 0600,
- hisi_hba->debugfs_bist_dentry,
- hisi_hba, &hisi_sas_debugfs_bist_mode_ops);
-
- debugfs_create_file("enable", 0600, hisi_hba->debugfs_bist_dentry,
- hisi_hba, &hisi_sas_debugfs_bist_enable_ops);
-
- ports_dentry = debugfs_create_dir("port", hisi_hba->debugfs_bist_dentry);
-
- for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
- struct dentry *port_dentry;
- struct dentry *ffe_dentry;
- char name[256];
- int i;
-
- snprintf(name, 256, "%d", phy_no);
- port_dentry = debugfs_create_dir(name, ports_dentry);
- ffe_dentry = debugfs_create_dir("ffe", port_dentry);
- for (i = 0; i < FFE_CFG_MAX; i++) {
- if (i == FFE_RESV)
- continue;
- debugfs_create_file(hisi_sas_debugfs_ffe_name[i].name,
- 0600, ffe_dentry,
- &hisi_hba->debugfs_bist_ffe[phy_no][i],
- &hisi_sas_debugfs_ops);
- }
- }
-
- hisi_hba->debugfs_bist_linkrate = SAS_LINK_RATE_1_5_GBPS;
- }
-
- void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba)
- {
- struct device *dev = hisi_hba->dev;
- int i;
-
- hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev),
- hisi_sas_debugfs_dir);
- debugfs_create_file("trigger_dump", 0200,
- hisi_hba->debugfs_dir,
- hisi_hba,
- &hisi_sas_debugfs_trigger_dump_fops);
-
- /* create bist structures */
- hisi_sas_debugfs_bist_init(hisi_hba);
-
- hisi_hba->debugfs_dump_dentry =
- debugfs_create_dir("dump", hisi_hba->debugfs_dir);
-
- hisi_sas_debugfs_phy_down_cnt_init(hisi_hba);
-
- for (i = 0; i < hisi_sas_debugfs_dump_count; i++) {
- if (hisi_sas_debugfs_alloc(hisi_hba, i)) {
- debugfs_remove_recursive(hisi_hba->debugfs_dir);
- dev_dbg(dev, "failed to init debugfs!\n");
- break;
- }
- }
- }
- EXPORT_SYMBOL_GPL(hisi_sas_debugfs_init);
-
- void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba)
- {
- debugfs_remove_recursive(hisi_hba->debugfs_dir);
- }
- EXPORT_SYMBOL_GPL(hisi_sas_debugfs_exit);
-
- int hisi_sas_remove(struct platform_device *pdev)
- {
- struct sas_ha_struct *sha = platform_get_drvdata(pdev);
- struct hisi_hba *hisi_hba = sha->lldd_ha;
- struct Scsi_Host *shost = sha->core.shost;
-
- if (timer_pending(&hisi_hba->timer))
- del_timer(&hisi_hba->timer);
-
- sas_unregister_ha(sha);
- sas_remove_host(sha->core.shost);
-
- hisi_sas_free(hisi_hba);
- scsi_host_put(shost);
- return 0;
- }
- EXPORT_SYMBOL_GPL(hisi_sas_remove);
-
- bool hisi_sas_debugfs_enable;
- EXPORT_SYMBOL_GPL(hisi_sas_debugfs_enable);
- module_param_named(debugfs_enable, hisi_sas_debugfs_enable, bool, 0444);
- MODULE_PARM_DESC(hisi_sas_debugfs_enable, "Enable driver debugfs (default disabled)");
-
- u32 hisi_sas_debugfs_dump_count = 1;
- EXPORT_SYMBOL_GPL(hisi_sas_debugfs_dump_count);
- module_param_named(debugfs_dump_count, hisi_sas_debugfs_dump_count, uint, 0444);
- MODULE_PARM_DESC(hisi_sas_debugfs_dump_count, "Number of debugfs dumps to allow");
+ struct dentry *hisi_sas_debugfs_dir;
+ EXPORT_SYMBOL_GPL(hisi_sas_debugfs_dir);
static __init int hisi_sas_init(void)
{
MODULE_PARM_DESC(auto_affine_msi_experimental, "Enable auto-affinity of MSI IRQs as experimental:\n"
"default is off");
+ static void debugfs_work_handler_v3_hw(struct work_struct *work);
+
static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
{
void __iomem *regs = hisi_hba->regs + off;
DRV_NAME " phy", hisi_hba);
if (rc) {
dev_err(dev, "could not request phy interrupt, rc=%d\n", rc);
- rc = -ENOENT;
- goto free_irq_vectors;
+ return -ENOENT;
}
rc = devm_request_irq(dev, pci_irq_vector(pdev, 2),
DRV_NAME " channel", hisi_hba);
if (rc) {
dev_err(dev, "could not request chnl interrupt, rc=%d\n", rc);
- rc = -ENOENT;
- goto free_irq_vectors;
+ return -ENOENT;
}
rc = devm_request_irq(dev, pci_irq_vector(pdev, 11),
DRV_NAME " fatal", hisi_hba);
if (rc) {
dev_err(dev, "could not request fatal interrupt, rc=%d\n", rc);
- rc = -ENOENT;
- goto free_irq_vectors;
+ return -ENOENT;
}
if (hisi_sas_intr_conv)
if (rc) {
dev_err(dev, "could not request cq%d interrupt, rc=%d\n",
i, rc);
- rc = -ENOENT;
- goto free_irq_vectors;
+ return -ENOENT;
}
+ cq->irq_mask = pci_irq_get_affinity(pdev, i + BASE_VECTORS_V3_HW);
+ if (!cq->irq_mask) {
+ dev_err(dev, "could not get cq%d irq affinity!\n", i);
+ return -ENOENT;
+ }
}
return 0;
-
- free_irq_vectors:
- pci_free_irq_vectors(pdev);
- return rc;
}
static int hisi_sas_v3_init(struct hisi_hba *hisi_hba)
NULL
};
+ #define HISI_SAS_DEBUGFS_REG(x) {#x, x}
+
+ struct hisi_sas_debugfs_reg_lu {
+ char *name;
+ int off;
+ };
+
+ struct hisi_sas_debugfs_reg {
+ const struct hisi_sas_debugfs_reg_lu *lu;
+ int count;
+ int base_off;
+ };
+
static const struct hisi_sas_debugfs_reg_lu debugfs_port_reg_lu[] = {
HISI_SAS_DEBUGFS_REG(PHY_CFG),
HISI_SAS_DEBUGFS_REG(HARD_PHY_LINKRATE),
.lu = debugfs_port_reg_lu,
.count = 0x100,
.base_off = PORT_BASE,
- .read_port_reg = hisi_sas_phy_read32,
};
static const struct hisi_sas_debugfs_reg_lu debugfs_global_reg_lu[] = {
static const struct hisi_sas_debugfs_reg debugfs_global_reg = {
.lu = debugfs_global_reg_lu,
.count = 0x800,
- .read_global_reg = hisi_sas_read32,
};
static const struct hisi_sas_debugfs_reg_lu debugfs_axi_reg_lu[] = {
.lu = debugfs_axi_reg_lu,
.count = 0x61,
.base_off = AXI_MASTER_CFG_BASE,
- .read_global_reg = hisi_sas_read32,
};
static const struct hisi_sas_debugfs_reg_lu debugfs_ras_reg_lu[] = {
.lu = debugfs_ras_reg_lu,
.count = 0x10,
.base_off = RAS_BASE,
- .read_global_reg = hisi_sas_read32,
};
static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba)
};
static const struct hisi_sas_hw hisi_sas_v3_hw = {
- .hw_init = hisi_sas_v3_init,
.setup_itct = setup_itct_v3_hw,
.get_wideport_bitmap = get_wideport_bitmap_v3_hw,
.complete_hdr_size = sizeof(struct hisi_sas_complete_v3_hdr),
.get_events = phy_get_events_v3_hw,
.write_gpio = write_gpio_v3_hw,
.wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw,
- .debugfs_reg_array[DEBUGFS_GLOBAL] = &debugfs_global_reg,
- .debugfs_reg_array[DEBUGFS_AXI] = &debugfs_axi_reg,
- .debugfs_reg_array[DEBUGFS_RAS] = &debugfs_ras_reg,
- .debugfs_reg_port = &debugfs_port_reg,
- .snapshot_prepare = debugfs_snapshot_prepare_v3_hw,
- .snapshot_restore = debugfs_snapshot_restore_v3_hw,
- .read_iost_itct_cache = read_iost_itct_cache_v3_hw,
- .set_bist = debugfs_set_bist_v3_hw,
};
static struct Scsi_Host *
hisi_hba = shost_priv(shost);
INIT_WORK(&hisi_hba->rst_work, hisi_sas_rst_work_handler);
- INIT_WORK(&hisi_hba->debugfs_work, hisi_sas_debugfs_work_handler);
+ INIT_WORK(&hisi_hba->debugfs_work, debugfs_work_handler_v3_hw);
hisi_hba->hw = &hisi_sas_v3_hw;
hisi_hba->pci_dev = pdev;
hisi_hba->dev = dev;
return NULL;
}
+ static void debugfs_snapshot_cq_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int queue_entry_size = hisi_hba->hw->complete_hdr_size;
+ int dump_index = hisi_hba->debugfs_dump_index;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ memcpy(hisi_hba->debugfs_cq[dump_index][i].complete_hdr,
+ hisi_hba->complete_hdr[i],
+ HISI_SAS_QUEUE_SLOTS * queue_entry_size);
+ }
+
+ static void debugfs_snapshot_dq_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int queue_entry_size = sizeof(struct hisi_sas_cmd_hdr);
+ int dump_index = hisi_hba->debugfs_dump_index;
+ int i;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cmd_hdr *debugfs_cmd_hdr, *cmd_hdr;
+ int j;
+
+ debugfs_cmd_hdr = hisi_hba->debugfs_dq[dump_index][i].hdr;
+ cmd_hdr = hisi_hba->cmd_hdr[i];
+
+ for (j = 0; j < HISI_SAS_QUEUE_SLOTS; j++)
+ memcpy(&debugfs_cmd_hdr[j], &cmd_hdr[j],
+ queue_entry_size);
+ }
+ }
+
+ static void debugfs_snapshot_port_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int dump_index = hisi_hba->debugfs_dump_index;
+ const struct hisi_sas_debugfs_reg *port = &debugfs_port_reg;
+ int i, phy_cnt;
+ u32 offset;
+ u32 *databuf;
+
+ for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) {
+ databuf = hisi_hba->debugfs_port_reg[dump_index][phy_cnt].data;
+ for (i = 0; i < port->count; i++, databuf++) {
+ offset = port->base_off + 4 * i;
+ *databuf = hisi_sas_phy_read32(hisi_hba, phy_cnt,
+ offset);
+ }
+ }
+ }
+
+ static void debugfs_snapshot_global_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int dump_index = hisi_hba->debugfs_dump_index;
+ u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_GLOBAL].data;
+ int i;
+
+ for (i = 0; i < debugfs_axi_reg.count; i++, databuf++)
+ *databuf = hisi_sas_read32(hisi_hba, 4 * i);
+ }
+
+ static void debugfs_snapshot_axi_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int dump_index = hisi_hba->debugfs_dump_index;
+ u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_AXI].data;
+ const struct hisi_sas_debugfs_reg *axi = &debugfs_axi_reg;
+ int i;
+
+ for (i = 0; i < axi->count; i++, databuf++)
+ *databuf = hisi_sas_read32(hisi_hba, 4 * i + axi->base_off);
+ }
+
+ static void debugfs_snapshot_ras_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int dump_index = hisi_hba->debugfs_dump_index;
+ u32 *databuf = hisi_hba->debugfs_regs[dump_index][DEBUGFS_RAS].data;
+ const struct hisi_sas_debugfs_reg *ras = &debugfs_ras_reg;
+ int i;
+
+ for (i = 0; i < ras->count; i++, databuf++)
+ *databuf = hisi_sas_read32(hisi_hba, 4 * i + ras->base_off);
+ }
+
+ static void debugfs_snapshot_itct_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int dump_index = hisi_hba->debugfs_dump_index;
+ void *cachebuf = hisi_hba->debugfs_itct_cache[dump_index].cache;
+ void *databuf = hisi_hba->debugfs_itct[dump_index].itct;
+ struct hisi_sas_itct *itct;
+ int i;
+
+ read_iost_itct_cache_v3_hw(hisi_hba, HISI_SAS_ITCT_CACHE, cachebuf);
+
+ itct = hisi_hba->itct;
+
+ for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
+ memcpy(databuf, itct, sizeof(struct hisi_sas_itct));
+ databuf += sizeof(struct hisi_sas_itct);
+ }
+ }
+
+ static void debugfs_snapshot_iost_reg_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ int dump_index = hisi_hba->debugfs_dump_index;
+ int max_command_entries = HISI_SAS_MAX_COMMANDS;
+ void *cachebuf = hisi_hba->debugfs_iost_cache[dump_index].cache;
+ void *databuf = hisi_hba->debugfs_iost[dump_index].iost;
+ struct hisi_sas_iost *iost;
+ int i;
+
+ read_iost_itct_cache_v3_hw(hisi_hba, HISI_SAS_IOST_CACHE, cachebuf);
+
+ iost = hisi_hba->iost;
+
+ for (i = 0; i < max_command_entries; i++, iost++) {
+ memcpy(databuf, iost, sizeof(struct hisi_sas_iost));
+ databuf += sizeof(struct hisi_sas_iost);
+ }
+ }
+
+ static const char *
+ debugfs_to_reg_name_v3_hw(int off, int base_off,
+ const struct hisi_sas_debugfs_reg_lu *lu)
+ {
+ for (; lu->name; lu++) {
+ if (off == lu->off - base_off)
+ return lu->name;
+ }
+
+ return NULL;
+ }
+
+ static void debugfs_print_reg_v3_hw(u32 *regs_val, struct seq_file *s,
+ const struct hisi_sas_debugfs_reg *reg)
+ {
+ int i;
+
+ for (i = 0; i < reg->count; i++) {
+ int off = i * 4;
+ const char *name;
+
+ name = debugfs_to_reg_name_v3_hw(off, reg->base_off,
+ reg->lu);
+
+ if (name)
+ seq_printf(s, "0x%08x 0x%08x %s\n", off,
+ regs_val[i], name);
+ else
+ seq_printf(s, "0x%08x 0x%08x\n", off,
+ regs_val[i]);
+ }
+ }
+
+ static int debugfs_global_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_regs *global = s->private;
+
+ debugfs_print_reg_v3_hw(global->data, s,
+ &debugfs_global_reg);
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_global_v3_hw);
+
+ static int debugfs_axi_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_regs *axi = s->private;
+
+ debugfs_print_reg_v3_hw(axi->data, s,
+ &debugfs_axi_reg);
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_axi_v3_hw);
+
+ static int debugfs_ras_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_regs *ras = s->private;
+
+ debugfs_print_reg_v3_hw(ras->data, s,
+ &debugfs_ras_reg);
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_ras_v3_hw);
+
+ static int debugfs_port_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_port *port = s->private;
+ const struct hisi_sas_debugfs_reg *reg_port = &debugfs_port_reg;
+
+ debugfs_print_reg_v3_hw(port->data, s, reg_port);
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_port_v3_hw);
+
+ static void debugfs_show_row_64_v3_hw(struct seq_file *s, int index,
+ int sz, __le64 *ptr)
+ {
+ int i;
+
+ /* completion header size not fixed per HW version */
+ seq_printf(s, "index %04d:\n\t", index);
+ for (i = 1; i <= sz / 8; i++, ptr++) {
+ seq_printf(s, " 0x%016llx", le64_to_cpu(*ptr));
+ if (!(i % 2))
+ seq_puts(s, "\n\t");
+ }
+
+ seq_puts(s, "\n");
+ }
+
+ static void debugfs_show_row_32_v3_hw(struct seq_file *s, int index,
+ int sz, __le32 *ptr)
+ {
+ int i;
+
+ /* completion header size not fixed per HW version */
+ seq_printf(s, "index %04d:\n\t", index);
+ for (i = 1; i <= sz / 4; i++, ptr++) {
+ seq_printf(s, " 0x%08x", le32_to_cpu(*ptr));
+ if (!(i % 4))
+ seq_puts(s, "\n\t");
+ }
+ seq_puts(s, "\n");
+ }
+
+ static void debugfs_cq_show_slot_v3_hw(struct seq_file *s, int slot,
+ struct hisi_sas_debugfs_cq *debugfs_cq)
+ {
+ struct hisi_sas_cq *cq = debugfs_cq->cq;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ __le32 *complete_hdr = debugfs_cq->complete_hdr +
+ (hisi_hba->hw->complete_hdr_size * slot);
+
+ debugfs_show_row_32_v3_hw(s, slot,
+ hisi_hba->hw->complete_hdr_size,
+ complete_hdr);
+ }
+
+ static int debugfs_cq_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_cq *debugfs_cq = s->private;
+ int slot;
+
+ for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++)
+ debugfs_cq_show_slot_v3_hw(s, slot, debugfs_cq);
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_cq_v3_hw);
+
+ static void debugfs_dq_show_slot_v3_hw(struct seq_file *s, int slot,
+ void *dq_ptr)
+ {
+ struct hisi_sas_debugfs_dq *debugfs_dq = dq_ptr;
+ void *cmd_queue = debugfs_dq->hdr;
+ __le32 *cmd_hdr = cmd_queue +
+ sizeof(struct hisi_sas_cmd_hdr) * slot;
+
+ debugfs_show_row_32_v3_hw(s, slot, sizeof(struct hisi_sas_cmd_hdr),
+ cmd_hdr);
+ }
+
+ static int debugfs_dq_v3_hw_show(struct seq_file *s, void *p)
+ {
+ int slot;
+
+ for (slot = 0; slot < HISI_SAS_QUEUE_SLOTS; slot++)
+ debugfs_dq_show_slot_v3_hw(s, slot, s->private);
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_dq_v3_hw);
+
+ static int debugfs_iost_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_iost *debugfs_iost = s->private;
+ struct hisi_sas_iost *iost = debugfs_iost->iost;
+ int i, max_command_entries = HISI_SAS_MAX_COMMANDS;
+
+ for (i = 0; i < max_command_entries; i++, iost++) {
+ __le64 *data = &iost->qw0;
+
+ debugfs_show_row_64_v3_hw(s, i, sizeof(*iost), data);
+ }
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_iost_v3_hw);
+
+ static int debugfs_iost_cache_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_iost_cache *debugfs_iost_cache = s->private;
+ struct hisi_sas_iost_itct_cache *iost_cache =
+ debugfs_iost_cache->cache;
+ u32 cache_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ * 4;
+ int i, tab_idx;
+ __le64 *iost;
+
+ for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, iost_cache++) {
+ /*
+ * Data struct of IOST cache:
+ * Data[1]: BIT0~15: Table index
+ * Bit16: Valid mask
+ * Data[2]~[9]: IOST table
+ */
+ tab_idx = (iost_cache->data[1] & 0xffff);
+ iost = (__le64 *)iost_cache;
+
+ debugfs_show_row_64_v3_hw(s, tab_idx, cache_size, iost);
+ }
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_iost_cache_v3_hw);
+
+ static int debugfs_itct_v3_hw_show(struct seq_file *s, void *p)
+ {
+ int i;
+ struct hisi_sas_debugfs_itct *debugfs_itct = s->private;
+ struct hisi_sas_itct *itct = debugfs_itct->itct;
+
+ for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
+ __le64 *data = &itct->qw0;
+
+ debugfs_show_row_64_v3_hw(s, i, sizeof(*itct), data);
+ }
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_itct_v3_hw);
+
+ static int debugfs_itct_cache_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_debugfs_itct_cache *debugfs_itct_cache = s->private;
+ struct hisi_sas_iost_itct_cache *itct_cache =
+ debugfs_itct_cache->cache;
+ u32 cache_size = HISI_SAS_IOST_ITCT_CACHE_DW_SZ * 4;
+ int i, tab_idx;
+ __le64 *itct;
+
+ for (i = 0; i < HISI_SAS_IOST_ITCT_CACHE_NUM; i++, itct_cache++) {
+ /*
+ * Data struct of ITCT cache:
+ * Data[1]: BIT0~15: Table index
+ * Bit16: Valid mask
+ * Data[2]~[9]: ITCT table
+ */
+ tab_idx = itct_cache->data[1] & 0xffff;
+ itct = (__le64 *)itct_cache;
+
+ debugfs_show_row_64_v3_hw(s, tab_idx, cache_size, itct);
+ }
+
+ return 0;
+ }
+ DEFINE_SHOW_ATTRIBUTE(debugfs_itct_cache_v3_hw);
+
+ static void debugfs_create_files_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ u64 *debugfs_timestamp;
+ int dump_index = hisi_hba->debugfs_dump_index;
+ struct dentry *dump_dentry;
+ struct dentry *dentry;
+ char name[256];
+ int p;
+ int c;
+ int d;
+
+ snprintf(name, 256, "%d", dump_index);
+
+ dump_dentry = debugfs_create_dir(name, hisi_hba->debugfs_dump_dentry);
+
+ debugfs_timestamp = &hisi_hba->debugfs_timestamp[dump_index];
+
+ debugfs_create_u64("timestamp", 0400, dump_dentry,
+ debugfs_timestamp);
+
+ debugfs_create_file("global", 0400, dump_dentry,
+ &hisi_hba->debugfs_regs[dump_index][DEBUGFS_GLOBAL],
+ &debugfs_global_v3_hw_fops);
+
+ /* Create port dir and files */
+ dentry = debugfs_create_dir("port", dump_dentry);
+ for (p = 0; p < hisi_hba->n_phy; p++) {
+ snprintf(name, 256, "%d", p);
+
+ debugfs_create_file(name, 0400, dentry,
+ &hisi_hba->debugfs_port_reg[dump_index][p],
+ &debugfs_port_v3_hw_fops);
+ }
+
+ /* Create CQ dir and files */
+ dentry = debugfs_create_dir("cq", dump_dentry);
+ for (c = 0; c < hisi_hba->queue_count; c++) {
+ snprintf(name, 256, "%d", c);
+
+ debugfs_create_file(name, 0400, dentry,
+ &hisi_hba->debugfs_cq[dump_index][c],
+ &debugfs_cq_v3_hw_fops);
+ }
+
+ /* Create DQ dir and files */
+ dentry = debugfs_create_dir("dq", dump_dentry);
+ for (d = 0; d < hisi_hba->queue_count; d++) {
+ snprintf(name, 256, "%d", d);
+
+ debugfs_create_file(name, 0400, dentry,
+ &hisi_hba->debugfs_dq[dump_index][d],
+ &debugfs_dq_v3_hw_fops);
+ }
+
+ debugfs_create_file("iost", 0400, dump_dentry,
+ &hisi_hba->debugfs_iost[dump_index],
+ &debugfs_iost_v3_hw_fops);
+
+ debugfs_create_file("iost_cache", 0400, dump_dentry,
+ &hisi_hba->debugfs_iost_cache[dump_index],
+ &debugfs_iost_cache_v3_hw_fops);
+
+ debugfs_create_file("itct", 0400, dump_dentry,
+ &hisi_hba->debugfs_itct[dump_index],
+ &debugfs_itct_v3_hw_fops);
+
+ debugfs_create_file("itct_cache", 0400, dump_dentry,
+ &hisi_hba->debugfs_itct_cache[dump_index],
+ &debugfs_itct_cache_v3_hw_fops);
+
+ debugfs_create_file("axi", 0400, dump_dentry,
+ &hisi_hba->debugfs_regs[dump_index][DEBUGFS_AXI],
+ &debugfs_axi_v3_hw_fops);
+
+ debugfs_create_file("ras", 0400, dump_dentry,
+ &hisi_hba->debugfs_regs[dump_index][DEBUGFS_RAS],
+ &debugfs_ras_v3_hw_fops);
+ }
+
+ static void debugfs_snapshot_regs_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ debugfs_snapshot_prepare_v3_hw(hisi_hba);
+
+ debugfs_snapshot_global_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_port_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_axi_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_ras_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_cq_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_dq_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_itct_reg_v3_hw(hisi_hba);
+ debugfs_snapshot_iost_reg_v3_hw(hisi_hba);
+
+ debugfs_create_files_v3_hw(hisi_hba);
+
+ debugfs_snapshot_restore_v3_hw(hisi_hba);
+ }
+
+ static ssize_t debugfs_trigger_dump_v3_hw_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+ {
+ struct hisi_hba *hisi_hba = file->f_inode->i_private;
+ char buf[8];
+
+ if (hisi_hba->debugfs_dump_index >= hisi_sas_debugfs_dump_count)
+ return -EFAULT;
+
+ if (count > 8)
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (buf[0] != '1')
+ return -EFAULT;
+
+ queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
+
+ return count;
+ }
+
+ static const struct file_operations debugfs_trigger_dump_v3_hw_fops = {
+ .write = &debugfs_trigger_dump_v3_hw_write,
+ .owner = THIS_MODULE,
+ };
+
+ enum {
+ HISI_SAS_BIST_LOOPBACK_MODE_DIGITAL = 0,
+ HISI_SAS_BIST_LOOPBACK_MODE_SERDES,
+ HISI_SAS_BIST_LOOPBACK_MODE_REMOTE,
+ };
+
+ static const struct {
+ int value;
+ char *name;
+ } debugfs_loop_linkrate_v3_hw[] = {
+ { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" },
+ { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" },
+ { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" },
+ { SAS_LINK_RATE_12_0_GBPS, "12.0 Gbit" },
+ };
+
+ static int debugfs_bist_linkrate_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_hba *hisi_hba = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_linkrate_v3_hw); i++) {
+ int match = (hisi_hba->debugfs_bist_linkrate ==
+ debugfs_loop_linkrate_v3_hw[i].value);
+
+ seq_printf(s, "%s%s%s ", match ? "[" : "",
+ debugfs_loop_linkrate_v3_hw[i].name,
+ match ? "]" : "");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+ }
+
+ static ssize_t debugfs_bist_linkrate_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ char kbuf[16] = {}, *pkbuf;
+ bool found = false;
+ int i;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ if (count >= sizeof(kbuf))
+ return -EOVERFLOW;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EINVAL;
+
+ pkbuf = strstrip(kbuf);
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_linkrate_v3_hw); i++) {
+ if (!strncmp(debugfs_loop_linkrate_v3_hw[i].name,
+ pkbuf, 16)) {
+ hisi_hba->debugfs_bist_linkrate =
+ debugfs_loop_linkrate_v3_hw[i].value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return count;
+ }
+
+ static int debugfs_bist_linkrate_v3_hw_open(struct inode *inode,
+ struct file *filp)
+ {
+ return single_open(filp, debugfs_bist_linkrate_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_bist_linkrate_v3_hw_fops = {
+ .open = debugfs_bist_linkrate_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_bist_linkrate_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static const struct {
+ int value;
+ char *name;
+ } debugfs_loop_code_mode_v3_hw[] = {
+ { HISI_SAS_BIST_CODE_MODE_PRBS7, "PRBS7" },
+ { HISI_SAS_BIST_CODE_MODE_PRBS23, "PRBS23" },
+ { HISI_SAS_BIST_CODE_MODE_PRBS31, "PRBS31" },
+ { HISI_SAS_BIST_CODE_MODE_JTPAT, "JTPAT" },
+ { HISI_SAS_BIST_CODE_MODE_CJTPAT, "CJTPAT" },
+ { HISI_SAS_BIST_CODE_MODE_SCRAMBED_0, "SCRAMBED_0" },
+ { HISI_SAS_BIST_CODE_MODE_TRAIN, "TRAIN" },
+ { HISI_SAS_BIST_CODE_MODE_TRAIN_DONE, "TRAIN_DONE" },
+ { HISI_SAS_BIST_CODE_MODE_HFTP, "HFTP" },
+ { HISI_SAS_BIST_CODE_MODE_MFTP, "MFTP" },
+ { HISI_SAS_BIST_CODE_MODE_LFTP, "LFTP" },
+ { HISI_SAS_BIST_CODE_MODE_FIXED_DATA, "FIXED_DATA" },
+ };
+
+ static int debugfs_bist_code_mode_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_hba *hisi_hba = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_code_mode_v3_hw); i++) {
+ int match = (hisi_hba->debugfs_bist_code_mode ==
+ debugfs_loop_code_mode_v3_hw[i].value);
+
+ seq_printf(s, "%s%s%s ", match ? "[" : "",
+ debugfs_loop_code_mode_v3_hw[i].name,
+ match ? "]" : "");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+ }
+
+ static ssize_t debugfs_bist_code_mode_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+ {
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ char kbuf[16] = {}, *pkbuf;
+ bool found = false;
+ int i;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EOVERFLOW;
+
+ pkbuf = strstrip(kbuf);
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_code_mode_v3_hw); i++) {
+ if (!strncmp(debugfs_loop_code_mode_v3_hw[i].name,
+ pkbuf, 16)) {
+ hisi_hba->debugfs_bist_code_mode =
+ debugfs_loop_code_mode_v3_hw[i].value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return count;
+ }
+
+ static int debugfs_bist_code_mode_v3_hw_open(struct inode *inode,
+ struct file *filp)
+ {
+ return single_open(filp, debugfs_bist_code_mode_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_bist_code_mode_v3_hw_fops = {
+ .open = debugfs_bist_code_mode_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_bist_code_mode_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static ssize_t debugfs_bist_phy_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ unsigned int phy_no;
+ int val;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ val = kstrtouint_from_user(buf, count, 0, &phy_no);
+ if (val)
+ return val;
+
+ if (phy_no >= hisi_hba->n_phy)
+ return -EINVAL;
+
+ hisi_hba->debugfs_bist_phy_no = phy_no;
+
+ return count;
+ }
+
+ static int debugfs_bist_phy_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_hba *hisi_hba = s->private;
+
+ seq_printf(s, "%d\n", hisi_hba->debugfs_bist_phy_no);
+
+ return 0;
+ }
+
+ static int debugfs_bist_phy_v3_hw_open(struct inode *inode,
+ struct file *filp)
+ {
+ return single_open(filp, debugfs_bist_phy_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_bist_phy_v3_hw_fops = {
+ .open = debugfs_bist_phy_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_bist_phy_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static const struct {
+ int value;
+ char *name;
+ } debugfs_loop_modes_v3_hw[] = {
+ { HISI_SAS_BIST_LOOPBACK_MODE_DIGITAL, "digital" },
+ { HISI_SAS_BIST_LOOPBACK_MODE_SERDES, "serdes" },
+ { HISI_SAS_BIST_LOOPBACK_MODE_REMOTE, "remote" },
+ };
+
+ static int debugfs_bist_mode_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_hba *hisi_hba = s->private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_modes_v3_hw); i++) {
+ int match = (hisi_hba->debugfs_bist_mode ==
+ debugfs_loop_modes_v3_hw[i].value);
+
+ seq_printf(s, "%s%s%s ", match ? "[" : "",
+ debugfs_loop_modes_v3_hw[i].name,
+ match ? "]" : "");
+ }
+ seq_puts(s, "\n");
+
+ return 0;
+ }
+
+ static ssize_t debugfs_bist_mode_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ char kbuf[16] = {}, *pkbuf;
+ bool found = false;
+ int i;
+
+ if (hisi_hba->debugfs_bist_enable)
+ return -EPERM;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EOVERFLOW;
+
+ pkbuf = strstrip(kbuf);
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_loop_modes_v3_hw); i++) {
+ if (!strncmp(debugfs_loop_modes_v3_hw[i].name, pkbuf, 16)) {
+ hisi_hba->debugfs_bist_mode =
+ debugfs_loop_modes_v3_hw[i].value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return count;
+ }
+
+ static int debugfs_bist_mode_v3_hw_open(struct inode *inode,
+ struct file *filp)
+ {
+ return single_open(filp, debugfs_bist_mode_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_bist_mode_v3_hw_fops = {
+ .open = debugfs_bist_mode_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_bist_mode_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static ssize_t debugfs_bist_enable_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ struct seq_file *m = filp->private_data;
+ struct hisi_hba *hisi_hba = m->private;
+ unsigned int enable;
+ int val;
+
+ val = kstrtouint_from_user(buf, count, 0, &enable);
+ if (val)
+ return val;
+
+ if (enable > 1)
+ return -EINVAL;
+
+ if (enable == hisi_hba->debugfs_bist_enable)
+ return count;
+
+ val = debugfs_set_bist_v3_hw(hisi_hba, enable);
+ if (val < 0)
+ return val;
+
+ hisi_hba->debugfs_bist_enable = enable;
+
+ return count;
+ }
+
+ static int debugfs_bist_enable_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_hba *hisi_hba = s->private;
+
+ seq_printf(s, "%d\n", hisi_hba->debugfs_bist_enable);
+
+ return 0;
+ }
+
+ static int debugfs_bist_enable_v3_hw_open(struct inode *inode,
+ struct file *filp)
+ {
+ return single_open(filp, debugfs_bist_enable_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_bist_enable_v3_hw_fops = {
+ .open = debugfs_bist_enable_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_bist_enable_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static const struct {
+ char *name;
+ } debugfs_ffe_name_v3_hw[FFE_CFG_MAX] = {
+ { "SAS_1_5_GBPS" },
+ { "SAS_3_0_GBPS" },
+ { "SAS_6_0_GBPS" },
+ { "SAS_12_0_GBPS" },
+ { "FFE_RESV" },
+ { "SATA_1_5_GBPS" },
+ { "SATA_3_0_GBPS" },
+ { "SATA_6_0_GBPS" },
+ };
+
+ static ssize_t debugfs_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ struct seq_file *m = filp->private_data;
+ u32 *val = m->private;
+ int res;
+
+ res = kstrtouint_from_user(buf, count, 0, val);
+ if (res)
+ return res;
+
+ return count;
+ }
+
+ static int debugfs_v3_hw_show(struct seq_file *s, void *p)
+ {
+ u32 *val = s->private;
+
+ seq_printf(s, "0x%x\n", *val);
+
+ return 0;
+ }
+
+ static int debugfs_v3_hw_open(struct inode *inode, struct file *filp)
+ {
+ return single_open(filp, debugfs_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_v3_hw_fops = {
+ .open = debugfs_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static ssize_t debugfs_phy_down_cnt_v3_hw_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ struct seq_file *s = filp->private_data;
+ struct hisi_sas_phy *phy = s->private;
+ unsigned int set_val;
+ int res;
+
+ res = kstrtouint_from_user(buf, count, 0, &set_val);
+ if (res)
+ return res;
+
+ if (set_val > 0)
+ return -EINVAL;
+
+ atomic_set(&phy->down_cnt, 0);
+
+ return count;
+ }
+
+ static int debugfs_phy_down_cnt_v3_hw_show(struct seq_file *s, void *p)
+ {
+ struct hisi_sas_phy *phy = s->private;
+
+ seq_printf(s, "%d\n", atomic_read(&phy->down_cnt));
+
+ return 0;
+ }
+
+ static int debugfs_phy_down_cnt_v3_hw_open(struct inode *inode,
+ struct file *filp)
+ {
+ return single_open(filp, debugfs_phy_down_cnt_v3_hw_show,
+ inode->i_private);
+ }
+
+ static const struct file_operations debugfs_phy_down_cnt_v3_hw_fops = {
+ .open = debugfs_phy_down_cnt_v3_hw_open,
+ .read = seq_read,
+ .write = debugfs_phy_down_cnt_v3_hw_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+ };
+
+ static void debugfs_work_handler_v3_hw(struct work_struct *work)
+ {
+ struct hisi_hba *hisi_hba =
+ container_of(work, struct hisi_hba, debugfs_work);
+ int debugfs_dump_index = hisi_hba->debugfs_dump_index;
+ struct device *dev = hisi_hba->dev;
+ u64 timestamp = local_clock();
+
+ if (debugfs_dump_index >= hisi_sas_debugfs_dump_count) {
+ dev_warn(dev, "dump count exceeded!\n");
+ return;
+ }
+
+ do_div(timestamp, NSEC_PER_MSEC);
+ hisi_hba->debugfs_timestamp[debugfs_dump_index] = timestamp;
+
+ debugfs_snapshot_regs_v3_hw(hisi_hba);
+ hisi_hba->debugfs_dump_index++;
+ }
+
+ static void debugfs_release_v3_hw(struct hisi_hba *hisi_hba, int dump_index)
+ {
+ struct device *dev = hisi_hba->dev;
+ int i;
+
+ devm_kfree(dev, hisi_hba->debugfs_iost_cache[dump_index].cache);
+ devm_kfree(dev, hisi_hba->debugfs_itct_cache[dump_index].cache);
+ devm_kfree(dev, hisi_hba->debugfs_iost[dump_index].iost);
+ devm_kfree(dev, hisi_hba->debugfs_itct[dump_index].itct);
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ devm_kfree(dev, hisi_hba->debugfs_dq[dump_index][i].hdr);
+
+ for (i = 0; i < hisi_hba->queue_count; i++)
+ devm_kfree(dev,
+ hisi_hba->debugfs_cq[dump_index][i].complete_hdr);
+
+ for (i = 0; i < DEBUGFS_REGS_NUM; i++)
+ devm_kfree(dev, hisi_hba->debugfs_regs[dump_index][i].data);
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ devm_kfree(dev, hisi_hba->debugfs_port_reg[dump_index][i].data);
+ }
+
+ static const struct hisi_sas_debugfs_reg *debugfs_reg_array_v3_hw[DEBUGFS_REGS_NUM] = {
+ [DEBUGFS_GLOBAL] = &debugfs_global_reg,
+ [DEBUGFS_AXI] = &debugfs_axi_reg,
+ [DEBUGFS_RAS] = &debugfs_ras_reg,
+ };
+
+ static int debugfs_alloc_v3_hw(struct hisi_hba *hisi_hba, int dump_index)
+ {
+ const struct hisi_sas_hw *hw = hisi_hba->hw;
+ struct device *dev = hisi_hba->dev;
+ int p, c, d, r, i;
+ size_t sz;
+
+ for (r = 0; r < DEBUGFS_REGS_NUM; r++) {
+ struct hisi_sas_debugfs_regs *regs =
+ &hisi_hba->debugfs_regs[dump_index][r];
+
+ sz = debugfs_reg_array_v3_hw[r]->count * 4;
+ regs->data = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!regs->data)
+ goto fail;
+ regs->hisi_hba = hisi_hba;
+ }
+
+ sz = debugfs_port_reg.count * 4;
+ for (p = 0; p < hisi_hba->n_phy; p++) {
+ struct hisi_sas_debugfs_port *port =
+ &hisi_hba->debugfs_port_reg[dump_index][p];
+
+ port->data = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!port->data)
+ goto fail;
+ port->phy = &hisi_hba->phy[p];
+ }
+
+ sz = hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ for (c = 0; c < hisi_hba->queue_count; c++) {
+ struct hisi_sas_debugfs_cq *cq =
+ &hisi_hba->debugfs_cq[dump_index][c];
+
+ cq->complete_hdr = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!cq->complete_hdr)
+ goto fail;
+ cq->cq = &hisi_hba->cq[c];
+ }
+
+ sz = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
+ for (d = 0; d < hisi_hba->queue_count; d++) {
+ struct hisi_sas_debugfs_dq *dq =
+ &hisi_hba->debugfs_dq[dump_index][d];
+
+ dq->hdr = devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!dq->hdr)
+ goto fail;
+ dq->dq = &hisi_hba->dq[d];
+ }
+
+ sz = HISI_SAS_MAX_COMMANDS * sizeof(struct hisi_sas_iost);
+
+ hisi_hba->debugfs_iost[dump_index].iost =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_iost[dump_index].iost)
+ goto fail;
+
+ sz = HISI_SAS_IOST_ITCT_CACHE_NUM *
+ sizeof(struct hisi_sas_iost_itct_cache);
+
+ hisi_hba->debugfs_iost_cache[dump_index].cache =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_iost_cache[dump_index].cache)
+ goto fail;
+
+ sz = HISI_SAS_IOST_ITCT_CACHE_NUM *
+ sizeof(struct hisi_sas_iost_itct_cache);
+
+ hisi_hba->debugfs_itct_cache[dump_index].cache =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_itct_cache[dump_index].cache)
+ goto fail;
+
+ /* New memory allocation must be locate before itct */
+ sz = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
+
+ hisi_hba->debugfs_itct[dump_index].itct =
+ devm_kmalloc(dev, sz, GFP_KERNEL);
+ if (!hisi_hba->debugfs_itct[dump_index].itct)
+ goto fail;
+
+ return 0;
+ fail:
+ for (i = 0; i < hisi_sas_debugfs_dump_count; i++)
+ debugfs_release_v3_hw(hisi_hba, i);
+ return -ENOMEM;
+ }
+
+ static void debugfs_phy_down_cnt_init_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ struct dentry *dir = debugfs_create_dir("phy_down_cnt",
+ hisi_hba->debugfs_dir);
+ char name[16];
+ int phy_no;
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ snprintf(name, 16, "%d", phy_no);
+ debugfs_create_file(name, 0600, dir,
+ &hisi_hba->phy[phy_no],
+ &debugfs_phy_down_cnt_v3_hw_fops);
+ }
+ }
+
+ static void debugfs_bist_init_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ struct dentry *ports_dentry;
+ int phy_no;
+
+ hisi_hba->debugfs_bist_dentry =
+ debugfs_create_dir("bist", hisi_hba->debugfs_dir);
+ debugfs_create_file("link_rate", 0600,
+ hisi_hba->debugfs_bist_dentry, hisi_hba,
+ &debugfs_bist_linkrate_v3_hw_fops);
+
+ debugfs_create_file("code_mode", 0600,
+ hisi_hba->debugfs_bist_dentry, hisi_hba,
+ &debugfs_bist_code_mode_v3_hw_fops);
+
+ debugfs_create_file("fixed_code", 0600,
+ hisi_hba->debugfs_bist_dentry,
+ &hisi_hba->debugfs_bist_fixed_code[0],
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("fixed_code_1", 0600,
+ hisi_hba->debugfs_bist_dentry,
+ &hisi_hba->debugfs_bist_fixed_code[1],
+ &debugfs_v3_hw_fops);
+
+ debugfs_create_file("phy_id", 0600, hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_phy_v3_hw_fops);
+
+ debugfs_create_u32("cnt", 0600, hisi_hba->debugfs_bist_dentry,
+ &hisi_hba->debugfs_bist_cnt);
+
+ debugfs_create_file("loopback_mode", 0600,
+ hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_mode_v3_hw_fops);
+
+ debugfs_create_file("enable", 0600, hisi_hba->debugfs_bist_dentry,
+ hisi_hba, &debugfs_bist_enable_v3_hw_fops);
+
+ ports_dentry = debugfs_create_dir("port", hisi_hba->debugfs_bist_dentry);
+
+ for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+ struct dentry *port_dentry;
+ struct dentry *ffe_dentry;
+ char name[256];
+ int i;
+
+ snprintf(name, 256, "%d", phy_no);
+ port_dentry = debugfs_create_dir(name, ports_dentry);
+ ffe_dentry = debugfs_create_dir("ffe", port_dentry);
+ for (i = 0; i < FFE_CFG_MAX; i++) {
+ if (i == FFE_RESV)
+ continue;
+ debugfs_create_file(debugfs_ffe_name_v3_hw[i].name,
+ 0600, ffe_dentry,
+ &hisi_hba->debugfs_bist_ffe[phy_no][i],
+ &debugfs_v3_hw_fops);
+ }
+ }
+
+ hisi_hba->debugfs_bist_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ }
+
+ static void debugfs_init_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ struct device *dev = hisi_hba->dev;
+ int i;
+
+ hisi_hba->debugfs_dir = debugfs_create_dir(dev_name(dev),
+ hisi_sas_debugfs_dir);
+ debugfs_create_file("trigger_dump", 0200,
+ hisi_hba->debugfs_dir,
+ hisi_hba,
+ &debugfs_trigger_dump_v3_hw_fops);
+
+ /* create bist structures */
+ debugfs_bist_init_v3_hw(hisi_hba);
+
+ hisi_hba->debugfs_dump_dentry =
+ debugfs_create_dir("dump", hisi_hba->debugfs_dir);
+
+ debugfs_phy_down_cnt_init_v3_hw(hisi_hba);
+
+ for (i = 0; i < hisi_sas_debugfs_dump_count; i++) {
+ if (debugfs_alloc_v3_hw(hisi_hba, i)) {
+ debugfs_remove_recursive(hisi_hba->debugfs_dir);
+ dev_dbg(dev, "failed to init debugfs!\n");
+ break;
+ }
+ }
+ }
+
+ static void debugfs_exit_v3_hw(struct hisi_hba *hisi_hba)
+ {
+ debugfs_remove_recursive(hisi_hba->debugfs_dir);
+ }
+
static int
hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
}
if (hisi_sas_debugfs_enable)
- hisi_sas_debugfs_init(hisi_hba);
+ debugfs_init_v3_hw(hisi_hba);
rc = interrupt_preinit_v3_hw(hisi_hba);
if (rc)
- goto err_out_ha;
+ goto err_out_debugfs;
dev_err(dev, "%d hw queues\n", shost->nr_hw_queues);
rc = scsi_add_host(shost, dev);
if (rc)
- goto err_out_ha;
+ goto err_out_free_irq_vectors;
rc = sas_register_ha(sha);
if (rc)
goto err_out_register_ha;
- rc = hisi_hba->hw->hw_init(hisi_hba);
+ rc = hisi_sas_v3_init(hisi_hba);
if (rc)
goto err_out_register_ha;
err_out_register_ha:
scsi_remove_host(shost);
+ err_out_free_irq_vectors:
+ pci_free_irq_vectors(pdev);
+ err_out_debugfs:
+ debugfs_exit_v3_hw(hisi_hba);
err_out_ha:
- hisi_sas_debugfs_exit(hisi_hba);
+ hisi_sas_free(hisi_hba);
scsi_host_put(shost);
err_out_regions:
pci_release_regions(pdev);
pci_release_regions(pdev);
pci_disable_device(pdev);
hisi_sas_free(hisi_hba);
- hisi_sas_debugfs_exit(hisi_hba);
+ debugfs_exit_v3_hw(hisi_hba);
scsi_host_put(shost);
}
struct hisi_hba *hisi_hba = sha->lldd_ha;
struct device *dev = hisi_hba->dev;
struct Scsi_Host *shost = hisi_hba->shost;
- pci_power_t device_state;
int rc;
if (!pdev->pm_cap) {
hisi_sas_init_mem(hisi_hba);
- device_state = pci_choose_state(pdev, PMSG_SUSPEND);
- dev_warn(dev, "entering operating state [D%d]\n",
- device_state);
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, device_state);
+ dev_warn(dev, "entering suspend state\n");
hisi_sas_release_tasks(hisi_hba);
dev_warn(dev, "resuming from operating state [D%d]\n",
device_state);
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
- rc = pci_enable_device(pdev);
- if (rc) {
- dev_err(dev, "enable device failed during resume (%d)\n", rc);
- return rc;
- }
- pci_set_master(pdev);
scsi_unblock_requests(shost);
clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
rc = hw_init_v3_hw(hisi_hba);
if (rc) {
scsi_remove_host(shost);
- pci_disable_device(pdev);
return rc;
}
- hisi_hba->hw->phys_init(hisi_hba);
+ phys_init_v3_hw(hisi_hba);
sas_resume_ha(sha);
clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags);
return 0;
}
- static int suspend_v3_hw(struct device *device)
+ static int __maybe_unused suspend_v3_hw(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
return rc;
}
- static int resume_v3_hw(struct device *device)
+ static int __maybe_unused resume_v3_hw(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
.reset_done = hisi_sas_reset_done_v3_hw,
};
- static int runtime_suspend_v3_hw(struct device *dev)
- {
- return suspend_v3_hw(dev);
- }
-
- static int runtime_resume_v3_hw(struct device *dev)
- {
- return resume_v3_hw(dev);
- }
-
- static const struct dev_pm_ops hisi_sas_v3_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(suspend_v3_hw, resume_v3_hw)
- SET_RUNTIME_PM_OPS(runtime_suspend_v3_hw,
- runtime_resume_v3_hw, NULL)
- };
+ static UNIVERSAL_DEV_PM_OPS(hisi_sas_v3_pm_ops,
+ suspend_v3_hw,
+ resume_v3_hw,
+ NULL);
static struct pci_driver sas_v3_pci_driver = {
.name = DRV_NAME,
u8 sense_key, asc, ascq;
int sense_len;
int rc, ldstat = 0;
- u16 cmd_status;
- u8 scsi_status;
#define ASC_LUN_NOT_READY 0x04
#define ASCQ_LUN_NOT_READY_FORMAT_IN_PROGRESS 0x04
#define ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ 0x02
else
sense_len = c->err_info->SenseLen;
decode_sense_data(sense, sense_len, &sense_key, &asc, &ascq);
- cmd_status = c->err_info->CommandStatus;
- scsi_status = c->err_info->ScsiStatus;
cmd_free(h, c);
/* Determine the reason for not ready state */
u32 ndev_allocated = 0;
struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice;
int ncurrent = 0;
- int i, n_ext_target_devs, ndevs_to_allocate;
+ int i, ndevs_to_allocate;
int raid_ctlr_position;
bool physical_device;
DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS);
raid_ctlr_position = nphysicals + nlogicals;
/* adjust our table of devices */
- n_ext_target_devs = 0;
for (i = 0; i < nphysicals + nlogicals + 1; i++) {
u8 *lunaddrbytes, is_OBDR = 0;
int rc = 0;
struct scsi_cmnd *cmd)
{
struct scatterlist *sg;
- int use_sg, i, sg_limit, chained, last_sg;
+ int use_sg, i, sg_limit, chained;
struct SGDescriptor *curr_sg;
BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
curr_sg = cp->SG;
chained = use_sg > h->max_cmd_sg_entries;
sg_limit = chained ? h->max_cmd_sg_entries - 1 : use_sg;
- last_sg = scsi_sg_count(cmd) - 1;
scsi_for_each_sg(cmd, sg, sg_limit, i) {
hpsa_set_sg_descriptor(curr_sg, sg);
curr_sg++;
dev_warn(&pdev->dev,
"base address is invalid\n");
return -1;
- break;
}
}
if (offset == pci_bar_addr - PCI_BASE_ADDRESS_0)
static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- int dac, rc;
+ int rc;
struct ctlr_info *h;
int try_soft_reset = 0;
unsigned long flags;
/* configure PCI DMA stuff */
rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
- if (rc == 0) {
- dac = 1;
- } else {
+ if (rc != 0) {
rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (rc == 0) {
- dac = 0;
- } else {
+ if (rc != 0) {
dev_err(&pdev->dev, "no suitable DMA available\n");
goto clean3; /* shost, pci, lu, aer/h */
}
/* hook into SCSI subsystem */
rc = hpsa_scsi_add_host(h);
if (rc)
- goto clean7; /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
+ goto clean8; /* lastlogicals, perf, sg, cmd, irq, shost, pci, lu, aer/h */
/* Monitor the controller for firmware lockups */
h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
HPSA_EVENT_MONITOR_INTERVAL);
return 0;
+clean8: /* lastlogicals, perf, sg, cmd, irq, shost, pci, lu, aer/h */
+ kfree(h->lastlogicals);
clean7: /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
hpsa_free_performant_mode(h);
h->access.set_intr_mask(h, HPSA_INTR_OFF);
hpda_free_ctlr_info(h); /* init_one 1 */
}
- static int hpsa_suspend(__attribute__((unused)) struct pci_dev *pdev,
- __attribute__((unused)) pm_message_t state)
+ static int __maybe_unused hpsa_suspend(
+ __attribute__((unused)) struct device *dev)
{
return -ENOSYS;
}
- static int hpsa_resume(__attribute__((unused)) struct pci_dev *pdev)
+ static int __maybe_unused hpsa_resume
+ (__attribute__((unused)) struct device *dev)
{
return -ENOSYS;
}
+ static SIMPLE_DEV_PM_OPS(hpsa_pm_ops, hpsa_suspend, hpsa_resume);
+
static struct pci_driver hpsa_pci_driver = {
.name = HPSA,
.probe = hpsa_init_one,
.remove = hpsa_remove_one,
.id_table = hpsa_pci_device_id, /* id_table */
.shutdown = hpsa_shutdown,
- .suspend = hpsa_suspend,
- .resume = hpsa_resume,
+ .driver.pm = &hpsa_pm_ops,
};
/* Fill in bucket_map[], given nsgs (the max number of
} else if (trans_support & CFGTBL_Trans_io_accel2) {
u64 cfg_offset, cfg_base_addr_index;
u32 bft2_offset, cfg_base_addr;
- int rc;
- rc = hpsa_find_cfg_addrs(h->pdev, h->vaddr, &cfg_base_addr,
- &cfg_base_addr_index, &cfg_offset);
+ hpsa_find_cfg_addrs(h->pdev, h->vaddr, &cfg_base_addr,
+ &cfg_base_addr_index, &cfg_offset);
BUILD_BUG_ON(offsetof(struct io_accel2_cmd, sg) != 64);
bft2[15] = h->ioaccel_maxsg + HPSA_IOACCEL2_HEADER_SZ;
calc_bucket_map(bft2, ARRAY_SIZE(bft2), h->ioaccel_maxsg,
if (conn->task == task)
conn->task = NULL;
- if (conn->ping_task == task)
- conn->ping_task = NULL;
+ if (READ_ONCE(conn->ping_task) == task)
+ WRITE_ONCE(conn->ping_task, NULL);
/* release get from queueing */
__iscsi_put_task(task);
task->conn->session->age);
}
+ if (unlikely(READ_ONCE(conn->ping_task) == INVALID_SCSI_TASK))
+ WRITE_ONCE(conn->ping_task, task);
+
if (!ihost->workq) {
if (iscsi_prep_mgmt_task(conn, task))
goto free_task;
EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
/**
- * iscsi_cmd_rsp - SCSI Command Response processing
+ * iscsi_scsi_cmd_rsp - SCSI Command Response processing
* @conn: iscsi connection
* @hdr: iscsi header
* @task: scsi command task
struct iscsi_nopout hdr;
struct iscsi_task *task;
- if (!rhdr && conn->ping_task)
- return -EINVAL;
+ if (!rhdr) {
+ if (READ_ONCE(conn->ping_task))
+ return -EINVAL;
+ WRITE_ONCE(conn->ping_task, INVALID_SCSI_TASK);
+ }
memset(&hdr, 0, sizeof(struct iscsi_nopout));
hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
if (!task) {
+ if (!rhdr)
+ WRITE_ONCE(conn->ping_task, NULL);
iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
return -EIO;
} else if (!rhdr) {
/* only track our nops */
- conn->ping_task = task;
conn->last_ping = jiffies;
}
struct iscsi_conn *conn = task->conn;
int rc = 0;
- if (conn->ping_task != task) {
+ if (READ_ONCE(conn->ping_task) != task) {
/*
* If this is not in response to one of our
* nops then it must be from userspace.
*/
static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
{
- if (conn->ping_task &&
+ if (READ_ONCE(conn->ping_task) &&
time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
(conn->ping_timeout * HZ), jiffies))
return 1;
* Checking the transport already or nop from a cmd timeout still
* running
*/
- if (conn->ping_task) {
+ if (READ_ONCE(conn->ping_task)) {
task->have_checked_conn = true;
rc = BLK_EH_RESET_TIMER;
goto done;
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/irq_poll.h>
-#include <linux/blk-mq-pci.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
module_param(enable_sdev_max_qd, int, 0444);
MODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue. Default: 0");
-int host_tagset_enable = 1;
-module_param(host_tagset_enable, int, 0444);
-MODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: enable(1)");
-
MODULE_LICENSE("GPL");
MODULE_VERSION(MEGASAS_VERSION);
MODULE_AUTHOR("megaraidlinux.pdl@broadcom.com");
return 0;
}
-static int megasas_map_queues(struct Scsi_Host *shost)
-{
- struct megasas_instance *instance;
-
- instance = (struct megasas_instance *)shost->hostdata;
-
- if (shost->nr_hw_queues == 1)
- return 0;
-
- return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
- instance->pdev, instance->low_latency_index_start);
-}
-
static void megasas_aen_polling(struct work_struct *work);
/**
.eh_timed_out = megasas_reset_timer,
.shost_attrs = megaraid_host_attrs,
.bios_param = megasas_bios_param,
- .map_queues = megasas_map_queues,
.change_queue_depth = scsi_change_queue_depth,
.max_segment_size = 0xffffffff,
};
host->max_lun = MEGASAS_MAX_LUN;
host->max_cmd_len = 16;
- /* Use shared host tagset only for fusion adaptors
- * if there are managed interrupts (smp affinity enabled case).
- * Single msix_vectors in kdump, so shared host tag is also disabled.
- */
-
- host->host_tagset = 0;
- host->nr_hw_queues = 1;
-
- if ((instance->adapter_type != MFI_SERIES) &&
- (instance->msix_vectors > instance->low_latency_index_start) &&
- host_tagset_enable &&
- instance->smp_affinity_enable) {
- host->host_tagset = 1;
- host->nr_hw_queues = instance->msix_vectors -
- instance->low_latency_index_start;
- }
-
- dev_info(&instance->pdev->dev,
- "Max firmware commands: %d shared with nr_hw_queues = %d\n",
- instance->max_fw_cmds, host->nr_hw_queues);
/*
* Notify the mid-layer about the new controller
*/
megasas_return_cmd(instance, cmd);
}
- #ifdef CONFIG_PM
/**
* megasas_suspend - driver suspend entry point
- * @pdev: PCI device structure
- * @state: PCI power state to suspend routine
+ * @dev: Device structure
*/
- static int
- megasas_suspend(struct pci_dev *pdev, pm_message_t state)
+ static int __maybe_unused
+ megasas_suspend(struct device *dev)
{
struct megasas_instance *instance;
- instance = pci_get_drvdata(pdev);
+ instance = dev_get_drvdata(dev);
if (!instance)
return 0;
instance->unload = 1;
- dev_info(&pdev->dev, "%s is called\n", __func__);
+ dev_info(dev, "%s is called\n", __func__);
/* Shutdown SR-IOV heartbeat timer */
if (instance->requestorId && !instance->skip_heartbeat_timer_del)
if (instance->msix_vectors)
pci_free_irq_vectors(instance->pdev);
- pci_save_state(pdev);
- pci_disable_device(pdev);
-
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
return 0;
}
/**
* megasas_resume- driver resume entry point
- * @pdev: PCI device structure
+ * @dev: Device structure
*/
- static int
- megasas_resume(struct pci_dev *pdev)
+ static int __maybe_unused
+ megasas_resume(struct device *dev)
{
int rval;
struct Scsi_Host *host;
struct megasas_instance *instance;
u32 status_reg;
- instance = pci_get_drvdata(pdev);
+ instance = dev_get_drvdata(dev);
if (!instance)
return 0;
host = instance->host;
- pci_set_power_state(pdev, PCI_D0);
- pci_enable_wake(pdev, PCI_D0, 0);
- pci_restore_state(pdev);
-
- dev_info(&pdev->dev, "%s is called\n", __func__);
- /*
- * PCI prepping: enable device set bus mastering and dma mask
- */
- rval = pci_enable_device_mem(pdev);
- if (rval) {
- dev_err(&pdev->dev, "Enable device failed\n");
- return rval;
- }
-
- pci_set_master(pdev);
+ dev_info(dev, "%s is called\n", __func__);
/*
* We expect the FW state to be READY
fail_set_dma_mask:
fail_ready_state:
- pci_disable_device(pdev);
-
return -ENODEV;
}
- #else
- #define megasas_suspend NULL
- #define megasas_resume NULL
- #endif
static inline int
megasas_wait_for_adapter_operational(struct megasas_instance *instance)
/**
* megasas_shutdown - Shutdown entry point
- * @pdev: Generic device structure
+ * @pdev: PCI device structure
*/
static void megasas_shutdown(struct pci_dev *pdev)
{
int error = 0, i;
void *sense = NULL;
dma_addr_t sense_handle;
- unsigned long *sense_ptr;
+ void *sense_ptr;
u32 opcode = 0;
int ret = DCMD_SUCCESS;
}
if (ioc->sense_len) {
+ /* make sure the pointer is part of the frame */
+ if (ioc->sense_off >
+ (sizeof(union megasas_frame) - sizeof(__le64))) {
+ error = -EINVAL;
+ goto out;
+ }
+
sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len,
&sense_handle, GFP_KERNEL);
if (!sense) {
goto out;
}
- sense_ptr =
- (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off);
+ sense_ptr = (void *)cmd->frame + ioc->sense_off;
if (instance->consistent_mask_64bit)
- *sense_ptr = cpu_to_le64(sense_handle);
+ put_unaligned_le64(sense_handle, sense_ptr);
else
- *sense_ptr = cpu_to_le32(sense_handle);
+ put_unaligned_le32(sense_handle, sense_ptr);
}
/*
* copy out the sense
*/
if (ioc->sense_len) {
+ void __user *uptr;
/*
* sense_ptr points to the location that has the user
* sense buffer address
*/
- sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw +
- ioc->sense_off);
+ sense_ptr = (void *)ioc->frame.raw + ioc->sense_off;
+ if (in_compat_syscall())
+ uptr = compat_ptr(get_unaligned((compat_uptr_t *)
+ sense_ptr));
+ else
+ uptr = get_unaligned((void __user **)sense_ptr);
- if (copy_to_user((void __user *)((unsigned long)
- get_unaligned((unsigned long *)sense_ptr)),
- sense, ioc->sense_len)) {
+ if (copy_to_user(uptr, sense, ioc->sense_len)) {
dev_err(&instance->pdev->dev, "Failed to copy out to user "
"sense data\n");
error = -EFAULT;
return error;
}
+ static struct megasas_iocpacket *
+ megasas_compat_iocpacket_get_user(void __user *arg)
+ {
+ struct megasas_iocpacket *ioc;
+ struct compat_megasas_iocpacket __user *cioc = arg;
+ size_t size;
+ int err = -EFAULT;
+ int i;
+
+ ioc = kzalloc(sizeof(*ioc), GFP_KERNEL);
+ if (!ioc)
+ return ERR_PTR(-ENOMEM);
+ size = offsetof(struct megasas_iocpacket, frame) + sizeof(ioc->frame);
+ if (copy_from_user(ioc, arg, size))
+ goto out;
+
+ for (i = 0; i < MAX_IOCTL_SGE; i++) {
+ compat_uptr_t iov_base;
+
+ if (get_user(iov_base, &cioc->sgl[i].iov_base) ||
+ get_user(ioc->sgl[i].iov_len, &cioc->sgl[i].iov_len))
+ goto out;
+
+ ioc->sgl[i].iov_base = compat_ptr(iov_base);
+ }
+
+ return ioc;
+ out:
+ kfree(ioc);
+ return ERR_PTR(err);
+ }
+
static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
{
struct megasas_iocpacket __user *user_ioc =
struct megasas_instance *instance;
int error;
- ioc = memdup_user(user_ioc, sizeof(*ioc));
+ if (in_compat_syscall())
+ ioc = megasas_compat_iocpacket_get_user(user_ioc);
+ else
+ ioc = memdup_user(user_ioc, sizeof(struct megasas_iocpacket));
+
if (IS_ERR(ioc))
return PTR_ERR(ioc);
}
#ifdef CONFIG_COMPAT
- static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg)
- {
- struct compat_megasas_iocpacket __user *cioc =
- (struct compat_megasas_iocpacket __user *)arg;
- struct megasas_iocpacket __user *ioc =
- compat_alloc_user_space(sizeof(struct megasas_iocpacket));
- int i;
- int error = 0;
- compat_uptr_t ptr;
- u32 local_sense_off;
- u32 local_sense_len;
- u32 user_sense_off;
-
- if (clear_user(ioc, sizeof(*ioc)))
- return -EFAULT;
-
- if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) ||
- copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) ||
- copy_in_user(&ioc->sense_off, &cioc->sense_off, sizeof(u32)) ||
- copy_in_user(&ioc->sense_len, &cioc->sense_len, sizeof(u32)) ||
- copy_in_user(ioc->frame.raw, cioc->frame.raw, 128) ||
- copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32)))
- return -EFAULT;
-
- /*
- * The sense_ptr is used in megasas_mgmt_fw_ioctl only when
- * sense_len is not null, so prepare the 64bit value under
- * the same condition.
- */
- if (get_user(local_sense_off, &ioc->sense_off) ||
- get_user(local_sense_len, &ioc->sense_len) ||
- get_user(user_sense_off, &cioc->sense_off))
- return -EFAULT;
-
- if (local_sense_off != user_sense_off)
- return -EINVAL;
-
- if (local_sense_len) {
- void __user **sense_ioc_ptr =
- (void __user **)((u8 *)((unsigned long)&ioc->frame.raw) + local_sense_off);
- compat_uptr_t *sense_cioc_ptr =
- (compat_uptr_t *)(((unsigned long)&cioc->frame.raw) + user_sense_off);
- if (get_user(ptr, sense_cioc_ptr) ||
- put_user(compat_ptr(ptr), sense_ioc_ptr))
- return -EFAULT;
- }
-
- for (i = 0; i < MAX_IOCTL_SGE; i++) {
- if (get_user(ptr, &cioc->sgl[i].iov_base) ||
- put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) ||
- copy_in_user(&ioc->sgl[i].iov_len,
- &cioc->sgl[i].iov_len, sizeof(compat_size_t)))
- return -EFAULT;
- }
-
- error = megasas_mgmt_ioctl_fw(file, (unsigned long)ioc);
-
- if (copy_in_user(&cioc->frame.hdr.cmd_status,
- &ioc->frame.hdr.cmd_status, sizeof(u8))) {
- printk(KERN_DEBUG "megasas: error copy_in_user cmd_status\n");
- return -EFAULT;
- }
- return error;
- }
-
static long
megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case MEGASAS_IOC_FIRMWARE32:
- return megasas_mgmt_compat_ioctl_fw(file, arg);
+ return megasas_mgmt_ioctl_fw(file, arg);
case MEGASAS_IOC_GET_AEN:
return megasas_mgmt_ioctl_aen(file, arg);
}
.llseek = noop_llseek,
};
+ static SIMPLE_DEV_PM_OPS(megasas_pm_ops, megasas_suspend, megasas_resume);
+
/*
* PCI hotplug support registration structure
*/
.id_table = megasas_pci_table,
.probe = megasas_probe_one,
.remove = megasas_detach_one,
- .suspend = megasas_suspend,
- .resume = megasas_resume,
+ .driver.pm = &megasas_pm_ops,
.shutdown = megasas_shutdown,
};
}
/**
+ * _base_sync_drv_fw_timestamp - Sync Drive-Fw TimeStamp.
+ * @ioc: Per Adapter Object
+ *
+ * Return nothing.
+ */
+ static void _base_sync_drv_fw_timestamp(struct MPT3SAS_ADAPTER *ioc)
+ {
+ Mpi26IoUnitControlRequest_t *mpi_request;
+ Mpi26IoUnitControlReply_t *mpi_reply;
+ u16 smid;
+ ktime_t current_time;
+ u64 TimeStamp = 0;
+ u8 issue_reset = 0;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ ioc_err(ioc, "scsih_cmd in use %s\n", __func__);
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ ioc_err(ioc, "Failed obtaining a smid %s\n", __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+ }
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi26IoUnitControlRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_IO_UNIT_CONTROL;
+ mpi_request->Operation = MPI26_CTRL_OP_SET_IOC_PARAMETER;
+ mpi_request->IOCParameter = MPI26_SET_IOC_PARAMETER_SYNC_TIMESTAMP;
+ current_time = ktime_get_real();
+ TimeStamp = ktime_to_ms(current_time);
+ mpi_request->Reserved7 = cpu_to_le32(TimeStamp & 0xFFFFFFFF);
+ mpi_request->IOCParameterValue = cpu_to_le32(TimeStamp >> 32);
+ init_completion(&ioc->scsih_cmds.done);
+ ioc->put_smid_default(ioc, smid);
+ dinitprintk(ioc, ioc_info(ioc,
+ "Io Unit Control Sync TimeStamp (sending), @time %lld ms\n",
+ TimeStamp));
+ wait_for_completion_timeout(&ioc->scsih_cmds.done,
+ MPT3SAS_TIMESYNC_TIMEOUT_SECONDS*HZ);
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ mpt3sas_check_cmd_timeout(ioc,
+ ioc->scsih_cmds.status, mpi_request,
+ sizeof(Mpi2SasIoUnitControlRequest_t)/4, issue_reset);
+ goto issue_host_reset;
+ }
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+ dinitprintk(ioc, ioc_info(ioc,
+ "Io Unit Control sync timestamp (complete): ioc_status(0x%04x), loginfo(0x%08x)\n",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo)));
+ }
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ out:
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+ }
+
+ /**
* _base_fault_reset_work - workq handling ioc fault conditions
* @work: input argument, used to derive ioc
*
return; /* don't rearm timer */
}
ioc->ioc_coredump_loop = 0;
-
+ if (ioc->time_sync_interval &&
+ ++ioc->timestamp_update_count >= ioc->time_sync_interval) {
+ ioc->timestamp_update_count = 0;
+ _base_sync_drv_fw_timestamp(ioc);
+ }
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
rearm_timer:
if (ioc->fault_reset_work_q)
if (ioc->fault_reset_work_q)
return;
+ ioc->timestamp_update_count = 0;
/* initialize fault polling */
INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
return;
+ /*
+ * Older Firmware version doesn't support driver trigger pages.
+ * So, skip displaying 'config invalid type' type
+ * of error message.
+ */
+ if (request_hdr->Function == MPI2_FUNCTION_CONFIG) {
+ Mpi2ConfigRequest_t *rqst = (Mpi2ConfigRequest_t *)request_hdr;
+
+ if ((rqst->ExtPageType ==
+ MPI2_CONFIG_EXTPAGETYPE_DRIVER_PERSISTENT_TRIGGER) &&
+ !(ioc->logging_level & MPT_DEBUG_CONFIG)) {
+ return;
+ }
+ }
switch (ioc_status) {
reply_q->irq_poll_scheduled = false;
reply_q->irq_line_enable = true;
enable_irq(reply_q->os_irq);
+ /*
+ * Go for one more round of processing the
+ * reply descriptor post queue incase if HBA
+ * Firmware has posted some reply descriptors
+ * while reenabling the IRQ.
+ */
+ _base_process_reply_queue(reply_q);
}
return num_entries;
}
/**
+ * _base_get_event_diag_triggers - get event diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return nothing.
+ */
+ static void
+ _base_get_event_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+ {
+ Mpi26DriverTriggerPage2_t trigger_pg2;
+ struct SL_WH_EVENT_TRIGGER_T *event_tg;
+ MPI26_DRIVER_MPI_EVENT_TIGGER_ENTRY *mpi_event_tg;
+ Mpi2ConfigReply_t mpi_reply;
+ int r = 0, i = 0;
+ u16 count = 0;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg2(ioc, &mpi_reply,
+ &trigger_pg2);
+ if (r)
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return;
+ }
+
+ if (le16_to_cpu(trigger_pg2.NumMPIEventTrigger)) {
+ count = le16_to_cpu(trigger_pg2.NumMPIEventTrigger);
+ count = min_t(u16, NUM_VALID_ENTRIES, count);
+ ioc->diag_trigger_event.ValidEntries = count;
+
+ event_tg = &ioc->diag_trigger_event.EventTriggerEntry[0];
+ mpi_event_tg = &trigger_pg2.MPIEventTriggers[0];
+ for (i = 0; i < count; i++) {
+ event_tg->EventValue = le16_to_cpu(
+ mpi_event_tg->MPIEventCode);
+ event_tg->LogEntryQualifier = le16_to_cpu(
+ mpi_event_tg->MPIEventCodeSpecific);
+ event_tg++;
+ mpi_event_tg++;
+ }
+ }
+ }
+
+ /**
+ * _base_get_scsi_diag_triggers - get scsi diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return nothing.
+ */
+ static void
+ _base_get_scsi_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+ {
+ Mpi26DriverTriggerPage3_t trigger_pg3;
+ struct SL_WH_SCSI_TRIGGER_T *scsi_tg;
+ MPI26_DRIVER_SCSI_SENSE_TIGGER_ENTRY *mpi_scsi_tg;
+ Mpi2ConfigReply_t mpi_reply;
+ int r = 0, i = 0;
+ u16 count = 0;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg3(ioc, &mpi_reply,
+ &trigger_pg3);
+ if (r)
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg3, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return;
+ }
+
+ if (le16_to_cpu(trigger_pg3.NumSCSISenseTrigger)) {
+ count = le16_to_cpu(trigger_pg3.NumSCSISenseTrigger);
+ count = min_t(u16, NUM_VALID_ENTRIES, count);
+ ioc->diag_trigger_scsi.ValidEntries = count;
+
+ scsi_tg = &ioc->diag_trigger_scsi.SCSITriggerEntry[0];
+ mpi_scsi_tg = &trigger_pg3.SCSISenseTriggers[0];
+ for (i = 0; i < count; i++) {
+ scsi_tg->ASCQ = mpi_scsi_tg->ASCQ;
+ scsi_tg->ASC = mpi_scsi_tg->ASC;
+ scsi_tg->SenseKey = mpi_scsi_tg->SenseKey;
+
+ scsi_tg++;
+ mpi_scsi_tg++;
+ }
+ }
+ }
+
+ /**
+ * _base_get_mpi_diag_triggers - get mpi diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return nothing.
+ */
+ static void
+ _base_get_mpi_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+ {
+ Mpi26DriverTriggerPage4_t trigger_pg4;
+ struct SL_WH_MPI_TRIGGER_T *status_tg;
+ MPI26_DRIVER_IOCSTATUS_LOGINFO_TIGGER_ENTRY *mpi_status_tg;
+ Mpi2ConfigReply_t mpi_reply;
+ int r = 0, i = 0;
+ u16 count = 0;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg4(ioc, &mpi_reply,
+ &trigger_pg4);
+ if (r)
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg4, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return;
+ }
+
+ if (le16_to_cpu(trigger_pg4.NumIOCStatusLogInfoTrigger)) {
+ count = le16_to_cpu(trigger_pg4.NumIOCStatusLogInfoTrigger);
+ count = min_t(u16, NUM_VALID_ENTRIES, count);
+ ioc->diag_trigger_mpi.ValidEntries = count;
+
+ status_tg = &ioc->diag_trigger_mpi.MPITriggerEntry[0];
+ mpi_status_tg = &trigger_pg4.IOCStatusLoginfoTriggers[0];
+
+ for (i = 0; i < count; i++) {
+ status_tg->IOCStatus = le16_to_cpu(
+ mpi_status_tg->IOCStatus);
+ status_tg->IocLogInfo = le32_to_cpu(
+ mpi_status_tg->LogInfo);
+
+ status_tg++;
+ mpi_status_tg++;
+ }
+ }
+ }
+
+ /**
+ * _base_get_master_diag_triggers - get master diag trigger values from
+ * persistent pages
+ * @ioc : per adapter object
+ *
+ * Return nothing.
+ */
+ static void
+ _base_get_master_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+ {
+ Mpi26DriverTriggerPage1_t trigger_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ int r;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg1(ioc, &mpi_reply,
+ &trigger_pg1);
+ if (r)
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dinitprintk(ioc,
+ ioc_err(ioc,
+ "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n",
+ __func__, ioc_status));
+ return;
+ }
+
+ if (le16_to_cpu(trigger_pg1.NumMasterTrigger))
+ ioc->diag_trigger_master.MasterData |=
+ le32_to_cpu(
+ trigger_pg1.MasterTriggers[0].MasterTriggerFlags);
+ }
+
+ /**
+ * _base_check_for_trigger_pages_support - checks whether HBA FW supports
+ * driver trigger pages or not
+ * @ioc : per adapter object
+ *
+ * Returns trigger flags mask if HBA FW supports driver trigger pages,
+ * otherwise returns EFAULT.
+ */
+ static int
+ _base_check_for_trigger_pages_support(struct MPT3SAS_ADAPTER *ioc)
+ {
+ Mpi26DriverTriggerPage0_t trigger_pg0;
+ int r = 0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+
+ r = mpt3sas_config_get_driver_trigger_pg0(ioc, &mpi_reply,
+ &trigger_pg0);
+ if (r)
+ return -EFAULT;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ return -EFAULT;
+
+ return le16_to_cpu(trigger_pg0.TriggerFlags);
+ }
+
+ /**
+ * _base_get_diag_triggers - Retrieve diag trigger values from
+ * persistent pages.
+ * @ioc : per adapter object
+ *
+ * Return nothing.
+ */
+ static void
+ _base_get_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
+ {
+ u16 trigger_flags;
+
+ /*
+ * Default setting of master trigger.
+ */
+ ioc->diag_trigger_master.MasterData =
+ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
+
+ trigger_flags = _base_check_for_trigger_pages_support(ioc);
+ if (trigger_flags < 0)
+ return;
+
+ ioc->supports_trigger_pages = 1;
+
+ /*
+ * Retrieve master diag trigger values from driver trigger pg1
+ * if master trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID)
+ _base_get_master_diag_triggers(ioc);
+
+ /*
+ * Retrieve event diag trigger values from driver trigger pg2
+ * if event trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID)
+ _base_get_event_diag_triggers(ioc);
+
+ /*
+ * Retrieve scsi diag trigger values from driver trigger pg3
+ * if scsi trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID)
+ _base_get_scsi_diag_triggers(ioc);
+ /*
+ * Retrieve mpi error diag trigger values from driver trigger pg4
+ * if loginfo trigger bit enabled in TriggerFlags.
+ */
+ if ((u16)trigger_flags &
+ MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID)
+ _base_get_mpi_diag_triggers(ioc);
+ }
+
+ /**
+ * _base_update_diag_trigger_pages - Update the driver trigger pages after
+ * online FW update, incase updated FW supports driver
+ * trigger pages.
+ * @ioc : per adapter object
+ *
+ * Return nothing.
+ */
+ static void
+ _base_update_diag_trigger_pages(struct MPT3SAS_ADAPTER *ioc)
+ {
+
+ if (ioc->diag_trigger_master.MasterData)
+ mpt3sas_config_update_driver_trigger_pg1(ioc,
+ &ioc->diag_trigger_master, 1);
+
+ if (ioc->diag_trigger_event.ValidEntries)
+ mpt3sas_config_update_driver_trigger_pg2(ioc,
+ &ioc->diag_trigger_event, 1);
+
+ if (ioc->diag_trigger_scsi.ValidEntries)
+ mpt3sas_config_update_driver_trigger_pg3(ioc,
+ &ioc->diag_trigger_scsi, 1);
+
+ if (ioc->diag_trigger_mpi.ValidEntries)
+ mpt3sas_config_update_driver_trigger_pg4(ioc,
+ &ioc->diag_trigger_mpi, 1);
+ }
+
+ /**
* _base_static_config_pages - static start of day config pages
* @ioc: per adapter object
*/
{
Mpi2ConfigReply_t mpi_reply;
u32 iounit_pg1_flags;
-
+ int tg_flags = 0;
ioc->nvme_abort_timeout = 30;
mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0);
if (ioc->ir_firmware)
else
ioc->nvme_abort_timeout = ioc->manu_pg11.NVMeAbortTO;
}
-
+ ioc->time_sync_interval =
+ ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_MASK;
+ if (ioc->time_sync_interval) {
+ if (ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_UNIT_MASK)
+ ioc->time_sync_interval =
+ ioc->time_sync_interval * SECONDS_PER_HOUR;
+ else
+ ioc->time_sync_interval =
+ ioc->time_sync_interval * SECONDS_PER_MIN;
+ dinitprintk(ioc, ioc_info(ioc,
+ "Driver-FW TimeSync interval is %d seconds. ManuPg11 TimeSync Unit is in %s\n",
+ ioc->time_sync_interval, (ioc->manu_pg11.TimeSyncInterval &
+ MPT3SAS_TIMESYNC_UNIT_MASK) ? "Hour" : "Minute"));
+ } else {
+ if (ioc->is_gen35_ioc)
+ ioc_warn(ioc,
+ "TimeSync Interval in Manuf page-11 is not enabled. Periodic Time-Sync will be disabled\n");
+ }
mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2);
mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3);
mpt3sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8);
ioc->temp_sensors_count = ioc->iounit_pg8.NumSensors;
if (ioc->is_aero_ioc)
_base_update_ioc_page1_inlinewith_perf_mode(ioc);
+ if (ioc->is_gen35_ioc) {
+ if (ioc->is_driver_loading)
+ _base_get_diag_triggers(ioc);
+ else {
+ /*
+ * In case of online HBA FW update operation,
+ * check whether updated FW supports the driver trigger
+ * pages or not.
+ * - If previous FW has not supported driver trigger
+ * pages and newer FW supports them then update these
+ * pages with current diag trigger values.
+ * - If previous FW has supported driver trigger pages
+ * and new FW doesn't support them then disable
+ * support_trigger_pages flag.
+ */
+ tg_flags = _base_check_for_trigger_pages_support(ioc);
+ if (!ioc->supports_trigger_pages && tg_flags != -EFAULT)
+ _base_update_diag_trigger_pages(ioc);
+ else if (ioc->supports_trigger_pages &&
+ tg_flags == -EFAULT)
+ ioc->supports_trigger_pages = 0;
+ }
+ }
}
/**
r = _base_handshake_req_reply_wait(ioc,
sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request,
- sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10);
+ sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 30);
if (r != 0) {
ioc_err(ioc, "%s: handshake failed (r=%d)\n", __func__, r);
r = -EIO;
}
+ /* Reset TimeSync Counter*/
+ ioc->timestamp_update_count = 0;
return r;
}
Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL;
struct _pcie_device *pcie_device = NULL;
u16 smid;
- u8 timeout;
+ unsigned long timeout;
u8 issue_reset;
u32 sz, sz_arg;
void *psge;
(Mpi2SmpPassthroughRequest_t *)mpi_request;
u8 *data;
- /* ioc determines which port to use */
- smp_request->PhysicalPort = 0xFF;
+ if (!ioc->multipath_on_hba) {
+ /* ioc determines which port to use */
+ smp_request->PhysicalPort = 0xFF;
+ }
if (smp_request->PassthroughFlags &
MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE)
data = (u8 *)&smp_request->SGL;
static inline int
qla27xx_verify_template_checksum(struct qla27xx_fwdt_template *tmp)
{
- return qla27xx_template_checksum(tmp, tmp->template_size) == 0;
+ return qla27xx_template_checksum(tmp,
+ le32_to_cpu(tmp->template_size)) == 0;
}
static inline int
ulong len = 0;
if (qla27xx_fwdt_template_valid(tmp)) {
- len = tmp->template_size;
+ len = le32_to_cpu(tmp->template_size);
tmp = memcpy(buf, tmp, len);
ql27xx_edit_template(vha, tmp);
qla27xx_walk_template(vha, tmp, buf, &len);
ulong len = 0;
if (qla27xx_fwdt_template_valid(tmp)) {
- len = tmp->template_size;
+ len = le32_to_cpu(tmp->template_size);
qla27xx_walk_template(vha, tmp, NULL, &len);
}
{
struct qla27xx_fwdt_template *tmp = p;
- return tmp->template_size;
+ return le32_to_cpu(tmp->template_size);
}
int
{
ulong flags = 0;
-#ifndef __CHECKER__
if (!hardware_locked)
spin_lock_irqsave(&vha->hw->hardware_lock, flags);
-#endif
if (!vha->hw->mpi_fw_dump) {
ql_log(ql_log_warn, vha, 0x02f3, "-> mpi_fwdump no buffer\n");
} else {
}
bailout:
-#ifndef __CHECKER__
if (!hardware_locked)
spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
-#endif
}
void
case 0x24: /* depopulation in progress */
action = ACTION_DELAYED_RETRY;
break;
+ case 0x0a: /* ALUA state transition */
+ blk_stat = BLK_STS_AGAIN;
+ fallthrough;
default:
action = ACTION_FAIL;
break;
}
/**
- * scsi_dispatch_command - Dispatch a command to the low-level driver.
+ * scsi_dispatch_cmd - Dispatch a command to the low-level driver.
* @cmd: command block we are dispatching.
*
* Return: nonzero return request was rejected and device's queue needs to be
break;
case BLK_STS_RESOURCE:
case BLK_STS_ZONE_RESOURCE:
- if (atomic_read(&sdev->device_busy) ||
- scsi_device_blocked(sdev))
+ if (scsi_device_blocked(sdev))
ret = BLK_STS_DEV_RESOURCE;
break;
+ case BLK_STS_AGAIN:
+ scsi_req(req)->result = DID_BUS_BUSY << 16;
+ if (req->rq_flags & RQF_DONTPREP)
+ scsi_mq_uninit_cmd(cmd);
+ break;
default:
if (unlikely(!scsi_device_online(sdev)))
scsi_req(req)->result = DID_NO_CONNECT << 16;
EXPORT_SYMBOL(scsi_device_set_state);
/**
- * sdev_evt_emit - emit a single SCSI device uevent
+ * scsi_evt_emit - emit a single SCSI device uevent
* @sdev: associated SCSI device
* @evt: event to emit
*
}
/**
- * sdev_evt_thread - send a uevent for each scsi event
+ * scsi_evt_thread - send a uevent for each scsi event
* @work: work struct for scsi_device
*
* Dispatch queued events to their associated scsi_device kobjects
}
EXPORT_SYMBOL(sdev_enable_disk_events);
+ static unsigned char designator_prio(const unsigned char *d)
+ {
+ if (d[1] & 0x30)
+ /* not associated with LUN */
+ return 0;
+
+ if (d[3] == 0)
+ /* invalid length */
+ return 0;
+
+ /*
+ * Order of preference for lun descriptor:
+ * - SCSI name string
+ * - NAA IEEE Registered Extended
+ * - EUI-64 based 16-byte
+ * - EUI-64 based 12-byte
+ * - NAA IEEE Registered
+ * - NAA IEEE Extended
+ * - EUI-64 based 8-byte
+ * - SCSI name string (truncated)
+ * - T10 Vendor ID
+ * as longer descriptors reduce the likelyhood
+ * of identification clashes.
+ */
+
+ switch (d[1] & 0xf) {
+ case 8:
+ /* SCSI name string, variable-length UTF-8 */
+ return 9;
+ case 3:
+ switch (d[4] >> 4) {
+ case 6:
+ /* NAA registered extended */
+ return 8;
+ case 5:
+ /* NAA registered */
+ return 5;
+ case 4:
+ /* NAA extended */
+ return 4;
+ case 3:
+ /* NAA locally assigned */
+ return 1;
+ default:
+ break;
+ }
+ break;
+ case 2:
+ switch (d[3]) {
+ case 16:
+ /* EUI64-based, 16 byte */
+ return 7;
+ case 12:
+ /* EUI64-based, 12 byte */
+ return 6;
+ case 8:
+ /* EUI64-based, 8 byte */
+ return 3;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ /* T10 vendor ID */
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+ }
+
/**
* scsi_vpd_lun_id - return a unique device identification
* @sdev: SCSI device
*/
int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
{
- u8 cur_id_type = 0xff;
+ u8 cur_id_prio = 0;
u8 cur_id_size = 0;
const unsigned char *d, *cur_id_str;
const struct scsi_vpd *vpd_pg83;
return -ENXIO;
}
- /*
- * Look for the correct descriptor.
- * Order of preference for lun descriptor:
- * - SCSI name string
- * - NAA IEEE Registered Extended
- * - EUI-64 based 16-byte
- * - EUI-64 based 12-byte
- * - NAA IEEE Registered
- * - NAA IEEE Extended
- * - T10 Vendor ID
- * as longer descriptors reduce the likelyhood
- * of identification clashes.
- */
-
/* The id string must be at least 20 bytes + terminating NULL byte */
if (id_len < 21) {
rcu_read_unlock();
}
memset(id, 0, id_len);
- d = vpd_pg83->data + 4;
- while (d < vpd_pg83->data + vpd_pg83->len) {
- /* Skip designators not referring to the LUN */
- if ((d[1] & 0x30) != 0x00)
- goto next_desig;
+ for (d = vpd_pg83->data + 4;
+ d < vpd_pg83->data + vpd_pg83->len;
+ d += d[3] + 4) {
+ u8 prio = designator_prio(d);
+
+ if (prio == 0 || cur_id_prio > prio)
+ continue;
switch (d[1] & 0xf) {
case 0x1:
/* T10 Vendor ID */
if (cur_id_size > d[3])
break;
- /* Prefer anything */
- if (cur_id_type > 0x01 && cur_id_type != 0xff)
- break;
+ cur_id_prio = prio;
cur_id_size = d[3];
if (cur_id_size + 4 > id_len)
cur_id_size = id_len - 4;
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
id_size = snprintf(id, id_len, "t10.%*pE",
cur_id_size, cur_id_str);
break;
case 0x2:
/* EUI-64 */
- if (cur_id_size > d[3])
- break;
- /* Prefer NAA IEEE Registered Extended */
- if (cur_id_type == 0x3 &&
- cur_id_size == d[3])
- break;
+ cur_id_prio = prio;
cur_id_size = d[3];
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
switch (cur_id_size) {
case 8:
id_size = snprintf(id, id_len,
cur_id_str);
break;
default:
- cur_id_size = 0;
break;
}
break;
case 0x3:
/* NAA */
- if (cur_id_size > d[3])
- break;
+ cur_id_prio = prio;
cur_id_size = d[3];
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
switch (cur_id_size) {
case 8:
id_size = snprintf(id, id_len,
cur_id_str);
break;
default:
- cur_id_size = 0;
break;
}
break;
case 0x8:
/* SCSI name string */
- if (cur_id_size + 4 > d[3])
+ if (cur_id_size > d[3])
break;
/* Prefer others for truncated descriptor */
- if (cur_id_size && d[3] > id_len)
- break;
+ if (d[3] > id_len) {
+ prio = 2;
+ if (cur_id_prio > prio)
+ break;
+ }
+ cur_id_prio = prio;
cur_id_size = id_size = d[3];
cur_id_str = d + 4;
- cur_id_type = d[1] & 0xf;
if (cur_id_size >= id_len)
cur_id_size = id_len - 1;
memcpy(id, cur_id_str, cur_id_size);
- /* Decrease priority for truncated descriptor */
- if (cur_id_size != id_size)
- cur_id_size = 6;
break;
default:
break;
}
- next_desig:
- d += d[3] + 4;
}
rcu_read_unlock();
{UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE},
{UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE},
{UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE},
+ /*
+ * For DeepSleep, the link is first put in hibern8 and then off.
+ * Leaving the link in hibern8 is not supported.
+ */
+ {UFS_DEEPSLEEP_PWR_MODE, UIC_LINK_OFF_STATE},
};
static inline enum ufs_dev_pwr_mode
static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
static void ufshcd_hba_exit(struct ufs_hba *hba);
static int ufshcd_probe_hba(struct ufs_hba *hba, bool async);
- static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
- bool skip_ref_clk);
static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable);
static int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set);
static inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable);
+ static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba);
+ static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba);
static inline bool ufshcd_valid_tag(struct ufs_hba *hba, int tag)
{
unsigned int tag, const char *str)
{
sector_t lba = -1;
- u8 opcode = 0;
+ u8 opcode = 0, group_id = 0;
u32 intr, doorbell;
struct ufshcd_lrb *lrbp = &hba->lrb[tag];
struct scsi_cmnd *cmd = lrbp->cmd;
lba = cmd->request->bio->bi_iter.bi_sector;
transfer_len = be32_to_cpu(
lrbp->ucd_req_ptr->sc.exp_data_transfer_len);
+ if (opcode == WRITE_10)
+ group_id = lrbp->cmd->cmnd[6];
+ } else if (opcode == UNMAP) {
+ if (cmd->request) {
+ lba = scsi_get_lba(cmd);
+ transfer_len = blk_rq_bytes(cmd->request);
+ }
}
}
intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
trace_ufshcd_command(dev_name(hba->dev), str, tag,
- doorbell, transfer_len, intr, lba, opcode);
+ doorbell, transfer_len, intr, lba, opcode, group_id);
}
static void ufshcd_print_clk_freqs(struct ufs_hba *hba)
}
}
- static void ufshcd_print_err_hist(struct ufs_hba *hba,
- struct ufs_err_reg_hist *err_hist,
- char *err_name)
+ static void ufshcd_print_evt(struct ufs_hba *hba, u32 id,
+ char *err_name)
{
int i;
bool found = false;
+ struct ufs_event_hist *e;
- for (i = 0; i < UFS_ERR_REG_HIST_LENGTH; i++) {
- int p = (i + err_hist->pos) % UFS_ERR_REG_HIST_LENGTH;
+ if (id >= UFS_EVT_CNT)
+ return;
- if (err_hist->tstamp[p] == 0)
+ e = &hba->ufs_stats.event[id];
+
+ for (i = 0; i < UFS_EVENT_HIST_LENGTH; i++) {
+ int p = (i + e->pos) % UFS_EVENT_HIST_LENGTH;
+
+ if (e->tstamp[p] == 0)
continue;
dev_err(hba->dev, "%s[%d] = 0x%x at %lld us\n", err_name, p,
- err_hist->reg[p], ktime_to_us(err_hist->tstamp[p]));
+ e->val[p], ktime_to_us(e->tstamp[p]));
found = true;
}
dev_err(hba->dev, "No record of %s\n", err_name);
}
- static void ufshcd_print_host_regs(struct ufs_hba *hba)
+ static void ufshcd_print_evt_hist(struct ufs_hba *hba)
{
ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "host_regs: ");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.pa_err, "pa_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.dl_err, "dl_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.nl_err, "nl_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.tl_err, "tl_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.dme_err, "dme_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.auto_hibern8_err,
- "auto_hibern8_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.fatal_err, "fatal_err");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.link_startup_err,
- "link_startup_fail");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.resume_err, "resume_fail");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.suspend_err,
- "suspend_fail");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.dev_reset, "dev_reset");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.host_reset, "host_reset");
- ufshcd_print_err_hist(hba, &hba->ufs_stats.task_abort, "task_abort");
+ ufshcd_print_evt(hba, UFS_EVT_PA_ERR, "pa_err");
+ ufshcd_print_evt(hba, UFS_EVT_DL_ERR, "dl_err");
+ ufshcd_print_evt(hba, UFS_EVT_NL_ERR, "nl_err");
+ ufshcd_print_evt(hba, UFS_EVT_TL_ERR, "tl_err");
+ ufshcd_print_evt(hba, UFS_EVT_DME_ERR, "dme_err");
+ ufshcd_print_evt(hba, UFS_EVT_AUTO_HIBERN8_ERR,
+ "auto_hibern8_err");
+ ufshcd_print_evt(hba, UFS_EVT_FATAL_ERR, "fatal_err");
+ ufshcd_print_evt(hba, UFS_EVT_LINK_STARTUP_FAIL,
+ "link_startup_fail");
+ ufshcd_print_evt(hba, UFS_EVT_RESUME_ERR, "resume_fail");
+ ufshcd_print_evt(hba, UFS_EVT_SUSPEND_ERR,
+ "suspend_fail");
+ ufshcd_print_evt(hba, UFS_EVT_DEV_RESET, "dev_reset");
+ ufshcd_print_evt(hba, UFS_EVT_HOST_RESET, "host_reset");
+ ufshcd_print_evt(hba, UFS_EVT_ABORT, "task_abort");
ufshcd_vops_dbg_register_dump(hba);
}
*/
static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
{
- #define UFS_MIN_GEAR_TO_SCALE_DOWN UFS_HS_G1
int ret = 0;
struct ufs_pa_layer_attr new_pwr_info;
memcpy(&new_pwr_info, &hba->pwr_info,
sizeof(struct ufs_pa_layer_attr));
- if (hba->pwr_info.gear_tx > UFS_MIN_GEAR_TO_SCALE_DOWN
- || hba->pwr_info.gear_rx > UFS_MIN_GEAR_TO_SCALE_DOWN) {
+ if (hba->pwr_info.gear_tx > hba->clk_scaling.min_gear ||
+ hba->pwr_info.gear_rx > hba->clk_scaling.min_gear) {
/* save the current power mode */
memcpy(&hba->clk_scaling.saved_pwr_info.info,
&hba->pwr_info,
sizeof(struct ufs_pa_layer_attr));
/* scale down gear */
- new_pwr_info.gear_tx = UFS_MIN_GEAR_TO_SCALE_DOWN;
- new_pwr_info.gear_rx = UFS_MIN_GEAR_TO_SCALE_DOWN;
+ new_pwr_info.gear_tx = hba->clk_scaling.min_gear;
+ new_pwr_info.gear_rx = hba->clk_scaling.min_gear;
}
}
}
spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
+ pm_runtime_get_noresume(hba->dev);
+ if (!pm_runtime_active(hba->dev)) {
+ pm_runtime_put_noidle(hba->dev);
+ ret = -EAGAIN;
+ goto out;
+ }
start = ktime_get();
ret = ufshcd_devfreq_scale(hba, scale_up);
+ pm_runtime_put(hba->dev);
trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
(scale_up ? "up" : "down"),
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
+ ufshcd_hba_vreg_set_hpm(hba);
ufshcd_setup_clocks(hba, true);
ufshcd_enable_irq(hba);
*/
fallthrough;
case CLKS_OFF:
- ufshcd_scsi_block_requests(hba);
hba->clk_gating.state = REQ_CLKS_ON;
trace_ufshcd_clk_gating(dev_name(hba->dev),
hba->clk_gating.state);
- queue_work(hba->clk_gating.clk_gating_workq,
- &hba->clk_gating.ungate_work);
+ if (queue_work(hba->clk_gating.clk_gating_workq,
+ &hba->clk_gating.ungate_work))
+ ufshcd_scsi_block_requests(hba);
/*
* fall through to check if we should wait for this
* work to be done or not.
ufshcd_disable_irq(hba);
- if (!ufshcd_is_link_active(hba))
- ufshcd_setup_clocks(hba, false);
- else
- /* If link is active, device ref_clk can't be switched off */
- __ufshcd_setup_clocks(hba, false, true);
+ ufshcd_setup_clocks(hba, false);
+ /* Put the host controller in low power mode if possible */
+ ufshcd_hba_vreg_set_lpm(hba);
/*
* In case you are here to cancel this work the gating state
* would be marked as REQ_CLKS_ON. In this case keep the state
if (hba->clk_gating.active_reqs || hba->clk_gating.is_suspended ||
hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL ||
- ufshcd_any_tag_in_use(hba) || hba->outstanding_tasks ||
- hba->active_uic_cmd || hba->uic_async_done)
+ hba->outstanding_tasks ||
+ hba->active_uic_cmd || hba->uic_async_done ||
+ hba->clk_gating.state == CLKS_OFF)
return;
hba->clk_gating.state = REQ_CLKS_OFF;
return -EINVAL;
value = !!value;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
if (value == hba->clk_gating.is_enabled)
goto out;
- if (value) {
- ufshcd_release(hba);
- } else {
- spin_lock_irqsave(hba->host->host_lock, flags);
+ if (value)
+ __ufshcd_release(hba);
+ else
hba->clk_gating.active_reqs++;
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- }
hba->clk_gating.is_enabled = value;
out:
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
return count;
}
if (!ufshcd_is_clkscaling_supported(hba))
return;
+ if (!hba->clk_scaling.min_gear)
+ hba->clk_scaling.min_gear = UFS_HS_G1;
+
INIT_WORK(&hba->clk_scaling.suspend_work,
ufshcd_clk_scaling_suspend_work);
INIT_WORK(&hba->clk_scaling.resume_work,
snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d",
hba->host->host_no);
hba->clk_gating.clk_gating_workq = alloc_ordered_workqueue(wq_name,
- WQ_MEM_RECLAIM);
+ WQ_MEM_RECLAIM | WQ_HIGHPRI);
hba->clk_gating.is_enabled = true;
unsigned long flags;
if (wait_for_completion_timeout(&uic_cmd->done,
- msecs_to_jiffies(UIC_CMD_TIMEOUT)))
+ msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
- else
+ } else {
ret = -ETIMEDOUT;
+ dev_err(hba->dev,
+ "uic cmd 0x%x with arg3 0x%x completion timeout\n",
+ uic_cmd->command, uic_cmd->argument3);
+
+ if (!uic_cmd->cmd_active) {
+ dev_err(hba->dev, "%s: UIC cmd has been completed, return the result\n",
+ __func__);
+ ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
+ }
+ }
spin_lock_irqsave(hba->host->host_lock, flags);
hba->active_uic_cmd = NULL;
if (completion)
init_completion(&uic_cmd->done);
+ uic_cmd->cmd_active = 1;
ufshcd_dispatch_uic_cmd(hba, uic_cmd);
return 0;
(hba->clk_gating.state != CLKS_ON));
lrbp = &hba->lrb[tag];
+ if (unlikely(lrbp->in_use)) {
+ if (hba->pm_op_in_progress)
+ set_host_byte(cmd, DID_BAD_TARGET);
+ else
+ err = SCSI_MLQUEUE_HOST_BUSY;
+ ufshcd_release(hba);
+ goto out;
+ }
WARN_ON(lrbp->cmd);
lrbp->cmd = cmd;
init_completion(&wait);
lrbp = &hba->lrb[tag];
+ if (unlikely(lrbp->in_use)) {
+ err = -EBUSY;
+ goto out;
+ }
+
WARN_ON(lrbp->cmd);
err = ufshcd_compose_dev_cmd(hba, lrbp, cmd_type, tag);
if (unlikely(err))
err = ufshcd_wait_for_dev_cmd(hba, lrbp, timeout);
+ out:
ufshcd_add_query_upiu_trace(hba, tag,
err ? "query_complete_err" : "query_complete");
BUG_ON(!hba);
- ufshcd_hold(hba, false);
if (!attr_val) {
dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n",
__func__, opcode);
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
+ ufshcd_hold(hba, false);
+
mutex_lock(&hba->dev_cmd.lock);
ufshcd_init_query(hba, &request, &response, opcode, idn, index,
selector);
out_unlock:
mutex_unlock(&hba->dev_cmd.lock);
- out:
ufshcd_release(hba);
return err;
}
BUG_ON(!hba);
- ufshcd_hold(hba, false);
if (!desc_buf) {
dev_err(hba->dev, "%s: descriptor buffer required for opcode 0x%x\n",
__func__, opcode);
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
if (*buf_len < QUERY_DESC_MIN_SIZE || *buf_len > QUERY_DESC_MAX_SIZE) {
dev_err(hba->dev, "%s: descriptor buffer size (%d) is out of range\n",
__func__, *buf_len);
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
+ ufshcd_hold(hba, false);
+
mutex_lock(&hba->dev_cmd.lock);
ufshcd_init_query(hba, &request, &response, opcode, idn, index,
selector);
out_unlock:
hba->dev_cmd.query.descriptor = NULL;
mutex_unlock(&hba->dev_cmd.lock);
- out:
ufshcd_release(hba);
return err;
}
/* Get the length of descriptor */
ufshcd_map_desc_id_to_length(hba, desc_id, &buff_len);
if (!buff_len) {
- dev_err(hba->dev, "%s: Failed to get desc length", __func__);
+ dev_err(hba->dev, "%s: Failed to get desc length\n", __func__);
+ return -EINVAL;
+ }
+
+ if (param_offset >= buff_len) {
+ dev_err(hba->dev, "%s: Invalid offset 0x%x in descriptor IDN 0x%x, length 0x%x\n",
+ __func__, param_offset, desc_id, buff_len);
return -EINVAL;
}
/* Check whether we need temp memory */
if (param_offset != 0 || param_size < buff_len) {
- desc_buf = kmalloc(buff_len, GFP_KERNEL);
+ desc_buf = kzalloc(buff_len, GFP_KERNEL);
if (!desc_buf)
return -ENOMEM;
} else {
desc_buf, &buff_len);
if (ret) {
- dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d",
+ dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d\n",
__func__, desc_id, desc_index, param_offset, ret);
goto out;
}
/* Sanity check */
if (desc_buf[QUERY_DESC_DESC_TYPE_OFFSET] != desc_id) {
- dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header",
+ dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header\n",
__func__, desc_buf[QUERY_DESC_DESC_TYPE_OFFSET]);
ret = -EINVAL;
goto out;
buff_len = desc_buf[QUERY_DESC_LENGTH_OFFSET];
ufshcd_update_desc_length(hba, desc_id, desc_index, buff_len);
- /* Check wherher we will not copy more data, than available */
- if (is_kmalloc && (param_offset + param_size) > buff_len)
- param_size = buff_len - param_offset;
-
- if (is_kmalloc)
+ if (is_kmalloc) {
+ /* Make sure we don't copy more data than available */
+ if (param_offset + param_size > buff_len)
+ param_size = buff_len - param_offset;
memcpy(param_read_buf, &desc_buf[param_offset], param_size);
+ }
out:
if (is_kmalloc)
kfree(desc_buf);
return ret;
}
+ int ufshcd_dme_configure_adapt(struct ufs_hba *hba,
+ int agreed_gear,
+ int adapt_val)
+ {
+ int ret;
+
+ if (agreed_gear != UFS_HS_G4)
+ adapt_val = PA_NO_ADAPT;
+
+ ret = ufshcd_dme_set(hba,
+ UIC_ARG_MIB(PA_TXHSADAPTTYPE),
+ adapt_val);
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(ufshcd_dme_configure_adapt);
+
/**
* ufshcd_dme_enable - UIC command for DME_ENABLE
* @hba: per adapter instance
dev_err(hba->dev,
"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n",
cmd->command, cmd->argument3);
+
+ if (!cmd->cmd_active) {
+ dev_err(hba->dev, "%s: Power Mode Change operation has been completed, go check UPMCRS\n",
+ __func__);
+ goto check_upmcrs;
+ }
+
ret = -ETIMEDOUT;
goto out;
}
+check_upmcrs:
status = ufshcd_get_upmcrs(hba);
if (status != PWR_LOCAL) {
dev_err(hba->dev,
if (ret) {
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
- ufshcd_print_host_regs(hba);
+ ufshcd_print_evt_hist(hba);
}
spin_lock_irqsave(hba->host->host_lock, flags);
*/
static int ufshcd_hba_execute_hce(struct ufs_hba *hba)
{
- int retry;
+ int retry_outer = 3;
+ int retry_inner;
+ start:
if (!ufshcd_is_hba_active(hba))
/* change controller state to "reset state" */
ufshcd_hba_stop(hba);
ufshcd_delay_us(hba->vps->hba_enable_delay_us, 100);
/* wait for the host controller to complete initialization */
- retry = 50;
+ retry_inner = 50;
while (ufshcd_is_hba_active(hba)) {
- if (retry) {
- retry--;
+ if (retry_inner) {
+ retry_inner--;
} else {
dev_err(hba->dev,
"Controller enable failed\n");
+ if (retry_outer) {
+ retry_outer--;
+ goto start;
+ }
return -EIO;
}
usleep_range(1000, 1100);
return ufshcd_disable_tx_lcc(hba, true);
}
- void ufshcd_update_reg_hist(struct ufs_err_reg_hist *reg_hist,
- u32 reg)
+ void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val)
{
- reg_hist->reg[reg_hist->pos] = reg;
- reg_hist->tstamp[reg_hist->pos] = ktime_get();
- reg_hist->pos = (reg_hist->pos + 1) % UFS_ERR_REG_HIST_LENGTH;
+ struct ufs_event_hist *e;
+
+ if (id >= UFS_EVT_CNT)
+ return;
+
+ e = &hba->ufs_stats.event[id];
+ e->val[e->pos] = val;
+ e->tstamp[e->pos] = ktime_get();
+ e->pos = (e->pos + 1) % UFS_EVENT_HIST_LENGTH;
+
+ ufshcd_vops_event_notify(hba, id, &val);
}
- EXPORT_SYMBOL_GPL(ufshcd_update_reg_hist);
+ EXPORT_SYMBOL_GPL(ufshcd_update_evt_hist);
/**
* ufshcd_link_startup - Initialize unipro link startup
/* check if device is detected by inter-connect layer */
if (!ret && !ufshcd_is_device_present(hba)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.link_startup_err,
+ ufshcd_update_evt_hist(hba,
+ UFS_EVT_LINK_STARTUP_FAIL,
0);
dev_err(hba->dev, "%s: Device not present\n", __func__);
ret = -ENXIO;
* succeeds. So reset the local Uni-Pro and try again.
*/
if (ret && ufshcd_hba_enable(hba)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.link_startup_err,
+ ufshcd_update_evt_hist(hba,
+ UFS_EVT_LINK_STARTUP_FAIL,
(u32)ret);
goto out;
}
if (ret) {
/* failed to get the link up... retire */
- ufshcd_update_reg_hist(&hba->ufs_stats.link_startup_err,
+ ufshcd_update_evt_hist(hba,
+ UFS_EVT_LINK_STARTUP_FAIL,
(u32)ret);
goto out;
}
dev_err(hba->dev, "link startup failed %d\n", ret);
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
- ufshcd_print_host_regs(hba);
+ ufshcd_print_evt_hist(hba);
}
return ret;
}
dev_err(hba->dev,
"OCS error from controller = %x for tag %d\n",
ocs, lrbp->task_tag);
- ufshcd_print_host_regs(hba);
+ ufshcd_print_evt_hist(hba);
ufshcd_print_host_state(hba);
break;
} /* end of switch */
ufshcd_get_uic_cmd_result(hba);
hba->active_uic_cmd->argument3 =
ufshcd_get_dme_attr_val(hba);
+ if (!hba->uic_async_done)
+ hba->active_uic_cmd->cmd_active = 0;
complete(&hba->active_uic_cmd->done);
retval = IRQ_HANDLED;
}
if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) {
+ hba->active_uic_cmd->cmd_active = 0;
complete(hba->uic_async_done);
retval = IRQ_HANDLED;
}
struct scsi_cmnd *cmd;
int result;
int index;
+ bool update_scaling = false;
for_each_set_bit(index, &completed_reqs, hba->nutrs) {
lrbp = &hba->lrb[index];
+ lrbp->in_use = false;
lrbp->compl_time_stamp = ktime_get();
cmd = lrbp->cmd;
if (cmd) {
/* Do not touch lrbp after scsi done */
cmd->scsi_done(cmd);
__ufshcd_release(hba);
+ update_scaling = true;
} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE ||
lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) {
if (hba->dev_cmd.complete) {
ufshcd_add_command_trace(hba, index,
"dev_complete");
complete(hba->dev_cmd.complete);
+ update_scaling = true;
}
}
- if (ufshcd_is_clkscaling_supported(hba))
+ if (ufshcd_is_clkscaling_supported(hba) && update_scaling)
hba->clk_scaling.active_reqs--;
}
static void ufshcd_err_handling_prepare(struct ufs_hba *hba)
{
pm_runtime_get_sync(hba->dev);
- if (pm_runtime_suspended(hba->dev)) {
+ if (pm_runtime_status_suspended(hba->dev) || hba->is_sys_suspended) {
+ enum ufs_pm_op pm_op;
+
/*
* Don't assume anything of pm_runtime_get_sync(), if
* resume fails, irq and clocks can be OFF, and powers
if (!ufshcd_is_clkgating_allowed(hba))
ufshcd_setup_clocks(hba, true);
ufshcd_release(hba);
- ufshcd_vops_resume(hba, UFS_RUNTIME_PM);
+ pm_op = hba->is_sys_suspended ? UFS_SYSTEM_PM : UFS_RUNTIME_PM;
+ ufshcd_vops_resume(hba, pm_op);
} else {
ufshcd_hold(hba, false);
if (hba->clk_scaling.is_allowed) {
static inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)
{
- return (hba->ufshcd_state == UFSHCD_STATE_ERROR ||
+ return (!hba->is_powered || hba->ufshcd_state == UFSHCD_STATE_ERROR ||
(!(hba->saved_err || hba->saved_uic_err || hba->force_reset ||
ufshcd_is_link_broken(hba))));
}
struct request_queue *q;
int ret;
+ hba->is_sys_suspended = false;
/*
* Set RPM status of hba device to RPM_ACTIVE,
* this also clears its runtime error.
hba = container_of(work, struct ufs_hba, eh_work);
+ down(&hba->eh_sem);
spin_lock_irqsave(hba->host->host_lock, flags);
if (ufshcd_err_handling_should_stop(hba)) {
if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
spin_unlock_irqrestore(hba->host->host_lock, flags);
+ up(&hba->eh_sem);
return;
}
ufshcd_set_eh_in_progress(hba);
ufshcd_err_handling_prepare(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
ufshcd_scsi_block_requests(hba);
- /*
- * A full reset and restore might have happened after preparation
- * is finished, double check whether we should stop.
- */
- if (ufshcd_err_handling_should_stop(hba)) {
- if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
- hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
- goto out;
- }
hba->ufshcd_state = UFSHCD_STATE_RESET;
/* Complete requests that have door-bell cleared by h/w */
ufshcd_complete_requests(hba);
+ /*
+ * A full reset and restore might have happened after preparation
+ * is finished, double check whether we should stop.
+ */
+ if (ufshcd_err_handling_should_stop(hba))
+ goto skip_err_handling;
+
if (hba->dev_quirks & UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
bool ret;
/* release the lock as ufshcd_quirk_dl_nac_errors() may sleep */
ret = ufshcd_quirk_dl_nac_errors(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
- if (!ret && !hba->force_reset && ufshcd_is_link_active(hba))
+ if (!ret && ufshcd_err_handling_should_stop(hba))
goto skip_err_handling;
}
- if (hba->force_reset || ufshcd_is_link_broken(hba) ||
- ufshcd_is_saved_err_fatal(hba) ||
- ((hba->saved_err & UIC_ERROR) &&
- (hba->saved_uic_err & (UFSHCD_UIC_DL_NAC_RECEIVED_ERROR |
- UFSHCD_UIC_DL_TCx_REPLAY_ERROR))))
- needs_reset = true;
-
if ((hba->saved_err & (INT_FATAL_ERRORS | UFSHCD_UIC_HIBERN8_MASK)) ||
(hba->saved_uic_err &&
(hba->saved_uic_err != UFSHCD_UIC_PA_GENERIC_ERROR))) {
spin_unlock_irqrestore(hba->host->host_lock, flags);
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
- ufshcd_print_host_regs(hba);
+ ufshcd_print_evt_hist(hba);
ufshcd_print_tmrs(hba, hba->outstanding_tasks);
ufshcd_print_trs(hba, hba->outstanding_reqs, pr_prdt);
spin_lock_irqsave(hba->host->host_lock, flags);
* transfers forcefully because they will get cleared during
* host reset and restore
*/
- if (needs_reset)
+ if (hba->force_reset || ufshcd_is_link_broken(hba) ||
+ ufshcd_is_saved_err_fatal(hba) ||
+ ((hba->saved_err & UIC_ERROR) &&
+ (hba->saved_uic_err & (UFSHCD_UIC_DL_NAC_RECEIVED_ERROR |
+ UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))) {
+ needs_reset = true;
goto do_reset;
+ }
/*
* If LINERESET was caught, UFS might have been put to PWM mode,
dev_err_ratelimited(hba->dev, "%s: exit: saved_err 0x%x saved_uic_err 0x%x",
__func__, hba->saved_err, hba->saved_uic_err);
}
-
- out:
ufshcd_clear_eh_in_progress(hba);
spin_unlock_irqrestore(hba->host->host_lock, flags);
ufshcd_scsi_unblock_requests(hba);
ufshcd_err_handling_unprepare(hba);
+ up(&hba->eh_sem);
}
/**
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) &&
(reg & UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.pa_err, reg);
+ ufshcd_update_evt_hist(hba, UFS_EVT_PA_ERR, reg);
/*
* To know whether this error is fatal or not, DB timeout
* must be checked but this error is handled separately.
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER);
if ((reg & UIC_DATA_LINK_LAYER_ERROR) &&
(reg & UIC_DATA_LINK_LAYER_ERROR_CODE_MASK)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.dl_err, reg);
+ ufshcd_update_evt_hist(hba, UFS_EVT_DL_ERR, reg);
if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER);
if ((reg & UIC_NETWORK_LAYER_ERROR) &&
(reg & UIC_NETWORK_LAYER_ERROR_CODE_MASK)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.nl_err, reg);
+ ufshcd_update_evt_hist(hba, UFS_EVT_NL_ERR, reg);
hba->uic_error |= UFSHCD_UIC_NL_ERROR;
retval |= IRQ_HANDLED;
}
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER);
if ((reg & UIC_TRANSPORT_LAYER_ERROR) &&
(reg & UIC_TRANSPORT_LAYER_ERROR_CODE_MASK)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.tl_err, reg);
+ ufshcd_update_evt_hist(hba, UFS_EVT_TL_ERR, reg);
hba->uic_error |= UFSHCD_UIC_TL_ERROR;
retval |= IRQ_HANDLED;
}
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME);
if ((reg & UIC_DME_ERROR) &&
(reg & UIC_DME_ERROR_CODE_MASK)) {
- ufshcd_update_reg_hist(&hba->ufs_stats.dme_err, reg);
+ ufshcd_update_evt_hist(hba, UFS_EVT_DME_ERR, reg);
hba->uic_error |= UFSHCD_UIC_DME_ERROR;
retval |= IRQ_HANDLED;
}
irqreturn_t retval = IRQ_NONE;
if (hba->errors & INT_FATAL_ERRORS) {
- ufshcd_update_reg_hist(&hba->ufs_stats.fatal_err, hba->errors);
+ ufshcd_update_evt_hist(hba, UFS_EVT_FATAL_ERR,
+ hba->errors);
queue_eh_work = true;
}
__func__, (hba->errors & UIC_HIBERNATE_ENTER) ?
"Enter" : "Exit",
hba->errors, ufshcd_get_upmcrs(hba));
- ufshcd_update_reg_hist(&hba->ufs_stats.auto_hibern8_err,
+ ufshcd_update_evt_hist(hba, UFS_EVT_AUTO_HIBERN8_ERR,
hba->errors);
ufshcd_set_link_broken(hba);
queue_eh_work = true;
hba->saved_uic_err |= hba->uic_error;
/* dump controller state before resetting */
- if ((hba->saved_err & (INT_FATAL_ERRORS)) ||
+ if ((hba->saved_err &
+ (INT_FATAL_ERRORS | UFSHCD_UIC_HIBERN8_MASK)) ||
(hba->saved_uic_err &&
(hba->saved_uic_err != UFSHCD_UIC_PA_GENERIC_ERROR))) {
dev_err(hba->dev, "%s: saved_err 0x%x saved_uic_err 0x%x\n",
init_completion(&wait);
lrbp = &hba->lrb[tag];
- WARN_ON(lrbp->cmd);
+ if (unlikely(lrbp->in_use)) {
+ err = -EBUSY;
+ goto out;
+ }
+ WARN_ON(lrbp->cmd);
lrbp->cmd = NULL;
lrbp->sense_bufflen = 0;
lrbp->sense_buffer = NULL;
}
}
+ out:
blk_put_request(req);
out_unlock:
up_read(&hba->clk_scaling_lock);
out:
hba->req_abort_count = 0;
- ufshcd_update_reg_hist(&hba->ufs_stats.dev_reset, (u32)err);
+ ufshcd_update_evt_hist(hba, UFS_EVT_DEV_RESET, (u32)err);
if (!err) {
err = SUCCESS;
} else {
/**
* ufshcd_try_to_abort_task - abort a specific task
- * @cmd: SCSI command pointer
+ * @hba: Pointer to adapter instance
+ * @tag: Task tag/index to be aborted
*
* Abort the pending command in device by sending UFS_ABORT_TASK task management
* command, and in host controller by clearing the door-bell register. There can
BUG();
}
- /*
- * Task abort to the device W-LUN is illegal. When this command
- * will fail, due to spec violation, scsi err handling next step
- * will be to send LU reset which, again, is a spec violation.
- * To avoid these unnecessary/illegal step we skip to the last error
- * handling stage: reset and restore.
- */
- if (lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN)
- return ufshcd_eh_host_reset_handler(cmd);
-
ufshcd_hold(hba, false);
reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* If command is already aborted/completed, return SUCCESS */
* to reduce repeated printouts. For other aborted requests only print
* basic details.
*/
- scsi_print_command(hba->lrb[tag].cmd);
+ scsi_print_command(cmd);
if (!hba->req_abort_count) {
- ufshcd_update_reg_hist(&hba->ufs_stats.task_abort, 0);
- ufshcd_print_host_regs(hba);
+ ufshcd_update_evt_hist(hba, UFS_EVT_ABORT, tag);
+ ufshcd_print_evt_hist(hba);
ufshcd_print_host_state(hba);
ufshcd_print_pwr_info(hba);
ufshcd_print_trs(hba, 1 << tag, true);
goto cleanup;
}
+ /*
+ * Task abort to the device W-LUN is illegal. When this command
+ * will fail, due to spec violation, scsi err handling next step
+ * will be to send LU reset which, again, is a spec violation.
+ * To avoid these unnecessary/illegal steps, first we clean up
+ * the lrb taken by this cmd and mark the lrb as in_use, then
+ * queue the eh_work and bail.
+ */
+ if (lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN) {
+ ufshcd_update_evt_hist(hba, UFS_EVT_ABORT, lrbp->lun);
+ spin_lock_irqsave(host->host_lock, flags);
+ if (lrbp->cmd) {
+ __ufshcd_transfer_req_compl(hba, (1UL << tag));
+ __set_bit(tag, &hba->outstanding_reqs);
+ lrbp->in_use = true;
+ hba->force_reset = true;
+ ufshcd_schedule_eh_work(hba);
+ }
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+ goto out;
+ }
+
/* Skip task abort in case previous aborts failed and report failure */
if (lrbp->req_abort_skip)
err = -EIO;
out:
if (err)
dev_err(hba->dev, "%s: Host init failed %d\n", __func__, err);
- ufshcd_update_reg_hist(&hba->ufs_stats.host_reset, (u32)err);
+ ufshcd_update_evt_hist(hba, UFS_EVT_HOST_RESET, (u32)err);
return err;
}
*/
scsi_report_bus_reset(hba->host, 0);
if (err) {
+ hba->ufshcd_state = UFSHCD_STATE_ERROR;
hba->saved_err |= saved_err;
hba->saved_uic_err |= saved_uic_err;
}
static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
{
int ret = 0;
- struct scsi_device *sdev_rpmb;
struct scsi_device *sdev_boot;
hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
ufshcd_blk_pm_runtime_init(hba->sdev_ufs_device);
scsi_device_put(hba->sdev_ufs_device);
- sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
+ hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
- if (IS_ERR(sdev_rpmb)) {
- ret = PTR_ERR(sdev_rpmb);
+ if (IS_ERR(hba->sdev_rpmb)) {
+ ret = PTR_ERR(hba->sdev_rpmb);
goto remove_sdev_ufs_device;
}
- ufshcd_blk_pm_runtime_init(sdev_rpmb);
- scsi_device_put(sdev_rpmb);
+ ufshcd_blk_pm_runtime_init(hba->sdev_rpmb);
+ scsi_device_put(hba->sdev_rpmb);
sdev_boot = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
return ret;
}
+ static int
+ ufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp);
+
+ static int ufshcd_clear_ua_wlun(struct ufs_hba *hba, u8 wlun)
+ {
+ struct scsi_device *sdp;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (wlun == UFS_UPIU_UFS_DEVICE_WLUN)
+ sdp = hba->sdev_ufs_device;
+ else if (wlun == UFS_UPIU_RPMB_WLUN)
+ sdp = hba->sdev_rpmb;
+ else
+ BUG();
+ if (sdp) {
+ ret = scsi_device_get(sdp);
+ if (!ret && !scsi_device_online(sdp)) {
+ ret = -ENODEV;
+ scsi_device_put(sdp);
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (ret)
+ goto out_err;
+
+ ret = ufshcd_send_request_sense(hba, sdp);
+ scsi_device_put(sdp);
+ out_err:
+ if (ret)
+ dev_err(hba->dev, "%s: UAC clear LU=%x ret = %d\n",
+ __func__, wlun, ret);
+ return ret;
+ }
+
+ static int ufshcd_clear_ua_wluns(struct ufs_hba *hba)
+ {
+ int ret = 0;
+
+ if (!hba->wlun_dev_clr_ua)
+ goto out;
+
+ ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_UFS_DEVICE_WLUN);
+ if (!ret)
+ ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_RPMB_WLUN);
+ if (!ret)
+ hba->wlun_dev_clr_ua = false;
+ out:
+ if (ret)
+ dev_err(hba->dev, "%s: Failed to clear UAC WLUNS ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
/**
* ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance
struct ufs_hba *hba = (struct ufs_hba *)data;
int ret;
+ down(&hba->eh_sem);
/* Initialize hba, detect and initialize UFS device */
ret = ufshcd_probe_hba(hba, true);
+ up(&hba->eh_sem);
if (ret)
goto out;
pm_runtime_put_sync(hba->dev);
ufshcd_exit_clk_scaling(hba);
ufshcd_hba_exit(hba);
+ } else {
+ ufshcd_clear_ua_wluns(hba);
}
}
return 0;
}
- static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
- bool skip_ref_clk)
+ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on)
{
int ret = 0;
struct ufs_clk_info *clki;
list_for_each_entry(clki, head, list) {
if (!IS_ERR_OR_NULL(clki->clk)) {
- if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
+ /*
+ * Don't disable clocks which are needed
+ * to keep the link active.
+ */
+ if (ufshcd_is_link_active(hba) &&
+ clki->keep_link_active)
continue;
clk_state_changed = on ^ clki->enabled;
return ret;
}
- static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on)
- {
- return __ufshcd_setup_clocks(hba, on, false);
- }
-
static int ufshcd_init_clocks(struct ufs_hba *hba)
{
int ret = 0;
err = ufshcd_vops_init(hba);
if (err)
- goto out;
-
- err = ufshcd_vops_setup_regulators(hba, true);
- if (err)
- ufshcd_vops_exit(hba);
- out:
- if (err)
dev_err(hba->dev, "%s: variant %s init failed err %d\n",
__func__, ufshcd_get_var_name(hba), err);
+ out:
return err;
}
if (!hba->vops)
return;
- ufshcd_vops_setup_regulators(hba, false);
-
ufshcd_vops_exit(hba);
}
}
/*
* If autobkops is enabled, link can't be turned off because
- * turning off the link would also turn off the device.
+ * turning off the link would also turn off the device, except in the
+ * case of DeepSleep where the device is expected to remain powered.
*/
else if ((req_link_state == UIC_LINK_OFF_STATE) &&
(!check_for_bkops || !hba->auto_bkops_enabled)) {
* put the link in low power mode is to send the DME end point
* to device and then send the DME reset command to local
* unipro. But putting the link in hibern8 is much faster.
+ *
+ * Note also that putting the link in Hibern8 is a requirement
+ * for entering DeepSleep.
*/
ret = ufshcd_uic_hibern8_enter(hba);
if (ret) {
static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba)
{
- if (ufshcd_is_link_off(hba))
+ if (ufshcd_is_link_off(hba) || ufshcd_can_aggressive_pc(hba))
ufshcd_setup_hba_vreg(hba, false);
}
static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)
{
- if (ufshcd_is_link_off(hba))
+ if (ufshcd_is_link_off(hba) || ufshcd_can_aggressive_pc(hba))
ufshcd_setup_hba_vreg(hba, true);
}
static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
{
int ret = 0;
+ int check_for_bkops;
enum ufs_pm_level pm_lvl;
enum ufs_dev_pwr_mode req_dev_pwr_mode;
enum uic_link_state req_link_state;
}
if (req_dev_pwr_mode != hba->curr_dev_pwr_mode) {
- if ((ufshcd_is_runtime_pm(pm_op) && !hba->auto_bkops_enabled) ||
- !ufshcd_is_runtime_pm(pm_op)) {
+ if (!ufshcd_is_runtime_pm(pm_op))
/* ensure that bkops is disabled */
ufshcd_disable_auto_bkops(hba);
- }
if (!hba->dev_info.b_rpm_dev_flush_capable) {
ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode);
}
flush_work(&hba->eeh_work);
- ret = ufshcd_link_state_transition(hba, req_link_state, 1);
+
+ /*
+ * In the case of DeepSleep, the device is expected to remain powered
+ * with the link off, so do not check for bkops.
+ */
+ check_for_bkops = !ufshcd_is_ufs_dev_deepsleep(hba);
+ ret = ufshcd_link_state_transition(hba, req_link_state, check_for_bkops);
if (ret)
goto set_dev_active;
*/
ufshcd_disable_irq(hba);
- if (!ufshcd_is_link_active(hba))
- ufshcd_setup_clocks(hba, false);
- else
- /* If link is active, device ref_clk can't be switched off */
- __ufshcd_setup_clocks(hba, false, true);
+ ufshcd_setup_clocks(hba, false);
if (ufshcd_is_clkgating_allowed(hba)) {
hba->clk_gating.state = CLKS_OFF;
if (hba->clk_scaling.is_allowed)
ufshcd_resume_clkscaling(hba);
ufshcd_vreg_set_hpm(hba);
+ /*
+ * Device hardware reset is required to exit DeepSleep. Also, for
+ * DeepSleep, the link is off so host reset and restore will be done
+ * further below.
+ */
+ if (ufshcd_is_ufs_dev_deepsleep(hba)) {
+ ufshcd_vops_device_reset(hba);
+ WARN_ON(!ufshcd_is_link_off(hba));
+ }
if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba))
ufshcd_set_link_active(hba);
else if (ufshcd_is_link_off(hba))
ufshcd_host_reset_and_restore(hba);
set_dev_active:
+ /* Can also get here needing to exit DeepSleep */
+ if (ufshcd_is_ufs_dev_deepsleep(hba)) {
+ ufshcd_vops_device_reset(hba);
+ ufshcd_host_reset_and_restore(hba);
+ }
if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE))
ufshcd_disable_auto_bkops(hba);
enable_gating:
hba->pm_op_in_progress = 0;
if (ret)
- ufshcd_update_reg_hist(&hba->ufs_stats.suspend_err, (u32)ret);
+ ufshcd_update_evt_hist(hba, UFS_EVT_SUSPEND_ERR, (u32)ret);
return ret;
}
if (ret)
goto disable_vreg;
+ /* For DeepSleep, the only supported option is to have the link off */
+ WARN_ON(ufshcd_is_ufs_dev_deepsleep(hba) && !ufshcd_is_link_off(hba));
+
if (ufshcd_is_link_hibern8(hba)) {
ret = ufshcd_uic_hibern8_exit(hba);
if (!ret) {
/*
* A full initialization of the host and the device is
* required since the link was put to off during suspend.
+ * Note, in the case of DeepSleep, the device will exit
+ * DeepSleep due to device reset.
*/
ret = ufshcd_reset_and_restore(hba);
/*
out:
hba->pm_op_in_progress = 0;
if (ret)
- ufshcd_update_reg_hist(&hba->ufs_stats.resume_err, (u32)ret);
+ ufshcd_update_evt_hist(hba, UFS_EVT_RESUME_ERR, (u32)ret);
return ret;
}
int ret = 0;
ktime_t start = ktime_get();
+ down(&hba->eh_sem);
if (!hba || !hba->is_powered)
return 0;
hba->curr_dev_pwr_mode, hba->uic_link_state);
if (!ret)
hba->is_sys_suspended = true;
+ else
+ up(&hba->eh_sem);
return ret;
}
EXPORT_SYMBOL(ufshcd_system_suspend);
int ret = 0;
ktime_t start = ktime_get();
- if (!hba)
+ if (!hba) {
+ up(&hba->eh_sem);
return -EINVAL;
+ }
if (!hba->is_powered || pm_runtime_suspended(hba->dev))
/*
hba->curr_dev_pwr_mode, hba->uic_link_state);
if (!ret)
hba->is_sys_suspended = false;
+ up(&hba->eh_sem);
return ret;
}
EXPORT_SYMBOL(ufshcd_system_resume);
{
int ret = 0;
+ down(&hba->eh_sem);
if (!hba->is_powered)
goto out;
if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba))
goto out;
- if (pm_runtime_suspended(hba->dev)) {
- ret = ufshcd_runtime_resume(hba);
- if (ret)
- goto out;
- }
+ pm_runtime_get_sync(hba->dev);
ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM);
out:
if (ret)
dev_err(hba->dev, "%s failed, err %d\n", __func__, ret);
+ hba->is_powered = false;
+ up(&hba->eh_sem);
/* allow force shutdown even in case of errors */
return 0;
}
blk_mq_free_tag_set(&hba->tmf_tag_set);
blk_cleanup_queue(hba->cmd_queue);
scsi_remove_host(hba->host);
+ destroy_workqueue(hba->eh_wq);
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask);
ufshcd_hba_stop(hba);
INIT_WORK(&hba->eh_work, ufshcd_err_handler);
INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
+ sema_init(&hba->eh_sem, 1);
+
/* Initialize UIC command mutex */
mutex_init(&hba->uic_cmd_mutex);
err = ufshcd_hba_enable(hba);
if (err) {
dev_err(hba->dev, "Host controller enable failed\n");
- ufshcd_print_host_regs(hba);
+ ufshcd_print_evt_hist(hba);
ufshcd_print_host_state(hba);
goto free_tmf_queue;
}
exit_gating:
ufshcd_exit_clk_scaling(hba);
ufshcd_exit_clk_gating(hba);
+ destroy_workqueue(hba->eh_wq);
out_disable:
hba->is_irq_enabled = false;
ufshcd_hba_exit(hba);
DEV_CMD_TYPE_QUERY = 0x1,
};
+ enum ufs_event_type {
+ /* uic specific errors */
+ UFS_EVT_PA_ERR = 0,
+ UFS_EVT_DL_ERR,
+ UFS_EVT_NL_ERR,
+ UFS_EVT_TL_ERR,
+ UFS_EVT_DME_ERR,
+
+ /* fatal errors */
+ UFS_EVT_AUTO_HIBERN8_ERR,
+ UFS_EVT_FATAL_ERR,
+ UFS_EVT_LINK_STARTUP_FAIL,
+ UFS_EVT_RESUME_ERR,
+ UFS_EVT_SUSPEND_ERR,
+
+ /* abnormal events */
+ UFS_EVT_DEV_RESET,
+ UFS_EVT_HOST_RESET,
+ UFS_EVT_ABORT,
+
+ UFS_EVT_CNT,
+ };
+
/**
* struct uic_command - UIC command structure
* @command: UIC command
* @argument1: UIC command argument 1
* @argument2: UIC command argument 2
* @argument3: UIC command argument 3
+ * @cmd_active: Indicate if UIC command is outstanding
* @done: UIC command completion
*/
struct uic_command {
u32 argument1;
u32 argument2;
u32 argument3;
+ int cmd_active;
struct completion done;
};
((h)->curr_dev_pwr_mode = UFS_SLEEP_PWR_MODE)
#define ufshcd_set_ufs_dev_poweroff(h) \
((h)->curr_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE)
+ #define ufshcd_set_ufs_dev_deepsleep(h) \
+ ((h)->curr_dev_pwr_mode = UFS_DEEPSLEEP_PWR_MODE)
#define ufshcd_is_ufs_dev_active(h) \
((h)->curr_dev_pwr_mode == UFS_ACTIVE_PWR_MODE)
#define ufshcd_is_ufs_dev_sleep(h) \
((h)->curr_dev_pwr_mode == UFS_SLEEP_PWR_MODE)
#define ufshcd_is_ufs_dev_poweroff(h) \
((h)->curr_dev_pwr_mode == UFS_POWERDOWN_PWR_MODE)
+ #define ufshcd_is_ufs_dev_deepsleep(h) \
+ ((h)->curr_dev_pwr_mode == UFS_DEEPSLEEP_PWR_MODE)
/*
* UFS Power management levels.
- * Each level is in increasing order of power savings.
+ * Each level is in increasing order of power savings, except DeepSleep
+ * which is lower than PowerDown with power on but not PowerDown with
+ * power off.
*/
enum ufs_pm_level {
UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE */
+ UFS_PM_LVL_6, /* UFS_DEEPSLEEP_PWR_MODE, UIC_LINK_OFF_STATE */
UFS_PM_LVL_MAX
};
* @crypto_key_slot: the key slot to use for inline crypto (-1 if none)
* @data_unit_num: the data unit number for the first block for inline crypto
* @req_abort_skip: skip request abort task flag
+ * @in_use: indicates that this lrb is still in use
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
#endif
bool req_abort_skip;
+ bool in_use;
};
/**
* @max_freq: maximum frequency supported by the clock
* @min_freq: min frequency that can be used for clock scaling
* @curr_freq: indicates the current frequency that it is set to
+ * @keep_link_active: indicates that the clk should not be disabled if
+ link is active
* @enabled: variable to check against multiple enable/disable
*/
struct ufs_clk_info {
u32 max_freq;
u32 min_freq;
u32 curr_freq;
+ bool keep_link_active;
bool enabled;
};
* @get_ufs_hci_version: called to get UFS HCI version
* @clk_scale_notify: notifies that clks are scaled up/down
* @setup_clocks: called before touching any of the controller registers
- * @setup_regulators: called before accessing the host controller
* @hce_enable_notify: called before and after HCE enable bit is set to allow
* variant specific Uni-Pro initialization.
* @link_startup_notify: called before and after Link startup is carried out
* @phy_initialization: used to initialize phys
* @device_reset: called to issue a reset pulse on the UFS device
* @program_key: program or evict an inline encryption key
+ * @event_notify: called to notify important events
*/
struct ufs_hba_variant_ops {
const char *name;
enum ufs_notify_change_status);
int (*setup_clocks)(struct ufs_hba *, bool,
enum ufs_notify_change_status);
- int (*setup_regulators)(struct ufs_hba *, bool);
int (*hce_enable_notify)(struct ufs_hba *,
enum ufs_notify_change_status);
int (*link_startup_notify)(struct ufs_hba *,
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
void (*dbg_register_dump)(struct ufs_hba *hba);
int (*phy_initialization)(struct ufs_hba *);
- void (*device_reset)(struct ufs_hba *hba);
+ int (*device_reset)(struct ufs_hba *hba);
void (*config_scaling_param)(struct ufs_hba *hba,
struct devfreq_dev_profile *profile,
void *data);
int (*program_key)(struct ufs_hba *hba,
const union ufs_crypto_cfg_entry *cfg, int slot);
+ void (*event_notify)(struct ufs_hba *hba,
+ enum ufs_event_type evt, void *data);
};
/* clock gating state */
* @workq: workqueue to schedule devfreq suspend/resume work
* @suspend_work: worker to suspend devfreq
* @resume_work: worker to resume devfreq
+ * @min_gear: lowest HS gear to scale down to
* @is_allowed: tracks if scaling is currently allowed or not
* @is_busy_started: tracks if busy period has started or not
* @is_suspended: tracks if devfreq is suspended or not
struct workqueue_struct *workq;
struct work_struct suspend_work;
struct work_struct resume_work;
+ u32 min_gear;
bool is_allowed;
bool is_busy_started;
bool is_suspended;
};
- #define UFS_ERR_REG_HIST_LENGTH 8
+ #define UFS_EVENT_HIST_LENGTH 8
/**
- * struct ufs_err_reg_hist - keeps history of errors
+ * struct ufs_event_hist - keeps history of errors
* @pos: index to indicate cyclic buffer position
* @reg: cyclic buffer for registers value
* @tstamp: cyclic buffer for time stamp
*/
- struct ufs_err_reg_hist {
+ struct ufs_event_hist {
int pos;
- u32 reg[UFS_ERR_REG_HIST_LENGTH];
- ktime_t tstamp[UFS_ERR_REG_HIST_LENGTH];
+ u32 val[UFS_EVENT_HIST_LENGTH];
+ ktime_t tstamp[UFS_EVENT_HIST_LENGTH];
};
/**
* reset this after link-startup.
* @last_hibern8_exit_tstamp: Set time after the hibern8 exit.
* Clear after the first successful command completion.
- * @pa_err: tracks pa-uic errors
- * @dl_err: tracks dl-uic errors
- * @nl_err: tracks nl-uic errors
- * @tl_err: tracks tl-uic errors
- * @dme_err: tracks dme errors
- * @auto_hibern8_err: tracks auto-hibernate errors
- * @fatal_err: tracks fatal errors
- * @linkup_err: tracks link-startup errors
- * @resume_err: tracks resume errors
- * @suspend_err: tracks suspend errors
- * @dev_reset: tracks device reset events
- * @host_reset: tracks host reset events
- * @tsk_abort: tracks task abort events
*/
struct ufs_stats {
u32 last_intr_status;
u32 hibern8_exit_cnt;
ktime_t last_hibern8_exit_tstamp;
-
- /* uic specific errors */
- struct ufs_err_reg_hist pa_err;
- struct ufs_err_reg_hist dl_err;
- struct ufs_err_reg_hist nl_err;
- struct ufs_err_reg_hist tl_err;
- struct ufs_err_reg_hist dme_err;
-
- /* fatal errors */
- struct ufs_err_reg_hist auto_hibern8_err;
- struct ufs_err_reg_hist fatal_err;
- struct ufs_err_reg_hist link_startup_err;
- struct ufs_err_reg_hist resume_err;
- struct ufs_err_reg_hist suspend_err;
-
- /* abnormal events */
- struct ufs_err_reg_hist dev_reset;
- struct ufs_err_reg_hist host_reset;
- struct ufs_err_reg_hist task_abort;
+ struct ufs_event_hist event[UFS_EVT_CNT];
};
enum ufshcd_quirks {
* inline crypto engine, if it is present
*/
UFSHCD_CAP_CRYPTO = 1 << 8,
+
+ /*
+ * This capability allows the controller regulators to be put into
+ * lpm mode aggressively during clock gating.
+ * This would increase power savings.
+ */
+ UFSHCD_CAP_AGGR_POWER_COLLAPSE = 1 << 9,
+
+ /*
+ * This capability allows the host controller driver to use DeepSleep,
+ * if it is supported by the UFS device. The host controller driver must
+ * support device hardware reset via the hba->device_reset() callback,
+ * in order to exit DeepSleep state.
+ */
+ UFSHCD_CAP_DEEPSLEEP = 1 << 10,
};
struct ufs_hba_variant_params {
* "UFS device" W-LU.
*/
struct scsi_device *sdev_ufs_device;
+ struct scsi_device *sdev_rpmb;
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
u32 intr_mask;
u16 ee_ctrl_mask;
bool is_powered;
+ struct semaphore eh_sem;
/* Work Queues */
struct workqueue_struct *eh_wq;
#endif
}
+ static inline bool ufshcd_can_aggressive_pc(struct ufs_hba *hba)
+ {
+ return !!(ufshcd_is_link_hibern8(hba) &&
+ (hba->caps & UFSHCD_CAP_AGGR_POWER_COLLAPSE));
+ }
+
static inline bool ufshcd_is_auto_hibern8_supported(struct ufs_hba *hba)
{
return (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) &&
u32 val, unsigned long interval_us,
unsigned long timeout_ms);
void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk);
- void ufshcd_update_reg_hist(struct ufs_err_reg_hist *reg_hist,
- u32 reg);
+ void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val);
static inline void check_upiu_size(void)
{
extern int ufshcd_system_suspend(struct ufs_hba *hba);
extern int ufshcd_system_resume(struct ufs_hba *hba);
extern int ufshcd_shutdown(struct ufs_hba *hba);
+ extern int ufshcd_dme_configure_adapt(struct ufs_hba *hba,
+ int agreed_gear,
+ int adapt_val);
extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer);
extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
return 0;
}
+ static inline void ufshcd_vops_event_notify(struct ufs_hba *hba,
+ enum ufs_event_type evt,
+ void *data)
+ {
+ if (hba->vops && hba->vops->event_notify)
+ hba->vops->event_notify(hba, evt, data);
+ }
+
static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on,
enum ufs_notify_change_status status)
{
return 0;
}
- static inline int ufshcd_vops_setup_regulators(struct ufs_hba *hba, bool status)
- {
- if (hba->vops && hba->vops->setup_regulators)
- return hba->vops->setup_regulators(hba, status);
-
- return 0;
- }
-
static inline int ufshcd_vops_hce_enable_notify(struct ufs_hba *hba,
bool status)
{
return 0;
}
+ static inline int ufshcd_vops_phy_initialization(struct ufs_hba *hba)
+ {
+ if (hba->vops && hba->vops->phy_initialization)
+ return hba->vops->phy_initialization(hba);
+
+ return 0;
+ }
+
static inline int ufshcd_vops_pwr_change_notify(struct ufs_hba *hba,
bool status,
struct ufs_pa_layer_attr *dev_max_params,
static inline void ufshcd_vops_device_reset(struct ufs_hba *hba)
{
if (hba->vops && hba->vops->device_reset) {
- hba->vops->device_reset(hba);
- ufshcd_set_ufs_dev_active(hba);
- ufshcd_update_reg_hist(&hba->ufs_stats.dev_reset, 0);
+ int err = hba->vops->device_reset(hba);
+
+ if (!err)
+ ufshcd_set_ufs_dev_active(hba);
+ if (err != -EOPNOTSUPP)
+ ufshcd_update_evt_hist(hba, UFS_EVT_DEV_RESET, err);
}
}
uint8_t tmr_type;
uint32_t tmr_cmd_cnt;
- int16_t tmr_cmd_ids[0];
+ int16_t tmr_cmd_ids[];
};
/*
}
static int new_block_to_iov(struct tcmu_dev *udev, struct tcmu_cmd *cmd,
- struct iovec **iov, int prev_dbi, int *remain)
+ struct iovec **iov, int prev_dbi, int len)
{
/* Get the next dbi */
int dbi = tcmu_cmd_get_dbi(cmd);
+
/* Do not add more than DATA_BLOCK_SIZE to iov */
- int len = min_t(int, DATA_BLOCK_SIZE, *remain);
+ if (len > DATA_BLOCK_SIZE)
+ len = DATA_BLOCK_SIZE;
- *remain -= len;
/*
* The following code will gather and map the blocks to the same iovec
* when the blocks are all next to each other.
int dbi = -2;
/* We prepare the IOVs for DMA_FROM_DEVICE transfer direction */
- while (data_length > 0)
- dbi = new_block_to_iov(udev, cmd, iov, dbi, &data_length);
+ for (; data_length > 0; data_length -= DATA_BLOCK_SIZE)
+ dbi = new_block_to_iov(udev, cmd, iov, dbi, data_length);
}
static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd)
#define UPDATE_HEAD(head, used, size) smp_store_release(&head, ((head % size) + used) % size)
- static void scatter_data_area(struct tcmu_dev *udev, struct tcmu_cmd *tcmu_cmd,
- struct iovec **iov)
+ #define TCMU_SG_TO_DATA_AREA 1
+ #define TCMU_DATA_AREA_TO_SG 2
+
+ static inline void tcmu_copy_data(struct tcmu_dev *udev,
+ struct tcmu_cmd *tcmu_cmd, uint32_t direction,
+ struct scatterlist *sg, unsigned int sg_nents,
+ struct iovec **iov, size_t data_len)
{
- struct se_cmd *se_cmd = tcmu_cmd->se_cmd;
/* start value of dbi + 1 must not be a valid dbi */
- int i, dbi = -2;
- int block_remaining = 0;
- int data_len = se_cmd->data_length;
- void *from, *to = NULL;
- size_t copy_bytes, offset;
- struct scatterlist *sg;
- struct page *page = NULL;
-
- for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, i) {
- int sg_remaining = sg->length;
- from = kmap_atomic(sg_page(sg)) + sg->offset;
- while (sg_remaining > 0) {
- if (block_remaining == 0) {
- if (to) {
- flush_dcache_page(page);
- kunmap_atomic(to);
- }
-
- /* get next dbi and add to IOVs */
- dbi = new_block_to_iov(udev, tcmu_cmd, iov, dbi,
- &data_len);
- page = tcmu_get_block_page(udev, dbi);
- to = kmap_atomic(page);
- block_remaining = DATA_BLOCK_SIZE;
- }
-
- copy_bytes = min_t(size_t, sg_remaining,
- block_remaining);
- offset = DATA_BLOCK_SIZE - block_remaining;
- memcpy(to + offset, from + sg->length - sg_remaining,
- copy_bytes);
+ int dbi = -2;
+ size_t block_remaining, cp_len;
+ struct sg_mapping_iter sg_iter;
+ unsigned int sg_flags;
+ struct page *page;
+ void *data_page_start, *data_addr;
- sg_remaining -= copy_bytes;
- block_remaining -= copy_bytes;
+ if (direction == TCMU_SG_TO_DATA_AREA)
+ sg_flags = SG_MITER_ATOMIC | SG_MITER_FROM_SG;
+ else
+ sg_flags = SG_MITER_ATOMIC | SG_MITER_TO_SG;
+ sg_miter_start(&sg_iter, sg, sg_nents, sg_flags);
+
+ while (data_len) {
+ if (direction == TCMU_SG_TO_DATA_AREA)
+ dbi = new_block_to_iov(udev, tcmu_cmd, iov, dbi,
+ data_len);
+ else
+ dbi = tcmu_cmd_get_dbi(tcmu_cmd);
+ page = tcmu_get_block_page(udev, dbi);
+ if (direction == TCMU_DATA_AREA_TO_SG)
+ flush_dcache_page(page);
+ data_page_start = kmap_atomic(page);
+ block_remaining = DATA_BLOCK_SIZE;
+
+ while (block_remaining && data_len) {
+ if (!sg_miter_next(&sg_iter)) {
+ /* set length to 0 to abort outer loop */
+ data_len = 0;
+ pr_debug("tcmu_move_data: aborting data copy due to exhausted sg_list\n");
+ break;
+ }
+ cp_len = min3(sg_iter.length, block_remaining, data_len);
+
+ data_addr = data_page_start +
+ DATA_BLOCK_SIZE - block_remaining;
+ if (direction == TCMU_SG_TO_DATA_AREA)
+ memcpy(data_addr, sg_iter.addr, cp_len);
+ else
+ memcpy(sg_iter.addr, data_addr, cp_len);
+
+ data_len -= cp_len;
+ block_remaining -= cp_len;
+ sg_iter.consumed = cp_len;
}
- kunmap_atomic(from - sg->offset);
- }
+ sg_miter_stop(&sg_iter);
- if (to) {
- flush_dcache_page(page);
- kunmap_atomic(to);
+ kunmap_atomic(data_page_start);
+ if (direction == TCMU_SG_TO_DATA_AREA)
+ flush_dcache_page(page);
}
}
- static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd,
+ static void scatter_data_area(struct tcmu_dev *udev, struct tcmu_cmd *tcmu_cmd,
+ struct iovec **iov)
+ {
+ struct se_cmd *se_cmd = tcmu_cmd->se_cmd;
+
+ tcmu_copy_data(udev, tcmu_cmd, TCMU_SG_TO_DATA_AREA, se_cmd->t_data_sg,
+ se_cmd->t_data_nents, iov, se_cmd->data_length);
+ }
+
+ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *tcmu_cmd,
bool bidi, uint32_t read_len)
{
- struct se_cmd *se_cmd = cmd->se_cmd;
- int i, dbi;
- int block_remaining = 0;
- void *from = NULL, *to;
- size_t copy_bytes, offset;
- struct scatterlist *sg, *data_sg;
- struct page *page;
+ struct se_cmd *se_cmd = tcmu_cmd->se_cmd;
+ struct scatterlist *data_sg;
unsigned int data_nents;
- uint32_t count = 0;
if (!bidi) {
data_sg = se_cmd->t_data_sg;
* buffer blocks, and before gathering the Data-In buffer
* the Data-Out buffer blocks should be skipped.
*/
- count = cmd->dbi_cnt - cmd->dbi_bidi_cnt;
+ tcmu_cmd_set_dbi_cur(tcmu_cmd,
+ tcmu_cmd->dbi_cnt - tcmu_cmd->dbi_bidi_cnt);
data_sg = se_cmd->t_bidi_data_sg;
data_nents = se_cmd->t_bidi_data_nents;
}
- tcmu_cmd_set_dbi_cur(cmd, count);
-
- for_each_sg(data_sg, sg, data_nents, i) {
- int sg_remaining = sg->length;
- to = kmap_atomic(sg_page(sg)) + sg->offset;
- while (sg_remaining > 0 && read_len > 0) {
- if (block_remaining == 0) {
- if (from)
- kunmap_atomic(from);
-
- block_remaining = DATA_BLOCK_SIZE;
- dbi = tcmu_cmd_get_dbi(cmd);
- page = tcmu_get_block_page(udev, dbi);
- from = kmap_atomic(page);
- flush_dcache_page(page);
- }
- copy_bytes = min_t(size_t, sg_remaining,
- block_remaining);
- if (read_len < copy_bytes)
- copy_bytes = read_len;
- offset = DATA_BLOCK_SIZE - block_remaining;
- memcpy(to + sg->length - sg_remaining, from + offset,
- copy_bytes);
-
- sg_remaining -= copy_bytes;
- block_remaining -= copy_bytes;
- read_len -= copy_bytes;
- }
- kunmap_atomic(to - sg->offset);
- if (read_len == 0)
- break;
- }
- if (from)
- kunmap_atomic(from);
+ tcmu_copy_data(udev, tcmu_cmd, TCMU_DATA_AREA_TO_SG, data_sg,
+ data_nents, NULL, read_len);
}
static inline size_t spc_bitmap_free(unsigned long *bitmap, uint32_t thresh)