aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/Kconfig5
-rw-r--r--drivers/staging/Makefile2
-rw-r--r--drivers/staging/android/Kconfig2
-rw-r--r--drivers/staging/android/vsoc.c3
-rw-r--r--drivers/staging/axis-fifo/Kconfig7
-rw-r--r--drivers/staging/comedi/Kconfig254
-rw-r--r--drivers/staging/comedi/comedi_buf.c2
-rw-r--r--drivers/staging/comedi/comedi_fops.c32
-rw-r--r--drivers/staging/comedi/drivers.c11
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1710.c2
-rw-r--r--drivers/staging/comedi/drivers/dt2811.c2
-rw-r--r--drivers/staging/comedi/drivers/dt9812.c5
-rw-r--r--drivers/staging/comedi/drivers/dyna_pci10xx.c10
-rw-r--r--drivers/staging/comedi/drivers/ni_usb6501.c14
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c2
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c2
-rw-r--r--drivers/staging/comedi/drivers/usbduxsigma.c2
-rw-r--r--drivers/staging/comedi/drivers/vmk80xx.c8
-rw-r--r--drivers/staging/erofs/data.c2
-rw-r--r--drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev31
-rw-r--r--drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev62
-rw-r--r--drivers/staging/fieldbus/Documentation/fieldbus_dev.txt66
-rw-r--r--drivers/staging/fieldbus/Kconfig18
-rw-r--r--drivers/staging/fieldbus/Makefile7
-rw-r--r--drivers/staging/fieldbus/TODO5
-rw-r--r--drivers/staging/fieldbus/anybuss/Kconfig39
-rw-r--r--drivers/staging/fieldbus/anybuss/Makefile10
-rw-r--r--drivers/staging/fieldbus/anybuss/anybuss-client.h102
-rw-r--r--drivers/staging/fieldbus/anybuss/anybuss-controller.h47
-rw-r--r--drivers/staging/fieldbus/anybuss/arcx-anybus.c399
-rw-r--r--drivers/staging/fieldbus/anybuss/hms-profinet.c228
-rw-r--r--drivers/staging/fieldbus/anybuss/host.c1460
-rw-r--r--drivers/staging/fieldbus/dev_core.c351
-rw-r--r--drivers/staging/fieldbus/fieldbus_dev.h108
-rw-r--r--drivers/staging/gasket/gasket_page_table.c5
-rw-r--r--drivers/staging/greybus/power_supply.c4
-rw-r--r--drivers/staging/gs_fpgaboot/Kconfig1
-rw-r--r--drivers/staging/iio/adc/ad7192.c8
-rw-r--r--drivers/staging/iio/meter/ade7854.c2
-rw-r--r--drivers/staging/kpc2000/Kconfig46
-rw-r--r--drivers/staging/kpc2000/Makefile5
-rw-r--r--drivers/staging/kpc2000/TODO8
-rw-r--r--drivers/staging/kpc2000/kpc.h23
-rw-r--r--drivers/staging/kpc2000/kpc2000/Makefile4
-rw-r--r--drivers/staging/kpc2000/kpc2000/cell_probe.c466
-rw-r--r--drivers/staging/kpc2000/kpc2000/core.c436
-rw-r--r--drivers/staging/kpc2000/kpc2000/dma_common_defs.h43
-rw-r--r--drivers/staging/kpc2000/kpc2000/fileops.c128
-rw-r--r--drivers/staging/kpc2000/kpc2000/kp2000_module.c55
-rw-r--r--drivers/staging/kpc2000/kpc2000/pcie.h112
-rw-r--r--drivers/staging/kpc2000/kpc2000/uapi.h22
-rw-r--r--drivers/staging/kpc2000/kpc_i2c/Makefile4
-rw-r--r--drivers/staging/kpc2000/kpc_i2c/fileops.c181
-rw-r--r--drivers/staging/kpc2000/kpc_i2c/i2c_driver.c698
-rw-r--r--drivers/staging/kpc2000/kpc_spi/Makefile4
-rw-r--r--drivers/staging/kpc2000/kpc_spi/spi_driver.c507
-rw-r--r--drivers/staging/kpc2000/kpc_spi/spi_parts.h48
-rw-r--r--drivers/staging/media/rockchip/vpu/Kconfig1
-rw-r--r--drivers/staging/most/Documentation/ABI/configfs-most.txt204
-rw-r--r--drivers/staging/most/Documentation/driver_usage.txt131
-rw-r--r--drivers/staging/most/Kconfig2
-rw-r--r--drivers/staging/most/Makefile1
-rw-r--r--drivers/staging/most/cdev/cdev.c8
-rw-r--r--drivers/staging/most/configfs.c676
-rw-r--r--drivers/staging/most/core.c305
-rw-r--r--drivers/staging/most/core.h20
-rw-r--r--drivers/staging/most/net/net.c3
-rw-r--r--drivers/staging/most/sound/sound.c59
-rw-r--r--drivers/staging/most/usb/usb.c2
-rw-r--r--drivers/staging/most/video/video.c3
-rw-r--r--drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c134
-rw-r--r--drivers/staging/pi433/rf69.c45
-rw-r--r--drivers/staging/ralink-gdma/ralink-gdma.c12
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_recv.c11
-rw-r--r--drivers/staging/rtl8192e/Kconfig1
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c1
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c1
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.c2
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h4
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c2
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c2
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_ioctl_set.c2
-rw-r--r--drivers/staging/rtl8723bs/core/rtw_sta_mgt.c7
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_com.c18
-rw-r--r--drivers/staging/rtl8723bs/hal/hal_com_phycfg.c4
-rw-r--r--drivers/staging/rtl8723bs/hal/odm.c2
-rw-r--r--drivers/staging/rtl8723bs/hal/odm.h12
-rw-r--r--drivers/staging/rtl8723bs/hal/sdio_ops.c6
-rw-r--r--drivers/staging/rtl8723bs/include/drv_types.h6
-rw-r--r--drivers/staging/rtl8723bs/include/drv_types_sdio.h4
-rw-r--r--drivers/staging/rtl8723bs/include/ieee80211.h15
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_recv.h4
-rw-r--r--drivers/staging/rtl8723bs/include/rtw_xmit.h2
-rw-r--r--drivers/staging/rtl8723bs/os_dep/sdio_intf.c10
-rw-r--r--drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c30
-rw-r--r--drivers/staging/rtlwifi/base.c18
-rw-r--r--drivers/staging/rtlwifi/btcoexist/halbtc8822b1ant.c4
-rw-r--r--drivers/staging/rtlwifi/core.c18
-rw-r--r--drivers/staging/rtlwifi/pci.c4
-rw-r--r--drivers/staging/rtlwifi/rtl8822be/hw.c9
-rw-r--r--drivers/staging/speakup/main.c1
-rw-r--r--drivers/staging/speakup/selection.c212
-rw-r--r--drivers/staging/speakup/speakup.h1
-rw-r--r--drivers/staging/vc04_services/bcm2835-camera/controls.c58
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c3
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c2
-rw-r--r--drivers/staging/vt6655/upc.h2
-rw-r--r--drivers/staging/wilc1000/Kconfig1
-rw-r--r--drivers/staging/wilc1000/host_interface.c2
-rw-r--r--drivers/staging/wilc1000/wilc_netdev.c2
-rw-r--r--drivers/staging/wilc1000/wilc_wlan.c8
-rw-r--r--drivers/staging/wlan-ng/Kconfig1
112 files changed, 7427 insertions, 781 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 6319369cca89..b770c8c535f4 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
menuconfig STAGING
bool "Staging drivers"
- default n
---help---
This option allows you to select a number of drivers that are
not of the "normal" Linux kernel quality level. These drivers
@@ -121,4 +120,8 @@ source "drivers/staging/axis-fifo/Kconfig"
source "drivers/staging/erofs/Kconfig"
+source "drivers/staging/fieldbus/Kconfig"
+
+source "drivers/staging/kpc2000/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 763d18f56b10..0640bd632851 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -50,3 +50,5 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_EROFS_FS) += erofs/
+obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
+obj-$(CONFIG_KPC2000) += kpc2000/
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index e02026999161..d6d605d5cbde 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -5,7 +5,6 @@ if ANDROID
config ASHMEM
bool "Enable the Anonymous Shared Memory Subsystem"
- default n
depends on SHMEM
help
The ashmem subsystem is a new shared memory allocator, similar to
@@ -17,7 +16,6 @@ config ASHMEM
config ANDROID_VSOC
tristate "Android Virtual SoC support"
- default n
depends on PCI_MSI
help
This option adds support for the Virtual SoC driver needed to boot
diff --git a/drivers/staging/android/vsoc.c b/drivers/staging/android/vsoc.c
index 8a75bd27c413..00a1ec7b9154 100644
--- a/drivers/staging/android/vsoc.c
+++ b/drivers/staging/android/vsoc.c
@@ -259,7 +259,8 @@ do_create_fd_scoped_permission(struct vsoc_device_region *region_p,
atomic_t *owner_ptr = NULL;
struct vsoc_device_region *managed_region_p;
- if (copy_from_user(&np->permission, &arg->perm, sizeof(*np)) ||
+ if (copy_from_user(&np->permission,
+ &arg->perm, sizeof(np->permission)) ||
copy_from_user(&managed_fd,
&arg->managed_region_fd, sizeof(managed_fd))) {
return -EFAULT;
diff --git a/drivers/staging/axis-fifo/Kconfig b/drivers/staging/axis-fifo/Kconfig
index 09709cd8173d..3fffe4d6f327 100644
--- a/drivers/staging/axis-fifo/Kconfig
+++ b/drivers/staging/axis-fifo/Kconfig
@@ -5,7 +5,8 @@
config XIL_AXIS_FIFO
tristate "Xilinx AXI-Stream FIFO IP core driver"
depends on OF
- default n
help
- This adds support for the Xilinx AXI-Stream
- FIFO IP core driver.
+ This adds support for the Xilinx AXI-Stream FIFO IP core driver.
+ The AXI Streaming FIFO allows memory mapped access to a AXI Streaming
+ interface. The Xilinx AXI-Stream FIFO IP core can be used to interface
+ to the AXI Ethernet without the need to use DMA.
diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
index 73eda687bdc1..049b659fa6ad 100644
--- a/drivers/staging/comedi/Kconfig
+++ b/drivers/staging/comedi/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
config COMEDI
tristate "Data acquisition support (comedi)"
- ---help---
+ help
Enable support for a wide range of data acquisition devices
for Linux.
@@ -9,14 +9,14 @@ if COMEDI
config COMEDI_DEBUG
bool "Comedi debugging"
- ---help---
+ help
This is an option for use by developers; most people should
say N here. This enables comedi core and driver debugging.
config COMEDI_DEFAULT_BUF_SIZE_KB
int "Comedi default initial asynchronous buffer size in KiB"
default "2048"
- ---help---
+ help
This is the default asynchronous buffer size which is used for
commands running in the background in kernel space. This
defaults to 2048 KiB of memory so that a 16 channel card
@@ -25,7 +25,7 @@ config COMEDI_DEFAULT_BUF_SIZE_KB
config COMEDI_DEFAULT_BUF_MAXSIZE_KB
int "Comedi default maximum asynchronous buffer size in KiB"
default "20480"
- ---help---
+ help
This is the default maximum asynchronous buffer size which can
be requested by a userspace program without root privileges.
This is set to 20480 KiB so that a fast I/O card with 16
@@ -33,7 +33,7 @@ config COMEDI_DEFAULT_BUF_MAXSIZE_KB
menuconfig COMEDI_MISC_DRIVERS
bool "Comedi misc drivers"
- ---help---
+ help
Enable comedi misc drivers to be built
Note that the answer to this question won't directly affect the
@@ -45,7 +45,7 @@ if COMEDI_MISC_DRIVERS
config COMEDI_BOND
tristate "Comedi device bonding support"
select COMEDI_KCOMEDILIB
- ---help---
+ help
Enable support for a driver to 'bond' (merge) multiple subdevices
from multiple devices together as one.
@@ -56,7 +56,7 @@ config COMEDI_BOND
config COMEDI_TEST
tristate "Fake waveform generator support"
- ---help---
+ help
Enable support for the fake waveform generator.
This driver is mainly for testing purposes, but can also be used to
generate sample waveforms on systems that don't have data acquisition
@@ -67,7 +67,7 @@ config COMEDI_TEST
config COMEDI_PARPORT
tristate "Parallel port support"
- ---help---
+ help
Enable support for the standard parallel port.
A cheap and easy way to get a few more digital I/O lines. Steal
additional parallel ports from old computers or your neighbors'
@@ -79,7 +79,7 @@ config COMEDI_PARPORT
config COMEDI_SSV_DNP
tristate "SSV Embedded Systems DIL/Net-PC support"
depends on X86_32 || COMPILE_TEST
- ---help---
+ help
Enable support for SSV Embedded Systems DIL/Net-PC
To compile this driver as a module, choose M here: the module will be
@@ -89,7 +89,7 @@ endif # COMEDI_MISC_DRIVERS
menuconfig COMEDI_ISA_DRIVERS
bool "Comedi ISA and PC/104 drivers"
- ---help---
+ help
Enable comedi ISA and PC/104 drivers to be built
Note that the answer to this question won't directly affect the
@@ -101,7 +101,7 @@ if COMEDI_ISA_DRIVERS
config COMEDI_PCL711
tristate "Advantech PCL-711/711b and ADlink ACL-8112 ISA card support"
select COMEDI_8254
- ---help---
+ help
Enable support for Advantech PCL-711 and 711b, ADlink ACL-8112
To compile this driver as a module, choose M here: the module will be
@@ -110,7 +110,7 @@ config COMEDI_PCL711
config COMEDI_PCL724
tristate "Advantech PCL-722/724/731 and ADlink ACL-7122/7124/PET-48DIO"
select COMEDI_8255
- ---help---
+ help
Enable support for ISA and PC/104 based 8255 digital i/o boards. This
driver provides a legacy comedi driver wrapper for the generic 8255
support driver.
@@ -130,7 +130,7 @@ config COMEDI_PCL724
config COMEDI_PCL726
tristate "Advantech PCL-726 and compatible ISA card support"
- ---help---
+ help
Enable support for Advantech PCL-726 and compatible ISA cards.
To compile this driver as a module, choose M here: the module will be
@@ -138,7 +138,7 @@ config COMEDI_PCL726
config COMEDI_PCL730
tristate "Simple Digital I/O board support (8-bit ports)"
- ---help---
+ help
Enable support for various simple ISA or PC/104 Digital I/O boards.
These boards all use 8-bit I/O ports.
@@ -163,7 +163,7 @@ config COMEDI_PCL812
tristate "Advantech PCL-812/813 and ADlink ACL-8112/8113/8113/8216"
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8254
- ---help---
+ help
Enable support for Advantech PCL-812/PG, PCL-813/B, ADLink
ACL-8112DG/HG/PG, ACL-8113, ACL-8216, ICP DAS A-821PGH/PGL/PGL-NDA,
A-822PGH/PGL, A-823PGH/PGL, A-826PG and ICP DAS ISO-813 ISA cards
@@ -175,7 +175,7 @@ config COMEDI_PCL816
tristate "Advantech PCL-814 and PCL-816 ISA card support"
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8254
- ---help---
+ help
Enable support for Advantech PCL-814 and PCL-816 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -185,7 +185,7 @@ config COMEDI_PCL818
tristate "Advantech PCL-718 and PCL-818 ISA card support"
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8254
- ---help---
+ help
Enable support for Advantech PCL-818 ISA cards
PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818 and PCL-718
@@ -195,7 +195,7 @@ config COMEDI_PCL818
config COMEDI_PCM3724
tristate "Advantech PCM-3724 PC/104 card support"
select COMEDI_8255
- ---help---
+ help
Enable support for Advantech PCM-3724 PC/104 cards.
To compile this driver as a module, choose M here: the module will be
@@ -204,7 +204,7 @@ config COMEDI_PCM3724
config COMEDI_AMPLC_DIO200_ISA
tristate "Amplicon PC212E/PC214E/PC215E/PC218E/PC272E"
select COMEDI_AMPLC_DIO200
- ---help---
+ help
Enable support for Amplicon PC212E, PC214E, PC215E, PC218E and
PC272E ISA DIO boards
@@ -214,7 +214,7 @@ config COMEDI_AMPLC_DIO200_ISA
config COMEDI_AMPLC_PC236_ISA
tristate "Amplicon PC36AT DIO board support"
select COMEDI_AMPLC_PC236
- ---help---
+ help
Enable support for Amplicon PC36AT ISA DIO board.
To compile this driver as a module, choose M here: the module will be
@@ -222,7 +222,7 @@ config COMEDI_AMPLC_PC236_ISA
config COMEDI_AMPLC_PC263_ISA
tristate "Amplicon PC263 relay board support"
- ---help---
+ help
Enable support for Amplicon PC263 ISA relay board. This board has
16 reed relay output channels.
@@ -231,7 +231,7 @@ config COMEDI_AMPLC_PC263_ISA
config COMEDI_RTI800
tristate "Analog Devices RTI-800/815 ISA card support"
- ---help---
+ help
Enable support for Analog Devices RTI-800/815 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -239,7 +239,7 @@ config COMEDI_RTI800
config COMEDI_RTI802
tristate "Analog Devices RTI-802 ISA card support"
- ---help---
+ help
Enable support for Analog Devices RTI-802 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -247,7 +247,7 @@ config COMEDI_RTI802
config COMEDI_DAC02
tristate "Keithley Metrabyte DAC02 compatible ISA card support"
- ---help---
+ help
Enable support for Keithley Metrabyte DAC02 compatible ISA cards.
To compile this driver as a module, choose M here: the module will be
@@ -257,7 +257,7 @@ config COMEDI_DAS16M1
tristate "MeasurementComputing CIO-DAS16/M1DAS-16 ISA card support"
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for Measurement Computing CIO-DAS16/M1 ISA cards.
To compile this driver as a module, choose M here: the module will be
@@ -266,7 +266,7 @@ config COMEDI_DAS16M1
config COMEDI_DAS08_ISA
tristate "DAS-08 compatible ISA and PC/104 card support"
select COMEDI_DAS08
- ---help---
+ help
Enable support for Keithley Metrabyte/ComputerBoards DAS08
and compatible ISA and PC/104 cards:
Keithley Metrabyte/ComputerBoards DAS08, DAS08-PGM, DAS08-PGH,
@@ -281,7 +281,7 @@ config COMEDI_DAS16
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for Keithley Metrabyte/ComputerBoards DAS16
and compatible ISA and PC/104 cards:
Keithley Metrabyte DAS-16, DAS-16G, DAS-16F, DAS-1201, DAS-1202,
@@ -297,7 +297,7 @@ config COMEDI_DAS16
config COMEDI_DAS800
tristate "DAS800 and compatible ISA card support"
select COMEDI_8254
- ---help---
+ help
Enable support for Keithley Metrabyte DAS800 and compatible ISA cards
Keithley Metrabyte DAS-800, DAS-801, DAS-802
Measurement Computing CIO-DAS800, CIO-DAS801, CIO-DAS802 and
@@ -310,7 +310,7 @@ config COMEDI_DAS1800
tristate "DAS1800 and compatible ISA card support"
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8254
- ---help---
+ help
Enable support for DAS1800 and compatible ISA cards
Keithley Metrabyte DAS-1701ST, DAS-1701ST-DA, DAS-1701/AO,
DAS-1702ST, DAS-1702ST-DA, DAS-1702HR, DAS-1702HR-DA, DAS-1702/AO,
@@ -324,7 +324,7 @@ config COMEDI_DAS1800
config COMEDI_DAS6402
tristate "DAS6402 and compatible ISA card support"
select COMEDI_8254
- ---help---
+ help
Enable support for DAS6402 and compatible ISA cards
Computerboards, Keithley Metrabyte DAS6402 and compatibles
@@ -333,7 +333,7 @@ config COMEDI_DAS6402
config COMEDI_DT2801
tristate "Data Translation DT2801 ISA card support"
- ---help---
+ help
Enable support for Data Translation DT2801 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -341,7 +341,7 @@ config COMEDI_DT2801
config COMEDI_DT2811
tristate "Data Translation DT2811 ISA card support"
- ---help---
+ help
Enable support for Data Translation DT2811 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -349,7 +349,7 @@ config COMEDI_DT2811
config COMEDI_DT2814
tristate "Data Translation DT2814 ISA card support"
- ---help---
+ help
Enable support for Data Translation DT2814 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -357,7 +357,7 @@ config COMEDI_DT2814
config COMEDI_DT2815
tristate "Data Translation DT2815 ISA card support"
- ---help---
+ help
Enable support for Data Translation DT2815 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -365,7 +365,7 @@ config COMEDI_DT2815
config COMEDI_DT2817
tristate "Data Translation DT2817 ISA card support"
- ---help---
+ help
Enable support for Data Translation DT2817 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -374,7 +374,7 @@ config COMEDI_DT2817
config COMEDI_DT282X
tristate "Data Translation DT2821 series and DT-EZ ISA card support"
select COMEDI_ISADMA if ISA_DMA_API
- ---help---
+ help
Enable support for Data Translation DT2821 series including DT-EZ
DT2821, DT2821-F-16SE, DT2821-F-8DI, DT2821-G-16SE, DT2821-G-8DI,
DT2823 (dt2823), DT2824-PGH, DT2824-PGL, DT2825, DT2827, DT2828,
@@ -386,7 +386,7 @@ config COMEDI_DT282X
config COMEDI_DMM32AT
tristate "Diamond Systems MM-32-AT PC/104 board support"
select COMEDI_8255
- ---help---
+ help
Enable support for Diamond Systems MM-32-AT PC/104 boards
To compile this driver as a module, choose M here: the module will be
@@ -394,7 +394,7 @@ config COMEDI_DMM32AT
config COMEDI_FL512
tristate "FL512 ISA card support"
- ---help---
+ help
Enable support for FL512 ISA card
To compile this driver as a module, choose M here: the module will be
@@ -404,7 +404,7 @@ config COMEDI_AIO_AIO12_8
tristate "I/O Products PC/104 AIO12-8 Analog I/O Board support"
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for I/O Products PC/104 AIO12-8 Analog I/O Board
To compile this driver as a module, choose M here: the module will be
@@ -412,7 +412,7 @@ config COMEDI_AIO_AIO12_8
config COMEDI_AIO_IIRO_16
tristate "I/O Products PC/104 IIRO16 Board support"
- ---help---
+ help
Enable support for I/O Products PC/104 IIRO16 Relay And Isolated
Input Board
@@ -422,7 +422,7 @@ config COMEDI_AIO_IIRO_16
config COMEDI_II_PCI20KC
tristate "Intelligent Instruments PCI-20001C carrier support"
depends on HAS_IOMEM
- ---help---
+ help
Enable support for Intelligent Instruments PCI-20001C carrier
PCI-20001, PCI-20006 and PCI-20341
@@ -431,7 +431,7 @@ config COMEDI_II_PCI20KC
config COMEDI_C6XDIGIO
tristate "Mechatronic Systems Inc. C6x_DIGIO DSP daughter card support"
- ---help---
+ help
Enable support for Mechatronic Systems Inc. C6x_DIGIO DSP daughter
card
@@ -440,7 +440,7 @@ config COMEDI_C6XDIGIO
config COMEDI_MPC624
tristate "Micro/sys MPC-624 PC/104 board support"
- ---help---
+ help
Enable support for Micro/sys MPC-624 PC/104 board
To compile this driver as a module, choose M here: the module will be
@@ -448,7 +448,7 @@ config COMEDI_MPC624
config COMEDI_ADQ12B
tristate "MicroAxial ADQ12-B data acquisition and control card support"
- ---help---
+ help
Enable MicroAxial ADQ12-B daq and control card support.
To compile this driver as a module, choose M here: the module will be
@@ -458,7 +458,7 @@ config COMEDI_NI_AT_A2150
tristate "NI AT-A2150 ISA card support"
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8254
- ---help---
+ help
Enable support for National Instruments AT-A2150 cards
To compile this driver as a module, choose M here: the module will be
@@ -467,7 +467,7 @@ config COMEDI_NI_AT_A2150
config COMEDI_NI_AT_AO
tristate "NI AT-AO-6/10 EISA card support"
select COMEDI_8254
- ---help---
+ help
Enable support for National Instruments AT-AO-6/10 cards
To compile this driver as a module, choose M here: the module will be
@@ -477,7 +477,7 @@ config COMEDI_NI_ATMIO
tristate "NI AT-MIO E series ISA-PNP card support"
select COMEDI_8255
select COMEDI_NI_TIO
- ---help---
+ help
Enable support for National Instruments AT-MIO E series cards
National Instruments AT-MIO-16E-1 (ni_atmio),
AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3,
@@ -489,7 +489,7 @@ config COMEDI_NI_ATMIO
config COMEDI_NI_ATMIO16D
tristate "NI AT-MIO-16/AT-MIO-16D series ISA card support"
select COMEDI_8255
- ---help---
+ help
Enable support for National Instruments AT-MIO-16/AT-MIO-16D cards.
To compile this driver as a module, choose M here: the module will be
@@ -498,7 +498,7 @@ config COMEDI_NI_ATMIO16D
config COMEDI_NI_LABPC_ISA
tristate "NI Lab-PC and compatibles ISA support"
select COMEDI_NI_LABPC
- ---help---
+ help
Enable support for National Instruments Lab-PC and compatibles
Lab-PC-1200, Lab-PC-1200AI, Lab-PC+.
Kernel-level ISA plug-and-play support for the lab-pc-1200 boards has
@@ -509,7 +509,7 @@ config COMEDI_NI_LABPC_ISA
config COMEDI_PCMAD
tristate "Winsystems PCM-A/D12 and PCM-A/D16 PC/104 board support"
- ---help---
+ help
Enable support for Winsystems PCM-A/D12 and PCM-A/D16 PC/104 boards.
To compile this driver as a module, choose M here: the module will be
@@ -517,7 +517,7 @@ config COMEDI_PCMAD
config COMEDI_PCMDA12
tristate "Winsystems PCM-D/A-12 8-channel AO PC/104 board support"
- ---help---
+ help
Enable support for Winsystems PCM-D/A-12 8-channel AO PC/104 boards.
Note that the board is not ISA-PNP capable and thus needs the I/O
port comedi_config parameter.
@@ -527,7 +527,7 @@ config COMEDI_PCMDA12
config COMEDI_PCMMIO
tristate "Winsystems PCM-MIO PC/104 board support"
- ---help---
+ help
Enable support for Winsystems PCM-MIO multifunction PC/104 boards.
To compile this driver as a module, choose M here: the module will be
@@ -535,7 +535,7 @@ config COMEDI_PCMMIO
config COMEDI_PCMUIO
tristate "Winsystems PCM-UIO48A and PCM-UIO96A PC/104 board support"
- ---help---
+ help
Enable support for PCM-UIO48A and PCM-UIO96A PC/104 boards.
To compile this driver as a module, choose M here: the module will be
@@ -543,7 +543,7 @@ config COMEDI_PCMUIO
config COMEDI_MULTIQ3
tristate "Quanser Consulting MultiQ-3 ISA card support"
- ---help---
+ help
Enable support for Quanser Consulting MultiQ-3 ISA cards
To compile this driver as a module, choose M here: the module will be
@@ -551,7 +551,7 @@ config COMEDI_MULTIQ3
config COMEDI_S526
tristate "Sensoray s526 support"
- ---help---
+ help
Enable support for Sensoray s526
To compile this driver as a module, choose M here: the module will be
@@ -562,7 +562,7 @@ endif # COMEDI_ISA_DRIVERS
menuconfig COMEDI_PCI_DRIVERS
tristate "Comedi PCI drivers"
depends on PCI
- ---help---
+ help
Enable support for comedi PCI drivers.
To compile this support as a module, choose M here: the module will
@@ -573,7 +573,7 @@ if COMEDI_PCI_DRIVERS
config COMEDI_8255_PCI
tristate "Generic PCI based 8255 digital i/o board support"
select COMEDI_8255
- ---help---
+ help
Enable support for PCI based 8255 digital i/o boards. This driver
provides a PCI wrapper around the generic 8255 driver.
@@ -589,14 +589,14 @@ config COMEDI_8255_PCI
config COMEDI_ADDI_WATCHDOG
tristate
- ---help---
+ help
Provides support for the watchdog subdevice found on many ADDI-DATA
boards. This module will be automatically selected when needed. The
module will be called addi_watchdog.
config COMEDI_ADDI_APCI_1032
tristate "ADDI-DATA APCI_1032 support"
- ---help---
+ help
Enable support for ADDI-DATA APCI_1032 cards
To compile this driver as a module, choose M here: the module will be
@@ -604,7 +604,7 @@ config COMEDI_ADDI_APCI_1032
config COMEDI_ADDI_APCI_1500
tristate "ADDI-DATA APCI_1500 support"
- ---help---
+ help
Enable support for ADDI-DATA APCI_1500 cards
To compile this driver as a module, choose M here: the module will be
@@ -613,7 +613,7 @@ config COMEDI_ADDI_APCI_1500
config COMEDI_ADDI_APCI_1516
tristate "ADDI-DATA APCI-1016/1516/2016 support"
select COMEDI_ADDI_WATCHDOG
- ---help---
+ help
Enable support for ADDI-DATA APCI-1016, APCI-1516 and APCI-2016 boards.
These are 16 channel, optically isolated, digital I/O boards. The 1516
and 2016 boards also have a watchdog for resetting the outputs to "0".
@@ -624,7 +624,7 @@ config COMEDI_ADDI_APCI_1516
config COMEDI_ADDI_APCI_1564
tristate "ADDI-DATA APCI_1564 support"
select COMEDI_ADDI_WATCHDOG
- ---help---
+ help
Enable support for ADDI-DATA APCI_1564 cards
To compile this driver as a module, choose M here: the module will be
@@ -632,7 +632,7 @@ config COMEDI_ADDI_APCI_1564
config COMEDI_ADDI_APCI_16XX
tristate "ADDI-DATA APCI_16xx support"
- ---help---
+ help
Enable support for ADDI-DATA APCI_16xx cards
To compile this driver as a module, choose M here: the module will be
@@ -641,7 +641,7 @@ config COMEDI_ADDI_APCI_16XX
config COMEDI_ADDI_APCI_2032
tristate "ADDI-DATA APCI_2032 support"
select COMEDI_ADDI_WATCHDOG
- ---help---
+ help
Enable support for ADDI-DATA APCI_2032 cards
To compile this driver as a module, choose M here: the module will be
@@ -650,7 +650,7 @@ config COMEDI_ADDI_APCI_2032
config COMEDI_ADDI_APCI_2200
tristate "ADDI-DATA APCI_2200 support"
select COMEDI_ADDI_WATCHDOG
- ---help---
+ help
Enable support for ADDI-DATA APCI_2200 cards
To compile this driver as a module, choose M here: the module will be
@@ -659,7 +659,7 @@ config COMEDI_ADDI_APCI_2200
config COMEDI_ADDI_APCI_3120
tristate "ADDI-DATA APCI_3120/3001 support"
depends on HAS_DMA
- ---help---
+ help
Enable support for ADDI-DATA APCI_3120/3001 cards
To compile this driver as a module, choose M here: the module will be
@@ -667,7 +667,7 @@ config COMEDI_ADDI_APCI_3120
config COMEDI_ADDI_APCI_3501
tristate "ADDI-DATA APCI_3501 support"
- ---help---
+ help
Enable support for ADDI-DATA APCI_3501 cards
To compile this driver as a module, choose M here: the module will be
@@ -675,7 +675,7 @@ config COMEDI_ADDI_APCI_3501
config COMEDI_ADDI_APCI_3XXX
tristate "ADDI-DATA APCI_3xxx support"
- ---help---
+ help
Enable support for ADDI-DATA APCI_3xxx cards
To compile this driver as a module, choose M here: the module will be
@@ -683,7 +683,7 @@ config COMEDI_ADDI_APCI_3XXX
config COMEDI_ADL_PCI6208
tristate "ADLink PCI-6208A support"
- ---help---
+ help
Enable support for ADLink PCI-6208A cards
To compile this driver as a module, choose M here: the module will be
@@ -691,7 +691,7 @@ config COMEDI_ADL_PCI6208
config COMEDI_ADL_PCI7X3X
tristate "ADLink PCI-723X/743X isolated digital i/o board support"
- ---help---
+ help
Enable support for ADlink PCI-723X/743X isolated digital i/o boards.
Supported boards include the 32-channel PCI-7230 (16 in/16 out),
PCI-7233 (32 in), and PCI-7234 (32 out) as well as the 64-channel
@@ -702,7 +702,7 @@ config COMEDI_ADL_PCI7X3X
config COMEDI_ADL_PCI8164
tristate "ADLink PCI-8164 4 Axes Motion Control board support"
- ---help---
+ help
Enable support for ADlink PCI-8164 4 Axes Motion Control board
To compile this driver as a module, choose M here: the module will be
@@ -711,7 +711,7 @@ config COMEDI_ADL_PCI8164
config COMEDI_ADL_PCI9111
tristate "ADLink PCI-9111HR support"
select COMEDI_8254
- ---help---
+ help
Enable support for ADlink PCI9111 cards
To compile this driver as a module, choose M here: the module will be
@@ -721,7 +721,7 @@ config COMEDI_ADL_PCI9118
tristate "ADLink PCI-9118DG, PCI-9118HG, PCI-9118HR support"
depends on HAS_DMA
select COMEDI_8254
- ---help---
+ help
Enable support for ADlink PCI-9118DG, PCI-9118HG, PCI-9118HR cards
To compile this driver as a module, choose M here: the module will be
@@ -730,7 +730,7 @@ config COMEDI_ADL_PCI9118
config COMEDI_ADV_PCI1710
tristate "Advantech PCI-171x and PCI-1731 support"
select COMEDI_8254
- ---help---
+ help
Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
PCI-1713 and PCI-1731
@@ -739,7 +739,7 @@ config COMEDI_ADV_PCI1710
config COMEDI_ADV_PCI1720
tristate "Advantech PCI-1720 support"
- ---help---
+ help
Enable support for Advantech PCI-1720 Analog Output board.
To compile this driver as a module, choose M here: the module will be
@@ -747,7 +747,7 @@ config COMEDI_ADV_PCI1720
config COMEDI_ADV_PCI1723
tristate "Advantech PCI-1723 support"
- ---help---
+ help
Enable support for Advantech PCI-1723 cards
To compile this driver as a module, choose M here: the module will be
@@ -755,7 +755,7 @@ config COMEDI_ADV_PCI1723
config COMEDI_ADV_PCI1724
tristate "Advantech PCI-1724U support"
- ---help---
+ help
Enable support for Advantech PCI-1724U cards. These are 32-channel
analog output cards with voltage and current loop output ranges and
14-bit resolution.
@@ -765,7 +765,7 @@ config COMEDI_ADV_PCI1724
config COMEDI_ADV_PCI1760
tristate "Advantech PCI-1760 support"
- ---help---
+ help
Enable support for Advantech PCI-1760 board.
To compile this driver as a module, choose M here: the module will be
@@ -775,7 +775,7 @@ config COMEDI_ADV_PCI_DIO
tristate "Advantech PCI DIO card support"
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for Advantech PCI DIO cards
PCI-1730, PCI-1733, PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U,
PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756,
@@ -787,7 +787,7 @@ config COMEDI_ADV_PCI_DIO
config COMEDI_AMPLC_DIO200_PCI
tristate "Amplicon PCI215/PCI272/PCIe215/PCIe236/PCIe296 DIO support"
select COMEDI_AMPLC_DIO200
- ---help---
+ help
Enable support for Amplicon PCI215, PCI272, PCIe215, PCIe236
and PCIe296 DIO boards.
@@ -797,7 +797,7 @@ config COMEDI_AMPLC_DIO200_PCI
config COMEDI_AMPLC_PC236_PCI
tristate "Amplicon PCI236 DIO board support"
select COMEDI_AMPLC_PC236
- ---help---
+ help
Enable support for Amplicon PCI236 DIO board.
To compile this driver as a module, choose M here: the module will be
@@ -805,7 +805,7 @@ config COMEDI_AMPLC_PC236_PCI
config COMEDI_AMPLC_PC263_PCI
tristate "Amplicon PCI263 relay board support"
- ---help---
+ help
Enable support for Amplicon PCI263 relay board. This is a PCI board
with 16 reed relay output channels.
@@ -815,7 +815,7 @@ config COMEDI_AMPLC_PC263_PCI
config COMEDI_AMPLC_PCI224
tristate "Amplicon PCI224 and PCI234 support"
select COMEDI_8254
- ---help---
+ help
Enable support for Amplicon PCI224 and PCI234 AO boards
To compile this driver as a module, choose M here: the module will be
@@ -825,7 +825,7 @@ config COMEDI_AMPLC_PCI230
tristate "Amplicon PCI230 and PCI260 support"
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for Amplicon PCI230 and PCI260 Multifunction I/O
boards
@@ -834,7 +834,7 @@ config COMEDI_AMPLC_PCI230
config COMEDI_CONTEC_PCI_DIO
tristate "Contec PIO1616L digital I/O board support"
- ---help---
+ help
Enable support for the Contec PIO1616L digital I/O board
To compile this driver as a module, choose M here: the module will be
@@ -843,7 +843,7 @@ config COMEDI_CONTEC_PCI_DIO
config COMEDI_DAS08_PCI
tristate "DAS-08 PCI support"
select COMEDI_DAS08
- ---help---
+ help
Enable support for PCI DAS-08 cards.
To compile this driver as a module, choose M here: the module will be
@@ -851,7 +851,7 @@ config COMEDI_DAS08_PCI
config COMEDI_DT3000
tristate "Data Translation DT3000 series support"
- ---help---
+ help
Enable support for Data Translation DT3000 series
DT3001, DT3001-PGL, DT3002, DT3003, DT3003-PGL, DT3004, DT3005 and
DT3004-200
@@ -861,7 +861,7 @@ config COMEDI_DT3000
config COMEDI_DYNA_PCI10XX
tristate "Dynalog PCI DAQ series support"
- ---help---
+ help
Enable support for Dynalog PCI DAQ series
PCI-1050
@@ -870,7 +870,7 @@ config COMEDI_DYNA_PCI10XX
config COMEDI_GSC_HPDI
tristate "General Standards PCI-HPDI32 / PMC-HPDI32 support"
- ---help---
+ help
Enable support for General Standards Corporation high speed parallel
digital interface rs485 boards PCI-HPDI32 and PMC-HPDI32.
Only receive mode works, transmit not supported.
@@ -880,13 +880,13 @@ config COMEDI_GSC_HPDI
config COMEDI_MF6X4
tristate "Humusoft MF634 and MF624 DAQ Card support"
- ---help---
+ help
This driver supports both Humusoft MF634 and MF624 Data acquisition
cards. The legacy Humusoft MF614 card is not supported.
config COMEDI_ICP_MULTI
tristate "Inova ICP_MULTI support"
- ---help---
+ help
Enable support for Inova ICP_MULTI card
To compile this driver as a module, choose M here: the module will be
@@ -895,7 +895,7 @@ config COMEDI_ICP_MULTI
config COMEDI_DAQBOARD2000
tristate "IOtech DAQboard/2000 support"
select COMEDI_8255
- ---help---
+ help
Enable support for the IOtech DAQboard/2000
To compile this driver as a module, choose M here: the module will be
@@ -903,7 +903,7 @@ config COMEDI_DAQBOARD2000
config COMEDI_JR3_PCI
tristate "JR3/PCI force sensor board support"
- ---help---
+ help
Enable support for JR3/PCI force sensor boards
To compile this driver as a module, choose M here: the module will be
@@ -911,7 +911,7 @@ config COMEDI_JR3_PCI
config COMEDI_KE_COUNTER
tristate "Kolter-Electronic PCI Counter 1 card support"
- ---help---
+ help
Enable support for Kolter-Electronic PCI Counter 1 cards
To compile this driver as a module, choose M here: the module will be
@@ -920,7 +920,7 @@ config COMEDI_KE_COUNTER
config COMEDI_CB_PCIDAS64
tristate "MeasurementComputing PCI-DAS 64xx, 60xx, and 4020 support"
select COMEDI_8255
- ---help---
+ help
Enable support for ComputerBoards/MeasurementComputing PCI-DAS 64xx,
60xx, and 4020 series with the PLX 9080 PCI controller
@@ -931,7 +931,7 @@ config COMEDI_CB_PCIDAS
tristate "MeasurementComputing PCI-DAS support"
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for ComputerBoards/MeasurementComputing PCI-DAS with
AMCC S5933 PCIcontroller: PCI-DAS1602/16, PCI-DAS1602/16jr,
PCI-DAS1602/12, PCI-DAS1200, PCI-DAS1200jr, PCI-DAS1000, PCI-DAS1001
@@ -943,7 +943,7 @@ config COMEDI_CB_PCIDAS
config COMEDI_CB_PCIDDA
tristate "MeasurementComputing PCI-DDA series support"
select COMEDI_8255
- ---help---
+ help
Enable support for ComputerBoards/MeasurementComputing PCI-DDA
series: PCI-DDA08/12, PCI-DDA04/12, PCI-DDA02/12, PCI-DDA08/16,
PCI-DDA04/16 and PCI-DDA02/16
@@ -955,7 +955,7 @@ config COMEDI_CB_PCIMDAS
tristate "MeasurementComputing PCIM-DAS1602/16, PCIe-DAS1602/16 support"
select COMEDI_8254
select COMEDI_8255
- ---help---
+ help
Enable support for ComputerBoards/MeasurementComputing PCI Migration
series PCIM-DAS1602/16 and PCIe-DAS1602/16.
@@ -965,7 +965,7 @@ config COMEDI_CB_PCIMDAS
config COMEDI_CB_PCIMDDA
tristate "MeasurementComputing PCIM-DDA06-16 support"
select COMEDI_8255
- ---help---
+ help
Enable support for ComputerBoards/MeasurementComputing PCIM-DDA06-16
To compile this driver as a module, choose M here: the module will be
@@ -974,7 +974,7 @@ config COMEDI_CB_PCIMDDA
config COMEDI_ME4000
tristate "Meilhaus ME-4000 support"
select COMEDI_8254
- ---help---
+ help
Enable support for Meilhaus PCI data acquisition cards
ME-4650, ME-4670i, ME-4680, ME-4680i and ME-4680is
@@ -983,7 +983,7 @@ config COMEDI_ME4000
config COMEDI_ME_DAQ
tristate "Meilhaus ME-2000i, ME-2600i, ME-3000vm1 support"
- ---help---
+ help
Enable support for Meilhaus PCI data acquisition cards
ME-2000i, ME-2600i and ME-3000vm1
@@ -992,7 +992,7 @@ config COMEDI_ME_DAQ
config COMEDI_NI_6527
tristate "NI 6527 support"
- ---help---
+ help
Enable support for the National Instruments 6527 PCI card
To compile this driver as a module, choose M here: the module will be
@@ -1000,7 +1000,7 @@ config COMEDI_NI_6527
config COMEDI_NI_65XX
tristate "NI 65xx static dio PCI card support"
- ---help---
+ help
Enable support for National Instruments 65xx static dio boards.
Supported devices: National Instruments PCI-6509 (ni_65xx),
PXI-6509, PCI-6510, PCI-6511, PXI-6511, PCI-6512, PXI-6512, PCI-6513,
@@ -1014,7 +1014,7 @@ config COMEDI_NI_660X
tristate "NI 660x counter/timer PCI card support"
depends on HAS_DMA
select COMEDI_NI_TIOCMD
- ---help---
+ help
Enable support for National Instruments PCI-6601 (ni_660x), PCI-6602,
PXI-6602, PXI-6608, PCI-6624, and PXI-6624.
@@ -1023,7 +1023,7 @@ config COMEDI_NI_660X
config COMEDI_NI_670X
tristate "NI 670x PCI card support"
- ---help---
+ help
Enable support for National Instruments PCI-6703 and PCI-6704
To compile this driver as a module, choose M here: the module will be
@@ -1032,7 +1032,7 @@ config COMEDI_NI_670X
config COMEDI_NI_LABPC_PCI
tristate "NI Lab-PC PCI-1200 support"
select COMEDI_NI_LABPC
- ---help---
+ help
Enable support for National Instruments Lab-PC PCI-1200.
To compile this driver as a module, choose M here: the module will be
@@ -1043,7 +1043,7 @@ config COMEDI_NI_PCIDIO
depends on HAS_DMA
select COMEDI_MITE
select COMEDI_8255
- ---help---
+ help
Enable support for National Instruments PCI-DIO-32HS, PXI-6533,
PCI-6533 and PCI-6534
@@ -1055,7 +1055,7 @@ config COMEDI_NI_PCIMIO
depends on HAS_DMA
select COMEDI_NI_TIOCMD
select COMEDI_8255
- ---help---
+ help
Enable support for National Instruments PCI-MIO-E series and M series
(all boards): PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1,
PCI-MIO-16E-4, PCI-6014, PCI-6040E, PXI-6040E, PCI-6030E, PCI-6031E,
@@ -1075,7 +1075,7 @@ config COMEDI_NI_PCIMIO
config COMEDI_RTD520
tristate "Real Time Devices PCI4520/DM7520 support"
select COMEDI_8254
- ---help---
+ help
Enable support for Real Time Devices PCI4520/DM7520
To compile this driver as a module, choose M here: the module will be
@@ -1083,7 +1083,7 @@ config COMEDI_RTD520
config COMEDI_S626
tristate "Sensoray 626 support"
- ---help---
+ help
Enable support for Sensoray 626
To compile this driver as a module, choose M here: the module will be
@@ -1104,7 +1104,7 @@ endif # COMEDI_PCI_DRIVERS
menuconfig COMEDI_PCMCIA_DRIVERS
tristate "Comedi PCMCIA drivers"
depends on PCMCIA
- ---help---
+ help
Enable support for comedi PCMCIA drivers.
To compile this support as a module, choose M here: the module will
@@ -1115,7 +1115,7 @@ if COMEDI_PCMCIA_DRIVERS
config COMEDI_CB_DAS16_CS
tristate "CB DAS16 series PCMCIA support"
select COMEDI_8254
- ---help---
+ help
Enable support for the ComputerBoards/MeasurementComputing PCMCIA
cards DAS16/16, PCM-DAS16D/12 and PCM-DAS16s/16
@@ -1125,7 +1125,7 @@ config COMEDI_CB_DAS16_CS
config COMEDI_DAS08_CS
tristate "CB DAS08 PCMCIA support"
select COMEDI_DAS08
- ---help---
+ help
Enable support for the ComputerBoards/MeasurementComputing DAS-08
PCMCIA card
@@ -1134,7 +1134,7 @@ config COMEDI_DAS08_CS
config COMEDI_NI_DAQ_700_CS
tristate "NI DAQCard-700 PCMCIA support"
- ---help---
+ help
Enable support for the National Instruments PCMCIA DAQCard-700 DIO
To compile this driver as a module, choose M here: the module will be
@@ -1143,7 +1143,7 @@ config COMEDI_NI_DAQ_700_CS
config COMEDI_NI_DAQ_DIO24_CS
tristate "NI DAQ-Card DIO-24 PCMCIA support"
select COMEDI_8255
- ---help---
+ help
Enable support for the National Instruments PCMCIA DAQ-Card DIO-24
To compile this driver as a module, choose M here: the module will be
@@ -1152,7 +1152,7 @@ config COMEDI_NI_DAQ_DIO24_CS
config COMEDI_NI_LABPC_CS
tristate "NI DAQCard-1200 PCMCIA support"
select COMEDI_NI_LABPC
- ---help---
+ help
Enable support for the National Instruments PCMCIA DAQCard-1200
To compile this driver as a module, choose M here: the module will be
@@ -1162,7 +1162,7 @@ config COMEDI_NI_MIO_CS
tristate "NI DAQCard E series PCMCIA support"
select COMEDI_NI_TIO
select COMEDI_8255
- ---help---
+ help
Enable support for the National Instruments PCMCIA DAQCard E series
DAQCard-ai-16xe-50, DAQCard-ai-16e-4, DAQCard-6062E, DAQCard-6024E
and DAQCard-6036E
@@ -1172,7 +1172,7 @@ config COMEDI_NI_MIO_CS
config COMEDI_QUATECH_DAQP_CS
tristate "Quatech DAQP PCMCIA data capture card support"
- ---help---
+ help
Enable support for the Quatech DAQP PCMCIA data capture cards
DAQP-208 and DAQP-308
@@ -1184,7 +1184,7 @@ endif # COMEDI_PCMCIA_DRIVERS
menuconfig COMEDI_USB_DRIVERS
tristate "Comedi USB drivers"
depends on USB
- ---help---
+ help
Enable support for comedi USB drivers.
To compile this support as a module, choose M here: the module will
@@ -1194,7 +1194,7 @@ if COMEDI_USB_DRIVERS
config COMEDI_DT9812
tristate "DataTranslation DT9812 USB module support"
- ---help---
+ help
Enable support for the Data Translation DT9812 USB module
To compile this driver as a module, choose M here: the module will be
@@ -1202,7 +1202,7 @@ config COMEDI_DT9812
config COMEDI_NI_USB6501
tristate "NI USB-6501 support"
- ---help---
+ help
Enable support for the National Instruments USB-6501 module.
The NI USB-6501 is a Full-Speed USB 2.0 (12 Mbit/s) device that
@@ -1213,7 +1213,7 @@ config COMEDI_NI_USB6501
config COMEDI_USBDUX
tristate "ITL USB-DUX-D support"
- ---help---
+ help
Enable support for the Incite Technology Ltd USB-DUX-D Board
To compile this driver as a module, choose M here: the module will be
@@ -1221,7 +1221,7 @@ config COMEDI_USBDUX
config COMEDI_USBDUXFAST
tristate "ITL USB-DUXfast support"
- ---help---
+ help
Enable support for the Incite Technology Ltd USB-DUXfast Board
To compile this driver as a module, choose M here: the module will be
@@ -1229,7 +1229,7 @@ config COMEDI_USBDUXFAST
config COMEDI_USBDUXSIGMA
tristate "ITL USB-DUXsigma support"
- ---help---
+ help
Enable support for the Incite Technology Ltd USB-DUXsigma Board
To compile this driver as a module, choose M here: the module will be
@@ -1237,7 +1237,7 @@ config COMEDI_USBDUXSIGMA
config COMEDI_VMK80XX
tristate "Velleman VM110/VM140 USB Board support"
- ---help---
+ help
Build the Velleman USB Board Low-Level Driver supporting the
K8055/K8061 aka VM110/VM140 devices
@@ -1255,7 +1255,7 @@ config COMEDI_8255
config COMEDI_8255_SA
tristate "Standalone 8255 support"
select COMEDI_8255
- ---help---
+ help
Enable support for 8255 digital I/O as a standalone driver.
You should enable compilation this driver if you plan to use a board
@@ -1272,7 +1272,7 @@ config COMEDI_8255_SA
config COMEDI_KCOMEDILIB
tristate "Comedi kcomedilib"
- ---help---
+ help
Build the kcomedilib.
This is a kernel module used to open and manipulate Comedi devices
diff --git a/drivers/staging/comedi/comedi_buf.c b/drivers/staging/comedi/comedi_buf.c
index f693c2c0bec3..d2c8cc72a99d 100644
--- a/drivers/staging/comedi/comedi_buf.c
+++ b/drivers/staging/comedi/comedi_buf.c
@@ -211,6 +211,8 @@ int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
{
struct comedi_async *async = s->async;
+ lockdep_assert_held(&dev->mutex);
+
/* Round up new_size to multiple of PAGE_SIZE */
new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index 0caae4a5c471..f6d1287c7b83 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -164,6 +164,7 @@ static bool comedi_clear_board_dev(struct comedi_device *dev)
unsigned int i = dev->minor;
bool cleared = false;
+ lockdep_assert_held(&dev->mutex);
mutex_lock(&comedi_board_minor_table_lock);
if (dev == comedi_board_minor_table[i]) {
comedi_board_minor_table[i] = NULL;
@@ -260,6 +261,7 @@ comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor)
{
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (minor >= COMEDI_NUM_BOARD_MINORS) {
s = comedi_subdevice_from_minor(dev, minor);
if (!s || (s->subdev_flags & SDF_CMD_READ))
@@ -273,6 +275,7 @@ comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor)
{
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (minor >= COMEDI_NUM_BOARD_MINORS) {
s = comedi_subdevice_from_minor(dev, minor);
if (!s || (s->subdev_flags & SDF_CMD_WRITE))
@@ -336,6 +339,8 @@ static int resize_async_buffer(struct comedi_device *dev,
struct comedi_async *async = s->async;
int retval;
+ lockdep_assert_held(&dev->mutex);
+
if (new_size > async->max_bufsize)
return -EPERM;
@@ -726,6 +731,7 @@ static void do_become_nonbusy(struct comedi_device *dev,
{
struct comedi_async *async = s->async;
+ lockdep_assert_held(&dev->mutex);
comedi_update_subdevice_runflags(s, COMEDI_SRF_RUNNING, 0);
if (async) {
comedi_buf_reset(s);
@@ -745,6 +751,7 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
int ret = 0;
+ lockdep_assert_held(&dev->mutex);
if (comedi_is_subdevice_running(s) && s->cancel)
ret = s->cancel(dev, s);
@@ -758,6 +765,7 @@ void comedi_device_cancel_all(struct comedi_device *dev)
struct comedi_subdevice *s;
int i;
+ lockdep_assert_held(&dev->mutex);
if (!dev->attached)
return;
@@ -773,6 +781,7 @@ static int is_device_busy(struct comedi_device *dev)
struct comedi_subdevice *s;
int i;
+ lockdep_assert_held(&dev->mutex);
if (!dev->attached)
return 0;
@@ -805,6 +814,7 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
{
struct comedi_devconfig it;
+ lockdep_assert_held(&dev->mutex);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -860,6 +870,7 @@ static int do_bufconfig_ioctl(struct comedi_device *dev,
struct comedi_subdevice *s;
int retval = 0;
+ lockdep_assert_held(&dev->mutex);
if (copy_from_user(&bc, arg, sizeof(bc)))
return -EFAULT;
@@ -920,6 +931,7 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
struct comedi_subdevice *s;
struct comedi_devinfo devinfo;
+ lockdep_assert_held(&dev->mutex);
memset(&devinfo, 0, sizeof(devinfo));
/* fill devinfo structure */
@@ -966,6 +978,7 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
struct comedi_subdinfo *tmp, *us;
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
tmp = kcalloc(dev->n_subdevices, sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
@@ -1039,6 +1052,7 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
struct comedi_subdevice *s;
struct comedi_chaninfo it;
+ lockdep_assert_held(&dev->mutex);
if (copy_from_user(&it, arg, sizeof(it)))
return -EFAULT;
@@ -1098,6 +1112,7 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
int retval = 0;
bool become_nonbusy = false;
+ lockdep_assert_held(&dev->mutex);
if (copy_from_user(&bi, arg, sizeof(bi)))
return -EFAULT;
@@ -1282,6 +1297,7 @@ static int check_insn_device_config_length(struct comedi_insn *insn,
*/
static int get_valid_routes(struct comedi_device *dev, unsigned int *data)
{
+ lockdep_assert_held(&dev->mutex);
data[1] = dev->get_valid_routes(dev, data[1], data + 2);
return 0;
}
@@ -1293,6 +1309,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
int ret = 0;
int i;
+ lockdep_assert_held(&dev->mutex);
if (insn->insn & INSN_MASK_SPECIAL) {
/* a non-subdevice instruction */
@@ -1513,6 +1530,7 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
int i = 0;
int ret = 0;
+ lockdep_assert_held(&dev->mutex);
if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
return -EFAULT;
@@ -1605,6 +1623,7 @@ static int do_insn_ioctl(struct comedi_device *dev,
unsigned int n_data = MIN_SAMPLES;
int ret = 0;
+ lockdep_assert_held(&dev->mutex);
if (copy_from_user(&insn, arg, sizeof(insn)))
return -EFAULT;
@@ -1655,6 +1674,7 @@ static int __comedi_get_user_cmd(struct comedi_device *dev,
{
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (copy_from_user(cmd, arg, sizeof(*cmd))) {
dev_dbg(dev->class_dev, "bad cmd address\n");
return -EFAULT;
@@ -1713,6 +1733,7 @@ static int __comedi_get_user_chanlist(struct comedi_device *dev,
unsigned int *chanlist;
int ret;
+ lockdep_assert_held(&dev->mutex);
cmd->chanlist = NULL;
chanlist = memdup_user(user_chanlist,
cmd->chanlist_len * sizeof(unsigned int));
@@ -1754,6 +1775,8 @@ static int do_cmd_ioctl(struct comedi_device *dev,
unsigned int __user *user_chanlist;
int ret;
+ lockdep_assert_held(&dev->mutex);
+
/* get the user's cmd and do some simple validation */
ret = __comedi_get_user_cmd(dev, arg, &cmd);
if (ret)
@@ -1861,6 +1884,8 @@ static int do_cmdtest_ioctl(struct comedi_device *dev,
unsigned int __user *user_chanlist;
int ret;
+ lockdep_assert_held(&dev->mutex);
+
/* get the user's cmd and do some simple validation */
ret = __comedi_get_user_cmd(dev, arg, &cmd);
if (ret)
@@ -1914,6 +1939,7 @@ static int do_lock_ioctl(struct comedi_device *dev, unsigned long arg,
unsigned long flags;
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (arg >= dev->n_subdevices)
return -EINVAL;
s = &dev->subdevices[arg];
@@ -1946,6 +1972,7 @@ static int do_unlock_ioctl(struct comedi_device *dev, unsigned long arg,
{
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (arg >= dev->n_subdevices)
return -EINVAL;
s = &dev->subdevices[arg];
@@ -1980,6 +2007,7 @@ static int do_cancel_ioctl(struct comedi_device *dev, unsigned long arg,
{
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (arg >= dev->n_subdevices)
return -EINVAL;
s = &dev->subdevices[arg];
@@ -2013,6 +2041,7 @@ static int do_poll_ioctl(struct comedi_device *dev, unsigned long arg,
{
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->mutex);
if (arg >= dev->n_subdevices)
return -EINVAL;
s = &dev->subdevices[arg];
@@ -2048,6 +2077,7 @@ static int do_setrsubd_ioctl(struct comedi_device *dev, unsigned long arg,
struct comedi_file *cfp = file->private_data;
struct comedi_subdevice *s_old, *s_new;
+ lockdep_assert_held(&dev->mutex);
if (arg >= dev->n_subdevices)
return -EINVAL;
@@ -2090,6 +2120,7 @@ static int do_setwsubd_ioctl(struct comedi_device *dev, unsigned long arg,
struct comedi_file *cfp = file->private_data;
struct comedi_subdevice *s_old, *s_new;
+ lockdep_assert_held(&dev->mutex);
if (arg >= dev->n_subdevices)
return -EINVAL;
@@ -2995,6 +3026,7 @@ static int __init comedi_init(void)
goto out_cleanup_board_minors;
}
/* comedi_alloc_board_minor() locked the mutex */
+ lockdep_assert_held(&dev->mutex);
mutex_unlock(&dev->mutex);
}
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index 5a32b8fc000e..750a6ff3c03c 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -159,6 +159,8 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
int i;
struct comedi_subdevice *s;
+ lockdep_assert_held(&dev->attach_lock);
+ lockdep_assert_held(&dev->mutex);
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
s = &dev->subdevices[i];
@@ -196,6 +198,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
void comedi_device_detach(struct comedi_device *dev)
{
+ lockdep_assert_held(&dev->mutex);
comedi_device_cancel_all(dev);
down_write(&dev->attach_lock);
dev->attached = false;
@@ -643,6 +646,7 @@ static int __comedi_device_postconfig_async(struct comedi_device *dev,
unsigned int buf_size;
int ret;
+ lockdep_assert_held(&dev->mutex);
if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) {
dev_warn(dev->class_dev,
"async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n");
@@ -690,6 +694,7 @@ static int __comedi_device_postconfig(struct comedi_device *dev)
int ret;
int i;
+ lockdep_assert_held(&dev->mutex);
if (!dev->insn_device_config)
dev->insn_device_config = insn_device_inval;
@@ -747,6 +752,7 @@ static int comedi_device_postconfig(struct comedi_device *dev)
{
int ret;
+ lockdep_assert_held(&dev->mutex);
ret = __comedi_device_postconfig(dev);
if (ret < 0)
return ret;
@@ -946,6 +952,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
struct comedi_driver *driv;
int ret;
+ lockdep_assert_held(&dev->mutex);
if (dev->attached)
return -EBUSY;
@@ -1053,18 +1060,19 @@ int comedi_auto_config(struct device *hardware_device,
return PTR_ERR(dev);
}
/* Note: comedi_alloc_board_minor() locked dev->mutex. */
+ lockdep_assert_held(&dev->mutex);
dev->driver = driver;
dev->board_name = dev->driver->driver_name;
ret = driver->auto_attach(dev, context);
if (ret >= 0)
ret = comedi_device_postconfig(dev);
- mutex_unlock(&dev->mutex);
if (ret < 0) {
dev_warn(hardware_device,
"driver '%s' failed to auto-configure device.\n",
driver->driver_name);
+ mutex_unlock(&dev->mutex);
comedi_release_hardware_device(hardware_device);
} else {
/*
@@ -1074,6 +1082,7 @@ int comedi_auto_config(struct device *hardware_device,
dev_info(dev->class_dev,
"driver '%s' has successfully auto-configured '%s'.\n",
driver->driver_name, dev->board_name);
+ mutex_unlock(&dev->mutex);
}
return ret;
}
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
index 6a93b04f1fdf..dbff0f7e7cf5 100644
--- a/drivers/staging/comedi/drivers/adv_pci1710.c
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -317,7 +317,7 @@ static int pci1710_ai_read_sample(struct comedi_device *dev,
chan = sample >> 12;
if (chan != devpriv->act_chanlist[cur_chan]) {
dev_err(dev->class_dev,
- "A/D data droput: received from channel %d, expected %d\n",
+ "A/D data dropout: received from channel %d, expected %d\n",
chan, devpriv->act_chanlist[cur_chan]);
return -ENODATA;
}
diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c
index 05207a519755..8a1f9efe7d4e 100644
--- a/drivers/staging/comedi/drivers/dt2811.c
+++ b/drivers/staging/comedi/drivers/dt2811.c
@@ -52,7 +52,7 @@
#define DT2811_ADCSR_ADBUSY BIT(5) /* r 1=A/D busy */
#define DT2811_ADCSR_CLRERROR BIT(4)
#define DT2811_ADCSR_DMAENB BIT(3) /* r/w 1=dma ena */
-#define DT2811_ADCSR_INTENB BIT(2) /* r/w 1=interupts ena */
+#define DT2811_ADCSR_INTENB BIT(2) /* r/w 1=interrupts ena */
#define DT2811_ADCSR_ADMODE(x) (((x) & 0x3) << 0)
#define DT2811_ADGCR_REG 0x01 /* r/w A/D Gain/Channel */
diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c
index 9f165f1cefa5..634f57730c1e 100644
--- a/drivers/staging/comedi/drivers/dt9812.c
+++ b/drivers/staging/comedi/drivers/dt9812.c
@@ -835,11 +835,8 @@ static void dt9812_detach(struct comedi_device *dev)
if (!devpriv)
return;
- mutex_lock(&devpriv->mut);
-
+ mutex_destroy(&devpriv->mut);
usb_set_intfdata(intf, NULL);
-
- mutex_unlock(&devpriv->mut);
}
static struct comedi_driver dt9812_driver = {
diff --git a/drivers/staging/comedi/drivers/dyna_pci10xx.c b/drivers/staging/comedi/drivers/dyna_pci10xx.c
index 9bdd5bf2eb99..c224422bb126 100644
--- a/drivers/staging/comedi/drivers/dyna_pci10xx.c
+++ b/drivers/staging/comedi/drivers/dyna_pci10xx.c
@@ -106,10 +106,6 @@ static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
{
struct dyna_pci10xx_private *devpriv = dev->private;
int n;
- unsigned int chan, range;
-
- chan = CR_CHAN(insn->chanspec);
- range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
mutex_lock(&devpriv->mutex);
for (n = 0; n < insn->n; n++) {
@@ -194,17 +190,15 @@ static int dyna_pci10xx_auto_attach(struct comedi_device *dev,
s->n_chan = 16;
s->maxdata = 0x0FFF;
s->range_table = &range_pci1050_ai;
- s->len_chanlist = 16;
s->insn_read = dyna_pci10xx_insn_read_ai;
/* analog output */
s = &dev->subdevices[1];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
- s->n_chan = 16;
+ s->n_chan = 1;
s->maxdata = 0x0FFF;
s->range_table = &range_unipolar10;
- s->len_chanlist = 16;
s->insn_write = dyna_pci10xx_insn_write_ao;
/* digital input */
@@ -214,7 +208,6 @@ static int dyna_pci10xx_auto_attach(struct comedi_device *dev,
s->n_chan = 16;
s->maxdata = 1;
s->range_table = &range_digital;
- s->len_chanlist = 16;
s->insn_bits = dyna_pci10xx_di_insn_bits;
/* digital output */
@@ -224,7 +217,6 @@ static int dyna_pci10xx_auto_attach(struct comedi_device *dev,
s->n_chan = 16;
s->maxdata = 1;
s->range_table = &range_digital;
- s->len_chanlist = 16;
s->state = 0;
s->insn_bits = dyna_pci10xx_do_insn_bits;
diff --git a/drivers/staging/comedi/drivers/ni_usb6501.c b/drivers/staging/comedi/drivers/ni_usb6501.c
index 808ed92ed66f..360e86a19fe3 100644
--- a/drivers/staging/comedi/drivers/ni_usb6501.c
+++ b/drivers/staging/comedi/drivers/ni_usb6501.c
@@ -463,10 +463,8 @@ static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
size = usb_endpoint_maxp(devpriv->ep_tx);
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
- if (!devpriv->usb_tx_buf) {
- kfree(devpriv->usb_rx_buf);
+ if (!devpriv->usb_tx_buf)
return -ENOMEM;
- }
return 0;
}
@@ -518,6 +516,9 @@ static int ni6501_auto_attach(struct comedi_device *dev,
if (!devpriv)
return -ENOMEM;
+ mutex_init(&devpriv->mut);
+ usb_set_intfdata(intf, devpriv);
+
ret = ni6501_find_endpoints(dev);
if (ret)
return ret;
@@ -526,9 +527,6 @@ static int ni6501_auto_attach(struct comedi_device *dev,
if (ret)
return ret;
- mutex_init(&devpriv->mut);
- usb_set_intfdata(intf, devpriv);
-
ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
@@ -564,14 +562,12 @@ static void ni6501_detach(struct comedi_device *dev)
if (!devpriv)
return;
- mutex_lock(&devpriv->mut);
+ mutex_destroy(&devpriv->mut);
usb_set_intfdata(intf, NULL);
kfree(devpriv->usb_rx_buf);
kfree(devpriv->usb_tx_buf);
-
- mutex_unlock(&devpriv->mut);
}
static struct comedi_driver ni6501_driver = {
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index de177418190f..b8f54b7fb34a 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -1691,6 +1691,8 @@ static void usbdux_detach(struct comedi_device *dev)
usbdux_free_usb_buffers(dev);
mutex_unlock(&devpriv->mut);
+
+ mutex_destroy(&devpriv->mut);
}
static struct comedi_driver usbdux_driver = {
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index 0d54f394dbd2..04bc488385e6 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -993,6 +993,8 @@ static void usbduxfast_detach(struct comedi_device *dev)
kfree(devpriv->duxbuf);
mutex_unlock(&devpriv->mut);
+
+ mutex_destroy(&devpriv->mut);
}
static struct comedi_driver usbduxfast_driver = {
diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c
index af5605a875e2..3cc40d2544be 100644
--- a/drivers/staging/comedi/drivers/usbduxsigma.c
+++ b/drivers/staging/comedi/drivers/usbduxsigma.c
@@ -1577,6 +1577,8 @@ static void usbduxsigma_detach(struct comedi_device *dev)
usbduxsigma_free_usb_buffers(dev);
mutex_unlock(&devpriv->mut);
+
+ mutex_destroy(&devpriv->mut);
}
static struct comedi_driver usbduxsigma_driver = {
diff --git a/drivers/staging/comedi/drivers/vmk80xx.c b/drivers/staging/comedi/drivers/vmk80xx.c
index 6234b649d887..65dc6c51037e 100644
--- a/drivers/staging/comedi/drivers/vmk80xx.c
+++ b/drivers/staging/comedi/drivers/vmk80xx.c
@@ -682,10 +682,8 @@ static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
size = usb_endpoint_maxp(devpriv->ep_tx);
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
- if (!devpriv->usb_tx_buf) {
- kfree(devpriv->usb_rx_buf);
+ if (!devpriv->usb_tx_buf)
return -ENOMEM;
- }
return 0;
}
@@ -800,6 +798,8 @@ static int vmk80xx_auto_attach(struct comedi_device *dev,
devpriv->model = board->model;
+ sema_init(&devpriv->limit_sem, 8);
+
ret = vmk80xx_find_usb_endpoints(dev);
if (ret)
return ret;
@@ -808,8 +808,6 @@ static int vmk80xx_auto_attach(struct comedi_device *dev,
if (ret)
return ret;
- sema_init(&devpriv->limit_sem, 8);
-
usb_set_intfdata(intf, devpriv);
if (devpriv->model == VMK8055_MODEL)
diff --git a/drivers/staging/erofs/data.c b/drivers/staging/erofs/data.c
index 0714061ba888..c64ec76643d4 100644
--- a/drivers/staging/erofs/data.c
+++ b/drivers/staging/erofs/data.c
@@ -304,7 +304,7 @@ submit_bio_retry:
*last_block = current_block;
/* shift in advance in case of it followed by too many gaps */
- if (unlikely(bio->bi_vcnt >= bio->bi_max_vecs)) {
+ if (bio->bi_iter.bi_size >= bio->bi_max_vecs * PAGE_SIZE) {
/* err should reassign to 0 after submitting */
err = 0;
goto submit_bio_out;
diff --git a/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev b/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev
new file mode 100644
index 000000000000..45f631ea32a6
--- /dev/null
+++ b/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev
@@ -0,0 +1,31 @@
+What: /dev/fieldbus_devX
+Date: December 2018
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ The cdev interface to drivers for Fieldbus Device Memory
+ (aka. Process Memory).
+
+ The following file operations are supported:
+
+ open(2)
+ Create an I/O context associated with the file descriptor.
+
+ read(2)
+ Read from Process Memory's "read area".
+ Clears POLLERR | POLLPRI from the file descriptor.
+
+ write(2)
+ Write to Process Memory's "write area".
+
+ poll(2), select(2), epoll_wait(2) etc.
+ When a "Process Memory Read Area Changed" event occurs,
+ POLLERR | POLLPRI will be set on the file descriptor.
+ Note that POLLIN | POLLOUT events are always set, because the
+ process memory area is always readable and writable.
+
+ close(2)
+ Free up the I/O context that was associated
+ with the file descriptor.
+
+Users: TBD
diff --git a/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev b/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev
new file mode 100644
index 000000000000..439f14d33c3b
--- /dev/null
+++ b/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev
@@ -0,0 +1,62 @@
+What: /sys/class/fieldbus_dev/fieldbus_devX/card_name
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ Human-readable name of the Fieldbus Device.
+
+What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_type
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ The type of fieldbus implemented by this device.
+ Possible values:
+ 'unknown'
+ 'profinet'
+
+What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_id
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ The unique fieldbus id associated with this device.
+ The exact format of this id is fieldbus type dependent, e.g.
+ a mac address for profinet.
+
+What: /sys/class/fieldbus_dev/fieldbus_devX/read_area_size
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ The size, in bytes, of the Process Memory read area.
+ Note: this area is accessible by reading from the associated
+ character device (/dev/fieldbus_devX).
+
+What: /sys/class/fieldbus_dev/fieldbus_devX/write_area_size
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ The size, in bytes, of the Process Memory write area.
+ Note: this area is accessible by writing to the associated
+ character device (/dev/fieldbus_devX)
+
+What: /sys/class/fieldbus_dev/fieldbus_devX/online
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ Whether the fieldbus is online or offline.
+ Possible values:
+ '1' meaning 'online'
+ '0' meaning 'offline'
+ Note: an uevent is generated when this property changes.
+
+What: /sys/class/fieldbus_dev/fieldbus_devX/enabled
+KernelVersion: 5.1 (staging)
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+ Whether the device is enabled (power on) or
+ disabled (power off).
+ Possible values:
+ '1' meaning enabled
+ '0' meaning disabled
+ Normally a r/o property, but optionally r/w:
+ Writing '1' enables the device (power on) with default
+ settings.
+ Writing '0' disables the card (power off).
diff --git a/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt b/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt
new file mode 100644
index 000000000000..56af3f650fa3
--- /dev/null
+++ b/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt
@@ -0,0 +1,66 @@
+ Fieldbus-Device Subsystem
+ ============================================
+
+Part 0 - What is a Fieldbus Device ?
+------------------------------------
+
+Fieldbus is the name of a family of industrial computer network protocols used
+for real-time distributed control, standardized as IEC 61158.
+
+A complex automated industrial system -- such as manufacturing assembly line --
+usually needs a distributed control system -- an organized hierarchy of
+controller systems -- to function. In this hierarchy, there is usually a
+Human Machine Interface (HMI) at the top, where an operator can monitor or
+operate the system. This is typically linked to a middle layer of programmable
+logic controllers (PLC) via a non-time-critical communications system
+(e.g. Ethernet). At the bottom of the control chain is the fieldbus that links
+the PLCs to the components that actually do the work, such as sensors,
+actuators, electric motors, console lights, switches, valves and contactors.
+
+(Source: Wikipedia)
+
+A "Fieldbus Device" is such an actuator, motor, console light, switch, ...
+controlled via the Fieldbus by a PLC aka "Fieldbus Controller".
+
+Communication between PLC and device typically happens via process data memory,
+separated into input and output areas. The Fieldbus then cyclically transfers
+the PLC's output area to the device's input area, and vice versa.
+
+Part I - Why do we need this subsystem?
+---------------------------------------
+
+Fieldbus device (client) adapters are commercially available. They allow data
+exchange with a PLC aka "Fieldbus Controller" via process data memory.
+
+They are typically used when a Linux device wants to expose itself as an
+actuator, motor, console light, switch, etc. over the fieldbus.
+
+The purpose of this subsystem is:
+a) present a general, standardized, extensible API/ABI to userspace; and
+b) present a convenient interface to drivers.
+
+Part II - How can drivers use the subsystem?
+--------------------------------------------
+
+Any driver that wants to register as a Fieldbus Device should allocate and
+populate a 'struct fieldbus_dev' (from include/linux/fieldbus_dev.h).
+Registration then happens by calling fieldbus_dev_register().
+
+Part III - How can userspace use the subsystem?
+-----------------------------------------------
+
+Fieldbus protocols and adapters are diverse and varied. However, they share
+a limited few common behaviours and properties. This allows us to define
+a simple interface consisting of a character device and a set of sysfs files:
+
+See:
+Documentation/ABI/testing/sysfs-class-fieldbus-dev
+Documentation/ABI/testing/fieldbus-dev-cdev
+
+Note that this simple interface does not provide a way to modify adapter
+configuration settings. It is therefore useful only for adapters that get their
+configuration settings some other way, e.g. non-volatile memory on the adapter,
+through the network, ...
+
+At a later phase, this simple interface can easily co-exist with a future
+(netlink-based ?) configuration settings interface.
diff --git a/drivers/staging/fieldbus/Kconfig b/drivers/staging/fieldbus/Kconfig
new file mode 100644
index 000000000000..e5e28e52c59b
--- /dev/null
+++ b/drivers/staging/fieldbus/Kconfig
@@ -0,0 +1,18 @@
+menuconfig FIELDBUS_DEV
+ tristate "Fieldbus Device Support"
+ help
+ Support for Fieldbus Device Adapters.
+
+ Fieldbus device (client) adapters allow data exchange with a PLC aka.
+ "Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)
+
+ They are typically used when a Linux device wants to expose itself
+ as an actuator, motor, console light, switch, etc. over the fieldbus.
+
+ This framework is designed to provide a generic interface to Fieldbus
+ Devices from both the Linux Kernel and the userspace.
+
+ If unsure, say no.
+
+source "drivers/staging/fieldbus/anybuss/Kconfig"
+
diff --git a/drivers/staging/fieldbus/Makefile b/drivers/staging/fieldbus/Makefile
new file mode 100644
index 000000000000..bdf645d41344
--- /dev/null
+++ b/drivers/staging/fieldbus/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for fieldbus_dev drivers.
+#
+
+obj-$(CONFIG_FIELDBUS_DEV) += fieldbus_dev.o anybuss/
+fieldbus_dev-y := dev_core.o
diff --git a/drivers/staging/fieldbus/TODO b/drivers/staging/fieldbus/TODO
new file mode 100644
index 000000000000..6d6626af4ec7
--- /dev/null
+++ b/drivers/staging/fieldbus/TODO
@@ -0,0 +1,5 @@
+TODO:
+-Get more people/drivers to use the Fieldbus userspace ABI. It requires
+ verification/sign-off by multiple users.
+
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
diff --git a/drivers/staging/fieldbus/anybuss/Kconfig b/drivers/staging/fieldbus/anybuss/Kconfig
new file mode 100644
index 000000000000..41f241c73826
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/Kconfig
@@ -0,0 +1,39 @@
+config HMS_ANYBUSS_BUS
+ tristate "HMS Anybus-S Bus Support"
+ select REGMAP
+ depends on OF && FIELDBUS_DEV
+ help
+ Driver for the HMS Industrial Networks Anybus-S bus.
+ You can attach a single Anybus-S compatible card to it, which
+ typically provides fieldbus and industrial ethernet
+ functionality.
+
+if HMS_ANYBUSS_BUS
+
+config ARCX_ANYBUS_CONTROLLER
+ tristate "Arcx Anybus-S Controller"
+ depends on OF && GPIOLIB && HAS_IOMEM && REGULATOR
+ help
+ Select this to get support for the Arcx Anybus controller.
+ It connects to the SoC via a parallel memory bus, and
+ embeds up to two Anybus-S buses (slots).
+ There is also a CAN power readout, unrelated to the Anybus,
+ modelled as a regulator.
+
+config HMS_PROFINET
+ tristate "HMS Profinet IRT Controller (Anybus-S)"
+ depends on FIELDBUS_DEV && HMS_ANYBUSS_BUS
+ help
+ If you say yes here you get support for the HMS Industrial
+ Networks Profinet IRT Controller.
+
+ It will be registered with the kernel as a fieldbus_dev,
+ so userspace can interact with it via the fieldbus_dev userspace
+ interface(s).
+
+ This driver can also be built as a module. If so, the module
+ will be called hms-profinet.
+
+ If unsure, say N.
+
+endif
diff --git a/drivers/staging/fieldbus/anybuss/Makefile b/drivers/staging/fieldbus/anybuss/Makefile
new file mode 100644
index 000000000000..3ad3dcc6be56
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for anybuss drivers.
+#
+
+obj-$(CONFIG_HMS_ANYBUSS_BUS) += anybuss_core.o
+anybuss_core-y += host.o
+
+obj-$(CONFIG_ARCX_ANYBUS_CONTROLLER) += arcx-anybus.o
+obj-$(CONFIG_HMS_PROFINET) += hms-profinet.o
diff --git a/drivers/staging/fieldbus/anybuss/anybuss-client.h b/drivers/staging/fieldbus/anybuss/anybuss-client.h
new file mode 100644
index 000000000000..2e48fb8f0209
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/anybuss-client.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Anybus-S client adapter definitions
+ *
+ * Copyright 2018 Arcx Inc
+ */
+
+#ifndef __LINUX_ANYBUSS_CLIENT_H__
+#define __LINUX_ANYBUSS_CLIENT_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/poll.h>
+
+struct anybuss_host;
+
+struct anybuss_client {
+ struct device dev;
+ struct anybuss_host *host;
+ u16 fieldbus_type;
+ /*
+ * these can be optionally set by the client to receive event
+ * notifications from the host.
+ */
+ void (*on_area_updated)(struct anybuss_client *client);
+ void (*on_online_changed)(struct anybuss_client *client, bool online);
+};
+
+struct anybuss_client_driver {
+ struct device_driver driver;
+ int (*probe)(struct anybuss_client *adev);
+ int (*remove)(struct anybuss_client *adev);
+ u16 fieldbus_type;
+};
+
+int anybuss_client_driver_register(struct anybuss_client_driver *drv);
+void anybuss_client_driver_unregister(struct anybuss_client_driver *drv);
+
+static inline struct anybuss_client *to_anybuss_client(struct device *dev)
+{
+ return container_of(dev, struct anybuss_client, dev);
+}
+
+static inline struct anybuss_client_driver *
+to_anybuss_client_driver(struct device_driver *drv)
+{
+ return container_of(drv, struct anybuss_client_driver, driver);
+}
+
+static inline void *
+anybuss_get_drvdata(const struct anybuss_client *client)
+{
+ return dev_get_drvdata(&client->dev);
+}
+
+static inline void
+anybuss_set_drvdata(struct anybuss_client *client, void *data)
+{
+ dev_set_drvdata(&client->dev, data);
+}
+
+int anybuss_set_power(struct anybuss_client *client, bool power_on);
+
+enum anybuss_offl_mode {
+ AB_OFFL_MODE_CLEAR = 0,
+ AB_OFFL_MODE_FREEZE,
+ AB_OFFL_MODE_SET
+};
+
+struct anybuss_memcfg {
+ u16 input_io;
+ u16 input_dpram;
+ u16 input_total;
+
+ u16 output_io;
+ u16 output_dpram;
+ u16 output_total;
+
+ enum anybuss_offl_mode offl_mode;
+};
+
+int anybuss_start_init(struct anybuss_client *client,
+ const struct anybuss_memcfg *cfg);
+int anybuss_finish_init(struct anybuss_client *client);
+int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
+ void *buf, size_t count);
+int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
+ const void *buf, size_t count);
+int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
+ const void *buf, size_t count);
+int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
+ void *buf, size_t count);
+
+/* these help clients make a struct file_operations */
+int anybuss_write_input(struct anybuss_client *client,
+ const char __user *buf, size_t size,
+ loff_t *offset);
+int anybuss_read_output(struct anybuss_client *client,
+ char __user *buf, size_t size,
+ loff_t *offset);
+
+#endif /* __LINUX_ANYBUSS_CLIENT_H__ */
diff --git a/drivers/staging/fieldbus/anybuss/anybuss-controller.h b/drivers/staging/fieldbus/anybuss/anybuss-controller.h
new file mode 100644
index 000000000000..02fa0749043b
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/anybuss-controller.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Anybus-S controller definitions
+ *
+ * Copyright 2018 Arcx Inc
+ */
+
+#ifndef __LINUX_ANYBUSS_CONTROLLER_H__
+#define __LINUX_ANYBUSS_CONTROLLER_H__
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+/*
+ * To instantiate an Anybus-S host, a controller should provide the following:
+ * - a reset function which resets the attached card;
+ * - a regmap which provides access to the attached card's dpram;
+ * - the irq of the attached card
+ */
+/**
+ * struct anybuss_ops - Controller resources to instantiate an Anybus-S host
+ *
+ * @reset: asserts/deasserts the anybus card's reset line.
+ * @regmap: provides access to the card's dual-port RAM area.
+ * @irq: number of the interrupt connected to the card's interrupt line.
+ * @host_idx: for multi-host controllers, the host index:
+ * 0 for the first host on the controller, 1 for the second, etc.
+ */
+struct anybuss_ops {
+ void (*reset)(struct device *dev, bool assert);
+ struct regmap *regmap;
+ int irq;
+ int host_idx;
+};
+
+struct anybuss_host;
+
+struct anybuss_host * __must_check
+anybuss_host_common_probe(struct device *dev,
+ const struct anybuss_ops *ops);
+void anybuss_host_common_remove(struct anybuss_host *host);
+
+struct anybuss_host * __must_check
+devm_anybuss_host_common_probe(struct device *dev,
+ const struct anybuss_ops *ops);
+
+#endif /* __LINUX_ANYBUSS_CONTROLLER_H__ */
diff --git a/drivers/staging/fieldbus/anybuss/arcx-anybus.c b/drivers/staging/fieldbus/anybuss/arcx-anybus.c
new file mode 100644
index 000000000000..a167fb68e355
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/arcx-anybus.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Arcx Anybus-S Controller driver
+ *
+ * Copyright (C) 2018 Arcx Inc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+
+/* move to <linux/anybuss-controller.h> when taking this out of staging */
+#include "anybuss-controller.h"
+
+#define CPLD_STATUS1 0x80
+#define CPLD_CONTROL 0x80
+#define CPLD_CONTROL_CRST 0x40
+#define CPLD_CONTROL_RST1 0x04
+#define CPLD_CONTROL_RST2 0x80
+#define CPLD_STATUS1_AB 0x02
+#define CPLD_STATUS1_CAN_POWER 0x01
+#define CPLD_DESIGN_LO 0x81
+#define CPLD_DESIGN_HI 0x82
+#define CPLD_CAP 0x83
+#define CPLD_CAP_COMPAT 0x01
+#define CPLD_CAP_SEP_RESETS 0x02
+
+struct controller_priv {
+ struct device *class_dev;
+ bool common_reset;
+ struct gpio_desc *reset_gpiod;
+ void __iomem *cpld_base;
+ struct mutex ctrl_lock; /* protects CONTROL register */
+ u8 control_reg;
+ char version[3];
+ u16 design_no;
+};
+
+static void do_reset(struct controller_priv *cd, u8 rst_bit, bool reset)
+{
+ mutex_lock(&cd->ctrl_lock);
+ /*
+ * CPLD_CONTROL is write-only, so cache its value in
+ * cd->control_reg
+ */
+ if (reset)
+ cd->control_reg &= ~rst_bit;
+ else
+ cd->control_reg |= rst_bit;
+ writeb(cd->control_reg, cd->cpld_base + CPLD_CONTROL);
+ /*
+ * h/w work-around:
+ * the hardware is 'too fast', so a reset followed by an immediate
+ * not-reset will _not_ change the anybus reset line in any way,
+ * losing the reset. to prevent this from happening, introduce
+ * a minimum reset duration.
+ * Verified minimum safe duration required using a scope
+ * on 14-June-2018: 100 us.
+ */
+ if (reset)
+ usleep_range(100, 200);
+ mutex_unlock(&cd->ctrl_lock);
+}
+
+static int anybuss_reset(struct controller_priv *cd,
+ unsigned long id, bool reset)
+{
+ if (id >= 2)
+ return -EINVAL;
+ if (cd->common_reset)
+ do_reset(cd, CPLD_CONTROL_CRST, reset);
+ else
+ do_reset(cd, id ? CPLD_CONTROL_RST2 : CPLD_CONTROL_RST1, reset);
+ return 0;
+}
+
+static void export_reset_0(struct device *dev, bool assert)
+{
+ struct controller_priv *cd = dev_get_drvdata(dev);
+
+ anybuss_reset(cd, 0, assert);
+}
+
+static void export_reset_1(struct device *dev, bool assert)
+{
+ struct controller_priv *cd = dev_get_drvdata(dev);
+
+ anybuss_reset(cd, 1, assert);
+}
+
+/*
+ * parallel bus limitation:
+ *
+ * the anybus is 8-bit wide. we can't assume that the hardware will translate
+ * word accesses on the parallel bus to multiple byte-accesses on the anybus.
+ *
+ * the imx WEIM bus does not provide this type of translation.
+ *
+ * to be safe, we will limit parallel bus accesses to a single byte
+ * at a time for now.
+ */
+
+static int read_reg_bus(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ void __iomem *base = context;
+
+ *val = readb(base + reg);
+ return 0;
+}
+
+static int write_reg_bus(void *context, unsigned int reg,
+ unsigned int val)
+{
+ void __iomem *base = context;
+
+ writeb(val, base + reg);
+ return 0;
+}
+
+static struct regmap *create_parallel_regmap(struct platform_device *pdev,
+ int idx)
+{
+ struct regmap_config regmap_cfg = {
+ .reg_bits = 11,
+ .val_bits = 8,
+ /*
+ * single-byte parallel bus accesses are atomic, so don't
+ * require any synchronization.
+ */
+ .disable_locking = true,
+ .reg_read = read_reg_bus,
+ .reg_write = write_reg_bus,
+ };
+ struct resource *res;
+ void __iomem *base;
+ struct device *dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1);
+ if (resource_size(res) < (1 << regmap_cfg.reg_bits))
+ return ERR_PTR(-EINVAL);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return ERR_CAST(base);
+ return devm_regmap_init(dev, NULL, base, &regmap_cfg);
+}
+
+static struct anybuss_host *
+create_anybus_host(struct platform_device *pdev, int idx)
+{
+ struct anybuss_ops ops = {};
+
+ switch (idx) {
+ case 0:
+ ops.reset = export_reset_0;
+ break;
+ case 1:
+ ops.reset = export_reset_1;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+ ops.host_idx = idx;
+ ops.regmap = create_parallel_regmap(pdev, idx);
+ if (IS_ERR(ops.regmap))
+ return ERR_CAST(ops.regmap);
+ ops.irq = platform_get_irq(pdev, idx);
+ if (ops.irq <= 0)
+ return ERR_PTR(-EINVAL);
+ return devm_anybuss_host_common_probe(&pdev->dev, &ops);
+}
+
+static ssize_t version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct controller_priv *cd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cd->version);
+}
+static DEVICE_ATTR_RO(version);
+
+static ssize_t design_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct controller_priv *cd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", cd->design_no);
+}
+static DEVICE_ATTR_RO(design_number);
+
+static struct attribute *controller_attributes[] = {
+ &dev_attr_version.attr,
+ &dev_attr_design_number.attr,
+ NULL,
+};
+
+static struct attribute_group controller_attribute_group = {
+ .attrs = controller_attributes,
+};
+
+static const struct attribute_group *controller_attribute_groups[] = {
+ &controller_attribute_group,
+ NULL,
+};
+
+static void controller_device_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+static int can_power_is_enabled(struct regulator_dev *rdev)
+{
+ struct controller_priv *cd = rdev_get_drvdata(rdev);
+
+ return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
+}
+
+static struct regulator_ops can_power_ops = {
+ .is_enabled = can_power_is_enabled,
+};
+
+static const struct regulator_desc can_power_desc = {
+ .name = "regulator-can-power",
+ .id = -1,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &can_power_ops,
+};
+
+static struct class *controller_class;
+static DEFINE_IDA(controller_index_ida);
+
+static int controller_probe(struct platform_device *pdev)
+{
+ struct controller_priv *cd;
+ struct device *dev = &pdev->dev;
+ struct regulator_config config = { };
+ struct regulator_dev *regulator;
+ int err, id;
+ struct resource *res;
+ struct anybuss_host *host;
+ u8 status1, cap;
+
+ cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
+ if (!cd)
+ return -ENOMEM;
+ dev_set_drvdata(dev, cd);
+ mutex_init(&cd->ctrl_lock);
+ cd->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cd->reset_gpiod))
+ return PTR_ERR(cd->reset_gpiod);
+
+ /* CPLD control memory, sits at index 0 */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cd->cpld_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cd->cpld_base)) {
+ dev_err(dev,
+ "failed to map cpld base address\n");
+ err = PTR_ERR(cd->cpld_base);
+ goto out_reset;
+ }
+
+ /* identify cpld */
+ status1 = readb(cd->cpld_base + CPLD_STATUS1);
+ cd->design_no = (readb(cd->cpld_base + CPLD_DESIGN_HI) << 8) |
+ readb(cd->cpld_base + CPLD_DESIGN_LO);
+ snprintf(cd->version, sizeof(cd->version), "%c%d",
+ 'A' + ((status1 >> 5) & 0x7),
+ (status1 >> 2) & 0x7);
+ dev_info(dev, "design number %d, revision %s\n",
+ cd->design_no,
+ cd->version);
+ cap = readb(cd->cpld_base + CPLD_CAP);
+ if (!(cap & CPLD_CAP_COMPAT)) {
+ dev_err(dev, "unsupported controller [cap=0x%02X]", cap);
+ err = -ENODEV;
+ goto out_reset;
+ }
+
+ if (status1 & CPLD_STATUS1_AB) {
+ dev_info(dev, "has anybus-S slot(s)");
+ cd->common_reset = !(cap & CPLD_CAP_SEP_RESETS);
+ dev_info(dev, "supports %s", cd->common_reset ?
+ "a common reset" : "separate resets");
+ for (id = 0; id < 2; id++) {
+ host = create_anybus_host(pdev, id);
+ if (!IS_ERR(host))
+ continue;
+ err = PTR_ERR(host);
+ /* -ENODEV is fine, it just means no card detected */
+ if (err != -ENODEV)
+ goto out_reset;
+ }
+ }
+
+ id = ida_simple_get(&controller_index_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ err = id;
+ goto out_reset;
+ }
+ /* export can power readout as a regulator */
+ config.dev = dev;
+ config.driver_data = cd;
+ regulator = devm_regulator_register(dev, &can_power_desc, &config);
+ if (IS_ERR(regulator)) {
+ err = PTR_ERR(regulator);
+ goto out_reset;
+ }
+ /* make controller info visible to userspace */
+ cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL);
+ if (!cd->class_dev) {
+ err = -ENOMEM;
+ goto out_ida;
+ }
+ cd->class_dev->class = controller_class;
+ cd->class_dev->groups = controller_attribute_groups;
+ cd->class_dev->parent = dev;
+ cd->class_dev->id = id;
+ cd->class_dev->release = controller_device_release;
+ dev_set_name(cd->class_dev, "%d", cd->class_dev->id);
+ dev_set_drvdata(cd->class_dev, cd);
+ err = device_register(cd->class_dev);
+ if (err)
+ goto out_dev;
+ return 0;
+out_dev:
+ put_device(cd->class_dev);
+out_ida:
+ ida_simple_remove(&controller_index_ida, id);
+out_reset:
+ gpiod_set_value_cansleep(cd->reset_gpiod, 1);
+ return err;
+}
+
+static int controller_remove(struct platform_device *pdev)
+{
+ struct controller_priv *cd = platform_get_drvdata(pdev);
+ int id = cd->class_dev->id;
+
+ device_unregister(cd->class_dev);
+ ida_simple_remove(&controller_index_ida, id);
+ gpiod_set_value_cansleep(cd->reset_gpiod, 1);
+ return 0;
+}
+
+static const struct of_device_id controller_of_match[] = {
+ { .compatible = "arcx,anybus-controller" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, controller_of_match);
+
+static struct platform_driver controller_driver = {
+ .probe = controller_probe,
+ .remove = controller_remove,
+ .driver = {
+ .name = "arcx-anybus-controller",
+ .of_match_table = of_match_ptr(controller_of_match),
+ },
+};
+
+static int __init controller_init(void)
+{
+ int err;
+
+ controller_class = class_create(THIS_MODULE, "arcx_anybus_controller");
+ if (IS_ERR(controller_class))
+ return PTR_ERR(controller_class);
+ err = platform_driver_register(&controller_driver);
+ if (err)
+ class_destroy(controller_class);
+
+ return err;
+}
+
+static void __exit controller_exit(void)
+{
+ platform_driver_unregister(&controller_driver);
+ class_destroy(controller_class);
+ ida_destroy(&controller_index_ida);
+}
+
+module_init(controller_init);
+module_exit(controller_exit);
+
+MODULE_DESCRIPTION("Arcx Anybus-S Controller driver");
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/fieldbus/anybuss/hms-profinet.c b/drivers/staging/fieldbus/anybuss/hms-profinet.c
new file mode 100644
index 000000000000..c5db648aa65f
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/hms-profinet.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HMS Profinet Client Driver
+ *
+ * Copyright (C) 2018 Arcx Inc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+/* move to <linux/fieldbus_dev.h> when taking this out of staging */
+#include "../fieldbus_dev.h"
+
+/* move to <linux/anybuss-client.h> when taking this out of staging */
+#include "anybuss-client.h"
+
+#define PROFI_DPRAM_SIZE 512
+
+/*
+ * ---------------------------------------------------------------
+ * Anybus Profinet mailbox messages - definitions
+ * ---------------------------------------------------------------
+ * note that we're depending on the layout of these structures being
+ * exactly as advertised.
+ */
+
+struct msg_mac_addr {
+ u8 addr[6];
+};
+
+struct profi_priv {
+ struct fieldbus_dev fbdev;
+ struct anybuss_client *client;
+ struct mutex enable_lock; /* serializes card enable */
+ bool power_on;
+};
+
+static ssize_t
+profi_read_area(struct fieldbus_dev *fbdev, char __user *buf, size_t size,
+ loff_t *offset)
+{
+ struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+
+ return anybuss_read_output(priv->client, buf, size, offset);
+}
+
+static ssize_t
+profi_write_area(struct fieldbus_dev *fbdev, const char __user *buf,
+ size_t size, loff_t *offset)
+{
+ struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+
+ return anybuss_write_input(priv->client, buf, size, offset);
+}
+
+static int profi_id_get(struct fieldbus_dev *fbdev, char *buf,
+ size_t max_size)
+{
+ struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+ struct msg_mac_addr response;
+ int ret;
+
+ ret = anybuss_recv_msg(priv->client, 0x0010, &response,
+ sizeof(response));
+ if (ret < 0)
+ return ret;
+ return snprintf(buf, max_size, "%02X:%02X:%02X:%02X:%02X:%02X\n",
+ response.addr[0], response.addr[1],
+ response.addr[2], response.addr[3],
+ response.addr[4], response.addr[5]);
+}
+
+static bool profi_enable_get(struct fieldbus_dev *fbdev)
+{
+ struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+ bool power_on;
+
+ mutex_lock(&priv->enable_lock);
+ power_on = priv->power_on;
+ mutex_unlock(&priv->enable_lock);
+
+ return power_on;
+}
+
+static int __profi_enable(struct profi_priv *priv)
+{
+ int ret;
+ struct anybuss_client *client = priv->client;
+ /* Initialization Sequence, Generic Anybus Mode */
+ const struct anybuss_memcfg mem_cfg = {
+ .input_io = 220,
+ .input_dpram = PROFI_DPRAM_SIZE,
+ .input_total = PROFI_DPRAM_SIZE,
+ .output_io = 220,
+ .output_dpram = PROFI_DPRAM_SIZE,
+ .output_total = PROFI_DPRAM_SIZE,
+ .offl_mode = AB_OFFL_MODE_CLEAR,
+ };
+
+ /*
+ * switch anybus off then on, this ensures we can do a complete
+ * configuration cycle in case anybus was already on.
+ */
+ anybuss_set_power(client, false);
+ ret = anybuss_set_power(client, true);
+ if (ret)
+ goto err;
+ ret = anybuss_start_init(client, &mem_cfg);
+ if (ret)
+ goto err;
+ ret = anybuss_finish_init(client);
+ if (ret)
+ goto err;
+ priv->power_on = true;
+ return 0;
+
+err:
+ anybuss_set_power(client, false);
+ priv->power_on = false;
+ return ret;
+}
+
+static int __profi_disable(struct profi_priv *priv)
+{
+ struct anybuss_client *client = priv->client;
+
+ anybuss_set_power(client, false);
+ priv->power_on = false;
+ return 0;
+}
+
+static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable)
+{
+ int ret;
+ struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+
+ mutex_lock(&priv->enable_lock);
+ if (enable)
+ ret = __profi_enable(priv);
+ else
+ ret = __profi_disable(priv);
+ mutex_unlock(&priv->enable_lock);
+
+ return ret;
+}
+
+static void profi_on_area_updated(struct anybuss_client *client)
+{
+ struct profi_priv *priv = anybuss_get_drvdata(client);
+
+ fieldbus_dev_area_updated(&priv->fbdev);
+}
+
+static void profi_on_online_changed(struct anybuss_client *client, bool online)
+{
+ struct profi_priv *priv = anybuss_get_drvdata(client);
+
+ fieldbus_dev_online_changed(&priv->fbdev, online);
+}
+
+static int profinet_probe(struct anybuss_client *client)
+{
+ struct profi_priv *priv;
+ struct device *dev = &client->dev;
+ int err;
+
+ client->on_area_updated = profi_on_area_updated;
+ client->on_online_changed = profi_on_online_changed;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ mutex_init(&priv->enable_lock);
+ priv->client = client;
+ priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE;
+ priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE;
+ priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)";
+ priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET;
+ priv->fbdev.read_area = profi_read_area;
+ priv->fbdev.write_area = profi_write_area;
+ priv->fbdev.fieldbus_id_get = profi_id_get;
+ priv->fbdev.enable_get = profi_enable_get;
+ priv->fbdev.simple_enable_set = profi_simple_enable;
+ priv->fbdev.parent = dev;
+ err = fieldbus_dev_register(&priv->fbdev);
+ if (err < 0)
+ return err;
+ dev_info(dev, "card detected, registered as %s",
+ dev_name(priv->fbdev.dev));
+ anybuss_set_drvdata(client, priv);
+
+ return 0;
+}
+
+static int profinet_remove(struct anybuss_client *client)
+{
+ struct profi_priv *priv = anybuss_get_drvdata(client);
+
+ fieldbus_dev_unregister(&priv->fbdev);
+ return 0;
+}
+
+static struct anybuss_client_driver profinet_driver = {
+ .probe = profinet_probe,
+ .remove = profinet_remove,
+ .driver = {
+ .name = "hms-profinet",
+ .owner = THIS_MODULE,
+ },
+ .fieldbus_type = 0x0089,
+};
+
+static int __init profinet_init(void)
+{
+ return anybuss_client_driver_register(&profinet_driver);
+}
+module_init(profinet_init);
+
+static void __exit profinet_exit(void)
+{
+ return anybuss_client_driver_unregister(&profinet_driver);
+}
+module_exit(profinet_exit);
+
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/fieldbus/anybuss/host.c b/drivers/staging/fieldbus/anybuss/host.c
new file mode 100644
index 000000000000..e34d4249f5a7
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/host.c
@@ -0,0 +1,1460 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HMS Anybus-S Host Driver
+ *
+ * Copyright (C) 2018 Arcx Inc
+ */
+
+/*
+ * Architecture Overview
+ * =====================
+ * This driver (running on the CPU/SoC) and the Anybus-S card communicate
+ * by reading and writing data to/from the Anybus-S Dual-Port RAM (dpram).
+ * This is memory connected to both the SoC and Anybus-S card, which both sides
+ * can access freely and concurrently.
+ *
+ * Synchronization happens by means of two registers located in the dpram:
+ * IND_AB: written exclusively by the Anybus card; and
+ * IND_AP: written exclusively by this driver.
+ *
+ * Communication happens using one of the following mechanisms:
+ * 1. reserve, read/write, release dpram memory areas:
+ * using an IND_AB/IND_AP protocol, the driver is able to reserve certain
+ * memory areas. no dpram memory can be read or written except if reserved.
+ * (with a few limited exceptions)
+ * 2. send and receive data structures via a shared mailbox:
+ * using an IND_AB/IND_AP protocol, the driver and Anybus card are able to
+ * exchange commands and responses using a shared mailbox.
+ * 3. receive software interrupts:
+ * using an IND_AB/IND_AP protocol, the Anybus card is able to notify the
+ * driver of certain events such as: bus online/offline, data available.
+ * note that software interrupt event bits are located in a memory area
+ * which must be reserved before it can be accessed.
+ *
+ * The manual[1] is silent on whether these mechanisms can happen concurrently,
+ * or how they should be synchronized. However, section 13 (Driver Example)
+ * provides the following suggestion for developing a driver:
+ * a) an interrupt handler which updates global variables;
+ * b) a continuously-running task handling area requests (1 above)
+ * c) a continuously-running task handling mailbox requests (2 above)
+ * The example conspicuously leaves out software interrupts (3 above), which
+ * is the thorniest issue to get right (see below).
+ *
+ * The naive, straightforward way to implement this would be:
+ * - create an isr which updates shared variables;
+ * - create a work_struct which handles software interrupts on a queue;
+ * - create a function which does reserve/update/unlock in a loop;
+ * - create a function which does mailbox send/receive in a loop;
+ * - call the above functions from the driver's read/write/ioctl;
+ * - synchronize using mutexes/spinlocks:
+ * + only one area request at a time
+ * + only one mailbox request at a time
+ * + protect AB_IND, AB_IND against data hazards (e.g. read-after-write)
+ *
+ * Unfortunately, the presence of the software interrupt causes subtle yet
+ * considerable synchronization issues; especially problematic is the
+ * requirement to reserve/release the area which contains the status bits.
+ *
+ * The driver architecture presented here sidesteps these synchronization issues
+ * by accessing the dpram from a single kernel thread only. User-space throws
+ * "tasks" (i.e. 1, 2 above) into a task queue, waits for their completion,
+ * and the kernel thread runs them to completion.
+ *
+ * Each task has a task_function, which is called/run by the queue thread.
+ * That function communicates with the Anybus card, and returns either
+ * 0 (OK), a negative error code (error), or -EINPROGRESS (waiting).
+ * On OK or error, the queue thread completes and dequeues the task,
+ * which also releases the user space thread which may still be waiting for it.
+ * On -EINPROGRESS (waiting), the queue thread will leave the task on the queue,
+ * and revisit (call again) whenever an interrupt event comes in.
+ *
+ * Each task has a state machine, which is run by calling its task_function.
+ * It ensures that the task will go through its various stages over time,
+ * returning -EINPROGRESS if it wants to wait for an event to happen.
+ *
+ * Note that according to the manual's driver example, the following operations
+ * may run independent of each other:
+ * - area reserve/read/write/release (point 1 above)
+ * - mailbox operations (point 2 above)
+ * - switching power on/off
+ *
+ * To allow them to run independently, each operation class gets its own queue.
+ *
+ * Userspace processes A, B, C, D post tasks to the appropriate queue,
+ * and wait for task completion:
+ *
+ * process A B C D
+ * | | | |
+ * v v v v
+ * |<----- ========================================
+ * | | | |
+ * | v v v-------<-------+
+ * | +--------------------------------------+ |
+ * | | power q | mbox q | area q | |
+ * | |------------|------------|------------| |
+ * | | task | task | task | |
+ * | | task | task | task | |
+ * | | task wait | task wait | task wait | |
+ * | +--------------------------------------+ |
+ * | ^ ^ ^ |
+ * | | | | ^
+ * | +--------------------------------------+ |
+ * | | queue thread | |
+ * | |--------------------------------------| |
+ * | | single-threaded: | |
+ * | | loop: | |
+ * v | for each queue: | |
+ * | | run task state machine | |
+ * | | if task waiting: | |
+ * | | leave on queue | |
+ * | | if task done: | |
+ * | | complete task, remove from q | |
+ * | | if software irq event bits set: | |
+ * | | notify userspace | |
+ * | | post clear event bits task------>|>-------+
+ * | | wait for IND_AB changed event OR |
+ * | | task added event OR |
+ * | | timeout |
+ * | | end loop |
+ * | +--------------------------------------+
+ * | + wake up +
+ * | +--------------------------------------+
+ * | ^ ^
+ * | | |
+ * +-------->------- |
+ * |
+ * +--------------------------------------+
+ * | interrupt service routine |
+ * |--------------------------------------|
+ * | wake up queue thread on IND_AB change|
+ * +--------------------------------------+
+ *
+ * Note that the Anybus interrupt is dual-purpose:
+ * - after a reset, triggered when the card becomes ready;
+ * - during normal operation, triggered when AB_IND changes.
+ * This is why the interrupt service routine doesn't just wake up the
+ * queue thread, but also completes the card_boot completion.
+ *
+ * [1] https://www.anybus.com/docs/librariesprovider7/default-document-library/
+ * manuals-design-guides/hms-hmsi-27-275.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/atomic.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/random.h>
+#include <linux/kref.h>
+#include <linux/of_address.h>
+
+/* move to <linux/anybuss-*.h> when taking this out of staging */
+#include "anybuss-client.h"
+#include "anybuss-controller.h"
+
+#define DPRAM_SIZE 0x800
+#define MAX_MBOX_MSG_SZ 0x0FF
+#define TIMEOUT (HZ * 2)
+#define MAX_DATA_AREA_SZ 0x200
+#define MAX_FBCTRL_AREA_SZ 0x1BE
+
+#define REG_BOOTLOADER_V 0x7C0
+#define REG_API_V 0x7C2
+#define REG_FIELDBUS_V 0x7C4
+#define REG_SERIAL_NO 0x7C6
+#define REG_FIELDBUS_TYPE 0x7CC
+#define REG_MODULE_SW_V 0x7CE
+#define REG_IND_AB 0x7FF
+#define REG_IND_AP 0x7FE
+#define REG_EVENT_CAUSE 0x7ED
+#define MBOX_IN_AREA 0x400
+#define MBOX_OUT_AREA 0x520
+#define DATA_IN_AREA 0x000
+#define DATA_OUT_AREA 0x200
+#define FBCTRL_AREA 0x640
+
+#define EVENT_CAUSE_DC 0x01
+#define EVENT_CAUSE_FBOF 0x02
+#define EVENT_CAUSE_FBON 0x04
+
+#define IND_AB_UPDATED 0x08
+#define IND_AX_MIN 0x80
+#define IND_AX_MOUT 0x40
+#define IND_AX_IN 0x04
+#define IND_AX_OUT 0x02
+#define IND_AX_FBCTRL 0x01
+#define IND_AP_LOCK 0x08
+#define IND_AP_ACTION 0x10
+#define IND_AX_EVNT 0x20
+#define IND_AP_ABITS (IND_AX_IN | IND_AX_OUT | \
+ IND_AX_FBCTRL | \
+ IND_AP_ACTION | IND_AP_LOCK)
+
+#define INFO_TYPE_FB 0x0002
+#define INFO_TYPE_APP 0x0001
+#define INFO_COMMAND 0x4000
+
+#define OP_MODE_FBFC 0x0002
+#define OP_MODE_FBS 0x0004
+#define OP_MODE_CD 0x0200
+
+#define CMD_START_INIT 0x0001
+#define CMD_ANYBUS_INIT 0x0002
+#define CMD_END_INIT 0x0003
+
+/*
+ * ---------------------------------------------------------------
+ * Anybus mailbox messages - definitions
+ * ---------------------------------------------------------------
+ * note that we're depending on the layout of these structures being
+ * exactly as advertised.
+ */
+
+struct anybus_mbox_hdr {
+ __be16 id;
+ __be16 info;
+ __be16 cmd_num;
+ __be16 data_size;
+ __be16 frame_count;
+ __be16 frame_num;
+ __be16 offset_high;
+ __be16 offset_low;
+ __be16 extended[8];
+};
+
+struct msg_anybus_init {
+ __be16 input_io_len;
+ __be16 input_dpram_len;
+ __be16 input_total_len;
+ __be16 output_io_len;
+ __be16 output_dpram_len;
+ __be16 output_total_len;
+ __be16 op_mode;
+ __be16 notif_config;
+ __be16 wd_val;
+};
+
+/* ------------- ref counted tasks ------------- */
+
+struct ab_task;
+typedef int (*ab_task_fn_t)(struct anybuss_host *cd,
+ struct ab_task *t);
+typedef void (*ab_done_fn_t)(struct anybuss_host *cd);
+
+struct area_priv {
+ bool is_write;
+ u16 flags;
+ u16 addr;
+ size_t count;
+ u8 buf[MAX_DATA_AREA_SZ];
+};
+
+struct mbox_priv {
+ struct anybus_mbox_hdr hdr;
+ size_t msg_out_sz;
+ size_t msg_in_sz;
+ u8 msg[MAX_MBOX_MSG_SZ];
+};
+
+struct ab_task {
+ struct kmem_cache *cache;
+ struct kref refcount;
+ ab_task_fn_t task_fn;
+ ab_done_fn_t done_fn;
+ int result;
+ struct completion done;
+ unsigned long start_jiffies;
+ union {
+ struct area_priv area_pd;
+ struct mbox_priv mbox_pd;
+ };
+};
+
+static struct ab_task *ab_task_create_get(struct kmem_cache *cache,
+ ab_task_fn_t task_fn)
+{
+ struct ab_task *t;
+
+ t = kmem_cache_alloc(cache, GFP_KERNEL);
+ if (!t)
+ return NULL;
+ t->cache = cache;
+ kref_init(&t->refcount);
+ t->task_fn = task_fn;
+ t->done_fn = NULL;
+ t->result = 0;
+ init_completion(&t->done);
+ return t;
+}
+
+static void __ab_task_destroy(struct kref *refcount)
+{
+ struct ab_task *t = container_of(refcount, struct ab_task, refcount);
+ struct kmem_cache *cache = t->cache;
+
+ kmem_cache_free(cache, t);
+}
+
+static void ab_task_put(struct ab_task *t)
+{
+ kref_put(&t->refcount, __ab_task_destroy);
+}
+
+static struct ab_task *__ab_task_get(struct ab_task *t)
+{
+ kref_get(&t->refcount);
+ return t;
+}
+
+static void __ab_task_finish(struct ab_task *t, struct anybuss_host *cd)
+{
+ if (t->done_fn)
+ t->done_fn(cd);
+ complete(&t->done);
+}
+
+static void
+ab_task_dequeue_finish_put(struct kfifo *q, struct anybuss_host *cd)
+{
+ int ret;
+ struct ab_task *t;
+
+ ret = kfifo_out(q, &t, sizeof(t));
+ WARN_ON(!ret);
+ __ab_task_finish(t, cd);
+ ab_task_put(t);
+}
+
+static int
+ab_task_enqueue(struct ab_task *t, struct kfifo *q, spinlock_t *slock,
+ wait_queue_head_t *wq)
+{
+ int ret;
+
+ t->start_jiffies = jiffies;
+ __ab_task_get(t);
+ ret = kfifo_in_spinlocked(q, &t, sizeof(t), slock);
+ if (!ret) {
+ ab_task_put(t);
+ return -ENOMEM;
+ }
+ wake_up(wq);
+ return 0;
+}
+
+static int
+ab_task_enqueue_wait(struct ab_task *t, struct kfifo *q, spinlock_t *slock,
+ wait_queue_head_t *wq)
+{
+ int ret;
+
+ ret = ab_task_enqueue(t, q, slock, wq);
+ if (ret)
+ return ret;
+ ret = wait_for_completion_interruptible(&t->done);
+ if (ret)
+ return ret;
+ return t->result;
+}
+
+/* ------------------------ anybus hardware ------------------------ */
+
+struct anybuss_host {
+ struct device *dev;
+ struct anybuss_client *client;
+ void (*reset)(struct device *dev, bool assert);
+ struct regmap *regmap;
+ int irq;
+ int host_idx;
+ struct task_struct *qthread;
+ wait_queue_head_t wq;
+ struct completion card_boot;
+ atomic_t ind_ab;
+ spinlock_t qlock; /* protects IN side of powerq, mboxq, areaq */
+ struct kmem_cache *qcache;
+ struct kfifo qs[3];
+ struct kfifo *powerq;
+ struct kfifo *mboxq;
+ struct kfifo *areaq;
+ bool power_on;
+ bool softint_pending;
+};
+
+static void reset_assert(struct anybuss_host *cd)
+{
+ cd->reset(cd->dev, true);
+}
+
+static void reset_deassert(struct anybuss_host *cd)
+{
+ cd->reset(cd->dev, false);
+}
+
+static int test_dpram(struct regmap *regmap)
+{
+ int i;
+ unsigned int val;
+
+ for (i = 0; i < DPRAM_SIZE; i++)
+ regmap_write(regmap, i, (u8)i);
+ for (i = 0; i < DPRAM_SIZE; i++) {
+ regmap_read(regmap, i, &val);
+ if ((u8)val != (u8)i)
+ return -EIO;
+ }
+ return 0;
+}
+
+static int read_ind_ab(struct regmap *regmap)
+{
+ unsigned long timeout = jiffies + HZ / 2;
+ unsigned int a, b, i = 0;
+
+ while (time_before_eq(jiffies, timeout)) {
+ regmap_read(regmap, REG_IND_AB, &a);
+ regmap_read(regmap, REG_IND_AB, &b);
+ if (likely(a == b))
+ return (int)a;
+ if (i < 10) {
+ cpu_relax();
+ i++;
+ } else {
+ usleep_range(500, 1000);
+ }
+ }
+ WARN(1, "IND_AB register not stable");
+ return -ETIMEDOUT;
+}
+
+static int write_ind_ap(struct regmap *regmap, unsigned int ind_ap)
+{
+ unsigned long timeout = jiffies + HZ / 2;
+ unsigned int v, i = 0;
+
+ while (time_before_eq(jiffies, timeout)) {
+ regmap_write(regmap, REG_IND_AP, ind_ap);
+ regmap_read(regmap, REG_IND_AP, &v);
+ if (likely(ind_ap == v))
+ return 0;
+ if (i < 10) {
+ cpu_relax();
+ i++;
+ } else {
+ usleep_range(500, 1000);
+ }
+ }
+ WARN(1, "IND_AP register not stable");
+ return -ETIMEDOUT;
+}
+
+static irqreturn_t irq_handler(int irq, void *data)
+{
+ struct anybuss_host *cd = data;
+ int ind_ab;
+
+ /*
+ * irq handler needs exclusive access to the IND_AB register,
+ * because the act of reading the register acks the interrupt.
+ *
+ * store the register value in cd->ind_ab (an atomic_t), so that the
+ * queue thread is able to read it without causing an interrupt ack
+ * side-effect (and without spuriously acking an interrupt).
+ */
+ ind_ab = read_ind_ab(cd->regmap);
+ if (ind_ab < 0)
+ return IRQ_NONE;
+ atomic_set(&cd->ind_ab, ind_ab);
+ complete(&cd->card_boot);
+ wake_up(&cd->wq);
+ return IRQ_HANDLED;
+}
+
+/* ------------------------ power on/off tasks --------------------- */
+
+static int task_fn_power_off(struct anybuss_host *cd,
+ struct ab_task *t)
+{
+ struct anybuss_client *client = cd->client;
+
+ if (!cd->power_on)
+ return 0;
+ disable_irq(cd->irq);
+ reset_assert(cd);
+ atomic_set(&cd->ind_ab, IND_AB_UPDATED);
+ if (client->on_online_changed)
+ client->on_online_changed(client, false);
+ cd->power_on = false;
+ return 0;
+}
+
+static int task_fn_power_on_2(struct anybuss_host *cd,
+ struct ab_task *t)
+{
+ if (completion_done(&cd->card_boot)) {
+ cd->power_on = true;
+ return 0;
+ }
+ if (time_after(jiffies, t->start_jiffies + TIMEOUT)) {
+ disable_irq(cd->irq);
+ reset_assert(cd);
+ dev_err(cd->dev, "power on timed out");
+ return -ETIMEDOUT;
+ }
+ return -EINPROGRESS;
+}
+
+static int task_fn_power_on(struct anybuss_host *cd,
+ struct ab_task *t)
+{
+ unsigned int dummy;
+
+ if (cd->power_on)
+ return 0;
+ /*
+ * anybus docs: prevent false 'init done' interrupt by
+ * doing a dummy read of IND_AB register while in reset.
+ */
+ regmap_read(cd->regmap, REG_IND_AB, &dummy);
+ reinit_completion(&cd->card_boot);
+ enable_irq(cd->irq);
+ reset_deassert(cd);
+ t->task_fn = task_fn_power_on_2;
+ return -EINPROGRESS;
+}
+
+int anybuss_set_power(struct anybuss_client *client, bool power_on)
+{
+ struct anybuss_host *cd = client->host;
+ struct ab_task *t;
+ int err;
+
+ t = ab_task_create_get(cd->qcache, power_on ?
+ task_fn_power_on : task_fn_power_off);
+ if (!t)
+ return -ENOMEM;
+ err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+ ab_task_put(t);
+ return err;
+}
+EXPORT_SYMBOL_GPL(anybuss_set_power);
+
+/* ---------------------------- area tasks ------------------------ */
+
+static int task_fn_area_3(struct anybuss_host *cd, struct ab_task *t)
+{
+ struct area_priv *pd = &t->area_pd;
+
+ if (!cd->power_on)
+ return -EIO;
+ if (atomic_read(&cd->ind_ab) & pd->flags) {
+ /* area not released yet */
+ if (time_after(jiffies, t->start_jiffies + TIMEOUT))
+ return -ETIMEDOUT;
+ return -EINPROGRESS;
+ }
+ return 0;
+}
+
+static int task_fn_area_2(struct anybuss_host *cd, struct ab_task *t)
+{
+ struct area_priv *pd = &t->area_pd;
+ unsigned int ind_ap;
+ int ret;
+
+ if (!cd->power_on)
+ return -EIO;
+ regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+ if (!(atomic_read(&cd->ind_ab) & pd->flags)) {
+ /* we don't own the area yet */
+ if (time_after(jiffies, t->start_jiffies + TIMEOUT)) {
+ dev_warn(cd->dev, "timeout waiting for area");
+ dump_stack();
+ return -ETIMEDOUT;
+ }
+ return -EINPROGRESS;
+ }
+ /* we own the area, do what we're here to do */
+ if (pd->is_write)
+ regmap_bulk_write(cd->regmap, pd->addr, pd->buf,
+ pd->count);
+ else
+ regmap_bulk_read(cd->regmap, pd->addr, pd->buf,
+ pd->count);
+ /* ask to release the area, must use unlocked release */
+ ind_ap &= ~IND_AP_ABITS;
+ ind_ap |= pd->flags;
+ ret = write_ind_ap(cd->regmap, ind_ap);
+ if (ret)
+ return ret;
+ t->task_fn = task_fn_area_3;
+ return -EINPROGRESS;
+}
+
+static int task_fn_area(struct anybuss_host *cd, struct ab_task *t)
+{
+ struct area_priv *pd = &t->area_pd;
+ unsigned int ind_ap;
+ int ret;
+
+ if (!cd->power_on)
+ return -EIO;
+ regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+ /* ask to take the area */
+ ind_ap &= ~IND_AP_ABITS;
+ ind_ap |= pd->flags | IND_AP_ACTION | IND_AP_LOCK;
+ ret = write_ind_ap(cd->regmap, ind_ap);
+ if (ret)
+ return ret;
+ t->task_fn = task_fn_area_2;
+ return -EINPROGRESS;
+}
+
+static struct ab_task *
+create_area_reader(struct kmem_cache *qcache, u16 flags, u16 addr,
+ size_t count)
+{
+ struct ab_task *t;
+ struct area_priv *ap;
+
+ t = ab_task_create_get(qcache, task_fn_area);
+ if (!t)
+ return NULL;
+ ap = &t->area_pd;
+ ap->flags = flags;
+ ap->addr = addr;
+ ap->is_write = false;
+ ap->count = count;
+ return t;
+}
+
+static struct ab_task *
+create_area_writer(struct kmem_cache *qcache, u16 flags, u16 addr,
+ const void *buf, size_t count)
+{
+ struct ab_task *t;
+ struct area_priv *ap;
+
+ t = ab_task_create_get(qcache, task_fn_area);
+ if (!t)
+ return NULL;
+ ap = &t->area_pd;
+ ap->flags = flags;
+ ap->addr = addr;
+ ap->is_write = true;
+ ap->count = count;
+ memcpy(ap->buf, buf, count);
+ return t;
+}
+
+static struct ab_task *
+create_area_user_writer(struct kmem_cache *qcache, u16 flags, u16 addr,
+ const void __user *buf, size_t count)
+{
+ struct ab_task *t;
+ struct area_priv *ap;
+
+ t = ab_task_create_get(qcache, task_fn_area);
+ if (!t)
+ return ERR_PTR(-ENOMEM);
+ ap = &t->area_pd;
+ ap->flags = flags;
+ ap->addr = addr;
+ ap->is_write = true;
+ ap->count = count;
+ if (copy_from_user(ap->buf, buf, count)) {
+ ab_task_put(t);
+ return ERR_PTR(-EFAULT);
+ }
+ return t;
+}
+
+static bool area_range_ok(u16 addr, size_t count, u16 area_start,
+ size_t area_sz)
+{
+ u16 area_end_ex = area_start + area_sz;
+ u16 addr_end_ex;
+
+ if (addr < area_start)
+ return false;
+ if (addr >= area_end_ex)
+ return false;
+ addr_end_ex = addr + count;
+ if (addr_end_ex > area_end_ex)
+ return false;
+ return true;
+}
+
+/* -------------------------- mailbox tasks ----------------------- */
+
+static int task_fn_mbox_2(struct anybuss_host *cd, struct ab_task *t)
+{
+ struct mbox_priv *pd = &t->mbox_pd;
+ unsigned int ind_ap;
+
+ if (!cd->power_on)
+ return -EIO;
+ regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+ if (((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MOUT) == 0) {
+ /* output message not here */
+ if (time_after(jiffies, t->start_jiffies + TIMEOUT))
+ return -ETIMEDOUT;
+ return -EINPROGRESS;
+ }
+ /* grab the returned header and msg */
+ regmap_bulk_read(cd->regmap, MBOX_OUT_AREA, &pd->hdr,
+ sizeof(pd->hdr));
+ regmap_bulk_read(cd->regmap, MBOX_OUT_AREA + sizeof(pd->hdr),
+ pd->msg, pd->msg_in_sz);
+ /* tell anybus we've consumed the message */
+ ind_ap ^= IND_AX_MOUT;
+ return write_ind_ap(cd->regmap, ind_ap);
+}
+
+static int task_fn_mbox(struct anybuss_host *cd, struct ab_task *t)
+{
+ struct mbox_priv *pd = &t->mbox_pd;
+ unsigned int ind_ap;
+ int ret;
+
+ if (!cd->power_on)
+ return -EIO;
+ regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+ if ((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MIN) {
+ /* mbox input area busy */
+ if (time_after(jiffies, t->start_jiffies + TIMEOUT))
+ return -ETIMEDOUT;
+ return -EINPROGRESS;
+ }
+ /* write the header and msg to input area */
+ regmap_bulk_write(cd->regmap, MBOX_IN_AREA, &pd->hdr,
+ sizeof(pd->hdr));
+ regmap_bulk_write(cd->regmap, MBOX_IN_AREA + sizeof(pd->hdr),
+ pd->msg, pd->msg_out_sz);
+ /* tell anybus we gave it a message */
+ ind_ap ^= IND_AX_MIN;
+ ret = write_ind_ap(cd->regmap, ind_ap);
+ if (ret)
+ return ret;
+ t->start_jiffies = jiffies;
+ t->task_fn = task_fn_mbox_2;
+ return -EINPROGRESS;
+}
+
+static void log_invalid_other(struct device *dev,
+ struct anybus_mbox_hdr *hdr)
+{
+ size_t ext_offs = ARRAY_SIZE(hdr->extended) - 1;
+ u16 code = be16_to_cpu(hdr->extended[ext_offs]);
+
+ dev_err(dev, " Invalid other: [0x%02X]", code);
+}
+
+static const char * const EMSGS[] = {
+ "Invalid Message ID",
+ "Invalid Message Type",
+ "Invalid Command",
+ "Invalid Data Size",
+ "Message Header Malformed (offset 008h)",
+ "Message Header Malformed (offset 00Ah)",
+ "Message Header Malformed (offset 00Ch - 00Dh)",
+ "Invalid Address",
+ "Invalid Response",
+ "Flash Config Error",
+};
+
+static int mbox_cmd_err(struct device *dev, struct mbox_priv *mpriv)
+{
+ int i;
+ u8 ecode;
+ struct anybus_mbox_hdr *hdr = &mpriv->hdr;
+ u16 info = be16_to_cpu(hdr->info);
+ u8 *phdr = (u8 *)hdr;
+ u8 *pmsg = mpriv->msg;
+
+ if (!(info & 0x8000))
+ return 0;
+ ecode = (info >> 8) & 0x0F;
+ dev_err(dev, "mailbox command failed:");
+ if (ecode == 0x0F)
+ log_invalid_other(dev, hdr);
+ else if (ecode < ARRAY_SIZE(EMSGS))
+ dev_err(dev, " Error code: %s (0x%02X)",
+ EMSGS[ecode], ecode);
+ else
+ dev_err(dev, " Error code: 0x%02X\n", ecode);
+ dev_err(dev, "Failed command:");
+ dev_err(dev, "Message Header:");
+ for (i = 0; i < sizeof(mpriv->hdr); i += 2)
+ dev_err(dev, "%02X%02X", phdr[i], phdr[i + 1]);
+ dev_err(dev, "Message Data:");
+ for (i = 0; i < mpriv->msg_in_sz; i += 2)
+ dev_err(dev, "%02X%02X", pmsg[i], pmsg[i + 1]);
+ dev_err(dev, "Stack dump:");
+ dump_stack();
+ return -EIO;
+}
+
+static int _anybus_mbox_cmd(struct anybuss_host *cd,
+ u16 cmd_num, bool is_fb_cmd,
+ const void *msg_out, size_t msg_out_sz,
+ void *msg_in, size_t msg_in_sz,
+ const void *ext, size_t ext_sz)
+{
+ struct ab_task *t;
+ struct mbox_priv *pd;
+ struct anybus_mbox_hdr *h;
+ size_t msg_sz = max(msg_in_sz, msg_out_sz);
+ u16 info;
+ int err;
+
+ if (msg_sz > MAX_MBOX_MSG_SZ)
+ return -EINVAL;
+ if (ext && ext_sz > sizeof(h->extended))
+ return -EINVAL;
+ t = ab_task_create_get(cd->qcache, task_fn_mbox);
+ if (!t)
+ return -ENOMEM;
+ pd = &t->mbox_pd;
+ h = &pd->hdr;
+ info = is_fb_cmd ? INFO_TYPE_FB : INFO_TYPE_APP;
+ /*
+ * prevent uninitialized memory in the header from being sent
+ * across the anybus
+ */
+ memset(h, 0, sizeof(*h));
+ h->info = cpu_to_be16(info | INFO_COMMAND);
+ h->cmd_num = cpu_to_be16(cmd_num);
+ h->data_size = cpu_to_be16(msg_out_sz);
+ h->frame_count = cpu_to_be16(1);
+ h->frame_num = cpu_to_be16(1);
+ h->offset_high = cpu_to_be16(0);
+ h->offset_low = cpu_to_be16(0);
+ if (ext)
+ memcpy(h->extended, ext, ext_sz);
+ memcpy(pd->msg, msg_out, msg_out_sz);
+ pd->msg_out_sz = msg_out_sz;
+ pd->msg_in_sz = msg_in_sz;
+ err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+ if (err)
+ goto out;
+ /*
+ * mailbox mechanism worked ok, but maybe the mbox response
+ * contains an error ?
+ */
+ err = mbox_cmd_err(cd->dev, pd);
+ if (err)
+ goto out;
+ memcpy(msg_in, pd->msg, msg_in_sz);
+out:
+ ab_task_put(t);
+ return err;
+}
+
+/* ------------------------ anybus queues ------------------------ */
+
+static void process_q(struct anybuss_host *cd, struct kfifo *q)
+{
+ struct ab_task *t;
+ int ret;
+
+ ret = kfifo_out_peek(q, &t, sizeof(t));
+ if (!ret)
+ return;
+ t->result = t->task_fn(cd, t);
+ if (t->result != -EINPROGRESS)
+ ab_task_dequeue_finish_put(q, cd);
+}
+
+static bool qs_have_work(struct kfifo *qs, size_t num)
+{
+ size_t i;
+ struct ab_task *t;
+ int ret;
+
+ for (i = 0; i < num; i++, qs++) {
+ ret = kfifo_out_peek(qs, &t, sizeof(t));
+ if (ret && (t->result != -EINPROGRESS))
+ return true;
+ }
+ return false;
+}
+
+static void process_qs(struct anybuss_host *cd)
+{
+ size_t i;
+ struct kfifo *qs = cd->qs;
+ size_t nqs = ARRAY_SIZE(cd->qs);
+
+ for (i = 0; i < nqs; i++, qs++)
+ process_q(cd, qs);
+}
+
+static void softint_ack(struct anybuss_host *cd)
+{
+ unsigned int ind_ap;
+
+ cd->softint_pending = false;
+ if (!cd->power_on)
+ return;
+ regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+ ind_ap &= ~IND_AX_EVNT;
+ ind_ap |= atomic_read(&cd->ind_ab) & IND_AX_EVNT;
+ write_ind_ap(cd->regmap, ind_ap);
+}
+
+static void process_softint(struct anybuss_host *cd)
+{
+ struct anybuss_client *client = cd->client;
+ static const u8 zero;
+ int ret;
+ unsigned int ind_ap, ev;
+ struct ab_task *t;
+
+ if (!cd->power_on)
+ return;
+ if (cd->softint_pending)
+ return;
+ regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+ if (!((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_EVNT))
+ return;
+ /* process software interrupt */
+ regmap_read(cd->regmap, REG_EVENT_CAUSE, &ev);
+ if (ev & EVENT_CAUSE_FBON) {
+ if (client->on_online_changed)
+ client->on_online_changed(client, true);
+ dev_dbg(cd->dev, "Fieldbus ON");
+ }
+ if (ev & EVENT_CAUSE_FBOF) {
+ if (client->on_online_changed)
+ client->on_online_changed(client, false);
+ dev_dbg(cd->dev, "Fieldbus OFF");
+ }
+ if (ev & EVENT_CAUSE_DC) {
+ if (client->on_area_updated)
+ client->on_area_updated(client);
+ dev_dbg(cd->dev, "Fieldbus data changed");
+ }
+ /*
+ * reset the event cause bits.
+ * this must be done while owning the fbctrl area, so we'll
+ * enqueue a task to do that.
+ */
+ t = create_area_writer(cd->qcache, IND_AX_FBCTRL,
+ REG_EVENT_CAUSE, &zero, sizeof(zero));
+ if (!t) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ t->done_fn = softint_ack;
+ ret = ab_task_enqueue(t, cd->powerq, &cd->qlock, &cd->wq);
+ ab_task_put(t);
+ cd->softint_pending = true;
+out:
+ WARN_ON(ret);
+ if (ret)
+ softint_ack(cd);
+}
+
+static int qthread_fn(void *data)
+{
+ struct anybuss_host *cd = data;
+ struct kfifo *qs = cd->qs;
+ size_t nqs = ARRAY_SIZE(cd->qs);
+ unsigned int ind_ab;
+
+ /*
+ * this kernel thread has exclusive access to the anybus's memory.
+ * only exception: the IND_AB register, which is accessed exclusively
+ * by the interrupt service routine (ISR). This thread must not touch
+ * the IND_AB register, but it does require access to its value.
+ *
+ * the interrupt service routine stores the register's value in
+ * cd->ind_ab (an atomic_t), where we may safely access it, with the
+ * understanding that it can be modified by the ISR at any time.
+ */
+
+ while (!kthread_should_stop()) {
+ /*
+ * make a local copy of IND_AB, so we can go around the loop
+ * again in case it changed while processing queues and softint.
+ */
+ ind_ab = atomic_read(&cd->ind_ab);
+ process_qs(cd);
+ process_softint(cd);
+ wait_event_timeout(cd->wq,
+ (atomic_read(&cd->ind_ab) != ind_ab) ||
+ qs_have_work(qs, nqs) ||
+ kthread_should_stop(),
+ HZ);
+ /*
+ * time out so even 'stuck' tasks will run eventually,
+ * and can time out.
+ */
+ }
+
+ return 0;
+}
+
+/* ------------------------ anybus exports ------------------------ */
+
+int anybuss_start_init(struct anybuss_client *client,
+ const struct anybuss_memcfg *cfg)
+{
+ int ret;
+ u16 op_mode;
+ struct anybuss_host *cd = client->host;
+ struct msg_anybus_init msg = {
+ .input_io_len = cpu_to_be16(cfg->input_io),
+ .input_dpram_len = cpu_to_be16(cfg->input_dpram),
+ .input_total_len = cpu_to_be16(cfg->input_total),
+ .output_io_len = cpu_to_be16(cfg->output_io),
+ .output_dpram_len = cpu_to_be16(cfg->output_dpram),
+ .output_total_len = cpu_to_be16(cfg->output_total),
+ .notif_config = cpu_to_be16(0x000F),
+ .wd_val = cpu_to_be16(0),
+ };
+
+ switch (cfg->offl_mode) {
+ case AB_OFFL_MODE_CLEAR:
+ op_mode = 0;
+ break;
+ case AB_OFFL_MODE_FREEZE:
+ op_mode = OP_MODE_FBFC;
+ break;
+ case AB_OFFL_MODE_SET:
+ op_mode = OP_MODE_FBS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ msg.op_mode = cpu_to_be16(op_mode | OP_MODE_CD);
+ ret = _anybus_mbox_cmd(cd, CMD_START_INIT, false, NULL, 0,
+ NULL, 0, NULL, 0);
+ if (ret)
+ return ret;
+ return _anybus_mbox_cmd(cd, CMD_ANYBUS_INIT, false,
+ &msg, sizeof(msg), NULL, 0, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_start_init);
+
+int anybuss_finish_init(struct anybuss_client *client)
+{
+ struct anybuss_host *cd = client->host;
+
+ return _anybus_mbox_cmd(cd, CMD_END_INIT, false, NULL, 0,
+ NULL, 0, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_finish_init);
+
+int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
+ void *buf, size_t count)
+{
+ struct anybuss_host *cd = client->host;
+ struct ab_task *t;
+ int ret;
+
+ if (count == 0)
+ return 0;
+ if (!area_range_ok(addr, count, FBCTRL_AREA,
+ MAX_FBCTRL_AREA_SZ))
+ return -EFAULT;
+ t = create_area_reader(cd->qcache, IND_AX_FBCTRL, addr, count);
+ if (!t)
+ return -ENOMEM;
+ ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+ if (ret)
+ goto out;
+ memcpy(buf, t->area_pd.buf, count);
+out:
+ ab_task_put(t);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(anybuss_read_fbctrl);
+
+int anybuss_write_input(struct anybuss_client *client,
+ const char __user *buf, size_t size,
+ loff_t *offset)
+{
+ ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size);
+ struct anybuss_host *cd = client->host;
+ struct ab_task *t;
+ int ret;
+
+ if (len <= 0)
+ return 0;
+ t = create_area_user_writer(cd->qcache, IND_AX_IN,
+ DATA_IN_AREA + *offset, buf, len);
+ if (IS_ERR(t))
+ return PTR_ERR(t);
+ ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+ ab_task_put(t);
+ if (ret)
+ return ret;
+ /* success */
+ *offset += len;
+ return len;
+}
+EXPORT_SYMBOL_GPL(anybuss_write_input);
+
+int anybuss_read_output(struct anybuss_client *client,
+ char __user *buf, size_t size,
+ loff_t *offset)
+{
+ ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size);
+ struct anybuss_host *cd = client->host;
+ struct ab_task *t;
+ int ret;
+
+ if (len <= 0)
+ return 0;
+ t = create_area_reader(cd->qcache, IND_AX_OUT,
+ DATA_OUT_AREA + *offset, len);
+ if (!t)
+ return -ENOMEM;
+ ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+ if (ret)
+ goto out;
+ if (copy_to_user(buf, t->area_pd.buf, len))
+ ret = -EFAULT;
+out:
+ ab_task_put(t);
+ if (ret)
+ return ret;
+ /* success */
+ *offset += len;
+ return len;
+}
+EXPORT_SYMBOL_GPL(anybuss_read_output);
+
+int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
+ const void *buf, size_t count)
+{
+ struct anybuss_host *cd = client->host;
+
+ return _anybus_mbox_cmd(cd, cmd_num, true, buf, count, NULL, 0,
+ NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_send_msg);
+
+int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
+ const void *buf, size_t count)
+{
+ struct anybuss_host *cd = client->host;
+
+ return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, NULL, 0,
+ buf, count);
+}
+EXPORT_SYMBOL_GPL(anybuss_send_ext);
+
+int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
+ void *buf, size_t count)
+{
+ struct anybuss_host *cd = client->host;
+
+ return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, buf, count,
+ NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_recv_msg);
+
+/* ------------------------ bus functions ------------------------ */
+
+static int anybus_bus_match(struct device *dev,
+ struct device_driver *drv)
+{
+ struct anybuss_client_driver *adrv =
+ to_anybuss_client_driver(drv);
+ struct anybuss_client *adev =
+ to_anybuss_client(dev);
+
+ return adrv->fieldbus_type == adev->fieldbus_type;
+}
+
+static int anybus_bus_probe(struct device *dev)
+{
+ struct anybuss_client_driver *adrv =
+ to_anybuss_client_driver(dev->driver);
+ struct anybuss_client *adev =
+ to_anybuss_client(dev);
+
+ if (!adrv->probe)
+ return -ENODEV;
+ return adrv->probe(adev);
+}
+
+static int anybus_bus_remove(struct device *dev)
+{
+ struct anybuss_client_driver *adrv =
+ to_anybuss_client_driver(dev->driver);
+
+ if (adrv->remove)
+ return adrv->remove(to_anybuss_client(dev));
+ return 0;
+}
+
+static struct bus_type anybus_bus = {
+ .name = "anybuss",
+ .match = anybus_bus_match,
+ .probe = anybus_bus_probe,
+ .remove = anybus_bus_remove,
+};
+
+int anybuss_client_driver_register(struct anybuss_client_driver *drv)
+{
+ drv->driver.bus = &anybus_bus;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(anybuss_client_driver_register);
+
+void anybuss_client_driver_unregister(struct anybuss_client_driver *drv)
+{
+ return driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(anybuss_client_driver_unregister);
+
+static void client_device_release(struct device *dev)
+{
+ kfree(to_anybuss_client(dev));
+}
+
+static int taskq_alloc(struct device *dev, struct kfifo *q)
+{
+ void *buf;
+ size_t size = 64 * sizeof(struct ab_task *);
+
+ buf = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!buf)
+ return -EIO;
+ return kfifo_init(q, buf, size);
+}
+
+static int anybus_of_get_host_idx(struct device_node *np)
+{
+ const __be32 *host_idx;
+
+ host_idx = of_get_address(np, 0, NULL, NULL);
+ if (!host_idx)
+ return -ENOENT;
+ return __be32_to_cpu(*host_idx);
+}
+
+static struct device_node *
+anybus_of_find_child_device(struct device *dev, int host_idx)
+{
+ struct device_node *node;
+
+ if (!dev || !dev->of_node)
+ return NULL;
+ for_each_child_of_node(dev->of_node, node) {
+ if (anybus_of_get_host_idx(node) == host_idx)
+ return node;
+ }
+ return NULL;
+}
+
+struct anybuss_host * __must_check
+anybuss_host_common_probe(struct device *dev,
+ const struct anybuss_ops *ops)
+{
+ int ret, i;
+ u8 val[4];
+ u16 fieldbus_type;
+ struct anybuss_host *cd;
+
+ cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
+ if (!cd)
+ return ERR_PTR(-ENOMEM);
+ cd->dev = dev;
+ cd->host_idx = ops->host_idx;
+ init_completion(&cd->card_boot);
+ init_waitqueue_head(&cd->wq);
+ for (i = 0; i < ARRAY_SIZE(cd->qs); i++) {
+ ret = taskq_alloc(dev, &cd->qs[i]);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+ if (WARN_ON(ARRAY_SIZE(cd->qs) < 3))
+ return ERR_PTR(-EINVAL);
+ cd->powerq = &cd->qs[0];
+ cd->mboxq = &cd->qs[1];
+ cd->areaq = &cd->qs[2];
+ cd->reset = ops->reset;
+ if (!cd->reset)
+ return ERR_PTR(-EINVAL);
+ cd->regmap = ops->regmap;
+ if (!cd->regmap)
+ return ERR_PTR(-EINVAL);
+ spin_lock_init(&cd->qlock);
+ cd->qcache = kmem_cache_create(dev_name(dev),
+ sizeof(struct ab_task), 0, 0, NULL);
+ if (!cd->qcache)
+ return ERR_PTR(-ENOMEM);
+ cd->irq = ops->irq;
+ if (cd->irq <= 0) {
+ ret = -EINVAL;
+ goto err_qcache;
+ }
+ /*
+ * use a dpram test to check if a card is present, this is only
+ * possible while in reset.
+ */
+ reset_assert(cd);
+ if (test_dpram(cd->regmap)) {
+ dev_err(dev, "no Anybus-S card in slot");
+ ret = -ENODEV;
+ goto err_qcache;
+ }
+ ret = devm_request_threaded_irq(dev, cd->irq, NULL, irq_handler,
+ IRQF_ONESHOT, dev_name(dev), cd);
+ if (ret) {
+ dev_err(dev, "could not request irq");
+ goto err_qcache;
+ }
+ /*
+ * startup sequence:
+ * perform dummy IND_AB read to prevent false 'init done' irq
+ * (already done by test_dpram() above)
+ * release reset
+ * wait for first interrupt
+ * interrupt came in: ready to go !
+ */
+ reset_deassert(cd);
+ ret = wait_for_completion_timeout(&cd->card_boot, TIMEOUT);
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ if (ret < 0)
+ goto err_reset;
+ /*
+ * according to the anybus docs, we're allowed to read these
+ * without handshaking / reserving the area
+ */
+ dev_info(dev, "Anybus-S card detected");
+ regmap_bulk_read(cd->regmap, REG_BOOTLOADER_V, val, 2);
+ dev_info(dev, "Bootloader version: %02X%02X",
+ val[0], val[1]);
+ regmap_bulk_read(cd->regmap, REG_API_V, val, 2);
+ dev_info(dev, "API version: %02X%02X", val[0], val[1]);
+ regmap_bulk_read(cd->regmap, REG_FIELDBUS_V, val, 2);
+ dev_info(dev, "Fieldbus version: %02X%02X", val[0], val[1]);
+ regmap_bulk_read(cd->regmap, REG_SERIAL_NO, val, 4);
+ dev_info(dev, "Serial number: %02X%02X%02X%02X",
+ val[0], val[1], val[2], val[3]);
+ add_device_randomness(&val, 4);
+ regmap_bulk_read(cd->regmap, REG_FIELDBUS_TYPE, &fieldbus_type,
+ sizeof(fieldbus_type));
+ fieldbus_type = be16_to_cpu(fieldbus_type);
+ dev_info(dev, "Fieldbus type: %04X", fieldbus_type);
+ regmap_bulk_read(cd->regmap, REG_MODULE_SW_V, val, 2);
+ dev_info(dev, "Module SW version: %02X%02X",
+ val[0], val[1]);
+ /* put card back reset until a client driver releases it */
+ disable_irq(cd->irq);
+ reset_assert(cd);
+ atomic_set(&cd->ind_ab, IND_AB_UPDATED);
+ /* fire up the queue thread */
+ cd->qthread = kthread_run(qthread_fn, cd, dev_name(dev));
+ if (IS_ERR(cd->qthread)) {
+ dev_err(dev, "could not create kthread");
+ ret = PTR_ERR(cd->qthread);
+ goto err_reset;
+ }
+ /*
+ * now advertise that we've detected a client device (card).
+ * the bus infrastructure will match it to a client driver.
+ */
+ cd->client = kzalloc(sizeof(*cd->client), GFP_KERNEL);
+ if (!cd->client) {
+ ret = -ENOMEM;
+ goto err_kthread;
+ }
+ cd->client->fieldbus_type = fieldbus_type;
+ cd->client->host = cd;
+ cd->client->dev.bus = &anybus_bus;
+ cd->client->dev.parent = dev;
+ cd->client->dev.release = client_device_release;
+ cd->client->dev.of_node =
+ anybus_of_find_child_device(dev, cd->host_idx);
+ dev_set_name(&cd->client->dev, "anybuss.card%d", cd->host_idx);
+ ret = device_register(&cd->client->dev);
+ if (ret)
+ goto err_device;
+ return cd;
+err_device:
+ device_unregister(&cd->client->dev);
+err_kthread:
+ kthread_stop(cd->qthread);
+err_reset:
+ reset_assert(cd);
+err_qcache:
+ kmem_cache_destroy(cd->qcache);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(anybuss_host_common_probe);
+
+void anybuss_host_common_remove(struct anybuss_host *host)
+{
+ struct anybuss_host *cd = host;
+
+ device_unregister(&cd->client->dev);
+ kthread_stop(cd->qthread);
+ reset_assert(cd);
+ kmem_cache_destroy(cd->qcache);
+}
+EXPORT_SYMBOL_GPL(anybuss_host_common_remove);
+
+static void host_release(struct device *dev, void *res)
+{
+ struct anybuss_host **dr = res;
+
+ anybuss_host_common_remove(*dr);
+}
+
+struct anybuss_host * __must_check
+devm_anybuss_host_common_probe(struct device *dev,
+ const struct anybuss_ops *ops)
+{
+ struct anybuss_host **dr;
+ struct anybuss_host *host;
+
+ dr = devres_alloc(host_release, sizeof(struct anybuss_host *),
+ GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ host = anybuss_host_common_probe(dev, ops);
+ if (IS_ERR(host)) {
+ devres_free(dr);
+ return host;
+ }
+ *dr = host;
+ devres_add(dev, dr);
+ return host;
+}
+EXPORT_SYMBOL_GPL(devm_anybuss_host_common_probe);
+
+static int __init anybus_init(void)
+{
+ int ret;
+
+ ret = bus_register(&anybus_bus);
+ if (ret)
+ pr_err("could not register Anybus-S bus: %d\n", ret);
+ return ret;
+}
+module_init(anybus_init);
+
+static void __exit anybus_exit(void)
+{
+ bus_unregister(&anybus_bus);
+}
+module_exit(anybus_exit);
+
+MODULE_DESCRIPTION("HMS Anybus-S Host Driver");
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/fieldbus/dev_core.c b/drivers/staging/fieldbus/dev_core.c
new file mode 100644
index 000000000000..60b85140675a
--- /dev/null
+++ b/drivers/staging/fieldbus/dev_core.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Fieldbus Device Driver Core
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+
+/* move to <linux/fieldbus_dev.h> when taking this out of staging */
+#include "fieldbus_dev.h"
+
+/* Maximum number of fieldbus devices */
+#define MAX_FIELDBUSES 32
+
+/* the dev_t structure to store the dynamically allocated fieldbus devices */
+static dev_t fieldbus_devt;
+static DEFINE_IDA(fieldbus_ida);
+static DEFINE_MUTEX(fieldbus_mtx);
+
+static const char ctrl_enabled[] = "enabled";
+static const char ctrl_disabled[] = "disabled";
+
+static ssize_t online_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", !!fb->online);
+}
+static DEVICE_ATTR_RO(online);
+
+static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+ if (!fb->enable_get)
+ return -EINVAL;
+ return sprintf(buf, "%d\n", !!fb->enable_get(fb));
+}
+
+static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+ bool value;
+ int ret;
+
+ if (!fb->simple_enable_set)
+ return -ENOTSUPP;
+ ret = kstrtobool(buf, &value);
+ if (ret)
+ return ret;
+ ret = fb->simple_enable_set(fb, value);
+ if (ret < 0)
+ return ret;
+ return n;
+}
+static DEVICE_ATTR_RW(enabled);
+
+static ssize_t card_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+ /*
+ * card_name was provided by child driver, could potentially be long.
+ * protect against buffer overrun.
+ */
+ return snprintf(buf, PAGE_SIZE, "%s\n", fb->card_name);
+}
+static DEVICE_ATTR_RO(card_name);
+
+static ssize_t read_area_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%zu\n", fb->read_area_sz);
+}
+static DEVICE_ATTR_RO(read_area_size);
+
+static ssize_t write_area_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%zu\n", fb->write_area_sz);
+}
+static DEVICE_ATTR_RO(write_area_size);
+
+static ssize_t fieldbus_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+ return fb->fieldbus_id_get(fb, buf, PAGE_SIZE);
+}
+static DEVICE_ATTR_RO(fieldbus_id);
+
+static ssize_t fieldbus_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+ const char *t;
+
+ switch (fb->fieldbus_type) {
+ case FIELDBUS_DEV_TYPE_PROFINET:
+ t = "profinet";
+ break;
+ default:
+ t = "unknown";
+ break;
+ }
+
+ return sprintf(buf, "%s\n", t);
+}
+static DEVICE_ATTR_RO(fieldbus_type);
+
+static struct attribute *fieldbus_attrs[] = {
+ &dev_attr_enabled.attr,
+ &dev_attr_card_name.attr,
+ &dev_attr_fieldbus_id.attr,
+ &dev_attr_read_area_size.attr,
+ &dev_attr_write_area_size.attr,
+ &dev_attr_online.attr,
+ &dev_attr_fieldbus_type.attr,
+ NULL,
+};
+
+static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
+ int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct fieldbus_dev *fb = dev_get_drvdata(dev);
+ umode_t mode = attr->mode;
+
+ if (attr == &dev_attr_enabled.attr) {
+ mode = 0;
+ if (fb->enable_get)
+ mode |= 0444;
+ if (fb->simple_enable_set)
+ mode |= 0200;
+ }
+
+ return mode;
+}
+
+static const struct attribute_group fieldbus_group = {
+ .attrs = fieldbus_attrs,
+ .is_visible = fieldbus_is_visible,
+};
+__ATTRIBUTE_GROUPS(fieldbus);
+
+static struct class fieldbus_class = {
+ .name = "fieldbus_dev",
+ .owner = THIS_MODULE,
+ .dev_groups = fieldbus_groups,
+};
+
+struct fb_open_file {
+ struct fieldbus_dev *fbdev;
+ int dc_event;
+};
+
+static int fieldbus_open(struct inode *inode, struct file *filp)
+{
+ struct fb_open_file *of;
+ struct fieldbus_dev *fbdev = container_of(inode->i_cdev,
+ struct fieldbus_dev,
+ cdev);
+
+ of = kzalloc(sizeof(*of), GFP_KERNEL);
+ if (!of)
+ return -ENOMEM;
+ of->fbdev = fbdev;
+ filp->private_data = of;
+ return 0;
+}
+
+static int fieldbus_release(struct inode *node, struct file *filp)
+{
+ struct fb_open_file *of = filp->private_data;
+
+ kfree(of);
+ return 0;
+}
+
+static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size,
+ loff_t *offset)
+{
+ struct fb_open_file *of = filp->private_data;
+ struct fieldbus_dev *fbdev = of->fbdev;
+
+ of->dc_event = fbdev->dc_event;
+ return fbdev->read_area(fbdev, buf, size, offset);
+}
+
+static ssize_t fieldbus_write(struct file *filp, const char __user *buf,
+ size_t size, loff_t *offset)
+{
+ struct fb_open_file *of = filp->private_data;
+ struct fieldbus_dev *fbdev = of->fbdev;
+
+ return fbdev->write_area(fbdev, buf, size, offset);
+}
+
+static unsigned int fieldbus_poll(struct file *filp, poll_table *wait)
+{
+ struct fb_open_file *of = filp->private_data;
+ struct fieldbus_dev *fbdev = of->fbdev;
+ unsigned int mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
+
+ poll_wait(filp, &fbdev->dc_wq, wait);
+ /* data changed ? */
+ if (fbdev->dc_event != of->dc_event)
+ mask |= POLLPRI | POLLERR;
+ return mask;
+}
+
+static const struct file_operations fieldbus_fops = {
+ .open = fieldbus_open,
+ .release = fieldbus_release,
+ .read = fieldbus_read,
+ .write = fieldbus_write,
+ .poll = fieldbus_poll,
+ .llseek = generic_file_llseek,
+ .owner = THIS_MODULE,
+};
+
+void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
+{
+ fb->dc_event++;
+ wake_up_all(&fb->dc_wq);
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
+
+void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
+{
+ fb->online = online;
+ kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
+
+static void __fieldbus_dev_unregister(struct fieldbus_dev *fb)
+{
+ if (!fb)
+ return;
+ device_destroy(&fieldbus_class, fb->cdev.dev);
+ cdev_del(&fb->cdev);
+ ida_simple_remove(&fieldbus_ida, fb->id);
+}
+
+void fieldbus_dev_unregister(struct fieldbus_dev *fb)
+{
+ mutex_lock(&fieldbus_mtx);
+ __fieldbus_dev_unregister(fb);
+ mutex_unlock(&fieldbus_mtx);
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
+
+static int __fieldbus_dev_register(struct fieldbus_dev *fb)
+{
+ dev_t devno;
+ int err;
+
+ if (!fb)
+ return -EINVAL;
+ if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get)
+ return -EINVAL;
+ fb->id = ida_simple_get(&fieldbus_ida, 0, MAX_FIELDBUSES, GFP_KERNEL);
+ if (fb->id < 0)
+ return fb->id;
+ devno = MKDEV(MAJOR(fieldbus_devt), fb->id);
+ init_waitqueue_head(&fb->dc_wq);
+ cdev_init(&fb->cdev, &fieldbus_fops);
+ err = cdev_add(&fb->cdev, devno, 1);
+ if (err) {
+ pr_err("fieldbus_dev%d unable to add device %d:%d\n",
+ fb->id, MAJOR(fieldbus_devt), fb->id);
+ goto err_cdev;
+ }
+ fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb,
+ "fieldbus_dev%d", fb->id);
+ if (IS_ERR(fb->dev)) {
+ err = PTR_ERR(fb->dev);
+ goto err_dev_create;
+ }
+ return 0;
+
+err_dev_create:
+ cdev_del(&fb->cdev);
+err_cdev:
+ ida_simple_remove(&fieldbus_ida, fb->id);
+ return err;
+}
+
+int fieldbus_dev_register(struct fieldbus_dev *fb)
+{
+ int err;
+
+ mutex_lock(&fieldbus_mtx);
+ err = __fieldbus_dev_register(fb);
+ mutex_unlock(&fieldbus_mtx);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_register);
+
+static int __init fieldbus_init(void)
+{
+ int err;
+
+ err = class_register(&fieldbus_class);
+ if (err < 0) {
+ pr_err("fieldbus_dev: could not register class\n");
+ return err;
+ }
+ err = alloc_chrdev_region(&fieldbus_devt, 0,
+ MAX_FIELDBUSES, "fieldbus_dev");
+ if (err < 0) {
+ pr_err("fieldbus_dev: unable to allocate char dev region\n");
+ goto err_alloc;
+ }
+ return 0;
+
+err_alloc:
+ class_unregister(&fieldbus_class);
+ return err;
+}
+
+static void __exit fieldbus_exit(void)
+{
+ unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES);
+ class_unregister(&fieldbus_class);
+ ida_destroy(&fieldbus_ida);
+}
+
+subsys_initcall(fieldbus_init);
+module_exit(fieldbus_exit);
+
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
+MODULE_DESCRIPTION("Fieldbus Device Driver Core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/fieldbus/fieldbus_dev.h b/drivers/staging/fieldbus/fieldbus_dev.h
new file mode 100644
index 000000000000..a10fc3b446dc
--- /dev/null
+++ b/drivers/staging/fieldbus/fieldbus_dev.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Fieldbus Device Driver Core
+ *
+ */
+
+#ifndef __FIELDBUS_DEV_H
+#define __FIELDBUS_DEV_H
+
+#include <linux/cdev.h>
+#include <linux/wait.h>
+
+enum fieldbus_dev_type {
+ FIELDBUS_DEV_TYPE_UNKNOWN = 0,
+ FIELDBUS_DEV_TYPE_PROFINET,
+};
+
+/**
+ * struct fieldbus_dev - Fieldbus device
+ * @read_area: [DRIVER] function to read the process data area of the
+ * device. same parameters/return values as
+ * the read function in struct file_operations
+ * @write_area: [DRIVER] function to write to the process data area of
+ * the device. same parameters/return values as
+ * the write function in struct file_operations
+ * @write_area_sz [DRIVER] size of the writable process data area
+ * @read_area_sz [DRIVER] size of the readable process data area
+ * @card_name [DRIVER] name of the card, e.g. "ACME Inc. profinet"
+ * @fieldbus_type [DRIVER] fieldbus type of this device, e.g.
+ * FIELDBUS_DEV_TYPE_PROFINET
+ * @enable_get [DRIVER] function which returns true if the card
+ * is enabled, false otherwise
+ * @fieldbus_id_get [DRIVER] function to retrieve the unique fieldbus id
+ * by which this device can be identified;
+ * return value follows the snprintf convention
+ * @simple_enable_set [DRIVER] (optional) function to enable the device
+ * according to its default settings
+ * @parent [DRIVER] (optional) the device's parent device
+ */
+struct fieldbus_dev {
+ ssize_t (*read_area)(struct fieldbus_dev *fbdev, char __user *buf,
+ size_t size, loff_t *offset);
+ ssize_t (*write_area)(struct fieldbus_dev *fbdev,
+ const char __user *buf, size_t size,
+ loff_t *offset);
+ size_t write_area_sz, read_area_sz;
+ const char *card_name;
+ enum fieldbus_dev_type fieldbus_type;
+ bool (*enable_get)(struct fieldbus_dev *fbdev);
+ int (*fieldbus_id_get)(struct fieldbus_dev *fbdev, char *buf,
+ size_t max_size);
+ int (*simple_enable_set)(struct fieldbus_dev *fbdev, bool enable);
+ struct device *parent;
+
+ /* private data */
+ int id;
+ struct cdev cdev;
+ struct device *dev;
+ int dc_event;
+ wait_queue_head_t dc_wq;
+ bool online;
+};
+
+#if IS_ENABLED(CONFIG_FIELDBUS_DEV)
+
+/**
+ * fieldbus_dev_unregister()
+ * - unregister a previously registered fieldbus device
+ * @fb: Device structure previously registered
+ **/
+void fieldbus_dev_unregister(struct fieldbus_dev *fb);
+
+/**
+ * fieldbus_dev_register()
+ * - register a device with the fieldbus device subsystem
+ * @fb: Device structure filled by the device driver
+ **/
+int __must_check fieldbus_dev_register(struct fieldbus_dev *fb);
+
+/**
+ * fieldbus_dev_area_updated()
+ * - notify the subsystem that an external fieldbus controller updated
+ * the process data area
+ * @fb: Device structure
+ **/
+void fieldbus_dev_area_updated(struct fieldbus_dev *fb);
+
+/**
+ * fieldbus_dev_online_changed()
+ * - notify the subsystem that the fieldbus online status changed
+ * @fb: Device structure
+ **/
+void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online);
+
+#else /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
+
+static inline void fieldbus_dev_unregister(struct fieldbus_dev *fb) {}
+static inline int __must_check fieldbus_dev_register(struct fieldbus_dev *fb)
+{
+ return -ENOTSUPP;
+}
+
+static inline void fieldbus_dev_area_updated(struct fieldbus_dev *fb) {}
+static inline void fieldbus_dev_online_changed(struct fieldbus_dev *fb,
+ bool online) {}
+
+#endif /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
+#endif /* __FIELDBUS_DEV_H */
diff --git a/drivers/staging/gasket/gasket_page_table.c b/drivers/staging/gasket/gasket_page_table.c
index 26755d9ca41d..600928f63577 100644
--- a/drivers/staging/gasket/gasket_page_table.c
+++ b/drivers/staging/gasket/gasket_page_table.c
@@ -768,8 +768,7 @@ static bool gasket_is_extended_dev_addr_bad(struct gasket_page_table *pg_tbl,
page_lvl0_idx = gasket_extended_lvl0_page_idx(pg_tbl, dev_addr);
/* Get the count of affected level 0 pages. */
- num_lvl0_pages = (num_pages + GASKET_PAGES_PER_SUBTABLE - 1) /
- GASKET_PAGES_PER_SUBTABLE;
+ num_lvl0_pages = DIV_ROUND_UP(num_pages, GASKET_PAGES_PER_SUBTABLE);
if (gasket_components_to_dev_address(pg_tbl, 0, page_global_idx,
page_offset) != dev_addr) {
@@ -1258,7 +1257,7 @@ int gasket_alloc_coherent_memory(struct gasket_dev *gasket_dev, u64 size,
dma_addr_t handle;
void *mem;
int j;
- unsigned int num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+ unsigned int num_pages = DIV_ROUND_UP(size, PAGE_SIZE);
const struct gasket_driver_desc *driver_desc =
gasket_get_driver_desc(gasket_dev);
diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c
index 0529e5628c24..34b40a409ea3 100644
--- a/drivers/staging/greybus/power_supply.c
+++ b/drivers/staging/greybus/power_supply.c
@@ -520,8 +520,8 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
op = gb_operation_create(connection,
GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
- sizeof(req), sizeof(*resp) + props_count *
- sizeof(struct gb_power_supply_props_desc),
+ sizeof(*req),
+ struct_size(resp, props, props_count),
GFP_KERNEL);
if (!op)
return -ENOMEM;
diff --git a/drivers/staging/gs_fpgaboot/Kconfig b/drivers/staging/gs_fpgaboot/Kconfig
index 232788def2c0..968a153c4ab6 100644
--- a/drivers/staging/gs_fpgaboot/Kconfig
+++ b/drivers/staging/gs_fpgaboot/Kconfig
@@ -4,6 +4,5 @@
#
config GS_FPGABOOT
tristate "Xilinx FPGA firmware download module"
- default n
help
Xilinx FPGA firmware download module
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 2d86167aeeed..b6d12fe7c12a 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -109,10 +109,10 @@
#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */
#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */
-#define AD7193_CH_AIN1P_AIN2M 0x000 /* AIN1(+) - AIN2(-) */
-#define AD7193_CH_AIN3P_AIN4M 0x001 /* AIN3(+) - AIN4(-) */
-#define AD7193_CH_AIN5P_AIN6M 0x002 /* AIN5(+) - AIN6(-) */
-#define AD7193_CH_AIN7P_AIN8M 0x004 /* AIN7(+) - AIN8(-) */
+#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */
+#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */
+#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */
+#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */
#define AD7193_CH_TEMP 0x100 /* Temp senseor */
#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */
#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */
diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c
index b7fbf9022046..68da6ecde6a3 100644
--- a/drivers/staging/iio/meter/ade7854.c
+++ b/drivers/staging/iio/meter/ade7854.c
@@ -268,7 +268,7 @@ static IIO_DEV_ATTR_VPEAK(0644,
static IIO_DEV_ATTR_IPEAK(0644,
ade7854_read_32bit,
ade7854_write_32bit,
- ADE7854_VPEAK);
+ ADE7854_IPEAK);
static IIO_DEV_ATTR_APHCAL(0644,
ade7854_read_16bit,
ade7854_write_16bit,
diff --git a/drivers/staging/kpc2000/Kconfig b/drivers/staging/kpc2000/Kconfig
new file mode 100644
index 000000000000..926e770d6e0e
--- /dev/null
+++ b/drivers/staging/kpc2000/Kconfig
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config KPC2000
+ bool "Daktronics KPC Device support"
+ depends on PCI
+ help
+ Select this if you wish to use the Daktronics KPC PCI devices
+
+ If unsure, say N.
+
+config KPC2000_CORE
+ tristate "Daktronics KPC PCI UIO device"
+ depends on KPC2000
+ help
+ Say Y here if you wish to support the Daktronics KPC PCI
+ device in UIO mode.
+
+ To compile this driver as a module, choose M here: the module
+ will be called kpc2000
+
+ If unsure, say N.
+
+config KPC2000_SPI
+ tristate "Kaktronics KPC SPI device"
+ depends on KPC2000 && SPI
+ help
+ Say Y here if you wish to support the Daktronics KPC PCI
+ device in SPI mode.
+
+ To compile this driver as a module, choose M here: the module
+ will be called kpc2000_spi
+
+ If unsure, say N.
+
+config KPC2000_I2C
+ tristate "Kaktronics KPC I2C device"
+ depends on KPC2000 && I2C
+ help
+ Say Y here if you wish to support the Daktronics KPC PCI
+ device in I2C mode.
+
+ To compile this driver as a module, choose M here: the module
+ will be called kpc2000_i2c
+
+ If unsure, say N.
+
diff --git a/drivers/staging/kpc2000/Makefile b/drivers/staging/kpc2000/Makefile
new file mode 100644
index 000000000000..6fcb2ee7b27d
--- /dev/null
+++ b/drivers/staging/kpc2000/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_KPC2000) += kpc2000/
+obj-$(CONFIG_KPC2000_I2C) += kpc_i2c/
+obj-$(CONFIG_KPC2000_SPI) += kpc_spi/
diff --git a/drivers/staging/kpc2000/TODO b/drivers/staging/kpc2000/TODO
new file mode 100644
index 000000000000..8c7af29fefae
--- /dev/null
+++ b/drivers/staging/kpc2000/TODO
@@ -0,0 +1,8 @@
+- the kpc_spi driver doesn't seem to let multiple transactions (to different instances of the core) happen in parallel...
+- The kpc_i2c driver is a hot mess, it should probably be cleaned up a ton. It functions against current hardware though.
+- pcard->card_num in kp2000_pcie_probe() is a global variable and needs atomic / locking / something better.
+- probe_core_uio() probably needs error handling
+- the loop in kp2000_probe_cores() that uses probe_core_uio() also probably needs error handling
+- would be nice if the AIO fileops in kpc_dma could be made to work
+ - probably want to add a CONFIG_ option to control compilation of the AIO functions
+- if the AIO fileops in kpc_dma start working, next would be making iov_count > 1 work too
diff --git a/drivers/staging/kpc2000/kpc.h b/drivers/staging/kpc2000/kpc.h
new file mode 100644
index 000000000000..a3fc9c9221aa
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KPC_H_
+#define KPC_H_
+
+/* ***** Driver Names ***** */
+#define KP_DRIVER_NAME_KP2000 "kp2000"
+#define KP_DRIVER_NAME_INVALID "kpc_invalid"
+#define KP_DRIVER_NAME_DMA_CONTROLLER "kpc_nwl_dma"
+#define KP_DRIVER_NAME_UIO "uio_pdrv_genirq"
+#define KP_DRIVER_NAME_I2C "kpc_i2c"
+#define KP_DRIVER_NAME_SPI "kpc_spi"
+
+struct kpc_core_device_platdata {
+ u32 card_id;
+ u32 build_version;
+ u32 hardware_revision;
+ u64 ssid;
+ u64 ddna;
+};
+
+#define PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0 0x4b03
+
+#endif /* KPC_H_ */
diff --git a/drivers/staging/kpc2000/kpc2000/Makefile b/drivers/staging/kpc2000/kpc2000/Makefile
new file mode 100644
index 000000000000..28ab1e185f9f
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-m := kpc2000.o
+kpc2000-objs += kp2000_module.o core.o cell_probe.o fileops.o
diff --git a/drivers/staging/kpc2000/kpc2000/cell_probe.c b/drivers/staging/kpc2000/kpc2000/cell_probe.c
new file mode 100644
index 000000000000..ad2cc0a3bfa1
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/cell_probe.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/uio_driver.h>
+#include "pcie.h"
+
+/* Core (Resource) Table Layout:
+ * one Resource per record (8 bytes)
+ * 6 5 4 3 2 1 0
+ * 3210987654321098765432109876543210987654321098765432109876543210
+ * IIIIIIIIIIII Core Type [up to 4095 types]
+ * D S2C DMA Present
+ * DDD S2C DMA Channel Number [up to 8 channels]
+ * LLLLLLLLLLLLLLLL Register Count (64-bit registers) [up to 65535 registers]
+ * OOOOOOOOOOOOOOOO Core Offset (in 4kB blocks) [up to 65535 cores]
+ * D C2S DMA Present
+ * DDD C2S DMA Channel Number [up to 8 channels]
+ * II IRQ Count [0 to 3 IRQs per core]
+ 1111111000
+ * IIIIIII IRQ Base Number [up to 128 IRQs per card]
+ * ___ Spare
+ *
+ */
+
+#define KPC_OLD_DMA_CH_NUM(present, channel) ((present) ? (0x8 | ((channel) & 0x7)) : 0)
+#define KPC_OLD_S2C_DMA_CH_NUM(cte) KPC_OLD_DMA_CH_NUM(cte.s2c_dma_present, cte.s2c_dma_channel_num)
+#define KPC_OLD_C2S_DMA_CH_NUM(cte) KPC_OLD_DMA_CH_NUM(cte.c2s_dma_present, cte.c2s_dma_channel_num)
+
+#define KP_CORE_ID_INVALID 0
+#define KP_CORE_ID_I2C 3
+#define KP_CORE_ID_SPI 5
+
+struct core_table_entry {
+ u16 type;
+ u32 offset;
+ u32 length;
+ bool s2c_dma_present;
+ u8 s2c_dma_channel_num;
+ bool c2s_dma_present;
+ u8 c2s_dma_channel_num;
+ u8 irq_count;
+ u8 irq_base_num;
+};
+
+static
+void parse_core_table_entry_v0(struct core_table_entry *cte, const u64 read_val)
+{
+ cte->type = ((read_val & 0xFFF0000000000000) >> 52);
+ cte->offset = ((read_val & 0x00000000FFFF0000) >> 16) * 4096;
+ cte->length = ((read_val & 0x0000FFFF00000000) >> 32) * 8;
+ cte->s2c_dma_present = ((read_val & 0x0008000000000000) >> 51);
+ cte->s2c_dma_channel_num = ((read_val & 0x0007000000000000) >> 48);
+ cte->c2s_dma_present = ((read_val & 0x0000000000008000) >> 15);
+ cte->c2s_dma_channel_num = ((read_val & 0x0000000000007000) >> 12);
+ cte->irq_count = ((read_val & 0x0000000000000C00) >> 10);
+ cte->irq_base_num = ((read_val & 0x00000000000003F8) >> 3);
+}
+
+static
+void dbg_cte(struct kp2000_device *pcard, struct core_table_entry *cte)
+{
+ dev_dbg(&pcard->pdev->dev, "CTE: type:%3d offset:%3d (%3d) length:%3d (%3d) s2c:%d c2s:%d irq_count:%d base_irq:%d\n",
+ cte->type,
+ cte->offset,
+ cte->offset / 4096,
+ cte->length,
+ cte->length / 8,
+ (cte->s2c_dma_present ? cte->s2c_dma_channel_num : -1),
+ (cte->c2s_dma_present ? cte->c2s_dma_channel_num : -1),
+ cte->irq_count,
+ cte->irq_base_num
+ );
+}
+
+static
+void parse_core_table_entry(struct core_table_entry *cte, const u64 read_val, const u8 entry_rev)
+{
+ switch (entry_rev) {
+ case 0: parse_core_table_entry_v0(cte, read_val); break;
+ default: cte->type = 0; break;
+ }
+}
+
+
+int probe_core_basic(unsigned int core_num, struct kp2000_device *pcard, char *name, const struct core_table_entry cte)
+{
+ struct mfd_cell cell = {0};
+ struct resource resources[2] = {0};
+
+ struct kpc_core_device_platdata core_pdata = {
+ .card_id = pcard->card_id,
+ .build_version = pcard->build_version,
+ .hardware_revision = pcard->hardware_revision,
+ .ssid = pcard->ssid,
+ .ddna = pcard->ddna,
+ };
+
+ dev_dbg(&pcard->pdev->dev, "Found Basic core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8);
+
+
+ cell.platform_data = &core_pdata;
+ cell.pdata_size = sizeof(struct kpc_core_device_platdata);
+ cell.name = name;
+ cell.id = core_num;
+ cell.num_resources = 2;
+
+ resources[0].start = cte.offset;
+ resources[0].end = cte.offset + (cte.length - 1);
+ resources[0].flags = IORESOURCE_MEM;
+
+ resources[1].start = pcard->pdev->irq;
+ resources[1].end = pcard->pdev->irq;
+ resources[1].flags = IORESOURCE_IRQ;
+
+ cell.resources = resources;
+
+ return mfd_add_devices(
+ PCARD_TO_DEV(pcard), // parent
+ pcard->card_num * 100, // id
+ &cell, // struct mfd_cell *
+ 1, // ndevs
+ &pcard->regs_base_resource,
+ 0, // irq_base
+ NULL // struct irq_domain *
+ );
+}
+
+
+struct kpc_uio_device {
+ struct list_head list;
+ struct kp2000_device *pcard;
+ struct device *dev;
+ struct uio_info uioinfo;
+ struct core_table_entry cte;
+ u16 core_num;
+};
+
+static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct kpc_uio_device *kudev = dev_get_drvdata(dev);
+
+ #define ATTR_NAME_CMP(v) (strcmp(v, attr->attr.name) == 0)
+ if ATTR_NAME_CMP("offset"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.offset);
+ } else if ATTR_NAME_CMP("size"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.length);
+ } else if ATTR_NAME_CMP("type"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.type);
+ }
+ else if ATTR_NAME_CMP("s2c_dma"){
+ if (kudev->cte.s2c_dma_present){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.s2c_dma_channel_num);
+ } else {
+ return scnprintf(buf, PAGE_SIZE, "not present\n");
+ }
+ } else if ATTR_NAME_CMP("c2s_dma"){
+ if (kudev->cte.c2s_dma_present){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.c2s_dma_channel_num);
+ } else {
+ return scnprintf(buf, PAGE_SIZE, "not present\n");
+ }
+ }
+ else if ATTR_NAME_CMP("irq_count"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.irq_count);
+ } else if ATTR_NAME_CMP("irq_base_num"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.irq_base_num);
+ } else if ATTR_NAME_CMP("core_num"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->core_num);
+ } else {
+ return 0;
+ }
+ #undef ATTR_NAME_CMP
+}
+
+
+DEVICE_ATTR(offset, 0444, show_attr, NULL);
+DEVICE_ATTR(size, 0444, show_attr, NULL);
+DEVICE_ATTR(type, 0444, show_attr, NULL);
+DEVICE_ATTR(s2c_dma_ch, 0444, show_attr, NULL);
+DEVICE_ATTR(c2s_dma_ch, 0444, show_attr, NULL);
+DEVICE_ATTR(s2c_dma, 0444, show_attr, NULL);
+DEVICE_ATTR(c2s_dma, 0444, show_attr, NULL);
+DEVICE_ATTR(irq_count, 0444, show_attr, NULL);
+DEVICE_ATTR(irq_base_num, 0444, show_attr, NULL);
+DEVICE_ATTR(core_num, 0444, show_attr, NULL);
+struct attribute * kpc_uio_class_attrs[] = {
+ &dev_attr_offset.attr,
+ &dev_attr_size.attr,
+ &dev_attr_type.attr,
+ &dev_attr_s2c_dma_ch.attr,
+ &dev_attr_c2s_dma_ch.attr,
+ &dev_attr_s2c_dma.attr,
+ &dev_attr_c2s_dma.attr,
+ &dev_attr_irq_count.attr,
+ &dev_attr_irq_base_num.attr,
+ &dev_attr_core_num.attr,
+ NULL,
+};
+
+
+static
+int kp2000_check_uio_irq(struct kp2000_device *pcard, u32 irq_num)
+{
+ u64 interrupt_active = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE);
+ u64 interrupt_mask_inv = ~readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ u64 irq_check_mask = (1 << irq_num);
+ if (interrupt_active & irq_check_mask){ // if it's active (interrupt pending)
+ if (interrupt_mask_inv & irq_check_mask){ // and if it's not masked off
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static
+irqreturn_t kuio_handler(int irq, struct uio_info *uioinfo)
+{
+ struct kpc_uio_device *kudev = uioinfo->priv;
+ if (irq != kudev->pcard->pdev->irq)
+ return IRQ_NONE;
+
+ if (kp2000_check_uio_irq(kudev->pcard, kudev->cte.irq_base_num)){
+ writeq((1 << kudev->cte.irq_base_num), kudev->pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); // Clear the active flag
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static
+int kuio_irqcontrol(struct uio_info *uioinfo, s32 irq_on)
+{
+ struct kpc_uio_device *kudev = uioinfo->priv;
+ struct kp2000_device *pcard = kudev->pcard;
+ u64 mask;
+
+ lock_card(pcard);
+ mask = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ if (irq_on){
+ mask &= ~(1 << (kudev->cte.irq_base_num));
+ } else {
+ mask |= (1 << (kudev->cte.irq_base_num));
+ }
+ writeq(mask, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ unlock_card(pcard);
+
+ return 0;
+}
+
+int probe_core_uio(unsigned int core_num, struct kp2000_device *pcard, char *name, const struct core_table_entry cte)
+{
+ struct kpc_uio_device *kudev;
+ int rv;
+
+ dev_dbg(&pcard->pdev->dev, "Found UIO core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8);
+
+ kudev = kzalloc(sizeof(struct kpc_uio_device), GFP_KERNEL);
+ if (!kudev){
+ dev_err(&pcard->pdev->dev, "probe_core_uio: failed to kzalloc kpc_uio_device\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&kudev->list);
+ kudev->pcard = pcard;
+ kudev->cte = cte;
+ kudev->core_num = core_num;
+
+ kudev->uioinfo.priv = kudev;
+ kudev->uioinfo.name = name;
+ kudev->uioinfo.version = "0.0";
+ if (cte.irq_count > 0){
+ kudev->uioinfo.irq_flags = IRQF_SHARED;
+ kudev->uioinfo.irq = pcard->pdev->irq;
+ kudev->uioinfo.handler = kuio_handler;
+ kudev->uioinfo.irqcontrol = kuio_irqcontrol;
+ } else {
+ kudev->uioinfo.irq = 0;
+ }
+
+ kudev->uioinfo.mem[0].name = "uiomap";
+ kudev->uioinfo.mem[0].addr = pci_resource_start(pcard->pdev, REG_BAR) + cte.offset;
+ kudev->uioinfo.mem[0].size = (cte.length + PAGE_SIZE-1) & ~(PAGE_SIZE-1); // Round up to nearest PAGE_SIZE boundary
+ kudev->uioinfo.mem[0].memtype = UIO_MEM_PHYS;
+
+ kudev->dev = device_create(kpc_uio_class, &pcard->pdev->dev, MKDEV(0,0), kudev, "%s.%d.%d.%d", kudev->uioinfo.name, pcard->card_num, cte.type, kudev->core_num);
+ if (IS_ERR(kudev->dev)) {
+ dev_err(&pcard->pdev->dev, "probe_core_uio device_create failed!\n");
+ return -ENODEV;
+ }
+ dev_set_drvdata(kudev->dev, kudev);
+
+ rv = uio_register_device(kudev->dev, &kudev->uioinfo);
+ if (rv){
+ dev_err(&pcard->pdev->dev, "probe_core_uio failed uio_register_device: %d\n", rv);
+ return rv;
+ }
+
+ list_add_tail(&kudev->list, &pcard->uio_devices_list);
+
+ return 0;
+}
+
+
+static int create_dma_engine_core(struct kp2000_device *pcard, size_t engine_regs_offset, int engine_num, int irq_num)
+{
+ struct mfd_cell cell = {0};
+ struct resource resources[2] = {0};
+
+ dev_dbg(&pcard->pdev->dev, "create_dma_core(pcard = [%p], engine_regs_offset = %zx, engine_num = %d)\n", pcard, engine_regs_offset, engine_num);
+
+ cell.platform_data = NULL;
+ cell.pdata_size = 0;
+ cell.id = engine_num;
+ cell.name = KP_DRIVER_NAME_DMA_CONTROLLER;
+ cell.num_resources = 2;
+
+ resources[0].start = engine_regs_offset;
+ resources[0].end = engine_regs_offset + (KPC_DMA_ENGINE_SIZE - 1);
+ resources[0].flags = IORESOURCE_MEM;
+
+ resources[1].start = irq_num;
+ resources[1].end = irq_num;
+ resources[1].flags = IORESOURCE_IRQ;
+
+ cell.resources = resources;
+
+ return mfd_add_devices(
+ PCARD_TO_DEV(pcard), // parent
+ pcard->card_num * 100, // id
+ &cell, // struct mfd_cell *
+ 1, // ndevs
+ &pcard->dma_base_resource,
+ 0, // irq_base
+ NULL // struct irq_domain *
+ );
+}
+
+static int kp2000_setup_dma_controller(struct kp2000_device *pcard)
+{
+ int err;
+ unsigned int i;
+ u64 capabilities_reg;
+
+ // S2C Engines
+ for (i = 0 ; i < 32 ; i++){
+ capabilities_reg = readq( pcard->dma_bar_base + KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i) );
+ if (capabilities_reg & ENGINE_CAP_PRESENT_MASK){
+ err = create_dma_engine_core(pcard, (KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), i, pcard->pdev->irq);
+ if (err) goto err_out;
+ }
+ }
+ // C2S Engines
+ for (i = 0 ; i < 32 ; i++){
+ capabilities_reg = readq( pcard->dma_bar_base + KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i) );
+ if (capabilities_reg & ENGINE_CAP_PRESENT_MASK){
+ err = create_dma_engine_core(pcard, (KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), 32+i, pcard->pdev->irq);
+ if (err) goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out:
+ dev_err(&pcard->pdev->dev, "kp2000_setup_dma_controller: failed to add a DMA Engine: %d\n", err);
+ return err;
+}
+
+int kp2000_probe_cores(struct kp2000_device *pcard)
+{
+ int err = 0;
+ int i;
+ int current_type_id;
+ u64 read_val;
+ unsigned int highest_core_id = 0;
+ struct core_table_entry cte;
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_probe_cores(pcard = %p / %d)\n", pcard, pcard->card_num);
+
+ err = kp2000_setup_dma_controller(pcard);
+ if (err) return err;
+
+ INIT_LIST_HEAD(&pcard->uio_devices_list);
+
+ // First, iterate the core table looking for the highest CORE_ID
+ for (i = 0 ; i < pcard->core_table_length ; i++){
+ read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8));
+ parse_core_table_entry(&cte, read_val, pcard->core_table_rev);
+ dbg_cte(pcard, &cte);
+ if (cte.type > highest_core_id){
+ highest_core_id = cte.type;
+ }
+ if (cte.type == KP_CORE_ID_INVALID){
+ dev_info(&pcard->pdev->dev, "Found Invalid core: %016llx\n", read_val);
+ }
+ }
+ // Then, iterate over the possible core types.
+ for (current_type_id = 1 ; current_type_id <= highest_core_id ; current_type_id++){
+ unsigned int core_num = 0;
+ // Foreach core type, iterate the whole table and instantiate subdevices for each core.
+ // Yes, this is O(n*m) but the actual runtime is small enough that it's an acceptable tradeoff.
+ for (i = 0 ; i < pcard->core_table_length ; i++){
+ read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8));
+ parse_core_table_entry(&cte, read_val, pcard->core_table_rev);
+
+ if (cte.type == current_type_id){
+ switch (cte.type){
+ case KP_CORE_ID_I2C:
+ err = probe_core_basic(core_num, pcard, KP_DRIVER_NAME_I2C, cte);
+ break;
+
+ case KP_CORE_ID_SPI:
+ err = probe_core_basic(core_num, pcard, KP_DRIVER_NAME_SPI, cte);
+ break;
+
+ default:
+ err = probe_core_uio(core_num, pcard, "kpc_uio", cte);
+ break;
+ }
+ if (err){
+ dev_err(&pcard->pdev->dev, "kp2000_probe_cores: failed to add core %d: %d\n", i, err);
+ return err;
+ }
+ core_num++;
+ }
+ }
+ }
+
+ // Finally, instantiate a UIO device for the core_table.
+ cte.type = 0; // CORE_ID_BOARD_INFO
+ cte.offset = 0; // board info is always at the beginning
+ cte.length = 512*8;
+ cte.s2c_dma_present = false;
+ cte.s2c_dma_channel_num = 0;
+ cte.c2s_dma_present = false;
+ cte.c2s_dma_channel_num = 0;
+ cte.irq_count = 0;
+ cte.irq_base_num = 0;
+ err = probe_core_uio(0, pcard, "kpc_uio", cte);
+ if (err){
+ dev_err(&pcard->pdev->dev, "kp2000_probe_cores: failed to add board_info core: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+void kp2000_remove_cores(struct kp2000_device *pcard)
+{
+ struct list_head *ptr;
+ struct list_head *next;
+ list_for_each_safe(ptr, next, &pcard->uio_devices_list){
+ struct kpc_uio_device *kudev = list_entry(ptr, struct kpc_uio_device, list);
+ uio_unregister_device(&kudev->uioinfo);
+ device_unregister(kudev->dev);
+ list_del(&kudev->list);
+ kfree(kudev);
+ }
+}
+
diff --git a/drivers/staging/kpc2000/kpc2000/core.c b/drivers/staging/kpc2000/kpc2000/core.c
new file mode 100644
index 000000000000..35b87cdc4fb0
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/core.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include "pcie.h"
+
+
+/*******************************************************
+ * SysFS Attributes
+ ******************************************************/
+static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct kp2000_device *pcard;
+
+ if (!pdev) return -ENXIO;
+ pcard = pci_get_drvdata(pdev);
+ if (!pcard) return -ENXIO;
+
+ if (strcmp("ssid", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%016llx\n", pcard->ssid); } else
+ if (strcmp("ddna", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%016llx\n", pcard->ddna); } else
+ if (strcmp("card_id", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->card_id); } else
+ if (strcmp("hw_rev", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->hardware_revision); } else
+ if (strcmp("build", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_version); } else
+ if (strcmp("build_date", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_datestamp); } else
+ if (strcmp("build_time", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_timestamp); } else
+ { return -ENXIO; }
+}
+
+static ssize_t show_cpld_config_reg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct kp2000_device *pcard;
+ u64 val;
+
+ if (!pdev)
+ return -ENXIO;
+
+ pcard = pci_get_drvdata(pdev);
+ if (!pcard)
+ return -ENXIO;
+
+ val = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+ return scnprintf(buf, PAGE_SIZE, "%016llx\n", val);
+}
+static ssize_t cpld_reconfigure(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ long wr_val;
+ struct kp2000_device *pcard;
+ int rv;
+
+ if (!pdev) return -ENXIO;
+ pcard = pci_get_drvdata(pdev);
+ if (!pcard) return -ENXIO;
+
+ rv = kstrtol(buf, 0, &wr_val);
+ if (rv < 0) return rv;
+ if (wr_val > 7) return -EINVAL;
+
+ wr_val = wr_val << 8;
+ wr_val |= 0x1; // Set the "Configure Go" bit
+ writeq(wr_val, pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+ return count;
+}
+
+
+DEVICE_ATTR(ssid, 0444, show_attr, NULL);
+DEVICE_ATTR(ddna, 0444, show_attr, NULL);
+DEVICE_ATTR(card_id, 0444, show_attr, NULL);
+DEVICE_ATTR(hw_rev, 0444, show_attr, NULL);
+DEVICE_ATTR(build, 0444, show_attr, NULL);
+DEVICE_ATTR(build_date, 0444, show_attr, NULL);
+DEVICE_ATTR(build_time, 0444, show_attr, NULL);
+DEVICE_ATTR(cpld_reg, 0444, show_cpld_config_reg, NULL);
+DEVICE_ATTR(cpld_reconfigure, 0220, NULL, cpld_reconfigure);
+
+static const struct attribute * kp_attr_list[] = {
+ &dev_attr_ssid.attr,
+ &dev_attr_ddna.attr,
+ &dev_attr_card_id.attr,
+ &dev_attr_hw_rev.attr,
+ &dev_attr_build.attr,
+ &dev_attr_build_date.attr,
+ &dev_attr_build_time.attr,
+ &dev_attr_cpld_reg.attr,
+ &dev_attr_cpld_reconfigure.attr,
+ NULL,
+};
+
+
+/*******************************************************
+ * Functions
+ ******************************************************/
+
+static void wait_and_read_ssid(struct kp2000_device *pcard)
+{
+ u64 read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID);
+ unsigned long timeout;
+
+ if (read_val & 0x8000000000000000){
+ pcard->ssid = read_val;
+ return;
+ }
+
+ timeout = jiffies + (HZ * 2);
+ do {
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID);
+ if (read_val & 0x8000000000000000){
+ pcard->ssid = read_val;
+ return;
+ }
+ cpu_relax();
+ //schedule();
+ } while (time_before(jiffies, timeout));
+
+ dev_notice(&pcard->pdev->dev, "SSID didn't show up!\n");
+
+ #if 0
+ // Timed out waiting for the SSID to show up, just use the DDNA instead?
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA);
+ pcard->ssid = read_val;
+ #else
+ // Timed out waiting for the SSID to show up, stick all zeros in the value
+ pcard->ssid = 0;
+ #endif
+}
+
+static int read_system_regs(struct kp2000_device *pcard)
+{
+ u64 read_val;
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_MAGIC_NUMBER);
+ if (read_val != KP2000_MAGIC_VALUE){
+ dev_err(&pcard->pdev->dev, "Invalid magic! Got: 0x%016llx Want: 0x%016lx\n", read_val, KP2000_MAGIC_VALUE);
+ return -EILSEQ;
+ }
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_CARD_ID_AND_BUILD);
+ pcard->card_id = (read_val & 0xFFFFFFFF00000000) >> 32;
+ pcard->build_version = (read_val & 0x00000000FFFFFFFF) >> 0;
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_DATE_AND_TIME_STAMPS);
+ pcard->build_datestamp = (read_val & 0xFFFFFFFF00000000) >> 32;
+ pcard->build_timestamp = (read_val & 0x00000000FFFFFFFF) >> 0;
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_CORE_TABLE_OFFSET);
+ pcard->core_table_length = (read_val & 0xFFFFFFFF00000000) >> 32;
+ pcard->core_table_offset = (read_val & 0x00000000FFFFFFFF) >> 0;
+
+ wait_and_read_ssid(pcard);
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_HW_ID);
+ pcard->core_table_rev = (read_val & 0x0000000000000F00) >> 8;
+ pcard->hardware_revision = (read_val & 0x000000000000001F);
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA);
+ pcard->ddna = read_val;
+
+ dev_info(&pcard->pdev->dev, "system_regs: %08x %08x %08x %08x %02x %d %d %016llx %016llx\n",
+ pcard->card_id,
+ pcard->build_version,
+ pcard->build_datestamp,
+ pcard->build_timestamp,
+ pcard->hardware_revision,
+ pcard->core_table_rev,
+ pcard->core_table_length,
+ pcard->ssid,
+ pcard->ddna
+ );
+
+ if (pcard->core_table_rev > 1){
+ dev_err(&pcard->pdev->dev, "core table entry revision is higher than we can deal with, cannot continue with this card!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+irqreturn_t kp2000_irq_handler(int irq, void *dev_id)
+{
+ struct kp2000_device *pcard = (struct kp2000_device*)dev_id;
+ SetBackEndControl(pcard->dma_common_regs, KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE | KPC_DMA_CARD_USER_INTERRUPT_ACTIVE);
+ return IRQ_HANDLED;
+}
+
+int kp2000_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int err = 0;
+ struct kp2000_device *pcard;
+ static int card_count = 1;
+ int rv;
+ unsigned long reg_bar_phys_addr;
+ unsigned long reg_bar_phys_len;
+ unsigned long dma_bar_phys_addr;
+ unsigned long dma_bar_phys_len;
+ u16 regval;
+
+ dev_dbg(&pdev->dev, "kp2000_pcie_probe(pdev = [%p], id = [%p])\n", pdev, id);
+
+ //{ Step 1: Allocate a struct for the pcard
+ pcard = kzalloc(sizeof(struct kp2000_device), GFP_KERNEL);
+ if (NULL == pcard){
+ dev_err(&pdev->dev, "probe: failed to allocate private card data\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&pdev->dev, "probe: allocated struct kp2000_device @ %p\n", pcard);
+ //}
+
+ //{ Step 2: Initialize trivial pcard elements
+ pcard->card_num = card_count;
+ card_count++;
+ scnprintf(pcard->name, 16, "kpcard%d", pcard->card_num);
+
+ mutex_init(&pcard->sem);
+ lock_card(pcard);
+
+ pcard->pdev = pdev;
+ pci_set_drvdata(pdev, pcard);
+ //}
+
+ //{ Step 3: Enable PCI device
+ err = pci_enable_device(pcard->pdev);
+ if (err){
+ dev_err(&pcard->pdev->dev, "probe: failed to enable PCIE2000 PCIe device (%d)\n", err);
+ goto out3;
+ }
+ //}
+
+ //{ Step 4: Setup the Register BAR
+ reg_bar_phys_addr = pci_resource_start(pcard->pdev, REG_BAR);
+ reg_bar_phys_len = pci_resource_len(pcard->pdev, REG_BAR);
+
+ pcard->regs_bar_base = ioremap_nocache(reg_bar_phys_addr, PAGE_SIZE);
+ if (NULL == pcard->regs_bar_base){
+ dev_err(&pcard->pdev->dev, "probe: REG_BAR could not remap memory to virtual space\n");
+ err = -ENODEV;
+ goto out4;
+ }
+ dev_dbg(&pcard->pdev->dev, "probe: REG_BAR virt hardware address start [%p]\n", pcard->regs_bar_base);
+
+ err = pci_request_region(pcard->pdev, REG_BAR, KP_DRIVER_NAME_KP2000);
+ if (err){
+ iounmap(pcard->regs_bar_base);
+ dev_err(&pcard->pdev->dev, "probe: failed to acquire PCI region (%d)\n", err);
+ err = -ENODEV;
+ goto out4;
+ }
+
+ pcard->regs_base_resource.start = reg_bar_phys_addr;
+ pcard->regs_base_resource.end = reg_bar_phys_addr + reg_bar_phys_len - 1;
+ pcard->regs_base_resource.flags = IORESOURCE_MEM;
+ //}
+
+ //{ Step 5: Setup the DMA BAR
+ dma_bar_phys_addr = pci_resource_start(pcard->pdev, DMA_BAR);
+ dma_bar_phys_len = pci_resource_len(pcard->pdev, DMA_BAR);
+
+ pcard->dma_bar_base = ioremap_nocache(dma_bar_phys_addr, dma_bar_phys_len);
+ if (NULL == pcard->dma_bar_base){
+ dev_err(&pcard->pdev->dev, "probe: DMA_BAR could not remap memory to virtual space\n");
+ err = -ENODEV;
+ goto out5;
+ }
+ dev_dbg(&pcard->pdev->dev, "probe: DMA_BAR virt hardware address start [%p]\n", pcard->dma_bar_base);
+
+ pcard->dma_common_regs = pcard->dma_bar_base + KPC_DMA_COMMON_OFFSET;
+
+ err = pci_request_region(pcard->pdev, DMA_BAR, "kp2000_pcie");
+ if (err){
+ iounmap(pcard->dma_bar_base);
+ dev_err(&pcard->pdev->dev, "probe: failed to acquire PCI region (%d)\n", err);
+ err = -ENODEV;
+ goto out5;
+ }
+
+ pcard->dma_base_resource.start = dma_bar_phys_addr;
+ pcard->dma_base_resource.end = dma_bar_phys_addr + dma_bar_phys_len - 1;
+ pcard->dma_base_resource.flags = IORESOURCE_MEM;
+ //}
+
+ //{ Step 6: System Regs
+ pcard->sysinfo_regs_base = pcard->regs_bar_base;
+ err = read_system_regs(pcard);
+ if (err)
+ goto out6;
+
+ // Disable all "user" interrupts because they're not used yet.
+ writeq(0xFFFFFFFFFFFFFFFF, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ //}
+
+ //{ Step 7: Configure PCI thingies
+ // let the card master PCIe
+ pci_set_master(pcard->pdev);
+ // enable IO and mem if not already done
+ pci_read_config_word(pcard->pdev, PCI_COMMAND, &regval);
+ regval |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pcard->pdev, PCI_COMMAND, regval);
+
+ // Clear relaxed ordering bit
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN, 0);
+
+ // Set Max_Payload_Size and Max_Read_Request_Size
+ regval = (0x0) << 5; // Max_Payload_Size = 128 B
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, regval);
+ regval = (0x0) << 12; // Max_Read_Request_Size = 128 B
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_READRQ, regval);
+
+ // Enable error reporting for: Correctable Errors, Non-Fatal Errors, Fatal Errors, Unsupported Requests
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, 0, PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+
+ err = dma_set_mask(PCARD_TO_DEV(pcard), DMA_BIT_MASK(64));
+ if (err){
+ dev_err(&pcard->pdev->dev, "CANNOT use DMA mask %0llx\n", DMA_BIT_MASK(64));
+ goto out7;
+ }
+ dev_dbg(&pcard->pdev->dev, "Using DMA mask %0llx\n", dma_get_mask(PCARD_TO_DEV(pcard)));
+ //}
+
+ //{ Step 8: Configure IRQs
+ err = pci_enable_msi(pcard->pdev);
+ if (err < 0)
+ goto out8a;
+
+ rv = request_irq(pcard->pdev->irq, kp2000_irq_handler, IRQF_SHARED, pcard->name, pcard);
+ if (rv){
+ dev_err(&pcard->pdev->dev, "kp2000_pcie_probe: failed to request_irq: %d\n", rv);
+ goto out8b;
+ }
+ //}
+
+ //{ Step 9: Setup sysfs attributes
+ err = sysfs_create_files(&(pdev->dev.kobj), kp_attr_list);
+ if (err){
+ dev_err(&pdev->dev, "Failed to add sysfs files: %d\n", err);
+ goto out9;
+ }
+ //}
+
+ //{ Step 10: Setup misc device
+ pcard->miscdev.minor = MISC_DYNAMIC_MINOR;
+ pcard->miscdev.fops = &kp2000_fops;
+ pcard->miscdev.parent = &pcard->pdev->dev;
+ pcard->miscdev.name = pcard->name;
+
+ err = misc_register(&pcard->miscdev);
+ if (err){
+ dev_err(&pcard->pdev->dev, "kp2000_pcie_probe: misc_register failed: %d\n", err);
+ goto out10;
+ }
+ //}
+
+ //{ Step 11: Probe cores
+ err = kp2000_probe_cores(pcard);
+ if (err)
+ goto out11;
+ //}
+
+ //{ Step 12: Enable IRQs in HW
+ SetBackEndControl(pcard->dma_common_regs, KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE);
+ //}
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_pcie_probe() complete!\n");
+ unlock_card(pcard);
+ return 0;
+
+ out11:
+ misc_deregister(&pcard->miscdev);
+ out10:
+ sysfs_remove_files(&(pdev->dev.kobj), kp_attr_list);
+ out9:
+ free_irq(pcard->pdev->irq, pcard);
+ out8b:
+ pci_disable_msi(pcard->pdev);
+ out8a:
+ out7:
+ out6:
+ iounmap(pcard->dma_bar_base);
+ pci_release_region(pdev, DMA_BAR);
+ pcard->dma_bar_base = NULL;
+ out5:
+ iounmap(pcard->regs_bar_base);
+ pci_release_region(pdev, REG_BAR);
+ pcard->regs_bar_base = NULL;
+ out4:
+ pci_disable_device(pcard->pdev);
+ out3:
+ unlock_card(pcard);
+ kfree(pcard);
+ return err;
+}
+
+
+void kp2000_pcie_remove(struct pci_dev *pdev)
+{
+ struct kp2000_device *pcard = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "kp2000_pcie_remove(pdev=%p)\n", pdev);
+
+ if (pcard == NULL) return;
+
+ lock_card(pcard);
+ kp2000_remove_cores(pcard);
+ mfd_remove_devices(PCARD_TO_DEV(pcard));
+ misc_deregister(&pcard->miscdev);
+ sysfs_remove_files(&(pdev->dev.kobj), kp_attr_list);
+ free_irq(pcard->pdev->irq, pcard);
+ pci_disable_msi(pcard->pdev);
+ if (pcard->dma_bar_base != NULL){
+ iounmap(pcard->dma_bar_base);
+ pci_release_region(pdev, DMA_BAR);
+ pcard->dma_bar_base = NULL;
+ }
+ if (pcard->regs_bar_base != NULL){
+ iounmap(pcard->regs_bar_base);
+ pci_release_region(pdev, REG_BAR);
+ pcard->regs_bar_base = NULL;
+ }
+ pci_disable_device(pcard->pdev);
+ pci_set_drvdata(pdev, NULL);
+ unlock_card(pcard);
+ kfree(pcard);
+}
diff --git a/drivers/staging/kpc2000/kpc2000/dma_common_defs.h b/drivers/staging/kpc2000/kpc2000/dma_common_defs.h
new file mode 100644
index 000000000000..f35e636b1fb7
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/dma_common_defs.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KPC_DMA_COMMON_DEFS_H_
+#define KPC_DMA_COMMON_DEFS_H_
+
+#define KPC_DMA_COMMON_OFFSET 0x4000
+#define KPC_DMA_S2C_BASE_OFFSET 0x0000
+#define KPC_DMA_C2S_BASE_OFFSET 0x2000
+#define KPC_DMA_ENGINE_SIZE 0x0100
+#define ENGINE_CAP_PRESENT_MASK 0x1
+
+
+#define KPC_DMA_CARD_IRQ_ENABLE (1 << 0)
+#define KPC_DMA_CARD_IRQ_ACTIVE (1 << 1)
+#define KPC_DMA_CARD_IRQ_PENDING (1 << 2)
+#define KPC_DMA_CARD_IRQ_MSI (1 << 3)
+#define KPC_DMA_CARD_USER_INTERRUPT_MODE (1 << 4)
+#define KPC_DMA_CARD_USER_INTERRUPT_ACTIVE (1 << 5)
+#define KPC_DMA_CARD_IRQ_MSIX_MODE (1 << 6)
+#define KPC_DMA_CARD_MAX_PAYLOAD_SIZE_MASK 0x0700
+#define KPC_DMA_CARD_MAX_READ_REQUEST_SIZE_MASK 0x7000
+#define KPC_DMA_CARD_S2C_INTERRUPT_STATUS_MASK 0x00FF0000
+#define KPC_DMA_CARD_C2S_INTERRUPT_STATUS_MASK 0xFF000000
+
+static inline void SetBackEndControl(void __iomem *regs, u32 value)
+{
+ writel(value, regs + 0);
+}
+static inline u32 GetBackEndStatus(void __iomem *regs)
+{
+ return readl(regs + 0);
+}
+
+static inline u32 BackEndControlSetClear(void __iomem *regs, u32 set_bits, u32 clear_bits)
+{
+ u32 start_val = GetBackEndStatus(regs);
+ u32 new_val = start_val;
+ new_val &= ~clear_bits;
+ new_val |= set_bits;
+ SetBackEndControl(regs, new_val);
+ return start_val;
+}
+
+#endif /* KPC_DMA_COMMON_DEFS_H_ */
diff --git a/drivers/staging/kpc2000/kpc2000/fileops.c b/drivers/staging/kpc2000/kpc2000/fileops.c
new file mode 100644
index 000000000000..4bfba59570e6
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/fileops.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/cdev.h>
+#include <linux/uaccess.h> /* copy_*_user */
+#include <linux/rwsem.h>
+#include <linux/idr.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include "pcie.h"
+#include "uapi.h"
+
+int kp2000_cdev_open(struct inode *inode, struct file *filp)
+{
+ struct kp2000_device *pcard = container_of(filp->private_data, struct kp2000_device, miscdev);
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_cdev_open(filp = [%p], pcard = [%p])\n", filp, pcard);
+
+ filp->private_data = pcard; /* so other methods can access it */
+
+ return 0;
+}
+
+int kp2000_cdev_close(struct inode *inode, struct file *filp)
+{
+ struct kp2000_device *pcard = filp->private_data;
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_cdev_close(filp = [%p], pcard = [%p])\n", filp, pcard);
+ return 0;
+}
+
+
+ssize_t kp2000_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+ struct kp2000_device *pcard = filp->private_data;
+ int cnt = 0;
+ int ret;
+#define BUFF_CNT 1024
+ char buff[BUFF_CNT] = {0}; //NOTE: Increase this so it is at least as large as all the scnprintfs. And don't use unbounded strings. "%s"
+ //NOTE: also, this is a really shitty way to implement the read() call, but it will work for any size 'count'.
+
+ if (WARN(NULL == buf, "kp2000_cdev_read: buf is a NULL pointer!\n"))
+ return -EINVAL;
+
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Card ID : 0x%08x\n", pcard->card_id);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Version : 0x%08x\n", pcard->build_version);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Date : 0x%08x\n", pcard->build_datestamp);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Time : 0x%08x\n", pcard->build_timestamp);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Core Table Offset : 0x%08x\n", pcard->core_table_offset);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Core Table Length : 0x%08x\n", pcard->core_table_length);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Hardware Revision : 0x%08x\n", pcard->hardware_revision);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "SSID : 0x%016llx\n", pcard->ssid);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "DDNA : 0x%016llx\n", pcard->ddna);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "IRQ Mask : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK));
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "IRQ Active : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE));
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "CPLD : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG));
+
+ if (*f_pos >= cnt)
+ return 0;
+
+ if (count > cnt)
+ count = cnt;
+
+ ret = copy_to_user(buf, buff + *f_pos, count);
+ if (ret)
+ return -EFAULT;
+ *f_pos += count;
+ return count;
+}
+
+ssize_t kp2000_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+ return -EINVAL;
+}
+
+long kp2000_cdev_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param)
+{
+ struct kp2000_device *pcard = filp->private_data;
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_cdev_ioctl(filp = [%p], ioctl_num = 0x%08x, ioctl_param = 0x%016lx) pcard = [%p]\n", filp, ioctl_num, ioctl_param, pcard);
+
+ switch (ioctl_num){
+ case KP2000_IOCTL_GET_CPLD_REG: return readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+ case KP2000_IOCTL_GET_PCIE_ERROR_REG: return readq(pcard->sysinfo_regs_base + REG_PCIE_ERROR_COUNT);
+
+ case KP2000_IOCTL_GET_EVERYTHING: {
+ struct kp2000_regs temp;
+ int ret;
+ temp.card_id = pcard->card_id;
+ temp.build_version = pcard->build_version;
+ temp.build_datestamp = pcard->build_datestamp;
+ temp.build_timestamp = pcard->build_timestamp;
+ temp.hw_rev = pcard->hardware_revision;
+ temp.ssid = pcard->ssid;
+ temp.ddna = pcard->ddna;
+ temp.cpld_reg = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+
+ ret = copy_to_user((void*)ioctl_param, (void*)&temp, sizeof(temp));
+ if (ret)
+ return -EFAULT;
+
+ return sizeof(temp);
+ }
+
+ default:
+ return -ENOTTY;
+ }
+ return -ENOTTY;
+}
+
+
+struct file_operations kp2000_fops = {
+ .owner = THIS_MODULE,
+ .open = kp2000_cdev_open,
+ .release = kp2000_cdev_close,
+ .read = kp2000_cdev_read,
+ //.write = kp2000_cdev_write,
+ //.poll = kp2000_cdev_poll,
+ //.fasync = kp2000_cdev_fasync,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = kp2000_cdev_ioctl,
+};
+
diff --git a/drivers/staging/kpc2000/kpc2000/kp2000_module.c b/drivers/staging/kpc2000/kpc2000/kp2000_module.c
new file mode 100644
index 000000000000..661b0b74ed66
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/kp2000_module.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include "pcie.h"
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lee.Brooke@Daktronics.com, Matt.Sickler@Daktronics.com");
+MODULE_SOFTDEP("pre: uio post: kpc_nwl_dma kpc_i2c kpc_spi");
+
+struct class *kpc_uio_class;
+ATTRIBUTE_GROUPS(kpc_uio_class);
+
+static const struct pci_device_id kp2000_pci_device_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, kp2000_pci_device_ids);
+
+static struct pci_driver kp2000_driver_inst = {
+ .name = "kp2000_pcie",
+ .id_table = kp2000_pci_device_ids,
+ .probe = kp2000_pcie_probe,
+ .remove = kp2000_pcie_remove
+};
+
+
+static int __init kp2000_pcie_init(void)
+{
+ kpc_uio_class = class_create(THIS_MODULE, "kpc_uio");
+ if (IS_ERR(kpc_uio_class))
+ return PTR_ERR(kpc_uio_class);
+
+ kpc_uio_class->dev_groups = kpc_uio_class_groups;
+ return pci_register_driver(&kp2000_driver_inst);
+}
+module_init(kp2000_pcie_init);
+
+static void __exit kp2000_pcie_exit(void)
+{
+ pci_unregister_driver(&kp2000_driver_inst);
+ class_destroy(kpc_uio_class);
+}
+module_exit(kp2000_pcie_exit);
diff --git a/drivers/staging/kpc2000/kpc2000/pcie.h b/drivers/staging/kpc2000/kpc2000/pcie.h
new file mode 100644
index 000000000000..893aebfd1449
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/pcie.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KP2000_PCIE_H
+#define KP2000_PCIE_H
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include "../kpc.h"
+#include "dma_common_defs.h"
+
+
+/* System Register Map (BAR 1, Start Addr 0)
+ *
+ * BAR Size:
+ * 1048576 (0x100000) bytes = 131072 (0x20000) registers = 256 pages (4K)
+ *
+ * 6 5 4 3 2 1 0
+ * 3210987654321098765432109876543210987654321098765432109876543210
+ * 0 <--------------------------- MAGIC ---------------------------->
+ * 1 <----------- Card ID ---------><----------- Revision ---------->
+ * 2 <--------- Date Stamp --------><--------- Time Stamp ---------->
+ * 3 <-------- Core Tbl Len -------><-------- Core Tbl Offset ------>
+ * 4 <---------------------------- SSID ---------------------------->
+ * 5 < HWID >
+ * 6 <------------------------- FPGA DDNA -------------------------->
+ * 7 <------------------------ CPLD Config ------------------------->
+ * 8 <----------------------- IRQ Mask Flags ----------------------->
+ * 9 <---------------------- IRQ Active Flags ---------------------->
+ */
+
+#define REG_WIDTH 8
+#define REG_MAGIC_NUMBER (0 * REG_WIDTH)
+#define REG_CARD_ID_AND_BUILD (1 * REG_WIDTH)
+#define REG_DATE_AND_TIME_STAMPS (2 * REG_WIDTH)
+#define REG_CORE_TABLE_OFFSET (3 * REG_WIDTH)
+#define REG_FPGA_SSID (4 * REG_WIDTH)
+#define REG_FPGA_HW_ID (5 * REG_WIDTH)
+#define REG_FPGA_DDNA (6 * REG_WIDTH)
+#define REG_CPLD_CONFIG (7 * REG_WIDTH)
+#define REG_INTERRUPT_MASK (8 * REG_WIDTH)
+#define REG_INTERRUPT_ACTIVE (9 * REG_WIDTH)
+#define REG_PCIE_ERROR_COUNT (10 * REG_WIDTH)
+
+#define KP2000_MAGIC_VALUE 0x196C61482231894D
+
+#define PCI_VENDOR_ID_DAKTRONICS 0x1c33
+#define PCI_DEVICE_ID_DAKTRONICS 0x6021
+
+#define DMA_BAR 0
+#define REG_BAR 1
+
+struct kp2000_device {
+ struct pci_dev *pdev;
+ struct miscdevice miscdev;
+ char name[16];
+
+ unsigned int card_num;
+ struct mutex sem;
+
+ void __iomem *sysinfo_regs_base;
+ void __iomem *regs_bar_base;
+ struct resource regs_base_resource;
+ void __iomem *dma_bar_base;
+ void __iomem *dma_common_regs;
+ struct resource dma_base_resource;
+
+ // "System Registers"
+ u32 card_id;
+ u32 build_version;
+ u32 build_datestamp;
+ u32 build_timestamp;
+ u32 core_table_offset;
+ u32 core_table_length;
+ u8 core_table_rev;
+ u8 hardware_revision;
+ u64 ssid;
+ u64 ddna;
+
+ // IRQ stuff
+ unsigned int irq;
+
+ struct list_head uio_devices_list;
+};
+
+extern struct class *kpc_uio_class;
+extern struct attribute *kpc_uio_class_attrs[];
+
+int kp2000_pcie_probe(struct pci_dev *dev, const struct pci_device_id *id);
+void kp2000_pcie_remove(struct pci_dev *pdev);
+int kp2000_probe_cores(struct kp2000_device *pcard);
+void kp2000_remove_cores(struct kp2000_device *pcard);
+
+extern struct file_operations kp2000_fops;
+
+
+// Define this quick little macro because the expression is used frequently
+#define PCARD_TO_DEV(pcard) (&(pcard->pdev->dev))
+
+static inline void
+lock_card(struct kp2000_device *pcard)
+{
+ BUG_ON(pcard == NULL);
+ mutex_lock(&pcard->sem);
+}
+static inline void
+unlock_card(struct kp2000_device *pcard)
+{
+ BUG_ON(pcard == NULL);
+ mutex_unlock(&pcard->sem);
+}
+
+
+#endif /* KP2000_PCIE_H */
diff --git a/drivers/staging/kpc2000/kpc2000/uapi.h b/drivers/staging/kpc2000/kpc2000/uapi.h
new file mode 100644
index 000000000000..ef8008bcd33d
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/uapi.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KP2000_CDEV_UAPI_H_
+#define KP2000_CDEV_UAPI_H_
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+struct kp2000_regs {
+ __u32 card_id;
+ __u32 build_version;
+ __u32 build_datestamp;
+ __u32 build_timestamp;
+ __u32 hw_rev;
+ __u64 ssid;
+ __u64 ddna;
+ __u64 cpld_reg;
+};
+
+#define KP2000_IOCTL_GET_CPLD_REG _IOR('k', 9, __u32)
+#define KP2000_IOCTL_GET_PCIE_ERROR_REG _IOR('k', 11, __u32)
+#define KP2000_IOCTL_GET_EVERYTHING _IOR('k', 8, struct kp2000_regs*)
+
+#endif /* KP2000_CDEV_UAPI_H_ */
diff --git a/drivers/staging/kpc2000/kpc_i2c/Makefile b/drivers/staging/kpc2000/kpc_i2c/Makefile
new file mode 100644
index 000000000000..73ec07ac7d39
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc_i2c/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-m := kpc2000_i2c.o
+kpc2000_i2c-objs := i2c_driver.o fileops.o
diff --git a/drivers/staging/kpc2000/kpc_i2c/fileops.c b/drivers/staging/kpc2000/kpc_i2c/fileops.c
new file mode 100644
index 000000000000..e749c0994491
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc_i2c/fileops.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0+
+#if 0
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/cdev.h>
+#include <asm/uaccess.h> /* copy_*_user */
+
+#include "i2c_driver.h"
+
+int i2c_cdev_open(struct inode *inode, struct file *filp)
+{
+ struct i2c_device *lddev;
+
+ if(NULL == inode) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_open: inode is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_open: inode is a NULL pointer\n");
+ return -EINVAL;
+ }
+ if(NULL == filp) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_open: filp is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_open: filp is a NULL pointer\n");
+ return -EINVAL;
+ }
+
+ lddev = container_of(inode->i_cdev, struct i2c_device, cdev);
+ //printk(KERN_DEBUG "<pl_i2c> i2c_cdev_open(filp = [%p], lddev = [%p])\n", filp, lddev);
+ DBG_PRINT(KERN_DEBUG, "i2c_cdev_open(filp = [%p], lddev = [%p])\n", filp, lddev);
+
+ filp->private_data = lddev; /* so other methods can access it */
+
+ return 0; /* success */
+}
+
+int i2c_cdev_close(struct inode *inode, struct file *filp)
+{
+ struct i2c_device *lddev;
+
+ if(NULL == inode) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_close: inode is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_close: inode is a NULL pointer\n");
+ return -EINVAL;
+ }
+ if(NULL == filp) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_close: filp is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_close: filp is a NULL pointer\n");
+ return -EINVAL;
+ }
+
+ lddev = filp->private_data;
+ //printk(KERN_DEBUG "<pl_i2c> i2c_cdev_close(filp = [%p], lddev = [%p])\n", filp, lddev);
+ DBG_PRINT(KERN_DEBUG, "i2c_cdev_close(filp = [%p], lddev = [%p])\n", filp, lddev);
+
+ return 0;
+}
+
+ssize_t i2c_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+ size_t copy;
+ ssize_t ret = 0;
+ int err = 0;
+ u64 read_val;
+ char tmp_buf[48] = { 0 };
+ struct i2c_device *lddev = filp->private_data;
+
+ if(NULL == filp) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_read: filp is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_read: filp is a NULL pointer\n");
+ return -EINVAL;
+ }
+ if(NULL == buf) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_read: buf is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_read: buf is a NULL pointer\n");
+ return -EINVAL;
+ }
+ if(NULL == f_pos) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_read: f_pos is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_read: f_pos is a NULL pointer\n");
+ return -EINVAL;
+ }
+
+ if(count < sizeof(tmp_buf)) {
+ //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: buffer is too small (count = %d, should be at least %d bytes)\n", (int)count, (int)sizeof(tmp_buf));
+ DBG_PRINT(KERN_INFO, "i2c_cdev_read: buffer is too small (count = %d, should be at least %d bytes)\n", (int)count, (int)sizeof(tmp_buf));
+ return -EINVAL;
+ }
+ if(((*f_pos * 8) + lddev->pldev->resource[0].start) > lddev->pldev->resource[0].end) {
+ //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: bad read addr %016llx\n", (*f_pos * 8) + lddev->pldev->resource[0].start);
+ DBG_PRINT(KERN_INFO, "i2c_cdev_read: bad read addr %016llx\n", (*f_pos * 8) + lddev->pldev->resource[0].start);
+ //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: addr end %016llx\n", lddev->pldev->resource[0].end);
+ DBG_PRINT(KERN_INFO, "i2c_cdev_read: addr end %016llx\n", lddev->pldev->resource[0].end);
+ //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: EOF reached\n");
+ DBG_PRINT(KERN_INFO, "i2c_cdev_read: EOF reached\n");
+ return 0;
+ }
+
+ down_read(&lddev->rw_sem);
+
+ read_val = *(lddev->regs + *f_pos);
+ copy = clamp_t(size_t, count, 1, sizeof(tmp_buf));
+ copy = scnprintf(tmp_buf, copy, "reg: 0x%x val: 0x%llx\n", (unsigned int)*f_pos, read_val);
+ err = copy_to_user(buf, tmp_buf, copy);
+ if(err) {
+ //printk(KERN_INFO "<pl_i2c> i2c_cdev_read: could not copy to user (err = %d)\n", err);
+ DBG_PRINT(KERN_INFO, "i2c_cdev_read: could not copy to user (err = %d)\n", err);
+ return -EINVAL;
+ }
+
+ ret = (ssize_t)copy;
+ (*f_pos)++;
+
+ up_read(&lddev->rw_sem);
+
+ return ret;
+}
+
+ssize_t i2c_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+ u8 reg;
+ u8 val;
+ char tmp[8] = { 0 };
+ struct i2c_device *lddev = filp->private_data;
+
+ if(NULL == filp) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_write: filp is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_write: filp is a NULL pointer\n");
+ return -EINVAL;
+ }
+ if(NULL == buf) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_write: buf is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_write: buf is a NULL pointer\n");
+ return -EINVAL;
+ }
+ if(NULL == f_pos) {
+ //printk(KERN_WARNING "<pl_i2c> i2c_cdev_write: f_pos is a NULL pointer\n");
+ DBG_PRINT(KERN_WARNING, "i2c_cdev_write: f_pos is a NULL pointer\n");
+ return -EINVAL;
+ }
+
+ //printk(KERN_DEBUG "<pl_i2c> i2c_cdev_write(filp = [%p], lddev = [%p])\n", filp, lddev);
+ DBG_PRINT(KERN_DEBUG, "i2c_cdev_write(filp = [%p], lddev = [%p])\n", filp, lddev);
+
+ down_write(&lddev->rw_sem);
+
+ if(count >= 2) {
+ if(copy_from_user(tmp, buf, 2)) {
+ return -EFAULT;
+ }
+
+ reg = tmp[0] - '0';
+ val = tmp[1] - '0';
+
+ //printk(KERN_DEBUG " reg = %d val = %d\n", reg, val);
+ DBG_PRINT(KERN_DEBUG, " reg = %d val = %d\n", reg, val);
+
+ if(reg >= 0 && reg < 16) {
+ //printk(KERN_DEBUG " Writing 0x%x to %p\n", val, lddev->regs + reg);
+ DBG_PRINT(KERN_DEBUG, " Writing 0x%x to %p\n", val, lddev->regs + reg);
+ *(lddev->regs + reg) = val;
+ }
+ }
+
+ (*f_pos)++;
+
+ up_write(&lddev->rw_sem);
+
+ return count;
+}
+
+struct file_operations i2c_fops = {
+ .owner = THIS_MODULE,
+ .open = i2c_cdev_open,
+ .release = i2c_cdev_close,
+ .read = i2c_cdev_read,
+ .write = i2c_cdev_write,
+};
+#endif
diff --git a/drivers/staging/kpc2000/kpc_i2c/i2c_driver.c b/drivers/staging/kpc2000/kpc_i2c/i2c_driver.c
new file mode 100644
index 000000000000..6bb6ad4abe87
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc_i2c/i2c_driver.c
@@ -0,0 +1,698 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2014-2018 Daktronics,
+ Matt Sickler <matt.sickler@daktronics.com>,
+ Jordon Hofer <jordon.hofer@daktronics.com>
+ Adapted i2c-i801.c for use with Kadoka hardware.
+ Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
+ Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
+ <mdsxyz123@yahoo.com>
+ Copyright (C) 2007 - 2012 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2010 Intel Corporation,
+ David Woodhouse <dwmw2@infradead.org>
+*/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/rwsem.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include "../kpc.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt.Sickler@Daktronics.com");
+MODULE_SOFTDEP("pre: i2c-dev");
+
+struct i2c_device {
+ unsigned long smba;
+ struct i2c_adapter adapter;
+ struct platform_device *pldev;
+ struct rw_semaphore rw_sem;
+ unsigned int features;
+};
+
+/*****************************
+ *** Part 1 - i2c Handlers ***
+ *****************************/
+
+#define REG_SIZE 8
+
+/* I801 SMBus address offsets */
+#define SMBHSTSTS(p) ((0 * REG_SIZE) + (p)->smba)
+#define SMBHSTCNT(p) ((2 * REG_SIZE) + (p)->smba)
+#define SMBHSTCMD(p) ((3 * REG_SIZE) + (p)->smba)
+#define SMBHSTADD(p) ((4 * REG_SIZE) + (p)->smba)
+#define SMBHSTDAT0(p) ((5 * REG_SIZE) + (p)->smba)
+#define SMBHSTDAT1(p) ((6 * REG_SIZE) + (p)->smba)
+#define SMBBLKDAT(p) ((7 * REG_SIZE) + (p)->smba)
+#define SMBPEC(p) ((8 * REG_SIZE) + (p)->smba) /* ICH3 and later */
+#define SMBAUXSTS(p) ((12 * REG_SIZE) + (p)->smba) /* ICH4 and later */
+#define SMBAUXCTL(p) ((13 * REG_SIZE) + (p)->smba) /* ICH4 and later */
+
+/* PCI Address Constants */
+#define SMBBAR 4
+#define SMBHSTCFG 0x040
+
+/* Host configuration bits for SMBHSTCFG */
+#define SMBHSTCFG_HST_EN 1
+#define SMBHSTCFG_SMB_SMI_EN 2
+#define SMBHSTCFG_I2C_EN 4
+
+/* Auxiliary control register bits, ICH4+ only */
+#define SMBAUXCTL_CRC 1
+#define SMBAUXCTL_E32B 2
+
+/* kill bit for SMBHSTCNT */
+#define SMBHSTCNT_KILL 2
+
+/* Other settings */
+#define MAX_RETRIES 400
+#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
+
+/* I801 command constants */
+#define I801_QUICK 0x00
+#define I801_BYTE 0x04
+#define I801_BYTE_DATA 0x08
+#define I801_WORD_DATA 0x0C
+#define I801_PROC_CALL 0x10 /* unimplemented */
+#define I801_BLOCK_DATA 0x14
+#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
+#define I801_BLOCK_LAST 0x34
+#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */
+#define I801_START 0x40
+#define I801_PEC_EN 0x80 /* ICH3 and later */
+
+/* I801 Hosts Status register bits */
+#define SMBHSTSTS_BYTE_DONE 0x80
+#define SMBHSTSTS_INUSE_STS 0x40
+#define SMBHSTSTS_SMBALERT_STS 0x20
+#define SMBHSTSTS_FAILED 0x10
+#define SMBHSTSTS_BUS_ERR 0x08
+#define SMBHSTSTS_DEV_ERR 0x04
+#define SMBHSTSTS_INTR 0x02
+#define SMBHSTSTS_HOST_BUSY 0x01
+
+#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | SMBHSTSTS_INTR)
+
+/* Older devices have their ID defined in <linux/pci_ids.h> */
+#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
+#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
+/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
+#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70
+#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71
+#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72
+#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
+#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
+#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
+
+
+#define FEATURE_SMBUS_PEC (1 << 0)
+#define FEATURE_BLOCK_BUFFER (1 << 1)
+#define FEATURE_BLOCK_PROC (1 << 2)
+#define FEATURE_I2C_BLOCK_READ (1 << 3)
+/* Not really a feature, but it's convenient to handle it as such */
+#define FEATURE_IDF (1 << 15)
+
+static unsigned int disable_features;
+module_param(disable_features, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(disable_features, "Disable selected driver features");
+
+// FIXME!
+#undef inb_p
+#define inb_p(a) readq((void*)a)
+#undef outb_p
+#define outb_p(d,a) writeq(d,(void*)a)
+
+/* Make sure the SMBus host is ready to start transmitting.
+ Return 0 if it is, -EBUSY if it is not. */
+static int i801_check_pre(struct i2c_device *priv)
+{
+ int status;
+
+ dev_dbg(&priv->adapter.dev, "i801_check_pre\n");
+
+ status = inb_p(SMBHSTSTS(priv));
+ if (status & SMBHSTSTS_HOST_BUSY) {
+ dev_err(&priv->adapter.dev, "SMBus is busy, can't use it! (status=%x)\n", status);
+ return -EBUSY;
+ }
+
+ status &= STATUS_FLAGS;
+ if (status) {
+ //dev_dbg(&priv->adapter.dev, "Clearing status flags (%02x)\n", status);
+ outb_p(status, SMBHSTSTS(priv));
+ status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS;
+ if (status) {
+ dev_err(&priv->adapter.dev, "Failed clearing status flags (%02x)\n", status);
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+/* Convert the status register to an error code, and clear it. */
+static int i801_check_post(struct i2c_device *priv, int status, int timeout)
+{
+ int result = 0;
+
+ dev_dbg(&priv->adapter.dev, "i801_check_post\n");
+
+ /* If the SMBus is still busy, we give up */
+ if (timeout) {
+ dev_err(&priv->adapter.dev, "Transaction timeout\n");
+ /* try to stop the current command */
+ dev_dbg(&priv->adapter.dev, "Terminating the current operation\n");
+ outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, SMBHSTCNT(priv));
+ usleep_range(1000, 2000);
+ outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL), SMBHSTCNT(priv));
+
+ /* Check if it worked */
+ status = inb_p(SMBHSTSTS(priv));
+ if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) {
+ dev_err(&priv->adapter.dev, "Failed terminating the transaction\n");
+ }
+ outb_p(STATUS_FLAGS, SMBHSTSTS(priv));
+ return -ETIMEDOUT;
+ }
+
+ if (status & SMBHSTSTS_FAILED) {
+ result = -EIO;
+ dev_err(&priv->adapter.dev, "Transaction failed\n");
+ }
+ if (status & SMBHSTSTS_DEV_ERR) {
+ result = -ENXIO;
+ dev_dbg(&priv->adapter.dev, "No response\n");
+ }
+ if (status & SMBHSTSTS_BUS_ERR) {
+ result = -EAGAIN;
+ dev_dbg(&priv->adapter.dev, "Lost arbitration\n");
+ }
+
+ if (result) {
+ /* Clear error flags */
+ outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv));
+ status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS;
+ if (status) {
+ dev_warn(&priv->adapter.dev, "Failed clearing status flags at end of transaction (%02x)\n", status);
+ }
+ }
+
+ return result;
+}
+
+static int i801_transaction(struct i2c_device *priv, int xact)
+{
+ int status;
+ int result;
+ int timeout = 0;
+
+ dev_dbg(&priv->adapter.dev, "i801_transaction\n");
+
+ result = i801_check_pre(priv);
+ if (result < 0) {
+ return result;
+ }
+ /* the current contents of SMBHSTCNT can be overwritten, since PEC,
+ * INTREN, SMBSCMD are passed in xact */
+ outb_p(xact | I801_START, SMBHSTCNT(priv));
+
+ /* We will always wait for a fraction of a second! */
+ do {
+ usleep_range(250, 500);
+ status = inb_p(SMBHSTSTS(priv));
+ } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES));
+
+ result = i801_check_post(priv, status, timeout > MAX_RETRIES);
+ if (result < 0) {
+ return result;
+ }
+
+ outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv));
+ return 0;
+}
+
+/* wait for INTR bit as advised by Intel */
+static void i801_wait_hwpec(struct i2c_device *priv)
+{
+ int timeout = 0;
+ int status;
+
+ dev_dbg(&priv->adapter.dev, "i801_wait_hwpec\n");
+
+ do {
+ usleep_range(250, 500);
+ status = inb_p(SMBHSTSTS(priv));
+ } while ((!(status & SMBHSTSTS_INTR)) && (timeout++ < MAX_RETRIES));
+
+ if (timeout > MAX_RETRIES) {
+ dev_dbg(&priv->adapter.dev, "PEC Timeout!\n");
+ }
+
+ outb_p(status, SMBHSTSTS(priv));
+}
+
+static int i801_block_transaction_by_block(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int hwpec)
+{
+ int i, len;
+ int status;
+
+ dev_dbg(&priv->adapter.dev, "i801_block_transaction_by_block\n");
+
+ inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */
+
+ /* Use 32-byte buffer to process this transaction */
+ if (read_write == I2C_SMBUS_WRITE) {
+ len = data->block[0];
+ outb_p(len, SMBHSTDAT0(priv));
+ for (i = 0; i < len; i++) {
+ outb_p(data->block[i+1], SMBBLKDAT(priv));
+ }
+ }
+
+ status = i801_transaction(priv, I801_BLOCK_DATA | ENABLE_INT9 | I801_PEC_EN * hwpec);
+ if (status) {
+ return status;
+ }
+
+ if (read_write == I2C_SMBUS_READ) {
+ len = inb_p(SMBHSTDAT0(priv));
+ if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
+ return -EPROTO;
+ }
+
+ data->block[0] = len;
+ for (i = 0; i < len; i++) {
+ data->block[i + 1] = inb_p(SMBBLKDAT(priv));
+ }
+ }
+ return 0;
+}
+
+static int i801_block_transaction_byte_by_byte(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec)
+{
+ int i, len;
+ int smbcmd;
+ int status;
+ int result;
+ int timeout;
+
+ dev_dbg(&priv->adapter.dev, "i801_block_transaction_byte_by_byte\n");
+
+ result = i801_check_pre(priv);
+ if (result < 0) {
+ return result;
+ }
+
+ len = data->block[0];
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ outb_p(len, SMBHSTDAT0(priv));
+ outb_p(data->block[1], SMBBLKDAT(priv));
+ }
+
+ for (i = 1; i <= len; i++) {
+ if (i == len && read_write == I2C_SMBUS_READ) {
+ if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+ smbcmd = I801_I2C_BLOCK_LAST;
+ } else {
+ smbcmd = I801_BLOCK_LAST;
+ }
+ } else {
+ if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) {
+ smbcmd = I801_I2C_BLOCK_DATA;
+ } else {
+ smbcmd = I801_BLOCK_DATA;
+ }
+ }
+ outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv));
+
+ if (i == 1) {
+ outb_p(inb(SMBHSTCNT(priv)) | I801_START, SMBHSTCNT(priv));
+ }
+ /* We will always wait for a fraction of a second! */
+ timeout = 0;
+ do {
+ usleep_range(250, 500);
+ status = inb_p(SMBHSTSTS(priv));
+ } while ((!(status & SMBHSTSTS_BYTE_DONE)) && (timeout++ < MAX_RETRIES));
+
+ result = i801_check_post(priv, status, timeout > MAX_RETRIES);
+ if (result < 0) {
+ return result;
+ }
+ if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+ len = inb_p(SMBHSTDAT0(priv));
+ if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&priv->adapter.dev, "Illegal SMBus block read size %d\n", len);
+ /* Recover */
+ while (inb_p(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) {
+ outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
+ }
+ outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv));
+ return -EPROTO;
+ }
+ data->block[0] = len;
+ }
+
+ /* Retrieve/store value in SMBBLKDAT */
+ if (read_write == I2C_SMBUS_READ) {
+ data->block[i] = inb_p(SMBBLKDAT(priv));
+ }
+ if (read_write == I2C_SMBUS_WRITE && i+1 <= len) {
+ outb_p(data->block[i+1], SMBBLKDAT(priv));
+ }
+ /* signals SMBBLKDAT ready */
+ outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv));
+ }
+
+ return 0;
+}
+
+static int i801_set_block_buffer_mode(struct i2c_device *priv)
+{
+ dev_dbg(&priv->adapter.dev, "i801_set_block_buffer_mode\n");
+
+ outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv));
+ if ((inb_p(SMBAUXCTL(priv)) & SMBAUXCTL_E32B) == 0) {
+ return -EIO;
+ }
+ return 0;
+}
+
+/* Block transaction function */
+static int i801_block_transaction(struct i2c_device *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec)
+{
+ int result = 0;
+ //unsigned char hostc;
+
+ dev_dbg(&priv->adapter.dev, "i801_block_transaction\n");
+
+ if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (read_write == I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ //TODO: Figure out the right thing to do here...
+ //pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
+ //pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN);
+ } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) {
+ dev_err(&priv->adapter.dev, "I2C block read is unsupported!\n");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if (read_write == I2C_SMBUS_WRITE || command == I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (data->block[0] < 1) {
+ data->block[0] = 1;
+ }
+ if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+ data->block[0] = I2C_SMBUS_BLOCK_MAX;
+ }
+ } else {
+ data->block[0] = 32; /* max for SMBus block reads */
+ }
+
+ /* Experience has shown that the block buffer can only be used for
+ SMBus (not I2C) block transactions, even though the datasheet
+ doesn't mention this limitation. */
+ if ((priv->features & FEATURE_BLOCK_BUFFER) && command != I2C_SMBUS_I2C_BLOCK_DATA && i801_set_block_buffer_mode(priv) == 0) {
+ result = i801_block_transaction_by_block(priv, data, read_write, hwpec);
+ } else {
+ result = i801_block_transaction_byte_by_byte(priv, data, read_write, command, hwpec);
+ }
+ if (result == 0 && hwpec) {
+ i801_wait_hwpec(priv);
+ }
+ if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_WRITE) {
+ /* restore saved configuration register value */
+ //TODO: Figure out the right thing to do here...
+ //pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc);
+ }
+ return result;
+}
+
+/* Return negative errno on error. */
+static s32 i801_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data)
+{
+ int hwpec;
+ int block = 0;
+ int ret, xact = 0;
+ struct i2c_device *priv = i2c_get_adapdata(adap);
+
+ dev_dbg(&priv->adapter.dev, "i801_access (addr=%0d) flags=%x read_write=%x command=%x size=%x",
+ addr, flags, read_write, command, size );
+
+ hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA;
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ dev_dbg(&priv->adapter.dev, " [acc] SMBUS_QUICK\n");
+ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv));
+ xact = I801_QUICK;
+ break;
+ case I2C_SMBUS_BYTE:
+ dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BYTE\n");
+
+ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv));
+ if (read_write == I2C_SMBUS_WRITE) {
+ outb_p(command, SMBHSTCMD(priv));
+ }
+ xact = I801_BYTE;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BYTE_DATA\n");
+ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv));
+ outb_p(command, SMBHSTCMD(priv));
+ if (read_write == I2C_SMBUS_WRITE) {
+ outb_p(data->byte, SMBHSTDAT0(priv));
+ }
+ xact = I801_BYTE_DATA;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ dev_dbg(&priv->adapter.dev, " [acc] SMBUS_WORD_DATA\n");
+ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv));
+ outb_p(command, SMBHSTCMD(priv));
+ if (read_write == I2C_SMBUS_WRITE) {
+ outb_p(data->word & 0xff, SMBHSTDAT0(priv));
+ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
+ }
+ xact = I801_WORD_DATA;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ dev_dbg(&priv->adapter.dev, " [acc] SMBUS_BLOCK_DATA\n");
+ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv));
+ outb_p(command, SMBHSTCMD(priv));
+ block = 1;
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ dev_dbg(&priv->adapter.dev, " [acc] SMBUS_I2C_BLOCK_DATA\n");
+ /* NB: page 240 of ICH5 datasheet shows that the R/#W
+ * bit should be cleared here, even when reading */
+ outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
+ if (read_write == I2C_SMBUS_READ) {
+ /* NB: page 240 of ICH5 datasheet also shows
+ * that DATA1 is the cmd field when reading */
+ outb_p(command, SMBHSTDAT1(priv));
+ } else {
+ outb_p(command, SMBHSTCMD(priv));
+ }
+ block = 1;
+ break;
+ default:
+ dev_dbg(&priv->adapter.dev, " [acc] Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ if (hwpec) { /* enable/disable hardware PEC */
+ dev_dbg(&priv->adapter.dev, " [acc] hwpec: yes\n");
+ outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv));
+ } else {
+ dev_dbg(&priv->adapter.dev, " [acc] hwpec: no\n");
+ outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), SMBAUXCTL(priv));
+ }
+
+ if (block) {
+ //ret = 0;
+ dev_dbg(&priv->adapter.dev, " [acc] block: yes\n");
+ ret = i801_block_transaction(priv, data, read_write, size, hwpec);
+ } else {
+ dev_dbg(&priv->adapter.dev, " [acc] block: no\n");
+ ret = i801_transaction(priv, xact | ENABLE_INT9);
+ }
+
+ /* Some BIOSes don't like it when PEC is enabled at reboot or resume
+ time, so we forcibly disable it after every transaction. Turn off
+ E32B for the same reason. */
+ if (hwpec || block) {
+ dev_dbg(&priv->adapter.dev, " [acc] hwpec || block\n");
+ outb_p(inb_p(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
+ }
+ if (block) {
+ dev_dbg(&priv->adapter.dev, " [acc] block\n");
+ return ret;
+ }
+ if (ret) {
+ dev_dbg(&priv->adapter.dev, " [acc] ret %d\n", ret);
+ return ret;
+ }
+ if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) {
+ dev_dbg(&priv->adapter.dev, " [acc] I2C_SMBUS_WRITE || I801_QUICK -> ret 0\n");
+ return 0;
+ }
+
+ switch (xact & 0x7f) {
+ case I801_BYTE: /* Result put in SMBHSTDAT0 */
+ case I801_BYTE_DATA:
+ dev_dbg(&priv->adapter.dev, " [acc] I801_BYTE or I801_BYTE_DATA\n");
+ data->byte = inb_p(SMBHSTDAT0(priv));
+ break;
+ case I801_WORD_DATA:
+ dev_dbg(&priv->adapter.dev, " [acc] I801_WORD_DATA\n");
+ data->word = inb_p(SMBHSTDAT0(priv)) + (inb_p(SMBHSTDAT1(priv)) << 8);
+ break;
+ }
+ return 0;
+}
+
+
+
+static u32 i801_func(struct i2c_adapter *adapter)
+{
+ struct i2c_device *priv = i2c_get_adapdata(adapter);
+
+ /* original settings
+ u32 f = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
+ ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
+ ((priv->features & FEATURE_I2C_BLOCK_READ) ?
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0);
+ */
+
+ // http://lxr.free-electrons.com/source/include/uapi/linux/i2c.h#L85
+
+ u32 f =
+ I2C_FUNC_I2C | /* 0x00000001 (I enabled this one) */
+ !I2C_FUNC_10BIT_ADDR | /* 0x00000002 */
+ !I2C_FUNC_PROTOCOL_MANGLING | /* 0x00000004 */
+ ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | /* 0x00000008 */
+ !I2C_FUNC_SMBUS_BLOCK_PROC_CALL | /* 0x00008000 */
+ I2C_FUNC_SMBUS_QUICK | /* 0x00010000 */
+ !I2C_FUNC_SMBUS_READ_BYTE | /* 0x00020000 */
+ !I2C_FUNC_SMBUS_WRITE_BYTE | /* 0x00040000 */
+ !I2C_FUNC_SMBUS_READ_BYTE_DATA | /* 0x00080000 */
+ !I2C_FUNC_SMBUS_WRITE_BYTE_DATA | /* 0x00100000 */
+ !I2C_FUNC_SMBUS_READ_WORD_DATA | /* 0x00200000 */
+ !I2C_FUNC_SMBUS_WRITE_WORD_DATA | /* 0x00400000 */
+ !I2C_FUNC_SMBUS_PROC_CALL | /* 0x00800000 */
+ !I2C_FUNC_SMBUS_READ_BLOCK_DATA | /* 0x01000000 */
+ !I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | /* 0x02000000 */
+ ((priv->features & FEATURE_I2C_BLOCK_READ) ? I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0) | /* 0x04000000 */
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | /* 0x08000000 */
+
+ I2C_FUNC_SMBUS_BYTE | /* _READ_BYTE _WRITE_BYTE */
+ I2C_FUNC_SMBUS_BYTE_DATA | /* _READ_BYTE_DATA _WRITE_BYTE_DATA */
+ I2C_FUNC_SMBUS_WORD_DATA | /* _READ_WORD_DATA _WRITE_WORD_DATA */
+ I2C_FUNC_SMBUS_BLOCK_DATA | /* _READ_BLOCK_DATA _WRITE_BLOCK_DATA */
+ !I2C_FUNC_SMBUS_I2C_BLOCK | /* _READ_I2C_BLOCK _WRITE_I2C_BLOCK */
+ !I2C_FUNC_SMBUS_EMUL; /* _QUICK _BYTE _BYTE_DATA _WORD_DATA _PROC_CALL _WRITE_BLOCK_DATA _I2C_BLOCK _PEC */
+ return f;
+}
+
+static const struct i2c_algorithm smbus_algorithm = {
+ .smbus_xfer = i801_access,
+ .functionality = i801_func,
+};
+
+
+
+/********************************
+ *** Part 2 - Driver Handlers ***
+ ********************************/
+int pi2c_probe(struct platform_device *pldev)
+{
+ int err;
+ struct i2c_device *priv;
+ struct resource *res;
+
+ dev_dbg(&pldev->dev, "pi2c_probe(pldev = %p '%s')\n", pldev, pldev->name);
+
+ priv = kzalloc(sizeof(struct i2c_device), GFP_KERNEL);
+ if (!priv) {
+ return -ENOMEM;
+ }
+
+ i2c_set_adapdata(&priv->adapter, priv);
+ priv->adapter.owner = THIS_MODULE;
+ priv->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ priv->adapter.algo = &smbus_algorithm;
+
+ res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
+ priv->smba = (unsigned long)ioremap_nocache(res->start, res->end - res->start);
+
+ priv->pldev = pldev;
+ pldev->dev.platform_data = priv;
+
+ priv->features |= FEATURE_IDF;
+ priv->features |= FEATURE_I2C_BLOCK_READ;
+ priv->features |= FEATURE_SMBUS_PEC;
+ priv->features |= FEATURE_BLOCK_BUFFER;
+
+ //init_MUTEX(&lddata->sem);
+ init_rwsem(&priv->rw_sem);
+
+ /* set up the sysfs linkage to our parent device */
+ priv->adapter.dev.parent = &pldev->dev;
+
+ /* Retry up to 3 times on lost arbitration */
+ priv->adapter.retries = 3;
+
+ //snprintf(priv->adapter.name, sizeof(priv->adapter.name), "Fake SMBus I801 adapter at %04lx", priv->smba);
+ snprintf(priv->adapter.name, sizeof(priv->adapter.name), "Fake SMBus I801 adapter");
+
+ err = i2c_add_adapter(&priv->adapter);
+ if (err) {
+ dev_err(&priv->adapter.dev, "Failed to add SMBus adapter\n");
+ return err;
+ }
+
+ return 0;
+}
+
+int pi2c_remove(struct platform_device *pldev)
+{
+ struct i2c_device *lddev;
+ dev_dbg(&pldev->dev, "pi2c_remove(pldev = %p '%s')\n", pldev, pldev->name);
+
+ lddev = (struct i2c_device *)pldev->dev.platform_data;
+
+ i2c_del_adapter(&lddev->adapter);
+
+ //TODO: Figure out the right thing to do here...
+ //pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
+ //pci_release_region(dev, SMBBAR);
+ //pci_set_drvdata(dev, NULL);
+
+ //cdev_del(&lddev->cdev);
+ if(lddev != 0) {
+ kfree(lddev);
+ pldev->dev.platform_data = 0;
+ }
+
+ return 0;
+}
+
+struct platform_driver i2c_plat_driver_i = {
+ .probe = pi2c_probe,
+ .remove = pi2c_remove,
+ .driver = {
+ .name = KP_DRIVER_NAME_I2C,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(i2c_plat_driver_i);
diff --git a/drivers/staging/kpc2000/kpc_spi/Makefile b/drivers/staging/kpc2000/kpc_spi/Makefile
new file mode 100644
index 000000000000..3018d200484f
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc_spi/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-m += kpc2000_spi.o
+kpc2000_spi-objs := spi_driver.o
diff --git a/drivers/staging/kpc2000/kpc_spi/spi_driver.c b/drivers/staging/kpc2000/kpc_spi/spi_driver.c
new file mode 100644
index 000000000000..b38149b752fb
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc_spi/spi_driver.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * KP2000 SPI controller driver
+ *
+ * Copyright (C) 2014-2018 Daktronics
+ * Author: Matt Sickler <matt.sickler@daktronics.com>
+ * Very loosely based on spi-omap2-mcspi.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/gcd.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+#include <linux/mtd/partitions.h>
+
+#include "../kpc.h"
+#include "spi_parts.h"
+
+
+/***************
+ * SPI Defines *
+ ***************/
+#define KP_SPI_REG_CONFIG 0x0 /* 0x00 */
+#define KP_SPI_REG_STATUS 0x1 /* 0x08 */
+#define KP_SPI_REG_FFCTRL 0x2 /* 0x10 */
+#define KP_SPI_REG_TXDATA 0x3 /* 0x18 */
+#define KP_SPI_REG_RXDATA 0x4 /* 0x20 */
+
+#define KP_SPI_CLK 48000000
+#define KP_SPI_MAX_FIFODEPTH 64
+#define KP_SPI_MAX_FIFOWCNT 0xFFFF
+
+#define KP_SPI_REG_CONFIG_TRM_TXRX 0
+#define KP_SPI_REG_CONFIG_TRM_RX 1
+#define KP_SPI_REG_CONFIG_TRM_TX 2
+
+#define KP_SPI_REG_STATUS_RXS 0x01
+#define KP_SPI_REG_STATUS_TXS 0x02
+#define KP_SPI_REG_STATUS_EOT 0x04
+#define KP_SPI_REG_STATUS_TXFFE 0x10
+#define KP_SPI_REG_STATUS_TXFFF 0x20
+#define KP_SPI_REG_STATUS_RXFFE 0x40
+#define KP_SPI_REG_STATUS_RXFFF 0x80
+
+
+
+/******************
+ * SPI Structures *
+ ******************/
+struct kp_spi {
+ struct spi_master *master;
+ u64 __iomem *base;
+ unsigned long phys;
+ struct device *dev;
+ int fifo_depth;
+ unsigned int pin_dir:1;
+};
+
+
+struct kp_spi_controller_state {
+ void __iomem *base;
+ unsigned long phys;
+ unsigned char chip_select;
+ int word_len;
+ s64 conf_cache;
+};
+
+
+union kp_spi_config {
+ /* use this to access individual elements */
+ struct __attribute__((packed)) spi_config_bitfield {
+ unsigned char pha : 1; /* spim_clk Phase */
+ unsigned char pol : 1; /* spim_clk Polarity */
+ unsigned char epol : 1; /* spim_csx Polarity */
+ unsigned char dpe : 1; /* Transmission Enable */
+ unsigned char wl : 5; /* Word Length */
+ unsigned char : 3;
+ unsigned char trm : 2; /* TxRx Mode */
+ unsigned char cs : 4; /* Chip Select */
+ unsigned char wcnt : 7; /* Word Count */
+ unsigned char ffen : 1; /* FIFO Enable */
+ unsigned char spi_en : 1; /* SPI Enable */
+ unsigned char : 5;
+ } bitfield;
+ /* use this to grab the whole register */
+ u32 reg;
+};
+
+
+
+union kp_spi_status {
+ struct __attribute__((packed)) spi_status_bitfield {
+ unsigned char rx : 1; /* Rx Status */
+ unsigned char tx : 1; /* Tx Status */
+ unsigned char eo : 1; /* End of Transfer */
+ unsigned char : 1;
+ unsigned char txffe : 1; /* Tx FIFO Empty */
+ unsigned char txfff : 1; /* Tx FIFO Full */
+ unsigned char rxffe : 1; /* Rx FIFO Empty */
+ unsigned char rxfff : 1; /* Rx FIFO Full */
+ unsigned int : 24;
+ } bitfield;
+ u32 reg;
+};
+
+
+
+union kp_spi_ffctrl {
+ struct __attribute__((packed)) spi_ffctrl_bitfield {
+ unsigned char ffstart : 1; /* FIFO Start */
+ unsigned int : 31;
+ } bitfield;
+ u32 reg;
+};
+
+
+
+/***************
+ * SPI Helpers *
+ ***************/
+static inline int
+kp_spi_bytes_per_word(int word_len)
+{
+ if (word_len <= 8){
+ return 1;
+ }
+ else if (word_len <= 16) {
+ return 2;
+ }
+ else { /* word_len <= 32 */
+ return 4;
+ }
+}
+
+static inline u64
+kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx)
+{
+ u64 __iomem *addr = cs->base;
+ u64 val;
+
+ addr += idx;
+ if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)){
+ return cs->conf_cache;
+ }
+ val = readq((void*)addr);
+ return val;
+}
+
+static inline void
+kp_spi_write_reg(struct kp_spi_controller_state *cs, int idx, u64 val)
+{
+ u64 __iomem *addr = cs->base;
+ addr += idx;
+ writeq(val, (void*)addr);
+ if (idx == KP_SPI_REG_CONFIG)
+ cs->conf_cache = val;
+}
+
+static int
+kp_spi_wait_for_reg_bit(struct kp_spi_controller_state *cs, int idx, unsigned long bit)
+{
+ unsigned long timeout;
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (!(kp_spi_read_reg(cs, idx) & bit)) {
+ if (time_after(jiffies, timeout)) {
+ if (!(kp_spi_read_reg(cs, idx) & bit)) {
+ return -ETIMEDOUT;
+ } else {
+ return 0;
+ }
+ }
+ cpu_relax();
+ }
+ return 0;
+}
+
+static unsigned
+kp_spi_txrx_pio(struct spi_device *spidev, struct spi_transfer *transfer)
+{
+ struct kp_spi_controller_state *cs = spidev->controller_state;
+ unsigned int count = transfer->len;
+ unsigned int c = count;
+
+ int i;
+ u8 *rx = transfer->rx_buf;
+ const u8 *tx = transfer->tx_buf;
+ int processed = 0;
+
+ if (tx) {
+ for (i = 0 ; i < c ; i++) {
+ char val = *tx++;
+
+ if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_TXS) < 0) {
+ goto out;
+ }
+
+ kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, val);
+ processed++;
+ }
+ }
+ else if(rx) {
+ for (i = 0 ; i < c ; i++) {
+ char test=0;
+
+ kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, 0x00);
+
+ if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_RXS) < 0) {
+ goto out;
+ }
+
+ test = kp_spi_read_reg(cs, KP_SPI_REG_RXDATA);
+ *rx++ = test;
+ processed++;
+ }
+ }
+
+ if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) {
+ //TODO: Figure out how to abort transaction?? This has never happened in practice though...
+ }
+
+ out:
+ return processed;
+}
+
+/*****************
+ * SPI Functions *
+ *****************/
+static int
+kp_spi_setup(struct spi_device *spidev)
+{
+ union kp_spi_config sc;
+ struct kp_spi *kpspi = spi_master_get_devdata(spidev->master);
+ struct kp_spi_controller_state *cs;
+
+ /* setup controller state */
+ cs = spidev->controller_state;
+ if (!cs) {
+ cs = kzalloc(sizeof(*cs), GFP_KERNEL);
+ if(!cs) {
+ return -ENOMEM;
+ }
+ cs->base = kpspi->base;
+ cs->phys = kpspi->phys;
+ cs->chip_select = spidev->chip_select;
+ cs->word_len = spidev->bits_per_word;
+ cs->conf_cache = -1;
+ spidev->controller_state = cs;
+ }
+
+ /* set config register */
+ sc.bitfield.wl = spidev->bits_per_word - 1;
+ sc.bitfield.cs = spidev->chip_select;
+ sc.bitfield.spi_en = 0;
+ sc.bitfield.trm = 0;
+ sc.bitfield.ffen = 0;
+ kp_spi_write_reg(spidev->controller_state, KP_SPI_REG_CONFIG, sc.reg);
+ return 0;
+}
+
+static int
+kp_spi_transfer_one_message(struct spi_master *master, struct spi_message *m)
+{
+ struct kp_spi_controller_state *cs;
+ struct spi_device *spidev;
+ struct kp_spi *kpspi;
+ struct spi_transfer *transfer;
+ union kp_spi_config sc;
+ int status = 0;
+
+ spidev = m->spi;
+ kpspi = spi_master_get_devdata(master);
+ m->actual_length = 0;
+ m->status = 0;
+
+ cs = spidev->controller_state;
+
+ /* reject invalid messages and transfers */
+ if (list_empty(&m->transfers)) {
+ return -EINVAL;
+ }
+
+ /* validate input */
+ list_for_each_entry(transfer, &m->transfers, transfer_list) {
+ const void *tx_buf = transfer->tx_buf;
+ void *rx_buf = transfer->rx_buf;
+ unsigned len = transfer->len;
+
+ if (transfer->speed_hz > KP_SPI_CLK || (len && !(rx_buf || tx_buf))) {
+ dev_dbg(kpspi->dev, " transfer: %d Hz, %d %s%s, %d bpw\n",
+ transfer->speed_hz,
+ len,
+ tx_buf ? "tx" : "",
+ rx_buf ? "rx" : "",
+ transfer->bits_per_word);
+ dev_dbg(kpspi->dev, " transfer -EINVAL\n");
+ return -EINVAL;
+ }
+ if (transfer->speed_hz && (transfer->speed_hz < (KP_SPI_CLK >> 15))) {
+ dev_dbg(kpspi->dev, "speed_hz %d below minimum %d Hz\n",
+ transfer->speed_hz,
+ KP_SPI_CLK >> 15);
+ dev_dbg(kpspi->dev, " speed_hz -EINVAL\n");
+ return -EINVAL;
+ }
+ }
+
+ /* assert chip select to start the sequence*/
+ sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG);
+ sc.bitfield.spi_en = 1;
+ kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg);
+
+ /* work */
+ if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) {
+ dev_info(kpspi->dev, "EOT timed out\n");
+ goto out;
+ }
+
+ /* do the transfers for this message */
+ list_for_each_entry(transfer, &m->transfers, transfer_list) {
+ if (transfer->tx_buf == NULL && transfer->rx_buf == NULL && transfer->len) {
+ status = -EINVAL;
+ break;
+ }
+
+ /* transfer */
+ if (transfer->len) {
+ unsigned int word_len = spidev->bits_per_word;
+ unsigned count;
+
+ /* set up the transfer... */
+ sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG);
+
+ /* ...direction */
+ if (transfer->tx_buf) {
+ sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_TX;
+ }
+ else if (transfer->rx_buf) {
+ sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_RX;
+ }
+
+ /* ...word length */
+ if (transfer->bits_per_word) {
+ word_len = transfer->bits_per_word;
+ }
+ cs->word_len = word_len;
+ sc.bitfield.wl = word_len-1;
+
+ /* ...chip select */
+ sc.bitfield.cs = spidev->chip_select;
+
+ /* ...and write the new settings */
+ kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg);
+
+ /* do the transfer */
+ count = kp_spi_txrx_pio(spidev, transfer);
+ m->actual_length += count;
+
+ if (count != transfer->len) {
+ status = -EIO;
+ break;
+ }
+ }
+
+ if (transfer->delay_usecs) {
+ udelay(transfer->delay_usecs);
+ }
+ }
+
+ /* de-assert chip select to end the sequence */
+ sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG);
+ sc.bitfield.spi_en = 0;
+ kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg);
+
+ out:
+ /* done work */
+ spi_finalize_current_message(master);
+ return 0;
+}
+
+static void
+kp_spi_cleanup(struct spi_device *spidev)
+{
+ struct kp_spi_controller_state *cs = spidev->controller_state;
+ if (cs) {
+ kfree(cs);
+ }
+}
+
+
+
+/******************
+ * Probe / Remove *
+ ******************/
+static int
+kp_spi_probe(struct platform_device *pldev)
+{
+ struct kpc_core_device_platdata *drvdata = (struct kpc_core_device_platdata *)pldev->dev.platform_data;
+ struct spi_master *master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi));
+ struct kp_spi *kpspi;
+ struct resource *r;
+ int status = 0;
+ int i;
+
+ drvdata = (struct kpc_core_device_platdata *)pldev->dev.platform_data;
+ if (!drvdata){
+ dev_err(&pldev->dev, "kp_spi_probe: platform_data is NULL!\n");
+ return -ENODEV;
+ }
+
+ master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi));
+ if (master == NULL) {
+ dev_err(&pldev->dev, "kp_spi_probe: master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* set up the spi functions */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ master->bits_per_word_mask = (unsigned int)SPI_BPW_RANGE_MASK(4, 32);
+ master->setup = kp_spi_setup;
+ master->transfer_one_message = kp_spi_transfer_one_message;
+ master->cleanup = kp_spi_cleanup;
+
+ platform_set_drvdata(pldev, master);
+
+ kpspi = spi_master_get_devdata(master);
+ kpspi->master = master;
+ kpspi->dev = &pldev->dev;
+
+ master->num_chipselect = 4;
+ if (pldev->id != -1) {
+ master->bus_num = pldev->id;
+ }
+ kpspi->pin_dir = 0;
+
+ r = platform_get_resource(pldev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pldev->dev, "kp_spi_probe: Unable to get platform resources\n");
+ status = -ENODEV;
+ goto free_master;
+ }
+
+ kpspi->phys = (unsigned long)ioremap_nocache(r->start, r->end - r->start);
+ kpspi->base = (u64 __iomem *)kpspi->phys;
+
+ status = spi_register_master(master);
+ if (status < 0) {
+ dev_err(&pldev->dev, "Unable to register SPI device\n");
+ goto free_master;
+ }
+
+ /* register the slave boards */
+ #define NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(table) \
+ for (i = 0 ; i < ARRAY_SIZE(table) ; i++) { \
+ spi_new_device(master, &(table[i])); \
+ }
+
+ switch ((drvdata->card_id & 0xFFFF0000) >> 16){
+ case PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0:
+ NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(p2kr0_board_info);
+ break;
+ default:
+ dev_err(&pldev->dev, "Unknown hardware, cant know what partition table to use!\n");
+ goto free_master;
+ break;
+ }
+
+ return status;
+
+ free_master:
+ spi_master_put(master);
+ return status;
+}
+
+static int
+kp_spi_remove(struct platform_device *pldev)
+{
+ struct spi_master * master = platform_get_drvdata(pldev);
+ spi_unregister_master(master);
+ return 0;
+}
+
+
+static struct platform_driver kp_spi_driver = {
+ .driver = {
+ .name = KP_DRIVER_NAME_SPI,
+ .owner = THIS_MODULE,
+ },
+ .probe = kp_spi_probe,
+ .remove = kp_spi_remove,
+};
+
+module_platform_driver(kp_spi_driver);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kp_spi");
diff --git a/drivers/staging/kpc2000/kpc_spi/spi_parts.h b/drivers/staging/kpc2000/kpc_spi/spi_parts.h
new file mode 100644
index 000000000000..33e62acc5e08
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc_spi/spi_parts.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __KPC_SPI_SPI_PARTS_H__
+#define __KPC_SPI_SPI_PARTS_H__
+
+static struct mtd_partition p2kr0_spi0_parts[] = {
+ { .name = "SLOT_0", .size = 7798784, .offset = 0, },
+ { .name = "SLOT_1", .size = 7798784, .offset = MTDPART_OFS_NXTBLK },
+ { .name = "SLOT_2", .size = 7798784, .offset = MTDPART_OFS_NXTBLK },
+ { .name = "SLOT_3", .size = 7798784, .offset = MTDPART_OFS_NXTBLK },
+ { .name = "CS0_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK }
+};
+static struct mtd_partition p2kr0_spi1_parts[] = {
+ { .name = "SLOT_4", .size = 7798784, .offset = 0, },
+ { .name = "SLOT_5", .size = 7798784, .offset = MTDPART_OFS_NXTBLK },
+ { .name = "SLOT_6", .size = 7798784, .offset = MTDPART_OFS_NXTBLK },
+ { .name = "SLOT_7", .size = 7798784, .offset = MTDPART_OFS_NXTBLK },
+ { .name = "CS1_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK }
+};
+
+static struct flash_platform_data p2kr0_spi0_pdata = {
+ .name = "SPI0",
+ .nr_parts = ARRAY_SIZE(p2kr0_spi0_parts),
+ .parts = p2kr0_spi0_parts,
+};
+static struct flash_platform_data p2kr0_spi1_pdata = {
+ .name = "SPI1",
+ .nr_parts = ARRAY_SIZE(p2kr0_spi1_parts),
+ .parts = p2kr0_spi1_parts,
+};
+
+static struct spi_board_info p2kr0_board_info[] = {
+ {
+ .modalias = "n25q256a11",
+ .bus_num = 1,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ .platform_data = &p2kr0_spi0_pdata
+ },
+ {
+ .modalias = "n25q256a11",
+ .bus_num = 1,
+ .chip_select = 1,
+ .mode = SPI_MODE_0,
+ .platform_data = &p2kr0_spi1_pdata
+ },
+};
+
+#endif
diff --git a/drivers/staging/media/rockchip/vpu/Kconfig b/drivers/staging/media/rockchip/vpu/Kconfig
index 208f0379b31f..fc54bbf6753d 100644
--- a/drivers/staging/media/rockchip/vpu/Kconfig
+++ b/drivers/staging/media/rockchip/vpu/Kconfig
@@ -6,7 +6,6 @@ config VIDEO_ROCKCHIP_VPU
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
- default n
help
Support for the Video Processing Unit present on Rockchip SoC,
which accelerates video and image encoding and decoding.
diff --git a/drivers/staging/most/Documentation/ABI/configfs-most.txt b/drivers/staging/most/Documentation/ABI/configfs-most.txt
new file mode 100644
index 000000000000..25b3e18c4d91
--- /dev/null
+++ b/drivers/staging/most/Documentation/ABI/configfs-most.txt
@@ -0,0 +1,204 @@
+What: /sys/kernel/config/most_<component>
+Date: March 8, 2019
+KernelVersion: 5.2
+Description: Interface is used to configure and connect device channels
+ to component drivers.
+
+ Attributes are visible only when configfs is mounted. To mount
+ configfs in /sys/kernel/config directory use:
+ # mount -t configfs none /sys/kernel/config/
+
+
+What: /sys/kernel/config/most_cdev/<link>
+Date: March 8, 2019
+KernelVersion: 5.2
+Description:
+ The attributes:
+
+ buffer_size configure the buffer size for this channel
+
+ subbuffer_size configure the sub-buffer size for this channel
+ (needed for synchronous and isochrnous data)
+
+
+ num_buffers configure number of buffers used for this
+ channel
+
+ datatype configure type of data that will travel over
+ this channel
+
+ direction configure whether this link will be an input
+ or output
+
+ dbr_size configure DBR data buffer size (this is used
+ for MediaLB communiction only)
+
+ packets_per_xact
+ configure the number of packets that will be
+ collected from the network before being
+ transmitted via USB (this is used for USB
+ communiction only)
+
+ device name of the device the link is to be attached to
+
+ channel name of the channel the link is to be attached to
+
+ comp_params pass parameters needed by some components
+
+ create_link write '1' to this attribute to trigger the
+ creation of the link. In case of speculative
+ configuration, the creation is post-poned until
+ a physical device is being attached to the bus.
+
+ destroy_link write '1' to this attribute to destroy an
+ active link
+
+What: /sys/kernel/config/most_video/<link>
+Date: March 8, 2019
+KernelVersion: 5.2
+Description:
+ The attributes:
+
+ buffer_size configure the buffer size for this channel
+
+ subbuffer_size configure the sub-buffer size for this channel
+ (needed for synchronous and isochrnous data)
+
+
+ num_buffers configure number of buffers used for this
+ channel
+
+ datatype configure type of data that will travel over
+ this channel
+
+ direction configure whether this link will be an input
+ or output
+
+ dbr_size configure DBR data buffer size (this is used
+ for MediaLB communiction only)
+
+ packets_per_xact
+ configure the number of packets that will be
+ collected from the network before being
+ transmitted via USB (this is used for USB
+ communiction only)
+
+ device name of the device the link is to be attached to
+
+ channel name of the channel the link is to be attached to
+
+ comp_params pass parameters needed by some components
+
+ create_link write '1' to this attribute to trigger the
+ creation of the link. In case of speculative
+ configuration, the creation is post-poned until
+ a physical device is being attached to the bus.
+
+ destroy_link write '1' to this attribute to destroy an
+ active link
+
+What: /sys/kernel/config/most_net/<link>
+Date: March 8, 2019
+KernelVersion: 5.2
+Description:
+ The attributes:
+
+ buffer_size configure the buffer size for this channel
+
+ subbuffer_size configure the sub-buffer size for this channel
+ (needed for synchronous and isochrnous data)
+
+
+ num_buffers configure number of buffers used for this
+ channel
+
+ datatype configure type of data that will travel over
+ this channel
+
+ direction configure whether this link will be an input
+ or output
+
+ dbr_size configure DBR data buffer size (this is used
+ for MediaLB communiction only)
+
+ packets_per_xact
+ configure the number of packets that will be
+ collected from the network before being
+ transmitted via USB (this is used for USB
+ communiction only)
+
+ device name of the device the link is to be attached to
+
+ channel name of the channel the link is to be attached to
+
+ comp_params pass parameters needed by some components
+
+ create_link write '1' to this attribute to trigger the
+ creation of the link. In case of speculative
+ configuration, the creation is post-poned until
+ a physical device is being attached to the bus.
+
+ destroy_link write '1' to this attribute to destroy an
+ active link
+
+What: /sys/kernel/config/most_sound/<card>
+Date: March 8, 2019
+KernelVersion: 5.2
+Description:
+ The attributes:
+
+ create_card write '1' to this attribute to trigger the
+ registration of the sound card with the ALSA
+ subsystem.
+
+What: /sys/kernel/config/most_sound/<card>/<link>
+Date: March 8, 2019
+KernelVersion: 5.2
+Description:
+ The attributes:
+
+ buffer_size configure the buffer size for this channel
+
+ subbuffer_size configure the sub-buffer size for this channel
+ (needed for synchronous and isochrnous data)
+
+
+ num_buffers configure number of buffers used for this
+ channel
+
+ datatype configure type of data that will travel over
+ this channel
+
+ direction configure whether this link will be an input
+ or output
+
+ dbr_size configure DBR data buffer size (this is used
+ for MediaLB communiction only)
+
+ packets_per_xact
+ configure the number of packets that will be
+ collected from the network before being
+ transmitted via USB (this is used for USB
+ communiction only)
+
+ device name of the device the link is to be attached to
+
+ channel name of the channel the link is to be attached to
+
+ comp_params pass parameters needed by some components
+
+ create_link write '1' to this attribute to trigger the
+ creation of the link. In case of speculative
+ configuration, the creation is post-poned until
+ a physical device is being attached to the bus.
+
+ destroy_link write '1' to this attribute to destroy an
+ active link
+
+What: /sys/kernel/config/rdma_cm/<hca>/ports/<port-num>/default_roce_tos
+Date: March 8, 2019
+KernelVersion: 5.2
+Description: RDMA-CM QPs from HCA <hca> at port <port-num>
+ will be created with this TOS as default.
+ This can be overridden by using the rdma_set_option API.
+ The possible RoCE TOS values are 0-255.
diff --git a/drivers/staging/most/Documentation/driver_usage.txt b/drivers/staging/most/Documentation/driver_usage.txt
index da7a8f405b9b..56d79195bb3c 100644
--- a/drivers/staging/most/Documentation/driver_usage.txt
+++ b/drivers/staging/most/Documentation/driver_usage.txt
@@ -115,36 +115,75 @@ following components are available
Section 2 Usage of the MOST Driver
- Section 2.1 Configuration
-
-See ABI/sysfs-bus-most.txt
-
-
- Section 2.2 Routing Channels
-
-To connect a configured channel to a certain core component and make it
-accessible for user space applications, the driver attribute 'add_link' is
-used. The configuration string passed to it has the following format:
-
- "device_name:channel_name:component_name:link_name[.param]"
-
-It is the concatenation of up to four substrings separated by a colon. The
-substrings contain the names of the MOST interface, the channel, the
-component driver and a custom name with which the link is going to be
-referenced with. Since some components need additional information, the
-link name can be extended with a component-specific parameter (separated by
-a dot). In case the character device component is loaded, the handle would
-also appear as a device node in the /dev directory.
-
-Cdev component example:
- $ echo "mdev0:ep_81:cdev:my_rx_channel" >$(DRV_DIR)/add_link
-
-
-Sound component example:
-
-The sound component needs additional parameters to determine the audio
-resolution that is going to be used and to trigger the registration of a
-sound card with ALSA. The following audio formats are available:
+ Section 2.1 Configuration and Data Link
+
+The driver is to be configured via configfs. Each loaded component kernel
+object (see section 1.3) registers a subsystem with configfs, which is used to
+configure and establish communiction pathways (links) to attached devices on
+the bus. To do so, the user has to descend into the component's configuration
+directory and create a new directory (child config itmes). The name of this
+directory will be used as a reference for the link and it will contain the
+following attributes:
+
+ - buffer_size
+ configure the buffer size for this channel
+ - subbuffer_size
+ configure the sub-buffer size for this channel (needed for
+ synchronous and isochrnous data)
+ - num_buffers
+ configure number of buffers used for this channel
+ - datatype
+ configure type of data that will travel over this channel
+ - direction
+ configure whether this link will be an input or output
+ - dbr_size
+ configure DBR data buffer size (this is used for MediaLB communiction
+ only)
+ - packets_per_xact
+ configure the number of packets that will be collected from the
+ network before being transmitted via USB (this is used for USB
+ communiction only)
+ - device
+ name of the device the link is to be attached to
+ - channel
+ name of the channel the link is to be attached to
+ - comp_params
+ pass parameters needed by some components
+ - create_link
+ write '1' to this attribute to trigger the creation of the link. In
+ case of speculative configuration, the creation is post-poned until
+ a physical device is being attached to the bus.
+ - destroy_link
+ write '1' to this attribute to destroy an already established link
+
+
+See ABI/sysfs-bus-most.txt and ABI/configfs-most.txt
+
+
+ Section 2.2 Configure a Sound Card
+
+Setting up synchronous channels to be mapped as an ALSA sound adapter is a two
+step process. Firstly, a directory (child config group) has to be created
+inside the most_sound's configuration directory. This adapter dir will
+represent the sound adapter. The name of the directory is for user reference
+only and has no further influence, as all sound adapters will be given a static
+name in ALSA. The sound adapter will have the following attribute:
+
+ - create_card
+ write '1' to this attribute to trigger the registration of the card
+ with the ALSA subsystem.
+ In case of speculative configuration, the creation is post-poned
+ until a physical device is being attached to the bus.
+
+Secondly, links will have to be created inside the adapter dir as described in
+section 2.1. These links will become the PCM devices of the sound card. The
+name of a PCM device will be inherited from the directory name. When all
+channels have been configured and created, the sound card itself can be created
+by writing '1' to the create_card attribute.
+
+The sound component needs an additional parameter to determine the audio
+resolution that is going to be used.
+The following audio formats are available:
- "1x8" (Mono)
- "2x16" (16-bit stereo)
@@ -152,18 +191,8 @@ sound card with ALSA. The following audio formats are available:
- "2x32" (32-bit stereo)
- "6x16" (16-bit surround 5.1)
-To make the sound module create a sound card and register it with ALSA the
-string "create" needs to be attached to the module parameter section of the
-configuration string. To create a sound card with with two playback devices
-(linked to channel ep01 and ep02) and one capture device (linked to channel
-ep83) the following is written to the add_link file:
-
- $ echo "mdev0:ep01:sound:most51_playback.6x16" >$(DRV_DIR)/add_link
- $ echo "mdev0:ep02:sound:most_playback.2x16" >$(DRV_DIR)/add_link
- $ echo "mdev0:ep83:sound:most_capture.2x16.create" >$(DRV_DIR)/add_link
-
-The link names (most51_playback, most_playback and most_capture) will
-become the names of the PCM devices of the sound card.
+The resolution string has to be written to the link directory's comp_params
+attribute.
Section 2.3 USB Padding
@@ -174,13 +203,13 @@ hardware, which is for performance optimization purposes of the USB
transmission.
When transmitting synchronous data the allocated channel width needs to be
-written to 'set_subbuffer_size'. Additionally, the number of MOST frames
-that should travel to the host within one USB transaction need to be
-written to 'packets_per_xact'.
+written to 'subbuffer_size'. Additionally, the number of MOST frames that
+should travel to the host within one USB transaction need to be written to
+'packets_per_xact'.
The driver, then, calculates the synchronous threshold as follows:
- frame_size = set_subbuffer_size * packets_per_xact
+ frame_size = subbuffer_size * packets_per_xact
In case 'packets_per_xact' is set to 0xFF the maximum number of packets,
allocated within one MOST frame, is calculated that fit into _one_ 512 byte
@@ -192,15 +221,15 @@ This frame_size is the number of synchronous data within an USB
transaction, which renders MTU_USB - frame_size bytes for padding.
When transmitting isochronous AVP data the desired packet size needs to be
-written to 'set_subbuffer_size' and hardware will always expect two
-isochronous packets within one USB transaction. This renders
+written to 'subbuffer_size' and hardware will always expect two isochronous
+packets within one USB transaction. This renders
- MTU_USB - (2 * set_subbuffer_size)
+ MTU_USB - (2 * subbuffer_size)
bytes for padding.
-Note that at least (2 * set_subbuffer_size) bytes for isochronous data or
-(set_subbuffer_size * packts_per_xact) bytes for synchronous data need to
+Note that at least (2 * subbuffer_size) bytes for isochronous data or
+(subbuffer_size * packts_per_xact) bytes for synchronous data need to
be put in the transmission buffer and passed to the driver.
Since adapter drivers are allowed to change a chosen configuration to best
diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig
index 535455c39fba..db32ea7d1743 100644
--- a/drivers/staging/most/Kconfig
+++ b/drivers/staging/most/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
menuconfig MOST
tristate "MOST support"
- depends on HAS_DMA
+ depends on HAS_DMA && CONFIGFS_FS
default n
---help---
Say Y here if you want to enable MOST support.
diff --git a/drivers/staging/most/Makefile b/drivers/staging/most/Makefile
index c7662f65f6db..85ea5a434ced 100644
--- a/drivers/staging/most/Makefile
+++ b/drivers/staging/most/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MOST) += most_core.o
most_core-y := core.o
+most_core-y += configfs.o
ccflags-y += -I $(srctree)/drivers/staging/
obj-$(CONFIG_MOST_CDEV) += cdev/
diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c
index f2b347cda8b7..d98977c57a4b 100644
--- a/drivers/staging/most/cdev/cdev.c
+++ b/drivers/staging/most/cdev/cdev.c
@@ -425,7 +425,7 @@ static int comp_tx_completion(struct most_interface *iface, int channel_id)
* Returns 0 on success or error code otherwise.
*/
static int comp_probe(struct most_interface *iface, int channel_id,
- struct most_channel_config *cfg, char *name)
+ struct most_channel_config *cfg, char *name, char *args)
{
struct comp_channel *c;
unsigned long cl_flags;
@@ -527,8 +527,13 @@ static int __init mod_init(void)
err = most_register_component(&comp.cc);
if (err)
goto free_cdev;
+ err = most_register_configfs_subsys(&comp.cc);
+ if (err)
+ goto deregister_comp;
return 0;
+deregister_comp:
+ most_deregister_component(&comp.cc);
free_cdev:
unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE);
dest_ida:
@@ -543,6 +548,7 @@ static void __exit mod_exit(void)
pr_info("exit module\n");
+ most_deregister_configfs_subsys(&comp.cc);
most_deregister_component(&comp.cc);
list_for_each_entry_safe(c, tmp, &channel_list, list) {
diff --git a/drivers/staging/most/configfs.c b/drivers/staging/most/configfs.c
new file mode 100644
index 000000000000..1d8bf29e0ffb
--- /dev/null
+++ b/drivers/staging/most/configfs.c
@@ -0,0 +1,676 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * configfs.c - Implementation of configfs interface to the driver stack
+ *
+ * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/configfs.h>
+#include <most/core.h>
+
+struct mdev_link {
+ struct config_item item;
+ struct list_head list;
+ bool create_link;
+ bool destroy_link;
+ u16 num_buffers;
+ u16 buffer_size;
+ u16 subbuffer_size;
+ u16 packets_per_xact;
+ u16 dbr_size;
+ char datatype[PAGE_SIZE];
+ char direction[PAGE_SIZE];
+ char name[PAGE_SIZE];
+ char device[PAGE_SIZE];
+ char channel[PAGE_SIZE];
+ char comp[PAGE_SIZE];
+ char comp_params[PAGE_SIZE];
+};
+
+static struct list_head mdev_link_list;
+
+static int set_cfg_buffer_size(struct mdev_link *link)
+{
+ if (!link->buffer_size)
+ return -ENODATA;
+ return most_set_cfg_buffer_size(link->device, link->channel,
+ link->buffer_size);
+}
+
+static int set_cfg_subbuffer_size(struct mdev_link *link)
+{
+ if (!link->subbuffer_size)
+ return -ENODATA;
+ return most_set_cfg_subbuffer_size(link->device, link->channel,
+ link->subbuffer_size);
+}
+
+static int set_cfg_dbr_size(struct mdev_link *link)
+{
+ if (!link->dbr_size)
+ return -ENODATA;
+ return most_set_cfg_dbr_size(link->device, link->channel,
+ link->dbr_size);
+}
+
+static int set_cfg_num_buffers(struct mdev_link *link)
+{
+ if (!link->num_buffers)
+ return -ENODATA;
+ return most_set_cfg_num_buffers(link->device, link->channel,
+ link->num_buffers);
+}
+
+static int set_cfg_packets_xact(struct mdev_link *link)
+{
+ if (!link->packets_per_xact)
+ return -ENODATA;
+ return most_set_cfg_packets_xact(link->device, link->channel,
+ link->packets_per_xact);
+}
+
+static int set_cfg_direction(struct mdev_link *link)
+{
+ if (!strlen(link->direction))
+ return -ENODATA;
+ return most_set_cfg_direction(link->device, link->channel,
+ link->direction);
+}
+
+static int set_cfg_datatype(struct mdev_link *link)
+{
+ if (!strlen(link->datatype))
+ return -ENODATA;
+ return most_set_cfg_datatype(link->device, link->channel,
+ link->datatype);
+}
+
+static int (*set_config_val[])(struct mdev_link *link) = {
+ set_cfg_buffer_size,
+ set_cfg_subbuffer_size,
+ set_cfg_dbr_size,
+ set_cfg_num_buffers,
+ set_cfg_packets_xact,
+ set_cfg_direction,
+ set_cfg_datatype,
+};
+
+static struct mdev_link *to_mdev_link(struct config_item *item)
+{
+ return container_of(item, struct mdev_link, item);
+}
+
+static int set_config_and_add_link(struct mdev_link *mdev_link)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(set_config_val); i++) {
+ ret = set_config_val[i](mdev_link);
+ if (ret < 0 && ret != -ENODEV) {
+ pr_err("Config failed\n");
+ return ret;
+ }
+ }
+
+ return most_add_link(mdev_link->device, mdev_link->channel,
+ mdev_link->comp, mdev_link->name,
+ mdev_link->comp_params);
+}
+
+static ssize_t mdev_link_create_link_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ bool tmp;
+ int ret;
+
+ ret = kstrtobool(page, &tmp);
+ if (ret)
+ return ret;
+ if (!tmp)
+ return count;
+ ret = set_config_and_add_link(mdev_link);
+ if (ret && ret != -ENODEV)
+ return ret;
+ list_add_tail(&mdev_link->list, &mdev_link_list);
+ mdev_link->create_link = tmp;
+ return count;
+}
+
+static ssize_t mdev_link_destroy_link_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ bool tmp;
+ int ret;
+
+ ret = kstrtobool(page, &tmp);
+ if (ret)
+ return ret;
+ if (!tmp)
+ return count;
+ mdev_link->destroy_link = tmp;
+ ret = most_remove_link(mdev_link->device, mdev_link->channel,
+ mdev_link->comp);
+ if (ret)
+ return ret;
+ if (!list_empty(&mdev_link_list))
+ list_del(&mdev_link->list);
+ return count;
+}
+
+static ssize_t mdev_link_direction_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->direction);
+}
+
+static ssize_t mdev_link_direction_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+
+ if (!sysfs_streq(page, "dir_rx") && !sysfs_streq(page, "rx") &&
+ !sysfs_streq(page, "dir_tx") && !sysfs_streq(page, "tx"))
+ return -EINVAL;
+ strcpy(mdev_link->direction, page);
+ return count;
+}
+
+static ssize_t mdev_link_datatype_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->datatype);
+}
+
+static ssize_t mdev_link_datatype_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+
+ if (!sysfs_streq(page, "control") && !sysfs_streq(page, "async") &&
+ !sysfs_streq(page, "sync") && !sysfs_streq(page, "isoc") &&
+ !sysfs_streq(page, "isoc_avp"))
+ return -EINVAL;
+ strcpy(mdev_link->datatype, page);
+ return count;
+}
+
+static ssize_t mdev_link_device_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->device);
+}
+
+static ssize_t mdev_link_device_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+
+ strcpy(mdev_link->device, page);
+ return count;
+}
+
+static ssize_t mdev_link_channel_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->channel);
+}
+
+static ssize_t mdev_link_channel_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+
+ strcpy(mdev_link->channel, page);
+ return count;
+}
+
+static ssize_t mdev_link_comp_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->comp);
+}
+
+static ssize_t mdev_link_comp_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+
+ strcpy(mdev_link->comp, page);
+ return count;
+}
+
+static ssize_t mdev_link_comp_params_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n",
+ to_mdev_link(item)->comp_params);
+}
+
+static ssize_t mdev_link_comp_params_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+
+ strcpy(mdev_link->comp_params, page);
+ return count;
+}
+
+static ssize_t mdev_link_num_buffers_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%d\n",
+ to_mdev_link(item)->num_buffers);
+}
+
+static ssize_t mdev_link_num_buffers_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ int ret;
+
+ ret = kstrtou16(page, 0, &mdev_link->num_buffers);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t mdev_link_buffer_size_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%d\n",
+ to_mdev_link(item)->buffer_size);
+}
+
+static ssize_t mdev_link_buffer_size_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ int ret;
+
+ ret = kstrtou16(page, 0, &mdev_link->buffer_size);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t mdev_link_subbuffer_size_show(struct config_item *item,
+ char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%d\n",
+ to_mdev_link(item)->subbuffer_size);
+}
+
+static ssize_t mdev_link_subbuffer_size_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ int ret;
+
+ ret = kstrtou16(page, 0, &mdev_link->subbuffer_size);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t mdev_link_packets_per_xact_show(struct config_item *item,
+ char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%d\n",
+ to_mdev_link(item)->packets_per_xact);
+}
+
+static ssize_t mdev_link_packets_per_xact_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ int ret;
+
+ ret = kstrtou16(page, 0, &mdev_link->packets_per_xact);
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t mdev_link_dbr_size_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%d\n", to_mdev_link(item)->dbr_size);
+}
+
+static ssize_t mdev_link_dbr_size_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ int ret;
+
+ ret = kstrtou16(page, 0, &mdev_link->dbr_size);
+ if (ret)
+ return ret;
+ return count;
+}
+
+CONFIGFS_ATTR_WO(mdev_link_, create_link);
+CONFIGFS_ATTR_WO(mdev_link_, destroy_link);
+CONFIGFS_ATTR(mdev_link_, device);
+CONFIGFS_ATTR(mdev_link_, channel);
+CONFIGFS_ATTR(mdev_link_, comp);
+CONFIGFS_ATTR(mdev_link_, comp_params);
+CONFIGFS_ATTR(mdev_link_, num_buffers);
+CONFIGFS_ATTR(mdev_link_, buffer_size);
+CONFIGFS_ATTR(mdev_link_, subbuffer_size);
+CONFIGFS_ATTR(mdev_link_, packets_per_xact);
+CONFIGFS_ATTR(mdev_link_, datatype);
+CONFIGFS_ATTR(mdev_link_, direction);
+CONFIGFS_ATTR(mdev_link_, dbr_size);
+
+static struct configfs_attribute *mdev_link_attrs[] = {
+ &mdev_link_attr_create_link,
+ &mdev_link_attr_destroy_link,
+ &mdev_link_attr_device,
+ &mdev_link_attr_channel,
+ &mdev_link_attr_comp,
+ &mdev_link_attr_comp_params,
+ &mdev_link_attr_num_buffers,
+ &mdev_link_attr_buffer_size,
+ &mdev_link_attr_subbuffer_size,
+ &mdev_link_attr_packets_per_xact,
+ &mdev_link_attr_datatype,
+ &mdev_link_attr_direction,
+ &mdev_link_attr_dbr_size,
+ NULL,
+};
+
+static void mdev_link_release(struct config_item *item)
+{
+ struct mdev_link *mdev_link = to_mdev_link(item);
+ int ret;
+
+ if (!list_empty(&mdev_link_list)) {
+ ret = most_remove_link(mdev_link->device, mdev_link->channel,
+ mdev_link->comp);
+ if (ret && (ret != -ENODEV))
+ pr_err("Removing link failed.\n");
+ list_del(&mdev_link->list);
+ }
+ kfree(to_mdev_link(item));
+}
+
+static struct configfs_item_operations mdev_link_item_ops = {
+ .release = mdev_link_release,
+};
+
+static const struct config_item_type mdev_link_type = {
+ .ct_item_ops = &mdev_link_item_ops,
+ .ct_attrs = mdev_link_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+struct most_common {
+ struct config_group group;
+};
+
+static struct most_common *to_most_common(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct most_common, group);
+}
+
+static struct config_item *most_common_make_item(struct config_group *group,
+ const char *name)
+{
+ struct mdev_link *mdev_link;
+
+ mdev_link = kzalloc(sizeof(*mdev_link), GFP_KERNEL);
+ if (!mdev_link)
+ return ERR_PTR(-ENOMEM);
+
+ config_item_init_type_name(&mdev_link->item, name,
+ &mdev_link_type);
+
+ if (!strcmp(group->cg_item.ci_namebuf, "most_cdev"))
+ strcpy(mdev_link->comp, "cdev");
+ else if (!strcmp(group->cg_item.ci_namebuf, "most_net"))
+ strcpy(mdev_link->comp, "net");
+ else if (!strcmp(group->cg_item.ci_namebuf, "most_video"))
+ strcpy(mdev_link->comp, "video");
+ strcpy(mdev_link->name, name);
+ return &mdev_link->item;
+}
+
+static void most_common_release(struct config_item *item)
+{
+ kfree(to_most_common(item));
+}
+
+static struct configfs_item_operations most_common_item_ops = {
+ .release = most_common_release,
+};
+
+static struct configfs_group_operations most_common_group_ops = {
+ .make_item = most_common_make_item,
+};
+
+static const struct config_item_type most_common_type = {
+ .ct_item_ops = &most_common_item_ops,
+ .ct_group_ops = &most_common_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem most_cdev_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "most_cdev",
+ .ci_type = &most_common_type,
+ },
+ },
+};
+
+static struct configfs_subsystem most_net_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "most_net",
+ .ci_type = &most_common_type,
+ },
+ },
+};
+
+static struct configfs_subsystem most_video_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "most_video",
+ .ci_type = &most_common_type,
+ },
+ },
+};
+
+struct most_snd_grp {
+ struct config_group group;
+ bool create_card;
+ struct list_head list;
+};
+
+static struct most_snd_grp *to_most_snd_grp(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct most_snd_grp, group);
+}
+
+static struct config_item *most_snd_grp_make_item(struct config_group *group,
+ const char *name)
+{
+ struct mdev_link *mdev_link;
+
+ mdev_link = kzalloc(sizeof(*mdev_link), GFP_KERNEL);
+ if (!mdev_link)
+ return ERR_PTR(-ENOMEM);
+
+ config_item_init_type_name(&mdev_link->item, name, &mdev_link_type);
+ mdev_link->create_link = 0;
+ strcpy(mdev_link->name, name);
+ strcpy(mdev_link->comp, "sound");
+ return &mdev_link->item;
+}
+
+static ssize_t most_snd_grp_create_card_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct most_snd_grp *snd_grp = to_most_snd_grp(item);
+ int ret;
+ bool tmp;
+
+ ret = kstrtobool(page, &tmp);
+ if (ret)
+ return ret;
+ if (tmp) {
+ ret = most_cfg_complete("sound");
+ if (ret)
+ return ret;
+ }
+ snd_grp->create_card = tmp;
+ return count;
+}
+
+CONFIGFS_ATTR_WO(most_snd_grp_, create_card);
+
+static struct configfs_attribute *most_snd_grp_attrs[] = {
+ &most_snd_grp_attr_create_card,
+ NULL,
+};
+
+static void most_snd_grp_release(struct config_item *item)
+{
+ struct most_snd_grp *group = to_most_snd_grp(item);
+
+ list_del(&group->list);
+ kfree(group);
+}
+
+static struct configfs_item_operations most_snd_grp_item_ops = {
+ .release = most_snd_grp_release,
+};
+
+static struct configfs_group_operations most_snd_grp_group_ops = {
+ .make_item = most_snd_grp_make_item,
+};
+
+static const struct config_item_type most_snd_grp_type = {
+ .ct_item_ops = &most_snd_grp_item_ops,
+ .ct_group_ops = &most_snd_grp_group_ops,
+ .ct_attrs = most_snd_grp_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+struct most_sound {
+ struct configfs_subsystem subsys;
+ struct list_head soundcard_list;
+};
+
+static struct config_group *most_sound_make_group(struct config_group *group,
+ const char *name)
+{
+ struct most_snd_grp *most;
+ struct most_sound *ms = container_of(to_configfs_subsystem(group),
+ struct most_sound, subsys);
+
+ list_for_each_entry(most, &ms->soundcard_list, list) {
+ if (!most->create_card) {
+ pr_info("adapter configuration still in progress.\n");
+ return ERR_PTR(-EPROTO);
+ }
+ }
+ most = kzalloc(sizeof(*most), GFP_KERNEL);
+ if (!most)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&most->group, name, &most_snd_grp_type);
+ list_add_tail(&most->list, &ms->soundcard_list);
+ return &most->group;
+}
+
+static struct configfs_group_operations most_sound_group_ops = {
+ .make_group = most_sound_make_group,
+};
+
+static const struct config_item_type most_sound_type = {
+ .ct_group_ops = &most_sound_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct most_sound most_sound_subsys = {
+ .subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "most_sound",
+ .ci_type = &most_sound_type,
+ },
+ },
+ },
+};
+
+int most_register_configfs_subsys(struct core_component *c)
+{
+ int ret;
+
+ if (!strcmp(c->name, "cdev"))
+ ret = configfs_register_subsystem(&most_cdev_subsys);
+ else if (!strcmp(c->name, "net"))
+ ret = configfs_register_subsystem(&most_net_subsys);
+ else if (!strcmp(c->name, "video"))
+ ret = configfs_register_subsystem(&most_video_subsys);
+ else if (!strcmp(c->name, "sound"))
+ ret = configfs_register_subsystem(&most_sound_subsys.subsys);
+ else
+ return -ENODEV;
+
+ if (ret) {
+ pr_err("Error %d while registering subsystem %s\n",
+ ret, c->name);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(most_register_configfs_subsys);
+
+void most_interface_register_notify(const char *mdev)
+{
+ bool register_snd_card = false;
+ struct mdev_link *mdev_link;
+
+ list_for_each_entry(mdev_link, &mdev_link_list, list) {
+ if (!strcmp(mdev_link->device, mdev)) {
+ set_config_and_add_link(mdev_link);
+ if (!strcmp(mdev_link->comp, "sound"))
+ register_snd_card = true;
+ }
+ }
+ if (register_snd_card)
+ most_cfg_complete("sound");
+}
+
+void most_deregister_configfs_subsys(struct core_component *c)
+{
+ if (!strcmp(c->name, "cdev"))
+ configfs_unregister_subsystem(&most_cdev_subsys);
+ else if (!strcmp(c->name, "net"))
+ configfs_unregister_subsystem(&most_net_subsys);
+ else if (!strcmp(c->name, "video"))
+ configfs_unregister_subsystem(&most_video_subsys);
+ else if (!strcmp(c->name, "sound"))
+ configfs_unregister_subsystem(&most_sound_subsys.subsys);
+}
+EXPORT_SYMBOL_GPL(most_deregister_configfs_subsys);
+
+int __init configfs_init(void)
+{
+ config_group_init(&most_cdev_subsys.su_group);
+ mutex_init(&most_cdev_subsys.su_mutex);
+
+ config_group_init(&most_net_subsys.su_group);
+ mutex_init(&most_net_subsys.su_mutex);
+
+ config_group_init(&most_video_subsys.su_group);
+ mutex_init(&most_video_subsys.su_mutex);
+
+ config_group_init(&most_sound_subsys.subsys.su_group);
+ mutex_init(&most_sound_subsys.subsys.su_mutex);
+
+ INIT_LIST_HEAD(&most_sound_subsys.soundcard_list);
+ INIT_LIST_HEAD(&mdev_link_list);
+
+ return 0;
+}
diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c
index 18936cdb1083..b1f7f707cacd 100644
--- a/drivers/staging/most/core.c
+++ b/drivers/staging/most/core.c
@@ -272,19 +272,6 @@ static ssize_t set_number_of_buffers_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers);
}
-static ssize_t set_number_of_buffers_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct most_channel *c = to_channel(dev);
- int ret = kstrtou16(buf, 0, &c->cfg.num_buffers);
-
- if (ret)
- return ret;
- return count;
-}
-
static ssize_t set_buffer_size_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -294,19 +281,6 @@ static ssize_t set_buffer_size_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size);
}
-static ssize_t set_buffer_size_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct most_channel *c = to_channel(dev);
- int ret = kstrtou16(buf, 0, &c->cfg.buffer_size);
-
- if (ret)
- return ret;
- return count;
-}
-
static ssize_t set_direction_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -320,28 +294,6 @@ static ssize_t set_direction_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "unconfigured\n");
}
-static ssize_t set_direction_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct most_channel *c = to_channel(dev);
-
- if (!strcmp(buf, "dir_rx\n")) {
- c->cfg.direction = MOST_CH_RX;
- } else if (!strcmp(buf, "rx\n")) {
- c->cfg.direction = MOST_CH_RX;
- } else if (!strcmp(buf, "dir_tx\n")) {
- c->cfg.direction = MOST_CH_TX;
- } else if (!strcmp(buf, "tx\n")) {
- c->cfg.direction = MOST_CH_TX;
- } else {
- pr_info("WARN: invalid attribute settings\n");
- return -EINVAL;
- }
- return count;
-}
-
static ssize_t set_datatype_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -356,28 +308,6 @@ static ssize_t set_datatype_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "unconfigured\n");
}
-static ssize_t set_datatype_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- int i;
- struct most_channel *c = to_channel(dev);
-
- for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) {
- if (!strcmp(buf, ch_data_type[i].name)) {
- c->cfg.data_type = ch_data_type[i].most_ch_data_type;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(ch_data_type)) {
- pr_info("WARN: invalid attribute settings\n");
- return -EINVAL;
- }
- return count;
-}
-
static ssize_t set_subbuffer_size_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -387,19 +317,6 @@ static ssize_t set_subbuffer_size_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size);
}
-static ssize_t set_subbuffer_size_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct most_channel *c = to_channel(dev);
- int ret = kstrtou16(buf, 0, &c->cfg.subbuffer_size);
-
- if (ret)
- return ret;
- return count;
-}
-
static ssize_t set_packets_per_xact_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -409,19 +326,6 @@ static ssize_t set_packets_per_xact_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact);
}
-static ssize_t set_packets_per_xact_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct most_channel *c = to_channel(dev);
- int ret = kstrtou16(buf, 0, &c->cfg.packets_per_xact);
-
- if (ret)
- return ret;
- return count;
-}
-
static ssize_t set_dbr_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -430,18 +334,6 @@ static ssize_t set_dbr_size_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.dbr_size);
}
-static ssize_t set_dbr_size_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct most_channel *c = to_channel(dev);
- int ret = kstrtou16(buf, 0, &c->cfg.dbr_size);
-
- if (ret)
- return ret;
- return count;
-}
-
#define to_dev_attr(a) container_of(a, struct device_attribute, attr)
static umode_t channel_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int index)
@@ -469,13 +361,13 @@ static DEVICE_ATTR_RO(number_of_stream_buffers);
static DEVICE_ATTR_RO(size_of_stream_buffer);
static DEVICE_ATTR_RO(size_of_packet_buffer);
static DEVICE_ATTR_RO(channel_starving);
-static DEVICE_ATTR_RW(set_buffer_size);
-static DEVICE_ATTR_RW(set_number_of_buffers);
-static DEVICE_ATTR_RW(set_direction);
-static DEVICE_ATTR_RW(set_datatype);
-static DEVICE_ATTR_RW(set_subbuffer_size);
-static DEVICE_ATTR_RW(set_packets_per_xact);
-static DEVICE_ATTR_RW(set_dbr_size);
+static DEVICE_ATTR_RO(set_buffer_size);
+static DEVICE_ATTR_RO(set_number_of_buffers);
+static DEVICE_ATTR_RO(set_direction);
+static DEVICE_ATTR_RO(set_datatype);
+static DEVICE_ATTR_RO(set_subbuffer_size);
+static DEVICE_ATTR_RO(set_packets_per_xact);
+static DEVICE_ATTR_RO(set_dbr_size);
static struct attribute *channel_attrs[] = {
DEV_ATTR(available_directions),
@@ -701,6 +593,7 @@ static struct most_channel *get_channel(char *mdev, char *mdev_ch)
static
inline int link_channel_to_component(struct most_channel *c,
struct core_component *comp,
+ char *name,
char *comp_param)
{
int ret;
@@ -714,7 +607,8 @@ inline int link_channel_to_component(struct most_channel *c,
return -ENOSPC;
*comp_ptr = comp;
- ret = comp->probe_channel(c->iface, c->channel_id, &c->cfg, comp_param);
+ ret = comp->probe_channel(c->iface, c->channel_id, &c->cfg, name,
+ comp_param);
if (ret) {
*comp_ptr = NULL;
return ret;
@@ -722,65 +616,118 @@ inline int link_channel_to_component(struct most_channel *c,
return 0;
}
-/**
- * add_link_store - store function for add_link attribute
- * @drv: device driver
- * @buf: buffer
- * @len: buffer length
- *
- * This parses the string given by buf and splits it into
- * four substrings. Note: last substring is optional. In case a cdev
- * component is loaded the optional 4th substring will make up the name of
- * device node in the /dev directory. If omitted, the device node will
- * inherit the channel's name within sysfs.
- *
- * Searches for (device, channel) pair and probes the component
- *
- * Example:
- * (1) echo "mdev0:ch6:cdev:my_rxchannel" >add_link
- * (2) echo "mdev1:ep81:cdev" >add_link
- *
- * (1) would create the device node /dev/my_rxchannel
- * (2) would create the device node /dev/mdev1-ep81
- */
-static ssize_t add_link_store(struct device_driver *drv,
- const char *buf,
- size_t len)
+int most_set_cfg_buffer_size(char *mdev, char *mdev_ch, u16 val)
{
- struct most_channel *c;
- struct core_component *comp;
- char buffer[STRING_SIZE];
- char *mdev;
- char *mdev_ch;
- char *comp_name;
- char *comp_param;
- char devnod_buf[STRING_SIZE];
- int ret;
- size_t max_len = min_t(size_t, len + 1, STRING_SIZE);
+ struct most_channel *c = get_channel(mdev, mdev_ch);
- strlcpy(buffer, buf, max_len);
- ret = split_string(buffer, &mdev, &mdev_ch, &comp_name, &comp_param);
- if (ret)
- return ret;
- comp = match_component(comp_name);
- if (!comp)
+ if (!c)
return -ENODEV;
- if (!comp_param || *comp_param == 0) {
- snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev,
- mdev_ch);
- comp_param = devnod_buf;
+ c->cfg.buffer_size = val;
+ return 0;
+}
+
+int most_set_cfg_subbuffer_size(char *mdev, char *mdev_ch, u16 val)
+{
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+
+ if (!c)
+ return -ENODEV;
+ c->cfg.subbuffer_size = val;
+ return 0;
+}
+
+int most_set_cfg_dbr_size(char *mdev, char *mdev_ch, u16 val)
+{
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+
+ if (!c)
+ return -ENODEV;
+ c->cfg.dbr_size = val;
+ return 0;
+}
+
+int most_set_cfg_num_buffers(char *mdev, char *mdev_ch, u16 val)
+{
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+
+ if (!c)
+ return -ENODEV;
+ c->cfg.num_buffers = val;
+ return 0;
+}
+
+int most_set_cfg_datatype(char *mdev, char *mdev_ch, char *buf)
+{
+ int i;
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+
+ if (!c)
+ return -ENODEV;
+ for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) {
+ if (!strcmp(buf, ch_data_type[i].name)) {
+ c->cfg.data_type = ch_data_type[i].most_ch_data_type;
+ break;
+ }
}
- c = get_channel(mdev, mdev_ch);
+ if (i == ARRAY_SIZE(ch_data_type))
+ pr_info("WARN: invalid attribute settings\n");
+ return 0;
+}
+
+int most_set_cfg_direction(char *mdev, char *mdev_ch, char *buf)
+{
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+
+ if (!c)
+ return -ENODEV;
+ if (!strcmp(buf, "dir_rx\n")) {
+ c->cfg.direction = MOST_CH_RX;
+ } else if (!strcmp(buf, "rx\n")) {
+ c->cfg.direction = MOST_CH_RX;
+ } else if (!strcmp(buf, "dir_tx\n")) {
+ c->cfg.direction = MOST_CH_TX;
+ } else if (!strcmp(buf, "tx\n")) {
+ c->cfg.direction = MOST_CH_TX;
+ } else {
+ pr_info("Invalid direction\n");
+ return -ENODATA;
+ }
+ return 0;
+}
+
+int most_set_cfg_packets_xact(char *mdev, char *mdev_ch, u16 val)
+{
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+
if (!c)
return -ENODEV;
+ c->cfg.packets_per_xact = val;
+ return 0;
+}
+
+int most_cfg_complete(char *comp_name)
+{
+ struct core_component *comp;
+
+ comp = match_component(comp_name);
+ if (!comp)
+ return -ENODEV;
- ret = link_channel_to_component(c, comp, comp_param);
- if (ret)
- return ret;
- return len;
+ return comp->cfg_complete();
}
+int most_add_link(char *mdev, char *mdev_ch, char *comp_name, char *link_name,
+ char *comp_param)
+{
+ struct most_channel *c = get_channel(mdev, mdev_ch);
+ struct core_component *comp = match_component(comp_name);
+
+ if (!c || !comp)
+ return -ENODEV;
+
+ return link_channel_to_component(c, comp, link_name, comp_param);
+}
/**
* remove_link_store - store function for remove_link attribute
* @drv: device driver
@@ -823,17 +770,36 @@ static ssize_t remove_link_store(struct device_driver *drv,
return len;
}
+int most_remove_link(char *mdev, char *mdev_ch, char *comp_name)
+{
+ struct most_channel *c;
+ struct core_component *comp;
+
+ comp = match_component(comp_name);
+ if (!comp)
+ return -ENODEV;
+ c = get_channel(mdev, mdev_ch);
+ if (!c)
+ return -ENODEV;
+
+ if (comp->disconnect_channel(c->iface, c->channel_id))
+ return -EIO;
+ if (c->pipe0.comp == comp)
+ c->pipe0.comp = NULL;
+ if (c->pipe1.comp == comp)
+ c->pipe1.comp = NULL;
+ return 0;
+}
+
#define DRV_ATTR(_name) (&driver_attr_##_name.attr)
static DRIVER_ATTR_RO(links);
static DRIVER_ATTR_RO(components);
-static DRIVER_ATTR_WO(add_link);
static DRIVER_ATTR_WO(remove_link);
static struct attribute *mc_attrs[] = {
DRV_ATTR(links),
DRV_ATTR(components),
- DRV_ATTR(add_link),
DRV_ATTR(remove_link),
NULL,
};
@@ -1431,7 +1397,7 @@ int most_register_interface(struct most_interface *iface)
INIT_LIST_HEAD(&iface->p->channel_list);
iface->p->dev_id = id;
- snprintf(iface->p->name, STRING_SIZE, "mdev%d", id);
+ strcpy(iface->p->name, iface->description);
iface->dev.init_name = iface->p->name;
iface->dev.bus = &mc.bus;
iface->dev.parent = &mc.dev;
@@ -1487,6 +1453,7 @@ int most_register_interface(struct most_interface *iface)
}
pr_info("registered new device mdev%d (%s)\n",
id, iface->description);
+ most_interface_register_notify(iface->description);
return 0;
err_free_most_channel:
@@ -1621,7 +1588,7 @@ static int __init most_init(void)
err = -ENOMEM;
goto err_unregister_driver;
}
-
+ configfs_init();
return 0;
err_unregister_driver:
diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h
index 64cc02f161e7..652aaa771029 100644
--- a/drivers/staging/most/core.h
+++ b/drivers/staging/most/core.h
@@ -266,11 +266,13 @@ struct core_component {
struct list_head list;
const char *name;
int (*probe_channel)(struct most_interface *iface, int channel_idx,
- struct most_channel_config *cfg, char *name);
+ struct most_channel_config *cfg, char *name,
+ char *param);
int (*disconnect_channel)(struct most_interface *iface,
int channel_idx);
int (*rx_completion)(struct mbo *mbo);
int (*tx_completion)(struct most_interface *iface, int channel_idx);
+ int (*cfg_complete)(void);
};
/**
@@ -318,5 +320,19 @@ int most_start_channel(struct most_interface *iface, int channel_idx,
struct core_component *comp);
int most_stop_channel(struct most_interface *iface, int channel_idx,
struct core_component *comp);
-
+int __init configfs_init(void);
+int most_register_configfs_subsys(struct core_component *comp);
+void most_deregister_configfs_subsys(struct core_component *comp);
+int most_add_link(char *mdev, char *mdev_ch, char *comp_name, char *link_name,
+ char *comp_param);
+int most_remove_link(char *mdev, char *mdev_ch, char *comp_name);
+int most_set_cfg_buffer_size(char *mdev, char *mdev_ch, u16 val);
+int most_set_cfg_subbuffer_size(char *mdev, char *mdev_ch, u16 val);
+int most_set_cfg_dbr_size(char *mdev, char *mdev_ch, u16 val);
+int most_set_cfg_num_buffers(char *mdev, char *mdev_ch, u16 val);
+int most_set_cfg_datatype(char *mdev, char *mdev_ch, char *buf);
+int most_set_cfg_direction(char *mdev, char *mdev_ch, char *buf);
+int most_set_cfg_packets_xact(char *mdev, char *mdev_ch, u16 val);
+int most_cfg_complete(char *comp_name);
+void most_interface_register_notify(const char *mdev_name);
#endif /* MOST_CORE_H_ */
diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c
index e20584b1b112..c8a64e209027 100644
--- a/drivers/staging/most/net/net.c
+++ b/drivers/staging/most/net/net.c
@@ -293,7 +293,8 @@ static struct net_dev_context *get_net_dev_hold(struct most_interface *iface)
}
static int comp_probe_channel(struct most_interface *iface, int channel_idx,
- struct most_channel_config *ccfg, char *name)
+ struct most_channel_config *ccfg, char *name,
+ char *args)
{
struct net_dev_context *nd;
struct net_dev_channel *ch;
diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c
index 79ab3a78c5ec..7c998673a6f8 100644
--- a/drivers/staging/most/sound/sound.c
+++ b/drivers/staging/most/sound/sound.c
@@ -20,6 +20,7 @@
#include <most/core.h>
#define DRIVER_NAME "sound"
+#define STRING_SIZE 80
static struct core_component comp;
@@ -471,17 +472,11 @@ static const struct snd_pcm_ops pcm_ops = {
.page = snd_pcm_lib_get_vmalloc_page,
};
-static int split_arg_list(char *buf, char **device_name, u16 *ch_num,
- char **sample_res, u8 *create)
+static int split_arg_list(char *buf, u16 *ch_num, char **sample_res)
{
char *num;
int ret;
- *device_name = strsep(&buf, ".");
- if (!*device_name) {
- pr_err("Missing sound card name\n");
- return -EIO;
- }
num = strsep(&buf, "x");
if (!num)
goto err;
@@ -492,8 +487,6 @@ static int split_arg_list(char *buf, char **device_name, u16 *ch_num,
if (!*sample_res)
goto err;
- if (buf && !strcmp(buf, "create"))
- *create = 1;
return 0;
err:
@@ -579,7 +572,7 @@ static void release_adapter(struct sound_adapter *adpt)
*/
static int audio_probe_channel(struct most_interface *iface, int channel_id,
struct most_channel_config *cfg,
- char *arg_list)
+ char *device_name, char *arg_list)
{
struct channel *channel;
struct sound_adapter *adpt;
@@ -588,10 +581,9 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id,
int capture_count = 0;
int ret;
int direction;
- char *device_name;
u16 ch_num;
- u8 create = 0;
char *sample_res;
+ char arg_list_cpy[STRING_SIZE];
if (!iface)
return -EINVAL;
@@ -600,9 +592,8 @@ static int audio_probe_channel(struct most_interface *iface, int channel_id,
pr_err("Incompatible channel type\n");
return -EINVAL;
}
-
- ret = split_arg_list(arg_list, &device_name, &ch_num, &sample_res,
- &create);
+ strlcpy(arg_list_cpy, arg_list, STRING_SIZE);
+ ret = split_arg_list(arg_list_cpy, &ch_num, &sample_res);
if (ret < 0)
return ret;
@@ -673,12 +664,6 @@ skip_adpt_alloc:
strscpy(pcm->name, device_name, sizeof(pcm->name));
snd_pcm_set_ops(pcm, direction, &pcm_ops);
- if (create) {
- ret = snd_card_register(adpt->card);
- if (ret < 0)
- goto err_free_adpt;
- adpt->registered = true;
- }
return 0;
err_free_adpt:
@@ -686,6 +671,26 @@ err_free_adpt:
return ret;
}
+static int audio_create_sound_card(void)
+{
+ int ret;
+ struct sound_adapter *adpt;
+
+ list_for_each_entry(adpt, &adpt_list, list) {
+ if (!adpt->registered)
+ goto adpt_alloc;
+ }
+ return -ENODEV;
+adpt_alloc:
+ ret = snd_card_register(adpt->card);
+ if (ret < 0) {
+ release_adapter(adpt);
+ return ret;
+ }
+ adpt->registered = true;
+ return 0;
+}
+
/**
* audio_disconnect_channel - function to disconnect a channel
* @iface: pointer to interface instance
@@ -782,20 +787,30 @@ static struct core_component comp = {
.disconnect_channel = audio_disconnect_channel,
.rx_completion = audio_rx_completion,
.tx_completion = audio_tx_completion,
+ .cfg_complete = audio_create_sound_card,
};
static int __init audio_init(void)
{
+ int ret;
+
pr_info("init()\n");
INIT_LIST_HEAD(&adpt_list);
- return most_register_component(&comp);
+ ret = most_register_component(&comp);
+ if (ret)
+ pr_err("Failed to register %s\n", comp.name);
+ ret = most_register_configfs_subsys(&comp);
+ if (ret)
+ pr_err("Failed to register %s configfs subsys\n", comp.name);
+ return ret;
}
static void __exit audio_exit(void)
{
pr_info("exit()\n");
+ most_deregister_configfs_subsys(&comp);
most_deregister_component(&comp);
}
diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c
index c0293d8d5934..360cb5b7a10b 100644
--- a/drivers/staging/most/usb/usb.c
+++ b/drivers/staging/most/usb/usb.c
@@ -1072,7 +1072,7 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
mdev->iface.num_channels = num_endpoints;
snprintf(mdev->description, sizeof(mdev->description),
- "usb_device %d-%s:%d.%d",
+ "%d-%s:%d.%d",
usb_dev->bus->busnum,
usb_dev->devpath,
usb_dev->config->desc.bConfigurationValue,
diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c
index ad7e28ab9a4f..adca250062e1 100644
--- a/drivers/staging/most/video/video.c
+++ b/drivers/staging/most/video/video.c
@@ -453,7 +453,8 @@ static void comp_v4l2_dev_release(struct v4l2_device *v4l2_dev)
}
static int comp_probe_channel(struct most_interface *iface, int channel_idx,
- struct most_channel_config *ccfg, char *name)
+ struct most_channel_config *ccfg, char *name,
+ char *args)
{
int ret;
struct most_video_dev *mdev = get_comp_dev(iface, channel_idx);
diff --git a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
index 118302c122ee..2576f179e30a 100644
--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
+++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c
@@ -11,11 +11,12 @@
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
#include <mt7621.h>
#include <ralink_regs.h>
#define RALINK_CLKCFG1 0x30
-#define CHIP_REV_MT7621_E2 0x0101
#define PCIE_PORT_CLK_EN(x) BIT(24 + (x))
@@ -95,24 +96,32 @@ struct mt7621_pci_phy_instance {
/**
* struct mt7621_pci_phy - Mt7621 Pcie PHY core
* @dev: pointer to device
+ * @regmap: kernel regmap pointer
* @phys: pointer to Mt7621 PHY device
* @nphys: number of PHY devices for this core
+ * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst'
+ * needs to be executed. Depends on chip revision.
*/
struct mt7621_pci_phy {
struct device *dev;
+ struct regmap *regmap;
struct mt7621_pci_phy_instance **phys;
int nphys;
+ bool bypass_pipe_rst;
};
-static inline u32 phy_read(struct mt7621_pci_phy_instance *instance, u32 reg)
+static inline u32 phy_read(struct mt7621_pci_phy *phy, u32 reg)
{
- return readl(instance->port_base + reg);
+ u32 val;
+
+ regmap_read(phy->regmap, reg, &val);
+
+ return val;
}
-static inline void phy_write(struct mt7621_pci_phy_instance *instance,
- u32 val, u32 reg)
+static inline void phy_write(struct mt7621_pci_phy *phy, u32 val, u32 reg)
{
- writel(val, instance->port_base + reg);
+ regmap_write(phy->regmap, reg, val);
}
static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy,
@@ -122,10 +131,10 @@ static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy,
RG_PE1_PIPE_REG : RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH;
u32 reg;
- reg = phy_read(instance, offset);
+ reg = phy_read(phy, offset);
reg &= ~(RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC);
reg |= (RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC);
- phy_write(instance, reg, offset);
+ phy_write(phy, reg, offset);
}
static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy,
@@ -139,72 +148,72 @@ static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy,
reg = (reg >> 6) & 0x7;
/* Set PCIe Port PHY to disable SSC */
/* Debug Xtal Type */
- val = phy_read(instance, RG_PE1_FRC_H_XTAL_REG);
+ val = phy_read(phy, RG_PE1_FRC_H_XTAL_REG);
val &= ~(RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE);
val |= RG_PE1_FRC_H_XTAL_TYPE;
val |= RG_PE1_H_XTAL_TYPE_VAL(0x00);
- phy_write(instance, val, RG_PE1_FRC_H_XTAL_REG);
+ phy_write(phy, val, RG_PE1_FRC_H_XTAL_REG);
/* disable port */
offset = (instance->index != 1) ?
RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
- val = phy_read(instance, offset);
+ val = phy_read(phy, offset);
val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
val |= RG_PE1_FRC_PHY_EN;
- phy_write(instance, val, offset);
+ phy_write(phy, val, offset);
/* Set Pre-divider ratio (for host mode) */
- val = phy_read(instance, RG_PE1_H_PLL_REG);
+ val = phy_read(phy, RG_PE1_H_PLL_REG);
val &= ~(RG_PE1_H_PLL_PREDIV);
if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
val |= RG_PE1_H_PLL_PREDIV_VAL(0x01);
- phy_write(instance, val, RG_PE1_H_PLL_REG);
+ phy_write(phy, val, RG_PE1_H_PLL_REG);
dev_info(dev, "Xtal is 40MHz\n");
} else { /* 25MHz | 20MHz Xtal */
val |= RG_PE1_H_PLL_PREDIV_VAL(0x00);
- phy_write(instance, val, RG_PE1_H_PLL_REG);
+ phy_write(phy, val, RG_PE1_H_PLL_REG);
if (reg >= 6) {
dev_info(dev, "Xtal is 25MHz\n");
/* Select feedback clock */
- val = phy_read(instance, RG_PE1_H_PLL_FBKSEL_REG);
+ val = phy_read(phy, RG_PE1_H_PLL_FBKSEL_REG);
val &= ~(RG_PE1_H_PLL_FBKSEL);
val |= RG_PE1_H_PLL_FBKSEL_VAL(0x01);
- phy_write(instance, val, RG_PE1_H_PLL_FBKSEL_REG);
+ phy_write(phy, val, RG_PE1_H_PLL_FBKSEL_REG);
/* DDS NCPO PCW (for host mode) */
- val = phy_read(instance, RG_PE1_H_LCDDS_SSC_PRD_REG);
+ val = phy_read(phy, RG_PE1_H_LCDDS_SSC_PRD_REG);
val &= ~(RG_PE1_H_LCDDS_SSC_PRD);
val |= RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18000000);
- phy_write(instance, val, RG_PE1_H_LCDDS_SSC_PRD_REG);
+ phy_write(phy, val, RG_PE1_H_LCDDS_SSC_PRD_REG);
/* DDS SSC dither period control */
- val = phy_read(instance, RG_PE1_H_LCDDS_SSC_PRD_REG);
+ val = phy_read(phy, RG_PE1_H_LCDDS_SSC_PRD_REG);
val &= ~(RG_PE1_H_LCDDS_SSC_PRD);
val |= RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18d);
- phy_write(instance, val, RG_PE1_H_LCDDS_SSC_PRD_REG);
+ phy_write(phy, val, RG_PE1_H_LCDDS_SSC_PRD_REG);
/* DDS SSC dither amplitude control */
- val = phy_read(instance, RG_PE1_H_LCDDS_SSC_DELTA_REG);
+ val = phy_read(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG);
val &= ~(RG_PE1_H_LCDDS_SSC_DELTA |
RG_PE1_H_LCDDS_SSC_DELTA1);
val |= RG_PE1_H_LCDDS_SSC_DELTA_VAL(0x4a);
val |= RG_PE1_H_LCDDS_SSC_DELTA1_VAL(0x4a);
- phy_write(instance, val, RG_PE1_H_LCDDS_SSC_DELTA_REG);
+ phy_write(phy, val, RG_PE1_H_LCDDS_SSC_DELTA_REG);
} else {
dev_info(dev, "Xtal is 20MHz\n");
}
}
/* DDS clock inversion */
- val = phy_read(instance, RG_PE1_LCDDS_CLK_PH_INV_REG);
+ val = phy_read(phy, RG_PE1_LCDDS_CLK_PH_INV_REG);
val &= ~(RG_PE1_LCDDS_CLK_PH_INV);
val |= RG_PE1_LCDDS_CLK_PH_INV;
- phy_write(instance, val, RG_PE1_LCDDS_CLK_PH_INV_REG);
+ phy_write(phy, val, RG_PE1_LCDDS_CLK_PH_INV_REG);
/* Set PLL bits */
- val = phy_read(instance, RG_PE1_H_PLL_REG);
+ val = phy_read(phy, RG_PE1_H_PLL_REG);
val &= ~(RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR |
RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN);
val |= RG_PE1_H_PLL_BC_VAL(0x02);
@@ -212,19 +221,19 @@ static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy,
val |= RG_PE1_H_PLL_IR_VAL(0x02);
val |= RG_PE1_H_PLL_IC_VAL(0x01);
val |= RG_PE1_PLL_DIVEN_VAL(0x02);
- phy_write(instance, val, RG_PE1_H_PLL_REG);
+ phy_write(phy, val, RG_PE1_H_PLL_REG);
- val = phy_read(instance, RG_PE1_H_PLL_BR_REG);
+ val = phy_read(phy, RG_PE1_H_PLL_BR_REG);
val &= ~(RG_PE1_H_PLL_BR);
val |= RG_PE1_H_PLL_BR_VAL(0x00);
- phy_write(instance, val, RG_PE1_H_PLL_BR_REG);
+ phy_write(phy, val, RG_PE1_H_PLL_BR_REG);
if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */
/* set force mode enable of da_pe1_mstckdiv */
- val = phy_read(instance, RG_PE1_MSTCKDIV_REG);
+ val = phy_read(phy, RG_PE1_MSTCKDIV_REG);
val &= ~(RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV);
val |= (RG_PE1_MSTCKDIV_VAL(0x01) | RG_PE1_FRC_MSTCKDIV);
- phy_write(instance, val, RG_PE1_MSTCKDIV_REG);
+ phy_write(phy, val, RG_PE1_MSTCKDIV_REG);
}
}
@@ -232,9 +241,8 @@ static int mt7621_pci_phy_init(struct phy *phy)
{
struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy);
struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
- u32 chip_rev_id = rt_sysc_r32(SYSC_REG_CHIP_REV);
- if ((chip_rev_id & 0xFFFF) == CHIP_REV_MT7621_E2)
+ if (mphy->bypass_pipe_rst)
mt7621_bypass_pipe_rst(mphy, instance);
mt7621_set_phy_for_ssc(mphy, instance);
@@ -245,15 +253,16 @@ static int mt7621_pci_phy_init(struct phy *phy)
static int mt7621_pci_phy_power_on(struct phy *phy)
{
struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy);
+ struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
u32 offset = (instance->index != 1) ?
RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
u32 val;
/* Enable PHY and disable force mode */
- val = phy_read(instance, offset);
+ val = phy_read(mphy, offset);
val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
val |= (RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
- phy_write(instance, val, offset);
+ phy_write(mphy, val, offset);
return 0;
}
@@ -261,15 +270,16 @@ static int mt7621_pci_phy_power_on(struct phy *phy)
static int mt7621_pci_phy_power_off(struct phy *phy)
{
struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy);
+ struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent);
u32 offset = (instance->index != 1) ?
RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH;
u32 val;
/* Disable PHY */
- val = phy_read(instance, offset);
+ val = phy_read(mphy, offset);
val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN);
val |= RG_PE1_FRC_PHY_EN;
- phy_write(instance, val, offset);
+ phy_write(mphy, val, offset);
return 0;
}
@@ -305,15 +315,25 @@ static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev,
return mt7621_phy->phys[args->args[0]]->phy;
}
+static const struct soc_device_attribute mt7621_pci_quirks_match[] = {
+ { .soc_id = "mt7621", .revision = "E2" }
+};
+
+static const struct regmap_config mt7621_pci_phy_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x700,
+};
+
static int mt7621_pci_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct device_node *child_np;
+ const struct soc_device_attribute *attr;
struct phy_provider *provider;
struct mt7621_pci_phy *phy;
- struct resource res;
- int port, ret;
+ struct resource *res;
+ int port;
void __iomem *port_base;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
@@ -326,38 +346,44 @@ static int mt7621_pci_phy_probe(struct platform_device *pdev)
if (!phy->phys)
return -ENOMEM;
+ attr = soc_device_match(mt7621_pci_quirks_match);
+ if (attr)
+ phy->bypass_pipe_rst = true;
+
phy->dev = dev;
platform_set_drvdata(pdev, phy);
- ret = of_address_to_resource(np, 0, &res);
- if (ret) {
- dev_err(dev, "failed to get address resource(id-%d)\n", port);
- return ret;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get address resource\n");
+ return -ENXIO;
}
- port_base = devm_ioremap_resource(dev, &res);
+ port_base = devm_ioremap_resource(dev, res);
if (IS_ERR(port_base)) {
dev_err(dev, "failed to remap phy regs\n");
return PTR_ERR(port_base);
}
+ phy->regmap = devm_regmap_init_mmio(phy->dev, port_base,
+ &mt7621_pci_phy_regmap_config);
+ if (IS_ERR(phy->regmap))
+ return PTR_ERR(phy->regmap);
+
for (port = 0; port < MAX_PHYS; port++) {
struct mt7621_pci_phy_instance *instance;
struct phy *pphy;
instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
- if (!instance) {
- ret = -ENOMEM;
- goto put_child;
- }
+ if (!instance)
+ return -ENOMEM;
phy->phys[port] = instance;
pphy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
- ret = PTR_ERR(phy);
- goto put_child;
+ return PTR_ERR(phy);
}
instance->port_base = port_base;
@@ -369,10 +395,6 @@ static int mt7621_pci_phy_probe(struct platform_device *pdev)
provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate);
return PTR_ERR_OR_ZERO(provider);
-
-put_child:
- of_node_put(child_np);
- return ret;
}
static const struct of_device_id mt7621_pci_phy_ids[] = {
diff --git a/drivers/staging/pi433/rf69.c b/drivers/staging/pi433/rf69.c
index 340ffa7ccf18..4cd16257f0aa 100644
--- a/drivers/staging/pi433/rf69.c
+++ b/drivers/staging/pi433/rf69.c
@@ -349,18 +349,51 @@ int rf69_disable_amplifier(struct spi_device *spi, u8 amplifier_mask)
int rf69_set_output_power_level(struct spi_device *spi, u8 power_level)
{
- // TODO: Dependency to PA0,1,2 setting
- power_level += 18;
+ u8 pa_level, ocp, test_pa1, test_pa2;
+ bool pa0, pa1, pa2, high_power;
+ u8 min_power_level;
+
+ // check register pa_level
+ pa_level = rf69_read_reg(spi, REG_PALEVEL);
+ pa0 = pa_level & MASK_PALEVEL_PA0;
+ pa1 = pa_level & MASK_PALEVEL_PA1;
+ pa2 = pa_level & MASK_PALEVEL_PA2;
+
+ // check high power mode
+ ocp = rf69_read_reg(spi, REG_OCP);
+ test_pa1 = rf69_read_reg(spi, REG_TESTPA1);
+ test_pa2 = rf69_read_reg(spi, REG_TESTPA2);
+ high_power = (ocp == 0x0f) && (test_pa1 == 0x5d) && (test_pa2 == 0x7c);
+
+ if (pa0 && !pa1 && !pa2) {
+ power_level += 18;
+ min_power_level = 0;
+ } else if (!pa0 && pa1 && !pa2) {
+ power_level += 18;
+ min_power_level = 16;
+ } else if (!pa0 && pa1 && pa2) {
+ if (high_power)
+ power_level += 11;
+ else
+ power_level += 14;
+ min_power_level = 16;
+ } else {
+ goto failed;
+ }
// check input value
- if (power_level > 0x1f) {
- dev_dbg(&spi->dev, "set: illegal input param");
- return -EINVAL;
- }
+ if (power_level > 0x1f)
+ goto failed;
+
+ if (power_level < min_power_level)
+ goto failed;
// write value
return rf69_read_mod_write(spi, REG_PALEVEL, MASK_PALEVEL_OUTPUT_POWER,
power_level);
+failed:
+ dev_dbg(&spi->dev, "set: illegal input param");
+ return -EINVAL;
}
int rf69_set_pa_ramp(struct spi_device *spi, enum pa_ramp pa_ramp)
diff --git a/drivers/staging/ralink-gdma/ralink-gdma.c b/drivers/staging/ralink-gdma/ralink-gdma.c
index d12ecdc01e61..de3e357b2640 100644
--- a/drivers/staging/ralink-gdma/ralink-gdma.c
+++ b/drivers/staging/ralink-gdma/ralink-gdma.c
@@ -164,12 +164,6 @@ static inline void gdma_dma_write(struct gdma_dma_dev *dma_dev,
writel(val, dma_dev->base + reg);
}
-static struct gdma_dma_desc *gdma_dma_alloc_desc(unsigned int num_sgs)
-{
- return kzalloc(sizeof(struct gdma_dma_desc) +
- sizeof(struct gdma_dma_sg) * num_sgs, GFP_ATOMIC);
-}
-
static enum gdma_dma_transfer_size gdma_dma_maxburst(u32 maxburst)
{
if (maxburst < 2)
@@ -526,7 +520,7 @@ static struct dma_async_tx_descriptor *gdma_dma_prep_slave_sg(
struct scatterlist *sg;
unsigned int i;
- desc = gdma_dma_alloc_desc(sg_len);
+ desc = kzalloc(struct_size(desc, sg, sg_len), GFP_ATOMIC);
if (!desc) {
dev_err(c->device->dev, "alloc sg decs error\n");
return NULL;
@@ -581,7 +575,7 @@ static struct dma_async_tx_descriptor *gdma_dma_prep_dma_memcpy(
xfer_count = GDMA_REG_CTRL0_TX_MASK;
num_periods = DIV_ROUND_UP(len, xfer_count);
- desc = gdma_dma_alloc_desc(num_periods);
+ desc = kzalloc(struct_size(desc, sg, num_periods), GFP_ATOMIC);
if (!desc) {
dev_err(c->device->dev, "alloc memcpy decs error\n");
return NULL;
@@ -626,7 +620,7 @@ static struct dma_async_tx_descriptor *gdma_dma_prep_dma_cyclic(
}
num_periods = buf_len / period_len;
- desc = gdma_dma_alloc_desc(num_periods);
+ desc = kzalloc(struct_size(desc, sg, num_periods), GFP_ATOMIC);
if (!desc) {
dev_err(c->device->dev, "alloc cyclic decs error\n");
return NULL;
diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c
index b7abe8c7c8cd..087f6c9a5826 100644
--- a/drivers/staging/rtl8188eu/core/rtw_recv.c
+++ b/drivers/staging/rtl8188eu/core/rtw_recv.c
@@ -632,14 +632,9 @@ static void count_rx_stats(struct adapter *padapter,
}
}
-int sta2sta_data_frame(
- struct adapter *adapter,
- struct recv_frame *precv_frame,
- struct sta_info **psta
-);
-
-int sta2sta_data_frame(struct adapter *adapter, struct recv_frame *precv_frame,
- struct sta_info **psta)
+static int sta2sta_data_frame(struct adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta)
{
int ret = _SUCCESS;
struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig
index 8b3da2b00cb3..11528d17bb3c 100644
--- a/drivers/staging/rtl8192e/Kconfig
+++ b/drivers/staging/rtl8192e/Kconfig
@@ -2,7 +2,6 @@
config RTLLIB
tristate "Support for rtllib wireless devices"
depends on WLAN && m
- default n
select LIB80211
help
If you have a wireless card that uses rtllib, say
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
index fece982475e8..c5e44bbe997c 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
@@ -11,7 +11,6 @@
bool rtl92e_send_cmd_pkt(struct net_device *dev, u32 type, const void *data,
u32 len)
{
-
bool rt_status = true;
struct r8192_priv *priv = rtllib_priv(dev);
u16 frag_length = 0, frag_offset = 0;
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
index 5d2f86afb24b..5215a0b5fd45 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
@@ -1619,5 +1619,4 @@ void rtl92e_scan_op_backup(struct net_device *dev, u8 Operation)
break;
}
}
-
}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
index bab41ab7c0a5..1d992d5c4e17 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
@@ -33,12 +33,10 @@ static void _rtl92e_parse_pci_configuration(struct pci_dev *pdev,
bool rtl92e_check_adapter(struct pci_dev *pdev, struct net_device *dev)
{
struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
- u16 VenderID;
u16 DeviceID;
u8 RevisionID;
u16 IrqLine;
- VenderID = pdev->vendor;
DeviceID = pdev->device;
RevisionID = pdev->revision;
pci_read_config_word(pdev, 0x3C, &IrqLine);
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h b/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
index 64d5359cf7e2..b7769bca9740 100644
--- a/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
@@ -107,13 +107,13 @@ typedef struct _HT_INFORMATION_ELE {
typedef enum _HT_SPEC_VER {
HT_SPEC_VER_IEEE = 0,
HT_SPEC_VER_EWC = 1,
-}HT_SPEC_VER, *PHT_SPEC_VER;
+} HT_SPEC_VER, *PHT_SPEC_VER;
typedef enum _HT_AGGRE_MODE_E {
HT_AGG_AUTO = 0,
HT_AGG_FORCE_ENABLE = 1,
HT_AGG_FORCE_DISABLE = 2,
-}HT_AGGRE_MODE_E, *PHT_AGGRE_MODE_E;
+} HT_AGGRE_MODE_E, *PHT_AGGRE_MODE_E;
/*
* The Data structure is used to keep HT related variables when card is
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c
index 4bb6c32b9bf0..418c92403904 100644
--- a/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c
@@ -373,7 +373,7 @@ bool GetTs(
if(!list_empty(pUnusedList)) {
(*ppTS) = list_entry(pUnusedList->next, struct ts_common_info, list);
list_del_init(&(*ppTS)->list);
- if(TxRxSelect==TX_DIR) {
+ if (TxRxSelect == TX_DIR) {
struct tx_ts_record *tmp = container_of(*ppTS, struct tx_ts_record, ts_common_info);
ResetTxTsEntry(tmp);
} else {
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c
index e370841a79ca..7c7267d0fc9e 100644
--- a/drivers/staging/rtl8712/rtl871x_mlme.c
+++ b/drivers/staging/rtl8712/rtl871x_mlme.c
@@ -1120,8 +1120,6 @@ int r8712_select_and_join_from_scan(struct mlme_priv *pmlmepriv)
}
pnetwork = container_of(pmlmepriv->pscanned,
struct wlan_network, list);
- if (!pnetwork)
- return _FAIL;
pmlmepriv->pscanned = pmlmepriv->pscanned->next;
if (pmlmepriv->assoc_by_bssid) {
dst_ssid = pnetwork->network.MacAddress;
diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c
index 0c8a050b2a81..bd75bca1ac6e 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c
@@ -44,7 +44,7 @@ u8 rtw_validate_ssid(struct ndis_802_11_ssid *ssid)
for (i = 0; i < ssid->SsidLength; i++) {
/* wifi, printable ascii code must be supported */
if (!((ssid->Ssid[i] >= 0x20) && (ssid->Ssid[i] <= 0x7e))) {
- RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("ssid has nonprintabl ascii\n"));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("ssid has non-printable ascii\n"));
ret = false;
break;
}
diff --git a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c
index b9d36db762a9..2474fa6fb49b 100644
--- a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c
+++ b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c
@@ -204,8 +204,7 @@ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
if (list_empty(&pfree_sta_queue->queue)) {
/* spin_unlock_bh(&(pfree_sta_queue->lock)); */
spin_unlock_bh(&(pstapriv->sta_hash_lock));
- psta = NULL;
- return psta;
+ return NULL;
} else {
psta = LIST_CONTAINOR(get_next(&pfree_sta_queue->queue), struct sta_info, list);
@@ -586,12 +585,10 @@ exit:
struct sta_info *rtw_get_bcmc_stainfo(struct adapter *padapter)
{
- struct sta_info *psta;
struct sta_priv *pstapriv = &padapter->stapriv;
u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- psta = rtw_get_stainfo(pstapriv, bc_addr);
- return psta;
+ return rtw_get_stainfo(pstapriv, bc_addr);
}
u8 rtw_access_ctrl(struct adapter *padapter, u8 *mac_addr)
diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c
index f1c91483ca07..e5f1153527b9 100644
--- a/drivers/staging/rtl8723bs/hal/hal_com.c
+++ b/drivers/staging/rtl8723bs/hal/hal_com.c
@@ -1618,14 +1618,14 @@ void rtw_get_raw_rssi_info(void *sel, struct adapter *padapter)
isCCKrate = psample_pkt_rssi->data_rate <= DESC_RATE11M;
if (isCCKrate)
- psample_pkt_rssi->mimo_singal_strength[0] = psample_pkt_rssi->pwdball;
+ psample_pkt_rssi->mimo_signal_strength[0] = psample_pkt_rssi->pwdball;
for (rf_path = 0; rf_path < pHalData->NumTotalRFPath; rf_path++) {
DBG_871X_SEL_NL(
sel,
- "RF_PATH_%d =>singal_strength:%d(%%), singal_quality:%d(%%)\n",
- rf_path, psample_pkt_rssi->mimo_singal_strength[rf_path],
- psample_pkt_rssi->mimo_singal_quality[rf_path]
+ "RF_PATH_%d =>signal_strength:%d(%%), signal_quality:%d(%%)\n",
+ rf_path, psample_pkt_rssi->mimo_signal_strength[rf_path],
+ psample_pkt_rssi->mimo_signal_quality[rf_path]
);
if (!isCCKrate) {
@@ -1651,11 +1651,11 @@ void rtw_dump_raw_rssi_info(struct adapter *padapter)
isCCKrate = psample_pkt_rssi->data_rate <= DESC_RATE11M;
if (isCCKrate)
- psample_pkt_rssi->mimo_singal_strength[0] = psample_pkt_rssi->pwdball;
+ psample_pkt_rssi->mimo_signal_strength[0] = psample_pkt_rssi->pwdball;
for (rf_path = 0; rf_path < pHalData->NumTotalRFPath; rf_path++) {
- DBG_871X("RF_PATH_%d =>singal_strength:%d(%%), singal_quality:%d(%%)"
- , rf_path, psample_pkt_rssi->mimo_singal_strength[rf_path], psample_pkt_rssi->mimo_singal_quality[rf_path]);
+ DBG_871X("RF_PATH_%d =>signal_strength:%d(%%), signal_quality:%d(%%)"
+ , rf_path, psample_pkt_rssi->mimo_signal_strength[rf_path], psample_pkt_rssi->mimo_signal_quality[rf_path]);
if (!isCCKrate) {
printk(", rx_ofdm_pwr:%d(dBm), rx_ofdm_snr:%d(dB)\n",
@@ -1682,8 +1682,8 @@ void rtw_store_phy_info(struct adapter *padapter, union recv_frame *prframe)
psample_pkt_rssi->pwr_all = pPhyInfo->recv_signal_power;
for (rf_path = 0; rf_path < pHalData->NumTotalRFPath; rf_path++) {
- psample_pkt_rssi->mimo_singal_strength[rf_path] = pPhyInfo->rx_mimo_signal_strength[rf_path];
- psample_pkt_rssi->mimo_singal_quality[rf_path] = pPhyInfo->rx_mimo_signal_quality[rf_path];
+ psample_pkt_rssi->mimo_signal_strength[rf_path] = pPhyInfo->rx_mimo_signal_strength[rf_path];
+ psample_pkt_rssi->mimo_signal_quality[rf_path] = pPhyInfo->rx_mimo_signal_quality[rf_path];
if (!isCCKrate) {
psample_pkt_rssi->ofdm_pwr[rf_path] = pPhyInfo->RxPwr[rf_path];
psample_pkt_rssi->ofdm_snr[rf_path] = pPhyInfo->RxSNR[rf_path];
diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c
index 828b328adab8..336764464e7d 100644
--- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c
+++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c
@@ -1723,7 +1723,7 @@ s8 phy_get_tx_pwr_lmt(struct adapter *adapter, u32 reg_pwr_tbl_sel,
idx_rate_sctn = get_rate_sctn_idx(data_rate);
if (band_type == BAND_ON_5G && idx_rate_sctn == 0)
- DBG_871X("Wrong rate 0x%x: No CCK in 5G Band\n", DataRate);
+ DBG_871X("Wrong rate 0x%x: No CCK in 5G Band\n", DataRate);
/* workaround for wrong index combination to obtain tx power limit, */
/* OFDM only exists in BW 20M */
@@ -1749,6 +1749,7 @@ s8 phy_get_tx_pwr_lmt(struct adapter *adapter, u32 reg_pwr_tbl_sel,
if (band_type == BAND_ON_2_4G) {
s8 limits[10] = {0}; u8 i = 0;
+
for (i = 0; i < MAX_REGULATION_NUM; i++)
limits[i] = hal_data->TxPwrLimit_2_4G[i]
[idx_bandwidth]
@@ -1766,6 +1767,7 @@ s8 phy_get_tx_pwr_lmt(struct adapter *adapter, u32 reg_pwr_tbl_sel,
} else if (band_type == BAND_ON_5G) {
s8 limits[10] = {0}; u8 i = 0;
+
for (i = 0; i < MAX_REGULATION_NUM; ++i)
limits[i] = hal_data->TxPwrLimit_5G[i]
[idx_bandwidth]
diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c
index 381065ed722c..e3f4307f3d20 100644
--- a/drivers/staging/rtl8723bs/hal/odm.c
+++ b/drivers/staging/rtl8723bs/hal/odm.c
@@ -691,7 +691,7 @@ void ODM_CmnInfoHook(PDM_ODM_T pDM_Odm, ODM_CMNINFO_E CmnInfo, void *pValue)
/* break; */
/* case ODM_CMNINFO_MAC_STATUS: */
- /* pDM_Odm->pMacInfo = (ODM_MAC_INFO *)pValue; */
+ /* pDM_Odm->pMacInfo = (struct odm_mac_status_info *)pValue; */
/* break; */
/* To remove the compiler warning, must add an empty default statement to handle the other values. */
default:
diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h
index 4f1aeb8ded29..6ba77bb70889 100644
--- a/drivers/staging/rtl8723bs/hal/odm.h
+++ b/drivers/staging/rtl8723bs/hal/odm.h
@@ -261,7 +261,7 @@ struct odm_packet_info {
bool is_beacon;
};
-typedef struct _ODM_Phy_Dbg_Info_ {
+struct odm_phy_dbg_info {
/* ODM Write, debug info */
s8 RxSNRdB[4];
u32 NumQryPhyStatus;
@@ -271,11 +271,11 @@ typedef struct _ODM_Phy_Dbg_Info_ {
/* Others */
s32 RxEVM[4];
-} ODM_PHY_DBG_INFO_T;
+};
-typedef struct _ODM_Mac_Status_Info_ {
+struct odm_mac_status_info {
u8 test;
-} ODM_MAC_INFO;
+};
typedef enum tag_Dynamic_ODM_Support_Ability_Type {
/* BB Team */
@@ -1092,11 +1092,11 @@ typedef struct DM_Out_Source_Dynamic_Mechanism_Structure {
/* Define ........... */
/* Latest packet phy info (ODM write) */
- ODM_PHY_DBG_INFO_T PhyDbgInfo;
+ struct odm_phy_dbg_info PhyDbgInfo;
/* PHY_INFO_88E PhyInfo; */
/* Latest packet phy info (ODM write) */
- ODM_MAC_INFO *pMacInfo;
+ struct odm_mac_status_info *pMacInfo;
/* MAC_INFO_88E MacInfo; */
/* Different Team independt structure?? */
diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c
index 050ef9f2be64..a60162046e5a 100644
--- a/drivers/staging/rtl8723bs/hal/sdio_ops.c
+++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c
@@ -425,7 +425,7 @@ static u32 sdio_read_port(
)
{
struct adapter *adapter;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
struct hal_com_data *hal;
u32 oldcnt;
s32 err;
@@ -473,7 +473,7 @@ static u32 sdio_write_port(
)
{
struct adapter *adapter;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
s32 err;
struct xmit_buf *xmitbuf = (struct xmit_buf *)mem;
@@ -1207,7 +1207,7 @@ u8 RecvOnePkt(struct adapter *adapter, u32 size)
{
struct recv_buf *recvbuf;
struct dvobj_priv *sddev;
- PSDIO_DATA psdio_data;
+ struct sdio_data *psdio;
struct sdio_func *func;
u8 res = false;
diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h
index d29cc18a0ecc..0fd84c93e72b 100644
--- a/drivers/staging/rtl8723bs/include/drv_types.h
+++ b/drivers/staging/rtl8723bs/include/drv_types.h
@@ -216,7 +216,6 @@ struct registry_priv
#define BSSID_SZ(field) sizeof(((struct wlan_bssid_ex *) 0)->field)
#include <drv_types_sdio.h>
-#define INTF_DATA SDIO_DATA
#define is_primary_adapter(adapter) (1)
#define get_iface_type(adapter) (IFACE_PORT0)
@@ -472,9 +471,8 @@ struct dvobj_priv
/*-------- below is for SDIO INTERFACE --------*/
-#ifdef INTF_DATA
- INTF_DATA intf_data;
-#endif
+struct sdio_data intf_data;
+
};
#define dvobj_to_pwrctl(dvobj) (&(dvobj->pwrctl_priv))
diff --git a/drivers/staging/rtl8723bs/include/drv_types_sdio.h b/drivers/staging/rtl8723bs/include/drv_types_sdio.h
index 23bf30ece2df..09263ad27ce9 100644
--- a/drivers/staging/rtl8723bs/include/drv_types_sdio.h
+++ b/drivers/staging/rtl8723bs/include/drv_types_sdio.h
@@ -16,7 +16,7 @@
#include <linux/mmc/card.h>
#endif
-typedef struct sdio_data
+struct sdio_data
{
u8 func_number;
@@ -26,6 +26,6 @@ typedef struct sdio_data
struct sdio_func *func;
void *sys_sdio_irq_thd;
-} SDIO_DATA, *PSDIO_DATA;
+};
#endif
diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h
index 9efb4dcb9d3a..74c028fbe8f7 100644
--- a/drivers/staging/rtl8723bs/include/ieee80211.h
+++ b/drivers/staging/rtl8723bs/include/ieee80211.h
@@ -202,7 +202,7 @@ enum NETWORK_TYPE
#define IsSupportedVHT(NetType) (((NetType) & (WIRELESS_11AC)) ? true : false)
-typedef struct ieee_param {
+struct ieee_param {
u32 cmd;
u8 sta_addr[ETH_ALEN];
union {
@@ -240,13 +240,13 @@ typedef struct ieee_param {
u8 buf[0];
} bcn_ie;
} u;
-}ieee_param;
+};
-typedef struct ieee_param_ex {
+struct ieee_param_ex {
u32 cmd;
u8 sta_addr[ETH_ALEN];
u8 data[0];
-}ieee_param_ex;
+};
struct sta_data{
u16 aid;
@@ -870,13 +870,6 @@ static inline int is_zero_mac_addr(const u8 *addr)
#define CFG_IEEE80211_RESERVE_FCS (1<<0)
#define CFG_IEEE80211_COMPUTE_FCS (1<<1)
-typedef struct tx_pending_t{
- int frag;
- struct ieee80211_txb *txb;
-}tx_pending_t;
-
-
-
#define MAXTID 16
#define IEEE_A (1<<0)
diff --git a/drivers/staging/rtl8723bs/include/rtw_recv.h b/drivers/staging/rtl8723bs/include/rtw_recv.h
index 7d54cf211315..5de946e66302 100644
--- a/drivers/staging/rtl8723bs/include/rtw_recv.h
+++ b/drivers/staging/rtl8723bs/include/rtw_recv.h
@@ -120,8 +120,8 @@ struct rx_raw_rssi
u8 pwdball;
s8 pwr_all;
- u8 mimo_singal_strength[4];/* in 0~100 index */
- u8 mimo_singal_quality[4];
+ u8 mimo_signal_strength[4];/* in 0~100 index */
+ u8 mimo_signal_quality[4];
s8 ofdm_pwr[4];
u8 ofdm_snr[4];
diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h
index 37f42b2f22f1..ea1396005a13 100644
--- a/drivers/staging/rtl8723bs/include/rtw_xmit.h
+++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h
@@ -83,7 +83,7 @@ do{\
#define TXDESC_OFFSET TXDESC_SIZE
-enum TXDESC_SC{
+enum TXDESC_SC {
SC_DONT_CARE = 0x00,
SC_UPPER = 0x01,
SC_LOWER = 0x02,
diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
index 6d02904de63f..052482554f74 100644
--- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
+++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
@@ -74,7 +74,7 @@ static void sd_sync_int_hdl(struct sdio_func *func)
static int sdio_alloc_irq(struct dvobj_priv *dvobj)
{
- PSDIO_DATA psdio_data;
+ struct sdio_data *psdio_data;
struct sdio_func *func;
int err;
@@ -102,7 +102,7 @@ static int sdio_alloc_irq(struct dvobj_priv *dvobj)
static void sdio_free_irq(struct dvobj_priv *dvobj)
{
- PSDIO_DATA psdio_data;
+ struct sdio_data *psdio_data;
struct sdio_func *func;
int err;
@@ -176,7 +176,7 @@ static void gpio_hostwakeup_free_irq(struct adapter *padapter)
static u32 sdio_init(struct dvobj_priv *dvobj)
{
- PSDIO_DATA psdio_data;
+ struct sdio_data *psdio_data;
struct sdio_func *func;
int err;
@@ -248,7 +248,7 @@ static struct dvobj_priv *sdio_dvobj_init(struct sdio_func *func)
{
int status = _FAIL;
struct dvobj_priv *dvobj = NULL;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
dvobj = devobj_init();
if (dvobj == NULL) {
@@ -327,7 +327,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct
int status = _FAIL;
struct net_device *pnetdev;
struct adapter *padapter = NULL;
- PSDIO_DATA psdio = &dvobj->intf_data;
+ struct sdio_data *psdio = &dvobj->intf_data;
padapter = vzalloc(sizeof(*padapter));
if (padapter == NULL) {
diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c b/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c
index 43a9d922e3aa..1787534487b4 100644
--- a/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c
+++ b/drivers/staging/rtl8723bs/os_dep/sdio_ops_linux.c
@@ -12,7 +12,7 @@
static bool rtw_sdio_claim_host_needed(struct sdio_func *func)
{
struct dvobj_priv *dvobj = sdio_get_drvdata(func);
- PSDIO_DATA sdio_data = &dvobj->intf_data;
+ struct sdio_data *sdio_data = &dvobj->intf_data;
if (sdio_data->sys_sdio_irq_thd && sdio_data->sys_sdio_irq_thd == current)
return false;
@@ -21,7 +21,7 @@ static bool rtw_sdio_claim_host_needed(struct sdio_func *func)
inline void rtw_sdio_set_irq_thd(struct dvobj_priv *dvobj, void *thd_hdl)
{
- PSDIO_DATA sdio_data = &dvobj->intf_data;
+ struct sdio_data *sdio_data = &dvobj->intf_data;
sdio_data->sys_sdio_irq_thd = thd_hdl;
}
@@ -30,7 +30,7 @@ u8 sd_f0_read8(struct intf_hdl *pintfhdl, u32 addr, s32 *err)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
u8 v = 0;
struct sdio_func *func;
@@ -67,7 +67,7 @@ s32 _sd_cmd52_read(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
int err = 0, i;
struct sdio_func *func;
@@ -102,7 +102,7 @@ s32 sd_cmd52_read(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
int err = 0;
struct sdio_func *func;
@@ -137,7 +137,7 @@ s32 _sd_cmd52_write(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
int err = 0, i;
struct sdio_func *func;
@@ -172,7 +172,7 @@ s32 sd_cmd52_write(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
int err = 0;
struct sdio_func *func;
@@ -202,7 +202,7 @@ u8 sd_read8(struct intf_hdl *pintfhdl, u32 addr, s32 *err)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
u8 v = 0;
struct sdio_func *func;
@@ -234,7 +234,7 @@ u32 sd_read32(struct intf_hdl *pintfhdl, u32 addr, s32 *err)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
u32 v = 0;
struct sdio_func *func;
bool claim_needed;
@@ -299,7 +299,7 @@ void sd_write8(struct intf_hdl *pintfhdl, u32 addr, u8 v, s32 *err)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
struct sdio_func *func;
bool claim_needed;
@@ -328,7 +328,7 @@ void sd_write32(struct intf_hdl *pintfhdl, u32 addr, u32 v, s32 *err)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
struct sdio_func *func;
bool claim_needed;
@@ -404,7 +404,7 @@ s32 _sd_read(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, void *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
int err = -EPERM;
struct sdio_func *func;
@@ -461,7 +461,7 @@ s32 sd_read(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, void *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
struct sdio_func *func;
bool claim_needed;
@@ -505,7 +505,7 @@ s32 _sd_write(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, void *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
struct sdio_func *func;
u32 size;
@@ -565,7 +565,7 @@ s32 sd_write(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, void *pdata)
{
struct adapter *padapter;
struct dvobj_priv *psdiodev;
- PSDIO_DATA psdio;
+ struct sdio_data *psdio;
struct sdio_func *func;
bool claim_needed;
s32 err = -EPERM;
diff --git a/drivers/staging/rtlwifi/base.c b/drivers/staging/rtlwifi/base.c
index b8358ebbf912..b8b89e4c54d4 100644
--- a/drivers/staging/rtlwifi/base.c
+++ b/drivers/staging/rtlwifi/base.c
@@ -1730,7 +1730,6 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_tid_data *tid_data;
struct rtl_sta_info *sta_entry = NULL;
if (!sta)
@@ -1743,7 +1742,6 @@ int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return -EINVAL;
sta_entry = (struct rtl_sta_info *)sta->drv_priv;
- tid_data = &sta_entry->tids[tid];
sta_entry->tids[tid].agg.agg_state = RTL_AGG_STOP;
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
@@ -2467,11 +2465,8 @@ bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
struct rtl_beacon_keys bcn_key = {};
struct rtl_beacon_keys *cur_bcn_key;
u8 *ht_cap;
- u8 ht_cap_len;
u8 *ht_oper;
- u8 ht_oper_len;
u8 *ds_param;
- u8 ds_param_len;
if (mac->opmode != NL80211_IFTYPE_STATION)
return false;
@@ -2502,18 +2497,15 @@ bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
/***** Parsing DS Param IE ******/
ds_param = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_DS_PARAMS);
- if (ds_param && !(ds_param[1] < sizeof(*ds_param))) {
- ds_param_len = ds_param[1];
+ if (ds_param && !(ds_param[1] < sizeof(*ds_param)))
bcn_key.bcn_channel = ds_param[2];
- } else {
+ else
ds_param = NULL;
- }
/***** Parsing HT Cap. IE ******/
ht_cap = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_CAPABILITY);
if (ht_cap && !(ht_cap[1] < sizeof(*ht_cap))) {
- ht_cap_len = ht_cap[1];
ht_cap_ie = (struct ieee80211_ht_cap *)&ht_cap[2];
bcn_key.ht_cap_info = ht_cap_ie->cap_info;
} else {
@@ -2523,12 +2515,10 @@ bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
/***** Parsing HT Info. IE ******/
ht_oper = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_OPERATION);
- if (ht_oper && !(ht_oper[1] < sizeof(*ht_oper))) {
- ht_oper_len = ht_oper[1];
+ if (ht_oper && !(ht_oper[1] < sizeof(*ht_oper)))
ht_oper_ie = (struct ieee80211_ht_operation *)&ht_oper[2];
- } else {
+ else
ht_oper = NULL;
- }
/* update bcn_key */
diff --git a/drivers/staging/rtlwifi/btcoexist/halbtc8822b1ant.c b/drivers/staging/rtlwifi/btcoexist/halbtc8822b1ant.c
index ade271cb4aab..32c797430512 100644
--- a/drivers/staging/rtlwifi/btcoexist/halbtc8822b1ant.c
+++ b/drivers/staging/rtlwifi/btcoexist/halbtc8822b1ant.c
@@ -4730,7 +4730,7 @@ void ex_btc8822b1ant_media_status_notify(struct btc_coexist *btcoexist, u8 type)
if (wifi_under_b_mode) {
RT_TRACE(
rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
- "[BTCoex], ********** (media status notity under b mode) **********\n");
+ "[BTCoex], ********** (media status notify under b mode) **********\n");
btcoexist->btc_write_1byte(btcoexist, 0x6cd,
0x00); /* CCK Tx */
btcoexist->btc_write_1byte(btcoexist, 0x6cf,
@@ -4738,7 +4738,7 @@ void ex_btc8822b1ant_media_status_notify(struct btc_coexist *btcoexist, u8 type)
} else {
RT_TRACE(
rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
- "[BTCoex], ********** (media status notity not under b mode) **********\n");
+ "[BTCoex], ********** (media status notify not under b mode) **********\n");
btcoexist->btc_write_1byte(btcoexist, 0x6cd,
0x00); /* CCK Tx */
btcoexist->btc_write_1byte(btcoexist, 0x6cf,
diff --git a/drivers/staging/rtlwifi/core.c b/drivers/staging/rtlwifi/core.c
index a9902818ae7e..1e4e858f7517 100644
--- a/drivers/staging/rtlwifi/core.c
+++ b/drivers/staging/rtlwifi/core.c
@@ -341,25 +341,25 @@ static u16 crc16_ccitt(u8 data, u16 crc)
u16 result;
for (i = 0; i < 8; i++) {
- crc_bit15 = ((crc & BIT(15)) ? 1 : 0);
- data_bit = (data & (BIT(0) << i) ? 1 : 0);
+ crc_bit15 = !!(crc & BIT(15));
+ data_bit = !!(data & BIT(i));
shift_in = crc_bit15 ^ data_bit;
result = crc << 1;
if (shift_in == 0)
- result &= (~BIT(0));
+ result &= ~BIT(0);
else
result |= BIT(0);
- crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in;
+ crc_bit11 = !!(crc & BIT(11)) ^ shift_in;
if (crc_bit11 == 0)
- result &= (~BIT(12));
+ result &= ~BIT(12);
else
result |= BIT(12);
- crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in;
+ crc_bit4 = !!(crc & BIT(4)) ^ shift_in;
if (crc_bit4 == 0)
- result &= (~BIT(5));
+ result &= ~BIT(5);
else
result |= BIT(5);
@@ -377,9 +377,7 @@ static u16 _calculate_wol_pattern_crc(u8 *pattern, u16 len)
for (i = 0; i < len; i++)
crc = crc16_ccitt(pattern[i], crc);
- crc = ~crc;
-
- return crc;
+ return ~crc;
}
static void _rtl_add_wowlan_patterns(struct ieee80211_hw *hw,
diff --git a/drivers/staging/rtlwifi/pci.c b/drivers/staging/rtlwifi/pci.c
index 4bb5703bd715..b97c76ec22ea 100644
--- a/drivers/staging/rtlwifi/pci.c
+++ b/drivers/staging/rtlwifi/pci.c
@@ -2134,7 +2134,7 @@ static int rtl_pci_intr_mode_msi(struct ieee80211_hw *hw)
if (ret < 0)
return ret;
- ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+ ret = request_irq(rtlpci->pdev->irq, _rtl_pci_interrupt,
IRQF_SHARED, KBUILD_MODNAME, hw);
if (ret < 0) {
pci_disable_msi(rtlpci->pdev);
@@ -2155,7 +2155,7 @@ static int rtl_pci_intr_mode_legacy(struct ieee80211_hw *hw)
struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
int ret;
- ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+ ret = request_irq(rtlpci->pdev->irq, _rtl_pci_interrupt,
IRQF_SHARED, KBUILD_MODNAME, hw);
if (ret < 0)
return ret;
diff --git a/drivers/staging/rtlwifi/rtl8822be/hw.c b/drivers/staging/rtlwifi/rtl8822be/hw.c
index dac22c21f821..1de4e903a4de 100644
--- a/drivers/staging/rtlwifi/rtl8822be/hw.c
+++ b/drivers/staging/rtlwifi/rtl8822be/hw.c
@@ -2141,8 +2141,6 @@ static void rtl8822be_update_hal_rate_mask(struct ieee80211_hw *hw,
u32 ratr_bitmap, ratr_bitmap_msb = 0;
u8 ratr_index;
enum wireless_mode wirelessmode = 0;
- u8 curtxbw_40mhz =
- (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0;
bool b_shortgi = false;
u8 rate_mask[7];
u8 macid = 0;
@@ -2153,11 +2151,8 @@ static void rtl8822be_update_hal_rate_mask(struct ieee80211_hw *hw,
RT_TRACE(rtlpriv, COMP_RATR, DBG_LOUD, "wireless mode = 0x%x\n",
wirelessmode);
- if (mac->opmode == NL80211_IFTYPE_STATION ||
- mac->opmode == NL80211_IFTYPE_MESH_POINT) {
- curtxbw_40mhz = mac->bw_40;
- } else if (mac->opmode == NL80211_IFTYPE_AP ||
- mac->opmode == NL80211_IFTYPE_ADHOC)
+ if (mac->opmode == NL80211_IFTYPE_AP ||
+ mac->opmode == NL80211_IFTYPE_ADHOC)
macid = sta->aid + 1;
if (wirelessmode == WIRELESS_MODE_N_5G ||
wirelessmode == WIRELESS_MODE_AC_5G ||
diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c
index b6a65b0c1896..488f2539aa9a 100644
--- a/drivers/staging/speakup/main.c
+++ b/drivers/staging/speakup/main.c
@@ -2319,6 +2319,7 @@ static void __exit speakup_exit(void)
unregister_keyboard_notifier(&keyboard_notifier_block);
unregister_vt_notifier(&vt_notifier_block);
speakup_unregister_devsynth();
+ speakup_cancel_selection();
speakup_cancel_paste();
del_timer_sync(&cursor_timer);
kthread_stop(speakup_task);
diff --git a/drivers/staging/speakup/selection.c b/drivers/staging/speakup/selection.c
index 0ed1fefee0e9..a8b4d0c5ab7e 100644
--- a/drivers/staging/speakup/selection.c
+++ b/drivers/staging/speakup/selection.c
@@ -9,178 +9,138 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/atomic.h>
+#include <linux/console.h>
#include "speakup.h"
-/* ------ cut and paste ----- */
-/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
-#define ishardspace(c) ((c) == ' ')
-
unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
-
-/* Variables for selection control. */
-/* must not be deallocated */
struct vc_data *spk_sel_cons;
-/* cleared by clear_selection */
-static int sel_start = -1;
-static int sel_end;
-static int sel_buffer_lth;
-static char *sel_buffer;
-static unsigned char sel_pos(int n)
-{
- return inverse_translate(spk_sel_cons,
- screen_glyph(spk_sel_cons, n), 0);
-}
+struct speakup_selection_work {
+ struct work_struct work;
+ struct tiocl_selection sel;
+ struct tty_struct *tty;
+};
void speakup_clear_selection(void)
{
- sel_start = -1;
+ console_lock();
+ clear_selection();
+ console_unlock();
}
-/* does screen address p correspond to character at LH/RH edge of screen? */
-static int atedge(const int p, int size_row)
+static void __speakup_set_selection(struct work_struct *work)
{
- return !(p % size_row) || !((p + 2) % size_row);
-}
+ struct speakup_selection_work *ssw =
+ container_of(work, struct speakup_selection_work, work);
-/* constrain v such that v <= u */
-static unsigned short limit(const unsigned short v, const unsigned short u)
-{
- return (v > u) ? u : v;
-}
+ struct tty_struct *tty;
+ struct tiocl_selection sel;
-int speakup_set_selection(struct tty_struct *tty)
-{
- int new_sel_start, new_sel_end;
- char *bp, *obp;
- int i, ps, pe;
- struct vc_data *vc = vc_cons[fg_console].d;
+ sel = ssw->sel;
- spk_xs = limit(spk_xs, vc->vc_cols - 1);
- spk_ys = limit(spk_ys, vc->vc_rows - 1);
- spk_xe = limit(spk_xe, vc->vc_cols - 1);
- spk_ye = limit(spk_ye, vc->vc_rows - 1);
- ps = spk_ys * vc->vc_size_row + (spk_xs << 1);
- pe = spk_ye * vc->vc_size_row + (spk_xe << 1);
+ /* this ensures we copy sel before releasing the lock below */
+ rmb();
- if (ps > pe) /* make sel_start <= sel_end */
- swap(ps, pe);
+ /* release the lock by setting tty of the struct to NULL */
+ tty = xchg(&ssw->tty, NULL);
if (spk_sel_cons != vc_cons[fg_console].d) {
- speakup_clear_selection();
spk_sel_cons = vc_cons[fg_console].d;
- dev_warn(tty->dev,
- "Selection: mark console not the same as cut\n");
- return -EINVAL;
+ pr_warn("Selection: mark console not the same as cut\n");
+ goto unref;
}
- new_sel_start = ps;
- new_sel_end = pe;
-
- /* select to end of line if on trailing space */
- if (new_sel_end > new_sel_start &&
- !atedge(new_sel_end, vc->vc_size_row) &&
- ishardspace(sel_pos(new_sel_end))) {
- for (pe = new_sel_end + 2; ; pe += 2)
- if (!ishardspace(sel_pos(pe)) ||
- atedge(pe, vc->vc_size_row))
- break;
- if (ishardspace(sel_pos(pe)))
- new_sel_end = pe;
- }
- if ((new_sel_start == sel_start) && (new_sel_end == sel_end))
- return 0; /* no action required */
-
- sel_start = new_sel_start;
- sel_end = new_sel_end;
- /* Allocate a new buffer before freeing the old one ... */
- bp = kmalloc((sel_end - sel_start) / 2 + 1, GFP_ATOMIC);
- if (!bp) {
- speakup_clear_selection();
- return -ENOMEM;
- }
- kfree(sel_buffer);
- sel_buffer = bp;
-
- obp = bp;
- for (i = sel_start; i <= sel_end; i += 2) {
- *bp = sel_pos(i);
- if (!ishardspace(*bp++))
- obp = bp;
- if (!((i + 2) % vc->vc_size_row)) {
- /* strip trailing blanks from line and add newline,
- * unless non-space at end of line.
- */
- if (obp != bp) {
- bp = obp;
- *bp++ = '\r';
- }
- obp = bp;
- }
+ console_lock();
+ set_selection_kernel(&sel, tty);
+ console_unlock();
+
+unref:
+ tty_kref_put(tty);
+}
+
+static struct speakup_selection_work speakup_sel_work = {
+ .work = __WORK_INITIALIZER(speakup_sel_work.work,
+ __speakup_set_selection)
+};
+
+int speakup_set_selection(struct tty_struct *tty)
+{
+ /* we get kref here first in order to avoid a subtle race when
+ * cancelling selection work. getting kref first establishes the
+ * invariant that if speakup_sel_work.tty is not NULL when
+ * speakup_cancel_selection() is called, it must be the case that a put
+ * kref is pending.
+ */
+ tty_kref_get(tty);
+ if (cmpxchg(&speakup_sel_work.tty, NULL, tty)) {
+ tty_kref_put(tty);
+ return -EBUSY;
}
- sel_buffer_lth = bp - sel_buffer;
+ /* now we have the 'lock' by setting tty member of
+ * speakup_selection_work. wmb() ensures that writes to
+ * speakup_sel_work don't happen before cmpxchg() above.
+ */
+ wmb();
+
+ speakup_sel_work.sel.xs = spk_xs + 1;
+ speakup_sel_work.sel.ys = spk_ys + 1;
+ speakup_sel_work.sel.xe = spk_xe + 1;
+ speakup_sel_work.sel.ye = spk_ye + 1;
+ speakup_sel_work.sel.sel_mode = TIOCL_SELCHAR;
+
+ schedule_work_on(WORK_CPU_UNBOUND, &speakup_sel_work.work);
+
return 0;
}
-struct speakup_paste_work {
- struct work_struct work;
+void speakup_cancel_selection(void)
+{
struct tty_struct *tty;
-};
+
+ cancel_work_sync(&speakup_sel_work.work);
+ /* setting to null so that if work fails to run and we cancel it,
+ * we can run it again without getting EBUSY forever from there on.
+ * we need to use xchg here to avoid race with speakup_set_selection()
+ */
+ tty = xchg(&speakup_sel_work.tty, NULL);
+ if (tty)
+ tty_kref_put(tty);
+}
static void __speakup_paste_selection(struct work_struct *work)
{
- struct speakup_paste_work *spw =
- container_of(work, struct speakup_paste_work, work);
- struct tty_struct *tty = xchg(&spw->tty, NULL);
- struct vc_data *vc = (struct vc_data *)tty->driver_data;
- int pasted = 0, count;
- struct tty_ldisc *ld;
- DECLARE_WAITQUEUE(wait, current);
-
- ld = tty_ldisc_ref(tty);
- if (!ld)
- goto tty_unref;
- tty_buffer_lock_exclusive(&vc->port);
-
- add_wait_queue(&vc->paste_wait, &wait);
- while (sel_buffer && sel_buffer_lth > pasted) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_throttled(tty)) {
- schedule();
- continue;
- }
- count = sel_buffer_lth - pasted;
- count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
- count);
- pasted += count;
- }
- remove_wait_queue(&vc->paste_wait, &wait);
- __set_current_state(TASK_RUNNING);
+ struct speakup_selection_work *ssw =
+ container_of(work, struct speakup_selection_work, work);
+ struct tty_struct *tty = xchg(&ssw->tty, NULL);
- tty_buffer_unlock_exclusive(&vc->port);
- tty_ldisc_deref(ld);
-tty_unref:
+ paste_selection(tty);
tty_kref_put(tty);
}
-static struct speakup_paste_work speakup_paste_work = {
+static struct speakup_selection_work speakup_paste_work = {
.work = __WORK_INITIALIZER(speakup_paste_work.work,
__speakup_paste_selection)
};
int speakup_paste_selection(struct tty_struct *tty)
{
- if (cmpxchg(&speakup_paste_work.tty, NULL, tty))
+ tty_kref_get(tty);
+ if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) {
+ tty_kref_put(tty);
return -EBUSY;
+ }
- tty_kref_get(tty);
schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work);
return 0;
}
void speakup_cancel_paste(void)
{
+ struct tty_struct *tty;
+
cancel_work_sync(&speakup_paste_work.work);
- tty_kref_put(speakup_paste_work.tty);
+ tty = xchg(&speakup_paste_work.tty, NULL);
+ if (tty)
+ tty_kref_put(tty);
}
diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h
index e4f4f00be2dc..74fe49c2c511 100644
--- a/drivers/staging/speakup/speakup.h
+++ b/drivers/staging/speakup/speakup.h
@@ -72,6 +72,7 @@ void synth_buffer_add(u16 ch);
void synth_buffer_clear(void);
void speakup_clear_selection(void);
int speakup_set_selection(struct tty_struct *tty);
+void speakup_cancel_selection(void);
int speakup_paste_selection(struct tty_struct *tty);
void speakup_cancel_paste(void);
void speakup_register_devsynth(void);
diff --git a/drivers/staging/vc04_services/bcm2835-camera/controls.c b/drivers/staging/vc04_services/bcm2835-camera/controls.c
index a2c55cb2192a..e39ab5fae724 100644
--- a/drivers/staging/vc04_services/bcm2835-camera/controls.c
+++ b/drivers/staging/vc04_services/bcm2835-camera/controls.c
@@ -930,49 +930,49 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD,
-100, 100, 0, 1, NULL,
MMAL_PARAMETER_SATURATION,
- &ctrl_set_rational,
+ ctrl_set_rational,
false
},
{
V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD,
-100, 100, 0, 1, NULL,
MMAL_PARAMETER_SHARPNESS,
- &ctrl_set_rational,
+ ctrl_set_rational,
false
},
{
V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD,
-100, 100, 0, 1, NULL,
MMAL_PARAMETER_CONTRAST,
- &ctrl_set_rational,
+ ctrl_set_rational,
false
},
{
V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD,
0, 100, 50, 1, NULL,
MMAL_PARAMETER_BRIGHTNESS,
- &ctrl_set_rational,
+ ctrl_set_rational,
false
},
{
V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU,
0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu,
MMAL_PARAMETER_ISO,
- &ctrl_set_iso,
+ ctrl_set_iso,
false
},
{
V4L2_CID_ISO_SENSITIVITY_AUTO, MMAL_CONTROL_TYPE_STD_MENU,
0, 1, V4L2_ISO_SENSITIVITY_AUTO, 1, NULL,
MMAL_PARAMETER_ISO,
- &ctrl_set_iso,
+ ctrl_set_iso,
false
},
{
V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD,
0, 1, 0, 1, NULL,
MMAL_PARAMETER_VIDEO_STABILISATION,
- &ctrl_set_value,
+ ctrl_set_value,
false
},
/* {
@@ -983,7 +983,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU,
~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL,
MMAL_PARAMETER_EXPOSURE_MODE,
- &ctrl_set_exposure,
+ ctrl_set_exposure,
false
},
/* todo this needs mixing in with set exposure
@@ -996,7 +996,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
/* Units of 100usecs */
1, 1 * 1000 * 10, 100 * 10, 1, NULL,
MMAL_PARAMETER_SHUTTER_SPEED,
- &ctrl_set_exposure,
+ ctrl_set_exposure,
false
},
{
@@ -1004,7 +1004,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
0, ARRAY_SIZE(ev_bias_qmenu) - 1,
(ARRAY_SIZE(ev_bias_qmenu) + 1) / 2 - 1, 0, ev_bias_qmenu,
MMAL_PARAMETER_EXPOSURE_COMP,
- &ctrl_set_value_ev,
+ ctrl_set_value_ev,
false
},
{
@@ -1012,7 +1012,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
0, 1,
0, 1, NULL,
0, /* Dummy MMAL ID as it gets mapped into FPS range*/
- &ctrl_set_exposure,
+ ctrl_set_exposure,
false
},
{
@@ -1020,7 +1020,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
MMAL_CONTROL_TYPE_STD_MENU,
~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL,
MMAL_PARAMETER_EXP_METERING_MODE,
- &ctrl_set_metering_mode,
+ ctrl_set_metering_mode,
false
},
{
@@ -1028,56 +1028,56 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
MMAL_CONTROL_TYPE_STD_MENU,
~0x3ff, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL,
MMAL_PARAMETER_AWB_MODE,
- &ctrl_set_awb_mode,
+ ctrl_set_awb_mode,
false
},
{
V4L2_CID_RED_BALANCE, MMAL_CONTROL_TYPE_STD,
1, 7999, 1000, 1, NULL,
MMAL_PARAMETER_CUSTOM_AWB_GAINS,
- &ctrl_set_awb_gains,
+ ctrl_set_awb_gains,
false
},
{
V4L2_CID_BLUE_BALANCE, MMAL_CONTROL_TYPE_STD,
1, 7999, 1000, 1, NULL,
MMAL_PARAMETER_CUSTOM_AWB_GAINS,
- &ctrl_set_awb_gains,
+ ctrl_set_awb_gains,
false
},
{
V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU,
0, 15, V4L2_COLORFX_NONE, 0, NULL,
MMAL_PARAMETER_IMAGE_EFFECT,
- &ctrl_set_image_effect,
+ ctrl_set_image_effect,
false
},
{
V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD,
0, 0xffff, 0x8080, 1, NULL,
MMAL_PARAMETER_COLOUR_EFFECT,
- &ctrl_set_colfx,
+ ctrl_set_colfx,
false
},
{
V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD,
0, 360, 0, 90, NULL,
MMAL_PARAMETER_ROTATION,
- &ctrl_set_rotate,
+ ctrl_set_rotate,
false
},
{
V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD,
0, 1, 0, 1, NULL,
MMAL_PARAMETER_MIRROR,
- &ctrl_set_flip,
+ ctrl_set_flip,
false
},
{
V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD,
0, 1, 0, 1, NULL,
MMAL_PARAMETER_MIRROR,
- &ctrl_set_flip,
+ ctrl_set_flip,
false
},
{
@@ -1085,14 +1085,14 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
0, ARRAY_SIZE(bitrate_mode_qmenu) - 1,
0, 0, bitrate_mode_qmenu,
MMAL_PARAMETER_RATECONTROL,
- &ctrl_set_bitrate_mode,
+ ctrl_set_bitrate_mode,
false
},
{
V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD,
25 * 1000, 25 * 1000 * 1000, 10 * 1000 * 1000, 25 * 1000, NULL,
MMAL_PARAMETER_VIDEO_BIT_RATE,
- &ctrl_set_bitrate,
+ ctrl_set_bitrate,
false
},
{
@@ -1100,7 +1100,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
1, 100,
30, 1, NULL,
MMAL_PARAMETER_JPEG_Q_FACTOR,
- &ctrl_set_image_encode_output,
+ ctrl_set_image_encode_output,
false
},
{
@@ -1108,7 +1108,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
0, ARRAY_SIZE(mains_freq_qmenu) - 1,
1, 1, mains_freq_qmenu,
MMAL_PARAMETER_FLICKER_AVOID,
- &ctrl_set_flicker_avoidance,
+ ctrl_set_flicker_avoidance,
false
},
{
@@ -1116,7 +1116,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
0, 1,
0, 1, NULL,
MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER,
- &ctrl_set_video_encode_param_output,
+ ctrl_set_video_encode_param_output,
true /* Errors ignored as requires latest firmware to work */
},
{
@@ -1129,7 +1129,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 1, NULL,
MMAL_PARAMETER_PROFILE,
- &ctrl_set_video_encode_profile_level,
+ ctrl_set_video_encode_profile_level,
false
},
{
@@ -1149,7 +1149,7 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
V4L2_MPEG_VIDEO_H264_LEVEL_4_0, 1, NULL,
MMAL_PARAMETER_PROFILE,
- &ctrl_set_video_encode_profile_level,
+ ctrl_set_video_encode_profile_level,
false
},
{
@@ -1158,14 +1158,14 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = {
V4L2_SCENE_MODE_TEXT,
V4L2_SCENE_MODE_NONE, 1, NULL,
MMAL_PARAMETER_PROFILE,
- &ctrl_set_scene_mode,
+ ctrl_set_scene_mode,
false
},
{
V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, MMAL_CONTROL_TYPE_STD,
0, 0x7FFFFFFF, 60, 1, NULL,
MMAL_PARAMETER_INTRAPERIOD,
- &ctrl_set_video_encode_param_output,
+ ctrl_set_video_encode_param_output,
false
},
};
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index 31eb7d5889f1..a9a22917ecdb 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -179,6 +179,9 @@ vchiq_platform_init_state(struct vchiq_state *state)
struct vchiq_2835_state *platform_state;
state->platform_state = kzalloc(sizeof(*platform_state), GFP_KERNEL);
+ if (!state->platform_state)
+ return VCHIQ_ERROR;
+
platform_state = (struct vchiq_2835_state *)state->platform_state;
platform_state->inited = 1;
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c
index 6057a90436fc..0c387b6473a5 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c
@@ -2209,6 +2209,8 @@ vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero)
local->debug[DEBUG_ENTRIES] = DEBUG_MAX;
status = vchiq_platform_init_state(state);
+ if (status != VCHIQ_SUCCESS)
+ return VCHIQ_ERROR;
/*
bring up slot handler thread
diff --git a/drivers/staging/vt6655/upc.h b/drivers/staging/vt6655/upc.h
index d028ad2ba0d4..e086ec6e77f7 100644
--- a/drivers/staging/vt6655/upc.h
+++ b/drivers/staging/vt6655/upc.h
@@ -20,10 +20,8 @@
/*--------------------- Export Definitions -------------------------*/
-
/* For memory mapped IO */
-
#define VNSvInPortB(dwIOAddress, pbyData) \
(*(pbyData) = ioread8(dwIOAddress))
diff --git a/drivers/staging/wilc1000/Kconfig b/drivers/staging/wilc1000/Kconfig
index 81d052a46e57..59e58550d139 100644
--- a/drivers/staging/wilc1000/Kconfig
+++ b/drivers/staging/wilc1000/Kconfig
@@ -34,7 +34,6 @@ config WILC1000_SPI
config WILC1000_HW_OOB_INTR
bool "WILC1000 out of band interrupt"
depends on WILC1000_SDIO
- default n
help
This option enables out-of-band interrupt support for the WILC1000
chipset. This OOB interrupt is intended to provide a faster interrupt
diff --git a/drivers/staging/wilc1000/host_interface.c b/drivers/staging/wilc1000/host_interface.c
index e1a35bb426f9..ed15bd1bcd56 100644
--- a/drivers/staging/wilc1000/host_interface.c
+++ b/drivers/staging/wilc1000/host_interface.c
@@ -774,7 +774,7 @@ int wilc_disconnect(struct wilc_vif *vif)
result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
wilc_get_vif_idx(vif));
if (result) {
- netdev_err(vif->ndev, "Failed to send dissconect\n");
+ netdev_err(vif->ndev, "Failed to send disconnect\n");
return result;
}
diff --git a/drivers/staging/wilc1000/wilc_netdev.c b/drivers/staging/wilc1000/wilc_netdev.c
index 1787154ee088..ba78c08a17f1 100644
--- a/drivers/staging/wilc1000/wilc_netdev.c
+++ b/drivers/staging/wilc1000/wilc_netdev.c
@@ -708,7 +708,7 @@ static void wilc_set_multicast_list(struct net_device *dev)
return;
}
- mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_KERNEL);
+ mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC);
if (!mc_list)
return;
diff --git a/drivers/staging/wilc1000/wilc_wlan.c b/drivers/staging/wilc1000/wilc_wlan.c
index c2389695fe20..0a713409ea98 100644
--- a/drivers/staging/wilc1000/wilc_wlan.c
+++ b/drivers/staging/wilc1000/wilc_wlan.c
@@ -11,7 +11,7 @@
static inline bool is_wilc1000(u32 id)
{
- return ((id & 0xfffff000) == 0x100000 ? true : false);
+ return (id & 0xfffff000) == 0x100000;
}
static inline void acquire_bus(struct wilc *wilc, enum bus_acquire acquire)
@@ -316,7 +316,7 @@ int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
if (wilc->quit)
return 0;
- tqe = kmalloc(sizeof(*tqe), GFP_KERNEL);
+ tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
if (!tqe)
return 0;
@@ -408,7 +408,7 @@ void chip_wakeup(struct wilc *wilc)
wilc->hif_func->hif_write_reg(wilc, 1, reg & ~BIT(1));
do {
- usleep_range(2 * 1000, 2 * 1000);
+ usleep_range(2000, 2500);
wilc_get_chipid(wilc, true);
} while (wilc_get_chipid(wilc, true) == 0);
} while (wilc_get_chipid(wilc, true) == 0);
@@ -423,7 +423,7 @@ void chip_wakeup(struct wilc *wilc)
&clk_status_reg);
while ((clk_status_reg & 0x1) == 0) {
- usleep_range(2 * 1000, 2 * 1000);
+ usleep_range(2000, 2500);
wilc->hif_func->hif_read_reg(wilc, 0xf1,
&clk_status_reg);
diff --git a/drivers/staging/wlan-ng/Kconfig b/drivers/staging/wlan-ng/Kconfig
index b796bfd429c4..ac136663fa8e 100644
--- a/drivers/staging/wlan-ng/Kconfig
+++ b/drivers/staging/wlan-ng/Kconfig
@@ -4,7 +4,6 @@ config PRISM2_USB
depends on WLAN && USB && CFG80211
select WIRELESS_EXT
select WEXT_PRIV
- default n
help
This is the wlan-ng prism 2.5/3 USB driver for a wide range of
old USB wireless devices.